← StripeCheckup

Vibe-coded a Stripe integration? Here's the layer your security scanner doesn't check

If you built your app in Lovable, Bolt, Replit, v0, or Base44, you've probably already heard the security warnings: unpatched Row Level Security policies, service-role keys leaking into the browser bundle, missing auth checks on generated CRUD endpoints. That's real. Multiple scanners now exist just for that — they connect to your Supabase project or crawl your deployed URL and flag exactly those gaps in a couple minutes for a few dollars.

None of them touch Stripe. Not because it's less important — because it's a different layer entirely. RLS and auth scanners can tell you your database is locked down tight. They have no way to tell you that a customer paid, Stripe fired the event, and your webhook handler silently never ran, or ran but didn't cover the specific event type that just happened. That bug doesn't live in your database. It lives in the gap between what Stripe sent and what your code was listening for.

Here's what that gap actually looks like in practice, from having shipped it three times myself across five separate Stripe integrations this year:

7 things a Stripe integration built fast usually gets wrong

  1. Webhook-only fulfillment, no fallback. If the only thing that ever marks an order "paid" is a webhook delivery, one dropped delivery (Stripe retries, but not forever) means a real customer paid and your app has no record of it. There should always be a second path — polling Checkout Sessions, or checking on next login — that catches what the webhook missed.
  2. Listening for subscription.deleted but not subscription.updated. When a card fails and your dunning settings mark a subscription unpaid or past_due instead of canceling it outright, that's an updated event, not a deleted one. A handler that only reacts to cancellation will leave a non-paying customer with paid access indefinitely.
  3. Payment Links with no metadata. Fast-built payment links often ship with zero custom fields — a sale comes through with no way to tell what the buyer actually wanted, if you're selling more than one thing.
  4. No idempotency check on the webhook handler. Stripe can and does deliver the same event more than once. A handler that isn't idempotent will double-fulfill, double-email, or double-charge internal credit on a retry.
  5. A dev webhook URL still enabled in production. An ngrok tunnel or localhost endpoint left registered in the Stripe dashboard after launch means every event since has gone nowhere, and nothing tells you that's happening.
  6. Checkout Sessions with no customer record attached. A session can complete payment without ever getting linked to a user in your own system, if the redirect/session-id handoff has a gap. Money in, no fulfillment.
  7. An API key with more access than the integration needs. Fast-built integrations often reuse one key everywhere instead of scoping a restricted key per use case — not a Stripe-specific issue, but the same instinct that causes the RLS problem shows up here too.

None of these require reading your code. They're all visible from Stripe's own API, if you know to check the right things: your actual event history, your webhook config, your subscription statuses, your payment link fields. That's what StripeCheckup does — paste a read-only key, get a report in under a minute, $49 one-time. There's also a free 3-check version on the homepage if you just want the webhook-coverage check without paying anything.