type:
snippet
category:
shipping
name:
Restricting Countries and States on the Checkout
versions:
1.0, 1.1
tags:
snippets, shipping, advance
date:
2012-09-05

The functionality detailed on this page is for FoxyCart versions 1.0 and newer. If you're using 0.7.2 or older, please see this page.

Using version 2.0? There is native functionality that covers this snippet within the configuration section of the administration. There is also a new snippet available for our latest version, available from here.

Restricting Countries and States on the Checkout

The following functionality adds some helper functions to your checkout to allow you to more easily restrict the countries and states that can be used by your customers to check out.

The functionality described on this page require advanced javascript knowledge, and are not officially supported. They are included in our official wiki because certain shipping methods and functionality are not natively supported, and though we are working on radically improving our shipping functionality, in the meantime these methods may be great workarounds. Use with caution, test, and post in our forum if you run into problems.

See the changelog for details on updates to this script.

Step 1: Add Javascript

Add the following right before the closing </head> tag in your checkout template:

<script type='text/javascript' charset='utf-8'>
	//<![CDATA[
	jQuery(document).ready(function() {
 
		/* BEGIN CUSTOM LOCATION LOGIC */
 
 
 
		/* END CUSTOM LOCATION LOGIC */
 
		FC.locations.updateFoxyComplete(true);
	});
	//]]>
</script>
 
<script type='text/javascript' charset='utf-8'>
	//<![CDATA[
	// Country/State Helper Functions v1.1
	// Do not modify the following functions
 
	FC.locations.removeCountries = function(countries, locationArrayNames) {
		if (typeof countries == 'undefined') { return false }
		if (typeof countries == 'string') { countries = [countries]; }
		locationArrayNames = FC.locations.validateLocationArrayNames(locationArrayNames);
 
		for (l in locationArrayNames) {
			var locationArray = FC.locations.getLocationArray(locationArrayNames[l]);
			for (var c in countries) {
				if (typeof locationArray[countries[c]] == 'undefined') { break; }
				delete locationArray[countries[c]];
			}
		}
 
		return true;
	}
 
	FC.locations.limitCountriesTo = function(countries, locationArrayNames) {
		if (typeof countries == 'undefined') { return false }
		if (typeof countries == 'string') { countries = [countries]; }
		locationArrayNames = FC.locations.validateLocationArrayNames(locationArrayNames);
 
		for (l in locationArrayNames) {
			var newLocations = {};
			var locationArray = FC.locations.getLocationArray(locationArrayNames[l]);
			for (var c in countries) {
				if (typeof locationArray[countries[c]] == 'undefined') { break; }
				newLocations[countries[c]] = locationArray[countries[c]];
			}
 
			// Prevent the countries being set to nothing
			if (newLocations == {}) { return false; }
 
			if (locationArrayNames[l] == 'customer') {
				FC.locations.config.locations = newLocations;
			} else {
				FC.locations.config.shippingLocations = newLocations;
			}
		}
		return true;
	}
 
	FC.locations.removeStates = function(country, states, locationArrayNames) {
		if (typeof country == 'undefined' || typeof states == 'undefined') { return false }
		if (typeof states == 'string') { states = [states]; }
		locationArrayNames = FC.locations.validateLocationArrayNames(locationArrayNames);
 
		for (l in locationArrayNames) {
			var locationArray = FC.locations.getLocationArray(locationArrayNames[l]);
			if (typeof locationArray[country] == 'undefined') { return false; }
			for (var s in states) {
				if (typeof locationArray[country].r[states[s]] == 'undefined') { break; }
				delete locationArray[country].r[states[s]];
			}
		}
		return true;
	}
 
	FC.locations.limitStatesTo = function(country, states, locationArrayNames) {
		if (typeof country == 'undefined' || typeof states == 'undefined') { return false }
		if (typeof states == 'string') { states = [states]; }
		locationArrayNames = FC.locations.validateLocationArrayNames(locationArrayNames);
 
		for (l in locationArrayNames) {
			var newLocations = {};
			var locationArray = FC.locations.getLocationArray(locationArrayNames[l]);
			if (typeof locationArray[country] == 'undefined') { return false; }
			for (var s in states) {
				if (typeof locationArray[country].r[states[s]] == 'undefined') { break; }
				newLocations[states[s]] = locationArray[country].r[states[s]];
			}
 
			if (locationArrayNames[l] == 'customer') {
				FC.locations.config.locations[country].r = newLocations;
			} else {
				FC.locations.config.shippingLocations[country].r = newLocations;
			}
		}
		return true;
	}
 
	FC.locations.updateFoxyComplete = function(blockErrors) {
		FC.checkout.config.evaluateAjaxRequests = false;
 
		FC.checkout.setAutoComplete('customer_country');
		if (jQuery('#customer_country_name') != '') {
			FC.checkout.validateLocationName('customer_country');
		}
		if (jQuery('#customer_state_name').val() != '') {
			FC.checkout.validateLocationName('customer_state');
		}
		if (blockErrors) {
			FC.checkout.updateErrorDisplay('customer_country_name', false);
			FC.checkout.updateErrorDisplay('customer_state_name', false);
		}
		if (!FC.checkout.config.hasMultiship) {
			FC.checkout.setAutoComplete('shipping_country');
			if (jQuery('#shipping_country_name') != '') {
				FC.checkout.validateLocationName('shipping_country');
			}
			if (jQuery('#shipping_state_name') != '') {
				FC.checkout.validateLocationName('shipping_state');
			}
			if (blockErrors) {
				FC.checkout.updateErrorDisplay('shipping_country_name', false);
				FC.checkout.updateErrorDisplay('shipping_state_name', false);
			}
 
			FC.checkout.config.evaluateAjaxRequests = true;
			FC.checkout.updateShipping(-1);
			FC.checkout.updateTaxes(-1);
		} else {
			for (var i = 0; i < FC.checkout.config.multishipDetails.length; i++) {
				FC.checkout.setAutoComplete('shipto_' + i + '_country');
				if (jQuery('#shipto_' + i + '_country_name') != '') {
					FC.checkout.validateLocationName('shipto_' + i + '_country');
				}
				if (jQuery('#shipto_' + i + '_state_name') != '') {
					FC.checkout.validateLocationName('shipto_' + i + '_state');
				}
				if (blockErrors) {
					FC.checkout.updateErrorDisplay('shipto_' + i + '_country_name', false);
					FC.checkout.updateErrorDisplay('shipto_' + i + '_state_name', false);
				}
			}
 
			FC.checkout.config.evaluateAjaxRequests = true;
			for (var i = 0; i < FC.checkout.config.multishipDetails.length; i++) {
				FC.checkout.updateShipping(i);
				FC.checkout.updateTaxes(i);
			}
		}
	}
 
	FC.locations.getLocationArray = function(locationArrayNames) {
		return (locationArrayNames == 'customer') ? FC.locations.config.locations : FC.locations.config.shippingLocations;
	}
 
	FC.locations.validateLocationArrayNames = function(locationArrayNames) {
		if (typeof locationArrayNames == 'undefined' || locationArrayNames == '' || locationArrayNames == 'both') { locationArrayNames = ['customer', 'shipping']; }
		if (typeof locationArrayNames == 'string') { locationArrayNames = [locationArrayNames]; }
		return locationArrayNames;
	}
	//]]>
</script>

Step 2: Add Custom Location Rules

Now the fun part, based on whatever criteria you want, add in the different country/state restrictions you require for your checkout. Add your custom code between the /* BEGIN CUSTOM LOCATION LOGIC */ and /* END CUSTOM LOCATION LOGIC */ lines in the first script block. There are six functions available to you.

FC.locations.removeCountries()

Removes the specified countries from the specified locations arrays

Parameters:

  • countries (String or Array) - Either a string or an array of strings of the 2 character ISO country codes
  • locationArrayNames (String or Array) - Optional - Either a string of either 'customer' , 'shipping' or 'both' , or an array of both like ['customer', 'shipping']. If not specified, 'both' is the automatic default.

Example:

  • FC.locations.removeCountries('US'); - Removes the United States from both the billing and the shipping country location fields
  • FC.locations.removeCountries(['CA', 'GB'], 'shipping'); - Removes Canada and the United Kingdom from the shipping location fields

FC.locations.limitCountriesTo()

Restricts the specified locations arrays to just the countries specified

Parameters:

  • countries (String or Array) - Either a string or an array of strings of the 2 character ISO country codes
  • locationArrayNames (String or Array) - Optional - Either a string of either 'customer' , 'shipping' or 'both' , or an array of both like ['customer', 'shipping']. If not specified, 'both' is the automatic default.

Example:

  • FC.locations.limitCountriesTo('AU'); - Allows only Australia to be selected in the billing and shipping country location fields
  • FC.locations.limitCountriesTo(['US', 'CA'], 'shipping'); - Allows only the United States and Canada to be selected for the shipping country

FC.locations.removeStates()

Removes the specified states from the specified locations arrays for the specified country

Parameters:

  • country (String) - A string of the 2 character ISO country codes
  • states (String or Array) - Either a string or an array of strings of the ISO state codes
  • locationArrayNames (String or Array) - Optional - Either a string of either 'customer' , 'shipping' or 'both' , or an array of both like ['customer', 'shipping']. If not specified, 'both' is the automatic default.

Example:

  • FC.locations.removeStates('US', 'AK'); - Prevents Alaska from being selected as a state when the United States is selected
  • FC.locations.removeStates('US', ['AK', 'HI'], 'shipping'); - Prevents Alaska and Hawaii from being selected as a state for shipping when the United States is selected as the shipping country

FC.locations.limitStatesTo()

Restricts the states for the specified country to only those specified

Parameters:

  • country (String) - A string of the 2 character ISO country codes
  • states (String or Array) - Either a string or an array of strings of the ISO state codes
  • locationArrayNames (String or Array) - Optional - Either a string of either 'customer' , 'shipping' or 'both' , or an array of both like ['customer', 'shipping']. If not specified, 'both' is the automatic default.

Example:

  • FC.locations.limitStatesTo('US', 'CA'); - Allows only California to be selected as a state when the United States is selected
  • FC.locations.limitStatesTo('AU', ['SA', 'VIC', 'QLD'], 'shipping'); - Allows only South Australia, Victoria and Queensland from being selected for shipping when Australia is selected as the shipping country.

FC.locations.updateFoxyComplete()

Updates the location fields with the new changes

Parameters:

  • blockErrors (Boolean) - Specify whether or not errors should be suppressed when validating the locations, to prevent 'empty field' errors.

Example:

  • FC.locations.updateFoxyComplete(true);

Notes: This should only be called once all of the required restrictions have been completed. This function has also been included by default in the included logic after the closing /* END CUSTOM LOCATION LOGIC */ comment, so unless you're doing something custom, you shouldn't have to worry about this function.

Examples

Example 1

  1. Restricting the checkout to only allow shipping to the United States, but allow billing to be from any country
FC.locations.limitCountriesTo('US', 'shipping');
FC.checkout.requireShippingAddress();

Example 2

  1. Only allow customers from the US and Canada, and not from Alaska or Hawaii.
FC.locations.limitCountriesTo(['US', 'CA']);
FC.locations.removeStates('US', ['AK', 'HI']);

Example 3

  1. Remove all non-standard US States
FC.locations.removeStates('US', ['AF','AA','AC','AE','AM','AP','AS','PR','VI','GU','FM','MH','MP'], 'both');

Example 4

  1. Remove countries that are currently under sanction restrictions from the US Office of Foreign Assets Control (Note that this list may not be up to date)
FC.locations.removeCountries(['CU','IR','SD','KP']);

What about making it read-only?

As part of the new autocomplete functionality in 1.0, any location object that only has one option will automatically become read-only with that location selected by default. If you want to set a field read only, utilise the limitCountriesTo() or limitStatesTo() functions with only a single location specified.

Changelog

  • 2012/05/09 - v1.0 - Initial version
  • 2013/03/19 - v1.1 - Changed updateFoxyComplete() to prevent validateLocationName() from calling updateShipping(), to prevent the repetition of shipping results displayed

Site Tools