Lab - Create a Cart and Checkout Flow in Postman

Plan: Developer Foundations

Lesson 23 of 28 · 45 min

Introduction

In this exercise, you’ll build on the Postman workflows from previous labs with a workflow that will mimic the way common cart/checkout operations interact with the BigCommerce APIs in a headless context; this kind of prototyping is an essential first step toward understanding and implementing such a workflow in your own code.

Certain catalog data and shipping/payment configuration must be present in your BigCommerce store in order for the exercise queries to succeed. We’ll detail the requirements below.

Prerequisites

  • A BigCommerce sandbox store or trial store, or a full production store
  • Postman
  • An existing Postman environment and collection as configured in previous labs

Shipping Configuration

Your store must be configured with a shipping zone that:

  • Includes Austin, TX (or you can feel free to change the details of the shipping address we use)
  • Has at least one available shipping method

For more about configuring shipping, see the article below:

Shipping Setup

KB Article

View Resource

Product Prerequisites

Your store should contain at least one product that:

  • Is physical
  • Is in stock and purchasable
  • Has at least one in-stock variant option
  • Has no required modifier options
  • Qualifies for the available shipping method

The first product found and captured by the previous “Get Paginated Products” request must qualify for the available shipping method.

Postman Recap

As a reminder, your work in previous labs should have resulted in the following Postman configuration, which will be important for the following exercise steps:

  • An environment with variables set for v3_token, store_hash, storefront_channel_id and private_token (and optionally storefront_token from the browser-token request)
  • A collection with Bearer Token authentication using the value of private_token
  • A “REST Create Private Token (server-to-server)” request
  • A “GQL Get Paginated Products” request

Before beginning the following exercise, make sure to:

  1. Run “REST Create Storefront CIT” to populate storefront_impersonation_token on the environment with a valid token.
  2. Run “GQL Get Paginated Products” until all tests pass and product_id and variant_id are populated on the collection.

Sequence Overview

The workflow we’ll be building is an example of the requests potentially involved for a headless storefront to manage carts, checkouts, and payments. The steps involved include retrieving information about a product, adding it to the cart, and completing the billing, shipping and payment steps of checkout. Most of the requests we’ll be building use the GraphQL Storefront API, though certain steps like generating a GraphQL token and performing payment processing will require REST API actions.

At a high level, our Postman collection will follow this sequence:

  1. Create a cart and add the product found previously, storing the cart and line item IDs.
  2. Get checkout redirect URLs for the new cart, which would be used if your storefront will direct the customer to BigCommerce native checkout.
  3. Add a shipping consignment to the checkout with a hard-coded shipping address, then store the first available shipping option form the response.
  4. Update the shipping consignment to select the shipping method.
  5. Add a billing address to the checkout with hard-coded details.
  6. “Complete” the checkout, resulting in a new order record that will be considered incomplete until payment is applied.

Create a Cart

Step 1: Create the Request

This initial request to create a cart requires the values for product_id and variant_id stored on the collection by the “Get Paginated Products” request.

When a cart should be associated with a registered customer, which is necessary in order to access stored credit cards in the payment steps, you must use the assignCartToCustomer mutation after the cart is created.

  1. Create a new “GQL Create Cart” HTTP request and save it to your collection. This request requires the following details
FieldValue
MethodPOST
URLhttps://store-{{store_hash}}-{{storefront_channel_id}}.mybigcommerce.com/graphql
AuthorizationInherit auth from parent
Settings > Disable cookie jarOn
Body typeGraphQL
  1. In the Headers tab, select your GraphQL headers preset to apply “Accept” and “Content-Type” headers
  2. Enter the following query in the Body tab:
mutation CreateCart(
$itemQty: Int = 1,
$productId: Int!,
$variantId: Int
) {
cart {
createCart(
input: {
lineItems: [
{
quantity: $itemQty,
productEntityId: $productId,
variantEntityId: $variantId
}
]
}
) {
cart {
entityId
lineItems {
physicalItems {
entityId
}
}
}
}
}
}
  1. Enter the following in the GraphQL Variables section of the Body tab:
{
"itemQty": 1,
"productId": {{product_id}},
"variantId": {{variant_id}}
}
  1. Send the request and observe the results.

Step 2: Add Tests and Store Values

We’ll once again add tests to ensure we got the response we expected, and also store information we’ll need in subsequent requests. In this case, we need to store the cart ID and the line item ID.

  1. Enter the following “Post-response” value in the Scripts tab:
pm.test('Response is not an error', () => {
pm.response.to.not.be.error
pm.response.to.not.have.jsonBody("errors")
});
pm.test('Response is JSON with data', () => {
pm.response.to.have.jsonBody("data")
});
const cart = pm.response.json().data?.cart?.createCart?.cart;
pm.test('Response includes a cart ID', () => {
pm.expect(
cart?.entityId
).to.be.a('string')
});
  1. Send the request again and make sure the tests are successful
  2. Add the following script after the initial code in Scripts:
pm.collectionVariables.unset("cart_id");
pm.collectionVariables.unset("line_item_id");
pm.collectionVariables.set("cart_id", cart?.entityId);
pm.collectionVariables.set(
"line_item_id",
cart?.lineItems?.physicalItems[0]?.entityId
);
  1. Send the request again, and verify that the cart_id and line_item_id variables are populated on the collection

Fetch the Cart

  1. Create a new “GQL Get Cart” HTTP request and save it to your collection
  2. Set the same configuration that was used for the previous request, including the HTTP method, URL, Authorization, Headers, Body type and “Disable cookie jar” setting
  3. Enter the following query:
query GetCart(
$cartId: String!
) {
site {
cart(entityId: $cartId) {
id
entityId
currencyCode
baseAmount {
value
}
amount {
value
}
lineItems {
physicalItems {
entityId
productEntityId
variantEntityId
sku
name
imageUrl
quantity
listPrice {
value
}
originalPrice {
value
}
salePrice {
value
}
}
digitalItems {
entityId
productEntityId
variantEntityId
sku
name
imageUrl
quantity
listPrice {
value
}
originalPrice {
value
}
salePrice {
value
}
}
}
}
}
}
  1. Enter the following in the GraphQL Variables section of the Body tab:
{
"cartId": "{{cart_id}}"
}
  1. Send the request and observe the result.

Create Cart Redirect URLs

The following request simulates the workflow that would be used if directing customers to the BigCommerce native checkout.

Step 1: Create the Request

  1. Create a new “GQL Get Cart Redirect URLs” HTTP request and save it to your collection.
  2. Set the same configuration that was used for the previous request, including the HTTP method, URL, Authorization, Headers, Body type and “Disable cookie jar” setting.
  3. Enter the following query:
mutation GetCartRedirectUrls(
$cartId: String!
) {
cart {
createCartRedirectUrls(
input: {
cartEntityId: $cartId
}
) {
redirectUrls {
redirectedCheckoutUrl
embeddedCheckoutUrl
}
}
}
}
  1. Enter the following in the GraphQL Variables section of the Body tab:
{
"cartId": "{{cart_id}}"
}
  1. Send the request and observe the result.

Step 2: Add Tests

  1. Enter the following “Post-response” value in the Scripts tab:
pm.test('Response is not an error', () => {
pm.response.to.not.be.error
pm.response.to.not.have.jsonBody("errors")
});
pm.test('Response is JSON with data', () => {
pm.response.to.have.jsonBody("data")
});
const urls = pm.response.json().data?.cart?.createCartRedirectUrls?.redirectUrls;
pm.test('Response includes redirect URLs', () => {
pm.expect(
urls?.redirectedCheckoutUrl
).to.not.be.empty;
pm.expect(
urls?.embeddedCheckoutUrl
).to.not.be.empty;
});
  1. Send the request again and make sure the tests are successful.

When checkout will take place on the BigCommerce hosted checkout, this is where the GraphQL workflow would end. The following requests in your workflow simulate the necessary operations for building out a full checkout experience in your own application.

Fetch Checkout Details

The request to get the checkout will use the same ID as the cart.

  1. Create a new “GQL Get Checkout” HTTP request and save it to your collection.
  2. Set the same configuration that was used for the previous request, including the HTTP method, URL, Authorization, Headers, Body type and “Disable cookie jar” setting.
  3. Enter the following query:
query GetCart(
$cartId: String!
) {
site {
checkout(entityId: $cartId) {
cart {
id
entityId
currencyCode
baseAmount {
value
}
amount {
value
}
}
shippingCostTotal {
value
}
handlingCostTotal {
value
}
taxTotal {
value
}
subtotal {
value
}
grandTotal {
value
}
billingAddress {
firstName
lastName
email
address1
city
stateOrProvince
countryCode
postalCode
phone
}
shippingConsignments {
address {
firstName
lastName
email
address1
city
stateOrProvince
countryCode
postalCode
phone
}
availableShippingOptions {
description
type
}
selectedShippingOption {
description
type
}
shippingCost {
value
}
handlingCost {
value
}
lineItemIds
}
}
}
}
  1. Enter the following in the GraphQL Variables section of the Body tab:
{
"cartId": "{{cart_id}}"
}

Create a Consignment

A consignment is an object that includes:

  • At least one physical line item
  • Exactly one shipping address
  • Exactly one selected shipping method

Each physical line item in the cart must be associated with a consignment before the checkout is complete and able to be converted into an order.

Step 1: Create the Request

  1. Create a new HTTP request and save it in your collection with the name “GQL Create Consignment.”
  2. Set the same configuration that was used for the previous request, including the HTTP method, URL, Authorization, Headers, Body type and “Disable cookie jar” setting.
  3. Enter the following query:
mutation AddConsignments(
$cartId: String!,
$lineItemId: String!,
$lineItemQty: Int = 1
) {
checkout {
addCheckoutShippingConsignments(
input: {
checkoutEntityId: $cartId,
data: {
consignments: [{
address: {
firstName: "John",
lastName: "Doe",
email: "john.doe@example.org",
address1: "11305 Four Points Dr",
city: "Austin",
stateOrProvince: "Texas",
stateOrProvinceCode: "TX",
countryCode: "US",
postalCode: "78726",
phone: "5128654500",
shouldSaveAddress: false
},
lineItems: [{
lineItemEntityId: $lineItemId,
quantity: $lineItemQty
}]
}]
}
}
) {
checkout {
shippingConsignments {
entityId
availableShippingOptions {
entityId
}
}
}
}
}
}
  1. Enter the following in the GraphQL Variables section of the Body tab. Note that we’re relying on the values we’ve stored from the previous request to supply the details of our consignment creation.
{
"cartId": "{{cart_id}}",
"lineItemId": "{{line_item_id}}",
"lineItemQty": 1
}
  1. Send the request. If you have a valid shipping method configured for a zone that applies to our hard-coded address, you should see available shipping options in the response.

Step 2: Add Tests and Store Values

Let’s apply the usual sorts of tests to this response. This time around, we also need to make sure to store the IDs of the consignment and the first available shipping option.

  1. Enter the following “Post-response” value in the Scripts tab:
pm.test("Response is not an error", () => {
pm.response.to.not.be.error
pm.response.to.not.have.jsonBody("errors")
});
pm.test('Response is JSON with data', () => {
pm.response.to.have.jsonBody("data")
});
const consignments = pm.response.json()
.data?.checkout?.addCheckoutShippingConsignments
?.checkout?.shippingConsignments;
pm.test('Response includes exactly one consignment', () => {
pm.expect(consignments?.length).to.equal(1);
});
pm.test('Consignment includes at least one available shipping option', () => {
pm.expect(
consignments[0]?.availableShippingOptions?.length
).to.be.above(0);
});
  1. Send the request again and make sure the tests are successful.

Don’t worry about creating a consignment multiple times. When a new consignment is created with the same line item as a previous consignment, the previous record is destroyed. 3. Add the following script after the initial code:

const consignmentId = consignments[0]?.entityId;
const shippingOptionId = consignments[0]?.availableShippingOptions[0]?.entityId;
pm.collectionVariables.unset("consignment_id");
pm.collectionVariables.unset("shipping_option_id");
pm.collectionVariables.set("consignment_id", consignmentId);
pm.collectionVariables.set("shipping_option_id", shippingOptionId);
  1. Send the request again, and verify that the consignment_id and shipping_option_id variables are populated on the collection.

Add a Shipping Selection to the Consignment

Step 1: Create the Request

After creating the consignment and retrieving the available shipping options, we must update the consignment with the selected shipping option’s ID.

  1. Create a new HTTP request and save it in your collection with the name “GQL Select Shipping.”
  2. Set the same configuration that was used for the previous request, including the HTTP method, URL, Authorization, Headers, Body type and “Disable cookie jar” setting.
  3. Enter the following query:
mutation SelectShipping(
$cartId: String!,
$consignmentId: String!,
$shippingOptionId: String!
) {
checkout {
selectCheckoutShippingOption(
input: {
checkoutEntityId: $cartId,
consignmentEntityId: $consignmentId,
data: {
shippingOptionEntityId: $shippingOptionId
}
}
) {
checkout {
shippingConsignments {
selectedShippingOption {
entityId
}
}
}
}
}
}
  1. Enter the following in the GraphQL Variables section of the Body tab:
{
"cartId": "{{cart_id}}",
"consignmentId": "{{consignment_id}}",
"shippingOptionId": "{{shipping_option_id}}"
}
  1. Send the request.

Step 2: Add Tests

Unlike the previous requests, we don’t need to store any data from this response. We just need to include the right tests to make sure the shipping option was selected as intended.

  1. Enter the following “Post-response” value in the Scripts tab:
pm.test('Response is not an error', () => {
pm.response.to.not.be.error;
pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
pm.response.to.have.jsonBody("data");
})
const consignments = pm.response.json()
.data?.checkout?.selectCheckoutShippingOption
?.checkout?.shippingConsignments;
pm.test('Response includes exactly one consignment', () => {
pm.expect(consignments?.length).to.equal(1);
});
pm.test(
'Selected shipping option ID matches stored shipping option ID',
() => {
pm.expect(
consignments[0]?.selectedShippingOption?.entityId
).to.equal(
pm.collectionVariables.get('shipping_option_id')
)
}
);
  1. Send the request again and verify the tests are successful.

Create a Billing Address

Step 1: Create the Request

We’ve completed the shipping step! Now let’s add the billing address.

  1. Create a new HTTP request and save it in your collection with the name “GQL Create Billing Address.”
  2. Set the same configuration that was used for the previous request, including the HTTP method, URL, Authorization, Headers, Body type and “Disable cookie jar” setting.
  3. Enter the following query. Feel free to change any of the values in the billing address as long as you supply a valid address, name, and email address.
mutation AddBillingAddress(
$cartId: String!
) {
checkout {
addCheckoutBillingAddress(
input: {
checkoutEntityId: $cartId,
data: {
address: {
firstName: "John",
lastName: "Doe",
email: "john.doe@example.org",
address1: "11307 Six Points Dr",
city: "Austin",
stateOrProvince: "Texas",
stateOrProvinceCode: "TX",
countryCode: "US",
postalCode: "78726",
phone: "5128654500",
shouldSaveAddress: false
}
}
}
) {
checkout {
billingAddress {
entityId
email
countryCode
}
}
}
}
}
  1. Enter the following in the GraphQL Variables section of the Body tab:
{
"cartId": "{{cart_id}}"
}
  1. Send the request.

Step 2: Add Tests

  1. Enter the following “Post-response” value in the Scripts tab:
pm.test('Response is not an error', () => {
pm.response.to.not.be.error;
pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
pm.response.to.have.jsonBody("data");
});
const billingAddress = pm.response.json()
.data?.checkout?.addCheckoutBillingAddress
?.checkout?.billingAddress;
pm.test(
'Billing address includes an id that is a string',
() => {
pm.expect(billingAddress?.entityId).to.be.a('string');
}
);
pm.test(
'Billing address includes an email that is a non-empty string',
() => {
pm.expect(
billingAddress?.email
).to.be.a('string').that.is.not.empty;
}
);
pm.test(
'Billing address includes a country_code that is 2 characters',
() => {
pm.expect(
billingAddress?.countryCode
).to.be.a('string').that.has.lengthOf(2);
}
);
  1. Send the request and verify the tests are successful.

Create an Order

Step 1: Create the Request

At this point, the checkout object should have all required fields to convert it into an order. We’ll use the completeCheckout mutation to convert the checkout to an order, which defaults to the “Incomplete” status in BigCommerce until payment is applied.

This will be our last stop for this exercise, since the REST Payments API is required to capture payment and complete the order.

  1. Create a new HTTP request and save it in your collection with the name “GQL Complete Checkout.”
  2. Set the same configuration that was used for the previous request, including the HTTP method, URL, Authorization, Headers, Body type and “Disable cookie jar” setting.
  3. Enter the following query:
mutation CompleteCheckout(
$cartId: String!
) {
checkout {
completeCheckout(
input: {
checkoutEntityId: $cartId
}
) {
orderEntityId
}
}
}
  1. Enter the following in the GraphQL Variables section of the Body tab:
{
"cartId": "{{cart_id}}"
}
  1. Send the request and observe the response. Until payment is processed on the current checkout, you can use this mutation to create multiple incomplete orders from the same checkout.

Step 2: Add Tests and Store Values

In addition to the standard tests, you must store the resulting order ID if you intend to complete the checkout workflow with REST requests to capture payment.

  1. Enter the following “Post-response” value in the Scripts tab:
pm.test('Response is not an error', () => {
pm.response.to.not.be.error;
pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
pm.response.to.have.jsonBody("data");
});
pm.test('Response includes a numeric order ID', () => {
pm.expect(
pm.response.json()
.data?.checkout?.completeCheckout?.orderEntityId
).to.be.a('number');
});
  1. Send the request again and verify that tests are successful.
  2. Add the following script after the initial tests:
const orderId = pm.response.json()
.data?.checkout?.completeCheckout?.orderEntityId;
pm.collectionVariables.unset("order_id");
pm.collectionVariables.set("order_id", orderId);
  1. Send the request again and verify that the order_id variable is populated on the collection.

Taking It Further

Try combining customer login with cart operations by providing a valid customer access token in the cart requests. Observe and test the effects of a createCartRedirectUrls request with a customer access token, which should result in a URL that automatically logs the customer in.