# CR-021　`authorization-service` 三支契約端點**完全未實作**，與 `SDD-007 §5.1.2` 桶 A 之盤點錯誤

| 項目 | 內容 |
|---|---|
| 文件編號 | CR-021 |
| 版本 | **1.0** |
| 提出日 | 2026-09-23 |
| 提出者 | WorkBuddy AI（開發團隊） |
| 緣起 | `C-14` 桶 A 續作（授權策略檢核／離線授權與代授權／沖正機制三頁）之**動工前查證**：欲依 `SDD-007 §5.1.2` 桶 A 施作時，發現其依賴之三支端點**程式完全不存在** |
| 影響範圍 | `contracts/openapi/authorization-service.yaml`；`SDD-007 §5.1.2`（桶 A 盤點）；`CSP-001 §7.1`（G-8 檢查點）；`tools/verify_contract_dto.py`（新增端點存在性檢查） |
| 關聯前例 | `CR-020` F1（契約宣告欄位、實作未接收）——**同為「契約有、實作無」，本 CR 在端點層級**；`CR-016 §8`（回應型別不符） |

---

## 1. 問題定性：**契約宣告了消費端打不到的端點**

`contracts/openapi/authorization-service.yaml` 宣告 5 個端點，
而實作（`AuthorizationController`）**只有 3 個**：

| # | 契約端點 | 實作 | 判定 |
|---|---|---|---|
| 1 | `POST /api/v1/authorizations` | ✅ 有 | — |
| 2 | `GET /api/v1/authorizations` | ✅ 有 | — |
| 3 | `GET /api/v1/authorizations/{authNo}` | ✅ 有 | — |
| 4 | **`POST /api/v1/authorizations/{authId}/reversals`** | ❌ **無** | **未實作** |
| 5 | **`POST /api/v1/authorizations/{authId}/advices`** | ❌ **無** | **未實作** |
| 6 | **`GET /api/v1/authorizations/{authId}/check-results`** | ❌ **無** | **未實作** |

**實測方式**：全服務搜尋 `RestController` 僅得 1 支 `AuthorizationController`；
以 `reversals`／`advices`／`check-results`／`CheckResult`／`ReversalRequest`
為關鍵字搜尋 `src/main/java` **結果為 0**（連 DTO 都沒有）。

> ⚠️ 契約對這三支還**詳列了 requestBody schema、13 項檢核明細說明、錯誤碼**
> （如「已沖正回 `CC-AUTH-3002`（409）」「`PARTIAL` 必帶 `amount≤原金額`」），
> 消費端依契約開發將**全部得到 404**。

---

## 2. 連帶發現：`SDD-007 §5.1.2` 桶 A 之盤點方法錯誤（**我方缺失**）

`§5.1.2`（2026-09-23，`SDD-007` 1.10）當初之盤點方式為
「**頁面所需功能 ↔ 該服務端點語意**」做語意匹配，
但**未驗證「該端點在契約與實作中確實存在」**。

結果把下列三頁誤列為「**桶 A 可立即實作**」：

| 頁面 | 路徑 | 依賴端點 | 實況 |
|---|---|---|---|
| 授權策略檢核 | `/auth/policy` | `GET /authorizations/{authId}/check-results` | **端點不存在** |
| 離線授權與代授權 | `/auth/stip` | `POST /authorizations/{authId}/advices` | **端點不存在** |
| 沖正機制 | `/switch/auth/reversal` | `POST /authorizations/{authId}/reversals` | **端點不存在** |

★ **教訓（已制度化，見 §4）**：
> 「服務有端點」≠「該頁有對應端點」——**這是 `§5.1.2` 原本要解決的問題**；
> 但本次暴露出**更前一層**的漏洞：
> **「契約有端點」≠「實作有端點」**。
> 盤點時若只查契約（或只做語意匹配），即會把「不存在的端點」當成可用。

---

## 3. 併發現：`limit-service` 之契約／實作**路徑寫法差異**（1 則，需確認）

| 契約 | 實作 | 判定 |
|---|---|---|
| `GET /api/v1/cards/{cardId}/limits` | `GET /api/v1/accounts/{accountId}/limits` | **路徑寫法差異**（同 verb、同末端段 `limits`） |

二者**資源識別維度不同**（`cardId` vs `accountId`），非單純別名——
若消費端依契約以 `cardId` 查額度將 404。**待確認**：係修契約（改為 `accounts/{accountId}`）
或修實作（補一支 by-card 路徑）。

---

## 4. 制度化：G-8 新增「**端點存在性**」檢查面（不再只靠人工）

`tools/verify_contract_dto.py` 原已能偵測「契約有端點、Controller 無對應方法」，
但僅歸入 `infos`（**不影響結論**）→ 此類缺陷被**靜默放過**。

本期改為二分級，並**納入結論**：

| 情形 | 歸類 | 訊息 |
|---|---|---|
| 同服務**無**任何「同 verb 且同末端段」之實作路徑 | **`hits`（確定未實作）** | 「消費端依契約呼叫將 404」 |
| 同服務**有**「同 verb 且同末端段」之其他路徑 | **`warns`（疑似路徑寫法差異）** | 列出候選路徑，請人工確認 |

**為何要分級**：直接全部升為 `hits` 會對 `limit-service` 這類「路徑寫法差異」
產生**偽陽性**；本專案對偽陽性零容忍（會侵蝕工具可信度），故先比對末端段。

**回測**（`--selftest`）由 **4/4 擴充為 6/6**，新增兩個人工樣本：
- `EXPECT-HIT-missing-endpoint`：契約有、Controller 完全無 → 必須命中
- `EXPECT-WARN-path-alias`：契約 `cards/{cardId}/limits` vs 實作 `accounts/{accountId}/limits`
  → **必須為 warn 且不得為 hit**（證明不會偽陽性）

---

## 5. 待裁定

| # | 議題 | 選項 |
|---|---|---|
| 1 | 三支未實作端點 | **(a)** 本期補實作（**建議**——契約已詳載語意，且 `SDD-005`／`SCS` 應有對應設計）；**(b)** 自契約移除並加註「本期不提供」 |
| 2 | 三頁之處置 | 移至**桶 C（待後端）**——**不造空殼**（客戶硬性要求） |
| 3 | `limit-service` 路徑差異 | 修契約（改 `accounts/{accountId}`）／修實作（補 by-card） |

> **我方建議**：**先做 (1)-(a)**。理由：契約對這三支之語意已相當完整
> （13 項檢核明細、`FULL`／`PARTIAL` 沖正、強制授權軌跡），
> 且「沖正」在信用卡業務為**必要功能**（`SDD-005 §3` 已有佔用狀態機之
> `HELD --取消／沖正--> RELEASED` 轉換），**不是可省略的項目**。
> 若採 (b)，等於宣告「本期不做沖正」，恐與 RFP 需求不符。

---

## 6. 誠實揭露

1. **本 CR 之盤點錯誤源自我方**（`§5.1.2` 於 `SDD-007` 1.10 建立時未驗證端點存在），
   非既有程式缺陷——程式缺陷（三支未實作）則是本次查證所發現。
2. **尚未動工**：本輪僅查證與制度化，**未開始撰寫這三頁**（避免造空殼）。
3. **未驗證範圍**：`advices` 之語意（契約載「人工授權／強制授權（Override）」）
   與頁面名「離線授權與代授權（STIP）」**是否為同一件事**，尚待確認
   ——契約描述回 `APPROVED/STANDIN`，似同時涵蓋兩者，但**語意不完全相同**，
   頁面命名與歸屬建議於補實作時一併釐清。

---

## 7. 執行結果（2026-09-23，BEAM PMO「請繼續」→ 採 §5 (a) 補實作）

| 端點 | 狀態 | 真機驗證 |
|---|---|---|
| `GET …/{authNo}/check-results` | ✅ 已實作 | 回**契約形狀陣列**（`AMOUNT`/`CURRENCY`/`CARD_STATUS`/… 逐項 `PASS`＋reasonCode）；**併修正契約陣列性**（原誤宣告單一物件）；**查無 → `CC-AUTH-4040`／404**（初版 `ResponseStatusException` 落兜底 500，已改） |
| `POST …/{authNo}/reversals` | ✅ 已實作（** Stand-in 佔用除外**） | 實測發現 runtime 掛載 **`StandInLimitPort`**（種子授權皆為 Stand-in 本地佔用）→ 初版 500，已改為明確業務拒絕（`CC-AUTH-3001`／422）。**FULL 沖正 happy path 待「Stand-in 佔用沖正設計」裁定後方可真機驗證** |
| `POST …/{authNo}/advices` | ⬜ **語意待裁定**（`SCS-001 §7.7`：(a) 後送 vs (b) 人工覆核） | 未實作（不猜語意）；`/auth/stip` 頁續留桶 C |

**落地**：`V5__auth_reversals.sql`（AUTH_REVERSALS＋AUTHORIZATIONS.REVERSED_AT）；`LimitPort.release`（Remote 轉呼 limit-service／StandIn、Jdbc **fail-loud**）；`ReversalService`（已沖正→`CC-AUTH-3002`、查無→`CC-AUTH-4040`、PARTIAL≤holdAmount、**釋放＋紀錄同交易**；**決策欄不修改**）。`SCS-001` → **1.1**、`SDD-006` → **1.9**（`CC-AUTH-4040`，123 碼）。
**誠實揭露**：①查無授權初版 500（`ResponseStatusException` 未被 handler 映射）→ 已改 `CC-AUTH-4040`，**連既有明細端點一併修正**（同服務不得兩套真相）；②檢核視圖初版誤沿用 `AuthorizeResponse.CheckResultView`（新落差），真機實測後改為契約形狀。

---

## 8. 修訂記錄

| 版本 | 日期 | 修訂內容 |
|---|---|---|
| 1.1 | 2026-09-23 | BEAM PMO「請繼續」→ **採 §5 (a) 補實作**：check-results ✅／reversals ✅（** Stand-in 佔用除外**，待裁定）／advices ⬜（§7.7 兩語意待裁定）。新增 **§7 執行結果**（含兩項誠實揭露）。`SCS-001` → **1.1**、`SDD-006` → **1.9**、`HANDOVER` → **1.73**（#A-95） |
| 1.0 | 2026-09-23 | 初版：`authorization-service` 三支契約端點完全未實作之實測證據；`SDD-007 §5.1.2` 桶 A 盤點方法錯誤之更正；`limit-service` 路徑寫法差異；`tools/verify_contract_dto.py` 新增「端點存在性」檢查（二分級、回測 6/6） |
