DEV Community

ghost
ghost

Posted on Originally published at ghostnoteai.dev

Our first customer paid four hours after launch, and the app gave him nothing

Our first paying customer arrived four hours after we flipped the switch on launch day. Stripe showed his $9.99 subscription processed cleanly. Money moved, receipt sent. The app, meanwhile, kept him on the free tier like nothing had happened.

The bug: one config flag

The plan upgrade happens in a Supabase Edge Function that listens for Stripe's webhooks. That function was deployed with verify_jwt = true. A sensible default for endpoints your signed-in users call, and exactly wrong for a webhook: Stripe's servers don't carry a Supabase JWT, so every event was rejected with a 401 before a single line of our code ran.

The fix was one line. Turn off JWT verification for that endpoint and authenticate the webhook the way Stripe intends, by verifying Stripe's signature on the payload.

The decision

That still left a customer who had paid and received nothing for half an hour. Two options: refund him and ask him to try again, or make it right unilaterally. We comped him a lifetime license as customer number one. He's still around.

The bonus find

Reading the same code in a panic surfaced something worse than the bug we were looking for: a row-level security gap that would have let any logged-in user update the plan column on their own row. Effectively plan='lifetime' for free, no Stripe involved. Never exploited, closed the same evening.

If you sell through Supabase

  • Webhook endpoints don't get JWTs. Disable verify_jwt on them and verify the provider's signature instead. The 401 happens before your code runs, so no log you wrote will show you why.
  • Audit RLS on every column a user could want to change. Plan, role and credit fields especially. Deny by default; only your service role touches entitlements.
  • Run a real dollar through production before launch. Test mode exercises your code. It does not exercise your configuration, and configuration is what failed here.

Originally published on the GhostNote blog. GhostNote is an invisible AI overlay for Windows and Mac.

Top comments (0)