Fix · Paid users still locked out? Webhooks, paused hosting and AI bills

Customer paid on Stripe but account still shows free plan

The charge succeeded in Stripe, but your database still holds the free plan, so a paying customer stays gated. Usually the event that should grant access either did not reach your handler or reached it and was ignored.

Likely causes, most common first

Cause 1 · in 1 of 3 matching cases

The webhook endpoint is disabled, failing or rejecting requests

Stripe tells your app about a payment by sending an event to your webhook endpoint. Stripe counts redirect responses as failures, and the endpoint has to return a 2xx quickly. If your framework alters the request body, signature verification fails. In live mode Stripe retries for up to three days. If the endpoint was disabled or deleted when a retry came due, Stripe stops retrying that event.

How to tell: The Event deliveries tab for the endpoint lists the paid event as Failed or Pending.

Cause 2

The handler returns 200 but does not act on the event that carries the payment

A Checkout payment creates checkout.session.completed. A delayed payment method sends checkout.session.async_payment_succeeded later. A subscription renewal arrives as invoice.paid, and Stripe says you can provision access when that event arrives and the subscription status is active. If your handler matches a different event type or skips the payment_status check, it can return 200 and leave the plan unchanged.

How to tell: The delivery shows 200 but the plan column did not change, and your handler logs show an unhandled event type or an early return.

Cause 3

Access is granted from the landing page or depends on event order

The page a customer returns to after paying may not load, for example if they lose their connection. Stripe says customers aren't guaranteed to visit it. Stripe also doesn't guarantee event order and can deliver an event more than once. If your handler expects customer.subscription.created before invoice.paid, it can look up a row that does not exist yet.

How to tell: Affected customers upgrade after you resend the event by hand, and your logs show invoice.paid arriving before the subscription row exists.

Check and fix it, step by step

  1. Open the event deliveries for your endpoint

    In Workbench, select the endpoint under Webhooks and open the Event deliveries tab. Find the checkout.session.completed or invoice.paid event for the affected customer. Note whether it is Delivered, Pending or Failed, and its HTTP status code.

    Docs: docs.stripe.com →

  2. Match the status code to Stripe's fix list

    Stripe counts redirect responses as failures, so check that the registered URL does not redirect. For a 4xx, make sure the endpoint is publicly accessible and accepts POST. For a 500, read your application logs. For a timeout, return a 2xx before the slow work.

    Docs: docs.stripe.com →

  3. Verify signatures against the raw body and the right secret

    Verify the Stripe-Signature header using the raw request body and the signing secret of that endpoint. Make sure your framework does not modify the raw body before verification. If you moved from test to live, confirm the handler uses the secret of the live endpoint.

    Docs: docs.stripe.com →

  4. Check which events grant access for one-off payments

    The handler should call your grant function on checkout.session.completed and on checkout.session.async_payment_succeeded for delayed payment methods. Check the payment_status property of the Checkout Session before granting.

    Docs: docs.stripe.com →

  5. Grant subscription renewals from invoice.paid

    On invoice.paid, find the customer, retrieve the subscription and confirm its status is active before extending the access expiry date. Revoke access when the status becomes canceled or unpaid.

    Docs: docs.stripe.com →

  6. Resend the events your endpoint missed

    After fixing the endpoint, resend each missed event from the Dashboard (up to 15 days after creation) or with the CLI (up to 30 days). A manual resend doesn't cancel automatic retries, so the grant must tolerate a repeat.

    stripe events resend <event_id> --webhook-endpoint=<endpoint_id>

    Docs: docs.stripe.com →

  7. Make the grant safe to repeat and to run out of order

    Log the event IDs you have processed and skip repeats. Make the grant function safe to call several times for the same Checkout Session ID, even concurrently. If an event arrives first, retrieve the missing objects through the API instead of assuming an order.

    Docs: docs.stripe.com →

Quick check: Workbench > Webhooks > endpoint > Event deliveries: is the paid checkout.session.completed or invoice.paid event Failed or Pending?

How often this shows up in our data

3 of the 937 verified cases from the last 12 months in our data match this symptom (0.3%). The most common cause was “The webhook endpoint is disabled, failing or rejecting requests” (1 of 3); 2 didn't show which cause. How we collect and verify cases.

Questions

Why does Stripe show the payment but my app still shows the free plan?

Stripe reports a payment to your app through webhook events, and your app has to act on them. If the endpoint returned a non-2xx status, or the handler did not grant access for that event, the plan stays as it was.


How long does Stripe retry a failed webhook?

In live mode Stripe attempts delivery for up to three days with exponential backoff. If the destination has been disabled or deleted when a retry comes due, Stripe stops retrying that event.


Can I replay an event after the retries have stopped?

Yes, within limits. The Dashboard Resend button works for up to 15 days after the event was created, and stripe events resend works for up to 30 days.


Is it enough to grant access on the success page after checkout?

Not on its own. Stripe recommends triggering fulfillment from the landing page as well, so a customer who is present gets access straight away, but customers aren't guaranteed to visit that page. Set up a webhook handler so Stripe can send payment events directly to your server.


Sources

Every cause and step above was checked against these pages on 10 Nov 2026.

The broader pattern

This is one symptom of a wider failure pattern: Paid users still locked out? Webhooks, paused hosting and AI bills. The guide covers every cause we see for it, on any stack.

Get a heads-up when Stripe breaks something

Stripe

Leave your email and we'll let you know when something big changes for Stripe. Unsubscribe any time by replying. Gemmein Limited. Research terms · Privacy

← All fixes