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

Shipping

A Shipping Haiku

Packages… point a,
point b, beyond? My mind drifts
to the endless pain.

Prior to launching FoxyCart we had months of discussions about shipping. One of the scenarios that we discussed was a mattress store. The mattress store may need to ship very large and heavy items, but also may need to ship pillows. They may also need to ship things via custom shipping carriers or freight options. In our early discussions, the afore-attributed anonymous lead developer said something like, “That's a ridiculous scenario. The average merchant won't have anywhere near the complexity in their shipping requirements.” The other person agreed, and FoxyCart development moved on.

Unfortunately, while the specifics of the mattress seller haven't come up, shipping requirements can vary so wildly from business to business that no one system can meet every need. FoxyCart's current shipping functionality does some things very well, while other things may require intermediate or advanced customizations to get right. Read on to see both how FoxyCart's shipping capabilities can meet your needs currently, as well as our plans for the future.

Different Types of Shipping Functionality

FoxyCart currently offers a few different approaches to shipping, listed here and discussed in more detail below:

  • Live rates through USPS, UPS, and FedEx.
  • Flat rates per category.
  • Special rates for “Free Ground Delivery” and “Customer Pickup”.
  • Handling fees can be applied per category in a variety of ways
  • Non-shippable / intangible / downloadable products, which don't get shipping applied, but are listed here just to clarify.

Shipping and Categories

Because a single store may have multiple categories, each with its own shipping options and handling fees, it's important to understand how combining different shipping rates works. Assume you have 4 product categories: two of them may be set to use live rates, one set to no shipping, and one set to flat rates. If the customer has products from all four categories in one cart, the checkout will present the customer with all of the returned live rates, each combined with all of the additional flat fees and handling fees for the other categories.

So, regardless how many categories or options are selected, all the shipping fees will be combined when presented to the customer.

Live Shipping Rates

Live shipping rates are configured store-wide, but can be enabled per category. As of this FoxyCart version, live rates are requested from the selected carriers using the options selected in your store's config. The total weight of your products are sent to the shipping carrier(s) to get the rates, but the dimensions of the shipment are not sent, and default to a 1'x1'x1' package unless the package type supercedes that default.

FoxyCart also currently does not split products into multiple shipments, so in some situations with extremely heavy carts the shipping carrier may respond back with an error.

We are planning a rebuild of our shipping functionality for a future version, so please add a comment with your needs and vote for this feature request.

Residential v. Commercial Rate Requests

Some carriers offer different prices or services to residential addreses than they do to commercial addresses. You can select from three options in your store's “shipping” page in the admin:

  1. Rate as Residential. (Force residential rate requests.)
  2. Rate as Commercial. (Force as commercial rate requests.)
  3. Rate based on Company field. If you select this option the address will be rated as a commercial address if the company field (on the checkout form) for that address is not blank, otherwise it will be rated as a residential address.

Along with the above setting in the administration, you can also override it per transaction using the h:shipto_residential custom session boolean attribute. You can set it as part of a normal add to cart or a JSONP request as either true or false depending on if the transaction is a residential shipment or not.

Wholesale v. Retail Rates

Some carriers display different rates based on the drop type you select. For example, UPS defaults to Wholesale if you select “Daily Pickup”, uses Retail for “Customer Counter” and “Suggested Retail Rates” and uses what it calls Occasional Rates for “One Time Pickup”, “On Call Air”, “Letter Center” and “Air Service Center”. Some systems will always show a discount when you are logged in to the API or to their website (as opposed to getting a rate request from their website without logging in). For this and other reasons, we recommend you use your own shipping account to ensure the rates more accurately reflect your actual shipping costs.

Special Rate Notes

FedEx

  • FedEx Ground is only available for commercial/business shipments or for deliveries over 70 pounds, where as FedEx Home Delivery is it's residential counterpart for packages up to 70 pounds. If you have the shipping rates set to 'commercial' (at the top of the shipping page of your stores admin) or to 'based on the company field' and the customer has entered a company name, then FedEx Ground will appear. If you've set it to be 'residential' or the customer didn't enter a company name, then FedEx Home Delivery will appear.

UPS

  • UPS Standard in the US is only available for shipments that originate in the US and have a destination in either Canada or Mexico. Full details. UPS Ground might be what you're after.
    • UPS Ground won't be returned if you've selected an express container type.

USPS

  • USPS Retail Ground (formerly Standard Post) is generally only available for shipment zones 5-9 within the US. Full details. If you're looking for a full domestic rate, look at the First Class or Priority Mail rates.

Flat Rate Shipping

Flat rate shipping, unlike the live rates above, is configured per category and not store-wide. The flat rate value is applied once per category, regardless of the number of products in the category. (See the handling fees section for other options.)

"Custom" Options

There are two “special” options that are treated as live rates:

  • Free Ground Shipping
  • Free Customer Pickup

Both options will only be shown to customers that have a shipping country matching your store's country, as configured in your store's settings. Also, the text displayed for both options is configurable, so you could use these as other types of “free” shipping entirely.

To enable these options, navigate to your store's “Shipping” page, and towards the bottom enable the “use custom” option and select the rates you'd like to use.

Custom Shipping Endpoint

To allow you to provide completely custom shipping options, a custom endpoint option is available to you. This sends the shipping request from the cart and checkout to an endpoint script of your choosing. There you can calculate custom rates or query a subsequent request on to different carriers before returning the custom rates to the customer.

To enable this option, navigate to the “Shipping” section of your store's FoxyCart administration, at the bottom of the page enable the “use custom endpoint” option and provide a URL to your endpoint. It also relies on a category with a delivery type of “Shipped using live shipping rates”

As the custom shipping endpoint relies on setting categories to use live shipping rate delivery, it will also require that you enter a default weight for products in that category, and will also display the products weight in the cart. To stop the weight displaying in the cart, enable the “Customise cart display” option on the configuration page in the administration, de-select the “Show product weight” option and save the changes to the page.

Helper Libraries

We have some helper shipping endpoint libraries which will take care of some of the heavy lifting for you - so you can just focus on creating your custom shipping logic. We currently have the following languages, and will expand on them in the future as we're able. Refer to the README for each language for how to use them.

Handling the request

When the customer enters their shipping details, a POST request is sent off to your custom shipping endpoint with a JSON payload representing the current cart. It follows the same structure as our Hypermedia API, as the majority of the data comes from there.

The following is an example payload, showing a cart with two products (one live rate and one flat rate) along with a coupon:

{
    "_links": {
    },
    "_embedded": {
        "fx:items": [
            {
                "_links": {
                },
                "_embedded": {
                    "fx:item_category": {
                        "_links": {
                        },
                        "admin_email_template_uri": "",
                        "customer_email_template_uri": "",
                        "code": "flat",
                        "name": "Flat Fee",
                        "item_delivery_type": "flat_rate",
                        "max_downloads_per_customer": 3,
                        "max_downloads_time_period": 24,
                        "default_weight": 0,
                        "default_weight_unit": "LBS",
                        "default_length_unit": "IN",
                        "shipping_flat_rate_type": "per_order",
                        "shipping_flat_rate": 7,
                        "handling_fee_type": "none",
                        "handling_fee": 1,
                        "handling_fee_minimum": 0,
                        "handling_fee_percentage": 80,
                        "customs_value": 0,
                        "discount_type": "",
                        "discount_name": "Repeat",
                        "discount_details": "allunits|250-90",
                        "send_customer_email": "",
                        "send_admin_email": "",
                        "admin_email": "",
                        "date_created": "",
                        "date_modified": ""
                    }
                },
                "item_category_uri": "https://api.foxycart.com/item_categories/79",
                "name": "Flat Rate Product",
                "price": 9.5,
                "quantity": 1,
                "quantity_min": 0,
                "quantity_max": 0,
                "weight": 0,
                "code": "my-flat-product",
                "parent_code": "",
                "discount_name": "",
                "discount_type": "",
                "discount_details": "",
                "subscription_frequency": "",
                "subscription_start_date": "",
                "subscription_next_transaction_date": "",
                "subscription_end_date": "",
                "is_future_line_item": "",
                "shipto": "Me",
                "url": "",
                "image": "",
                "length": 0,
                "width": 0,
                "height": 0,
                "expires": 0,
                "date_created": "",
                "date_modified": "2016-07-03T02:31:37-0700"
            },
            {
                "_links": {
                },
                "_embedded": {
                    "fx:item_options": [
                        {
                            "_links": {
                            },
                            "name": "color",
                            "value": "red",
                            "price_mod": 0,
                            "weight_mod": 0,
                            "date_created": "",
                            "date_modified": ""
                        }
                    ],
                    "fx:item_category": {
                        "_links": {
                        },                                            
                        "admin_email_template_uri": "",
                        "customer_email_template_uri": "",
                        "code": "live",
                        "name": "Live Rates",
                        "item_delivery_type": "shipped",
                        "max_downloads_per_customer": 3,
                        "max_downloads_time_period": 24,
                        "default_weight": 1,
                        "default_weight_unit": "LBS",
                        "default_length_unit": "IN",
                        "shipping_flat_rate_type": "per_order",
                        "shipping_flat_rate": 0,
                        "handling_fee_type": "flat_per_order",
                        "handling_fee": 5,
                        "handling_fee_minimum": 0,
                        "handling_fee_percentage": 10,
                        "customs_value": 0,
                        "discount_type": "quantity_percentage",
                        "discount_name": "Repeat",
                        "discount_details": "repeat|2-10",
                        "send_customer_email": "",
                        "send_admin_email": "",
                        "admin_email": "",
                        "date_created": "",
                        "date_modified": ""
                    }
                },
                "item_category_uri": "https://api.foxycart.com/item_categories/83",
                "name": "Test Product",
                "price": 25.99,
                "quantity": 1,
                "quantity_min": 0,
                "quantity_max": 0,
                "weight": 1,
                "code": "",
                "parent_code": "",
                "discount_name": "",
                "discount_type": "",
                "discount_details": "",
                "subscription_frequency": "",
                "subscription_start_date": "",
                "subscription_next_transaction_date": "",
                "subscription_end_date": "",
                "is_future_line_item": "",
                "shipto": "Me",
                "url": "",
                "image": "",
                "length": 0,
                "width": 0,
                "height": 0,
                "expires": 0,
                "date_created": "",
                "date_modified": "2016-07-03T02:30:52-0700"
            }
        ],
        "fx:shipment": {
            "address_name": "",
            "first_name": "",
            "last_name": "",
            "company": "",
            "address1": "",
            "address2": "",
            "city": "SAINT PAUL",
            "region": "MN",
            "postal_code": 55116,
            "country": "US",
            "origin_region": "MN",
            "origin_postal_code": 55116,
            "origin_country": "US",
            "shipping_service_id": 0,
            "shipping_service_description": "",
            "is_residential": true,
            "item_count": 2,
            "total_weight": 1,
            "total_customs_value": 0,
            "total_handling_fee": 5,
            "total_flat_rate_shipping": 7,
            "total_item_price": 35.49,
            "total_tax": 4.57,
            "total_shipping": 12,
            "total_price": 52.06
        },
        "fx:discounts": [
            {
                "code": "SUMMER",
                "amount": -1.77,
                "name": "Summer Special",
                "display": -1.77,
                "is_taxable": "",
                "is_future_discount": ""
            }
        ],
        "fx:custom_fields": [
            {
                "name": "sign_on_delivery",
                "value": "Yes",
                "is_hidden": 0
            }
        ]
    },
    "customer_uri": "",
    "locale_code": "en_US",
    "total_item_price": 35.49,
    "total_tax": 4.57,
    "total_shipping": 12,
    "total_future_shipping": 0,
    "total_order": 50.29,
    "date_created": "",
    "date_modified": "2016-07-02T15:03:46-0700"
}

Notes:

  • The items included in the payload will only be those for the shipping address being requested. This means if your store is using multiship and the customer enters a shipping address for a shipto, only the products for that shipto will be included in the payload.
  • The payload includes several _links arrays. These contain helpful URI's that could be used through the Hypermedia API if you're also making use of that. If not, these can be safely ignored.

Sending a response

In response, FoxyCart expects a JSON payload in the following format to be output on the page (prettified for display purposes):

Success
{
    "ok": true,
    "data": {
        "shipping_results": [
            {
                "service_id": 10001,
                "price": 10.55,
                "method": "FoxyPost",
                "service_name": "Standard",
            },
            {
                "service_id": 10002,
                "price": 20.99,
                "method": "FoxyPost",
                "service_name": "Express",
            }
        ]
   	}
}

Note: service_id from a custom shipping endpoint needs to be 10000 or higher, and should be unique per rate.

Error
{
    "ok": false,
    "details": "Sorry, we're unable to ship to that region"
}

Only a valid JSON object should be output to your custom endpoint. If you output other elements to the page that is not part of a valid JSON object, the rates will fail to load and a generic error will be displayed.

Passing custom fields to the custom shipping endpoint

By default - any custom session attributes currently in the customers session will be included in the payload within the fx:custom_fields node. This includes anything added to the cart session using h: prepended before the attribute in an add to cart.

Sometimes though you need to capture information from a customer on the checkout related to the shipping, such as whether a signature is required on delivery, which affects the rates calculated on your custom endpoint. To do that, you can add custom checkout fields to your store, and add a data-fc-shipping-custom-field data attribute to the form elements. Whenever any fields with that data attribute are changed, it will trigger the shipping rates to the refreshed, including those values in the payload sent to your endpoint.

<!-- Checkbox -->
<input type="checkbox" id="sign_on_delivery" name="sign_on_delivery" value="Yes" {% if sign_on_delivery == "Yes" %}checked{% endif %} data-fc-shipping-custom-field>
<label for="sign_on_delivery">Sign on Delivery?</label>
 
<!-- Radio inputs -->
<input type="radio" id="lift_required_yes" name="lift_required" value="Yes" {% if lift_required == "Yes" %}checked{% endif %} data-fc-shipping-custom-field>
<label for="lift_required_yes">Include a lift</label><br/>
<input type="radio" id="lift_required_no" name="lift_required" value="No" {% if lift_required == "No" %}checked{% endif %} data-fc-shipping-custom-field> <label for="lift_required_no">Don't include a lift</label>
 
<!-- Text input -->
<input type="text" id="message" name="message" value="{{ message }}" data-fc-shipping-custom-field>
 
<!-- Select options -->
<select name="pickup_location" data-fc-shipping-custom-field>
  <option value=""></option>
  <option value="factory">Factory</option>
  <option value="store">Store</option>
</select>

If a value is already present in the cart session, and a custom checkout field shares the same name - the custom checkout field will overwrite the existing custom field in the session when sent to the custom shipping endpoint.

If needed, you can also pass custom fields to the shipping endpoint using javascript and our events, specifically cart-shipping-options-update and checkout-shipping-options-update. Updates made by these events will overwrite any existing attributes already in the session, but will be overwritten by any matching custom checkout fields.

FC.client.on("checkout-shipping-options-update", function(params) {
	params.custom_fields = { my_custom_attr: true };
});

Common Errors

“Error: This store has not been setup correctly to calculate shipping to this location with this weight.”

If you receive this error in your store, that means that either no rates were returned from your custom endpoint, or it didn't return a valid JSON object.

  1. Firstly ensure that your endpoint is set to return at least one rate or an error for all requests.
  2. If you are - ensure that only the JSON output is printed on the page. You can't have any other text on the page.

Handling Fees

Handling fees, like flat rates, are configured per category, but can be applied in a few different ways:

  1. Flat fee per shipment with products in this category
  2. Flat fee per product in this category
  3. Flat fee per shipment + % of price for products in this category
  4. Flat fee per shipment OR % of order total with products in this category. Whichever is greater.

When paired with a flat fee, the handling fee can become quite a powerful tool for adjusting shipping costs. Handling fees also work with live rates, but note that handing fees do not apply if a category is set to “no shipping”.

Fulfillment Houses

FoxyCart stores can integrate with a variety of fulfillment houses using functionality like the API and the transaction datafeed. Or check our integrations section for existing code or services (like Order Desk, a third-party bridge between FoxyCart and Shipwire.com).

Feature Requests, Plans, and Known Limitations

There are many common requests that can be achieved with FoxyCart, but require a little bit of code to make work. Please view this list of links, and ask in our forum if you don't see what you're looking for (or if you need help).

We are planning a rebuild of our shipping functionality for a future version, so please add a comment with your needs so we can be sure our new solution handles your situation.

Specific Workarounds

Site Tools