Imagine a shopping experience where you can share a cart across multiple websites on different domains. You navigate a website, find a product you like, and add it to your cart. Then you visit another website, find another product, and add it to the same shopping cart. When ready to checkout, you pay for your order and receive multiple parcels, one from each store you shopped from.
This experience would be like a marketplace, but on distinct domains. Think of large groups who grow by buying new companies. When they acquire an entity, they also acquire their ecommerce stack. One of their biggest challenges is managing many different technologies. One brand might be running on Magento. Another one on Shopify, WooCommerce, or Salesforce Commerce Cloud. Each with different payment gateways and order management workflows.
Wouldn't it be great if such an organization could consolidate the shopping experience across multiple domains and manage orders across them all? To take it a step further, what if those domains belonged to totally different organizations? Each organization could easily add ecommerce to their website, social channel, or app that is not shoppable, and offer an "Add to cart" button that is shared with other merchants on the planet.
Thanks to its stateless nature, such an experience would be like a universal shopping cart or wishlist. Users would have a list of active shopping carts and order history linked to their identities. Potentially, an AI-powered personal shopper could shop around based on their preferences. All of this might become a reality one day. Or maybe not. The good news is that the technology for implementing a cross-site shopping cart is already here, and it's quite straightforward.
Implementing a cross-site cart
If you want to implement a shared shopping cart across multiple sales channels, you will need a shopping cart API, as described in my article about stateless carts. Stateless carts enable any user or channel to access and manipulate the same cart regardless of user session, by knowing the cart ID (and authentication token). Therefore, it is only necessary to share a cart ID across multiple origins in order to share the cart.
Sharing a storage across multiple subdomains is easy, since cookies can be used for all subdomains (such as *.example.com). Rather, you cannot use the browser's local storage, since it is limited to each subdomain.
Sharing a storage across TLDs is another story. It's not possible to use cookies, so you need a cross-domain storage mechanism that acts as a shared session. The idea is to implement cross-origin storage with a web application that just needs to access the browser's local storage and manage HTTP headers. All websites that need to share storage (i.e. the cart) would just embed this application as an iframe.
Each website would use the window.postMessage() method to post messages to the iframe application. A message could ask the iframe application to store a cart ID and another message could retrieve the cart ID. The iframe application would listen to these messages and store/retrieve the cart ID from its local storage, becoming a shared storage for all websites.
The following code implements the logic for the iframe app:
<!-- https://cross-origin-storage.app -->
<!DOCTYPE html>
<html>
<head>
<script>
window.addEventListener('message', (event) => {
switch (event.data.action) {
case 'setItem':
localStorage.setItem(event.data.key, event.data.value)
break;
case 'getItem':
window.parent.postMessage(
{
action: 'getItem',
value: localStorage.getItem(event.data.key)
},
'*'
)
break;
}
}, false);
</script>
</head>
<body>
</body>
</html>
Instead, this snippet embeds the application into an invisible iframe, exposing the methods for setting and retrieving shared items:
const iframe = document.createElement('iframe')
iframe.width = '0'
iframe.height = '0'
iframe.src = 'https://cross-origin-storage.app'
document.body.appendChild(iframe)
window.crossOriginStorage = {
async setItem(key, value) {
iframe.contentWindow.postMessage({ action: 'setItem', key, value }, '*')
},
async getItem(key) {
return new Promise((resolve) => {
const callback = (event) => {
switch (event.data.action) {
case 'getItem':
window.removeEventListener('message', callback)
resolve(event.data.value)
break;
}
}
window.addEventListener('message', callback, false)
iframe.contentWindow.postMessage({ action: 'getItem', key }, '*')
})
}
}
Finally, you'd use the above library to store and retrieve a shopping cart ID like this:
await crossOriginStorage.setItem('orderId', 'ABC123')
await crossOriginStorage.getItem('orderId')
Note how our library's setItem() and getItem() implement the same interface as localStorage. With the ability to store and retrieve any information, you can do interesting things beyond sharing a cart ID. For example, you could store a user ID so customers can log in and check out as registered users on multiple sites.
Security considerations
The solution above presents a security concern. Anyone could include our library in any website and interact with our shared cart. In order to avoid this possibility, the iframe application should be able to set the Content-Security-Policy header and restrict the list of domains that are allowed to embed it in an iframe. With such an header, any other site trying to embed the application into an iframe would get an error, preventing unauthorized user to access the shared storage.
As a final note, Chrome is already working on a Shared Storage proposal for cross-site storage. possibly to prepare for the third-party cookie phase out that Google planned for 2024. In the event that this becomes a Chrome feature, Mozilla and Microsoft will probably follow, making the implementation of shared carts even easier.