# 通知設計書 25-Order Transaction State (Paid)

## 概要

本ドキュメントは、Shopwareの注文決済ステータスが「支払い完了（Paid）」に変更された際に送信されるメール通知の設計書である。

### 本通知の処理概要

本通知は、顧客からの支払いが完了した際に、決済完了と注文処理開始を通知するメール送信機能を提供する。これは最も重要な取引確認メールの一つである。

**業務上の目的・背景**：ECサイトにおいて、顧客からの支払いが正常に完了したことは、取引成立の重要な節目である。支払い完了を顧客に通知することで、取引が正常に処理されたことを確認してもらい、商品の発送を待つことができる。これにより顧客の安心感を提供し、不要な問い合わせを減らす効果がある。

**通知の送信タイミング**：注文決済（OrderTransaction）エンティティのステートマシンにおいて、決済ステータスが`paid`に遷移した時点でイベント`state_enter.order_transaction.state.paid`が発火し、Flowによってメール送信がトリガーされる。

**通知の受信者**：注文を行った顧客（OrderCustomer）のメールアドレス宛に送信される。顧客の氏名・敬称情報も取得し、パーソナライズされた宛名で送信する。

**通知内容の概要**：支払い完了の確認として、支払い受領と注文処理開始の旨、注文番号、注文内容詳細（商品一覧、価格、配送先、請求先）、支払い方法、配送方法、および注文詳細確認用のディープリンクURLが含まれる。注文確認メールと同等の詳細情報が含まれる。

**期待されるアクション**：顧客は通知を受け取り、支払いが完了し注文処理が開始されたことを認識する。注文内容を確認し、商品の発送を待つ。問い合わせが必要な場合は店舗に連絡する。

## 通知種別

メール通知

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（Flow経由） |
| 優先度 | 高 |
| リトライ | Flowの設定に依存 |

### 送信先決定ロジック

`OrderStateMachineStateChangeEvent`クラスの`getMailStruct()`メソッドにより、注文に紐づく顧客（`order.orderCustomer`）のメールアドレスと氏名を取得して送信先とする。

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | SalesChannelの設定による |
| 送信元名称 | SalesChannelの設定による |
| 件名 | テンプレートで定義（デフォルト: 支払い確認通知） |
| 形式 | HTML/テキスト両対応 |

### 本文テンプレート

```twig
{% set currencyIsoCode = order.currency.isoCode %}
{% if order.orderCustomer.salutation %}{{ order.orderCustomer.salutation.translated.letterName ~ ' ' }}{% endif %}{{ order.orderCustomer.firstName }} {{ order.orderCustomer.lastName }},

We received your payment and will now start processing the order.
Order number: {{ order.orderNumber }}

Information on your order:

[注文商品一覧テーブル]
- Prod. no. | Product image | Description | Quantities | Price | Total

{% for lineItem in order.nestedLineItems %}
  {{ lineItem.payload.productNumber }} | {{ lineItem.label }} | {{ lineItem.quantity }} | {{ lineItem.unitPrice|currency(currencyIsoCode) }} | {{ lineItem.totalPrice|currency(currencyIsoCode) }}
{% endfor %}

Shipping costs: {{ order.deliveries.first.shippingCosts.totalPrice|currency(currencyIsoCode) }}
Net total: {{ order.amountNet|currency(currencyIsoCode) }}
Total gross: {{ order.price.totalPrice|currency(currencyIsoCode) }}

Selected payment type: {{ order.transactions.first.paymentMethod.translated.name }}
Selected shipping type: {{ order.deliveries.first.shippingMethod.translated.name }}

Billing address: {{ billingAddress.firstName }} {{ billingAddress.lastName }}, {{ billingAddress.street }}, {{ billingAddress.zipcode }} {{ billingAddress.city }}
Shipping address: {{ delivery.shippingOrderAddress.firstName }} {{ delivery.shippingOrderAddress.lastName }}, ...

You can check the current status of your order on our website under "My account" - "My orders" anytime: {{ rawUrl('frontend.account.order.single.page', { 'deepLinkCode': order.deepLinkCode }, salesChannel.domains|first.url) }}
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| - | - | - | 基本的に添付ファイルなし（Flowで請求書等を設定可能） |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| order | 注文エンティティ | order テーブル | Yes |
| order.orderCustomer | 注文顧客情報 | order_customer テーブル | Yes |
| order.orderNumber | 注文番号 | order.order_number | Yes |
| order.orderDateTime | 注文日時 | order.order_date_time | Yes |
| order.nestedLineItems | 注文明細（ネスト対応） | order_line_item テーブル | Yes |
| order.transactions | 決済情報コレクション | order_transaction テーブル | Yes |
| order.transactions.first.stateMachineState | 決済ステータス | state_machine_state テーブル | Yes |
| order.transactions.first.paymentMethod | 支払い方法 | payment_method テーブル | Yes |
| order.deliveries | 配送情報コレクション | order_delivery テーブル | Yes |
| order.deliveries.first.shippingMethod | 配送方法 | shipping_method テーブル | Yes |
| order.currency | 通貨情報 | currency テーブル | Yes |
| order.addresses | 住所情報 | order_address テーブル | Yes |
| order.deepLinkCode | ディープリンクコード | order.deep_link_code | Yes |
| salesChannel | 販売チャネル情報 | sales_channel テーブル | Yes |
| a11yDocuments | アクセシビリティドキュメント | コンテキストから | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ステートマシン遷移 | state_enter.order_transaction.state.paid | Flowで設定 | 決済ステータスがpaidに変更された時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| Flowが無効 | 対応するFlowアクションが無効化されている場合 |
| メールテンプレート未設定 | テンプレートIDが設定されていない場合 |
| 受信者メールアドレスなし | 顧客のメールアドレスが取得できない場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[決済ステータス変更トリガー] --> B[StateMachineRegistry::transition]
    B --> C[StateMachineStateChangeEvent発火]
    C --> D[OrderStateChangeEventListener::onOrderTransactionStateChange]
    D --> E[OrderStateMachineStateChangeEvent発火]
    E --> F[Flow処理開始]
    F --> G[SendMailAction::handleFlow]
    G --> H{メールテンプレート存在?}
    H -->|Yes| I[テンプレート展開]
    H -->|No| J[処理終了]
    I --> K[AbstractMailService::send]
    K --> L{送信結果}
    L -->|成功| M[送信完了]
    L -->|失敗| N[エラーログ記録]
    M --> O[終了]
    N --> O
    J --> O
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| order | 注文情報取得 | |
| order_customer | 顧客情報取得 | メール宛先 |
| order_transaction | 決済情報取得 | ステータス確認 |
| order_line_item | 注文明細取得 | 商品一覧表示 |
| order_delivery | 配送情報取得 | |
| order_address | 住所情報取得 | 請求先・配送先 |
| payment_method | 支払い方法取得 | |
| shipping_method | 配送方法取得 | |
| currency | 通貨情報取得 | |
| state_machine_state | ステータス名取得 | 多言語対応 |
| sales_channel | 販売チャネル情報 | 送信元情報 |
| mail_template | メールテンプレート | 本文・件名 |

### テーブル別参照項目詳細

#### order

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | 注文識別 | transactionのorder_id経由 |
| order_number | 注文番号表示 | |
| order_date_time | 注文日時表示 | |
| deep_link_code | 詳細ページリンク | |
| sales_channel_id | 販売チャネル特定 | |
| amount_net | 税抜き金額 | |
| currency_id | 通貨特定 | |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| state_machine_history | INSERT | ステータス遷移履歴 |

#### 送信ログテーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | entity_name | 'order_transaction' | StateMachineRegistryで記録 |
| INSERT | from_state_id | 遷移前ステータスID | |
| INSERT | to_state_id | 遷移後ステータスID | |
| INSERT | transition_action_name | 'paid' / 'pay' | |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 送信失敗 | SMTPエラー等 | ログ記録、リトライはFlow設定依存 |
| テンプレートエラー | Twig構文エラー | ログ記録、送信中断 |
| 宛先不正 | メールアドレス形式不正 | MailEventConfigurationException |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Flowの遅延アクション設定に依存 |
| リトライ間隔 | Flowの遅延アクション設定に依存 |
| リトライ対象エラー | 一時的な送信失敗 |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | システム設定に依存 |
| 1日あたり上限 | システム設定に依存 |

### 配信時間帯

特に制限なし（イベント発生時に即時送信）

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

- 顧客のメールアドレス、氏名、住所等の個人情報を扱うため、適切なアクセス制御が必要
- 注文金額、支払い方法等の取引情報が含まれるため、メール送信先の正確性が重要
- ディープリンクコードにより、ログインなしで注文詳細にアクセス可能なため、URLの漏洩に注意
- テンプレート内でのTwig変数は自動エスケープされる

## 備考

- メールテンプレートタイプ: `order_transaction.state.paid`
- MailTemplateTypes定数: `MAILTYPE_STATE_ENTER_ORDER_TRANSACTION_STATE_PAID`
- 支払い完了メールは詳細な注文情報を含むため、注文確認メールに近い内容となる
- アクセシビリティ対応のHTMLドキュメント（a11yDocuments）が利用可能な場合、リンクが含まれる
- OrderTransactionStateHandlerのpaid()メソッドでもトリガー可能

---

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

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

### 推奨読解順序

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

まず、決済ステータスの定義とメールテンプレートタイプを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | OrderTransactionStates.php | `src/Core/Checkout/Order/Aggregate/OrderTransaction/OrderTransactionStates.php` | 決済ステータスの定数定義（STATE_PAID = 'paid'、行12） |
| 1-2 | MailTemplateTypes.php | `src/Core/Content/MailTemplate/MailTemplateTypes.php` | メールテンプレートタイプ定数（行62: MAILTYPE_STATE_ENTER_ORDER_TRANSACTION_STATE_PAID） |
| 1-3 | OrderTransactionStateHandler.php | `src/Core/Checkout/Order/Aggregate/OrderTransaction/OrderTransactionStateHandler.php` | paid()メソッド（行102-113）でのステータス遷移 |

**読解のコツ**: paid状態は完全支払い完了を意味する。部分支払い（paid_partially）とは区別される。

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

ステートマシン遷移時のイベント発火の仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | StateMachineRegistry.php | `src/Core/System/StateMachine/StateMachineRegistry.php` | transition()メソッド（行113-216）でステータス遷移とイベント発火 |
| 2-2 | StateMachineStateChangeEvent.php | `src/Core/System/StateMachine/Event/StateMachineStateChangeEvent.php` | イベントクラスの構造、getStateEventName()（行44-47）でイベント名生成 |

**主要処理フロー**:
1. **行113-116**: transition()メソッド開始、システムスコープで実行
2. **行136-151**: 遷移先ステートの取得
3. **行153-165**: ステートマシン履歴の記録
4. **行167-169**: エンティティのステート更新
5. **行181-193**: STATE_MACHINE_TRANSITION_SIDE_LEAVE イベント発火
6. **行195-207**: STATE_MACHINE_TRANSITION_SIDE_ENTER イベント発火

#### Step 3: イベントリスナーを理解する

注文決済固有のイベント処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | OrderStateChangeEventListener.php | `src/Core/Checkout/Order/Listener/OrderStateChangeEventListener.php` | onOrderTransactionStateChange()（行82-102）で決済ステータス変更をハンドリング |
| 3-2 | OrderStateMachineStateChangeEvent.php | `src/Core/Checkout/Order/Event/OrderStateMachineStateChangeEvent.php` | メール送信用のイベントクラス、MailAware実装 |

**主要処理フロー**:
- **行82-102**: onOrderTransactionStateChange() - 決済エンティティから注文を取得し、OrderStateMachineStateChangeEventを発火
- **行157-162**: dispatchEvent() - 実際のイベントディスパッチ

#### Step 4: メール送信処理を理解する

Flowによるメール送信の仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | SendMailAction.php | `src/Core/Content/Flow/Dispatching/Action/SendMailAction.php` | handleFlow()（行86-176）でメール送信処理を実行 |

**主要処理フロー**:
- **行86-96**: 設定の取得とスキップ判定
- **行100-111**: MailAwareデータの検証
- **行113-117**: メールテンプレートの取得
- **行126-134**: 受信者の決定
- **行136-155**: メールデータの構築
- **行175**: 実際のメール送信

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

```
Payment Gateway / OrderTransactionStateHandler::paid()
    │
    └─ StateMachineRegistry::transition()
           │
           ├─ StateMachineStateChangeEvent 発火
           │      └─ OrderStateChangeEventListener::onOrderTransactionStateChange()
           │             └─ OrderStateMachineStateChangeEvent 発火
           │                    └─ Flow Dispatcher
           │                           └─ SendMailAction::handleFlow()
           │                                  └─ AbstractMailService::send()
           │
           └─ StateMachineHistoryRepository::create() [履歴記録]
```

### データフロー図

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

OrderTransaction  ───▶  StateMachineRegistry          ───▶  state_machine_history
(stateId変更)            │                                   (遷移履歴INSERT)
                         ├─ transition()
                         │
                         ▼
                    StateMachineStateChangeEvent
                         │
                         ▼
                    OrderStateChangeEventListener
                         │
                         ├─ Order取得（詳細情報含む）
                         │   - lineItems
                         │   - deliveries
                         │   - addresses
                         │   - transactions
                         │
                         ▼
                    OrderStateMachineStateChangeEvent  ───▶  メール送信
                         │                                   (顧客宛・詳細情報付き)
                         ▼
                    Flow / SendMailAction
                         │
                         ▼
                    AbstractMailService::send()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| OrderTransactionStates.php | `src/Core/Checkout/Order/Aggregate/OrderTransaction/OrderTransactionStates.php` | ソース | 決済ステータス定数定義 |
| OrderTransactionStateHandler.php | `src/Core/Checkout/Order/Aggregate/OrderTransaction/OrderTransactionStateHandler.php` | ソース | 決済ステータス遷移ハンドラー |
| MailTemplateTypes.php | `src/Core/Content/MailTemplate/MailTemplateTypes.php` | ソース | メールテンプレートタイプ定数 |
| StateMachineRegistry.php | `src/Core/System/StateMachine/StateMachineRegistry.php` | ソース | ステートマシン遷移処理 |
| StateMachineStateChangeEvent.php | `src/Core/System/StateMachine/Event/StateMachineStateChangeEvent.php` | ソース | ステート変更イベント |
| OrderStateChangeEventListener.php | `src/Core/Checkout/Order/Listener/OrderStateChangeEventListener.php` | ソース | 注文ステート変更リスナー |
| OrderStateMachineStateChangeEvent.php | `src/Core/Checkout/Order/Event/OrderStateMachineStateChangeEvent.php` | ソース | 注文ステートマシンイベント |
| SendMailAction.php | `src/Core/Content/Flow/Dispatching/Action/SendMailAction.php` | ソース | メール送信アクション |
| en-html.html.twig | `src/Core/Migration/Fixtures/mails/order_transaction.state.paid/en-html.html.twig` | テンプレート | 英語HTMLテンプレート |
| en-plain.html.twig | `src/Core/Migration/Fixtures/mails/order_transaction.state.paid/en-plain.html.twig` | テンプレート | 英語プレーンテンプレート |
| de-html.html.twig | `src/Core/Migration/Fixtures/mails/order_transaction.state.paid/de-html.html.twig` | テンプレート | ドイツ語HTMLテンプレート |
| de-plain.html.twig | `src/Core/Migration/Fixtures/mails/order_transaction.state.paid/de-plain.html.twig` | テンプレート | ドイツ語プレーンテンプレート |
