---
title: "Customer paid but their account was not upgraded"
description: "Stripe never reached your server, or the webhook failed its signature check, so the paid flag was never set. How to read Event deliveries and fix it."
url: https://whocanfixmyapp.com/problems/stripe-webhook-not-firing-paid-but-not-upgraded
---

[Home](https://whocanfixmyapp.com/) / [Problems](https://whocanfixmyapp.com/problems) / [Payments](https://whocanfixmyapp.com/problems#payments)

# Customer paid but their account was not upgraded

VC-32 · Stripe · Supabase · Updated October 2026 · By [Nathaniel Kelley](https://whocanfixmyapp.com/authors/nathaniel-kelley)

Quick verdict

Get an engineer for this one. Needs a server endpoint and a signature check.

- **How serious**: High
- **Fix it yourself?**: No, get an engineer
- **Typical cost**: €99–$990 for a single fix ([prices](https://whocanfixmyapp.com/pricing))
- **Top rated for Supabase**: [Redlab](https://whocanfixmyapp.com/companies/redlab) · **10.0**/10

**What you see.** Stripe shows a successful payment, but the customer's account still says Free and they email you asking for access.

## Why it happens

Stripe does not update your database. After checkout, Stripe sends a webhook, a signed message to an address on your server, carrying the event `checkout.session.completed`. Your code must receive it, verify the `Stripe-Signature` header with the `whsec_` signing secret, and then mark the account as paid. Stripe says you cannot rely on the success page, because the customer may lose their connection before it loads.

Three things break this chain. The endpoint was never registered in the Stripe dashboard, or only in the sandbox, and live mode has its own endpoints. The signing secret in your host's environment came from the CLI or the sandbox, so verification fails with a 400. Or the handler runs but updates the wrong user, because the checkout session never carried your user's id. Stripe retries failed deliveries for up to three days in live mode, so fixing the endpoint often recovers the missed events.

## Fix it yourself

1. In the Stripe dashboard, switch to live mode and open **Developers → Webhooks**. If no endpoint lists your live domain, that is the problem.
2. Click the endpoint and open **Event deliveries**. A 400 means signature verification failed, a 404 means the address is wrong, and no events means nothing is registered.
3. Click **Reveal secret** and compare the `whsec_` value with `STRIPE_WEBHOOK_SECRET` in your host's environment settings. They differ between sandbox and live.
4. Check the endpoint subscribes to `checkout.session.completed` and, for subscriptions, `invoice.paid` and `customer.subscription.updated`.
5. After fixing, click **Resend** on each failed event. Stripe allows this for 15 days. The customer should be upgraded within seconds.
6. Until then, upgrade affected customers by hand in your database and keep a list of who you touched.

## When to get an engineer

Hire now if customers have paid and are waiting, since every day adds refund requests and disputes. Tell the engineer your live endpoint address, where the app is hosted, and whether the webhook runs in a Supabase edge function or in your app's own server. A single fix at €99–$990 usually takes 1–3 days and should include tests for the paid flow, our item CODE-02, so the next deploy cannot silently break it again.

## Top-rated companies for Supabase apps

Ranked by our score: published prices, scope of work, AI tools named, timing and a low-risk first step. [How we score](https://whocanfixmyapp.com/methodology#score).

- 1 · [Redlab](https://whocanfixmyapp.com/companies/redlab) · **10.0**/10 · Best for: A full rescue by one team that keeps building · Free first step · from $490
- 2 · [Vibe Code Rescue (Lev Arakelyan)](https://whocanfixmyapp.com/companies/vibe-code-rescue-lev-arakelyan) · **9.0**/10 · Best for: A one-off check and a cleanup · From $299
- 3 · [LazyCodeLab](https://whocanfixmyapp.com/companies/lazycodelab) · **9.0**/10 · Best for: A one-off check and a cleanup · From $399

[All 29 Supabase companies, ranked](https://whocanfixmyapp.com/best/supabase-developers)

## Check these

PAY-01 · Critical

Paid accounts are unlocked by a verified payment webhook

Access should change only when your server receives a signed webhook from Stripe or your provider, not when the browser reaches a success page.

PAY-04 · Medium

Failed payments, cancellations and refunds are tested

Use your provider's test cards for declines and disputes, then check what the user sees and what access they keep.

CODE-02 · Medium

The flows that make money have tests

Sign-up, checkout and the core action. A few end-to-end tests catch most regressions from new prompts.

OPS-03 · High

Errors reach you before users do

Add error monitoring and uptime alerts. If you learn about outages from customers, this is missing.

OPS-04 · Medium

Secrets live in the host's environment settings

Keys belong in environment variables on the server, not in the repository or in front-end code.

## Questions

### Can I just upgrade the user on the success page instead?

Stripe says no. Customers can pay and never reach that page, and anyone can open the success URL without paying. Use the page for speed, but the verified webhook must be the thing that grants access.

### Why did it work in test mode?

Your sandbox endpoint and its signing secret were set up during development. Live mode needs its own endpoint and its own whsec_ secret, and the agent rarely creates those.

Sources: [Stripe: Fulfill orders](https://docs.stripe.com/checkout/fulfillment.md?ui=full-page); [Stripe: Receive events in your webhook endpoint](https://docs.stripe.com/webhooks); [Stripe: Go-live checklist](https://docs.stripe.com/get-started/checklist/go-live.md).

**Supabase guide**

What Supabase builds, where it breaks and how to check it: [Supabase RLS audit: how to check an AI-built app's database rules](https://whocanfixmyapp.com/guides/supabase).

**Same category**

- [Bolt app broke after I added payments: Stripe, webhooks and what to check](https://whocanfixmyapp.com/problems/bolt-app-broke-after-adding-payments)
- [Stripe is still in test mode on the live site](https://whocanfixmyapp.com/problems/stripe-still-in-test-mode-on-the-live-site)
- [Stripe checkout opens and then errors](https://whocanfixmyapp.com/problems/stripe-checkout-opens-then-errors)
- [Cancelled subscriptions still have access](https://whocanfixmyapp.com/problems/cancelled-subscriptions-still-have-access)
- [Stripe secret key is in my frontend code](https://whocanfixmyapp.com/problems/stripe-secret-key-in-the-frontend)

[All payments problems](https://whocanfixmyapp.com/problems#payments)

**Run every check**

The [production checklist](https://whocanfixmyapp.com/checklist) has all 24 checks with a verdict.
