A Stripe webhook that doesn't check the signature lets anyone post a fake “payment succeeded”. Without a webhook at all, subscriptions never update when payments fail or are cancelled.

What you see

Subscriptions stay active after a failed payment, or the webhook route acts on any JSON posted to it.

Why it happens

The webhook route skips stripe.webhooks.constructEvent, the call that proves an event came from Stripe, or no webhook route exists.

How to fix it

  1. Verify every event with stripe.webhooks.constructEvent(rawBody, signature, STRIPE_WEBHOOK_SECRET) before acting on it. It needs the raw request body, not parsed JSON.
  2. Handle checkout.session.completed and the customer.subscription events, and update your database from them.

Or ask your AI tool to fix it

Paste this into Lovable, Bolt, v0, Replit or Cursor:

Make the Stripe webhook route verify every event with stripe.webhooks.constructEvent using the raw request body and STRIPE_WEBHOOK_SECRET, return 400 when verification fails, and update the database on checkout.session.completed and customer.subscription.updated and .deleted.

The full page, with the request ready to copy, is at launchpad.axonscape.com/fix/stripe-webhook-not-verified. I work on Launchpad by Axonscape, which runs a free launch check for apps built with AI tools: it finds this and the other things that break at launch, from a public GitHub repo or a .zip of the code, and keeps nothing.