Nest + FastifyでStripe Webhookを受け取る[2022年8月版]
TL;DR
StripeのwebhookをNestで受け取ろうとすると割と手間だった
StripeのWebhookを受け取って処理を走らせようとすると割と面倒臭かった上に正確な(?)情報がほぼなかったので備忘として書いてみる
下記をやれば良いよ!
- rawbodyでリクエストを受け取る
- 受け取ったリクエストのheaderから
stripe-signature
を取り出して検証する - 検証が終わったらデータをよしなに利用する
rawBodyでリクエストを受け取る
Nestはめちゃくちゃ便利なフレームワークなので、リクエストを受け取るとよしなにパースして取扱しやすくしてくれます。が、StripeのWebhookはsignatureの検証をするためにrawbodyが必要です。
わざわざめんどくさいことしやがってStripeめ
ググったらhapiの例しかなかったり、body-parser使えとか書いてあったりしてめちゃくちゃ試行錯誤したけどうまくいかない...
それっぽいissueを読んだりそれっぽいプラグインをためしたりしたもののうまくいかず...
ExpressはともかくFastifyでどうしたら...
fastify-rawbodyを使う?
などと1日を溶かして挫折して寝て翌朝重い気持ちで再度ググったら
公式に答えがあると言うオチがつきました
困ったときは公式ドキュメントを見てみよう(大反省)
と言うわけでmain.ts
を書き換えます
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter()
+ {
+ rawBody: true,
+ }
);
await app.listen(3000,'0.0.0.0');
これでrawbodyが受け取れるようになるので、コントローラーを作成します
+ import { Controller, HttpException, HttpStatus, Post, RawBodyRequest, Req, } from '@nestjs/common';
+ import { FastifyRequest } from 'fastify';
+ import { StripeService } from './stripe.service';
+ @Controller()
+ class StripeController {
+ @Post('webhook/stripe')
+ handle(@Req() req: RawBodyRequest<FastifyRequest>) {
+ const sig = req.headers['stripe-signature'];
+ if (!sig)
+ throw new HttpException('missing signeature', HttpStatus.BAD_REQUEST);
+ const buf = req.rawBody;
+ const whsec = 'whsec_xxxx';
+ const stripe = new Stripe('sk_test_xxxx', { apiVersion: '2022-08-01' });
+ try {
+ const event = stripe.webhooks.constructEvent(buf, sig, whsec);
+ // stripe webhookをお好みで処理する
+ return await this.stripeService.processStripeWebhook(event);
+ } catch (error) {
+ console.error(`[StripeError]:${error}`);
+ throw error;
+ }
+ }
+ }
これでstripeのwebhookを受け取っていい感じに処理ができます
決済結果を受け取って在庫を減らしたり発送処理を駆動させたりなんだりよしなにやってください
おまけ
stripeはアレなのでevent
から取り出したevent.data.object
の型付けが自動でいい感じになりません。any
の乱用はダメな文明
と言うわけで、
StripeのIssueで見つけた方法を使って強制的に型をつけてあげましょう。
// StripeCheckoutを扱う場合はこう
const checkout = event.data.object as Stripe.Checkout.Session;
// Chargeを扱う場合はこう
const charge = event.data.object as Stripe.Charge;
// PaymentIntentを扱う場合はこう
const paymentIntent = event.data.object as Stripe.PaymentIntent;
webhookを受け取って利用するのは大体決済完了を検知して在庫を増減させたり発送を行なったり領収書メールを発送したりなどの処理を行いたいときだと思うので、
上記三つのどれかでイケると思うし、違ってもいろんな型があるので、
目的に合わせてStripeの公式ドキュメントやライブラリのソースを読んだりすれば
使いたい型は結構簡単に見つけられるかと思います
このevent.data.object
にいい感じに型がつかない問題はissueが立った2020年から今まで解決されていないので、当面手動でas
を使って型をつけてあげることになると思います
実際にはevent.data.object
を取り出す前にevent.type
をチェックしてswitch-case
文や条件分岐を使ってどの型を当てるかを分岐させることになると思うので、
event.type
のチェックも行うようにしましょう
以上、NestでStripeWebhookを受け取って利用したいと考えた人の参考になれば幸いです
そしてStripeは早く型付けて && rawbody使わないでいい方法を導入して...
Discussion