> ## Documentation Index
> Fetch the complete documentation index at: https://docs.craveup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Checkout Flow

> Finalize a capability-protected cart, confirm payment, and poll the authoritative order result.

Checkout uses the same cart capability, revision, and idempotency protections as other cart mutations. Creating a payment intent is a write and is never an anonymous lookup by cart ID.

## 1. Finalize the cart

```ts theme={null}
await storefront.cart.update("loc_123", cartId, {
  fulfillmentMethod: "takeout",
});

await storefront.cart.updateOrderTime("loc_123", cartId, {
  pickupType: "ASAP",
});

await storefront.cart.validateAndUpdateCustomer("loc_123", cartId, {
  customerName: "Alex Johnson",
  emailAddress: "alex@example.com",
  phoneNumber: "+1234567890",
});
```

Each call uses the latest cart revision. If a call returns `CART_CONFLICT`, refresh the cart and require an explicit retry.

## 2. Create the payment session

```ts theme={null}
const payment = await storefront.checkout.createPaymentSession(
  "loc_123",
  cartId,
  { idempotencyKey: crypto.randomUUID() },
);
```

```bash theme={null}
curl -X POST "https://api.craveup.com/api/v1/storefront/locations/loc_123/carts/cart_456/payment-session" \
  -H "X-Cart-Token: $CART_ACCESS_TOKEN" \
  -H 'If-Match: "cart-4"' \
  -H "Idempotency-Key: payment_01"
```

Branch on `payment.provider`. Pass a Stripe `clientSecret` to Stripe Elements, or initialize Square Web Payments with its application and location IDs. Raw card data and provider secrets never pass through storefront code.

## 3. Confirm payment

```tsx theme={null}
const { error } = await stripe.confirmPayment({
  elements,
  confirmParams: {
    return_url: `${window.location.origin}/order/confirmation`,
  },
});
```

Do not add the cart ID or capability to `return_url`. Keep the cart session in tab-scoped storage so the returning page can continue securely.

## 4. Poll the order result

```ts theme={null}
const result = await storefront.checkout.getOrderResult("loc_123", cartId);

switch (result.state) {
  case "payment_pending":
  case "order_pending":
    // Poll again with bounded backoff.
    break;
  case "completed":
    // Render result.order, then clear persisted cart capability state.
    break;
  case "failed":
    // Show a retry-safe failure using result.code, then clear terminal state.
    break;
}
```

Only `completed` is success. Never infer success from a redirect or from the absence of an error.

<CardGroup cols={2}>
  <Card title="Accept Payments" icon="money-bill" href="/guides/accept-payments">
    Integrate Stripe Elements without exposing server secrets.
  </Card>

  <Card title="Order Tracking" icon="location-dot" href="/guides/order-tracking">
    Implement bounded result polling and receipts.
  </Card>
</CardGroup>
