Documentation You are here: start » v » 2.0 » shipping » custom_code

Custom Shipping Code

The Custom Shipping Code option supports adding javascript code to your store's shipping section that is able to add new rates, or modify or remove existing rates that have been returned from other configured shipping rates for your store.

The Custom Shipping Code is processed after any other carriers that have been configured have run, including the Custom Shipping Endpoint. This means that those rates can also be modified as part of your custom code.

This custom code runs serverside, not client-side. Yes, it's javascript, but this javascript doesn't output to the browser. Instead, it's run serverside in Node.js (in a separate environment per store), so you can put private logic (such as coupon codes) in the code.

Example Payload

The custom shipping code receives the same payload as the custom shipping endpoint. See a sample payload.

Category Settings

You'll need to set each Category where you want the Custom Shipping Code to apply to Shipped using live shipping rates. You can do this after applying the custom shipping code to your shipping settings if you're not using live shipping rates already.

Enabling Custom Shipping Code

Enable custom code

To enable Custom Shipping Code for your store, login to your store's Foxy administration and head to the “shipping” section. You'll find the option to “use custom code” as the last option on the page.

Enabling the “use custom code” option will reveal a code editor where you can enter your custom JavaScript shipping code.

The Custom Shipping Code functionality behaves just like a normal carrier like USPS, FedEx or UPS, so requires that any categories that you'd like to have the shipping calculated with your custom code to have a delivery type of “Shipped using live shipping rates”.

Write your custom shipping code

We have a Shipping API that helps you interact with the rates, adding custom rates, or filtering, modifying or removing existing rates (either from our native live rate carriers, or custom rates you add). You can review the example code further down this page for examples of using the Shipping API to modify rates.

Within the shipping code editor, you will also have access to a couple of objects:

  • rates: The rates object for the cart, which will be prepopulated with any rates returned from other options enabled for the store. This object also provides access to the Shipping API.
  • cart: An object containing all of the information about the customers cart. For an overview of what this object looks like, see this page.

The code is processed within a Node.js v14 instance, UTC timezone, with access to the following Node modules:

Within the code editor, you can enter any javascript logic you require for calculating your custom shipping rates. As you enter your shipping code, the code editor will provide in-context feedback for any issues it may detect. Hover over any icons or highlighted code for additional details (note that there is a known issue with the code validations relating to lines of code containing await - you can ignore errors for this)

We recommend enabling the “enable shipping rate signing” option on the shipping page settings as well. If you have any javascript snippets in your store's configuration outside of the custom code entered on the shipping page that affects the returned shipping rates, or the weight used to calculate the shipping rate, you will want to leave this unchecked however.

Deploy your shipping code

Once you have your shipping code entered as desired, click the green “Update” button at the bottom of the page. You will see a message that the shipping code is being deployed, and to refresh the page to get an updated status. Wait for 15-20 seconds and refresh the page - if you see your shipping code displayed again, then your code was successfully deployed!

Migrating from existing snippets

If you are migrating from one of our existing snippets, you can review notes on switching from the snippets to using custom shipping code here.

Auto-Select First Rate

If you'd like your cart to automatically select the first rate shown, you can use the snippet outlined here.

Example Shipping Code

The following are a selection of examples of shipping code to give you an idea of how it can be utilised for your store. For details on the functions available within the custom code, you can review the Shipping API here.

Note that HTML is not currently supported in the rate carrier or service parameters. We do have plans to support it in the future.

Adding a fallback shipping rate

This example simply checks to see if there were any rates returned from third-party carriers like USPS, UPS and FedEx, and if not, returns a fallback $15 rate to ensure your customers will always see a rate.

if (!rates.exists()) {
	rates.add(10001, 15, '', 'Standard Shipping');
}

Flat rates with conditional free shipping

Provide a standard and express flat rate shipping option, setting the standard option to be free if at least $40 of products are present in the cart.

rates.add(10001, 5, 'FoxyPost', 'Standard');
rates.add(10002, 15, 'FoxyPost', 'Express');
if (cart['_embedded']['fx:shipment']['total_item_price'] >= 40) {
	rates.filter(10001).price(0).service('Free Shipping');
}

Flat rates based on order total

Different flat rates based on the product subtotal, with a “pick up” option if a threshold isn't met.

const total_item_price = cart['_embedded']['fx:shipment']['total_item_price'];
 
rates.add(10001, 75, 'FoxyPost', 'Standard');
if (total_item_price <= 200) {
  // Orders $200 and under can only be picked up.
  rates.filter(10001).price(0).service('Customer Pickup');
} else if (total_item_price <= 500) {
  // Orders from $200.01 to $500 ship for $75
  rates.filter(10001).price(75);
} else if (total_item_price <= 1000) {
  rates.filter(10001).price(150);
} else {
  // Orders over $1000, from the last if statement
  rates.filter(10001).price(300);
}

Remove the "1-Day" and "2-Day" text from USPS rates

In some instances, USPS adds an estimated timeframe to their returned rates. While they may be valid for the estimated delivery time when shipped - it can create unrealistic expectations for customers if it may take a couple days to ship. This example removes those timeframe strings from any returned rates.

rates.removeDayTimeframes();

Adding extra text to shipping rate labels

If you want to modify a returned shipping rate label to have a different service, you can do that using the API like this:

rates.filter('FedEx Home Delivery').service('Ground');

That would change the rate FedEx Home Delivery to instead be FedEx Ground.

You can also append to the existing service text though, so it will still use whatever the shipping provider returns. For example, the following would use FedEx Home Delivery again, but would append “ (Signature Required)” to the end of it:

let home_delivery = rates.filter('FedEx Home Delivery');
if (home_delivery.exists()) {
    home_delivery.service(home_delivery.service() + ' (Signature Required)');
}

If you wanted to make a similar adjustment to a lot of different rates, for example adding “ (Signature Required)” to all FedEx rates, you can do that like this:

rates.filter('fedex').each(function() {
    this.service(this.service() + ' (Signature Required)');
});

Providing flat rates based on the shipping country

The following example provides three tiers of shipping rates, one if the customer is within the UK (considered domestic in this example), another if the customer is within Europe, and another rate for anywhere else in the world.

const tier1 = ['GB'];
const tier2 = ['AL', 'AD', 'AM', 'AT', 'BY', 'BE', 'BA', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FO', 'FI', 'FR', 'GE', 'GI', 'GR', 'HU', 'HR', 'IE', 'IS', 'IT', 'LT', 'LU', 'LV', 'MC', 'MK', 'MT', 'NO', 'NL', 'PL', 'PT', 'RO', 'RU', 'SE', 'SI', 'SK', 'SM', 'TR', 'UA', 'VA'];
const country = cart['_embedded']['fx:shipment']['country'];
 
if (tier1.indexOf(country) > -1) {
  // United Kingdom
  rates.add(10001, 10, 'FoxyPost', 'Standard');
} else if (tier2.indexOf(country) > -1) {
  // Europe
  rates.add(10002, 20, 'FoxyPost', 'International');
} else {
  // Rest of world
  rates.add(10003, 30, 'FoxyPost', 'International');
}

Returning a flat rate for international orders

This example is a simple approach for only showing a flat rate option for international orders.

const country = cart['_embedded']['fx:shipment']['country'];
 
if (country != "US") {
  // The customer is shipping outside the US
  rates.hide();
  rates.add(10001, 25, 'FoxyPost', 'Standard');
}

Restricting shipping to only a specific set of postcodes

This example can be used to prevent customers from being able to complete the checkout if they don't have a shipping address within a specific set of postcodes. The allowed_postcodes array can be updated to include as many postcodes as needed. If a customer has a shipping address that doesn't have a matching postcode, they will instead see an error.

const allowed_postcodes = [55115, 55116];
const postal_code = Number(cart['_embedded']['fx:shipment']['postal_code']);
 
if (allowed_postcodes.includes(postal_code)) {
    rates.add(10001, 10, '', 'Standard Shipping');
} else {
    rates.error('Sorry, we are not currently able to ship to your location');
}

Example using non-numerical postcodes

Since some countries may use postcodes that include alphabetic characters, the example needs to be a little different in that case. Here is an example for postcodes in UK.

const allowed_postcodes = ['SE100AA', 'SE100AB'];
const postal_code = (cart['_embedded']['fx:shipment']['postal_code']).replace(/\s+/g, '').toUpperCase();
 
if (allowed_postcodes.includes(postal_code)) {
    rates.add(10001, 10, '', 'Standard Shipping');
} else {
    rates.error('Sorry, we are not currently able to ship to your location');
}

Hiding specific shipping rates depending on the shipping state

This option hides rates any express options if the shipping state is outside of the contiguous US

const contiguous = ['AL', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'FL', 'GA', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY', 'DC'];
const country = cart['_embedded']['fx:shipment']['country'];
const region = cart['_embedded']['fx:shipment']['region'];
 
if (country == "US" && contiguous.indexOf(region) == -1) {
	rates.filter('express').hide();
}

Adjust rates based on weight and item count

Provides three flat rates, increasing the costs if the weight exceeds 10, and removes the express option if there are more than 5 products.

rates.add(10001, 5, 'FoxyPost', 'Standard');
rates.add(10002, 9.45, 'FoxyPost', 'Priority');
rates.add(10003, 10, 'FoxyPost', 'Express (Next Day)');
 
if (cart['_embedded']['fx:shipment']['total_weight'] > 10) {
  rates.filter(10001).price(6);
  rates.filter(10002).price(10);
  rates.filter(10003).price(11.99);
}
 
if (cart['_embedded']['fx:shipment']['item_count'] > 5) {
  rates.filter(10003).hide();
}

Free shipping based on a coupon

If a specific coupon code (or a code that ends in a specific suffix) is present on the transaction, the following example hides all of the returned rates and instead adds a custom free shipping rate. NOTE that you may want additional logic here, as you may only want to allow free shipping for the contiguous US states, or only within your country, or excluding specific products.

This example would allow free shipping if a coupon with a code of freeshipping was present, or one that had a suffix of -fs, like mycode-fs or h3Sif4yg-fs

If you don't want to give a discount for the products and only free shipping, your coupon can be set to all the default settings with a coupon details string of 0-0.

for (let d in cart['_embedded']['fx:discounts']) {
  let code = cart['_embedded']['fx:discounts'][d]['code'];
  if (
    code == "freeshipping" // example of a specific code
    || code.match(/-fs$/) // example of a code suffix
  ) {
    rates.hide();
    rates.add(11000, 0, '', 'Free Shipping');
  }
}

Free shipping over a certain amount

This example provides free shipping if the subtotal for the order (total item price less any coupons) is greater than $50.

const total_item_price = cart['_embedded']['fx:shipment']['total_item_price'];
 
let discounts = 0;
for (let d in cart['_embedded']['fx:discounts']) {
    discounts += cart['_embedded']['fx:discounts'][d]['amount'];
}
 
rates.add(10001, 8, '', 'Standard Shipping');
 
if (total_item_price + discounts > 50) {
    rates.hide();
    rates.add(10002, 0, '', 'Free Shipping');
}

Modifying Rates by Customer Attributes

If you use attributes on your customers, such as a Loyalty_Level or Wholesaler_Status, you can key off that to trigger custom rates. The below code is just a partial bit to show how to access customer attributes, which you could combine with other examples on this page.

let loyaltyLevel = false;
 
if (
  cart._embedded["fx:customer"] &&
  cart._embedded["fx:customer"].id &&
  cart._embedded["fx:customer"].id != 0 &&
  cart._embedded["fx:customer"]._embedded &&
  cart._embedded["fx:customer"]._embedded["fx:attributes"]
) {
  for (
    let i = 0;
    i < cart._embedded["fx:customer"]._embedded["fx:attributes"].length;
    i++
  ) {
    const attr = cart._embedded["fx:customer"]._embedded["fx:attributes"][i];
    if (attr.name === "Loyalty_Level") {
      loyaltyLevel = attr.value;
    }
  }
}
 
if (loyaltyLevel && loyaltyLevel.match(/silver/i)) {
  rates.hide().add(10003, 0, "", "Free Shipping for Silver Level");
}

Restricting available rates based on product categories

If the customer has ordered products from the 'food' category, this example will only show overnight delivery options. If the customer has ordered 3 or more food items, update the priority overnight option to be free.

let food = 0;
for (let p in cart['_embedded']['fx:items']) {
  let item = cart['_embedded']['fx:items'][p];
  switch (item['_embedded']['fx:item_category']['code']) {
    case "food":
      food += item['quantity'];
      break;
  }
}
 
if (food > 0) {
  // Hide all shipping options, but show overnight rates
  rates.hide().filter("overnight").show();
 
  // If 3 or more food items, update priority overnight to be free
  if (food >= 3) rates.filter('priority overnight').price(0);
}

Restricting available rates based on custom fields

This example is similar to the above, but checks the value of a custom field from the session/checkout instead of the category. Review the "Passing custom fields to the custom shipping endpoint" section on the shipping page for details on how to pass custom values from the cart/checkout to your shipping code.

let my_field = '';
for (let c in cart['_embedded']['fx:custom_fields']) {
    let custom_field = cart['_embedded']['fx:custom_fields'][c];
    if (custom_field.name == 'my_field') {
        my_field = custom_field.value.toLowerCase();
    }
}
 
rates.add(10001, 8.99, '', 'Standard Shipping');
rates.add(10002, 15.50, '', 'Express Shipping');
 
if (my_field == 'my_value') {
    rates.hide();
    rates.filter(10002).price("+3.25").show();
}

Restricting to a single carrier for a specific country

This example, will only provide FedEx rates to Canada, but allow any other configured live rate carriers for any other countries

if (cart['_embedded']['fx:shipment']['country'] == "CA") {
	rates.hide().filter("fedex").show();
}

Calculating shipping fees from custom product options

This approach allows for shipping fees to be set per product via a custom product option called shipping. It relies on your product add to carts including an attribute like &shipping=12.5.

If there are shipping rates already present (from the native live carrier integrations or the custom shipping endpoint), then the product level shipping fees are added onto the existing rates, otherwise it creates a new rate with a label of “Standard Shipping”.

If you want to hide the custom product option on the cart, you can do that from the “configuration” page of the Foxy administration, look for the option labelled “Customize Cart Display” and add the product option name you used (shipping in this example).

let product_shipping = 0;
for (let p in cart['_embedded']['fx:items']) {
    let item = cart['_embedded']['fx:items'][p];
    for (let o in item['_embedded']['fx:item_options']) {
        let item_option = item['_embedded']['fx:item_options'][o];
        if (item_option['name'] == "shipping" && !isNaN(parseFloat(item_option['value']))) {
            product_shipping += parseFloat(item_option['value']);
        }
    }
}
 
if (product_shipping > 0) {
    if (rates.exists()) {
        rates.price("+" + product_shipping);
    } else {
        rates.add(10000, product_shipping, '', 'Standard Shipping');
    }
}

Increase rates by a percentage

This example filters rates on USPS and adds a 15% increase to all USPS prices.

rates.filter('USPS').price('+15%');

Increase rates by an amount

This example filters rates on UPS and adds 4 units of the appropriate currency to all UPS prices.

rates.filter('UPS').price('+4');

Getting shipping rates from Third Party Providers

If you're needing to get rates from other providers that Foxy doesn't currently support, then using a third-party service like GoShippo or EasyPost can be a great option. We support fetching their rates through the custom shipping code feature, and you can see details on setting that up on the following pages:

Calculate flat rate shipping based on distance

For stores that want to calculate a flat rate shipping cost based on the distance, rather than using a live rate provider like USPS, FedEx or UPS, this approach can use the Google Maps API to calculate the distance between the store address and the customers.

Calculating flat rate shipping by distance

Restricting shipping to a specific map area

If you have a specific geographical area that you allow shipping to, you can use Google Maps with the custom shipping code to only show certain options if the customers shipping address is within that area.

Restrict shipping to a specific map area

Site Tools