CheckoutElement
The unified checkout iframe. It renders contact capture, saved details, address, payment, provider authorization, and submit. Shopper verification happens in-panel; the parent SDK handles session persistence and identity binding.
CheckoutElement is the iframe behind <CimplifyCheckout> and the hosted Pay
checkout page. It owns the checkout UI and shopper verification: contact
recognition and the OTP challenge run entirely inside the iframe. The parent
SDK's role is persistence and identity binding — storing the refresh token
first-party so the shopper stays recognized on the next visit, and claiming
the storefront session so cart and chat share the verified identity.
Iframe URL
https://link.cimplify.io/elements/checkout?businessId=biz_…&nonce=<random>
# Alias (same component, same behavior):
https://link.cimplify.io/elements/payment?businessId=biz_…&nonce=<random>What It Renders
A vertically stacked checkout form whose sections respond to auth state and order type:
- Contact information: email or mobile number, shown inline before sign-in.
- Cimplify sign-in: after a valid contact, recognized Link members get an in-panel OTP step; the code is entered inside the iframe.
- Signed-in row: the moment the OTP verifies, the iframe shows the verified contact and a
Changeaction. - Order type: pickup / delivery / dine-in toggle when more than one type is enabled.
- Address section: saved addresses when signed in; otherwise a form with optional geolocation.
- Payment section: saved methods when signed in; otherwise the merchant's configured providers.
- Save info: "remember me for 1-click checkout" when the customer is signed in.
- Submit button: rendered when
renderSubmitButton: true. - Cart summary: shown when
set_carthas been sent.
Checkout Auth Flow
CheckoutElement Parent SDK
contact entered
membership check + OTP sent (in-panel)
shopper enters code (in-panel)
OTP verified — saved details hydrate
and checkout proceeds immediately
session_established ─────────────────▶ claims storefront session,
persists refresh token first-party
set_token ◀────────────────────────── echo of the same token (no-op)After a successful in-panel OTP:
- The iframe hydrates the shopper's saved addresses, payment methods, and store credit with its own verified session — checkout never waits on the parent page.
- In parallel it sends
session_establishedwith the access and refresh tokens. The parent SDK claims the storefront session (binding cart and chat to the verified identity) and stores the refresh token first-party, then broadcastsset_token; since the token matches, the iframe treats it as confirmation. - If the claim conflicts (the identity was superseded elsewhere), the parent
replies
session_establishment_failedand the iframe drops the hydrated details and returns to contact entry. - A non-member contact continues as a guest; the iframe sends
contact_providedso the parent can attach the contact to the order.
A parent on an older SDK that never answers session_established costs
nothing for the current visit — only cross-visit recognition and chat
identity binding, which resume once the storefront's SDK is updated.
Init Message
After ready, send the standard init. Fields specific to CheckoutElement:
| Field | Notes |
|---|---|
businessId | Required business context. |
publicKey | cpk_live_… or cpk_test_…. Used for API calls and test-mode detection. |
orderTypes | Subset of delivery | pickup | dine_in. Defaults to ["pickup", "delivery"]. |
defaultOrderType | Pre-selected order type. Falls back to the first allowed order type. |
renderSubmitButton | When true, the iframe renders its own Pay button and emits request_submit. |
submitLabel | Override Pay button copy. |
prefillEmail | Optional contact prefill. Despite the name, it can seed only the contact field; checkout will still normalize/validate before auth. |
appearance | See Appearance API. |
Mounting
React
<CimplifyCheckout> is the preferred integration for non-technical merchant
storefronts and agent-generated storefronts. It creates the iframe, handles
messages, starts OAuth, broadcasts tokens, processes checkout, and reports
status.
import { CimplifyClient } from "@cimplify/sdk";
import { CimplifyCheckout } from "@cimplify/sdk/react";
const client = new CimplifyClient({
publicKey: process.env.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY!,
credentials: "include",
});
export function Checkout({ cartId }: { cartId: string }) {
return (
<CimplifyCheckout
client={client}
cartId={cartId}
orderTypes={["delivery", "pickup"]}
defaultOrderType="delivery"
submitLabel="Pay now"
onComplete={(result) => {
if (result.success) window.location.assign(`/orders/${result.order!.id}`);
}}
onStatusChange={(status, ctx) => console.log(status, ctx.display_text)}
/>
);
}Checkout verification is self-contained — only
NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY is required. The OAuth routes from
Sign in with Cimplify are for site-wide
account features (order history, profile), not checkout.
Vanilla SDK
const elements = createElements(client, businessId, {
auth: {
clientId: "cim_client_…",
redirectUri: `${window.location.origin}/auth/callback`,
callbackUri: "/auth/callback",
},
});
const checkout = elements.create(ELEMENT_TYPES.CHECKOUT, {
orderTypes: ["delivery", "pickup"],
defaultOrderType: "delivery",
submitLabel: "Pay GH₵29.99",
});
checkout.on(EVENT_TYPES.REQUEST_SUBMIT, async () => {
const result = await elements.processCheckout({
cart_id: cart.id,
order_type: "delivery",
});
if (result.success) location.assign(`/orders/${result.order!.id}`);
});
checkout.mount("#checkout");Pre-Filling the Cart
set_cart renders the line-item summary alongside the form. CheckoutCartData:
interface CheckoutCartData {
items: CheckoutCartItem[];
subtotal: string;
tax_amount: string;
total_discounts: string;
service_charge: string;
total: string;
currency: string;
}
interface CheckoutCartItem {
name: string;
quantity: number;
unit_price: string;
total_price: string;
image_url?: string;
line_type: "simple" | "service" | "bundle" | "composite" | "digital";
variant_name?: string;
scheduled_start?: string;
scheduled_end?: string;
selections?: { name: string; quantity: number; variant_name?: string }[];
add_ons?: { name: string; price: string }[];
special_instructions?: string;
}Lifecycle
See checkout lifecycle for the full state
machine. The element emits checkout_status for each transition and
checkout_complete on terminal success or failure.
Payment Authorization Challenges
Provider challenges are separate from Cimplify sign-in. When a payment provider
needs an OTP, PIN, birthday, phone, or address to authorize payment, the element
switches to AuthorizationView and emits:
{
type: "checkout_status",
status: "awaiting_authorization",
context: { authorization_type: "otp", display_text: "Enter the code from your provider" }
}The shopper enters the challenge inside the checkout iframe. You do not render a separate challenge UI.
Recovery
If the shopper reloads while payment is in flight, the element rehydrates from
local storage on next mount and resumes from the last known state. The first
lifecycle event you receive will be checkout_status: "recovering".
Agent Checklist
When wiring checkout for a merchant:
- Prefer
<CimplifyCheckout>. - Set
NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY. - Let the iframe handle shopper verification and saved details — there is nothing to build for OTP.
- Let the SDK handle
session_establishedandset_token; keep the SDK current so recognition survives across visits. - Add the Sign in with Cimplify routes only if the storefront has account pages beyond checkout.
Next
- CimplifyCheckout (React): High-level wrapper
- Embedded iframe: Raw postMessage integration
- Element events: Full message contract
Universal Commerce Protocol (UCP)
A signed, capability-discoverable protocol AI agents use to browse, price, check out, and pay across any Cimplify business with verifiable consent.
Checkout sessions
A checkout session is a short-lived URL on `pay.cimplify.io` that hosts the full checkout for a specific cart. You create one server-side with your secret key, redirect the customer to the URL, and listen for the resulting webhook.