express.json() breaks Stripe and GitHub webhook HMAC

express.json(), bodyParser.json(), and Next.js req.json() turn the body into an object. The HMAC is over the bytes on the wire.

What you see

No signatures found matching the expected signature for payload
Are you passing the raw request body you received from Stripe?

GitHub: X-Hub-Signature-256 misses. Slack: invalid_signature.

Keep json() for the rest of the app

import express from 'express'
import { doorbell, preserveRawBody } from 'doorbell-js'

const app = express()
app.use(express.json({ verify: preserveRawBody }))
app.post('/webhooks/stripe', doorbell({
  stripe: { secret: process.env.STRIPE_WEBHOOK_SECRET, onAny: async () => {} },
}).express)

Or isolate the route

app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), hooks.express)
app.use(express.json())

If app.use(express.json()) is above the webhook, the raw parser never sees the bytes.

Stripe signature page · Markdown