# イベントを設定する

アプリケーションで Stripe イベントを受信、検証、処理します。

このガイドでは、イベントを受信し、各リクエストを検証し、アプリケーションでイベントを処理するイベントハンドラーを作成する方法について説明します。

[コードのクイックスタート](https://docs.stripe.com/webhooks/quickstart.md)

Stripe イベントの構造や使用すべきフォーマットについてまだ詳しくない場合は、まず[イベントの仕組み](https://docs.stripe.com/events/how-events-work.md)を参照してください。

## イベントフォーマットを選択する

新しい実装にはシンイベントを使用してください。シンイベントではバージョン管理が不要で、Stripe SDK で型指定されており、リソースを取得した時点の最新の状態が返されます。そのため、イベントの送信先で API バージョンの整合性を管理する必要がありません。シンイベントは API v1 および API v2 リソースによって生成されます。サポートされている全タイプのリストについては、[シンイベントカタログ](https://docs.stripe.com/api/v2/core/events/event-types.md)を参照してください。

スナップショットイベントは、以下のいずれかに該当する場合にのみ使用してください。

- サードパーティ製ツールや実装機能で完全な `Event` オブジェクトのペイロードを受信する必要がある場合。
- 追加の API コールを行わずに、変更されたリソース属性の以前の値が必要な場合。

スナップショットイベントは、関連するリソースの特定時点のスナップショットを含む完全な `Event` オブジェクトを送信します。これには、どのフィールドが変更されたかを示す `previous_attributes` プロパティが含まれます。また、API バージョンごとにバージョン管理されているため、エンドポイントの API バージョンと SDK バージョンを一致させておく必要があります。

詳細な比較については、[イベントの仕組み](https://docs.stripe.com/events/how-events-work.md#choosing-event-format)を参照してください。

## イベントハンドラーを作成する

Stripe からの `POST` リクエストを受け入れるエンドポイントをサーバーに追加します。メインのサンプルコードでは新しい実装に推奨されているシンイベントを使用しています。使用するイベントフォーマットによってコードは異なります。

#### シンイベント

署名を検証して型定義されたイベント通知を返すには、`parseEventNotification()` を使用します。リソースの最新状態を取得するには `fetchRelatedObject()` を呼び出し、変更されたフィールドなどの追加のイベントデータが必要な場合は `fetchEvent()` を呼び出します。

#### Node.js

```javascript

// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
// Find your keys at https://dashboard.stripe.com/apikeys.
const stripe = require('stripe')('<<YOUR_SECRET_KEY>>');

// This example uses Express to receive webhooks
const express = require('express');
const app = express();
const endpointSecret = 'whsec_...';

app.post('/webhook', express.raw({type: 'application/json'}), async (request, response) => {
  const sig = request.headers['stripe-signature'];
  let eventNotification;

  try {
    eventNotification = client.parseEventNotification(request.body, sig, endpointSecret);
  } catch (err) {
    response.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }

  // Handle the event notification
  switch (eventNotification.type) {
    case 'v1.billing.meter.error_report_triggered':
      // fetchRelatedObject() retrieves the latest version of the related resource
      const meter = await eventNotification.fetchRelatedObject();
      console.log('Meter error for:', meter.display_name);
      break;
    default:
      console.log(`Unhandled event type: ${eventNotification.type}`);
  }

  response.json({received: true});
});

app.listen(4242, () => console.log('Running on port 4242'));
```

#### スナップショットイベント (例外ケース)

Webhook の署名を検証するには `constructEvent()` を使用し、次に `type` フィールドを確認してどのイベントが発生したかを判定します。各イベントの `data.object` には、イベントが生成された時点における対象リソースのスナップショットが含まれています。このスナップショットは情報が古くなっている可能性があるため、重要なアクションを実行する前に、API から最新のリソースを取得してください。

#### Node.js

```javascript

// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
// Find your keys at https://dashboard.stripe.com/apikeys.
const stripe = require('stripe')('<<YOUR_SECRET_KEY>>');

// This example uses Express to receive webhooks
const express = require('express');
const app = express();
const endpointSecret = 'whsec_...';

app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => {
  const sig = request.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
  } catch (err) {
    response.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }

  // Handle the event
  switch (event.type) {
    case 'payment_intent.succeeded':
      const paymentIntent = event.data.object;
      console.log('PaymentIntent was successful!');
      break;
    case 'payment_method.attached':
      const paymentMethod = event.data.object;
      console.log('PaymentMethod was attached to a Customer!');
      break;
    // ... handle other event types
    default:
      console.log(`Unhandled event type ${event.type}`);
  }

  // Return a 200 response to acknowledge receipt of the event
  response.json({received: true});
});

app.listen(4242, () => console.log('Running on port 4242'));
```

新しいエンドポイントを追加した後で、サーバーを起動します。

## Stripe CLI をインストールして設定する

[npm](https://www.npmjs.com/) で Stripe CLI をインストールします。

```bash
npm install -g @stripe/cli@latest
```

インストール後、Stripe アカウントにログインします。

```bash
stripe login
```

CLI のインストール後、[エージェントツール](https://docs.stripe.com/cli/agent/setup)をインストールしたり、[オートコンプリート機能](https://docs.stripe.com/cli/completion)を設定したりすることもできます。

> Windows、macOS、Linux、Docker 向けのその他のインストールオプションについては、GitHub の [Stripe CLI readme](https://github.com/stripe/stripe-cli#installation) を参照してください。

Stripe CLI をインストールした後、コマンドラインで `stripe login` を実行してペアリングコードを生成し、Stripe アカウントと連携します。**Enter** を押すとブラウザーが起動し、ログインできます。

```bash
stripe login
Your pairing code is: humour-nifty-finer-magic
Press Enter to open up the browser (^C to quit)
```

生成された API キーは 90 日間有効です。ダッシュボードの [API キー](https://dashboard.stripe.com/apikeys)から変更または削除できます。

> ログイン時、およびそのプロジェクト向けのコマンド実行時に [–project-name](https://docs.stripe.com/cli/login#login-project-name) フラグを付けることで、プロジェクト固有の設定を作成できます。

## Webhook をローカルでテストする

CLI を使用して、ローカルの Webhook エンドポイントにイベントを転送します。

アプリケーションがポート 4242 で実行されていると仮定します。

```bash
stripe listen --forward-to http://localhost:4242/webhook
```

別のターミナルタブでモックイベントをトリガーします。

```bash
stripe trigger v1.billing.meter.error_report_triggered
```

`listen` タブには次の出力が表示されます。

```bash
[200 POST] OK v1.billing.meter.error_report_triggered
```

サーバーが動作しているターミナルには、`Meter error for:` に続いて対象メーターの表示名がログ出力されます。

代わりにスナップショットイベントのサンプル手順を進めている場合は、`payment_intent.succeeded` をトリガーすると、サーバー側に `PaymentIntent was successful!` とログ出力されます。

## Optional: Webhook の署名を確認する

Stripe は各イベントの `Stripe-Signature` ヘッダーに署名を含めているため、リクエストが確実に Stripe から送信されたものであることを検証できます。シンイベントの場合、`parseEventNotification()` が署名検証を自動的に処理します。スナップショットイベントの場合は、以下の 3 つのパラメーターを指定して `constructEvent()` を使用します。

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

まず、Webhook エンドポイントのシークレットを確認します。Stripe CLI を使用してローカルでテストしている場合は、`stripe listen` を実行した際に出力される CLI の表示から取得します。ダッシュボードで管理されているエンドポイントを使用している場合は、[ワークベンチ](https://dashboard.stripe.com/workbench/webhooks)で該当エンドポイントを開き、**シークレットを表示**をクリックします。このシークレットは `whsec_` から始まります。

#### Ruby

```ruby

# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
# Find your keys at https://dashboard.stripe.com/apikeys.
client = Stripe::StripeClient.new('<<YOUR_SECRET_KEY>>')

require 'stripe'
require 'sinatra'

# If you are testing your webhook locally with the Stripe CLI you
# can find the endpoint's secret by running `stripe listen`
# Otherwise, find your endpoint's secret in your webhook settings in
# the Developer Dashboard
endpoint_secret = 'whsec_...'

# Using the Sinatra framework
set :port, 4242

post '/my/webhook/url' do
  payload = request.body.read
  sig_header = request.env['HTTP_STRIPE_SIGNATURE']
  event = nil

  begin
    event = Stripe::Webhook.construct_event(
      payload, sig_header, endpoint_secret
    )
  rescue JSON::ParserError => e
    # Invalid payload
    puts "Error parsing payload: #{e.message}"
    status 400
    return
  rescue Stripe::SignatureVerificationError => e
    # Invalid signature
    puts "Error verifying webhook signature: #{e.message}"
    status 400
    return
  end

  # Handle the event
  case event.type
  when 'payment_intent.succeeded'
    payment_intent = event.data.object # contains a Stripe::PaymentIntent
    puts 'PaymentIntent was successful!'
  when 'payment_method.attached'
    payment_method = event.data.object # contains a Stripe::PaymentMethod
    puts 'PaymentMethod was attached to a Customer!'
  # ... handle other event types
  else
    puts "Unhandled event type: #{event.type}"
  end

  status 200
end
```

署名検証に加えて、Stripe の [IP アドレス](https://docs.stripe.com/ips.md)から送信された Webhook リクエストのみを受け付けるよう、サーバーやファイアウォールを設定します。

署名エラーに関するヘルプについては、[Webhook エンドポイントを管理する](https://docs.stripe.com/events/manage-webhook-endpoints.md#signature-errors)を参照してください。

## イベントハンドラーをデプロイする

本番環境に移行する準備ができたら:

1. ハンドラーに必要な権限のみを持つ、[本番環境](https://docs.stripe.com/keys.md#test-live-modes)用の[制限付き API キー](https://docs.stripe.com/keys.md#create-restricted-api-key)を作成します。たとえば、`fetchRelatedObject()` や `fetchEvent()` で取得するリソースに対する読み取りアクセスなどです。
2. [ワークベンチ](https://dashboard.stripe.com/workbench)を開き、**Webhook** タブに移動します。
3. **送信先を追加**をクリックします。
4. 受信する特定のイベントタイプを入力します。スナップショットの送信先を設定する場合は、API バージョンも選択してください。**続ける**をクリックします。
5. 送信先タイプのリストから **Webhook エンドポイント**を選択します。**続ける**をクリックします。
6. エンドポイントの URL を入力し、必要に応じて名前と説明を入力します。**送信先を作成**をクリックします。
7. 送信先の詳細ビューに表示されるエンドポイントシークレットをコピーし、`whsec_...` プレースホルダー部分に設定します。

イベントハンドラーはサーバー上で動作するため、制限付きキーまたはシークレットキーが必要です。公開可能キーでは、`fetchRelatedObject()` および `fetchEvent()` による API コールを認証できません。

API キーやエンドポイントシークレットをソースコードやクライアント側のアプリケーションに埋め込まないでください。代わりに、サーバープラットフォームのシークレット保管庫を使用してアプリケーションに提供します。プラットフォームにシークレット保管庫がない場合は、環境変数に設定してください。詳細については、[API キーのベストプラクティス](https://docs.stripe.com/keys-best-practices.md)を参照してください。

これで、アプリケーションで本番環境のイベントを受け取る準備ができました。Webhook エンドポイントの設定に関する詳細については、[Webhook エンドポイント API](https://docs.stripe.com/api/webhook_endpoints.md) を参照してください。
