業界・業務から探す
導入目的・課題から探す
データ・AIについて学ぶ
News
Hakkyについて
ウェビナーコラム
◆トップ【Hakkyの社内Wiki】Strapi
AI

執筆者:Handbook編集部

StripeのWebhookについて

Webhook とは

Webhook とは、

  • アプリケーションの更新情報を他のアプリケーションへリアルタイム提供する仕組みや概念
  • イベント(リポジトリにプッシュなど)発生時、指定した URL に POST リクエストする仕組み

のことです。

Stripe では、決算処理の状況等の情報を Webhook の原理を使って受け取ることができます。Stripe は、HTTPS を使用して、これらの通知を JSON ペイロードとしてアプリに送信します。その後、通知を受け取ったバックエンドシステムが必要な処理することになります。

Webhook を受信するためのステップ

  1. モニタリング対象のイベントと、解析するイベントペイロードを特定します。
  2. ローカルサーバーで、HTTP エンドポイント(URL)として Webhook エンドポイントを作成します。
  3. 各イベントオブジェクトを解析し、2xx 応答ステータスコードを返すことで、Stripe からのリクエストを処理します。
  4. Stripe 開発者ツール等を使用して、Webhook エンドポイントが正しく機能していることをテストします。
  5. Webhook エンドポイントをデプロイすることで、パブリックアクセス可能な HTTPS の URL になります。
  6. パブリックアクセス可能な HTTPS の URL を Stripe ダッシュボードに登録します。

Webhook エンドポイントの作成

エンドポイントの設定は頻繁に行うものではないので、ダッシュボードから行った方が便利です。

Stripe のダッシュボードを開き、画面右上の開発者 -> 画面左のWebhook -> 画面下のエンドポイントを追加を選択します。

下図のように Stripe イベントのエンドポイントやリッスンを設定できます。

  1. 画面左のエンドポイントURLに Stripe からイベントをリッスンしたいエンドポイント URL を記入します。
  2. 画面下のリッスンするイベントを選択でリッスンするイベントを個別に設定できます。
  3. 画面右には、エンドポイントでの処理のサンプルが記述されます。

すでにエンドポイントを設定した後で、リッスン等を変更する場合には、webhook 設定画面の右上にあるパンくずリストから変更を行います。リストから詳細情報の更新を選択します。

Webhookエンドポイントの編集ダイアログが表示されます。こちらで、送信イベントのところでリッスンするイベントの追加・削除や、エンドポイント URL の変更ができます。

また、ローカル環境でテストを選択することで、ローカル環境でのテスト方法のヒントも見ることができます。

一方で、エンドポイント URL は、プログラムで API にて作成することもできます。参考までにエンドポイントをPythonにて作成する方法のみを記しておきます。

import stripe
stripe.api_key = "sk_test_hogehoge****"

stripe.WebhookEndpoint.create(
  url="https://example.com/my/webhook/endpoint",
  enabled_events=[
    "charge.failed",
    "charge.succeeded",
  ],
)

ここで、引数で指定している値 urlenabled_events は、必須項目です。enabled_eventsにて、どのイベントで発火して通知するかを設定できます。

Webhook の取得

特定のイベントが発生して発火すると Webhook にてイベントオブジェクトが[POST]でエンドポイントへ通知されます。

イベントオブジェクトには、以下のような内容が記載されています。この中から必要な情報を利用します。 このイベントオブジェクトの"type"キーの値を解析することで、行われた処理を特定することができます。

{
  "id": "evt_hogehoge********",
  "object": "event",
  "api_version": "2020-08-27",
  "created": 1645565302,
  "data": {
    "object": {
      "id": "sub_hogehoge********",
      "object": "subscription",
      "application_fee_percent": null,
      "automatic_tax": {
        "enabled": false
      },
      "billing_cycle_anchor": 1645565299,
      "billing_thresholds": null,
      "cancel_at": null,
      "cancel_at_period_end": false,
      "canceled_at": null,
      "collection_method": "charge_automatically",
      "created": 1645565299,
      "current_period_end": 1647984499,
      "current_period_start": 1645565299,
      "customer": "cus_hogehoge********",
      "days_until_due": null,
      "default_payment_method": null,
      "default_source": null,
      "default_tax_rates": [],
      "discount": null,
      "ended_at": null,
      "items": {
        "object": "list",
        "data": [
          {
            "id": "si_hogehoge********",
            "object": "subscription_item",
            "billing_thresholds": null,
            "created": 1645565300,
            "metadata": {},
            "plan": {
              "id": "price_hogehoge********",
              "object": "plan",
              "active": false,
              "aggregate_usage": null,
              "amount": 8900,
              "amount_decimal": "8900",
              "billing_scheme": "per_unit",
              "created": 1645565299,
              "currency": "jpy",
              "interval": "month",
              "interval_count": 1,
              "livemode": false,
              "metadata": {},
              "nickname": null,
              "product": "prod_hogehoge********",
              "tiers_mode": null,
              "transform_usage": null,
              "trial_period_days": null,
              "usage_type": "licensed"
            },
            "price": {
              "id": "price_hogehoge********",
              "object": "price",
              "active": false,
              "billing_scheme": "per_unit",
              "created": 1645565299,
              "currency": "jpy",
              "livemode": false,
              "lookup_key": null,
              "metadata": {},
              "nickname": null,
              "product": "prod_hogehoge********",
              "recurring": {
                "aggregate_usage": null,
                "interval": "month",
                "interval_count": 1,
                "trial_period_days": null,
                "usage_type": "licensed"
              },
              "tax_behavior": "unspecified",
              "tiers_mode": null,
              "transform_quantity": null,
              "type": "recurring",
              "unit_amount": 8900,
              "unit_amount_decimal": "8900"
            },
            "quantity": 1,
            "subscription": "sub_hogehoge********",
            "tax_rates": []
          }
        ],
        "has_more": false,
        "total_count": 1,
        "url": "/v1/subscription_items?subscription=sub_hogehoge********"
      },
      "latest_invoice": "in_hogehoge********",
      "livemode": false,
      "metadata": {},
      "next_pending_invoice_item_invoice": null,
      "pause_collection": null,
      "payment_settings": {
        "payment_method_options": null,
        "payment_method_types": null
      },
      "pending_invoice_item_interval": null,
      "pending_setup_intent": null,
      "pending_update": null,
      "plan": {
        "id": "price_hogehoge********",
        "object": "plan",
        "active": false,
        "aggregate_usage": null,
        "amount": 8900,
        "amount_decimal": "8900",
        "billing_scheme": "per_unit",
        "created": 1645565299,
        "currency": "jpy",
        "interval": "month",
        "interval_count": 1,
        "livemode": false,
        "metadata": {},
        "nickname": null,
        "product": "prod_hogehoge********",
        "tiers_mode": null,
        "transform_usage": null,
        "trial_period_days": null,
        "usage_type": "licensed"
      },
      "quantity": 1,
      "schedule": null,
      "start_date": 1645565299,
      "status": "active",
      "transfer_data": null,
      "trial_end": null,
      "trial_start": null
    }
  },
  "livemode": false,
  "pending_webhooks": 0,
  "request": {
    "id": "req_hogehoge********",
    "idempotency_key": "cfa9a****************"
  },
  "type": "customer.subscription.created"
}

Pythonでイベント(PaymentIntent)をリッスンするコード例は以下の通りです。


import json
import os
import stripe

from flask import Flask, jsonify, request

endpoint_secret = 'whsec_hogehoge**************'

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    event = None
    payload = request.data
    sig_header = request.headers['STRIPE_SIGNATURE']

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError as e:
        # Invalid payload
        raise e
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        raise e

    # Handle the event
    if event['type'] == 'payment_intent.succeeded':
      payment_intent = event['data']['object']
    else:
      print('Unhandled event type {}'.format(event['type']))

    return jsonify(success=True)

エンドポイントは、webhook 通知を受け取ったら、複雑なロジックによるタイムアウトが発生する前に、以下のように迅速に成功のステータスコード (2xx) を返す必要があります。


    return jsonify(success=True)

また、エンドポイントがエラー等のレスポンスを返す場合には、以下の形式である必要があります。


    return jsonify(error=str(e)), 400

Webhook のローカル環境でのテスト

Webhook をローカル環境でテストすることができます。

$ stripe login
  • 次に、W ebhook にイベントを転送します。ここでは、別ターミナルを開いて、下記の通り、イベントを転送するようにします。
$ stripe listen --forward-to localhost:<<ポート番号>>/<<エンドポイント>>
  • 最後に、CLI を使用してイベントをトリガーします。別ターミナルから以下のようにイベントをトリガーすることで、Webhook のテストを行うことができます。
$ stripe trigger <<イベント>>

参考文献

info
備考

Hakky ではエンジニアを募集中です!まずは話してみたいなどでも構いませんので、ぜひお気軽に採用ページからお問い合わせくださいませ。

2025年06月02日に最終更新
読み込み中...