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

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.

Markdown version · GitHub