# Webhook の署名確認エラーを解決する

Webhook イベントをリッスンするときに一般的なエラーを修正する方法をご紹介します。

Webhook イベントを処理する際は、イベントが Stripe から送信されていることを [検証](https://docs.stripe.com/webhooks.md#verify-official-libraries) してエンドポイントを保護することを推奨します。そのために、`Stripe-Signature` ヘッダーを使用し、`constructEvent()` 関数に 3 つのパラメータを渡して呼び出します。

- `requestBody`: Stripe から送信されたリクエスト本文の文字列。
- `signature`: Stripe から送信されたリクエストの Stripe-Signature ヘッダー。
- `endpointSecret`: エンドポイントに関連付けられたシークレット。

この機能は次のようになります。

#### Ruby

```ruby
Stripe::Webhook.construct_event(request_body, signature, endpoint_secret)
```

次の `Webhook signature verification failed` エラーが発生した場合、`constructEvent()` 関数に渡した 3 つのパラメータのうち、少なくとも 1 つが正しくありません。

```
Webhook signature verification failed. Err: No signatures found matching the expected signature for payload.
```

## エンドポイントシークレットを確認する

最もよくあるエラーは、間違った endpoint secret を使用することです。ダッシュボードで作成した webhook エンドポイントを使用している場合は、[Dashboard](https://dashboard.stripe.com/test/webhooks) で対象のエンドポイントを開き、ページ上部付近にある **Reveal secret** リンクをクリックして秘密鍵を確認してください。Stripe CLI を使用している場合は、`stripe listen` コマンドを実行すると、秘密鍵がターミナルに表示されます。

いずれの場合も、シークレットは `whsec_` プレフィックスで始まりますが、シークレットそのものは異なります。CLI がダッシュボードで管理されているエンドポイントのシークレットを使用している場合、転送されるイベントの署名は確認しないでください。その逆も同様です。最後に、コードで使用している `endpointSecret` を出力し、上で確認した値と一致していることを確認してください。

## リクエスト本文を確認する

リクエスト本文は、Stripe が信する、変更の加えられていない UTF-8 エンコードの文字列でなければなりません。これを文字列として出力すると、次のようになります。

```
{
  "id": "evt_xxx",
  "object": "event",
  "data": {
      ...
  }
}
```

### 未加工のリクエスト本文を取得する

一部のフレームワークは、空白の追加や削除、キーと値のペアの並べ替え、文字列の JSON への変換、エンコードの変更などの処理を行って、リクエスト本文を編集する場合があります。これらのケースはすべて、署名確認の失敗につながります。

以下は、一般的な設定を使用してデータを解析または変更する可能性があるフレームワークのリストと、未加工のリクエスト本文を取得する方法に関するヒントをいくつか示したものです。

| フレームワーク | 取得方法 |
| --- | --- |
| Express を使用した stripe-node ライブラリ | [組み込みのクイックスタートガイド](https://docs.stripe.com/webhooks/quickstart.md?lang=node)に従ってください。 |
| BodyParser を使用した stripe-node ライブラリ | [GitHub の問題](https://github.com/stripe/stripe-node/issues/341) に掲載されている解決策を試してください。 |
| stripe-node ライブラリと Next.js [App Router](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) | この[実例](https://github.com/stripe/stripe-node/blob/master/examples/webhook-signing/nextjs/app/api/webhooks/route.ts)を見てください。 |
| stripe-node ライブラリと Next.js [Pages Router](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) | [この例](https://github.com/stripe/stripe-node/blob/master/examples/webhook-signing/nextjs/pages/api/webhooks.ts)のように、 `bodyParser` を無効にして `buffer(request)` の使用をお試しください。 |

Express で stripe-node ライブラリを使用する場合、`app.use(express.json())` は Webhook ルートの_後に_配置するよう注意してください。Express では、ミドルウェアの設定順序が重要です。もし `express.json()` を Webhook ルートより前に適用すると、リクエストボディが署名検証より先に解析されてしまい、検証が失敗します。例としては以下の通りです。

```javascript
// Webhook route in its original request form
app.post('/webhook', ...);

// Parse the request body in JSON for other routes
app.use(express.json());

// Put other routes here
app.post('/another-route', ...);
```

### Lambda 関数を使用した AWS API ゲートウェイ

AWS API Gateway と Lambda 関数で生のリクエストボディを取得するには、API Gateway で `application/json` の **Body Mapping Template** を設定してください。

```
{
  "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
```

そうでない場合は、ヘッダーから署名を抽出する際に、コードに問題がないか確認します。
