Explore various update actions to dynamically modify and enhance your customer Carts.
After completing this page, you should be able to:
- Apply a variety of update actions using the SDK to manage a Cart's contents, shipping details, and tax configuration.
- Incorporate optimistic locking by providing the correct Cart
version
in every update request to prevent data conflicts.
Once a Cart exists, you modify it by sending a
POST
request containing an array of update actions. Every update request must include the Cart's current version
to ensure optimistic concurrency control, preventing race conditions and accidental overwrites.Manage Line Items
Add a Product to the Cart (addLineItem)
This action adds a product from your catalog to the Cart.
import { apiRoot } from "../ctp-root";
import { Cart, CartUpdateAction } from "@Composable Commerce/platform-sdk";
/**
* Adds a product (Line Item) to an existing Cart.
* @param storeKey The key of the Store where the cart exists.
* @param cartId The ID of the cart to update.
* @param version The current version of the Cart for optimistic locking.
* @param sku The SKU of the Product Variant to add.
* @param quantity The number of items to add.
* @returns A Promise resolving to the updated Cart.
*/
async function addLineItemToCart(
storeKey: string,
cartId: string,
version: number,
sku: string,
quantity: number
): Promise<Cart> {
const addLineItemAction: CartUpdateAction = {
action: "addLineItem",
sku, // The SKU of the Product Variant
quantity,
};
const { body } = await apiRoot
.inStoreKeyWithStoreKeyValue({ storeKey })
.carts()
.withId({ ID: cartId })
.post({
body: {
version: version,
actions: [addLineItemAction],
},
})
.execute();
return body;
}
Line Items are merged (increase the quantity of the existing Line Item) when you add a Line Item that already exists in the Cart if the following attributes match:
- Line Item Key
- Product Variant (
productId
andvariantId
, orsku
) - Supply, Distribution Channels
externalPrice
,externalTotalPrice
externalTaxRate
,perMethodExternalTaxRate
inventoryMode
shippingDetails
- Custom Fields
Remove a Product from the Cart (removeLineItem)
This action removes a specified quantity of a Line Item. If
quantity
is omitted, the entire Line Item is removed./**
* Removes a specific quantity of a Line Item from an existing Cart.
* @param lineItemId The ID of the Line Item to remove from.
* @param quantity The number of items to remove. If omitted, removes the entire Line Item.
*/
async function removeLineItemFromCart(
storeKey: string,
cartId: string,
version: number,
lineItemId: string,
quantity?: number
): Promise<Cart> {
const removeLineItemAction: CartUpdateAction = {
action: "removeLineItem",
lineItemId,
quantity, // Omitting this removes the full quantity
};
// ... execute post request
}
Add an external Line Item (addCustomLineItem)
For products or services not in your Project catalog (for example, installation fees, replacement parts from a third party), use
addCustomLineItem
. This creates a CustomLineItem
in the Cart.import {
Cart,
CartUpdateAction,
TypedMoneyDraft,
} from "@Composable Commerce/platform-sdk";
/**
* Adds an external item (as a Custom Line Item) to an existing Cart.
* @param name Localized name of the external item, for example, { "en-US": "Installation Fee" }.
* @param quantity The quantity of the external item.
* @param money The price of the external item.
* @param slug A unique, URL-friendly identifier for the custom Line Item.
*/
async function addExternalLineItem(
storeKey: string,
cartId: string,
version: number,
name: { [key: string]: string },
quantity: number,
money: TypedMoneyDraft,
slug: string
): Promise<Cart> {
const addCustomLineItemAction: CartUpdateAction = {
action: "addCustomLineItem",
name,
quantity,
money,
slug,
};
// ... execute post request
}
Manage Addresses
Set Shipping (setShippingAddress)
To associate an existing Customer Address with a Cart, you will need to use the action
setBillingAddress
and pass the whole address record from the Customer record (including the ID). This will help you avoid data duplication.import { Cart, CartUpdateAction } from '@Composable Commerce/platform-sdk';
// Define a type for the address data for better type-safety and readability
type CartAddress = {
id: string;
firstName: string;
lastName: string;
streetName: string;
streetNumber: string;
postalCode: string;
city: string;
mobile?: string;
country: string;
};
/**
* Sets the shipping on a cart by providing the full address details.
* @param storeKey The key of the Store.
* @param cartId The ID of the Cart to update.
* @param version The current version of the Cart.
* @param shippingAddress The full address object for shipping.
*/
async function setAddressWithDetails(
storeKey: string,
cartId: string,
version: number,
shippingAddress: CartAddress,
): Promise<Cart> {
const actions: CartUpdateAction[] = [
{
action: 'setShippingAddress',
address: {
id: shippingAddress.id,
firstName: shippingAddress.firstName,
lastName: shippingAddress.lastName,
streetName: shippingAddress.streetName,
streetNumber: shippingAddress.streetNumber,
postalCode: shippingAddress.postalCode,
city: shippingAddress.city,
mobile: shippingAddress.mobile,
country: shippingAddress.country,
},
},
];
// The body of your API request would be:
const requestBody = {
version,
actions,
};
// This is a placeholder return for the example
return {} as Cart;
}
// --- How to use the function ---
// 1. Define your address data based on the customer record
const myAddress: CartAddress = {
id: 'ZKCtEGyo',
firstName: 'Example',
lastName: 'Person',
streetName: 'Down Under st',
streetNumber: '360',
postalCode: '3000',
city: 'Melbourne',
mobile: '+61434126439',
country: 'AU',
};
// 2. Call the function (assuming shipping and billing addresses are the same)
setAddressWithDetails(
'my-store',
'zen-electron-us',
5, // The current cart version
myAddress,
);
Advanced Cart features
Add a Shopping List to the Cart (addShoppingList)
This action efficiently adds all items from a specified Shopping List to the Cart.
/**
* Adds all items from a Shopping List to an existing Cart.
* @param shoppingListId The ID of the Shopping List to add.
*/
async function addShoppingListToCart(
storeKey: string,
cartId: string,
version: number,
shoppingListId: string
): Promise<Cart> {
const addShoppingListAction: CartUpdateAction = {
action: "addShoppingList",
shoppingList: {
typeId: "shopping-list",
id: shoppingListId,
},
};
// ... execute post request
}
Handle external tax calculations
To use an external tax service (for example, Avalara or Vertex), you must first change the Cart's
taxMode
. There are two modes to consider:taxMode: 'External'
: use this when your service provides tax rates per Line Item. You then use the setLineItemTaxRate action to apply these rates.taxMode: 'ExternalAmount'
: use this when your service provides a single, total tax amount for the entire Cart. You then use thesetCartTotalTax
action.
Step 1: Change the Tax Mode: this is a prerequisite for all external tax actions.
const changeTaxModeAction: CartUpdateAction = {
action: "changeTaxMode",
taxMode: "ExternalAmount", // or 'External'
};
Step 2: Set the external tax: after changing the mode, apply the tax data from your external service.
import {
Cart,
CartUpdateAction,
Money,
TaxPortionDraft,
ExternalTaxRateDraft,
CartSetCartTotalTaxAction,
CartSetLineItemTaxAmountAction,
} from "@Composable Commerce/platform-sdk";
// --- Helper Interface for clarity ---
/**
* Defines the data needed to set the external tax for a single Line Item.
*/
export interface LineItemTaxDetails {
lineItemId: string;
totalGross: Money;
taxRate: ExternalTaxRateDraft;
}
/**
* Sets an external tax amount for the entire cart and for each Line Item.
* This is used when the Cart's taxMode is 'ExternalAmount'.
*
* @param storeKey The key of the store.
* @param cartId The ID of the Cart to update.
* @param version The current version of the Cart.
* @param externalTotalGross The total gross amount of the Cart (including all taxes) from the external service.
* @param lineItemTaxDetails An array containing the tax details for each Line Item.
* @param externalTaxPortions An optional breakdown of the total Cart tax into different portions (for example, state or federal).
*/
async function setExternalTaxForCart(
storeKey: string,
cartId: string,
version: number,
externalTotalGross: Money,
lineItemTaxDetails: LineItemTaxDetails[],
externalTaxPortions?: TaxPortionDraft[]
): Promise<Cart> {
// This function will generate multiple update actions
const updateActions: CartUpdateAction[] = [];
// 1. Action to set the total tax for the entire cart
const setCartTotalTaxAction: CartSetCartTotalTaxAction = {
action: "setCartTotalTax",
externalTotalGross,
externalTaxPortions,
};
updateActions.push(setCartTotalTaxAction);
// 2. Actions to set the tax for each individual Line Item
for (const item of lineItemTaxDetails) {
const setLineItemTaxAction: CartSetLineItemTaxAmountAction = {
action: "setLineItemTaxAmount",
lineItemId: item.lineItemId,
externalTaxAmount: {
totalGross: item.totalGross,
taxRate: item.taxRate,
},
};
updateActions.push(setLineItemTaxAction);
}
/*
// Example of executing the request with the Composable Commerce SDK client
const updatedCart = await apiRoot
.inStore({ storeKey })
.carts()
.withId({ ID: cartId })
.post({
body: {
version,
actions: updateActions,
},
})
.execute();
return updatedCart.body;
*/
// For demonstration purposes, we'll return a mock Cart.
// Replace this with the actual API call above.
}
async function runExample() {
// --- Cart & Tax Service Data (Example) ---
const cartId = "c7a3b1e0-f9d5-4a2c-8e1d-9b4c0f8a7b6d";
const cartVersion = 5;
const storeKey = "zen-electron-us";
// Imagine your external tax service returned the following calculations for a Cart
// with two Line Items.
// Item 1: T-Shirt ($25.00) -> Gross Price: $26.88 (with 7.5% tax)
// Item 2: Jeans ($70.00) -> Gross Price: $75.25 (with 7.5% tax)
// Total Gross: $26.88 + $75.25 = $102.13
// Total gross price for the entire Cart
const cartTotalGross: Money = {
currencyCode: "USD",
centAmount: 10213, // $102.13
};
// Tax details for each Line Item
const lineItemsToTax: LineItemTaxDetails[] = [
{
lineItemId: "6f9f5a83-808c-49f9-a3ef-5e32cd377042",
totalGross: {
currencyCode: "USD",
centAmount: 2688, // $26.88
},
taxRate: {
name: "CA Sales Tax",
amount: 0.075, // 7.5%
country: "US",
state: "CA", // It's good practice to include the state for US taxes
},
},
{
lineItemId: "7f9f5a83-808c-49f9-a3ef-5e32cd377042",
totalGross: {
currencyCode: "USD",
centAmount: 7525, // $75.25
},
taxRate: {
name: "CA Sales Tax",
amount: 0.075, // 7.5%
country: "US",
state: "CA",
},
},
];
// Call the updated function with all the necessary data
await setExternalTaxForCart(
storeKey,
cartId,
cartVersion,
cartTotalGross,
lineItemsToTax
);
}
runExample();