Webhook の署名確認エラーを解決する
Webhook イベントをリッスンするときに一般的なエラーを修正する方法をご紹介します。
Webhook イベントを処理する際は、イベントが Stripe から送信されていることを確認して、エンドポイントを安全に保護することをお勧めします。そのためには、Stripe-Signature
ヘッダーを使用して次の 3 つのパラメーターを指定し、constructEvent()
関数を呼び出します。
requestBody
: Stripe から送信されたリクエスト本文の文字列signature
: Stripe から送信されたリクエストの Stripe-Signature ヘッダーendpointSecret
: エンドポイントに関連付けられたシークレット
この関数により、署名確認エラーが発生することがあります。
Webhook signature verification failed. Err: No signatures found matching the expected signature for payload.
このエラーを受け取った場合、上記の関数に渡した 3 つのパラメーターのうち、少なくとも 1 つが正しくありません。以下の手順では、各パラメーターが正しく設定されていることを確認する方法を説明します。
エンドポイントシークレットを確認する
最も一般的なエラーは、誤ったエンドポイントシークレットの使用によるものです。ダッシュボードで作成した Webhook エンドポイントを使用している場合は、ダッシュボードでエンドポイントを開いて、ページの上部にあるシークレットを表示リンクをクリックしてシークレットを表示します。
Stripe CLI を使用している場合は、stripe listen
コマンドを実行するとシークレットが Terminal に出力されます。
いずれの場合も、シークレットは whsec_
プレフィックスで始まりますが、シークレットそのものは異なります。 CLI がダッシュボードで管理されているエンドポイントのシークレットを使用している場合、転送されるイベントの署名は確認しないでください。その逆も同様です。
最後に、コードで使用されている endpointSecret
を出力して、それが上記のものと一致していることを確認します。
リクエスト本文を確認する
リクエスト本文は、Stripe が信する、変更の加えられていない UTF-8 エンコードの文字列でなければなりません。これを文字列として出力すると、次のようになります。
{ "id": "evt_xxx", "object": "event", "data": { ... } }
未加工のリクエスト本文を取得する
一部のフレームワークは、空白の追加や削除、キーと値のペアの並べ替え、文字列の JSON への変換、エンコードの変更などの処理を行って、リクエスト本文を編集する場合があります。これらのケースはすべて、署名確認の失敗につながります。
以下は、一般的な設定を使用してデータを解析または変更する可能性があるフレームワークのリストと、未加工のリクエスト本文を取得する方法に関するヒントをいくつか示したものです。
フレームワーク | 取得方法 |
---|---|
Express を使用した stripe-node ライブラリ | 組み込みのクイックスタートガイドに従ってください。 |
BodyParser を使用した stripe-node ライブラリ | GitHub のこの問題に記載されている解決策をお試しください。 |
Next.js エンドポイントを使用した stripe-node ライブラリ | この例のように、 bodyParser を無効にして buffer(request) の使用をお試しください。 |
Lambda 関数を使用した AWS API ゲートウェイ
Lambda 関数を使用して AWS API Gateway の未加工のリクエスト本文を取得するには、API ゲートウェイで本文のマッピングテンプレートを次のように設定します。
- コンテンツタイプ:
application/json
- テンプレートコンテンツ:
{ "method": "$context.httpMethod", "body": $input.json('$'), "rawBody": "$util.escapeJavaScript($input.body).replaceAll("\\'", "'")", "headers": { #foreach($param in $input.params().header.keySet()) "$param": "$util.escapeJavaScript($input.params().header.get($param))" #if($foreach.hasNext),#end #end } }
次に、Sigma 関数でイベントの rawBody
プロパティを使用して未加工の本文にアクセスし、イベントの headers
プロパティを使用してヘッダーにアクセスします。
署名を確認する
signature
パラメーターを出力して、次のようになることを確認します。
t=xxx,v1=yyy,v0=zzz
そうでない場合は、ヘッダーから署名を抽出する際に、コードに問題がないか確認します。