Stripe webhook signature verification in Node.js
Stripe signs timestamp + '.' + raw body. If those bytes change, stripe-node says:
No signatures found matching the expected signature for payload
The three inputs
- Raw body. Not
req.bodyafterexpress.json(). Stripe-Signatureheader.- Endpoint signing secret starting with
whsec_. Notsk_live_.
The Dashboard whsec_ and the stripe listen secret are different keys.
Next.js App Router
import { doorbell } from 'doorbell-js'
export const POST = doorbell({
stripe: {
secret: process.env.STRIPE_WEBHOOK_SECRET,
on: {
'checkout.session.completed': async (event) => fulfill(event.payload),
},
},
})
Do not call req.json() first.
Express
Use preserveRawBody if express.json() already runs, or mount the route on express.raw(). Why express.json breaks HMAC.
doorbell HMAC the bytes, then parse. stripe-node constructEvent decodes to a UTF-8 string first. Future Stripe timestamps still verify, same as stripe-node.