← Back to blog

A customer just paid you and is staring at a paywall

September 3, 2026

This is the failure mode that generates support tickets within minutes, not the one that quietly costs money for months unnoticed. Someone pays, Stripe redirects them back to your app, and your app still shows them the paywall — because the thing that was supposed to grant access hasn’t run yet, or never runs at all.

Two different bugs that look identical to the customer

1. Granting access on the redirect, not the webhook

It’s tempting to grant access as soon as Stripe Checkout redirects the customer to your success_url — the URL itself feels like confirmation. It isn’t. The redirect fires from the customer’s browser, independent of whether your backend has processed anything yet. If someone closes the tab a second early, refreshes, or the redirect races your webhook, you can end up granting access to a session that never actually completed, or — the more common version — showing the paywall on that redirected page because the webhook genuinely hasn’t arrived yet and nothing else granted access in the meantime.

2. The webhook that’s configured for test mode and nowhere else

This one is almost boring, and it’s the one that actually happens most: a webhook endpoint gets added and tested against Stripe’s test mode, works perfectly, ships. Nobody adds the matching endpoint under live mode in the Stripe dashboard, because it’s a separate configuration Stripe doesn’t forget to mention but is easy to miss if you set the account up in a hurry. Every real payment fires checkout.session.completed into a void. It doesn’t error — there’s simply no endpoint listening, so nothing about it shows up in your application’s logs at all. The only place it’s visible is Stripe’s own dashboard, under Developers → Webhooks, and only if you go looking.

Two-minute check: Stripe Dashboard → Developers → Webhooks. Confirm you have a registered endpoint under both Test mode and Live mode, and that the event list for each includes checkout.session.completed (or whichever event your access logic depends on). Then check the endpoint’s recent delivery attempts for anything marked failed.

Why this is worse than it sounds even when it’s rare

A silent billing-drift bug like a ghost customer costs money quietly. This one costs trust loudly — the customer who hit it is refreshing the page, then emailing support, then leaving a review, all within the hour. It doesn’t need to happen often to matter: a handful of people paying and hitting a paywall in their first five minutes is the kind of thing that shows up in your churn number disguised as “didn’t like the product,” when the actual product they never got to try was the problem.

The fix

What we built this for

I built Venwai after running into this exact gap. It connects to Stripe with a read-only key, polls a single endpoint you expose on your own app once a day, and diffs the two — then alerts you in Slack, Discord, Telegram, or email the moment they disagree. It catches both directions: customers who kept access after they should have lost it, and customers who paid and never got it in the first place.

Free while it’s in beta, no card required: venwai.com.