1. Scaffold with the CLI
2. Configure public values
[locationId] route.
3. Create separate browser and server clients
Browser code supplies a tab-scoped cartsessionStore and getAuthToken. Server Components create a client with the same explicit baseUrl and perform anonymous published reads only.
4. Follow the capability lifecycle
- Load published merchant, location, and menu data anonymously.
- Start an ordering session with a generated idempotency key.
- Store the returned cart capability in versioned
sessionStorage, scoped by canonical API environment, merchant, and location. - Let the SDK attach the capability,
If-Match, and idempotency headers. - On
CART_CONFLICT, reload the cart before asking the shopper to retry. - 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.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.
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-sessiongetToken({ 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.
