Stidner Shipping Widget is a handy UI-wrapper for Stidner Shipping API, that takes care of shipping stage in e-commerce checkout.
Widget is developed very raw in its core, so that it can support almost all types of checkout: it can communicate with different Payment Widgets (for example Payson, Klarna) and it also can be used "standalone", when no payment stage is needed.
The general widget lifecycle consists of the following stages:
Shipping Widget is an UI-wrapper of Order object, so you need to create on Order on get Widget instance.
When customer is navigated the the checkout page of your store (having Items added to cart), you use POST /order endpoint to create Order object based on given items - in response you'll get Order object.
After that you'll need to store uuid of Order somewhere in your system, bound to current checkout. The place of storage is up to you, but we recommend Cookies storage with the lifespan depending on the lifespan of your checkout. More complex e-commerce systems (like WooCommerce) store Order uuid inside own storage, less complex use Cookies.
If user updates cart, you need to update Items list in Order performing POST /order/{order_uuid} request, providing uuid of Order stored as explained above.
It's important to update existing Order, not create new one, on cart update - this way is more consistent and doesn't lead to creation 'blank' Orders on Stidner side. We monitor 'blank' Orders from each integration and if see that cart updates result in new Order creation - we contact the developer and advise yo optimise his integration.
Order object has two attributes, that concern Widget
You need to just place widget_embed contents on the checkout page. widget_embed contains not only iFrame with Widget, but it also connects Javascript files with helper functions for better Widget performance (for example, adjusting height of the Widget to its contents). Due to iFrame -> parent relations, some of the code should be located outside of the iFrame.
When customer performs actions inside the Widget (changes Shipping Option, selects Service Point, provides Address) - some of them are notified using Javascript-callbacks to parent window.
Depending on specific integration, you may want to catch these callbacks and react to them.
Because Widget is rarely used standalone, it doesn't have any means to finalize Order from inside. We also don't offer any Javascript-handler for this purpose due to security reasons (so no hacker can use browser console to force-finalize Order).
What you need to do is perform PUT /order/{order_uuid} request, setting attribute order_status=completed. After that (if needed) finalize checkout in your e-commerce platform and either redirect customer to success page, or reload checkout page - depending on the architecture, you are using.
On that result page display Widget again. Once Order enters the completed stage, its widget will have the confirmation message displayed instead of Shipping Options list.
On specific actions Widget iFrame sends to parent window Javascript callbacks and expects you to catch them and react, if needed.
order_updated
This event is sent every time, when Order is updated inside widget (for example, customer changed ShippingOption). It also has the changes array attached to the event, that shows, what essential changes happened to Order on current update.
Attribute | Description |
---|---|
shipping_price | Is included in 'changes' array if as a result of Order update shipping price was changed. Monitoring this attribute may be useful if Stidner Shipping Widget is operating in pair with Payment widget, and we need to update shipping price in correspondent Payment order. |
recipient_address | Is included in 'changes' array if as a result of Order update recipient address was changed. |
window.addEventListener(
"message",
function (messageEvent) {
var event_contents = null;
if (__event_is_object(messageEvent.data)) {
event_contents = messageEvent.data;
} else if (__event_is_json(messageEvent.data)) {
event_contents = $.parseJSON(messageEvent.data);
}
if (event_contents === null
|| event_contents.event_type === undefined
|| event_contents.event_type !== '__stidner_event') {
return;
}
//do something depending on 'event_contents.action' here
},
true
);
__event_is_object = function(data){
return typeof data === 'object';
};
__event_is_json = function(data) {
var isJson = false;
try {
var json = $.parseJSON(data);
isJson = typeof json === 'object' ;
} catch (ex) {
}
return isJson;
};
Widget doesn't handle payments, it also is rarely used standalone (because shipping + payment go together in 100% of the cases). But it is designed that way to be able to be easily integrated with almost every popular payment solution.
The core of communication between Widget and Payment System is fast and correct data exchange between two systems in the following cases:
The total sum to pay in Payment System is composed of two parts:
When Order is created - the correspondent Payment Order should be created with the specified shipping price. When shipping option, service point or product is changed (and that leads to shipping price changing) - price should be updated in correspondent Payment Order, and Payment Widget should be refreshed.
When customer provides his address in Widget, it needs to be instantly updated in Payment Order. After update Payment Widget should be refreshed.
This is necessary to reduce customer actions during the checkout - if he has already provided some data, checkout should not demand it again.
The same as the previous, only in the opposite direction. When customer provides his address in Payment Widget, it should be instantly updated in Widget (and Widget should be refreshed).
keeping customer address in Widget up to date when it's changed in Payment System
1 and 2 are handled by Javascript callbacks (see above) of Shipping Widget. 3 should be handled on Payment solution side (via the similar callbacks), and all major Payment solutions send these callbacks.
Below we will describe the full example of integration for the following case:
Below scheme shows the visualization of integration process.
Customer surfs the e-commerce store, adds desired items to cart. Adding to cart does not differ from standard checkout of any platform and needs no modifying.
Pay attention to storing items in e-commerce - they should be fully described in order to use Stidner Shipping functionality to a full scale. We recommend you to always specify the following attributes of item:
To register ShippingOrder perform POST /order request to Stidner API:
$request = new HttpRequest();
$request->setUrl('https://gateway.test.stidner.com/api/v1/order');
$request->setMethod(HTTP_METH_POST);
$request->setHeaders(array(
'authorization' => 'Basic MjAwOnN0aWRuZXJfc2FuZGJveF8zMGQ2NWI5Ni0zMWI4LTRmOTQtYmU4NC02YjE2ZDI1YjFiZDM=',
'content-type' => 'application/json'
));
$request->setBody('{
"async": true,
"source": "__stidner_widget",
"external_reference": "434",
"payment_system": "payson",
"currency": "SEK",
"items": [
{
"article_number": "sku_50",
"name": "X-box 2009",
"description": "A gaming pod",
"quantity": 2,
"unit_price": 2000,
"weight": 300
}
],
"addresses": [
{
"type": "recipient",
"customer_type": "person",
"name": null,
"country_code": "SE",
"postal_code": "46157",
"city": null,
"address_line": null,
"contact_name": null,
"contact_phone": null,
"contact_email": null
}
],
"test_mode": true,
"notification_url": "https://wordpress-site.loc/notify/434"
}');
$response = $request->send();
and get ShippingOrder object from response data attribute.
$order = $response->data
After that take uuid of Order and save it into Cookies to bind Order to current Checkout
setcookie('stidner_order_uuid', $order->uuid)
Perform POST /Checkouts request to Payson, in request provide shipping_price of Shipping Order
$request = new HttpRequest();
$request->setUrl('https://api.payson.se/2.0/Checkouts');
$request->setMethod(HTTP_METH_POST);
$request->setHeaders(array(
'authorization' => 'Basic 1234:kTIACC8ITL5WYDpTjnjRRtsn9AuIBEhUKxaV4TOuZRs=',
'content-type' => 'application/json'
));
$request->setBody('{
"order": {
"currency": "SEK",
"items": [
{
"name": "X-box 2009",
"quantity": 2,
"taxRate": 0.2,
"unitPrice": 2000
},
{
"name": "Stidner Shipping",
"quantity": 1,
"taxRate": 0.25,
"unitPrice": 5600
}
]
},
"merchant": {
"checkoutUri": "http://www.adress.xyz/Checkout?id=123",
"confirmationUri": "http://www.adress.xyz/Confirmation?id=123",
"notificationUri": "http://www.addres.xyz/Notification?id=123",
"termsUri": "http://www.addres.xyz/Terms"
}
}');
$response = $request->send();
and get Payment Order object from response data attribute
$payson_order = $response->data
Perform PUT /order/{order_uuid} request to Stidner and update ShippingOrder with payment_reference. It is important to connect the orders because if any non-standard behaviour happens during the checkout process, that will lead to page reload, you will need to get both ShippingOrder and PaymentOrder by their identifiers. Since ShippingOrder is master in this communication, you need to make sure, it has the connection to slave PaymentOrder.
$order->payment_reference = $payson_order->id;
/**
* Perform order update request to Stidner API here
**/
Append the code of both widgets to the checkout page while rendering it.
<!-- Stidner Widget -->
<script
src="https://widget.test.stidner.com/js/integration.js?v=94"
type="text/javascript">
</script>
<iframe
src="https://widget.test.stidner.com/order/429b6e40-affc-11e7-a644-39ac3e691588"
id="__stidner_shipping_iframe"
width="375"
frameborder="0">
</iframe>
<!-- Payson Widget -->
<iframe
id="checkoutIframe"
name="checkoutIframe"
src="http://embedded.payson.se/checkout?id=264d3bfd-f7a1-4122-8556-a56700f71eaa"
style="width:100%; height: 100%"
frameborder="0"
scrolling="no">
</iframe>
When customer performs any notable action inside ShippingWidget or PaymentWidget, they send the javascript event to parent page of the iframe (in other words - to Integration). Integration should react correspondently to these events, as described below.
Listen to order_updated event from ShippingWidget, paying attention to the changes array in it. If any of the below values is present in that array, you should react as described:
window.addEventListener(
"message",
function (messageEvent) {
if (messageEvent.data.action === 'order_updated') {
/**
* Transform contents of 'changes' array into integration action.
*/
var integration_action = null;
if (messageEvent.data.attributes.changes.indexOf('shipping_price') !== -1) {
integration_action = 'shipping_option_updated';
}else if(messageEvent.data.attributes.changes.indexOf('recipient_address') !== -1){
integration_action = 'stidner_widget_address_updated';
}
if(action !== null){
/**
* Lock Payson iframe to prevent from customer action there while updating.
* How to lock Payson iframe read at tech.payson.se
*/
__payson_iframe_lock();
/**
* Make ajax request to backend to perform correspondent action
*/
$.get(stidner.ajax_url, {action: integration_action})
.done(function (data) {
/**
* Unlock Payson iframe when request is finished
*/
__payson_iframe_unlock();
})
.fail(function (data) {
__payson_iframe_unlock();
});
}
}
},
true
);
Listen to PaysonEmbeddedAddressChanged event from PaysonWidget. When it is fired it means customer provided (or changed) his address at PaysonWidget - so you need to update the StidnerWidget with the same address.
window.addEventListener("PaysonEmbeddedAddressChanged", function (evt) {
/**
* Lock Stidner Widget to prevent customer from performing action there while update
*/
__stidner_shipping_iframe_lock();
/**
* Make ajax request to backend to perform correspondent action
*/
$.post(stidner.ajax_url, {action: 'payson_address_updated'})
.done(function () {
/**
* Unlock Stidner Widget after successful update
*/
__stidner_shipping_iframe_unlock();
})
.fail(function (data) {
__stidner_shipping_iframe_unlock();
});
});
Customer performs payment process in PaysonWidget. After PaysonOrder is successfully paid, it is automatically finalized on Payson side. After finalization Payson sends HTTP-notification to Integration - it should react with the following:
$order->order_status = 'completed';
/**
* Perform order update request to Stidner API here
**/
On this stage PaysonOrder is paid, ShippingOrder is completed - you need to display customer the confirmation of both orders. Both widgets are designed in the way, that when their orders enter the final stage, they automatically display the confirmation information of the Order instead of the checkout form.
So what you need to do is automatically redirect customer to confirmation page, where display both widgets (as you already displayed them above at step 5). Depending on how your e-commerce store is designed, you may reload the widgets using javascript - they will display confirmation after the reload.