examples/storefront Public
---id: payment-webhooktype: conceptowner: justincomponent: stripe-webhookdescribes: - workers/stripe-webhook/src/**reviewed: 2026-10-05---# The payment webhookAn order is paid when Stripe says so, never when the browser says so. Stripe sends events to `https://<stripe-webhook host>/stripe/webhook`, and the `storefront-stripe-webhook` Worker turns them into order changes.## What it does1. Verifies the `stripe-signature` header against `STRIPE_WEBHOOK_SECRET` with Web Crypto. A bad or missing signature gets a 400 and changes nothing.2. For `payment_intent.succeeded`, calls `OrdersService.markPaid(order, intent)` on storefront-api. The order id comes from the intent's metadata, which checkout set ([[checkout]]).3. For `payment_intent.canceled`, calls `OrdersService.cancel(order)`, which puts the stock back.4. Answers 200 to everything else, including events for intents without an `order_id`, so Stripe stops retrying them.The Worker has no database binding. It reaches orders only through the service binding `ORDERS` ([[0006-one-writer-for-the-database]]).## Retries and repeatsStripe delivers each event at least once and retries anything that does not get a 2xx. Both calls are safe to repeat:- `markPaid` only moves an order from `pending_payment` to `paid`, and only when the intent id matches the one stored at checkout.- `cancel` puts stock back in the same batch that cancels the order, and only while the order is pending.If OrdersService throws, the webhook answers 500 and Stripe tries again later.## A payment for a cancelled orderIf an order was cancelled and its payment still succeeds, `markPaid` leaves the order cancelled and the Worker logs `payment for a cancelled order, refund it` as an error. That should not happen once unpaid orders cancel their intent first, but the log line is there to search for.## Local testing`stripe listen --forward-to localhost:8788/stripe/webhook` prints a webhook secret for `.dev.vars` and forwards test-mode events. Run the Worker with `npx wrangler dev -c workers/stripe-webhook/wrangler.jsonc --port 8788` next to storefront-api.