【NestJS】stripe webhookの署名検証


概要

  1. 署名シークレットを取得
  2. リクエストに rawBody を追加する
  3. コントローラで request.rawBody より rawBody を取得する
  4. コントローラで stripe-signature より ヘッダの署名を取得する
  5. 署名の検証

ハマりポイント

署名の検証時に raw body が必要になるため、リクエストに rawBody を追加する必要がある

1. 署名シークレットを取得

  • 開発者の Webhook をクリック

  • エンドポイントを追加後、署名シークレットを取得

    • prefixwhsec_

2. リクエストに raw body を追加する

  • before

nest new [name] で生成した結果

main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();
  • after

リクエストに raw body を追加する

main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { json } from 'body-parser';

const cloneBuffer = require('clone-buffer');

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(
    json({
      verify: (req, res, buf, encoding) => {
        // リクエストに `raw body` を追加する
        if (req.headers['stripe-signature'] && Buffer.isBuffer(buf)) {
          (req as any).rawBody = cloneBuffer(buf);
        }
        return true;
      },
    }),
  );

  await app.listen(3000);
}
bootstrap();

3. コントローラから request.rawBodyrawBody を取得する

  • @Req を利用し、request を取得
controller.ts
@Req() request: Request
  • request から rawBody を取得する
controller.ts
(request as any).rawBody

4. コントローラで stripe-signature より ヘッダの署名を取得する

  • @Headers を利用し、stripe-signature ヘッダの値を取得
controller.ts
@Headers('stripe-signature') sig,

5. 署名の検証

stripe = new Stripe()
stripe.webhooks.constructEvent(
  (request as any).rawBody,
  sig,
  'whsec_...'
)

参考文献