Skip to content
Braith.

Stripe payment succeeded, but the user has no access.

Check the payment, the entitlement stored for the right account, and the access decision on the next request. A checkout success page is not proof that all three agree.

Confirm what actually succeeded.

Check the correct Stripe account and live/test mode. Verify the relevant payment or invoice state server-side. For delayed payment methods, completing checkout may happen before funds are confirmed.

A successful browser redirect does not establish paid access. Stripe documents Checkout fulfillment and asynchronous payments; choose events and entitlement policy for your billing model.

Follow the event into your app.

Inspect delivery attempts for your registered endpoint. A failed response suggests a delivery or handler problem; a successful response only means your endpoint acknowledged the event.

Verify signatures using the raw request body. Durably accept work before acknowledging it, make processing idempotent and handle retries and out-of-order events. Follow Stripe’s webhook guidance.

Check which account was updated.

Trace a stable internal account ID from checkout creation to the entitlement record. A billing email may differ from the sign-in email, so matching only on email can update the wrong account or none at all.

Check the purchased price/product mapping, the database write result and whether subsequent reads use that same entitlement record.

Try the protected feature as that customer.

Inspect the server-side access decision and the account’s current entitlements. A stale session, cached account response or client-side plan badge can disagree with the authoritative record.

Verify the protected action, not just the badge. Do not fix this by trusting a success query parameter or allowing the browser to grant paid access.

The webhook returned 200. Why is access still locked?

A 200 response confirms receipt, not the entitlement update. Follow that event ID into your queue and worker. Check for skipped event types, swallowed exceptions, rolled-back writes and a job marked complete before the write finished.

Record the processing result separately from receipt. Before replaying an event, make sure processing is idempotent so a retry cannot grant duplicate credits or repeat another paid action.

The subscription is active. Why does the app still show Free?

Compare the Stripe customer and subscription IDs with the signed-in account, purchased product and server-side entitlement. Then test the protected action. If access works but the badge says Free, investigate the display or cache; if access is denied, investigate provisioning and the access check.

Choose a deliberate access policy for trials, renewals, payment delays and cancellations. A subscription status alone does not establish that your app saved the right entitlement. See Stripe’s subscription events and access guidance.

Reproduce the gap without taking a payment.

Our small local example deliberately accepts a payment result but skips the access update. Nothing throws; the customer still gets denied. The corrected path verifies the outcome.

curl -O https://app.braith.dev/examples/payment-access.mjs
node payment-access.mjs

The example uses an in-memory account and makes no network requests or charges. Read the full source.

Monitor the result after fixing the cause.

Use a shared task ID for verified payment, entitlement and feature-access evidence. Allow for expected delays. Missing browser evidence alone is inconclusive.

Get payment, retry and account-mismatch test cases before releasing your fix. Braith can monitor this payment-to-access task alongside your existing payment records and error tracker.

Keep checking after the fix.

Braith reviews sampled customer activity for likely problems, including flows you have not defined. Core outcome checks add context where you need it.

Connect with Pro · £59/monthTry Free

Connect first. Pay only when you explicitly activate a paid plan.