Skip to main content

1. Scaffold with the CLI

The CLI may authenticate you to discover or validate merchant resources, but it does not write a private credential into the generated browser app.

2. Configure public values

The API origin is configuration, not a credential. Use the sandbox origin and publishable key in non-production environments. These merchant and location variables are standalone/local overrides. The hosted template resolves the merchant from its controlled host/subdomain and the location from the [locationId] route.

3. Create separate browser and server clients

Browser code supplies a tab-scoped cart sessionStore and getAuthToken. Server Components create a client with the same explicit baseUrl and perform anonymous published reads only.

4. Follow the capability lifecycle

  1. Load published merchant, location, and menu data anonymously.
  2. Start an ordering session with a generated idempotency key.
  3. Store the returned cart capability in versioned sessionStorage, scoped by canonical API environment, merchant, and location.
  4. Let the SDK attach the capability, If-Match, and idempotency headers.
  5. On CART_CONFLICT, reload the cart before asking the shopper to retry.
  6. Clear the capability after claim, deletion, expiry, or terminal checkout handling.

5. Checkout

Ask the server which collection modes the current cart may use. This is a preflight only; submission repeats every authority and exposure check.
Render Pay online only when the ONLINE option is available. Render Pay at store only when PAY_AT_STORE is available, and use its server-returned Square, Clover, or Toast label to explain where payment will be collected.
Use includeCustomerContext only when a signed-in shopper still has a guest capability and the selected provider needs customer association. Omit it for guest checkout; a claimed cart uses customer authentication automatically. Render account-scoped Stripe Elements only when outcome.status is CUSTOMER_ACTION_REQUIRED, then poll checkout.getOrderSubmissionOutcome with the returned attempt id. Treat PROCESSING as retryable, render success only for PAID, and surface ACTION_REQUIRED without creating another order. For pay at store, submit the same payload with collectionMode: "PAY_AT_STORE" and omit savedPaymentMethodId. An ACCEPTED response confirms the order, not payment. Continue polling the same attempt while the selected POS supplies native tender evidence. Never render a POS browser payment form, provider-hosted checkout, saved card, or gift-card input for pay at store. If evidence is delayed, show Verifying payment — do not charge again rather than creating a second collection attempt.

6. Customer and receipt access

Clerk owns customer sign-in and session recovery. After Clerk is ready, supply its active-session getToken({ template: "crave-customer" }) callback as getAuthToken, keep getMerchantSlug, and never persist the customer JWT yourself. Receipt links use a purpose-limited fragment capability: remove the fragment immediately, keep the token in tab-scoped storage keyed by API environment, merchant, and receipt ID, and pass it through receipts.get.

7. Deploy

  • Configure the exact storefront origin in the API CORS allowlist.
  • Keep provider and private integration credentials on the API service.
  • Run tests, lint, type checking, a production build, and a static-bundle secret scan.
  • Verify a real published catalog read and a sandbox ordering flow before promoting production.
See Deployment and the Storefront SDK for the full contract.