# バッチ設計書 17-payment-webhook-handler

## 概要

本ドキュメントは、Medusaフレームワークにおける決済Webhookイベント処理サブスクライバー「payment-webhook-handler」の設計仕様を定義する。

### 本バッチの処理概要

payment-webhook-handlerは、外部決済プロバイダー（Stripe、PayPalなど）から送信されるWebhookイベントを処理するイベントサブスクライバーである。決済の成功、承認、キャプチャなどの状態変化に応じて、Medusa内部のワークフローを実行し、注文処理を完結させる。

**業務上の目的・背景**：Eコマースにおいて決済処理は非同期で行われることが多く、決済プロバイダーからのWebhook通知を受け取って後続処理を実行する必要がある。例えば、クレジットカード決済が承認された後、カートを注文に変換し、決済をキャプチャする処理を自動的に行う。このハンドラは決済ライフサイクルの重要な部分を担い、顧客体験とビジネスオペレーションの両方に直接影響する。

**バッチの実行タイミング**：PaymentWebhookEvents.WebhookReceivedイベントが発行されたとき。外部決済プロバイダーがWebhookを送信し、Medusaがそれを受信・イベント発行したタイミングで実行される。

**主要な処理内容**：
1. Webhookペイロードの受信とBuffer型データの復元
2. 決済モジュールによるWebhookアクションとデータの取得
3. 対応するアクションに基づいてprocessPaymentWorkflowを実行
4. 決済のキャプチャ、承認、カート完了処理の実行

**前後の処理との関連**：決済プロバイダーのWebhookエンドポイントがイベントを発行する前段階がある。後続処理として、注文の作成、在庫の確保、通知の送信などが行われる。

**影響範囲**：payment、payment_session、cart、orderテーブル。決済ステータスの更新、カートから注文への変換。

## バッチ種別

イベントハンドラ / 決済処理

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 随時（イベント駆動） |
| 実行時刻 | 任意（Webhook受信時） |
| 実行曜日 | 該当なし |
| 実行日 | 該当なし |
| トリガー | PaymentWebhookEvents.WebhookReceivedイベント |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| 決済セッション存在 | 処理対象の決済セッションがDBに存在すること |
| 決済プロバイダー設定 | 対象の決済プロバイダーが正しく設定されていること |
| カート/注文存在 | 関連するカートまたは注文が存在すること |

### 実行可否判定

1. paymentService.getWebhookActionAndDataでアクションとデータを取得
2. processedEvent.dataがnullの場合は早期リターン
3. 以下のアクションの場合は処理をスキップ：
   - PaymentActions.NOT_SUPPORTED
   - PaymentActions.CANCELED
   - PaymentActions.FAILED
   - PaymentActions.REQUIRES_MORE

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| event | SubscriberArgs<ProviderWebhookPayload> | Yes | - | Webhookイベントデータ |
| container | MedusaContainer | Yes | - | 依存性注入コンテナ |

### ProviderWebhookPayloadの構造

| フィールド | 型 | 説明 |
|-----------|-----|------|
| payload.rawData | Buffer or SerializedBuffer | 生のWebhookデータ |
| その他 | プロバイダー依存 | 決済プロバイダー固有のデータ |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| イベントデータ | オブジェクト | Webhookペイロード |
| payment_sessionテーブル | DB | 決済セッション情報 |
| paymentテーブル | DB | 決済情報 |
| cartテーブル | DB | カート情報 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| paymentテーブル | DB | 決済ステータスの更新 |
| orderテーブル | DB | 注文の作成（カート完了時） |
| payment_sessionテーブル | DB | セッションステータスの更新 |

### 出力ファイル仕様

ファイル出力はなし。

## 処理フロー

### 処理シーケンス

```
1. イベント受信
   └─ PaymentWebhookEvents.WebhookReceivedイベントを受信
2. Bufferデータ復元
   └─ SerializedBuffer形式の場合、Buffer.from()で復元
3. Webhookアクション取得
   └─ paymentService.getWebhookActionAndData(input)
4. 早期リターン判定
   └─ dataがnull、またはサポート外/キャンセル/失敗/要追加アクションの場合
5. ワークフローエンジン取得
   └─ container.resolve(Modules.WORKFLOW_ENGINE)
6. processPaymentWorkflow実行
   └─ wfEngine.run(processPaymentWorkflowId, { input: processedEvent })
```

### processPaymentWorkflowの内部処理

```
1. 決済データ取得
   └─ payment_session_idから決済情報を取得
2. カート関連情報取得
   └─ payment_collection_id経由でカート情報を取得
3. ロック取得（カートがある場合）
   └─ acquireLockStep: 30秒タイムアウト、2分TTL
4. 決済キャプチャ（SUCCESSFUL + 既存決済あり）
   └─ capturePaymentWorkflow実行
5. 自動キャプチャ（SUCCESSFUL + 決済なし）
   └─ authorizePaymentSessionStep → capturePaymentWorkflow
6. 決済承認（AUTHORIZED + カートなし）
   └─ authorizePaymentSessionStep
7. ロック解放
   └─ releaseLockStep
8. カート完了（カートあり + 注文なし）
   └─ completeCartAfterPaymentStep
```

### フローチャート

```mermaid
flowchart TD
    A[WebhookReceived イベント受信] --> B{rawDataがSerializedBuffer?}
    B -->|Yes| C[Buffer.fromで復元]
    B -->|No| D[そのまま使用]
    C --> E[getWebhookActionAndData]
    D --> E
    E --> F{dataがnull?}
    F -->|Yes| G[早期リターン]
    F -->|No| H{actionがサポート外/キャンセル/失敗/要追加?}
    H -->|Yes| G
    H -->|No| I[WorkflowEngine取得]
    I --> J[processPaymentWorkflow実行]
    J --> K[決済・カート情報取得]
    K --> L{カートあり?}
    L -->|Yes| M[ロック取得]
    L -->|No| N[ロックなし]
    M --> O{action判定}
    N --> O
    O -->|SUCCESSFUL + 既存決済| P[capturePaymentWorkflow]
    O -->|SUCCESSFUL + 決済なし| Q[authorize → capture]
    O -->|AUTHORIZED + カートなし| R[authorizePaymentSessionStep]
    P --> S[ロック解放]
    Q --> S
    R --> S
    S --> T{カートあり + 注文なし?}
    T -->|Yes| U[completeCartAfterPaymentStep]
    T -->|No| V[処理終了]
    U --> V
```

## データベース操作仕様

### 操作別データベース影響一覧

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 決済情報取得 | payment | SELECT | session_idから決済を検索 |
| セッション情報取得 | payment_session | SELECT | セッション情報を取得 |
| カート情報取得 | cart_payment_collection | SELECT | カートとの関連を取得 |
| 注文情報取得 | order_cart | SELECT | 注文の存在確認 |
| 決済キャプチャ | payment | UPDATE | キャプチャ状態を記録 |
| 決済承認 | payment_session | UPDATE | 承認状態を記録 |
| 注文作成 | order | INSERT | カートから注文を作成 |

### テーブル別操作詳細

#### payment

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id | payment_session_id = ? | 決済IDの取得 |
| UPDATE | captured_at | 現在日時 | キャプチャ完了時 |
| UPDATE | amount | キャプチャ金額 | キャプチャ完了時 |

#### payment_session

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | payment_collection_id | id = ? | コレクションIDの取得 |
| UPDATE | status | authorized | 承認完了時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | データ不存在 | 決済セッションが見つからない | Webhookの再送を待つ |
| - | ワークフローエラー | processPaymentWorkflow失敗 | ログを確認し手動対応 |
| - | ロックタイムアウト | カートのロック取得に失敗 | 自動リトライまたは手動対応 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Webhookは決済プロバイダー側で再送される |
| リトライ間隔 | プロバイダー依存 |
| リトライ対象エラー | 一時的なエラー全般 |

### 障害時対応

1. エラーログを確認し、失敗箇所を特定
2. 決済プロバイダーのダッシュボードでWebhook配信状況を確認
3. 必要に応じて手動でワークフローを再実行
4. カート完了に失敗した場合はcompleteCartAfterPaymentStepを手動実行

## トランザクション仕様

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | ワークフローステップ単位 |
| コミットタイミング | 各ステップ完了時 |
| ロールバック条件 | ワークフロー失敗時（補償アクションが実行される） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | Webhook受信数に依存 |
| 目標処理時間 | 数秒以内 |
| メモリ使用量上限 | Node.jsデフォルト制限 |

## 排他制御

| 項目 | 内容 |
|-----|------|
| ロック対象 | カートID |
| ロックタイムアウト | 30秒 |
| ロックTTL | 2分 |
| ロック方式 | acquireLockStep / releaseLockStep |

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| デバッグログ | 各ステップ実行時 | ワークフローステップ名 |
| エラーログ | エラー発生時 | エラーメッセージとスタックトレース |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| Webhook処理エラー | 1件以上 | 運用チーム |
| 処理時間 | 10秒超過 | 運用チーム |

## 備考

- サブスクライバーID: payment-webhook-handler
- 対象イベント: PaymentWebhookEvents.WebhookReceived
- SerializedBuffer型のチェックは、JSONシリアライズされたBufferデータを復元するため
- completeCartAfterPaymentStepはcontinueOnPermanentFailure: trueが設定されており、カート完了に失敗しても決済処理は続行される
- 以下のアクションはこのハンドラでは処理されない：
  - NOT_SUPPORTED: サポートされていないイベント
  - CANCELED: キャンセルされた決済
  - FAILED: 失敗した決済
  - REQUIRES_MORE: 追加情報が必要（3Dセキュア等）
