Stripe checkout in production is a different project than the demo
Redirecting to a Checkout Session and rendering a success page takes an afternoon. Handling the webhook that arrives before the redirect — or not at all — is the part that actually ships a store.
Hammad Iqbal
Software Engineer
A client asking for a real storefront, not another page-builder template, meant checkout had to survive the cases a demo never hits: the tab closed mid-redirect, the webhook that arrives twice, the payment that succeeds while the browser thinks it failed.
- The success page and the webhook are two separate sources of truth — trust the webhook.
- Webhooks are delivered at-least-once, not exactly-once, so the handler has to be idempotent regardless.
- An order that's 'paid but not fulfilled' needs a visible state, not just a database row nobody checks.
The redirect isn't confirmation of payment
A customer can close the tab after paying but before the success redirect finishes loading, which means anything wired to that redirect — order confirmation, inventory decrement, fulfillment — is trusting a page load that isn't guaranteed to happen. The webhook fired by Stripe's servers is the actual source of truth; the redirect is just a nicer experience for the customer who stuck around.
Webhooks are delivered at-least-once, not exactly-once
Stripe retries a webhook delivery if it doesn't get a fast 200 back, which means the same event can hit the handler twice. Without an idempotency check, that's a duplicated order or a double-charged inventory decrement the first time a retry overlaps a slow database write.
- Store the Stripe event ID and skip anything already processed
- Verify the webhook signature before trusting the payload
- Log every webhook received, even ones you ignore
Give the admin panel a 'paid, not yet fulfilled' state
Building the admin panel for product and order management, the useful state wasn't 'paid' or 'shipped' — it was the gap in between. An order stuck in that middle state for an hour is either a fulfillment bottleneck or a webhook that silently failed, and either way a human needs to see it, not discover it from a customer email.
The Stripe integration that survives production is the webhook handler, not the checkout button. Budget for it accordingly.
Have a project this kind of thinking applies to?
Tell me what you're building — I read every message myself.