Stripe webhook works locally but not on Vercel
A production deployment changes the URL, secret, runtime, and environment. Check them in that order.
1. Confirm the production endpoint
Your Stripe destination must use the current HTTPS production URL and the exact /api/... route. Preview URLs change; do not register one as the live endpoint.
2. Use the destination secret
The Stripe CLI prints a local whsec_ value. It is not the secret for the Dashboard or Workbench destination. Copy the destination-specific signing secret into Vercel Production environment variables.
3. Preserve the raw body
In Next.js App Router, call await request.text() before any JSON parser. Signature verification fails if whitespace or bytes change.
4. Return a fast 2xx
Commit durable state, acknowledge promptly, and move expensive work to a queue. Log event.id, event.type, and the result without logging customer payloads.
Need the tested implementation?
The Hookproof kit includes the typed router, persistent idempotency adapters, fixtures, and deployment runbook.
GET THE $29 KIT