koa-bodyでStripeのWebhookを受ける

その他raw bodyを取得したい時に使えるやつ。

結論

import Koa from 'koa'
import Router from 'koa-router'
import koaBody from 'koa-body'
const koa = new Koa()
const router = new Router()

koa.use(koaBody({ includeUnparsed: true }))

router.post('/webhook', async (ctx, next) => {
   const unparsedBody = Symbol.for('unparsedBody')
   const request = ctx.request.body[unparsedBody]
    const event = stripe.webhooks.constructEvent(
      request,
      ctx.headers['stripe-signature'],
      '<<STRIPE_WEBHOOK_SECRET>>'
   )
   // something...
}
koa.use(router.routes())

結論に至るまで

StripeのWebhookはNode.js用のStripe SDKの「stripe.webhooks.constructEvent」にbodyやらheaderやらWebhook Secretやらを渡せば良しなにやってくれる。しかし、koa-bodyで適当にctx.request.bodyを渡してしまうとエラーになる。

import Koa from 'koa'
import Router from 'koa-router'
import koaBody from 'koa-body'
const koa = new Koa()
koa.use(koaBody())
const router = new Router()

router.post('/api/v1/webhook', async (ctx, next) => {
   const event = stripe.webhooks.constructEvent(
      ctx.request.body,
      ctx.headers['stripe-signature'],
      '<<STRIPE_WEBHOOK_SECRET>>'
   )
   // something...
}
koa.use(router.routes())

↑これはエラーになる。

ちゃんと返ってくるエラーを読むと、「生の(raw)」bodyをちゃんと渡してるか?とでてくる。ctx.request.bodyはkoa-bodyがいい感じに整形して使いやすくしている(雑な説明)ので、raw bodyを投げろということだ。

koa-bodyではなく、koa-bodyparserを導入すれば、ctx.request.rawBodyを参照すれば済むが、併用することはできないのでkoa-bodyからraw bodyを取得する術を探さねばならない。

漁っても日本語ドキュメントが出てこないし、Stack Overflowの回答なども的外れだったから普通にkoa-bodyのGitHubを読む。最初からこうしておけばよかった。ちゃんとoptionsに「includeUnparsed」を渡せば良いらしい。

includeUnparsed {Boolean} Toggles co-body returnRawBody option; if set to true, for form encoded and JSON requests the raw, unparsed request body will be attached to ctx.request.body using a Symbol (see details), default false

なるほどではあるが、Symbolを使わないといけないらしい。自分のように生ぬるいプログラマもどきはこんなもの使ったことがない。

MDNを読んでもわからなかったが、シンボル(Symbol)って何? - Qiitaが参考になった。仕組みはともあれ、Symbol(string)またはSymbol.for(string)で参照できそうだというのはわかった。

koa-bodyのコードを読むと、「unparsed.js」というファイルが別に用意され、そこには「module.exports = Symbol.for('unparsedBody');」と書いてある。つまり、今回はSymbol.forを使えば良いようだ。

以上から至ったのが、結論に出したコードである。



この記事が気に入ったらサポートをしてみませんか?