Skip to content
On this page

Embedded checkout in headless applications

Introduction

BigCommerce offers a ready-to-use checkout experience that can be embedded into your headless application. This allows you to provide a seamless checkout experience to your customers without having to build it yourself.

In this article, we will go through the steps required to embed the BigCommerce checkout into your headless application.

Prerequisites

I assume that you have a BigCommerce store, BigCommerce API access and a headless application integrated with BigCommerce that you want to embed the checkout into. If you don't have a BigCommerce store, you can create a free trial.

If you don't have a headless application integrated with BigCommerce, you can check out the Vue Storefront 2 integration for BigCommerce 😉

BigCommerce setup

If you are developing a headless application, you should have a custom channel set up for your storefront in the BigCommerce Channel Manager.

Channel manager;

If you don't have a custom channel set up, you can create one and create a site for it. It should be the URL of your headless application.

Creating a cart

Before embedding the checkout, first, you need to create a cart.

For headless applications, you should create a cart with the channel_id of your custom channel added to the request body. Also, you need to add a query parameter include=redirect_urls to the request URL. This will return the redirect_urls object in the response.

The createCart request could look like this:

ts
import fetch from "node-fetch";

const storeHash = process.env.STORE_HASH;
const authToken = process.env.AUTH_TOKEN;
const channelId = process.env.CHANNEL_ID;

const url = `https://api.bigcommerce.com/stores/${storeHash}/v3/carts?include=redirect_urls`;

const params = {
  customer_id: 0,
  line_items: [
    {
      quantity: 1,
      product_id: 111,
    },
  ],
  channel_id: channelId,
};

let options = {
  method: "POST",
  headers: {
    Accept: "application/json",
    "Content-Type": "application/json",
    "X-Auth-Token": authToken,
  },
  body: JSON.stringify(params),
};

const res = await fetch(url, options);
const cart = await res.json();

WARNING

If you are trying that on the frontend, you will most likely get a CORS error. To avoid that, you can use a proxy server to make the request. A cool solution is a Netlify Functions feature. It allows you to run serverless functions on Netlify.

The response of the createCart request will contain the redirect_urls object. It contains the information about:

  • cart_url - the URL of the cart page,
  • checkout_url - the URL of the standalone checkout page,
  • embedded_checkout_url - the URL of the embedded checkout.

As you can see, the embedded_checkout_url is the URL that we need to embed the checkout into our headless application.

Example response of createCart request:

json
{
  "data": {
    "id": "36199f6e-3830-4408-abc0-1b241143aa57",
    "customer_id": 0,
    "channel_id": 1234567,
    // ...
    "redirect_urls": {
      "cart_url": "https://playing-with-bigcommerce.mybigcommerce.com/cart.php?action=load&id=36199f6e-3830-4408-abc0-1b241143aa57&token=0779668447bb3cb2842aa03f6b0371bdf3d4c13294de545f2349aa6f800b7336",
      "checkout_url": "https://playing-with-bigcommerce.mybigcommerce.com/cart.php?action=loadInCheckout&id=36199f6e-3830-4408-abc0-1b241143aa57&token=0779668447bb3cb2842aa03f6b0371bdf3d4c13294de545f2349aa6f800b7336",
      "embedded_checkout_url": "https://playing-with-bigcommerce.mybigcommerce.com/cart.php?embedded=1&action=loadInCheckout&id=36199f6e-3830-4408-abc0-1b241143aa57&token=0779668447bb3cb2842aa03f6b0371bdf3d4c13294de545f2349aa6f800b7336"
    }
  },
  "meta": {}
}

Embedding the checkout

Once you have the embedded_checkout_url, you can embed the checkout into your headless application.

WARNING

The best way to embed the checkout is to use the BigCommerce Checkout SDK, however, it's not working with Nuxt 3 yet. I've created an issue for that. I will update this article once it's fixed. To continue, I will use the <iframe> directly.

To embed the checkout, you need to add the <iframe> to your application. The src attribute of the <iframe> should be the embedded_checkout_url from the createCart response.

html
<iframe
  src="<EMBEDDED_CHECKOUT_URL>"
  width="100%"
  height="100%"
  frameborder="0"
></iframe>

I looks like the job is done, but unfortunately, it's not. You probably see the broken checkout like this:

Broken checkout

The checkout is not working properly, and it's not because of the simple <iframe>. The same issue is happening with the BigCommerce Checkout SDK.

In the console, you can see an error like this one:

Refused to frame 'https://playing-with-bigcommerce.mybigcommerce.com/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors https://effortless-pastelito-df6116.netlify.app".

The problem is that the checkout is using the Content Security Policy (CSP) header to protect the checkout from malicious scripts. The CSP header is a security feature that allows you to control what resources the browser can load for a given page. It's a good thing, but it needs to be configured properly.

Configuring CSP header

To fix the CSP issue, you need to add the frame-ancestors directive to the CSP header. The frame-ancestors directive allows you to specify the origins that can embed the checkout.

To configure the header, you need to open the BigCommerce control panel and go to Settings > Security & Privacy. At the bottom of the page, you can see the Storefront section with Configure Content-Security-Policy Header Value field. All you need to do is to switch to Specify my own CSP header. In the checkbox, add a value for the CSP header:

frame-ancestors <YOUR_APP_URL>

Be careful to not add the trailing slash at the end of the URL. The CSP header should look like this:

frame-ancestors https://effortless-pastelito-df6116.netlify.app

CSP configuration

Multiple domains

If you want to embed the checkout on multiple domains, you can add multiple domains to the CSP header. For example, if you want to embed the checkout on https://effortless-pastelito-df6116.netlify.app and https://dev-embedded-checkout.netlify.app, you can add both domains to the CSP header:

frame-ancestors https://effortless-pastelito-df6116.netlify.app https://dev-embedded-checkout.netlify.app

Remember, that each domain needs to be assigned to a custom channel, which ID you need to use in the createCart request.

Running checkout locally

A lot of effort, but until your application is not deployed, you won't be able to see the checkout anyway. If you want to run the checkout locally, you can use the ngrok tool. It's a tool that allows you to expose your local server to the internet. It's really easy to use. You just need to run the following command:

bash
ngrok http <PORT>

The PORT is the port on which your application is running.

ngrok output

It's forwarding the traffic from https://really-long-and-random-url.ngrok-free.app to your local server. You can use this URL in the CSP header and also as the custom channel site URL.

BigCommerce custom channels

Finally, after visiting the https://really-long-and-random-url.ngrok-free.app URL, you can see the checkout embedded in your application:

Embedded checkout

INFO

Embedded checkout and authenticated users are even more fun, but I will write about it in another article.

Example application

If you want to see the Nuxt 3 application with the embedded checkout, you can check out the example application I've created. It's really simple, but it shows how to embed the checkout into a Nuxt 3 app. To call the createCart request without CORS errors, I've used a Netlify Functions feature.