# Cart and checkout (https://prood.com/docs/apps/storefront/cart-checkout)

The storefront cart uses a **cookie-backed cart ID** with BFF routes for client-side operations, and a **server action** to place orders and redirect to the checkout app.

## Cart lifecycle [#cart-lifecycle]

### 1. Cart creation [#1-cart-creation]

When a customer first adds an item:

```mermaid
sequenceDiagram
  participant C as Client
  participant BFF as Storefront BFF
  participant API as Commerce API

  C->>BFF: POST /api/commerce/cart/items
  BFF->>BFF: Read commerce_cart_id cookie
  alt No cookie
    BFF->>API: POST /v1/carts
  else Has cookie
    BFF->>API: GET /v1/carts/{id}
  end
  BFF->>API: POST /v1/carts/{id}/items
  BFF->>BFF: Set HTTP-only cookie
  BFF-->>C: Updated cart JSON
```

### 2. Cart cookie [#2-cart-cookie]

Defined in `lib/cart-cookie.ts`:

| Property | Value |
| --- | --- |
| Name | `commerce_cart_id` |
| HTTP-only | Yes |
| Max-Age | 30 days |
| SameSite | Lax |
| Secure | Yes (production) |

The cookie stores only the cart ID — not cart contents. Contents are always fetched from the API.

### 3. Cart operations [#3-cart-operations]

| Action | BFF route | API endpoint |
| --- | --- | --- |
| Read cart | `GET /api/commerce/cart` | `GET /v1/carts/{id}` |
| Add item | `POST /api/commerce/cart/items` | `POST /v1/carts/{id}/items` |
| Update quantity | `PATCH /api/commerce/cart/items/{itemId}` | `PATCH /v1/carts/{id}/items/{itemId}` |
| Remove item | `DELETE /api/commerce/cart/items/{itemId}` | `DELETE /v1/carts/{id}/items/{itemId}` |

### 4. Client state [#4-client-state]

`CartProvider` (React context) maintains client-side cart state for instant UI updates. It syncs with BFF routes on mount and after mutations.

## Checkout flow [#checkout-flow]

### Step 1 — Checkout page [#step-1--checkout-page]

The `/checkout` page renders `CheckoutFlow` which collects:

- Customer email, first name, last name, phone
- Shipping address (street, city, country, postal code)
- Optional billing address
- Shipping method selection (from API `GET /v1/carts/{id}/shipping-methods`)

### Step 2 — Place order (server action) [#step-2--place-order-server-action]

`app/(checkout)/checkout/actions.ts` → `startCheckout()`:

1. PUT shipping and billing addresses on the cart via the Commerce API
2. Set the shipping method the shopper chose; a cart with items that ship cannot be ordered without an address and a method, and a cart without any skips that step
3. `POST /v1/carts/{id}/place-order` — creates order with internal `customerId` (guest UUID or logged-in buyer)
4. `POST` checkout app `/api/sessions` with `orderId`, `customerId`, amount, `tenantId`, and `returnUrl` (no email in Redis metadata)
5. Redirect client to `checkoutUrl` from the session response

Email for payment (Stripe receipt) is collected at the checkout app pay boundary, not stored in the Redis session snapshot.

```ts
body: JSON.stringify({
  orderId: order.id,
  customerId: order.customerId,
  amount: order.totals.total.amount,
  currency: order.totals.total.currency,
  tenantId,
  returnUrl: `${storefrontOrigin}/order-confirmation?orderId=${order.id}`,
  providerId,
  fulfillment: "none",
})
```

### Step 3 — Payment (checkout app) [#step-3--payment-checkout-app]

The customer is redirected to `{CHECKOUT_URL}/c/{sessionId}`. See [Checkout app](/docs/apps/checkout) for payment UI details.

### Step 4 — Return to storefront [#step-4--return-to-storefront]

After successful payment:

1. Checkout app redirects to `/confirm/{sessionId}`
2. Success page redirects to storefront `returnUrl` (`/order-confirmation?order={id}`)
3. Order confirmation page fetches order details from API

## Error handling [#error-handling]

| Error | Handling |
| --- | --- |
| Cart not found when adding an item | BFF creates a new cart and retries; any other refusal leaves the cart as it was |
| Out of stock, or more than is left | API returns 422; client shows toast |
| Checkout session creation fails | Server action throws; checkout page shows error |
| Payment failed | Checkout app shows retry UI; order stays `pending` |

## Security considerations [#security-considerations]

- Cart cookie is HTTP-only — not accessible to JavaScript (XSS protection)
- Checkout session creation requires `x-checkout-secret` — only the storefront can create sessions
- Tenant ID is resolved server-side — never accepted from client input
- Order amounts are validated server-side by the API — not trusted from the client

## Related pages [#related-pages]

<Cards>
  <Card title="Checkout app" href="/docs/apps/checkout" description="Payment UI, sessions, and webhooks." />
  <Card title="Checkout flow (architecture)" href="/docs/architecture/checkout-flow" description="State machine and payment lifecycle." />
  <Card title="Commerce API — carts" href="/docs/api" description="OpenAPI reference for cart endpoints." />
</Cards>
