Stripe webhook returned 200, but paid access still failed
A webhook can succeed at the HTTP layer while your app never grants the user paid access. Shipshape checks the resulting state, not just delivery.
A common SaaS launch bug is quiet: Stripe sends a webhook, your endpoint returns 200 OK, Stripe stops retrying, and the user still does not get paid access. The transport worked, but the business state failed.
Why 200 OK is not enough
Webhook tools can forward, replay, and log events. That helps delivery. It does not prove that your app updated the right user, subscription row, entitlement flag, or organization membership before returning success.
The state assertion pattern
A launch-state test sends a safe local/staging event, then checks the app's resulting state: for example, checkout.session.completed should produce subscription.status = active, or user.created should create a profile row.
How Shipshape handles it
In shipshape.yml, define the webhook URL and expected state endpoint. Shipshape triggers the test event only when you explicitly pass --run-assertions, then marks the Paid State Gate as pass, fail, incomplete, or not configured.
FAQ
Why can a Stripe webhook return 200 but still be broken?
The handler may acknowledge the event before updating durable app state, or update the wrong user/row. Stripe sees success, but your app state is wrong.
Does Shipshape replace Stripe CLI or Hookdeck?
No. Those help delivery and replay. Shipshape checks the resulting app state after the event.
Related questions
- Supabase RLS Launch Proof checklist
- Gemini/API key wallet-drain checklist for AI apps
- What Shipshape proof packs prove — and what they do not
- Is Shipshape legit? What it does and doesn’t do