# 機能設計書 26-注文更新

## 概要

本ドキュメントは、Shopwareストアフロントにおける注文更新機能の設計を記述したものである。本機能は、支払方法を変更した注文の支払いを再処理し、注文を完了させる機能を提供する。

### 本機能の処理概要

注文更新機能は、注文編集画面で支払方法を変更した後、新しい支払方法での決済処理を実行し、注文を完了状態に遷移させる機能を提供する。

**業務上の目的・背景**：ECサイトにおいて、支払いが完了していない注文（決済エラー、後払い選択後の変更等）に対して、顧客が支払方法を変更して再度決済を試みるケースがある。この機能により、顧客は自己解決で支払いを完了でき、販売機会の損失を防ぐことができる。また、カスタマーサポートへの問い合わせを削減し、運用コストの低減にも寄与する。

**機能の利用シーン**：
- 決済エラーが発生した後、別の支払方法で再試行する場合
- 後払いから即時決済に変更して支払いを完了する場合
- クレジットカード情報を更新して決済を再実行する場合

**主要な処理内容**：
1. 注文データの取得と検証
2. 支払い変更可能状態の確認
3. 通貨の整合性チェックとコンテキスト切り替え
4. 支払方法の設定（SetPaymentOrderRoute）
5. 支払処理の実行（HandlePaymentMethodRoute）
6. 成功時は注文完了ページへ、失敗時はエラーページへリダイレクト

**関連システム・外部連携**：
- AbstractOrderRoute: 注文データ取得API
- AbstractSetPaymentOrderRoute: 支払方法設定API
- AbstractHandlePaymentMethodRoute: 支払処理実行API
- AbstractContextSwitchRoute: コンテキスト切り替えAPI
- OrderService: 注文サービス（支払変更可能判定）
- 外部決済プロバイダ（PSP）

**権限による制御**：
- 注文の所有者（顧客本人）のみ実行可能
- 支払い変更可能な状態の注文のみ

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | 注文編集ページ | 主画面 | 支払いの再処理（AccountOrderController::updateOrder） |
| 11 | 注文完了ページ | 遷移先画面 | 支払い成功後の表示 |

## 機能種別

CRUD操作（Update） / 決済処理 / ステータス遷移

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| orderId | string | Yes | 対象注文ID（URLパラメータ） | UUID形式 |
| その他 | mixed | No | リクエストボディの全パラメータ | 決済プロバイダ依存 |

### 入力データソース

- URLルートパラメータ（orderId）
- HTTPリクエストパラメータ（POST）
- SalesChannelContext（顧客情報、支払方法情報）

## 出力仕様

### 出力データ

出力データはなく、リダイレクトレスポンスのみ返却される。

### 出力先

- 成功時: `frontend.checkout.finish.page`（注文完了ページ）
  - パラメータ: orderId, changedPayment=true
- 失敗時: `frontend.checkout.finish.page`（支払失敗表示）
  - パラメータ: orderId, changedPayment=true, paymentFailed=true
- 外部決済の場合: 決済プロバイダからのリダイレクトURL

## 処理フロー

### 処理シーケンス

```
1. updateOrder（POST /account/order/update/{orderId}）
   └─ finishUrl, errorUrlを生成
   └─ 注文データを取得
   └─ 注文存在チェック
   └─ 支払い変更可能状態チェック
   └─ 通貨整合性チェック
      └─ 不一致の場合はコンテキスト切り替え
   └─ SetPaymentOrderRouteRequestEventを発火
   └─ AbstractSetPaymentOrderRoute.setPaymentを呼び出し
   └─ HandlePaymentMethodRouteRequestEventを発火
   └─ AbstractHandlePaymentMethodRoute.loadを呼び出し
   └─ 決済結果に応じてリダイレクト
      └─ 成功: 完了ページ
      └─ リダイレクト: 決済プロバイダURL
      └─ 失敗: エラーページ
```

### フローチャート

```mermaid
flowchart TD
    A[注文更新リクエスト] --> B[finishUrl/errorUrl生成]
    B --> C[注文データ取得]
    C --> D{注文存在?}
    D -->|No| E[OrderNotFoundException]
    D -->|Yes| F{支払変更可能?}
    F -->|No| G[PaymentMethodNotChangeableException]
    F -->|Yes| H{通貨一致?}
    H -->|No| I[コンテキスト通貨切り替え]
    I --> J[コンテキスト再取得]
    H -->|Yes| K[SetPaymentOrderRoute.setPayment]
    J --> K
    K --> L[HandlePaymentMethodRoute.load]
    L --> M{決済結果}
    M -->|成功| N[完了ページへリダイレクト]
    M -->|要リダイレクト| O[決済プロバイダURLへ]
    M -->|PaymentException| P[エラーページへリダイレクト]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-26-01 | 注文存在確認 | 指定されたorderIdの注文が存在すること | 常時 |
| BR-26-02 | 支払変更可能確認 | 注文トランザクションの状態が支払い変更可能であること | 常時 |
| BR-26-03 | 通貨整合性 | 注文の通貨とコンテキストの通貨が一致すること（不一致時は自動切り替え） | 常時 |
| BR-26-04 | finishUrlパラメータ | changedPayment=trueを付与して完了ページへ遷移 | 常時 |

### 計算ロジック

特になし（決済金額は既存の注文データを使用）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 注文取得 | order | SELECT | 対象注文の取得 |
| トランザクション取得 | order_transaction | SELECT | 支払いトランザクション状態確認 |
| 支払方法設定 | order_transaction | UPDATE | 新しい支払方法の適用 |
| トランザクション状態更新 | order_transaction | UPDATE | 支払い結果によるステータス更新 |

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

#### order_transaction

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | orderId = {orderId} | トランザクション状態確認 |
| UPDATE | paymentMethodId | 新しい支払方法ID | SetPaymentOrderRouteで更新 |
| UPDATE | stateId | 決済結果に応じたステータス | HandlePaymentMethodRouteで更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ORDER_NOT_FOUND | 注文不在 | 指定されたorderIdが存在しない | 注文一覧へリダイレクト、エラーメッセージ表示 |
| PAYMENT_METHOD_NOT_CHANGEABLE | 支払変更不可 | トランザクション状態が変更不可 | 例外スロー |
| PaymentException | 決済エラー | 決済処理に失敗 | 完了ページへリダイレクト（paymentFailed=true） |

### リトライ仕様

決済エラー発生時は、完了ページでエラーを表示。ユーザーは再度注文編集画面から支払方法を変更して再試行可能。

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

- SetPaymentOrderRouteとHandlePaymentMethodRouteは個別のトランザクションで実行
- 外部決済プロバイダとの通信は非同期の場合があり、Webhookによるステータス更新が後から行われる場合がある

## パフォーマンス要件

- 注文取得・検証: 500ms以内
- 支払処理: 決済プロバイダに依存（通常3-10秒）
- リダイレクト決済の場合はユーザー操作時間を含む

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

- POSTメソッドのみ受付（CSRF保護）
- 注文所有者の検証
- 決済情報は直接取り扱わず、PSPにリダイレクトまたはトークン化
- finishUrl/errorUrlは同一ドメイン内のみ許可

## 備考

- 決済プロバイダによっては外部サイトへのリダイレクトが発生する（PayPal、Stripe等）
- Webhookによる非同期通知の場合、完了ページ表示時点で決済が完了していない可能性がある
- v6.8.0.0以降の変更により、通貨の整合性チェックロジックに変更の可能性あり

---

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

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

### 推奨読解順序

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

注文トランザクションに関わるデータ構造を理解。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | OrderTransactionEntity.php | `src/Core/Checkout/Order/Aggregate/OrderTransaction/OrderTransactionEntity.php` | トランザクションエンティティ、paymentMethodId、stateId |
| 1-2 | OrderTransactionStates.php | `src/Core/Checkout/Order/Aggregate/OrderTransaction/OrderTransactionStates.php` | トランザクションステータス定数 |

**読解のコツ**: OrderTransactionEntityは支払い処理の状態を管理し、各決済試行に対して1レコードが作成される。

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

処理の起点となるコントローラーを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AccountOrderController.php | `src/Storefront/Controller/AccountOrderController.php` | updateOrderメソッド |

**主要処理フロー**:
1. **302-379行目**: `updateOrder`メソッド - 注文更新処理
2. **309-312行目**: finishUrl生成
3. **314-316行目**: 注文データ取得
4. **318-319行目**: 注文存在チェック
5. **322-324行目**: 支払変更可能チェック
6. **326-339行目**: 通貨整合性チェックとコンテキスト切り替え
7. **341行目**: errorUrl生成
8. **343-349行目**: SetPaymentOrderRoute呼び出し
9. **351-365行目**: HandlePaymentMethodRoute呼び出し
10. **362-373行目**: PaymentException処理
11. **375-378行目**: リダイレクト処理

#### Step 3: Store-APIルートを理解する

支払処理に使用されるAPIルートを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AbstractSetPaymentOrderRoute.php | `src/Core/Checkout/Order/SalesChannel/AbstractSetPaymentOrderRoute.php` | 支払方法設定API |
| 3-2 | AbstractHandlePaymentMethodRoute.php | `src/Core/Checkout/Payment/SalesChannel/AbstractHandlePaymentMethodRoute.php` | 支払処理実行API |

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

```
AccountOrderController
    │
    └─ updateOrder (POST /account/order/update/{orderId})
           │
           ├─ generateUrl('frontend.checkout.finish.page')
           │
           ├─ AbstractOrderRoute.load
           │      └─ 注文データ取得
           │
           ├─ OrderService.isPaymentChangeableByTransactionState
           │      └─ 支払変更可能判定
           │
           ├─ ContextSwitchRoute.switchContext (通貨不一致時)
           │
           ├─ SetPaymentOrderRouteRequestEvent dispatch
           │
           ├─ AbstractSetPaymentOrderRoute.setPayment
           │      └─ 支払方法をトランザクションに設定
           │
           ├─ HandlePaymentMethodRouteRequestEvent dispatch
           │
           └─ AbstractHandlePaymentMethodRoute.load
                  │
                  ├─ 成功 → 完了ページリダイレクト
                  ├─ 外部決済 → PSPへリダイレクト
                  └─ PaymentException → エラーページリダイレクト
```

### データフロー図

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

POSTリクエスト ─────────────────┬──────────────────────────────────► リダイレクト
(/account/order/update/{orderId})                                   レスポンス
                               │
                               ▼
                    ┌─────────────────────┐
                    │ AccountOrder        │
                    │ Controller          │◄──── order
                    │ ::updateOrder       │      order_transaction
                    └─────────┬───────────┘
                              │
                              ▼
                    ┌─────────────────────┐
                    │ SetPaymentOrder     │
                    │ Route               │────────────────────────► order_transaction
                    └─────────┬───────────┘                          (UPDATE paymentMethodId)
                              │
                              ▼
                    ┌─────────────────────┐
                    │ HandlePayment       │
                    │ MethodRoute         │────────────────────────► order_transaction
                    └─────────┬───────────┘                          (UPDATE stateId)
                              │                                      外部PSPへの通信
                              ▼
                    ┌─────────────────────┐
                    │ リダイレクト        │
                    │ → finish.page       │
                    │ or PSP URL          │
                    └─────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AccountOrderController.php | `src/Storefront/Controller/AccountOrderController.php` | ソース | コントローラー（エントリーポイント） |
| AbstractOrderRoute.php | `src/Core/Checkout/Order/SalesChannel/AbstractOrderRoute.php` | ソース | 注文取得API抽象クラス |
| AbstractSetPaymentOrderRoute.php | `src/Core/Checkout/Order/SalesChannel/AbstractSetPaymentOrderRoute.php` | ソース | 支払方法設定API抽象クラス |
| AbstractHandlePaymentMethodRoute.php | `src/Core/Checkout/Payment/SalesChannel/AbstractHandlePaymentMethodRoute.php` | ソース | 支払処理API抽象クラス |
| OrderService.php | `src/Core/Checkout/Order/SalesChannel/OrderService.php` | ソース | 注文サービス |
| SetPaymentOrderRouteRequestEvent.php | `src/Storefront/Event/RouteRequest/SetPaymentOrderRouteRequestEvent.php` | ソース | 支払設定イベント |
| HandlePaymentMethodRouteRequestEvent.php | `src/Storefront/Event/RouteRequest/HandlePaymentMethodRouteRequestEvent.php` | ソース | 支払処理イベント |
| OrderTransactionEntity.php | `src/Core/Checkout/Order/Aggregate/OrderTransaction/OrderTransactionEntity.php` | ソース | トランザクションエンティティ |
