Project

General

Profile

CR #201 » SCS-012_爭議帳款服務程式設計規格.md

內容更新:內部稱謂去識別化(2026-10-18) - Redmine Admin, 10/18/2026 05:32 AM

 

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.yaml aggregates)
  • 值物件: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_AT TIMESTAMP(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(DETAIL JSON 載明證據)。
  • 責任判斷:依舉證結果與組織規則定 LIABILITY。

CR-017(BEAM PMO 2026-09-22 裁定)後定案之實作要點

  1. 三軸並存:DISPUTE_REASON(爭議原因,契約 disputeType)/STAGE(卡組織程序階段,
    契約 orgStage)/WORKFLOW_STAGE(本行作業階段,契約 stage);
    另有 STATUS(案件處理,SDD-005 §7.2)與 LIABILITY(正交屬性)契約未回傳。
  2. 開案:STAGE = INQUIRY(SDD-005 §7.1 轉換 1 之「受理並調單」)、
    WORKFLOW_STAGE = OPENED、STATUS = OPEN;DISPUTE_TYPE 寫入 INQUIRY(程序類型)。
    ORG 預設 INTERNAL(契約未帶 org,見 §10)。
  3. 扣款/仲裁:依 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。
  4. 冪等:開案以冪等鍵派生主鍵;扣款提交若「同類型且已達目標階段」→ 回傳現況
    (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)之拒付時限與舉證規範須引用組織文件,待確認。
  • dispute-service 骨架與埠號尚未產生(規格先行) → 已產生,埠號 8093,已啟動實測。

CR-017 執行後新增之限制(2026-09-22)

  1. 卡組織(ORG)由服務端預設 INTERNAL:契約之 OpenDisputeRequest 未帶 org
    (DDL 為 NOT NULL);待契約擴充後改由請求帶入。
  2. 卡組織時限未參數化:RESPONSE_DEADLINE 固定為「開案 + 30 日」
    (契約示例之天數);組織別時限(VISA/MC/NCCC 各異)待規範文件接入。
  3. 無結案端點:契約僅 3 端點(開案/查詢/扣款);WON/LOST → CLOSED
    之流轉(SDD-005 §7.2 轉換 7~11)無 API 路徑可達——本期不為其發明端點。
  4. DISPUTE_TIMELINES 未寫入:稽核軌跡表已存在,惟本期未寫入階段動作
    (§7 原載「每階段動作寫 TIMELINES」)——待與狀態轉換一併實作。
  5. LIABILITY 恆為 PENDING:責任判斷需舉證結果與組織規則,本期不具備。
  6. REASON_CODE/ARN 未接:卡組織代碼與調單參考號需組織檔案/訊息接入。
  7. 逾期自動標 LOST 未實作:需排程掃描 RESPONSE_DEADLINE(同 #A-79 之批次手法),
    本期未做。

CR-027 批次 A-4 執行後新增之限制(2026-10-01)

  1. dispute.case.closed.v1 不可發射:契約(SDD-006 §4.2)之 payload 為 disputeId/
    resolution/adjustAmount,惟 Dispute 聚合無該二欄位,且 §10 #3 已載「無結案端點」
    → 無結案路徑可達。不臆造欄位;須先裁定「補聚合欄位」或「改契約 payload」
    (已記 CR-027 §6 批次 A-4)。本期僅實作 opened.v1。
  2. 事件消費者未實作:dispute.case.opened.v1 之消費者為 notification,
    惟尚未訂閱 → 現階段為「有人發、沒人收」。⚠️ 阻塞為「無對應模板」
    (需業務文案,CR-029 §5 C-6/CR-030 §4.2)。
    ★ 更正(CR-033):原載消費者含 posting——不成立(見 §7 之更正)。
  3. 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):

  1. ✅ 狀態機已建模:原「DISPUTE_CASES.status/STAGE 轉換強烈建議建模為
    SDD-005 正式狀態機」之建議已完成。SDD-005 §7 已拆分為兩個狀態機
    + 一個正交屬性:

    小節 名稱 對應欄位 值域(實測 CHECK)
    §7.1 爭議階段機 DISPUTE_CASES.stage INQUIRY/FIRST_PRESENTMENT/SECOND_PRESENTMENT/PRE_ARB/ARBITRATION/CLOSED
    §7.2 爭議案件狀態機 DISPUTE_CASES.status OPEN/EVIDENCE_PENDING/REPRESENTED/WON/LOST/CLOSED
    §7.3 正交屬性(非狀態機) DISPUTE_CASES.liability ISSUER/ACQUIRER/MERCHANT/PENDING

    原單一狀態機之值域與 DDL 幾乎無交集(僅 CLOSED 一值相同)——
    因其將「卡組織階段」與「案件處理/結果」兩個正交分類軸繪於一圖,
    且 ARBITRATION(階段)與 RESOLVED_*(責任歸屬)分屬不同欄位。
    實體模型本即正確(三欄分立),無須變更 DDL。

  2. WON/LOST 與 liability 之精確對應待確認:SDD-005 §7.3 已載明
    WON/LOST 定案後寫入 liability,但「孰為 ACQUIRER、孰為 MERCHANT」
    須依卡組織規則確認,不得由實作自行認定。

  3. PRE_ARB/ARBITRATION 之組織時限參數尚未取得:BRD-001 BF-09 僅至
    「提交卡組織仲裁」;BR-09-01 要求受卡組織時限控管,惟各階段時限值未提供,
    實作前須補(與上列第 2 點之組織文件同一來源)。

  4. 狀態機轉換全部尚未實作:本服務目前僅有骨架(無領域模型),
    SDD-005 §7.1 之 10 條與 §7.2 之 11 條轉換均無程式路徑。

  5. 平台層請求層例外之狀態碼缺口:缺必要標頭、非法 JSON、不支援之方法等
    會回 500 CC-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** 同步更正,並載明真正阻塞為「無對應模板」(需業務文案)。★ 零程式變更(純文件更正) |

(51-51/67)