cimplify
Cimplify Link

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 Change action.
  • 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_cart has 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_established with 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 broadcasts set_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_failed and the iframe drops the hydrated details and returns to contact entry.
  • A non-member contact continues as a guest; the iframe sends contact_provided so 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:

FieldNotes
businessIdRequired business context.
publicKeycpk_live_… or cpk_test_…. Used for API calls and test-mode detection.
orderTypesSubset of delivery | pickup | dine_in. Defaults to ["pickup", "delivery"].
defaultOrderTypePre-selected order type. Falls back to the first allowed order type.
renderSubmitButtonWhen true, the iframe renders its own Pay button and emits request_submit.
submitLabelOverride Pay button copy.
prefillEmailOptional contact prefill. Despite the name, it can seed only the contact field; checkout will still normalize/validate before auth.
appearanceSee 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:

  1. Prefer <CimplifyCheckout>.
  2. Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY.
  3. Let the iframe handle shopper verification and saved details — there is nothing to build for OTP.
  4. Let the SDK handle session_established and set_token; keep the SDK current so recognition survives across visits.
  5. Add the Sign in with Cimplify routes only if the storefront has account pages beyond checkout.

Next

On this page