# 画面設計書 51-返金作成画面

## 概要

本ドキュメントは、Medusa Admin Dashboard における「返金作成画面」の設計仕様を定義するものである。

### 本画面の処理概要

**業務上の目的・背景**：EC運営において、顧客からの返品リクエストや注文キャンセル、商品不良等の理由により、既に受領した支払いを顧客に返金する必要が生じる。本画面は、管理者が注文に紐づく支払いに対して返金処理を実行するためのインターフェースを提供し、返金金額の指定、返金理由の選択、およびメモの記録を可能にする。これにより、返金業務の効率化と返金履歴の適切な管理を実現する。

**画面へのアクセス方法**：注文詳細画面から「返金」アクションを選択することでアクセスする。URLパラメータとして支払いIDが指定されている場合は、その支払いが自動選択される。

**主要な操作・処理内容**：
1. 返金対象の支払いを選択する（URLパラメータで指定されていない場合）
2. 返金金額を入力する（デフォルトは支払い金額または未返金残高）
3. 返金理由を選択する（任意）
4. メモを入力する（任意）
5. 保存ボタンを押下して返金処理を実行する

**画面遷移**：
- 遷移元：注文詳細画面（`/orders/:id`）
- 遷移先：返金完了後、注文詳細画面に戻る

**権限による表示制御**：認証済みの管理者ユーザーのみアクセス可能。返金処理権限を持つユーザーのみ操作可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 32 | 返金処理 | 主機能 | 返金の作成・実行 |
| 35 | 返金理由管理 | 補助機能 | 返金理由の選択 |

## 画面種別

編集（モーダル形式・ドロワー）

## URL/ルーティング

```
/orders/:id/refund
/orders/:id/refund?paymentId=:paymentId
```

## 入出力項目

| 項目名 | 項目ID | データ型 | 必須 | 入力形式 | バリデーション | 説明 |
|--------|--------|----------|------|----------|----------------|------|
| 支払い | paymentId | string | ○ | セレクト | 有効な支払いID | 返金対象の支払いを選択 |
| 金額 | amount | object | ○ | 通貨入力 | 0以上、支払い金額以下 | 返金金額（value: string, float: number） |
| 返金理由 | refund_reason_id | string | - | セレクト | 有効な返金理由ID | 返金理由を選択 |
| メモ | note | string | - | テキストエリア | - | 返金に関するメモ |

## 表示項目

| 項目名 | 表示形式 | 説明 |
|--------|----------|------|
| 支払い情報 | テキスト | 支払い金額、プロバイダー、支払いID（短縮） |
| 通貨記号 | テキスト | 注文の通貨コードに対応する記号 |
| 既存返金額 | テキスト | 支払いに対する既存の返金合計額（選択肢の無効化判定に使用） |

## イベント仕様

### 1-支払い選択変更

支払いセレクトボックスの値が変更された場合：
- 選択された支払いIDを状態に保存
- 返金金額フィールドを更新（支払い金額または未返金残高の小さい方）
- キャンセル済みの支払いまたは全額返金済みの支払いは選択不可

### 2-金額入力

金額フィールドに入力があった場合：
- CurrencyInputコンポーネントによる通貨フォーマット
- value（文字列）とfloat（数値）の両方を保持
- 小数点以下桁数は通貨設定に従う

### 3-保存ボタン押下

保存ボタンが押下された場合：
1. フォームバリデーションを実行
2. `useRefundPayment`フックを使用してAPI呼び出し
3. APIエンドポイント：`POST /admin/payments/:paymentId/refund`
4. リクエストボディ：`{ amount, note, refund_reason_id }`
5. 成功時：トースト通知を表示し、モーダルを閉じて注文詳細画面に戻る
6. 失敗時：エラーメッセージをトーストで表示

### 4-キャンセルボタン押下

キャンセルボタンが押下された場合：
- フォームの変更を破棄
- モーダルを閉じて注文詳細画面に戻る

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | refund | INSERT | 返金レコードの作成 |
| 保存ボタン押下 | order_transaction | INSERT | 注文トランザクション（返金）の作成 |
| 保存ボタン押下 | order_credit_line | INSERT | 返金クレジットラインの作成（条件付き） |

### テーブル別更新項目詳細

#### refund

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | UUID自動生成 | - |
| INSERT | payment_id | 選択された支払いID | - |
| INSERT | amount | 入力された金額 | - |
| INSERT | raw_amount | BigNumber形式の金額 | - |
| INSERT | note | 入力されたメモ | - |
| INSERT | refund_reason_id | 選択された返金理由ID | 任意 |
| INSERT | created_by | ログインユーザーID | - |

#### order_transaction

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | order_id | 注文ID | - |
| INSERT | amount | 返金金額（負の値） | -1を乗算 |
| INSERT | currency_code | 支払いまたは注文の通貨コード | - |
| INSERT | reference_id | 返金ID | - |
| INSERT | reference | "refund" | - |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| refundPaymentSuccess | 成功 | 「{金額}の返金が完了しました」 | 返金処理成功時 |
| error | エラー | APIから返されたエラーメッセージ | 返金処理失敗時 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 注文が見つからない | エラーをスローし、エラーページを表示 |
| 支払いが選択されていない | 保存ボタンが無効化される |
| 返金金額が支払い金額を超過 | APIエラーとして処理、エラートースト表示 |
| キャプチャ済み金額を超える返金 | ワークフローでバリデーションエラー |
| ネットワークエラー | エラートーストを表示 |

## 備考

- ロイヤリティプラグインが有効な場合は、別フォーム（OrderBalanceSettlementForm）が表示される
- 返金金額のデフォルト値は、注文のpending_differenceが負の場合はその絶対値と支払い金額の小さい方
- 返金処理はrefundPaymentWorkflowで実行され、支払いプロバイダーへの返金処理も含む

---

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

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

### 推奨読解順序

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

まず、返金処理で使用されるデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | CreateRefundSchema | `packages/admin/dashboard/src/routes/orders/order-create-refund/components/create-refund-form/create-refund-form.tsx` | Zodスキーマによる入力データの型定義（32-39行目） |
| 1-2 | RefundPaymentWorkflowInput | `packages/core/core-flows/src/payment/workflows/refund-payment.ts` | ワークフロー入力の型定義（24-45行目） |

**読解のコツ**: TypeScriptの型定義とZodスキーマの対応関係を確認する。

#### Step 2: エントリーポイントを理解する

処理の起点となるファイル・関数を特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | OrderCreateRefund | `packages/admin/dashboard/src/routes/orders/order-create-refund/order-create-refund.tsx` | 画面コンポーネントのエントリーポイント |

**主要処理フロー**:
1. **11-19行目**: useOrderフックで注文データを取得
2. **18-19行目**: ロイヤリティプラグインの存在確認
3. **27-28行目**: プラグインの有無に応じてフォームコンポーネントを切り替え

#### Step 3: フォームコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | CreateRefundForm | `packages/admin/dashboard/src/routes/orders/order-create-refund/components/create-refund-form/create-refund-form.tsx` | 返金フォームの実装 |

**主要処理フロー**:
- **41-69行目**: useFormによるフォーム初期化
- **71-86行目**: 支払い選択時の金額自動設定
- **88行目**: useRefundPaymentフックの取得
- **90-115行目**: フォーム送信ハンドラ

#### Step 4: APIフックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | useRefundPayment | `packages/admin/dashboard/src/hooks/api/payments.tsx` | 返金APIミューテーションフック（92-116行目） |

**主要処理フロー**:
- **102行目**: sdk.admin.payment.refund呼び出し
- **103-110行目**: キャッシュの無効化処理

#### Step 5: バックエンドAPIを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | refund route | `packages/medusa/src/api/admin/payments/[id]/refund/route.ts` | 返金APIルート |

**主要処理フロー**:
- **17-23行目**: refundPaymentWorkflowの実行

#### Step 6: ワークフローを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | refundPaymentWorkflow | `packages/core/core-flows/src/payment/workflows/refund-payment.ts` | 返金ワークフロー（110-234行目） |

**主要処理フロー**:
- **113-127行目**: 支払い情報の取得
- **129-134行目**: 返金金額のバリデーション
- **166行目**: refundPaymentStepの実行
- **190-212行目**: 注文トランザクションの作成
- **214-225行目**: クレジットラインの作成

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

```
OrderCreateRefund (order-create-refund.tsx)
    │
    ├─ useOrder (注文データ取得)
    │
    └─ CreateRefundForm (create-refund-form.tsx)
           │
           ├─ useRefundPayment (payments.tsx)
           │      └─ sdk.admin.payment.refund
           │             └─ POST /admin/payments/:id/refund
           │                    └─ refundPaymentWorkflow
           │                           ├─ useRemoteQueryStep (支払い取得)
           │                           ├─ validateRefundPaymentExceedsCapturedAmountStep
           │                           ├─ refundPaymentStep
           │                           ├─ addOrderTransactionStep
           │                           ├─ createOrderRefundCreditLinesWorkflow
           │                           └─ emitEventStep
           │
           └─ useRefundReasons (返金理由一覧取得)
```

### データフロー図

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

支払い選択 ─────────┐
                    │
金額入力 ──────────┼───▶ CreateRefundForm ───▶ useRefundPayment ───▶ refundPaymentWorkflow
                    │         │                                              │
返金理由選択 ──────┤         │                                              ├─▶ refund (DB)
                    │         ▼                                              ├─▶ order_transaction (DB)
メモ入力 ──────────┘    フォームバリデーション                               └─▶ order_credit_line (DB)
                              │
                              ▼
                        トースト通知 ───▶ 注文詳細画面
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| order-create-refund.tsx | `packages/admin/dashboard/src/routes/orders/order-create-refund/order-create-refund.tsx` | ソース | 画面コンポーネント |
| create-refund-form.tsx | `packages/admin/dashboard/src/routes/orders/order-create-refund/components/create-refund-form/create-refund-form.tsx` | ソース | 返金フォーム |
| payments.tsx | `packages/admin/dashboard/src/hooks/api/payments.tsx` | ソース | 支払いAPIフック |
| refund-reasons.tsx | `packages/admin/dashboard/src/hooks/api/refund-reasons.tsx` | ソース | 返金理由APIフック |
| route.ts | `packages/medusa/src/api/admin/payments/[id]/refund/route.ts` | ソース | APIルート |
| refund-payment.ts | `packages/core/core-flows/src/payment/workflows/refund-payment.ts` | ソース | 返金ワークフロー |
