Live Rate Shipping Modification

This snippet has been replaced. In place of the following snippet, we now have a custom shipping code feature in FoxyCart 2.0 that allows you to modify returned live rates and provide custom rates to your customers without needing to add extra javascript to change the way the checkout works. Please use the new custom shipping code functionality instead of this snippet. Review this page for notes on migrating to the new functionality. This page will remain for reference.

Test! The following code is new, and while it has been tested in specific scenarios, there may be bugs or issues present. Ensure that you test thoroughly within your set up.

Using Multiship? Unfortunately this snippet is only designed to work with single ship stores. Multiship support coming soon.

The following functionality allows you to add, update and remove any number of live rate shipping options based on various criteria (eg: what categories are products in, total cost of the cart, shipping destination country). This script is limited to live rate shipping only.

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.

Custom shipping snippets do not work for subscriptions. The reason for this is because the snippets are done as Javascript modifications and run in the browser, while subscriptions run completely on the server side and never touch the browser. Subscriptions will always calculate on the server side based on how the category is configured.

See the changelog for details on updates to this script.

Step 1: Update Categories and Shipping

Update categories to 'Shipped using live rates' and select at least one live rate option from the 'shipping' page.

Step 2: Disable Shipping Rate Signing

If you're planning to add new custom rates, or modify the returned rates in any way (such as altering the price or changing the labels), you will need to ensure that shipping rate signing is disabled for your store. If you just plan to hide/show the returned rates, you can skip this step.

To disable shipping rate signing, head to the “shipping” section of your store's FoxyCart administration, and at the bottom of the page ensure that “enable shipping rate signing” is disabled.

Step 3: Add Javascript

Add the following to the “custom footer” field of the “Add custom header and footer code to your templates” section in your store's FoxyCart administration, in TEMPLATES » configuration:

{% if context == 'cart' or context == 'checkout' %}
<script type="text/javascript" charset="utf-8">
(function (FC, $) {
    FC.customLiveShipping = {};
 
    FC.customLiveShipping.config = {
        autoSelect: false  /* Set to true if you'd like the top shipping option to be automatically selected for the user */
    };
 
    FC.customLiveShipping.logic = function() {
    /* BEGIN CUSTOM SHIPPING LOGIC */
 
 
 
    /* END CUSTOM SHIPPING LOGIC */
    };
 
    /* Live Rate Shipping Modification Logic v2.0.16 */
    FC.customLiveShipping.add=function(a,d,c,b){FC.json.shipping_address.shipping_results.push({method:c,price:d,service_id:a,service_name:b})};FC.customLiveShipping.hide=function(a){a=FC.customLiveShipping.filterShippingOptions(a);for(var d=0;d<a.length;d++)FC.json.shipping_address.shipping_results[a[d]].hide=!0};FC.customLiveShipping.show=function(a){a=FC.customLiveShipping.filterShippingOptions(a);for(var d=0;d<a.length;d++)FC.json.shipping_address.shipping_results[a[d]].hide=!1};
    FC.customLiveShipping.update=function(a,d,c,b){a=FC.customLiveShipping.filterShippingOptions(a);for(var e=0;e<a.length;e++){if("number"===typeof d||"string"===typeof d&&""!==d){var f=FC.customLiveShipping.modifyPrice(FC.json.shipping_address.shipping_results[a[e]].price,d);FC.json.shipping_address.shipping_results[a[e]].price=f}"string"===typeof c&&(FC.json.shipping_address.shipping_results[a[e]].method=c);"string"===typeof b&&(FC.json.shipping_address.shipping_results[a[e]].service_name=b)}};
    FC.customLiveShipping.remove=function(a){FC.customLiveShipping.hide(a)};FC.customLiveShipping.reset=function(){FC.util.addressHasLocationInfo(FC.json.shipping_address)&&(0<FC.json.shipping_address.shipping_results.length&&(FC.customLiveShipping.ratesHash=""),FC.customLiveShipping.shipping_service_id=FC.json.shipping_address.shipping_service_id,FC.cart.showShippingLocationInput(),FC[FC.json.context].getShippingOptions({address:FC.json.shipping_address}))};
    function customLiveShippingHandler(){FC.customLiveShipping.execute()}function customCouponHandler(){FC.customLiveShipping.reset()}
    FC.customLiveShipping.execute=function(){if(0<FC.json.shipping_address.shipping_results.length&&FC.customLiveShipping.hasChanged()){for(var a=[],d=[],c=!1,b=0;b<FC.json.shipping_address.shipping_results.length;b++)-1==a.indexOf(FC.json.shipping_address.shipping_results[b].service_id)?(a.push(FC.json.shipping_address.shipping_results[b].service_id),d.push(FC.json.shipping_address.shipping_results[b])):c=!0;c&&(FC.json.shipping_address.shipping_results=d);FC.customLiveShipping.logic(FC.json.shipping_address);
    FC.customLiveShipping.hasOwnProperty("shipping_service_id")&&0==FC.json.shipping_address.shipping_service_id&&(FC.json.shipping_address.shipping_service_id=FC.customLiveShipping.shipping_service_id);a=!1;for(b=FC.json.shipping_address.shipping_results.length-1;0<=b;b--)FC.json.shipping_address.shipping_results[b].hide?FC.json.shipping_address.shipping_results.splice(b,1):FC.json.shipping_address.shipping_results[b].service_id==FC.json.shipping_address.shipping_service_id&&(a=!0);a||(FC.json.shipping_address.shipping_service_description=
    "",FC.json.shipping_address.shipping_service_id=0);FC.json.shipping_address.shipping_results.sort(function(a,b){return a.price-b.price});FC.customLiveShipping.config.autoSelect&&0==FC.json.shipping_address.shipping_service_id&&(FC.json.shipping_address.shipping_service_id=FC.json.shipping_address.shipping_results[0].service_id,FC.json.show_shipping_tbd=!1);FC.Template(FC.json.context).clearOutput();FC.cart.recalculateCartTotals();FC[FC.json.context].renderShippingRates(FC.json.shipping_address);0<
    FC.json.shipping_address.shipping_service_id&&jQuery("#shipping_service_id_"+FC.json.shipping_address.shipping_service_id).trigger("click.fc");FC.customLiveShipping.ratesHash=FC.util.hashString(JSON.stringify(FC.json.shipping_address.shipping_results))}delete FC.customLiveShipping.shipping_service_id};
    FC.customLiveShipping.hasChanged=function(){return FC.customLiveShipping.hasOwnProperty("ratesHash")&&""!=FC.customLiveShipping.ratesHash?FC.customLiveShipping.ratesHash!=FC.util.hashString(JSON.stringify(FC.json.shipping_address.shipping_results))?!0:!1:!0};
    FC.customLiveShipping.filterShippingOptions=function(a){if("number"==typeof a){for(var d=[],c=0;c<FC.json.shipping_address.shipping_results.length;c++)FC.json.shipping_address.shipping_results[c].service_id==a&&(d=[c]);return d}if("string"==typeof a){d=[];var b={};for(c=0;c<FC.json.shipping_address.shipping_results.length;c++){var e=FC.json.shipping_address.shipping_results[c];b[c]=e.method+" "+e.service_name}if("all"!=a.toLowerCase()){a=/(fedex|usps|ups)?\s?(.+)?/i.exec(a);if(void 0==a||""==a)return;
    for(c in b)void 0!=a[1]&&""!=a[1]&&-1==b[c].toLowerCase().indexOf(a[1].toLowerCase())?delete b[c]:void 0!=a[2]&&""!=a[2]&&-1==b[c].toLowerCase().indexOf(a[2].toLowerCase())&&delete b[c]}for(c in b)d.push(parseInt(c));return d}if("object"==typeof a){d=[];for(c=0;c<a.length;c++)for(b=0;b<FC.json.shipping_address.shipping_results.length;b++)FC.json.shipping_address.shipping_results[b].service_id==a[c]&&d.push(b);return d}};
    FC.customLiveShipping.modifyPrice=function(a,d){d=d.toString();var c=/([\+\-=\*\/])?(\d+(?:\.\d+)?)(%)?/.exec(d);a=parseFloat(a);var b=parseFloat(c[2]);void 0!=c[3]&&""!=c[3]&&(b=b/100*a);switch(void 0==c[1]&&""!=c[1]?"=":c[1]){case "+":a+=b;break;case "-":a-=b;break;case "/":a/=b;break;case "*":a*=b;break;default:a=b}return 0>a?0:a};
    FC.client.on("cart-coupon-add.done",customCouponHandler);FC.client.on("cart-coupon-remove.done",customCouponHandler);
    {% if context == "cart" and not cart_is_fullpage %}
        FC.client.on("cart-shipping-options-update.done",customLiveShippingHandler);FC.customLiveShipping.reset();
    {% else %}
        $(document).ajaxComplete(function(b,c,a){b=/ThisAction=(\w+)/;void 0!==a&&a.hasOwnProperty("url")&&-1!=a.url.search(b)&&a.url.match(b);FC.customLiveShipping.execute()});FC.client.on("customer-login.done",FC.customLiveShipping.reset);FC.client.on("ready.done",FC.customLiveShipping.reset);
        if("required"==FC.json.config.template_config.analytics_config.google_analytics.usage)FC.client.on("checkout-submit",function(b,a){setTimeout(a,500)});
    {% endif %}
})(FC, jQuery);
</script>
{% endif %}

Alternatively you can also add this code right at the end of the cart include template (not the cart or checkout templates, the cart include template will include it there for you).

If you have created your own cart or checkout templates, ensure you've followed the steps to include the custom code placeholders as detailed here

Step 4: Update Configuration

This script currently provides a single option to alter the way the script works, present at the top of the javascript in step 3.

FC.customLiveShipping.config = {
  'autoSelect': false
};
  • autoSelect: If set to true, the top option for rates will be automatically selected after your custom logic has run.

Step 5: Customise Shipping Options

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

FC.customLiveShipping.add()

Adds an additional shipping option to the checkout.

Parameters:

  • code (Number) - Can not be the same as another rate, we suggest 300 or above.
  • cost (Number) - A number to two decimal places
  • carrier (String)
  • service (String)

Example:

  • FC.customLiveShipping.add(300, 4.99, 'PostBox', 'Local Delivery');
  • FC.customLiveShipping.add(325, 30, ' ', 'Standard Mail');

Notes: Make sure that the code you give the added rate doesn't duplicate a code for a returned rate you've selected. We suggest just giving your custom added rates a high code number. Also, you don't have to provide both the carrier and the service parameters - but at least one of them is required.


FC.customLiveShipping.hide()

Hides one or many existing shipping options.

Parameters:

  • selector (Number, String or Array) - Can be the code number of a rate, a string containing the carrier and the service (or a combination of) or an array of codes.

Example:

  • FC.customLiveShipping.hide(1); - Will hide rate 1
  • FC.customLiveShipping.hide('all'); - Will hide all rates
  • FC.customLiveShipping.hide('FedEx'); - Will hide all rates for FedEx
  • FC.customLiveShipping.hide('Overnight'); - Will hide all rates with a service name that contains 'Overnight'
  • FC.customLiveShipping.hide('USPS Express'); - Will hide any rates from USPS that contain the word 'Express'
  • FC.customLiveShipping.hide([1,2,5,7]); - Will hide rates with codes 1,2,5 and 7

Notes: Any rates that are still hidden at the end of the custom logic block will be removed to prevent them being shown when you don't want them to. They will be returned if the shipping options are refreshed by an address change by the customer.


FC.customLiveShipping.show()

Shows one or many existing shipping options.

Parameters:

  • selector (Number, String or Array) - Can be the code number of a rate, a string containing the carrier and the service (or a combination of) or an array of codes.

Example:

  • FC.customLiveShipping.show(1); - Will show rate 1
  • FC.customLiveShipping.show('all'); - Will show all rates
  • FC.customLiveShipping.show('FedEx'); - Will show all rates for FedEx
  • FC.customLiveShipping.show('Overnight'); - Will show all rates with a service name that contains 'Overnight'
  • FC.customLiveShipping.show('USPS Express'); - Will show any rates from USPS that contain the word 'Express'
  • FC.customLiveShipping.show([1,2,5,7]); - Will show rates with codes 1,2,5 and 7

FC.customLiveShipping.update()

Updates the cost of one or many existing shipping options.

Parameters:

  • selector (Number, String or Array) - Can be the code number of a rate, a string containing the carrier and the service (or a combination of) or an array of codes.
  • modifier (String or Number) - Can either be a number (which sets the price to match) or a string containing the operator and a number eg '+20' , '-10' , '*2' , '/2.5' , '=15' . You can also append the string with a % sign to make the operation based on a percentage, eg '+20%' - add 20%, '-20%' - less 20%, '/20%' - divide by 20%, '*20%' - multiply by 20%.
  • carrier (String)
  • service (String)

Examples:

  • FC.customLiveShipping.update(1, 5); - Will set rate 1 to be $5
  • FC.customLiveShipping.update('all', '*2'); - Will set all returned rates to double their returned cost
  • FC.customLiveShipping.update('FedEx', '+5'); - Will set all rates for FedEx to be $5 more than what they returned as
  • FC.customLiveShipping.update('Overnight', '-5'); - Will set all rates with a service name that contains 'Overnight' to be $5 less than returned
  • FC.customLiveShipping.update('USPS Express', '=6'); - Will set any rates from USPS that contain the word 'Express' to be $6
  • FC.customLiveShipping.update([1,2,5,7], '/2'); - Will set rates with codes 1,2,5 and 7 to be half their returned cost
  • FC.customLiveShipping.update('USPS', '+20%'); - Will add 20% of the returned rate to each of the USPS rates
  • FC.customLiveShipping.update('USPS Ground', null, null, 'Super Saver'); - Will change “USPS Ground” to be called “USPS Super Saver”

FC.customLiveShipping.remove()

Removes one or many existing shipping options.

Parameters:

  • selector (Number, String or Array) - Can be the code number of a rate, a string containing the carrier and the service (or a combination of) or an array of codes.

Example:

  • FC.customLiveShipping.remove(1); - Will remove rate 1
  • FC.customLiveShipping.remove('all'); - Will remove all rates
  • FC.customLiveShipping.remove('FedEx'); - Will remove all rates for FedEx
  • FC.customLiveShipping.remove('Overnight'); - Will remove all rates with a service name that contains 'Overnight'
  • FC.customLiveShipping.remove('USPS Express'); - Will remove any rates from USPS that contain the word 'Express'
  • FC.customLiveShipping.remove([1,2,5,7]); - Will remove rates with codes 1,2,5 and 7

FC.customLiveShipping.reset()

Resets the shipping options by re-requesting them from the FoxyCart servers

Example:

  • FC.customLiveShipping.reset();

Notes: This function should not need to be called from within your custom logic. However you may want to call this function on custom fields you have added to the checkout that may affect shipping costs - for example if you have a gift wrapping service that only allows for certain shipping services and adds a premium to the shipping cost.


How the selector works

In the show(), hide(), update() and remove() functions, you can specify a selector, which can be either a number, a string or an array. See the following for details of how they work:

Number
Description: A selector for a single returned rate based on its code
Example: 1 or 25
String
Description: A more complex selector that searches the carrier and service for matches, allowing you to select a variable amount of returned rates. While it's not case sensitive, the spelling obviously must match what is returned in the rates.
Examples:
  • 'all' - By specifying this string, all returned rates will be modified
  • 'fedex' , 'ups' , 'usps' - will select all rates for the specified carrier. Note that only one carrier can be specified in a selector
  • 'overnight' , 'priority mail' , '2 day' - if the string does not match a carrier as above, it will be assumed it is a service, and will look for the string anywhere within it's service name. So if 'overnight' is specified, it would match a string of really quick overnight and overnight service.
  • 'fedex priority overnight' - if both the carrier and the service filter is specified (the carrier must always be the first word, followed by a space, followed by the service string), then it will be filtered by both. Again 'fedex' will match against the carrier name, and the rest of the string provided will match against the service. In this example, the service matching would be looking for priority overnight within the service name.
Array
Description: An array of numbers to select multiple rates by their code
Example: [1,4,6,8]

Examples

Example 1

  1. If the customer is shipping to the stores location, allow for pickup option, otherwise remove it
  2. If the order is more than $100 and they are shipping within the US, allow for free shipping.
var state = FC.json.shipping_address.region;
var country = FC.json.shipping_address.country;
 
if (state != "NY") FC.customLiveShipping.remove("pickup");
if ((country == "US" && FC.json.total_item_price <= 100) || country != "US") {
  FC.customLiveShipping.remove("free");
}

Example 2

  1. If the customer has ordered products from the 'food' category, only show overnight delivery options
  2. If the customer has ordered 3 or more food items, update the priority overnight option to be free
var food = 0;
for (p in FC.json.items) {
  switch (FC.json.items[p].category) {
    case "food":
      food += FC.json.items[p].quantity;
      break;
  }
}
 
if (food > 0) {
  // Hide all shipping options
  FC.customLiveShipping.hide('all');
  // Show only the overnight options
  FC.customLiveShipping.show('overnight');
  // If 3 or more food items, update priority overnight to be free
  if (food >= 3) FC.customLiveShipping.update('priority overnight', 0);
}

Example 3

  1. Allow free shipping only if a certain coupon code is present. In this case, a coupon whose name, not the code, contains the words “free shipping” (case insensitive).
    • Note that while the coupon's name is “free shipping”, it is activated by a code, which can be, for example, “FREE576”. Here we are looking for the coupon name “free shipping”, not the code “FREE576”.
  2. This assumes you've enabled the 'free ground shipping' custom rate in your store's “shipping” settings: Go to STORE » shipping, under the CUSTOM OPTIONS title check the “use custom:” box, then in the list of options that appears check the “free ground shipping” box. Click “Update”.
FC.customLiveShipping.hide('free');
if(!jQuery.isEmptyObject(FC.json.coupons)) {
	jQuery.each(FC.json.coupons, function(i, coupon){
		if(coupon.name.search(/free shipping/i) > -1) {
			FC.customLiveShipping.show('free');
		}
	});
}

Example 4

  1. Only allow free shipping if a certain number of products (more than 5 in this case) are ordered.
  2. This assumes you've enabled the 'free ground shipping' custom rate in your store's “shipping” settings.
FC.customLiveShipping.hide('free');
if(FC.json.item_count > 5) {
	FC.customLiveShipping.show('free');
}

Example 5

  1. Only provide FedEx rates to Canada, but allow any other selected live rate carriers for any other countries
if(FC.json.shipping_address.country == "CA") {
	FC.customLiveShipping.hide('all');
	FC.customLiveShipping.show('fedex');
}

Example 6

  1. If the total order is over $99, offer free shipping on the priority mail option (for both domestic and international orders). You'll need to have USPS priority mail option turned on in your shipping page of your store's FoxyCart administration.
  2. Make sure “shipping rate signing” is turned off in your shipping page of your store's FoxyCart administration. The approach to include custom free shipping will not work with the rate signing functionality, so we need to turn that off in this instance.
  3. If your cart contains a discount, the free shipping logic will be applied after the discount, so if the cart's original total is over $99, but a discount makes the total less than $99, the free shipping will not apply.
var eligible_amount = FC.json.total_item_price + FC.json.total_discount;
if (eligible_amount > 99) {
    FC.customLiveShipping.update('priority mail', 0);
}

Changelog

  • 2014/08/29 - v2.0 - Initial version
  • 2014/09/10 - v2.0.1 - Minor changes and bugfixes
  • 2014/09/18 - v2.0.2 - Fix for the shipping description not being set correctly
  • 2014/09/20 - v2.0.3 - Fix missing semi-colons causing issues in Sidecart
  • 2014/11/06 - v2.0.4 - Fix reset() to also check the needed address information is present
  • 2014/12/30 - v2.0.5 - Ensure that filterShippingOptions() returns an array when a number doesn't match a rate.
  • 2015/01/16 - v2.0.6 - Fix issue with early versions of IE acting strangely with regex
  • 2015/02/19 - v2.0.7 - Ensure rates are refreshed in the cart after a change to coupons
  • 2015/02/20 - v2.0.8 - Fix for issue on checkout submit that could potentially reset rates in specific scenarios
  • 2015/04/09 - v2.0.9 - Ensure a preselected rate still exists after entering a new address before auto-selecting a rate. Also re-order rates by least to most expensive after altering.
  • 2016/01/09 - v2.0.10 - Fixed an issue with coupons on the checkout
  • 2016/06/02 - v2.0.11 - Altered how the live rates logic hooks into the checkout events to make sure it only runs when it should.
  • 2016/06/16 - v2.0.12 - Ensure coupon changes trigger rate refresh on checkout
  • 2016/08/25 - v2.0.13 - Expanded the instances where the custom logic is attempted to be executed
  • 2016/09/29 - v2.0.14 - Corrected rate filter strings to support special characters
  • 2017/08/08 - v2.0.15 - Ensure custom rates are re-selected after a reset() call
  • 2018/02/17 - v2.0.16 - Ensuring rates are re-run correctly after checkout validation errors when Google Analytics is enabled

Site Tools