# SCS-014　特約商店收單服務（merchant-service）程式設計規格

| 項目 | 內容 |
|---|---|
| 文件編號 | SCS-014 |
| 版本 | **1.5**（§7.2 **已實作並真機驗證**：契約重生／V4 遷移／DDD 四層／11 例真 Oracle＋真 Garage 整合測試；補 §7.2.6 實作狀態與三項誠實揭露） |
| 狀態 | **已核定（規格先行）** |
| 對應需求 | `ACQ-4.1`（商戶管理）、`ACQ-4.2`（商店請款）、`ACQ-4.3`（商店對帳單） |
| 服務埠號 | 待骨架產生時依 `CSP-006 §4` 分配（自 8081 起編） |
| 套件根 | `com.beam.cardcore.mch` |
| 權威 schema | `CC_MCH` |
| 關聯文件 | `SDD-001 §5.9`、`SDD-002 §3.6`、`BRD-002`、`GOV-005` |

---

## 1. 對應需求

| FR 編號 | 需求名稱 | 來源 |
|---|---|---|
| `ACQ-4.1` | 商戶管理（特約商店） | `BRD-002` |
| `ACQ-4.2` | 商店請款 | `BRD-002` |
| `ACQ-4.3` | 商店對帳單 | `BRD-002` |

### 1.1 上層設計依據（本規格**不重複**這些圖，僅引用）

| 圖種 | 文件 | 數量（實測） | 本服務相關 |
|---|---|---|---|
| **ER-Diagram** | `SDD-001_領域模型與ER-Diagram` | 16 個 `erDiagram` | `MERCHANTS`/`MERCHANT_SETTLEMENTS` 實體（`SDD-001 §5.9`） |
| **DFD** | `SDD-002_DFD與流程分解` | 10 個 flowchart | 收單處理流（`SDD-002 §3.6`） |
| **Sequence** | `SDD-003_Sequence-Diagrams` | 15 個 `sequenceDiagram` | 商戶請款／對帳時序 |
| **狀態機** | `SDD-005_狀態機設計` | 10 個 `stateDiagram` | ⚠️ `MERCHANTS.status`（ACTIVE→SUSPENDED→TERMINATED）以 CHECK 控管，未定義正式狀態機 |
| **API Contract** | `SDD-006` + `contracts/openapi/merchant-service.yaml` | 17 份 | 本服務 **7 支端點**（特店 2／請款 1／對帳單 4） |

---

## 2. 服務職責與邊界

**承作**：
- 商戶主檔（`MERCHANTS`：名稱、類型、MCC、組織、收單行、終端機、風險等級、KMC 類別）
- 商戶結算（`MERCHANT_SETTLEMENTS`：毛額／費用／稅／淨額）
- 商戶請款、對帳單

**不做（屬他服務）**：
- 清算對帳 → `clearing-service`
- 交易授權 → `authorization-service`

---

## 3. 領域模型

- 聚合：`Merchant`（商戶）、`MerchantContract`（契約）、`MerchantSettlement`（結算，依 `services.yaml` aggregates）
- 值物件：`MerchantType`、`RiskLevel`、`OrgCode`、`SettlementStatus`、`Money`、`Currency`
- 狀態：`MERCHANTS.status ∈ {ACTIVE, SUSPENDED, TERMINATED}`（CHECK，預設 ACTIVE）；`MERCHANT_SETTLEMENTS.status ∈ {PENDING, SETTLED}`

---

## 4. 程式結構

| 套件／類別 | 職責 | 對應需求 |
|---|---|---|
| `domain/model/Merchant` `MerchantContract` `MerchantSettlement` | 聚合與值物件 | `ACQ-4.1`/`4.2`/`4.3` |
| `domain/port/MerchantRepository` | 持久化埠 | — |
| `application/MerchantService` | 商戶管理／請款／對帳 | `ACQ-4.1` |
| `infrastructure/jdbc/MerchantRepositoryImpl` | JDBC 實作 | — |
| `interfaces/rest/MerchantController` | REST 端點 | `ACQ-4.1` |

---

## 5. 資料模型（約束為 Oracle 實測值）

| 表 | 關鍵欄位 | 約束（實測） |
|---|---|---|
| `MERCHANTS` | `MERCHANT_ID` VARCHAR2(24) **UNIQUE**；`MERCHANT_NAME` VARCHAR2(120)；**`MERCHANT_FORM`**（商店型態，V2）；`MERCHANT_TYPE`（**業別**）；`MCC`；`ORG`；`ACQUIRER_ID`；`TERMINAL_IDS` JSON DEFAULT `'[]'`；**`PARENT_MERCHANT_ID`** RAW(16)（V2，自參考）；**`ACCEPTED_CARD_TYPES` JSON DEFAULT `'[]'`**（V2）；`SETTLEMENT_BANK`；`SETTLEMENT_ACCOUNT_ENC` **BLOB**（加密）；`RISK_LEVEL` DEFAULT `'LOW'`；`KMC_CATEGORY`；`STATUS` DEFAULT `'ACTIVE'`；`ONBOARDED_AT` | `merchant_form IN ('SINGLE','FRANCHISE','CHAIN','GROUP')`；`merchant_type IN ('RETAIL','EC','MOTO','RESTAURANT','TRAVEL','SUPERMARKET','OTHER')`；`risk_level IN ('LOW','MEDIUM','HIGH')`；`CK_MERCHANTS_STATUS`：`status IN ('APPLIED','ACTIVE','SUSPENDED','TERMINATED','CLOSED')` |
| `MERCHANT_SETTLEMENTS` | `SETTLEMENT_NO` VARCHAR2(32) **UNIQUE**；`MERCHANT_ID`（FK→`MERCHANTS.ID`）；`ORG`；`SETTLEMENT_DATE`；**`BATCH_NO`** VARCHAR2(32)（V2）；`BATCH_COUNT`（**筆數**，非批次號）；`GROSS_AMOUNT`/`FEE_AMOUNT`/`TAX_AMOUNT`/`NET_AMOUNT` NUMBER(19)；`CURRENCY` CHAR(3) DEFAULT `'TWD'`；`STATUS` DEFAULT `'PENDING'`；**`EXCEPTION_REASON`** VARCHAR2(240)（V2） | `CK_MERCHANT_SETTLEMENTS_STATUS`：`status IN ('SUBMITTED','VERIFIED','EXCEPTION','PAID','PENDING','SETTLED')`；FK(`MERCHANT_ID`) REFERENCES `MERCHANTS`(`ID`) |

> 含 `CREATED_AT`/`UPDATED_AT` TIMESTAMP(6) WITH TIME ZONE、`VERSION` 樂觀鎖。`SETTLEMENT_ACCOUNT_ENC` 為 BLOB 加密欄，落實 `common-security` 欄位加密（`AesGcmFieldEncryptor`）。
>
> **⚠️ 兩軸之區分（`CR-015`，BEAM PMO 2026-09-22 裁定採雙欄位）**：`MERCHANT_FORM` 為
> **商店型態**（單店／加盟／連鎖／集團），`MERCHANT_TYPE` 為**業別**（做什麼生意）——
> 二者正交（一間「超市」可以是「連鎖」），原契約以單一 `merchantType` 承載兩者、
> 與 DDL 業別值域完全無交集。**契約之 `merchantType` 對映 `MERCHANT_FORM`**，
> 業別則以新增之 `industryType` 對映 `MERCHANT_TYPE`。
>
> **V1 基線已凍結**（`SAD-003 §9`）；上述 V2 變更見 `V2__merchant_form_and_status.sql`。

---

## 6. API 設計（來自 `contracts/openapi/merchant-service.yaml`）

| 端點 | 方法 | 說明 | 對應 FR |
|---|---|---|---|
| `/api/v1/merchants` | POST | 商戶建檔 | `ACQ-4.1` |
| `/api/v1/merchants/{merchantId}` | GET | 商戶查詢 | `ACQ-4.1` |
| `/api/v1/merchant-settlements` | POST | 商戶結算（請款／對帳） | `ACQ-4.2`/`ACQ-4.3` |

> 錯誤碼：`409` 商戶重複（`MERCHANT_ID`）、`404` 找不到、`400` 參數錯誤（如無效 `MERCHANT_TYPE`）。

---

## 7. 關鍵邏輯

- **商戶建檔**：`TERMINAL_IDS` 以 JSON 陣列存多終端；`SETTLEMENT_ACCOUNT_ENC` 加密儲存（PII）。
- **結算**：彙總請款批次得淨額（`NET_AMOUNT = GROSS - FEE - TAX`），觸發 `clearing-service` 對帳。
- **風險**：`RISK_LEVEL` 影響交易監控強度（與 `authorization-service` 協調）。

**`CR-015`（BEAM PMO 2026-09-22 裁定）後定案之實作要點**

1. **兩軸獨立**：建檔之 `merchantType`（商店型態）與 `industryType`（業別）分別落
   `MERCHANT_FORM`／`MERCHANT_TYPE`；值域各依 DDL CHECK 檢核，未知值回 `CC-PLT-4003`。
2. **特店代碼**：請求未提供 → 服務端以 `merchant_id_seq` 產生（`MERCH` ＋ 6 位連號，可稽核）；
   提供且已存在 → `CC-MRC-1001`（422）。
3. **冪等**：以冪等鍵**派生主鍵**（`UUID v3`，前綴 `mch:merchant:`／`mch:settlement:`）——
   同鍵重送命中既有列並回傳原結果，不新增、不重抽序號。
   ⚠️ 契約所述「同鍵**異指紋**回 409」**未實作**（無指紋欄位可存，不挪用他欄）。
4. **請款金額為 stand-in**：由呼叫方帶入 `grossAmount`／`feeAmount`／`taxAmount`
   （收單／清算資料源未就緒）；待 `clearing-service`（`C-5`）落地後改為服務端彙總。
5. **異常判定僅「淨額為負」**：落地為 `EXCEPTION` ＋ `EXCEPTION_REASON`（**不拋錯**，
   作業面可查、可更正後重送）。**不納入**商戶狀態檢核（契約未定義；且無狀態轉換端點，
   加了將使正常路徑不可達——見 §10）。

### 7.1　事件輸出（Transactional Outbox；`CR-027` 批次 A-4）

**掛點**：`MerchantSettlementService.submit` 於 `settlementRepository.save(settlement)`
**之後**、**同一交易**內 append。

**事件**：`merchant.settlement.submitted.v1`（`SAD-002 §3.13`、`SDD-006 §4.2`）

| 項目 | 值 | 依據 |
|---|---|---|
| Kafka 主題 | **`cc.merchant.settlement.submitted`** | `OutboxPublisher.topicFor` |
| 分區鍵 | `aggregateId` ＝ **`settlementId`** | `SAD-004 §5.4`（同聚合有序） |
| `aggregate_type` | `merchant` | — |
| `event_type`（outbox 欄） | **`settlement.submitted`**（**不含**版本） | `SDD-001 §3.15` |
| `event_version` | `1` | — |
| payload | `settlementId`／`merchantId`／`grossAmount`／`feeAmount`／`netAmount`（**恰 5 欄**） | `SDD-006 §4.2` |

> **主題／`event_type` 之推導**：依 `SAD-004 §5.4` 既有慣例——事件全名去 `.vN` 後，
> **再移除一層與 `aggregate_type` 相同之首段**（`merchant.settlement.submitted.v1`
> → `settlement.submitted`）。

**payload 欄位之來源對映**（**不得自行推導**）：

| payload 欄 | 來源 | 說明 |
|---|---|---|
| `settlementId` | `settlement.id()` | 請款單主鍵（**亦為分區鍵**） |
| **`merchantId`** | **`merchant.merchantId()`** | ⚠️ **業務代碼**（如 `MERCH000002`），**非**資料庫主鍵 UUID——依 `CR-019 §3.5` 裁定 (a)：對外 `merchantId` 一律為業務代碼（與 `MerchantView`／`MerchantSettlementView` 一致） |
| `grossAmount` | `settlement.grossAmount()` | 請款總額（分） |
| `feeAmount` | `settlement.feeAmount()` | 手續費（分） |
| `netAmount` | `settlement.netAmount()` | 淨額（分）＝ `gross − fee − tax`（`§7` 結算公式，由聚合推導） |

> ⚠️ **金額一律最小單位（分）**（`SDD-006 §2.5`）——**禁止浮點**。
> ⚠️ payload **不含** `taxAmount`／`currency`（契約如此）；`netAmount` 已內含稅之影響。

**發射條件**：`submit` 成功建立請款單即發射**恰一則**。
**不發射之情形**：

| 情形 | 原因 |
|---|---|
| 冪等重播（同鍵派生主鍵命中既有列） | **早退於寫入之前** → **不發射** |
| 查無特店（`CC-PLT-4040`，404） | **拋錯於寫入之前** → **不發射** |
| 淨額為負 → `EXCEPTION` | **仍發射**——請款單**確實已建立**（僅狀態為異常），如實反映（`§7` 要點 5：不拋錯） |

**消費者**：`clearing`（`SAD-004 §5.2`）——⚠️ **尚未實作**（批次 C）。

---

### 7.2　商店對帳單（`ACQ-4.3`；`CR-051` 裁定，2026-10-17）

> ✅ **本節狀態＝已核定**（BEAM PMO 2026-10-17）：含 S-1～S-6 凍結語意、**S-3／S-4 補充**（種類／郵寄方式）、
> **S-7 API 形態**、資料模型承載方式（SCS＋Flyway）。核定後方進入 `gen_openapi.py` 契約與 Flyway 遷移。
> 需求的由來：RFP `FR.md §4.3`（列印週期／種類／郵寄方式／補印／電子發票）**原無任何承作**，
> 契約與 `SDD-006 §7` 亦無 statements 路徑（`CR-050 §1`）。

#### 7.2.1 凍結語意（摘自 `CR-051 §5` BEAM PMO 裁定）

| 裁定 | 本服務落地 |
|---|---|
| **S-1 (b) 本平台全責** | 本平台負責對帳單之**資料彙總＋文件產出**（列印／PDF／電子發票）——**不再**歸衛星系統 |
| **S-2 月結跨自然月** | 週期起訖**可設定**（例：每月 21 日～次月 20 日），**不對齊自然月** |
| **S-3 無範本 → 我方設計、可替換** | 範本**外置／參數化**（`template_code`），客戶提供後替換，**不寫死於程式** |
| **S-4 遞送歸銀行 mail room／委外** | 本平台產出後**交付 mail room**；**遞送記錄留在平台**（誰／何時／何批／交予誰／狀況） |
| **S-3 補充：種類**（全額發票／全收據(自然人)，RFP `§4.3 #2`） | 作為**特店屬性參數** `ticket_type`（`INVOICE`／`RECEIPT`）；產出時讀取並**留存於對帳單主檔**（留痕——避免日後改參數使**歷史對帳單語意漂移**） |
| **S-4 補充：郵寄方式**（1-掛號／2-限時／5-平信，RFP `§4.3 #3`） | 預設值由參數提供，實際值記錄於遞送記錄 `mail_type`（`REGISTERED`／`EXPRESS`／`ORDINARY`），供 mail room 委外追蹤 |
| **S-5 補印無上限、免授權** | **每次補印皆留痕**（稽核軌跡），不設次數上限、不需授權流程 |
| **S-6 電子發票 傳檔為主** | 產出**檔案**供財政部網頁 upload（主路徑）＋MQ 為輔 |
| **S-7**（依 S-1=(b) 推導） | API 含**供料查詢＋產出＋檔案匯出／遞送**（**待確認**） |

#### 7.2.2 參數化要項（`CR-045` 三位一體）

* **週期起訖**（`cycle`／`period_from`／`period_to`）——由參數或排程設定決定；
* **對帳單範本**（`template_code`）——可抽換；
* **發票／收據種類**（`ticket_type`＝`INVOICE`／`RECEIPT`）——**特店屬性參數**，產出時留存（留痕）；
* **郵寄方式**（`mail_type`＝`REGISTERED` 掛號／`EXPRESS` 限時／`ORDINARY` 平信；對齊 RFP `§4.3 #3` 之 1／2／5）
  ——預設由參數提供，實際值留存於遞送記錄；
* **補印次數上限**——目前＝**無上限**（參數保留，供日後設定）；
* **遞送通道**（`MAIL_ROOM`／`OUTSOURCE`／`E_INVOICE_UPLOAD`／`MQ`）。

> ★ **留痕原則**：參數可被調整，故「當次產出所用之值」一律**寫入對帳單主檔／遞送記錄**——
> 事後改參數**不得**改變已產出對帳單之語意（與 `SCS-002 §7.9.1`「軌跡 vs 現行定義」同原則）。

→ 參數缺值時一律 **fail-closed**（**不臆造**）；`merchant_settlements` **維持請款語意、不新增週期欄**
（`CR-050 §2.1`：不得以請款混充對帳單）。

#### 7.2.3 資料模型方向（新增表，待 Flyway 遷移）

* `merchant_statements`（對帳單主檔）：`merchant_id`、`cycle`、`period_from`、`period_to`、
  `ticket_type`（**留痕**）、`template_code`（**留痕**）、`status`、`file_ref`、`version`…
  ——週期由參數／排程給定。
* `statement_deliveries`（**遞送記錄**）：`statement_id`、`channel`、`mail_type`、`handed_to`、
  `handed_at`、`tracking_ref`、`status`——滿足 S-4「遞送外部化但**軌跡在平台**」。
* ⚠️ **資料模型承載方式（2026-10-17 BEAM PMO 核定）**：本項屬**新增能力**（不與既有語意衝突），
  由 **`SCS-014` ＋ Flyway 新版本**直接承載，**不另立** `CR-053` 式之資料模型 CR。
* **補印軌跡**：每次產出／補印於上述二表留痕（S-5）。
* **資料來源**：既有 `merchant_settlements`（`settlement_date`、`batch_count`、`gross/fee/tax/net_amount`）；
  索引 **`idx_ms_merchant (merchant_id, settlement_date DESC)` 已可支援「依特店＋日期區間」彙總**——日／兩日／月結皆可由日期區間得出。
* ⚠️ 新表屬**結構新增**，須以 **Flyway 新版本**交付（已套用之遷移不得修改，`SAD-003 §9`）。

#### 7.2.4 API 形態（草案；由 `gen_openapi.py` 規格先行）

| 方法 | 路徑（草案） | 用途 |
|---|---|---|
| `GET` | `/api/v1/merchant-statements?merchantId&from&to&cycle` | 供料／查詢 |
| `POST` | `/api/v1/merchant-statements/generate` | 產出（**補印＝同一 API 重產並留痕**；`ticket_type`／`template_code` 由參數帶入並留存） |
| `GET` | `/api/v1/merchant-statements/{id}/file` | 檔案匯出（供財政部網頁 upload） |
| `POST` | `/api/v1/merchant-statements/{id}/deliveries` | 遞送記錄登錄（S-4；含 `mail_type`、`channel`、`handed_to`、`tracking_ref`） |

★ **S-7 已確認**（BEAM PMO 2026-10-17 核定）：即上列 4 支（供料查詢＋產出＋檔案匯出＋遞送記錄）。
⚠️ 路徑須經 `gen_openapi.py` 產生後生效；**不得**以 `merchant-settlements` 混充（`CR-050 §2.1`）。

#### 7.2.5 守界

* 核定前**不動程式、不改表結構**；
* **回溯不得臆造**：歷史請款不得反推對帳單（類推 `V10`「既有佔用無 `instrument_id`、不得回填」之鐵律）；
* 產出失敗／遞送失敗皆**留痕且可重試（冪等）**；遞送之**實際投遞**為外部（mail room／委外），
  本平台僅記錄**交付與軌跡**，不宣稱投遞成功。

#### 7.2.6 實作狀態（2026-10-17，`#A-284`）

| 項目 | 狀態 | 證據 |
|---|---|---|
| 契約 4 端點＋5 schema | ✅ 已重生 | `gen_openapi.py` → 110 端點／0 錯誤；`verify_contract_dto` **不符 0** |
| 查詢參數（`merchantId`／`from`／`to`／`cycle`） | ✅ 已補 | 原產生器僅帶 `TraceIdHeader`（§7.2.4 已列參數卻未生成）→ 已補**專屬** query 元件 |
| `V4__merchant_statements.sql` | ✅ 已建 | `merchant_statements`／`statement_deliveries`＋`merchant_statement_no_seq`；`validate_sql` 違規 0 |
| DDD 四層＋REST | ✅ 已實作 | domain 不變式、jdbc（含真彙總）、service（冪等／留痕／檔案）、controller 4 端點 |
| 真機驗證 | ✅ 11 例 | `MerchantStatementServiceIntegrationTest`：真 Oracle（V1～V4）＋真 **Garage** ＋真服務編排，**零替身** |

**★ 期間查詢語意（契約已逐字載明，實作須一致）**：`from`／`to` 為**閉區間**且
「對帳單期間**完全落在**查詢範圍內」（`PERIOD_FROM >= from` 且 `PERIOD_TO <= to`）——
與請款交易查詢之**半開** `(from, to]` **語意不同**。實測：月結單 `09-21～10-20`
以 `09-01～09-30` 查詢**不命中**（正確），放寬至 `09-21～10-31` 才命中。

**★ 檔案產出之現況（如實揭露）**：本期產出 **CSV 資料檔**（UTF-8 含 BOM），內容為
對帳單標頭＋期間內請款明細；物件鍵以**對帳單主鍵**為準（`statements/<id>.csv`），
使同冪等鍵重試時覆蓋同一鍵、**不產生孤兒物件**。
RFP 所述「列印」之 **PDF 版式**待 S-3 範本到位後抽換（`templateCode` 已留痕）
——**不以 CSV 冒充已具備列印版式**。

**★ 遞送狀態判定**：契約 `StatementDeliveryRequest` **無** `status` 欄位 →
由服務端依「是否載明交付對象」判定：`handedTo` 有值 → `HANDED_OFF`（`handedAt`＝當下）；
否則 `PENDING`。`FAILED` 現行**無觸發來源**（契約無失敗回報端點），保留給未來外部結果回報，
**不可達**。

**★ 郵寄方式參數之適用範圍**：參數預設（`default-mail-type`）**僅套用於實體郵寄通道**
（`MAIL_ROOM`／`OUTSOURCE`）。電子發票（`E_INVOICE_UPLOAD`／`MQ`）本無「掛號／限時／平信」
語意，若一併套用會使軌跡出現**誤導性郵寄方式**（看似掛號寄出、實為傳檔）→ 記 `null`。
（★ 此為實作期發現之語意瑕疵，已於真機測試揭露並修正。）

---

## 8. 外部依賴與降級

| 依賴 | 失敗處理（Stand-in） |
|---|---|
| `clearing-service`（對帳）、`authorization-service`（風險參考） | 結算失敗重試（冪等）；商戶查詢為讀取，無即時強依賴 |

---

## 9. 測試案例（對應 `TRP-001`）

- `UT-ACQ-001`：商戶管理（`ACQ-4.1`）——建檔、查詢、加密欄位
- `UT-ACQ-002`：商店請款（`ACQ-4.2`）
- `UT-ACQ-003`：商店對帳單（`ACQ-4.3`）、風險等級邏輯

---

## 10. 已知限制（誠實揭露）

- `MERCHANTS.status` 轉換未建模為 `SDD-005` 正式狀態機
  ——**故本服務不提供狀態轉換端點**（核准／停用／終止），特店建檔後恆為 `APPLIED`。
- `SETTLEMENT_ACCOUNT_ENC` 之金鑰管理（KMC）與 `common-security` 之整合待基礎設施設計凍結
  ——本期建檔**不接收**該欄位（避免半套加密）。
- ~~`merchant-service` 骨架與埠號尚未產生（規格先行）~~ → **已產生，埠號 8095，已啟動實測**。

**`CR-015` 執行後新增之限制（2026-09-22）**

1. **請款金額為 stand-in**：由呼叫方帶入（`clearing-service`／`C-5` 未開發，服務端無處彙總）；
   待 `C-5` 落地後改為服務端彙總（`CR-015 §3.1 J`）。
2. **「同鍵異指紋回 409」未實作**：兩表無指紋欄位可存，且不挪用他欄（避免假綠）。
   已實作之冪等為「同鍵同主鍵」（不重複建檔）——涵蓋實際風險（重複建檔）。
3. **商戶狀態檢核未納入請款異常判定**：契約未定義「異常」清單；且無狀態轉換端點，
   若加「非 `ACTIVE` 不得請款」將使「建檔 → 請款」之正常路徑**永不可達**
   （與 `C-19` 之 `ALLOCATED` 同類陷阱）。待狀態機建模時一併裁定。
4. **`MERCHANT_SETTLEMENTS` 之 `PENDING`／`SETTLED` 為 DDL 遺留值**：已併入同一值域供相容，
   惟本服務 API **不產生**此二值（契約面為 `SUBMITTED`／`VERIFIED`／`EXCEPTION`／`PAID`）。
5. **無請款查詢端點**：契約僅 3 端點（建檔／查詢／請款），請款後之查詢須待契約擴充。
6. ~~**對帳單（`ACQ-4.3`）之檔案產出未實作**~~ → **已實作（2026-10-17，`#A-284`）**：
   契約 4 端點、`V4` 二表、DDD 四層與真機 11 例整合測試皆完成。**惟仍有三項限制**（見下）。

**`CR-027` 批次 A-4 執行後新增之限制（2026-10-01）**：

7. **事件消費者未實作**：`SAD-004 §5.2` 載 `merchant.settlement.submitted.v1` 之消費者為
   `clearing`，惟其**尚未訂閱** → 現階段「**有人發、沒人收**」（批次 C）。見 §7.1。
8. **`merchant.merchant.registered.v1` 未發射**：`SAD-002 §3.13` 列本服務發布該事件，
   惟 `SDD-006 §4.2` **未定義其 payload**（`CR-027 §6 批次 A-4` 表列為「⬜ 缺」）
   → **不臆造 payload**；待補契約後再議。
9. **`sequenceNo` 為「同 JVM 內遞增之時間戳基底序號」**：**多實例**部署可能重複；
   正解為「聚合層級持久化序號」（`SAD-004 §5.4`），本期未做（與 `CR-027 §5 #12` 同型）。

**對帳單實作後新增之限制（2026-10-17，`#A-284`）**：

10. **檔案格式為 CSV，非 PDF**：RFP 所述「列印」之版式檔待 S-3 範本到位後抽換
    （`templateCode` 已留痕）。**不以 CSV 冒充已具備列印版式**（見 §7.2.6）。
11. **遞送 `FAILED` 狀態不可達**：契約無失敗回報端點，本平台不臆造「推測失敗」規則
    ——該值保留給未來外部結果回報（見 §7.2.6）。
12. **對帳單不發射領域事件**：`SAD-002 §3.13`／`SDD-006 §4.2` 皆**未定義**對帳單事件之
    payload → **不臆造**（同 `#8` 之 `merchant.registered.v1` 處理原則）。

---

## 11. 修訂記錄

| 版本 | 日期 | 修訂者 | 說明 |
|---|---|---|---|
| 1.5 | 2026-10-17 | WorkBuddy AI | **§7.2 已實作並真機驗證（`#A-284`）**——① 契約重生（110 端點／0 錯誤，`verify_contract_dto` 不符 0）；② **補 GET 查詢參數**（`merchantId` 必填＋`from`／`to`／`cycle`；原產生器只帶 `TraceIdHeader`，與 §7.2.4 明列參數不符）；③ `V4__merchant_statements.sql`（二表＋序號產生器，`validate_sql` 違規 0）；④ DDD 四層＋4 端點實作（冪等／留痕／真彙總／真檔案上傳）；⑤ **新增 §7.2.6**（實作狀態表、期間「完全落在」語意、CSV 非 PDF、`FAILED` 不可達、郵寄參數僅實體通道）；⑥ **§10 新增 3 項限制**（10～12）。★ 測試：merchant-service **77 測 0 失敗**，其中新增 **11 例**真 Oracle＋真 Garage 整合測試（零替身） |
| 1.4 | 2026-10-17 | WorkBuddy AI | **§7.2 已核定**（BEAM PMO 2026-10-17 依建議核准）——① 補入 RFP `§4.3 #2` **種類**（`ticket_type`＝INVOICE／RECEIPT，**特店屬性參數**、產出時**留痕**）與 `#3` **郵寄方式**（`mail_type`＝REGISTERED／EXPRESS／ORDINARY，對齊 1／2／5，留存於遞送記錄）；② 確立**留痕原則**（參數可調，但「當次產出所用值」須寫入主檔／遞送記錄，事後改參數不得改變歷史對帳單語意，同 `SCS-002 §7.9.1`）；③ **S-7 API 形態確認**（供料查詢／產出／檔案匯出／遞送記錄 4 支）；④ **資料模型承載方式**：屬新增能力、不與既有語意衝突，由 **SCS-014＋Flyway 新版本**承載，不另立資料模型 CR。⚠️ 核定前不動程式／不改表；回溯不得臆造 |
| 1.3 | 2026-10-17 | WorkBuddy AI | **新增 §7.2 商店對帳單（`ACQ-4.3`）**——依 `CR-051` BEAM PMO 2026-10-17 裁定之 **S-1～S-6 凍結語意**：S-1 **(b) 本平台全責**（含產出／電子發票）、S-2 月結**跨自然月**（週期可設定）、S-3 範本**暫由我方設計、可抽換**、S-4 遞送歸**銀行 mail room／委外**但**遞送記錄留平台**、S-5 補印**無上限／免授權／但留痕**、S-6 電子發票**傳檔為主**＋MQ 為輔。含**參數化要項**（週期起訖／範本／補印／遞送通道；缺值 fail-closed）、**資料模型方向**（`merchant_statements`／`statement_deliveries`，待 Flyway 新版本）、**API 草案**（由 `gen_openapi.py` 產生）與**守界**（核定前不動程式／回溯不得臆造）。⚠️ **本節狀態＝待核定** |
| 1.0 | — | 開發團隊 | 初版（規格先行） |
| 1.1 | 2026-09-22 | WorkBuddy AI | **`CR-015` 執行完成（BEAM PMO 裁定：A 雙欄位、B～G 一次核准）**——①**§5 資料模型**：補 `MERCHANT_FORM`／`PARENT_MERCHANT_ID`／`ACCEPTED_CARD_TYPES`／`BATCH_NO`／`EXCEPTION_REASON` 與兩具名約束，並載明**兩軸之區分**；②**§7** 新增實作要點（兩軸獨立、代碼產生、冪等派生主鍵、金額 stand-in、異常僅淨額為負）；③**§10** 移除「骨架未產生」、新增 6 項限制（stand-in／異指紋未實作／狀態檢核未納入／遺留值／無查詢端點／對帳單檔案未產出）；④**補建本修訂記錄區**（原文件欠缺）。單測 27 例 0 失敗；真機 e2e 7 項（含 DB 複核雙軸獨立） |
| 1.2 | 2026-10-01 | WorkBuddy AI | **`CR-027` 批次 A-4（`#A-137`）**：①**§7.1 新增事件輸出規格**——`merchant.settlement.submitted.v1`：主題／分區鍵／payload 逐欄來源對映／發射與否（冪等重播與查無特店**不發射**、淨額為負之 `EXCEPTION` **仍發射**）；②★ **明確 `merchantId` 為業務代碼**（`CR-019 §3.5` 裁定 (a)），**非**資料庫主鍵 UUID；③**§10 新增 3 項限制**（消費者未實作、`merchant.registered.v1` 無 payload 故未發射、`sequenceNo` 多實例風險） |
