Bubble × Stripe Connect で作る!予約決済プラットフォームの作り方【第6回】Webhook設計

Bubble × Stripe Connect で作る!予約決済プラットフォームの作り方

第6回:Webhook設計 – 決済完了の検知とステータス更新


前回のおさらい

第5回では、サブスク課金を解説しました。

  • 有料掲載・広告プランの実装
  • Stripe Subscription の設定
  • プロモーションコード「FIRST100」

今回は、決済システムの要である Webhook を解説します。


なぜ Webhook が必要か

問題: 決済完了をどう知るか

[ユーザー] → [Stripe決済画面] → [決済完了]
                                    │
                                    ↓
                              ユーザーは success_url に戻る
                                    │
                                    ↓
                              でも、本当に決済された?

success_url に戻ってきただけでは、実際に決済が完了したかは分かりません

  • ユーザーがURLを直接叩いた可能性
  • 決済処理中にネットワークエラーが起きた可能性
  • 不正なアクセスの可能性

解決策: Stripe からの通知を受け取る

[Stripe] → [Webhook] → [あなたのサーバー]
              │
              ↓
         「決済が完了しました」
              │
              ↓
         データベースを更新

Stripe が「決済完了しました」と教えてくれるので、確実に処理できます


Webhook の仕組み

イベントの種類

イベント タイミング
checkout.session.completed Checkout Session が完了
payment_intent.succeeded 支払いが成功
invoice.paid 請求書が支払われた(サブスク更新時)
customer.subscription.deleted サブスクがキャンセルされた

今回主に使うのは:

  • checkout.session.completed: 決済完了時
  • invoice.paid: サブスク更新時
  • customer.subscription.deleted: サブスク解約時

Bubble での Webhook 設定

1. Backend Workflow の作成

Bubble の Settings → API → 「Enable Workflow API」をオン

Bubble の Backend workflows で新しいエンドポイントを作成:

Endpoint: stripe_webhook
Method: POST

このエンドポイントのURLは:

https://あなたのアプリ.bubbleapps.io/api/1.1/wf/stripe_webhook

2. Stripe ダッシュボードで登録

ダッシュボード → 開発者 → Webhook → 「エンドポイントを追加」

エンドポイントURL: https://xxx.bubbleapps.io/api/1.1/wf/stripe_webhook
イベント:
  - checkout.session.completed
  - invoice.paid
  - customer.subscription.deleted

3. Webhook シークレットの取得

登録後、「署名シークレット」(whsec_xxx)が表示されます。

これは署名検証に使います(後述)。


受信データの構造

checkout.session.completed

{
  "type": "checkout.session.completed",
  "data": {
    "object": {
      "id": "cs_test_xxx",
      "mode": "payment",
      "payment_status": "paid",
      "customer": "cus_xxx",
      "amount_total": 3000,
      "metadata": {
        "reservation_id": "xxx",
        "store_id": "xxx"
      }
    }
  }
}

invoice.paid(サブスク更新)

{
  "type": "invoice.paid",
  "data": {
    "object": {
      "id": "in_xxx",
      "customer": "cus_xxx",
      "subscription": "sub_xxx",
      "amount_paid": 55000
    }
  }
}

Bubble での処理フロー

予約決済の場合

[Webhook 受信]
    │
    ↓
[type = checkout.session.completed ?]
    │
    ├─→ No → 終了
    │
    └─→ Yes
          │
          ↓
        [mode = payment ?]
          │
          ├─→ No(subscription)→ サブスク処理へ
          │
          └─→ Yes
                │
                ↓
              [metadata から reservation_id を取得]
                │
                ↓
              [予約を検索]
                │
                ↓
              [予約のステータスを「支払済み」に更新]
                │
                ↓
              [決済金額を保存]

Bubble の Backend Workflow

Trigger: stripe_webhook

Step 1: Only when
  Request Data's type is "checkout.session.completed"
  Request Data's data's object's mode is "payment"

Step 2: Search for 予約
  reservation_id = Request Data's data's object's metadata's reservation_id

Step 3: Make changes to 予約
  支払いステータス = 支払済み
  決済金額 = Request Data's data's object's amount_total
  stripe_payment_id = Request Data's data's object's id

署名検証

Webhook が本当に Stripe から来たものか確認する必要があります。

なぜ必要か

誰でもあなたのエンドポイントにPOSTできてしまいます:

[悪意のある人] → [偽の Webhook] → [あなたのサーバー]
                    │
                    ↓
              「決済完了しました」(嘘)
                    │
                    ↓
              不正に処理が実行される

検証方法

Stripe は Webhook 送信時に Stripe-Signature ヘッダーを付けます。

これと Webhook シークレット(whsec_xxx)を使って検証します。

Bubble での実装:

Bubble 標準では署名検証が難しいため、以下の方法があります:

1. Stripe プラグインを使う: 署名検証機能が含まれていることが多い

2. 検証を省略してリスク受容: テスト環境では許容できる場合も

3. サーバーサイドで検証: 外部サービス(Make.com など)を経由

実運用では検証を推奨しますが、小規模なら「metadata で二重チェック」でも対応可能:

[Webhook 受信]
    │
    ↓
[reservation_id で予約を検索]
    │
    ├─→ 見つからない → 無視
    │
    └─→ 見つかった
          │
          ↓
        [予約の金額と Webhook の金額が一致?]
          │
          ├─→ 不一致 → 無視(ログに記録)
          │
          └─→ 一致 → 処理を実行

冪等性(べきとうせい)

同じ Webhook が複数回届くことがあります(Stripe のリトライ機能)。

問題

1回目: 予約を「支払済み」に更新
2回目: また「支払済み」に更新(問題なし)
3回目: ポイント付与 → これが重複すると困る!

対策

処理済みかどうかをチェック:

[Webhook 受信]
    │
    ↓
[stripe_payment_id で履歴を検索]
    │
    ├─→ 見つかった → 処理済み、スキップ
    │
    └─→ 見つからない → 処理を実行
          │
          ↓
        [処理完了後]
        stripe_payment_id を保存

エラーハンドリング

Stripe へのレスポンス

Webhook を受け取ったら、200 OK を返す必要があります。

  • 200 OK: Stripe は「届いた」と認識
  • 4xx/5xx: Stripe はリトライする(最大3日間)

Bubble の Backend Workflow は正常終了すれば自動で 200 を返します。

エラー時のリトライ

処理中にエラーが起きても、Stripe が再送してくれます。

ただし、冪等性を担保していないと重複処理の原因に。


デバッグ方法

1. Stripe ダッシュボードで確認

開発者 → Webhook → イベント

各イベントの:

  • 送信日時
  • レスポンス(200 OK / エラー)
  • リクエスト/レスポンスの詳細

2. 再送

同じイベントを再送できます。

デバッグに便利。

3. Stripe CLI(ローカル開発)

stripe listen --forward-to localhost:3000/webhook

ローカル環境でも Webhook を受け取れます。


次回予告

第7回(最終回)では、本番デプロイ を解説します。

  • テスト環境から本番環境への切り替え
  • Stripe 本番モードの設定
  • 審査対策

お楽しみに!


このシリーズの目次

タイトル 内容
1 イントロ プラットフォーム決済の設計
2 Stripe Connect設計 Express アカウント、オンボーディング
3 予約決済の実装 Destination Charges、手数料計算
4 クーポンの罠 プロモコードが使えない問題と回避策
5 サブスク課金 有料掲載・広告プランの実装
6 Webhook設計 ← 今ここ
7 本番デプロイ テスト→本番の切り替え、審査対策

いいね・フォローしていただけると励みになります!


実装のご相談

予約システム・サブスク決済の導入でお困りの方へ。

・Stripe / Stripe Connect の設計・実装

・Bubble / Make.com でのノーコード開発

・テスト環境→本番デプロイまで一貫対応

初期設定から運用まで、初心者の方にも丁寧にサポートします。

👉 ココナラで相談する

この技術、気になりませんか?

このページも含め、サイト全体がWordPressで作られています。
同じような技術で、あなたのビジネスも強化しませんか?

お気軽にご相談ください

29年の判断 + AI で、成果を再現しませんか?

(マーケティング × 技術) + AI。伝言ゲームゼロで、設計から実装まで。