We come across a requirement to restrict specific products (not complete catalogue) to sale in specific countries (different products per country). On a website with multi-currency per country and worldwide delivery of products its not a straight forward task.
If we want to restrict product for sale in a specific country then potential customers (including anonymous customers) should not able to add to basket and not able to search these products to avoid disappointment.
Available default Options
Markets: If we create different markets for each country, We can only add a “Valid” price for a country where a specific product can be sold. This approach seems fine and it works with Episerver find as well but it is very hard to maintain. We probably need to create 100s of markets as one per country. We also have to add and maintain prices of these markets and if the catalogue has products in 1000s then it will be very hard to manage. A potential customer will still be able to buy (if they manually change cookie value )
Restrict countries in payment provider (Allowed / Disallowed countries). So for example, if the UK is a restricted country in payment provider then customers from the UK would not able to buy any product. The problem with this approach is; payment provider restricts all products from a specific country. We want to restrict specific products per country.
Customer Groups: Product price can be restricted per customer group but it will be very hard to workout Customer groups if the customer is not logged-in and this approach has similar issues like Markets.
Solution
There are different moving parts of this solution
- Getting an accurate visitor location
- Defining which product or variant is restricted in which country.
- Restricting products or variants in search based on their restrictions.
- Restricting variant to Add to basket
Get an accurate visitor location
All Episerver DXP environments use Cloudflare CDN and it provides a request header (“CF-IPCountry”) that tell us visitor location. So we can create a helper method to get a visitor’s location.
using System.Web;
namespace Foundation.Helpers
{
public static class CustomerHelpers
{
public static string GetVisitorLocation(HttpRequestBase httpRequest)
{
var countryIso = httpRequest.Headers["CF-IPCountry"];
return countryIso;
}
}
}
Add variant Property for restricted countries
Create a property of variant that takes a list of all restricted countries. I have created string type property that uses a custom selection factory “CountriesSelectionFactory” and uses SlectMany property decoration to take more than one country ISO name.
Countries Selection Factory
using EPiServer.Shell.ObjectEditing;
using System.Collections.Generic;
namespace Foundation.Features.Blocks.ElevatedRoleBlock
{
public class CountriesSelectionFactory : ISelectionFactory
{
public virtual IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
{
return new ISelectItem[]
{
new SelectItem { Text = "United Kingdom", Value = "GB"},
new SelectItem { Text = "United states", Value = "US"},
new SelectItem { Text = "Canada", Value = "CA"}
// Other countries
};
}
}
}
Property
[Display(Name = "Restricted Countries", Order = 40)]
[SelectMany(SelectionFactoryType = typeof(CountriesSelectionFactory))]
[BackingType(typeof(PropertyString))]
public virtual string RestrictedCountries { get; set; }
Restricting products or variants in search based on their restrictions
We have created property in the variant so it will be available in Epi Find. We can create a filter while getting search results from Episerver find.
https://world.episerver.com/documentation/developer-guides/commerce/search/Configuring-facets-and-filters/
Restricting variant to Add to basket
There are two places you can restrict a variant to add to basket
- IPriceService : If you have created custom price service (an overload of default IPriceService) then you can return invalid price based on visitor location.
- Product/Variant controller: You have visitor location from the helper method and restricted countries from VariantBase. You can create a logic not to show Add to Basket button.