Skip to content

Stripe webhook problemStripe shows the webhook delivered, but my database never updated

The Stripe Dashboard is green: the event was delivered and your endpoint returned 200. But the order isn’t marked paid, or the account never upgraded. The 200 tells Stripe your endpoint answered, not what your code did next. Here’s where the update goes missing, or hand it over at a fixed price.

Short answer

A 200 only means your endpoint received the event. Stripe asks you to answer quickly, before the slow work, so if the database write fails after that, Stripe still records a successful delivery and has nothing to retry. Your own server logs hold the error. Check them first, then the event types your endpoint receives, repeated or out-of-order events, and the database key your handler writes with.

Where the update goes missing

  1. 01

    The write fails after the 200, and nothing logs it

    “Configure your handler to process incoming events with an asynchronous queue.” Stripe, Webhooks

    The handler answers Stripe first and does the work afterwards, so an error in the database write never reaches Stripe. Log every handler error and get alerted on it. Stripe recommends an asynchronous queue for handling spikes, and a queue is also a natural place to retry a failed write yourself.

  2. 02

    The endpoint doesn’t receive the event your code handles

    “Select the event types you want to receive.” Stripe, Webhooks

    Each endpoint receives only the event types selected for it. If your code acts on checkout.session.completed but the endpoint was set up for other events, what you see delivered isn’t what your code waits for. Stripe’s own example handler returns 200 for event types it doesn’t handle, so a mismatch still looks green. Compare the endpoint’s event list in the Dashboard with your code.

  3. 03

    Events arrive out of order, or twice

    “Stripe doesn't guarantee the delivery of events in the order that they're generated.” Stripe, Webhooks

    An invoice.paid event can arrive before the subscription your code expects to update, and the same event can arrive more than once. Record the event IDs you’ve processed and skip repeats, fetch a missing object from the API instead of assuming it exists, and don’t use the created time to order events.

  4. 04

    The handler works from an old copy of the object

    “You can also retrieve the API resource from the Stripe API to access the latest and up-to-date object definition.” Stripe, Webhooks

    An event carries the object as it was when the event happened. If your code needs its current state, such as a subscription that has changed since, retrieve it from the API before writing.

  5. 05

    In a Supabase app: row-level security blocks the write

    Check which key the handler writes with. With the publishable or anon key, row-level security applies to it like any visitor, and a policy can stop the update. A handler that runs only on the server can use the secret key, which must never reach the browser.

  6. 06

    It works in the sandbox but not in live mode

    Live mode has its own webhook endpoints and signing secrets. Work through the test-to-live checklist linked below.

Checked against the vendor’s docs on : Stripe docs: Receive Stripe events in your webhook endpoint; Supabase docs: Row Level Security.

Rather hand it over?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

Related: Stripe paid but account not upgraded · Stripe webhook signature error · Stripe works in test mode, not live · Supabase new row violates RLS · Supabase service_role key exposed

Questions

More on this error

Should I return an error so Stripe retries?

Stripe retries failed deliveries for up to three days in live mode, but it also wants a quick 2xx before slow work, or deliveries time out. The safer pattern is to answer quickly, queue the event, and retry a failed write from your own queue.

Can I resend the event once the bug is fixed?

Yes. Open the event in the Dashboard and click Resend, which works for up to 15 days after the event was created; the Stripe CLI can resend for up to 30 days. Make sure your handler skips events it has already processed first.

Can you make the webhook reliable for me?

Your Stripe and database accounts stay with you. We add error logging, duplicate protection and the missing event handling as a pull request you review and deploy, with any Dashboard changes listed in order.

Can you fix it for me?

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way.

A different error?

Describe it and get a written quote

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; we reply by email, usually within one business day.