Checking Stripe Webhook Signatures from NestJS

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

stripe.controller.ts
@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

main.ts
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

stripe.controller.ts
	@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
	}
}