CR #201 » SCS-012_爭議帳款服務程式設計規格.md
SCS-012 爭議帳款服務(dispute-service)程式設計規格
| 項目 | 內容 |
|---|---|
| 文件編號 | SCS-012 |
| 版本 |
1.5(CR-033:更正 §7 與 §10 #9 之消費者——原載「posting/notification」,經查現行 SAD-004 §5.2 未列 posting(SDD-003 §15 之 Dispute → Posting 屬結案路徑且為同步,非 opened 之消費)→ posting 非本事件之消費者;並載明真正阻塞為「無對應模板」。前版 1.4(CR-027 批次 A-4 事件輸出——dispute.case.opened.v1) |
| 狀態 |
已核定(規格先行);1.1 依 CR-003 補狀態機對照(SDD-005 §7);1.2 依 CR-017 補雙軸拆分;1.3 依 CR-026 補統計端點;1.4 依 CR-027 補事件輸出 |
| 對應需求 | ⚠️ 爭議/拒付需求(對應契約 /disputes 與 /chargebacks 端點);BRD-002 追蹤表未列顯式 FR 編號,待補
|
| 服務埠號 | 待骨架產生時依 CSP-006 §4 分配(自 8081 起編) |
| 套件根 | com.beam.cardcore.dis |
| 權威 schema | CC_DIS |
| 關聯文件 |
SDD-001 §5.8、SDD-003、BRD-002(待補 FR)、GOV-005
|
1. 對應需求
| FR 編號 | 需求名稱 | 來源 |
|---|---|---|
| (待補) | 爭議帳款/拒付(chargeback)處理 |
BRD-002 追蹤表未列;建議新增 DIS-*(如 DIS-1 爭議受理、DIS-2 拒付表示)並回填本表 |
⚠️ 追溯缺口:經查
BRD-002與BRD-003全目錄,無拒付/DISPUTE/CHARGEBACK之顯式 FR 編號,但契約dispute-service.yaml已定義POST /disputes、POST /disputes/{id}/chargebacks。此為需求側缺口,須由 SA 於BRD-002補 FR 並回填本 SCS(否則違反「無 SCS 不寫程式」之可追溯性要求)。
1.1 上層設計依據(本規格不重複這些圖,僅引用)
| 圖種 | 文件 | 數量(實測) | 本服務相關 |
|---|---|---|---|
| ER-Diagram | SDD-001_領域模型與ER-Diagram |
16 個 erDiagram
|
DISPUTE_CASES/DISPUTE_TIMELINES 實體(SDD-001 §5.8) |
| DFD | SDD-002_DFD與流程分解 |
10 個 flowchart | 爭議處理流 |
| Sequence | SDD-003_Sequence-Diagrams |
15 個 sequenceDiagram
|
爭議/拒付時序 |
| 狀態機 | SDD-005_狀態機設計 |
10 個 stateDiagram
|
⚠️ DISPUTE_CASES.status/STAGE 以 CHECK 控管,未定義正式狀態機(強烈建議補,見 §10) |
| API Contract |
SDD-006 + contracts/openapi/dispute-service.yaml
|
17 份 | 本服務 3 支端點 |
2. 服務職責與邊界
承作:
- 爭議案件(
DISPUTE_CASES:查詢、舉證、責任判斷、時限) - 爭議時序(
DISPUTE_TIMELINES:各階段動作稽核留痕) - 拒付表示(chargeback representation)
不做(屬他服務):
- 原交易來源 →
posting-service/authorization-service - 清算對帳 →
clearing-service
3. 領域模型
- 聚合:
DisputeCase(爭議案件)、DisputeEvidence(舉證,依services.yamlaggregates) - 值物件:
DisputeType、Stage、Liability、OrgCode、Money、Currency - 狀態:
DISPUTE_CASES.status ∈ {OPEN, EVIDENCE_PENDING, REPRESENTED, WON, LOST, CLOSED}(CHECK,預設 OPEN);STAGE ∈ {INQUIRY, FIRST_PRESENTMENT, SECOND_PRESENTMENT, PRE_ARB, ARBITRATION, CLOSED}(預設 INQUIRY);LIABILITY ∈ {ISSUER, ACQUIRER, MERCHANT, PENDING}
4. 程式結構
| 套件/類別 | 職責 | 對應需求 |
|---|---|---|
domain/model/DisputeCase DisputeEvidence
|
聚合與值物件 | (待 BRD-002 補 FR) |
domain/port/DisputeRepository |
持久化埠 | — |
application/DisputeService |
受理/舉證/責任判斷/拒付表示 | (待補 FR) |
infrastructure/jdbc/DisputeRepositoryImpl |
JDBC 實作 | — |
interfaces/rest/DisputeController |
REST 端點 | (待補 FR) |
5. 資料模型(約束為 Oracle 實測值)
| 表 | 關鍵欄位 | 約束(實測) |
|---|---|---|
DISPUTE_CASES |
DISPUTE_NO VARCHAR2(32) UNIQUE;ACCOUNT_ID;CARD_ID;TRANSACTION_ID;AUTHORIZATION_ID;ORG;ARN;REASON_CODE;DISPUTE_TYPE;DISPUTE_REASON(V2);STAGE DEFAULT 'INQUIRY';WORKFLOW_STAGE(V2);CURRENCY CHAR(3) DEFAULT 'TWD';AMOUNT NUMBER(19);EVIDENCE_REQUIRED/EVIDENCE_SUBMITTED JSON DEFAULT '[]';REPRESENTMENT_COUNT NUMBER(5);LIABILITY;RESPONSE_DEADLINE TIMESTAMP(6) WITH TIME ZONE;CHARGEBACK_TYPE(V2);CHARGEBACK_REASON(V2);STATUS DEFAULT 'OPEN';OPENED_AT/CLOSED_AT;ASSIGNED_TO
|
CK_DISPUTE_CASES_TYPE:dispute_type IN ('CHARGEBACK','INQUIRY','RETROACTIVE','FRAUD','DUPLICATE');CK_DISPUTE_CASES_REASON:dispute_reason IN ('UNAUTHORIZED','DUPLICATE','GOODS_SERVICES','OTHER');CK_DISPUTE_CASES_WORKFLOW_STAGE:7 值;CK_DISPUTE_CASES_CHARGEBACK_TYPE:3 值;org IN (7 值,含 INTERNAL);stage IN (6 值);amount >= 0;liability IN (4 值);status IN (6 值)
|
DISPUTE_TIMELINES |
DISPUTE_ID(FK→CASES.ID);STAGE;ACTION VARCHAR2(32);ACTOR VARCHAR2(40);OCCURRED_AT;DETAIL JSON |
FK(DISPUTE_ID) REFERENCES CASES(ID) |
含
CREATED_AT/UPDATED_ATTIMESTAMP(6) WITH TIME ZONE、VERSION樂觀鎖。⚠️ 兩軸之拆分(
CR-017,BEAM PMO 2026-09-22 裁定採雙欄位):
契約之disputeType(爭議原因)對映DISPUTE_REASON;
契約之stage(本行內部作業階段)對映WORKFLOW_STAGE。
DISPUTE_TYPE回歸程序類型語意(CHARGEBACK/INQUIRY/RETROACTIVE;
FRAUD/DUPLICATE為過渡期遺留值),STAGE保留卡組織程序階段
(SDD-005 §7.1)並以契約新欄orgStage回傳。V1 基線已凍結(
SAD-003 §9);V2 變更見V2__dispute_reason_workflow_stage_chargeback.sql。
6. API 設計(來自 contracts/openapi/dispute-service.yaml)
| 端點 | 方法 | 說明 | 對應 FR |
|---|---|---|---|
/api/v1/disputes |
POST | 爭議受理 | (待 BRD-002 補 FR) |
/api/v1/disputes/{disputeId} |
GET | 爭議查詢 | (待補 FR) |
/api/v1/disputes/{disputeId}/chargebacks |
POST | 拒付表示 | (待補 FR) |
/api/v1/disputes/stats |
GET | 爭議業務統計(CR-026;供 /dashboard 業務 KPI) |
(待 BRD-002 補 FR) |
錯誤碼:
404找不到案件、400舉證不完整、409時限外。
7. 關鍵邏輯
-
舉證時限:
RESPONSE_DEADLINE由組織規範推算(VISA/MC/NCCC 各異),逾期自動標LOST。 -
稽核留痕:每階段動作寫
DISPUTE_TIMELINES(DETAILJSON 載明證據)。 -
責任判斷:依舉證結果與組織規則定
LIABILITY。
CR-017(BEAM PMO 2026-09-22 裁定)後定案之實作要點
-
三軸並存:
DISPUTE_REASON(爭議原因,契約disputeType)/STAGE(卡組織程序階段,
契約orgStage)/WORKFLOW_STAGE(本行作業階段,契約stage);
另有STATUS(案件處理,SDD-005 §7.2)與LIABILITY(正交屬性)契約未回傳。 -
開案:
STAGE = INQUIRY(SDD-005 §7.1轉換 1 之「受理並調單」)、
WORKFLOW_STAGE = OPENED、STATUS = OPEN;DISPUTE_TYPE寫入INQUIRY(程序類型)。
ORG預設INTERNAL(契約未帶 org,見 §10)。 -
扣款/仲裁:依
FIRST → SECOND → ARBITRATION順序推進(SM-01,跳號回
CC-DSP-3001);兩軸同步前進——STAGE依§7.1、WORKFLOW_STAGE扣款 →UNDER_REVIEW、
仲裁 →ARBITRATION、STATUS→REPRESENTED;並記錄CHARGEBACK_TYPE/CHARGEBACK_REASON/
EVIDENCE_SUBMITTED、REPRESENTMENT_COUNT + 1。終態(CLOSED)→CC-DSP-3002。 -
冪等:開案以冪等鍵派生主鍵;扣款提交若「同類型且已達目標階段」→ 回傳現況
(SM-03重複執行同一轉換),不重複計次、不拋錯。
7.x 統計端點(CR-026,供 /dashboard 業務 KPI)
端點:GET /api/v1/disputes/stats → DisputeStats(★ CR-043 §3.1 #6:原 DisputeStatsView,與實作 domain/model/DisputeStats 統一)
聚合邏輯(純查詢、GROUP BY、不快取;直查 DISPUTE_CASES):
| 欄位 | 來源 | 說明 |
|---|---|---|
total |
COUNT(*) |
案件總數(由後端計算;前端不得自行相加分組) |
byStatus |
GROUP BY STATUS |
依 STATUS 之真實分佈(不補 0;無資料之狀態不列入) |
openCases |
COUNT(*) WHERE STATUS NOT IN ('WON','LOST','CLOSED') |
處理中案件數 |
⚠️
openCases定義不得自行推導:OPEN/EVIDENCE_PENDING/REPRESENTED計入;
WON/LOST/CLOSED不計入(終態)。與byStatus之關係:
openCases = byStatus['OPEN'] + byStatus['EVIDENCE_PENDING'] + byStatus['REPRESENTED']。
⚠️ 無查詢參數、不快取、不補零——與
SCS-003 §7.9、SCS-001 §7.x(授權)一致。
7.y 事件輸出(Transactional Outbox)
掛點:POST /api/v1/disputes(爭議受理)→ 同交易寫入 outbox(SDD-001 §3.15)
事件:dispute.case.opened.v1(SAD-002 §3.9、SDD-006 §4.2)
| 項目 | 值 | 依據 |
|---|---|---|
| Kafka 主題 |
cc.dispute.case.opened(=cc.<aggregateType>.<eventType 去版本>) |
OutboxPublisher.topicFor |
| 分區鍵 |
aggregateId = disputeId
|
SAD-004 §5.4(同聚合有序) |
aggregate_type |
dispute |
— |
event_type(outbox 欄) |
dispute.case.opened(不含版本) |
SDD-001 §3.15 |
event_version |
1 |
— |
| payload |
disputeId/accountId/transactionId/disputeType/amount(恰 5 欄) |
SDD-006 §4.2 |
寫入時機:DisputeService.open 於 repository.save(dispute) 之後、同一交易(@Transactional)內 append——
否則會出現「爭議已受理、事件未發」之落差。
冪等:開案以冪等鍵派生主鍵;同鍵重送於 save 之前即早退(回現況),故不重複發射事件。
⚠️
amount為最小單位(分)(SDD-006 §2.5)——禁止浮點。
⚠️
disputeType為契約軸(爭議原因):寫入 payload 者為DISPUTE_REASON(UNAUTHORIZED/DUPLICATE/
GOODS_SERVICES/OTHER),非DISPUTE_TYPE(程序類型,開案恆為INQUIRY)——見 §5 之雙軸拆分。
dispute.case.closed.v1 尚未發射(誠實揭露):其 payload 為 disputeId/resolution/adjustAmount,
惟 Dispute 聚合無 resolution/adjustAmount 欄位,且契約僅 3 端點、無結案路徑可達(見 §10 #3)
→ 不臆造欄位;待裁定後補(CR-027 §6)。
消費者:notification——⚠️ 尚未實作(CR-029 §5 C-6:無對應模板,需業務文案)。
★ 更正(
CR-033,2026-10-02):本節原載消費者含posting——不成立:
現行權威清單SAD-004 §5.2未列posting;SDD-003 §15之
Dispute → Posting(帳務調整:暫付/扣回)屬結案(case.closed)路徑且為同步,
非本事件(opened)之消費。→posting非dispute.case.opened.v1之消費者。
8. 外部依賴與降級
| 依賴 | 失敗處理(Stand-in) |
|---|---|
posting-service/authorization-service(原交易) |
經查詢取得;交易資料缺失標 EXCEPTION 轉人工 |
9. 測試案例(對應 TRP-001)
- 爭議受理→舉證→責任判斷→結案
- 拒付表示(chargeback representation)、時限外拒絕
- 舉證 JSON 結構校驗、組織別(ORG)規範差異
10. 已知限制(誠實揭露)
-
BRD-002缺爭議/拒付之顯式 FR 編號(追溯缺口,須 SA 補DIS-*並回填)。 - 各組織(VISA/MC/NCCC)之拒付時限與舉證規範須引用組織文件,待確認。
-
→ 已產生,埠號 8093,已啟動實測。dispute-service骨架與埠號尚未產生(規格先行)
CR-017 執行後新增之限制(2026-09-22)
-
卡組織(
ORG)由服務端預設INTERNAL:契約之OpenDisputeRequest未帶org
(DDL 為NOT NULL);待契約擴充後改由請求帶入。 -
卡組織時限未參數化:
RESPONSE_DEADLINE固定為「開案 + 30 日」
(契約示例之天數);組織別時限(VISA/MC/NCCC 各異)待規範文件接入。 -
無結案端點:契約僅 3 端點(開案/查詢/扣款);
WON/LOST→CLOSED
之流轉(SDD-005 §7.2轉換 7~11)無 API 路徑可達——本期不為其發明端點。 -
DISPUTE_TIMELINES未寫入:稽核軌跡表已存在,惟本期未寫入階段動作
(§7原載「每階段動作寫 TIMELINES」)——待與狀態轉換一併實作。 -
LIABILITY恆為PENDING:責任判斷需舉證結果與組織規則,本期不具備。 -
REASON_CODE/ARN未接:卡組織代碼與調單參考號需組織檔案/訊息接入。 -
逾期自動標
LOST未實作:需排程掃描RESPONSE_DEADLINE(同#A-79之批次手法),
本期未做。
CR-027 批次 A-4 執行後新增之限制(2026-10-01)
-
dispute.case.closed.v1不可發射:契約(SDD-006 §4.2)之 payload 為disputeId/
resolution/adjustAmount,惟Dispute聚合無該二欄位,且 §10 #3 已載「無結案端點」
→ 無結案路徑可達。不臆造欄位;須先裁定「補聚合欄位」或「改契約 payload」
(已記CR-027 §6 批次 A-4)。本期僅實作opened.v1。 -
事件消費者未實作:
dispute.case.opened.v1之消費者為notification,
惟尚未訂閱 → 現階段為「有人發、沒人收」。⚠️ 阻塞為「無對應模板」
(需業務文案,CR-029 §5 C-6/CR-030 §4.2)。
★ 更正(CR-033):原載消費者含posting——不成立(見 §7 之更正)。 -
sequenceNo為「同 JVM 內遞增之時間戳基底序號」:多實例部署可能重複;
與CR-027 §5 #12(card服務之同型問題)同一類——正解為「聚合層級持久化序號」
(SAD-004 §5.4),本期未做。
11. 修訂記錄
| 版本 | 日期 | 修訂者 | 說明 |
|---|---|---|---|
| 1.0 | 2026-09-19 | 開發團隊 | 初版(規格先行);載明四項已知限制 |
| 1.1 | 2026-09-19 | 開發團隊 | 依 CR-003 更新:①§10 移除「狀態機建議建模」之限制(已完成),新增 §10「規格補記」5 項——狀態機三欄對照、WON/LOST 與 liability 對應待確認、組織時限參數未取得、轉換全未實作、CR-004 平台缺口;②新增本修訂記錄區(原文件欠缺) |
| 1.2 | 2026-09-22 | WorkBuddy AI |
CR-017 執行完成(BEAM PMO 裁定:A/B 採雙欄位、C 補欄)——①**§5 資料模型**:補 DISPUTE_REASON/WORKFLOW_STAGE/CHARGEBACK_TYPE/CHARGEBACK_REASON 與 4 個具名約束,並載明兩軸之拆分;②**§7** 新增實作要點(三軸並存、開案初始值、扣款順序與兩軸同步、冪等);③**§10** 移除「骨架未產生」、新增 7 項限制;④補建本修訂記錄區(原文件欠缺)。單測 19 例 0 失敗;真機 e2e 六項(含雙軸同列獨立之 DB 複核) |
| 1.3 | 2026-10-01 | WorkBuddy AI |
CR-026 執行:①**§6 API 表新增 GET /api/v1/disputes/stats;②§7.x** 新增統計端點規格(total=COUNT(*)、byStatus=GROUP BY STATUS 不補零、openCases=STATUS NOT IN ('WON','LOST','CLOSED'));③契約 dispute-service.yaml 新增 DisputeStatsView(端點數 3→4);④DisputeStats domain model+DisputeRepository.stats()+JdbcDisputeRepository+DisputeService+DisputeController+DisputeServiceTest 真測 statsReflectsState。單測 20 例 0 失敗 |
| 1.4 | 2026-10-01 | WorkBuddy AI |
CR-027 批次 A-4(#A-133):①**§7.y 新增事件輸出規格**(dispute.case.opened.v1——主題/分區鍵/payload 5 欄/寫入時機/冪等;併載 closed.v1 不可發射之理由);②**§10 新增 3 項限制**(closed.v1 不可發射、消費者未實作、sequenceNo 多實例風險);③★ 併修文件缺陷——本檔原有兩個 ## 11. 修訂記錄(1.0/1.1 與 1.2/1.3 分列兩處),已合併為單一表
|
規格補記(2026-09-19,CR-003):
-
✅ 狀態機已建模:原「
DISPUTE_CASES.status/STAGE轉換強烈建議建模為
SDD-005正式狀態機」之建議已完成。SDD-005 §7已拆分為兩個狀態機
+ 一個正交屬性:小節 名稱 對應欄位 值域(實測 CHECK) §7.1 爭議階段機 DISPUTE_CASES.stageINQUIRY/FIRST_PRESENTMENT/SECOND_PRESENTMENT/PRE_ARB/ARBITRATION/CLOSED§7.2 爭議案件狀態機 DISPUTE_CASES.statusOPEN/EVIDENCE_PENDING/REPRESENTED/WON/LOST/CLOSED§7.3 正交屬性(非狀態機) DISPUTE_CASES.liabilityISSUER/ACQUIRER/MERCHANT/PENDING原單一狀態機之值域與 DDL 幾乎無交集(僅
CLOSED一值相同)——
因其將「卡組織階段」與「案件處理/結果」兩個正交分類軸繪於一圖,
且ARBITRATION(階段)與RESOLVED_*(責任歸屬)分屬不同欄位。
實體模型本即正確(三欄分立),無須變更 DDL。 -
WON/LOST與liability之精確對應待確認:SDD-005 §7.3已載明
WON/LOST定案後寫入liability,但「孰為ACQUIRER、孰為MERCHANT」
須依卡組織規則確認,不得由實作自行認定。 -
PRE_ARB/ARBITRATION之組織時限參數尚未取得:BRD-001BF-09 僅至
「提交卡組織仲裁」;BR-09-01要求受卡組織時限控管,惟各階段時限值未提供,
實作前須補(與上列第 2 點之組織文件同一來源)。 -
狀態機轉換全部尚未實作:本服務目前僅有骨架(無領域模型),
SDD-005 §7.1之 10 條與§7.2之 11 條轉換均無程式路徑。 -
平台層請求層例外之狀態碼缺口:缺必要標頭、非法 JSON、不支援之方法等
會回 500CC-PLT-5000(契約要求 400/405)。屬全平台問題,
已立CR-004(待核定)。
⚠️ 本檔原於此處另有一個
## 11. 修訂記錄(1.0/1.1),與前段(1.2/1.3)重複——
已於 1.4(#A-133)合併為單一表,本區塊移除。
| 1.5 | 2026-10-02 |CR-033:更正事件消費者之記載——①**§7** 原載「消費者:posting/notification」→ 更正為僅notification(現行SAD-004 §5.2未列posting;SDD-003 §15之Dispute → Posting(帳務調整)屬結案路徑且為同步,非opened之消費);②**§10 #9** 同步更正,並載明真正阻塞為「無對應模板」(需業務文案)。★ 零程式變更(純文件更正) |