Documentation
Important notice

This feature is currently in beta version. Integrate with this API only if recommended by Ingrid Support Team. 🚧


Reach out to Ingrid Support Team here to learn more about this feature.

Integration Guide

This part of the documentation will guide you through the steps of integrating your e-commerce site with Delivery Checkout. We'll go through patterns for embedding the shipping selector, handling updates to cart or customer information and marking the checkout session as completed in the Delivery Checkout API.

Creating a session

The first thing you need to do before you can embed the shipping selector in your checkout page is to create a session. This is done by calling session.create which returns session details and an HTML snippet.

Each Session has an ID that can be used to later fetch, update or complete the session. It is recommended that you store the session ID together with the customer's order.

Embed the Delivery Checkout

The responses of session.create and session.pull contain the attribute called html_snippet which includes the HTML snippet to display the shipping selector in your checkout page. Just put it in the part of the page where you want the Delivery Checkout to be displayed.

The snippet will then download and execute our bootstrap.js script which spawns the iframes and sets up channels of communication for session management.

const handleCreateSession = async () => {
try {
// Call your backend and they call siw/session.create
const sessionResponse = await createSession();
// Embed the widget on the page
widgetWrapperRef.current.innerHTML = HTMLSnippet;

// Trick the browser to run the script, explained below
replaceScriptNode(document.getElementById("shipwallet-container"));
} catch {
// We don't believe it's possible but if our service fails, you render the fallback for the widget here
}
};
Warning

If you're dynamically inserting the HTML snippet in the DOM, most browsers won't execute the script embedded in it. If you're lucky enough to have jQuery on your page, you can use its append() function or feel free to copy the snippet below. It removes and inserts back the script nodes to trick the browser to execute them

function replaceScriptNode(node) {
if (isScriptNode(node) && !isExternalScript(node)) {
node.parentNode.replaceChild(cloneScriptNode(node), node);
} else {
var i = 0,
children = node.childNodes;
while (i < children.length) {
replaceScriptNode(children[i++]);
}
}
return node;
}

function isScriptNode(node) {
return node.tagName === "SCRIPT";
}

function isExternalScript(node) {
return !!node.src && node.src !== "";
}

function cloneScriptNode(node) {
var script = document.createElement("script");
script.text = node.innerHTML;
for (var i = node.attributes.length - 1; i >= 0; i--) {
script.setAttribute(node.attributes[i].name, node.attributes[i].value);
}
return script;
}

replaceScriptNode(document.getElementById("shipwallet-container"));

Caveats

The HTML snippet is designed to only be added to the page once. So if you are using AJAX to load the snippet you should make sure that this only happens once. Adding or replacing it multiple times will cause errors.

Warning

html_snippet contains an element with unique id shipwallet-container. Make sure there’s no other element on the page with the same ID, otherwise the widget won't load.

You can override the default unique id with your own by requesting a merchant configuration change from our Customer Support.

Updating the cart and session

Checkout pages often allow the user to alter the contents of the cart, locale, customer information or anything else that the session is based on. Change in any of those may adjust the available shipping options or the visual apperance of the widget

Whenever session is updated Delivery Checkout will automatically recieve an update from SIW and reflect session changes in the UI

If you're looking for the old suspend/resume API, don't worry, it's still operative but you can start migrating to remove those calls and allow the Delivery Checkout to sync itself without your help 🔄

Handling abandoned carts

In some cases customers can drop out of the checkout process early. Either because they didn't have time to complete the purchase or they went on to continue shopping on the page or for some other reason. In cases like this, when the customer returns, we want them to continue where they left off. By storing the session ID, we can fetch an existing session instead of creating a new one.

Here's how it could be handled in React:

React.useEffect(() => {
initDeliveryCheckout();
}, []);

const initDeliveryCheckout = async () => {
const existingSessionID = window.localStorage.getItem("ingrid-session-id");
if (existingSessionID) {
const getSessionResponse = await getSession(existingSessionID);
handleIngridInitialize(getSessionResponse.data.session, getSessionResponse.data.html_snippet);
} else {
handleCreateSession();
}
};

const handleIngridInitialize = (session: SIWSessionDTO, HTMLSnippet: string) => {
// session.pull and session.create should be handled the same way. We've created this utility function
// to abstract adding the snippet to the page and setting up event listeners without doubling the code
widgetWrapperRef.current!.innerHTML = HTMLSnippet;
replaceScriptNode(document.getElementById("shipwallet-container"));

window._sw!((api) => {
api.on("data_changed", (data, meta) => {
if (meta.price_changed) {
setShippingPrice(data.price);
}
});
});
};

const handleCreateSession = async () => {
try {
const sessionResponse = await createSession();
handleIngridInitialize(sessionResponse.data.session, sessionResponse.data.html_snippet);

// You want to save the session ID somewhere, so when the user abandons the checkout
// you can later restore the session, instead of creating a new one
window.localStorage.setItem("ingrid-session-id", sessionResponse.data.session.id);
} catch {}
};
Note

If, for some reason, the session cannot be found when you try to call `session.update` or `session.pull` with a previous session ID we suggest that you handle this error in this case by creating an entirely new session.

Subscribing to user events

User can perform a number of actions that can affect your checkout, e.g. user changing the shipping option can cause a shipping price change. Luckily, the moment the widget attaches itself to the page, it sets up a Javascript API available via window._sw where you can set up listeners for events.

You would usually do this right after session.create or session.pull.

const handleCreateSession = async () => {
// ...call session create backend and add the snippet to the page

// Update the shipping price any time option is changed
window._sw(function (api) {
api.on("data_changed", function (data, meta) {
if (meta.price_changed) {
updateCart(data.price);
}
});
});
};

Available events

data_changed

Instead of exposing multiple 'change' events with multiple properties, Delivery Checkout exposes one 'change' event and allows you to decide what data you care about. The callback receives two arguments

data: {
delivery_type: 'delivery' | 'mailbox' | 'pickup' | 'instore';
price: number;
search_address: {
country: string;
postal_code: string;
address_lines?: string[];
city?: string;
region?: string;
};
shipping_method: string;
external_method_id?: string;
category_name: string;
pickup_location?: {
address: {
country: string;
postal_code: string;
address_lines: string[];
city: string;
coordinates: Coordinates;
};
name: string;
}
}

and

meta: {
delivery_type_changed: boolean;
external_method_id_changed: boolean;
price_changed: boolean;
search_address_changed: boolean;
shipping_method_changed: boolean;
initial_load: boolean;
category_name_changed: boolean;
pickup_location_changed: boolean;
}

meta.initial_load: true informs you that this is the moment where values are actually set, not updated, as this is the first render of the widget.

All that looks kind of complicated, how does one use it?

Say you want to know when the shipping price changes to update the total price on the cart

window._sw(function (api) {
api.on("data_changed", function (data, meta) {
if (meta.price_changed) {
updateCart(data.price);
}
});
});

If you want to also know when the shipping method changes

window._sw(function (api) {
api.on("data_changed", function (data, meta) {
if (meta.shipping_method_changed) {
sendToAnalyticsService(data.shipping_method);
}
if (meta.price_changed) {
updateCart(data.price);
}
});
});

You can have more if statements or setup multiple data_changed event listeners if that's your jam.

loaded

Emitted when the widget is fully loaded and interactive

window._sw(function (api) {
api.on("loaded", function () {
console.log("loaded");
});
});

no_shipping_options

Emitted when the address is provided but Ingrid can't find any shipping options for it. Note that this requires additional configuration on Ingrid side to be visible. To do that, you will have to contact our support team

window._sw(function (api) {
api.on("no_shipping_options", function () {
console.log("no shipping options");
});
});

Completing session

You should complete the session after receiving confirmation from payment system. It marks the session as complete and creates a Transport Order in the Ingrid system.

Tip

When issuing the complete session request, include the external order ID if possible. You can find more information about external order ID in the subsection using external order identifier.

Using external order identifier

It is highly encouraged to pass external order identifier (external ID for short) in your session create/update/complete requests. Passing it in one of those requests will store the external ID along with the other session data.

Note

Benefits of using external ID are:

  • being able to search by your external order ID in Ingrid Merchant Platform transport orders,
  • when booking with Ingrid, your external order ID will be propagated to the booking system,
  • being able to register and load the tracking widget using the external order ID provided by the merchant.

You can pass the external_id field as part of your session create/update/complete request. The following snippet shows how you can pass it in the request via curl.

curl --location --request POST 'https://api.ingrid.com/v1/delivery_checkout/session.create' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer your-site-token' \
--data-raw '{
"purchase_country": "SE",
"purchase_currency": "SEK",
"locales": [
"sv-SE"
],
"search_address": {
"postal_code": "11239",
"country": "SE"
},
"cart": {
"cart_id": "unique_id",
"total_value": 129900,
"currency": "SEK",
"items": [
{
"sku": "SKU12345",
"name": "Saucony Shadow 6000",
"quantity": 1,
"price": 1000
}
]
},
"external_id": "your-order-id"
}'

Last updated: Thu, Jun 30, 06:15 AM