FIX-01 · LOVABLE · STRIPE · SUPABASE
Lovable app: Stripe payment succeeded but nothing happens
The money arrived, Stripe says succeeded — and your app acts like nothing happened. Here's the missing link, and how to check it yourself in about ten minutes.
Updated July 2026 · 5 min read · free self-serve checks inside
Your customer paid. Stripe shows the charge, they got the receipt email, the money is on its way to your bank. But your app didn't react: no unlocked content, no order created, no plan upgrade. From the customer's side, it looks like you took their money and gave them nothing.
If your app was built with Lovable — or any AI builder on the Supabase + Stripe stack — this is the single most common payment bug, and it has one root cause far more often than any other.
The root cause: your app never hears about the payment
Stripe Checkout runs on Stripe's servers, not yours. When a customer pays, the payment succeeds inside Stripe. Your app finds out exactly one way: Stripe calls a URL on your backend — a webhook — and your code updates the database. No webhook, no update. The payment is real, and your app is deaf to it.
Lovable is genuinely good at generating the visible half: the pricing page, the checkout button, the redirect to Stripe. The invisible half — registering a webhook endpoint, verifying its signature, writing the result into Supabase — is where generated code most often stops short, half-finishes, or works in preview and dies in production.
Check 1: confirm the payment is real (1 minute)
In the Stripe Dashboard, open Payments. Find your customer's charge and confirm it shows Succeeded. If it's there, everything below applies. If it isn't, your problem is in checkout itself, not the webhook — the dead buy button guide covers that path.
Check 2: does a webhook endpoint exist at all? (2 minutes)
Open the Developers area (bottom-left of the Dashboard — Stripe now calls it Workbench) and select the Webhooks tab. You're looking for an endpoint whose URL points at your app's backend. For Lovable apps that's usually a Supabase Edge Function:
https://YOUR-PROJECT-REF.functions.supabase.co/stripe-webhookIf the list is empty, you've found the root cause: Stripe has nowhere to send the news. Nothing in your app's code can matter until an endpoint exists here.
Check 3: the endpoint exists — is it failing? (5 minutes)
Click the endpoint and look at its recent event deliveries. Every delivery shows the HTTP response your server gave. Three codes cover most Lovable cases:
- 401 Unauthorized — the classic Supabase one. Edge Functions require a Supabase login token (JWT) by default, and Stripe doesn't have one, so Supabase rejects the call before your code even runs.
- 400 Bad Request — usually signature verification failing: the
whsec_...secret your function holds doesn't match this endpoint (each endpoint has its own). - 500 / no response — the function crashed. Open Supabase → Edge Functions → your function → Logs, and read the last error.
The 401 has a clean, safe fix: tell Supabase that this one function should skip login checks — Stripe proves itself with its signature instead. In your project's supabase/config.toml:
[functions.stripe-webhook]
verify_jwt = false…then redeploy the function. The same setting exists as a toggle in the Supabase dashboard under the function's details. After changing it, hit Resend on a failed event in Stripe and watch the response code change.
Check 4: did the database actually update?
A 200 response is not the finish line. Open Supabase → Table Editor and look at the table your app checks for access — profiles, subscriptions, orders, names vary. Find the paying customer's row and see whether anything changed at the time of the payment. Delivered-but-never-written is its own failure mode, covered in the locked-out customers guide.
If the checks found it — and if they didn't
If your webhook list was empty, or a 401 was blocking everything, the checks above may genuinely be all you needed. Resend the failed events, watch them go green, and manually unlock anyone who paid during the outage — Stripe keeps every missed event, so nothing is lost.
If the endpoint exists, returns 200, and the database still doesn't move — or you're not comfortable redeploying functions on a live app — that's the harder version of this bug, and it's exactly the kind we take.
Diagnosed free. Fixed for a flat $250. Proven live.
01 · FREE HEALTH CHECK
Describe the symptom in two minutes. You get a plain-English root cause within hours — yours to keep even if you fix it yourself. No call, no strings.
02 · THE $250 FLAT FIX
One broken flow, fixed within 48 hours of access — proven with a recorded live test transaction you watch, and covered by a 30-day warranty. Miss the deadline, pay nothing.
QUICK ANSWERS
?Stripe says the payment succeeded. Why didn't my app update?
Because success happens inside Stripe, not inside your app. Your app only learns about a payment if a webhook endpoint is registered, reachable, and writing to your database. Any break in that chain leaves the payment real but invisible to your app.
?Where do I see webhook errors in Stripe?
In the Stripe Dashboard, open the Developers area (Workbench) and select the Webhooks tab, then click your endpoint. Every delivery attempt is listed with the HTTP response your server returned — 401, 400 and 500 each point to a different root cause.
?Is the redirect after checkout enough to unlock access?
No. The success-page redirect is cosmetic: customers can close the tab before it loads, and it doesn't update your database by itself. Reliable access unlocking has to come from a webhook handler.
?Did my customers' payments disappear while the webhook was broken?
No. The charges are all in Stripe, and Stripe keeps a log of every event it tried to deliver. Once the endpoint works, you can resend the missed events or manually grant access to the affected customers.