Securealy
Check · Payments · high · local

Stripe webhook points at the live domain

A webhook endpoint exists for the production domain, is enabled, and subscribes to the checkout and payment events the code handles.

Severity
high
Tier
Local: runs on your machine with your own keys
Category
Payments
Fix
Summary below; full runbook in the Fix Kit, Module 04: Take money
Id
stripe-webhook-wiring

What this means

Passing means a Stripe webhook endpoint exists for your production domain, is enabled, is in the same mode as the key your site runs on, and subscribes to the checkout and payment events your code handles. Failing means one of those is missing: no endpoint, an endpoint pointing at a preview URL, a disabled one, or one that never sends the event your handler waits for. Payments still succeed; your app just never hears about them.

How we test it

This check runs on your own machine through the CLI: npx securealy check-stripe, which reads STRIPE_SECRET_KEY from your environment and never prints it. It lists your webhook endpoints and reports whether one points at the live domain, whether it is enabled, whether it subscribes to the checkout and payment events the code handles, and whether the key's mode matches the domain. Pass is all four true. Warn is a correct endpoint missing one or more events. Fail is no endpoint for the production domain, or a mode mismatch.

Why it matters

Without it payments succeed and nothing is delivered. Stripe is the ledger of record and your own table is what the app enforces; the webhook is the only thing that writes one into the other. A webhook that never arrives, because the endpoint was registered against a preview URL or the secret came from the other mode, leaves someone who paid on the free plan, and they email you, or they do not and they churn. Every delivery that fails signature verification shows up as a quiet 400 in the Stripe dashboard and nowhere in yours.

The fix

Register an endpoint at https://YOUR_DOMAIN/api/webhook in the Stripe dashboard (Developers, Webhooks) or with the registration script in Module 4 step 6, subscribed to exactly the seven events templates/webhook.js handles (checkout.session.completed, the three customer.subscription events, invoice.paid, invoice.payment_failed, charge.refunded), once in test mode and again in live mode. Paste that endpoint's signing secret into the hosting environment as STRIPE_WEBHOOK_SECRET for the matching environment, Production for live, and confirm the handler reads the raw body and trims the secret. The full runbook is in the Fix Kit, Module 04.

Prompt for your AI

Paste this into the coding agent that built the app. It reproduces the finding and stops before changing anything; the fix steps are in the kit.

Run `npx securealy check-stripe` with STRIPE_SECRET_KEY loaded from .env.local and show me the output; never print the key.
Read the webhook handler (api/webhook.js or the route that verifies stripe-signature) and list every event.type it handles.
Compare that list with the events the CLI reports the endpoint subscribes to and tell me which are missing, which URL the endpoint points at, whether it is enabled, and whether the key is test or live.
Tell me which hosting environment holds STRIPE_WEBHOOK_SECRET, using `vercel env ls`, without printing any value.
Do not create, update or delete any webhook endpoint and do not change any env var; list what it would take to wire it correctly and stop.
Run once: curl -s -o /dev/null -w "%{http_code}\n" -X POST https://YOUR_DOMAIN/api/webhook -d '{}' and show me the output; expected: 400, which proves the handler verifies signatures.

Next

Grade your own URL

Every grade lists this check with its evidence for your site. Two free grades a day.