The short story
You are using NestJS and want to verify Stripe Webhook signature but your are facing the following error
Error: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing
Stripe needs the raw payload to validate the signature, but all your payloads are parsed with Nest, tricky one :(
My solution is to clone the payload with clone-buffer
To do so I use the body-parser
lib with the verify options.
Check the long story for full detail
The long story
So you are building your app smoothly with NestJS (https://docs.nestjs.com), and you enjoyed it so far :) You included Stripe (https://stripe.com/) to handle all your payment features, and yes they’re good at it. All is smooth, and it rocks.
Yeah I know, being a developper is soooo easy ;) and building apps is of course always an easy path.
But then you need to add an entry point for Stripe Webhooks (https://stripe.com/docs/webhooks) Ok, easy, follow the doc: https://stripe.com/docs/webhooks/signatures
@Controller('webhook/stripe')
export class StripeController {
stripe: Stripe;
constructor() {
if (!process.env.STRIPE_SECRET_KEY) {
throw new Error('Missing env var STRIPE_SECRET_KEY');
}
if (!process.env.STRIPE_ENDPOINT_SECRET) {
throw new Error('Missing env var STRIPE_ENDPOINT_SECRET');
}
this.stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
}
@Post('/')
async stripeChange(
@Headers('stripe-signature') signature,
@Body() body: any) {
let event;
try {
event = this.stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_ENDPOINT_SECRET);
} catch (err) {
throw new BadRequestException();
}
// your process here
}
}
Easy ? Well no, this is what I got :(
Error: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing
Reasons ? Stripe needs the raw payload (request body) of the POST request. Ok, lets use the expressjs request.rawBody then ? → NO, req.rawBody is no longer available since 1.5.1 ;)
My solution: use body-parser
verify option param to clone the payload with clone-buffer
import { json } from 'body-parser';
const cloneBuffer = require('clone-buffer');
app.use(json({
verify: (req: any, res, buf, encoding) => {
// important to store rawBody for Stripe signature verification
if (req.headers['stripe-signature'] && Buffer.isBuffer(buf)) {
req.rawBody = cloneBuffer(buf);
}
return true;
},
}));
Then update your controller to use the request.rawBody as follow
@Post('/')
async stripeChange(@Headers('stripe-signature') signature, @Req() request: any) {
let event;
try {
event = this.stripe.webhooks.constructEvent(request.rawBody, signature, process.env.STRIPE_ENDPOINT_SECRET);
} catch (err) {
throw new BadRequestException();
}
// your process here
}
}