# 機能設計書 34-アカウントホルダー管理

## 概要

本ドキュメントは、Medusaの支払いモジュールにおけるアカウントホルダー管理機能の設計を記載する。

### 本機能の処理概要

アカウントホルダー管理機能は、支払いプロバイダー（Stripe、PayPal等）における顧客アカウント情報を管理する機能である。顧客の決済アカウントの作成、更新、削除を行い、保存済み決済手段の管理を可能にする。

**業務上の目的・背景**：リピート顧客の利便性向上のため、決済情報を保存して次回購入時に再利用できる仕組みが必要である。本機能により、プロバイダー側で管理される顧客アカウント（Stripe Customer等）とMedusaの顧客情報を紐付けて管理する。

**機能の利用シーン**：
1. 顧客が初めて決済を行う際にアカウントホルダーを自動作成する場面
2. 顧客情報（メールアドレス等）が更新された際にアカウントホルダーを更新する場面
3. 顧客が退会する際にアカウントホルダーを削除する場面
4. 保存済み決済手段の一覧を取得する場面

**主要な処理内容**：
1. アカウントホルダーの作成（プロバイダーへの顧客登録）
2. アカウントホルダーの更新（プロバイダー側の顧客情報更新）
3. アカウントホルダーの削除（プロバイダー側の顧客削除）
4. 決済手段の一覧取得・保存

**関連システム・外部連携**：Stripe Customer API、PayPal Vault API等の外部顧客管理APIと連携。

**権限による制御**：顧客自身またはシステム内部から呼び出される。管理者による直接操作は限定的。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 決済手段管理画面 | 主機能 | 保存済み決済手段の表示・管理（画面機能マッピングに該当なし） |

## 機能種別

CRUD操作 / データ連携

## 入力仕様

### 入力パラメータ

#### アカウントホルダー作成

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| provider_id | string | Yes | 支払いプロバイダーID | 登録済みプロバイダーであること |
| context | object | No | 顧客情報等のコンテキスト | - |
| context.customer | object | No | 顧客情報 | - |
| context.customer.id | string | No | 顧客ID（冪等性キーとして使用） | - |
| context.customer.email | string | No | 顧客メールアドレス | - |

#### アカウントホルダー更新

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | アカウントホルダーID | 存在するIDであること |
| provider_id | string | Yes | 支払いプロバイダーID | - |
| context | object | Yes | 更新情報のコンテキスト | account_holderが必須 |
| metadata | object | No | メタデータ | - |

### 入力データソース

- 顧客情報：customerモジュールから取得
- DBテーブル：account_holder
- 外部プロバイダー：Stripe等の顧客データ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| account_holder | AccountHolderDTO | アカウントホルダー情報 |
| account_holder.id | string | アカウントホルダーID |
| account_holder.provider_id | string | プロバイダーID |
| account_holder.external_id | string | プロバイダー側の顧客ID |
| account_holder.email | string | メールアドレス |
| account_holder.data | object | プロバイダー固有データ |

### 出力先

- DBテーブル：account_holder
- 外部API：各決済プロバイダーの顧客API

## 処理フロー

### 処理シーケンス

```
1. アカウントホルダー作成
   ├─ コンテキストに既存アカウントホルダーがあれば早期リターン
   ├─ プロバイダーへ顧客作成リクエスト送信
   ├─ レスポンスがある場合、ローカルDBにAccountHolderレコード作成
   └─ 結果を返却

2. アカウントホルダー更新
   ├─ コンテキストにaccount_holderが必須
   ├─ プロバイダーへ顧客更新リクエスト送信
   ├─ ローカルDBのAccountHolderレコード更新
   └─ 結果を返却

3. アカウントホルダー削除
   ├─ ローカルDBからAccountHolderレコード取得
   ├─ ローカルDBのレコード削除
   ├─ プロバイダーへ顧客削除リクエスト送信
   └─ 完了
```

### フローチャート

```mermaid
flowchart TD
    A[開始: createAccountHolder] --> B{既存account_holder?}
    B -->|Yes| C[既存アカウント返却]
    B -->|No| D[プロバイダーへ顧客作成]
    D --> E{レスポンスあり?}
    E -->|No| F[空レスポンス返却]
    E -->|Yes| G[AccountHolderレコード作成]
    G --> H[結果を返却]
    C --> I[終了]
    F --> I
    H --> I
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-34-001 | 既存アカウント早期リターン | context.account_holderが存在する場合はそれを返却 | createAccountHolder時 |
| BR-34-002 | プロバイダーサポート確認 | プロバイダーがアカウントホルダー機能をサポートしていない場合は空レスポンス | 全操作時 |
| BR-34-003 | 外部ID一意性 | provider_id + external_idの組み合わせは一意 | AccountHolderレコード作成時 |
| BR-34-004 | 削除順序 | ローカルDB削除後にプロバイダー削除を実行 | deleteAccountHolder時 |

### 計算ロジック

特に複雑な計算ロジックはなし。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| アカウント作成 | account_holder | INSERT | アカウントホルダーレコード作成 |
| アカウント更新 | account_holder | UPDATE | アカウントホルダーレコード更新 |
| アカウント削除 | account_holder | DELETE | アカウントホルダーレコード削除 |

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

#### account_holder

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成（prefix: acchld） | - |
| INSERT | provider_id | プロバイダーID | 例：pp_stripe |
| INSERT | external_id | プロバイダー側の顧客ID | 例：cus_xxx |
| INSERT | email | 顧客メールアドレス | nullable |
| INSERT | data | プロバイダー固有データ | JSON、デフォルト{} |
| UPDATE | data | プロバイダーからの更新データ | - |
| UPDATE | metadata | メタデータ | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| INVALID_DATA | MedusaError | 更新時にcontext.account_holderがない | コンテキストに既存アカウント情報を含める |
| PROVIDER_NOT_SUPPORTED | Warning | プロバイダーがメソッドをサポートしていない | ログ出力、空レスポンス返却 |
| PROVIDER_ERROR | ProviderError | 外部プロバイダーでのエラー | プロバイダー固有のエラー処理 |

### リトライ仕様

- プロバイダーAPI呼び出しのリトライは各プロバイダー実装に依存
- 冪等性キーとしてcustomer.idを使用

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

- `@InjectManager`デコレータによりマネージャー管理
- ローカルDB操作とプロバイダー操作は別トランザクション
- 削除時はローカルDB削除を先に実行（プロバイダー削除失敗時の整合性考慮）

## パフォーマンス要件

- ローカルDB操作：ミリ秒単位
- 外部プロバイダー操作：1-3秒

## セキュリティ考慮事項

- 顧客のPII（個人情報）を含むため、適切なアクセス制御が必要
- プロバイダー側で顧客データが暗号化されることを前提
- external_idは外部サービスとの紐付けに使用されるため、漏洩に注意

## 備考

- プロバイダーがアカウントホルダー機能をサポートしていない場合、各メソッドは警告ログを出力して空レスポンスを返す
- Stripeの場合、external_idはStripe Customer ID（cus_xxx）に対応

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | account-holder.ts | `packages/modules/payment/src/models/account-holder.ts` | AccountHolderエンティティの構造 |

**読解のコツ**:
- **4行目**: idプレフィックスは`acchld`
- **6行目**: provider_idとexternal_idのユニークインデックス（13-16行目）
- **9行目**: dataフィールドはデフォルトで空オブジェクト

#### Step 2: モジュールサービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | payment-module.ts | `packages/modules/payment/src/services/payment-module.ts` | AccountHolder関連メソッド |

**主要処理フロー**:
- **1091-1129行目**: createAccountHolder
  - **1097-1098行目**: 既存アカウントの早期リターン
  - **1104-1113行目**: プロバイダーへの顧客作成
  - **1116-1126行目**: ローカルDBへのレコード作成
- **1131-1169行目**: updateAccountHolder
  - **1137-1141行目**: context.account_holderの必須チェック
  - **1147-1153行目**: プロバイダーへの更新
  - **1157-1166行目**: ローカルDBの更新
- **1171-1191行目**: deleteAccountHolder
  - **1177-1180行目**: 既存レコードの取得
  - **1183行目**: ローカルDBの削除
  - **1185-1190行目**: プロバイダーへの削除

#### Step 3: プロバイダーサービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | payment-provider.ts | `packages/modules/payment/src/services/payment-provider.ts` | プロバイダーへの委譲 |

**主要処理フロー**:
- **145-158行目**: retrieveAccountHolder
- **160-173行目**: createAccountHolder（オプショナル対応）
- **175-188行目**: updateAccountHolder（オプショナル対応）
- **190-203行目**: deleteAccountHolder（オプショナル対応）

### プログラム呼び出し階層図

```
PaymentModuleService.createAccountHolder()
    │
    ├─ context.account_holder存在チェック
    │      └─ 存在すれば早期リターン
    │
    ├─ paymentProviderService_.createAccountHolder()
    │      │
    │      └─ provider.createAccountHolder()（オプショナル）
    │             │
    │             └─ 外部プロバイダーAPI
    │
    └─ accountHolderService_.create()
           │
           └─ DBレコード作成
```

### データフロー図

```
[入力]                    [処理]                          [出力]

provider_id ───────────▶ createAccountHolder
context.customer               │
                               ▼
                    ┌───────────────────────────┐
                    │ プロバイダー顧客作成       │
                    │ (Stripe Customer API等)   │
                    └──────────┬────────────────┘
                               │
                               ▼
                    ┌───────────────────────────┐
                    │ account_holder            │
                    │ (INSERT)                  │
                    └──────────┬────────────────┘
                               │
                               ▼
                          AccountHolderDTO
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| account-holder.ts | `packages/modules/payment/src/models/account-holder.ts` | モデル | AccountHolderエンティティ定義 |
| payment-module.ts | `packages/modules/payment/src/services/payment-module.ts` | サービス | アカウントホルダーCRUD |
| payment-provider.ts | `packages/modules/payment/src/services/payment-provider.ts` | サービス | プロバイダーへの委譲 |
