Diagnostic guide

lovable stripe subscription not working

Generated apps get the happy path right and the edges wrong. Checkout works, the first payment often works, and then the parts that only matter later (cancellations, refunds, plan changes) turn out never to have been wired at all. None of them announce themselves.

most likely causes, in order

01Only the creation event is handled+

how it looks

New subscriptions work. Cancellations do nothing. Someone who cancelled in August still has access.

how to check

Search your webhook handler for customer.subscription.deleted. In generated apps it is frequently absent.

the fix

Handle deleted, updated, and charge.refunded, not just checkout.session.completed.

02Service-role key missing in the webhook+

how it looks

The handler runs cleanly and updates nothing. Zero rows affected.

how to check

The webhook must use the service-role key. With the anon key, RLS silently drops the write: there is no authenticated user in a webhook.

the fix

Server-side only. If it can reach the browser, rotate it today.

03The endpoint points at a preview deployment+

how it looks

It worked while you were building and stopped after you shipped.

how to check

Compare the registered webhook URL against your production domain.

the fix

Update it, then replay the failed events.

04The access column and the gate disagree+

how it looks

subscribed is true, the UI shows free.

how to check

Read the actual condition in the dashboard component. Generated code often checks a different column than the one the webhook writes.

the fix

One source of truth, read everywhere.

05Test-mode keys in production+

how it looks

Real payments never arrive. Everything looks fine in testing.

how to check

Live keys start with sk_live. The webhook signing secret is per-endpoint and per-mode.

the fix

Separate both by environment.

check it yourself

The two states worth checking on a Supabase build. Run this in your SQL editor, adjusting the table and column names to match your schema.

-- paid, no access: they will email you
select email, stripe_customer_id, subscribed, created_at
from public.subscribers
where stripe_customer_id is not null and subscribed = false;

-- access with no active record: nobody will ever email you
select email, subscribed, subscription_tier, subscription_end
from public.subscribers
where subscribed = true
  and (subscription_end is not null and subscription_end < now());

what this does not tell you

The second query is the one worth running twice. Anyone it returns has been using your product for free since their subscription lapsed, and there is no version of that story where they tell you.

Read-only, both sides. Three findings free, no account.

related