Rebasely Beta: data may be purged

examples/storefront Public

Markdown · 42 lines · 2.5 KiB · at 39ab077

---id: checkouttype: conceptowner: justincomponent: storefront-apidescribes:  - src/checkout.ts  - src/orders.ts  - src/payments.ts  - migrations/0003_orders.sqlreviewed: 2026-10-07---# Checkout`POST /carts/:id/checkout` with an email and a shipping address turns an open cart into an order and returns what the browser needs to take the payment. The steps are in the checkout flow on the Architecture tab.## Taking stockStock is taken here and nowhere else ([[0004-take-stock-at-checkout]]). The order, its lines, one `UPDATE products SET stock = stock - n` per line and closing the cart go to D1 as one batch. `products.stock` has `CHECK (stock >= 0)`, so when two shoppers race for the last pair of socks, the second batch fails as a whole and that shopper gets `409 out_of_stock` with what is left of each item. A first pass checks stock before the batch so the common case gets the same answer without a failed write.Lines keep the price that was in the cart. The shopper pays what the cart showed them.## ChargingAfter the batch, checkout asks the payment provider for a PaymentIntent for the order total ([[0005-stripe-payment-intents]]) and stores its id on the order. The response has the order and the intent's `client_secret`; the browser confirms the payment with Stripe directly, so card details never reach the Worker.If Stripe fails or times out, nothing has been charged, so the checkout is undone in one batch: the stock goes back, the order is deleted and the cart opens again. The shopper gets `502 payments_unavailable` and can retry.## RetriesChecking out the same cart again while its order is waiting for payment returns that order and the same client secret: the intent is created with the idempotency key `order-<id>`, so Stripe returns the existing one. Once the order is paid or cancelled, the cart answers `409 cart_closed`.## Order states| Status | Meaning || --- | --- || `pending_payment` | Created at checkout; stock is held for it || `paid` | Stripe reported the payment succeeded ([[payment-webhook]]) || `cancelled` | Not paid; the stock went back in the same batch || `fulfilled` | Handed to the warehouse ([[fulfilment]]) |State changes after checkout are in `src/orders.ts` (`markPaid`, `cancelOrder`) and only move forward from `pending_payment`, so repeating one is harmless. Each change is announced on the queue ([[order-events]]). `GET /orders/:id` returns the order. The id is a random UUID that only the shopper who checked out has.