Sign in →
Embed Widgets1 min read

AforoCheckoutFlow

Multi-step embedded checkout. Supports Stripe, Razorpay, and PayPal via postMessage-bridged iframes. Handles SUBSCRIBE and INVOICE_PAYMENT carts in a 4-phase state machine.

Updated 2026-06-15Suggest edits
Docs Embed Widgets CheckoutFlow
Live preview — synthetic data
AforoCheckoutFlow
Multi-step cart → customer → payment → confirm.
Loads the widget from https://embed-demo.aforo.ai with synthetic data.

What it renders#

A multi-step embedded checkout widget that owns the entire customer billing flow — from cart creation through payment confirmation — inside your page. No redirects, no popups. Two cart types:

  • SUBSCRIBE — start a new subscription to an offering
  • INVOICE_PAYMENT — pay an existing outstanding invoice

Phase machine#

The widget walks the customer through a 4-phase state machine. Each phase emits an aforo.checkout.step_changed event so your analytics can track funnel drop-off.

1. bootstrapping
Cart created server-side, gateway secrets fetched
2. customer-details
Email + billing address form
3. payment
Provider iframe (Stripe / Razorpay / PayPal)
4. confirming → completed
Server-side confirm, subscription / invoice marked paid
INFO
Terminal phases: completed (success), cancelled (customer dismissed), expired (30-minute cart TTL elapsed). The Cancel button uses a semantic role="alertdialog" confirmation with focus trap and Esc-to-dismiss.
From Operator Portal → Settings.
From Operator Portal → Embed Studio → Keys.
Minted by your backend per customer session — see Authentication.
Script tag
<div data-aforo-widget="checkout-flow"
     data-aforo-tenant-slug="your-tenant"
     data-aforo-embed-key="ek_live_replace_me"
     data-aforo-bridge-token="bridge.replace.me"
     data-aforo-cart-type="SUBSCRIBE"
     data-aforo-target-id="off_pro"></div>

<script src="https://cdn.aforo.ai/embed/loader.js" async></script>
NPM / React
import { AforoCheckoutFlow } from '@aforoai/storefront-widgets';

export function Pricing() {
  return (
    <AforoCheckoutFlow
      tenantSlug="your-tenant"
      embedKey="ek_live_replace_me"
  bridgeToken="bridge.replace.me"
  cartType="SUBSCRIBE"
  targetId="off_pro"
    />
  );
}
Preview with my account
Fill in Tenant slug above to enable this preview.

Mount examples#

app/subscribe/page.tsx
import { AforoCheckoutFlow } from '@aforoai/storefront-widgets';

export default async function SubscribePage() {
  const session = await getSession();
  const bridgeToken = await mintBridgeToken({
    externalId: session.customerId,
    email: session.email,
  });

  return (
    <AforoCheckoutFlow
      tenantSlug="acme"
      embedKey="embk_live_…"
      bridgeToken={bridgeToken}
      cartType="SUBSCRIBE"
      targetId="off_pro"
      onCompleted={(payload) => {
        console.log('Subscription created:', payload.subscriptionId);
        router.push('/welcome');
      }}
      onCancelled={() => {
        router.push('/pricing');
      }}
    />
  );
}

Props reference#

PropTypeDefaultDescription
tenantSlugstring(required)Your tenant slug
embedKeystring(required)Embed key from Embed Studio
bridgeTokenstring(required)Short-lived bridge token
cartType"SUBSCRIBE" | "INVOICE_PAYMENT"(required)Cart variant
targetIdstring(required)Offering ID (SUBSCRIBE) or invoice ID (INVOICE_PAYMENT)
onCompleted(payload) => voidCallback after server-side confirm succeeds
onCancelled() => voidCallback after customer cancels
theme"auto" | "light" | "dark""auto"Color scheme
themeOverridesPartial<ThemeTokens>?Per-widget theme tuning
localestring?navigator.languageLocale for currency formatting

Payment providers#

The widget supports three payment providers — selected by your tenant's configured primary gateway in Aforo (Storefront → Payment Providers):

  • Stripe — Payment Element via js.stripe.com/v3 iframe. Supports Cards, Apple Pay, Google Pay, ACH, SEPA. 3DS challenges flow through Stripe's hosted challenge page in a new tab.
  • Razorpay — Drop-in via checkout.razorpay.com. Supports Cards, UPI, NetBanking, Wallets. Primary for INR payments.
  • PayPal — Smart Payment Buttons via www.paypal.com. Approval flow runs in a popup; the widget listens for aforo:payment-challenge-completed when the popup returns.
WARNING
The widget renders the gateway iframe with sandbox="allow-scripts allow-same-origin allow-forms allow-popups"— deliberately omitting allow-top-navigation so a compromised provider iframe cannot redirect your page. This is iframe-busting defense per FR-SEC-15.

Events#

CheckoutFlow emits a rich event sequence. Notable privacy guarantee: aforo.checkout.customer_details_submitted contains booleans only — never raw address values, never the customer's name or email. The widget verifies this contract in its test suite to prevent regressions.

See the for the full envelope shape and the SSE-pushed aforo.payment.failed event the widget consumes during the payment phase.

Limitations#

  • 3DS / SCA inline modal is Phase 1. v0.1.x falls back to the gateway-hosted 3DS challenge in a new tab. The widget waits for aforo:payment-challenge-completed to resume the flow.
  • Coupon code input is Phase 1. The server-side V49 coupon engine ships today; the customer-facing input lands later.
  • Tax preview is Phase 1. The widget shows the subscription price excluding tax — tax appears on the resulting invoice. Inline tax preview at cart- create time is filed.
  • NET 30 "pay later" flow. Deferred until first B2B customer asks. Today, all carts settle at confirm time.