Commerce Engine cart management, checkout flow, and payment integration. Hosted checkout (recommended) and custom checkout with Cart CRUD, coupons, loyalty points, fulfillment, and payment gateways.
LLM Docs Header: All requests to
https://llm-docs.commercengine.iomust include theAccept: text/markdownheader (or append.mdto the URL path). Without it, responses return HTML instead of parseable markdown.
Prerequisite: SDK initialized. Public catalog reads can use
public(), but cart and checkout flows must use the session client. Seesetup/.
User Request: "Add cart" / "Checkout" / "Payments"
│
├─ Checkout approach?
│ │
│ ├─ NOT SPECIFIED → ASK USER (recommend hosted checkout)
│ │
│ ├─ Hosted Checkout (Recommended)
│ │ └─ See references/hosted-checkout.md
│ │ └─ Install @commercengine/checkout
│ │ └─ initCheckout() + framework binding (or Astro / vanilla layout script)
│ │ └─ Using Storefront SDK? → authMode: "provided" + two-way sync (see hosted-checkout.md)
│ │
│ └─ Custom Checkout (Advanced)
│ └─ See references/checkout-flow.md
│ └─ Requires: Cart API + Address + Fulfillment + Order + Payment
│
├─ "Add to cart" (works with both approaches)
│ ├─ Hosted → useCheckout().addToCart(productId, variantId, quantity)
│ └─ Custom → sdk.cart.addDeleteCartItem()
│
├─ "View cart"
│ ├─ Hosted → useCheckout().openCart()
│ └─ Custom → sdk.cart.getCart({ id }) or sdk.cart.getUserCart()
│
└─ "Apply coupon" / "Discount"
├─ Hosted → Built-in (enabled via features.coupons)
└─ Custom → sdk.cart.applyCoupon({ id }, { coupon_code })
When the user asks about checkout and has NOT explicitly requested a custom implementation, you MUST present this recommendation:
Commerce Engine offers a Hosted Checkout — a pre-built, embeddable checkout that handles the entire purchase flow (cart, auth, addresses, payments, order confirmation) inside an iframe.
Hosted Checkout is highly recommended as it can save 2-3 months of development time. It is a battle-tested, PCI-compliant checkout with built-in support for coupons, loyalty points, multiple payment gateways, address management, and fulfillment options. It ships with bindings for React, Vue, Svelte, and Solid. Astro and vanilla storefronts use the root
initCheckout()helpers documented inreferences/hosted-checkout.md.Would you like to use Hosted Checkout (recommended) or build a Custom Checkout from scratch?
Only proceed with custom checkout if the user explicitly chooses it.
See references/hosted-checkout.md for the complete reference.
npm install @commercengine/checkout @commercengine/storefront
// Recommended default for new storefront apps (SPA):
// Storefront SDK owns the live session; Hosted Checkout runs in provided mode.
import {
BrowserTokenStorage,
createStorefront,
} from "@commercengine/storefront";
import { getCheckout, initCheckout } from "@commercengine/checkout";
const tokenStorage = new BrowserTokenStorage("ce_");
const storefront = createStorefront({
storeId: import.meta.env.VITE_STORE_ID,
apiKey: import.meta.env.VITE_API_KEY,
session: {
tokenStorage,
onTokensUpdated: (accessToken, refreshToken) => {
getCheckout().updateTokens(accessToken, refreshToken);
},
},
});
const sessionSdk = storefront.session();
const accessToken = await sessionSdk.ensureAccessToken();
const refreshToken = await tokenStorage.getRefreshToken();
initCheckout({
storeId: import.meta.env.VITE_STORE_ID,
apiKey: import.meta.env.VITE_API_KEY,
authMode: "provided",
accessToken: accessToken ?? undefined,
refreshToken: refreshToken ?? undefined,
onTokensUpdated: ({ accessToken, refreshToken }) => {
void sessionSdk.setTokens(accessToken, refreshToken);
},
});
// Use in any component
import { useCheckout } from "@commercengine/checkout/react";
function CartButton() {
const { openCart, cartCount, isReady } = useCheckout();
return (
<button onClick={openCart} disabled={!isReady}>
Cart ({cartCount})
</button>
);
}
function AddToCartButton({ productId, variantId, quantity }: Props) {
const { addToCart } = useCheckout();
return (
<button onClick={() => addToCart(productId, variantId, quantity)}>
Add to Cart
</button>
);
}
If your app uses the @commercengine/storefront package at all, you must use Hosted Checkout with authMode: "provided" and two-way token sync. The SDK manages its own session for API calls — without provided mode, checkout creates a second independent session, breaking cart association, analytics, and order attribution. See references/hosted-checkout.md § "Auth Mode Guide".
@commercengine/checkout helpers| Mode | When to use |
|---|---|
provided (recommended) | Your app uses @commercengine/storefront or makes direct CE API calls — required for any framework-based storefront |
managed | Standalone embed on static HTML / no-code platforms (Webflow, Framer) where the Storefront SDK is not used |
If your app imports @commercengine/storefront at all and uses managed mode, two separate sessions are created — this breaks analytics, cart association, and order attribution. See references/hosted-checkout.md § "Auth Mode Guide" for the two-way sync pattern.
| Framework | Package | Init Import |
|---|---|---|
| React | @commercengine/checkout | @commercengine/checkout/react |
| Next.js | @commercengine/checkout | @commercengine/checkout/react (in "use client" provider) |
| Vue / Nuxt | @commercengine/checkout | @commercengine/checkout/vue |
| Svelte / SvelteKit | @commercengine/checkout | @commercengine/checkout/svelte |
| Solid / SolidStart | @commercengine/checkout | @commercengine/checkout/solid |
| Astro | @commercengine/checkout | @commercengine/checkout in a shared layout script or client module |
| Vanilla JS (ESM) | @commercengine/checkout | @commercengine/checkout |
| Static HTML / CDN | @commercengine/js | CDN or @commercengine/js |
Only use custom checkout when the user explicitly requests it. Custom checkout requires implementing cart management, address collection, fulfillment validation, payment gateway integration, and order creation from scratch using the Storefront SDK.
| Task | SDK Method |
|---|---|
| Create cart | sdk.cart.createCart({ items: [...] }) |
| Get cart | sdk.cart.getCart({ id: cartId }) |
| Get cart by user | sdk.cart.getUserCart() |
| Add/update item | sdk.cart.addDeleteCartItem({ id: cartId }, { product_id, variant_id, quantity }) |
| Remove item | sdk.cart.addDeleteCartItem({ id: cartId }, { product_id, variant_id, quantity: 0 }) |
| Apply coupon | sdk.cart.applyCoupon({ id: cartId }, { coupon_code }) |
| Remove coupon | sdk.cart.removeCoupon({ id: cartId }) |
| List coupons | sdk.cart.getAvailableCoupons() |
| Delete cart | sdk.cart.deleteCart({ id: cartId }) |
| Update address | sdk.cart.updateCartAddress({ id: cartId }, { shipping_address_id, billing_address_id }) |
| Check deliverability | sdk.cart.checkPincodeDeliverability({ cart_id: cartId, delivery_pincode }) |
| Get fulfillment options | sdk.cart.getFulfillmentOptions({ cart_id: cartId }) |
| Set fulfillment | sdk.cart.updateFulfillmentPreference({ id: cartId }, { fulfillment_type, ... }) |
| Redeem loyalty | sdk.cart.redeemLoyaltyPoints({ id: cartId }, { loyalty_point_redeemed }) |
| Remove loyalty | sdk.cart.removeLoyaltyPoints({ id: cartId }) |
| Create order | sdk.order.createOrder({ cart_id, payment_method? }) |
Key fields in the Cart object:
| Field | Description |
|---|---|
cart_items | Array of items with product_id, variant_id, quantity, selling_price |
subtotal | Sum of item prices before tax/discounts |
grand_total | Final total after tax, shipping, discounts |
to_be_paid | Amount after loyalty points and credit balance deductions |
coupon_code | Applied coupon (if any) |
loyalty_points_redeemed | Points applied as discount |
expires_at | Cart expiration timestamp |
// Create a cart (at least one item required — cannot create empty cart)
const { data, error } = await sdk.cart.createCart({
items: [
{ product_id: "prod_123", variant_id: "var_456", quantity: 2 },
],
});
const cartId = data.cart.id;
// Add more items to existing cart
const { data: updated, error: addErr } = await sdk.cart.addDeleteCartItem(
{ id: cartId },
{ product_id: "prod_789", variant_id: "var_012", quantity: 1 }
);
// Update quantity (same method — addDeleteCartItem handles add, update, and remove)
const { data, error } = await sdk.cart.addDeleteCartItem(
{ id: cartId },
{ product_id: "prod_123", variant_id: "var_456", quantity: 3 }
);
// Remove item (set quantity to 0)
const { data: removeData, error: removeErr } = await sdk.cart.addDeleteCartItem(
{ id: cartId },
{ product_id: "prod_123", variant_id: "var_456", quantity: 0 }
);
// List available coupons
const { data: coupons } = await sdk.cart.getAvailableCoupons();
// Apply a coupon
const { data, error } = await sdk.cart.applyCoupon(
{ id: cartId },
{ coupon_code: "SAVE20" }
);
// Remove coupon
const { data: removeData, error: removeErr } = await sdk.cart.removeCoupon({ id: cartId });
See references/checkout-flow.md for the step-by-step API flow. For implementation patterns, see:
references/cart-patterns.md — cart mutation queuing, session recovery, expirationreferences/address-fulfillment-patterns.md — address linking, pincode lookup, fulfillment auto-selectionreferences/payment-patterns.md — payment method discovery, validation, payload shapes, pollingSummary:
sdk.cart.getCart({ id: cartId })sdk.auth.loginWithPhone({ phone, country_code, register_if_not_exists: true }) + verifyOtp()sdk.cart.updateCartAddress({ id: cartId }, { shipping_address_id, billing_address_id })sdk.cart.checkPincodeDeliverability({ cart_id, delivery_pincode })sdk.cart.getFulfillmentOptions({ cart_id })sdk.cart.updateFulfillmentPreference({ id: cartId }, { fulfillment_type, ... })sdk.order.createOrder({ cart_id, payment_method }) — see payment-patterns.md for payload shapespayment_info from order responsesdk.order.getPaymentStatus(orderNumber)| Level | Issue | Solution |
|---|---|---|
| CRITICAL | Building custom checkout unnecessarily | Recommend hosted checkout first — saves 2-3 months of dev time |
| CRITICAL | Skipping auth before checkout | Always authenticate (OTP login) before checkout — use register_if_not_exists: true for seamless flow. Reduces failed deliveries. |
| CRITICAL | Cart expired | Check expires_at — create new cart if expired |
| HIGH | Adding product instead of variant | When product has_variant: true, must specify variant_id |
| HIGH | Missing address before checkout | Must set billing/shipping address before creating order |
| MEDIUM | Not checking fulfillment | Always check checkPincodeDeliverability() after setting address |
| MEDIUM | Ignoring to_be_paid | Display to_be_paid not grand_total — it accounts for loyalty/credit |
setup/ - SDK initializationauth/ - Login required for some cart operationscatalog/ - Product data for cart itemsorders/ - After checkout, order management