CR #201 » SDD-006_API-Contract.md
SDD-006 API Contract
| 項目 | 內容 |
|---|---|
| 文件編號 | SDD-006 |
| 版本 |
1.24(CR-049:① posting.transaction.reversed.v1 之 payload 由 4 欄擴為「4 契約欄 + 可選 postingDate」——沖正交易之入帳日=業務日錨點,下游回沖分錄不得以 occurredAt 反推(SCS-026 §7.5①);② campaign.reward.commanded.v1 之 recognition 補列 applicableRate/rateReason,並載明回沖指令同走此信封(originalPostingId/txType=REFUND/postingDate/refund:true),消費端以 txType=REFUND 為權威且欄位「recognition 優先、payload 次之」。前版 1.23(SCS-026 §7.14/§7.15:CreateCampaignRequest/CampaignView 加 priority(D9 優先序);campaign.reward.commanded.v1 之回沖指令(refund/originalPostingId)——campaign 內部契約)。前版 1.22(SCS-026 §7.13 R-3b:PointsAccountView 加 tierRank(層級序;權威在 rewards))。前版 1.21(CR-048:支付路徑結構化欄位跨服務透傳——AuthorizeRequest/PostingRequest 加可選 payPath;posting.transaction.posted.v1 payload 加可選 payPath(不升版);PointsAccountView 加 tier(R-3))。前版 1.20(SCS-026 §7.11:AccrualRequest 契約加可選 applicableRate(活動回饋率;BRD 例 1 算式))。前版 1.19(CR-047:rewards.points.accrued.v1 payload 加 4 可選欄位(campaignCode/valueMinor/instrumentId/postingDate;不升版);AccrualRequest 契約加 3 可選欄位)。前版 1.18(CR-046 AC 7:campaign 新增 GET /api/v1/campaigns/{campaignCode}/enrollments/{householdId}(查詢錄取狀態,含 accruedAmountMinor 門檻累計);EnrollmentView 加 accruedAmountMinor)。前版 1.17(CR-046 AC 7:campaign 新增 POST /api/v1/campaigns/{campaignCode}/enrollments)。前版 1.16(CR-046 B2-6:card.card.status-changed.v1 payload 新增可選 instrumentId,舊事件可為 null;不升事件版本)。前版 1.11(CR-028 hold 事件群四項矛盾之化解(2026-10-01):§4.2 刪 authorization.hold.created.v1/authorization.hold.released.v1 兩列、補登 limit.hold.created.v1;併更正 card.card.status-changed.v1 之 payload(原載差量 fromBlockCode/toBlockCode/reason,實作為變更後完整狀態——依 SCS-003 §7.10 更正,列 CR-027 §5 #13 待覆核);併標記 authorization.authorization.approved.v1 與 limit.hold.created.v1 之可選欄位。前版 1.10(GOV-007 §4:§3.4 補登 CC-PLT-3003(409 資源尚未就緒)——供報表下載之「作業未就緒(QUEUED/RUNNING/FAILED)」情境,與「狀態機非法轉換」語意區隔 → 錯誤碼 123 → 124。前版 1.9(CR-021:§3.4 補登 CC-AUTH-4040(查無授權)→ 錯誤碼 123;並修訂 check-results 契約之陣列性(單一物件 → AuthCheckResult[])。前版 1.8(C-9:補登 NOT 三碼 → 122) |
| 狀態 | 已核定 |
| 撰寫者 | SD |
| 審查者 | SA / TL / SEC |
| 對應需求 | SYS-4(API 完備度)、API 設計與管理、SYS-7 |
1. 說明
本文件定義全系統之 API 契約規範。契約本體(OpenAPI 3.1)以檔案交付:
cardcore/contracts/openapi/<service>.yaml # 17 個服務,共 65 個端點
契約由 cardcore/tools/gen_openapi.py 產生並驗證;禁止手改 YAML,
變更須修改產生器後重新產生(確保端點清單、$ref、錯誤碼一致)。
1.1 驗證結果
| 服務 | 端點數 | 驗證 |
|---|---|---|
| authorization-service | 5 | ✅ |
| limit-service | 6 | ✅ |
| posting-service | 6 | ✅ |
| payment-service | 4 | ✅ |
| card-service | 7 | ✅ |
| party-service | 4 | ✅ |
| product-service | 5 | ✅ |
| statement-service | 3 | ✅ |
| pricing-service | 3 | ✅ |
| installment-service | 3 | ✅ |
| rewards-service | 4 | ✅ |
| clearing-service | 3 | ✅ |
| dispute-service | 3 | ✅ |
| merchant-service | 3 | ✅ |
| notification-service | 2 | ✅ |
| report-service | 2 | ✅ |
| migration-service | 2 | ✅ |
| 合計 | 65 | 0 錯誤 |
驗證項目:OpenAPI 版本、info 必填、servers 存在、路徑符合 /api/v1/、
operationId 唯一、2xx 與 409 回應存在、寫入操作具 requestBody 與冪等標頭、
所有 $ref 可解析且無孤立元件。
2. 通用規範
2.1 請求標頭
| 標頭 | 必填 | 說明 |
|---|---|---|
Authorization |
✅ |
Bearer <JWT> 或 ApiKey <key>
|
X-Channel |
✅ |
WEB / MOBILE / PARTNER / BATCH / SYSTEM
|
X-Idempotency-Key |
✅(寫入) | 冪等鍵,8–120 字元 |
X-Trace-Id |
○ | 未帶則由閘道產生 |
Accept-Language |
○ |
zh-TW(預設)/ en-US
|
Content-Type |
✅ | application/json |
2.2 回應信封
所有回應一律採統一信封:
{
"traceId": "01HZX8K2M4N5P6Q7R8S9T0V1W2",
"data": { },
"error": null
}
失敗時:
{
"traceId": "01HZX8K2M4N5P6Q7R8S9T0V1W2",
"data": null,
"error": {
"code": "CC-LIM-1001",
"message": "可用額度不足",
"details": [{ "field": "amount", "reason": "exceeds available limit" }]
}
}
2.3 HTTP 狀態碼
| 狀態碼 | 語意 | 觸發情境 |
|---|---|---|
| 200 | 成功 | 查詢、更新成功 |
| 201 | 已建立 | 資源建立成功 |
| 202 | 已接受 | 非同步作業受理(批次、報表、清算) |
| 400 | 請求錯誤 | ValidationError |
| 401 | 未認證 | 權杖無效或過期 |
| 403 | 無權限 | 角色不足 |
| 404 | 不存在 | ResourceNotFoundError |
| 409 | 衝突 | 冪等鍵衝突、樂觀鎖衝突、狀態衝突 |
| 422 | 業務規則不允許 | BusinessRuleError |
| 429 | 超過速率 | 限流 |
| 500 | 系統錯誤 | InfrastructureError |
| 503 | 服務不可用 | 斷路器開啟、維護中、ExternalSystemError
|
2.4 分頁
| 參數 | 型別 | 預設 | 上限 |
|---|---|---|---|
page |
integer | 1 | — |
size |
integer | 50 | 200 |
分頁回應於 data.meta 帶 page、size、total。
2.5 金額與日期
| 型別 | 規範 |
|---|---|
| 金額 |
整數,最小單位(分);{"amount": 12345, "currency": "TWD"} = 123.45 元 |
| 日期 |
date:YYYY-MM-DD
|
| 時間 |
date-time:ISO 8601 含時區,一律 UTC
|
| 比率 |
number,小數表示(0.185 = 18.5%) |
禁止:金額使用浮點數(
BRD-001 C-01)。
★ 通則(
CR-030 §2 #10裁定):凡事件 payload 涉及金額者,一律帶currency(可選)。
理由:金額無幣別即無語意——多幣別帳戶下不可解讀。
業界依據:ISO 20022 之Amount一律為「Currency + Amount」複合型別;金融訊息中裸金額屬建模錯誤。
相容性:新增可選欄位依SAD-004 §5.3不升版;消費者須容忍缺欄(缺則不臆造幣別,
依各消費者之規格處理——例如SCS-016 §7.11/§7.12:不建立通知並記 WARN)。
2.6 識別碼
| 型別 | 格式 |
|---|---|
| 資源主鍵 | UUID v7(字串) |
| 業務編號 | 前綴 + 序號(如 AUTH20260917000001) |
| 追蹤識別碼 | ULID(26 字元) |
| 冪等鍵 | 客戶端產生,8–120 字元 |
3. 錯誤碼規範
3.1 編碼規則
CC-<SVC>-<NNNN>
| 段 | 說明 |
|---|---|
CC |
固定前綴(Credit Card) |
SVC |
4 碼服務代碼 |
NNNN |
4 碼;首位表類別,後三位為序號 |
3.2 服務代碼
| 代碼 | 服務 | 代碼 | 服務 |
|---|---|---|---|
AUTH |
authorization-service | RWD |
rewards-service |
LIM |
limit-service | CLR |
clearing-service |
CARD |
card-service | DSP |
dispute-service |
PTY |
party-service | MRC |
merchant-service |
PRD |
product-service | NOT |
notification-service |
POST |
posting-service | RPT |
report-service |
PAY |
payment-service | MIG |
migration-service |
STM |
statement-service | PLT |
platform/common |
PRC |
pricing-service | GW |
platform/gateway |
INS |
installment-service |
3.3 類別位
| 首位 | 類別 | HTTP | 可否重試 |
|---|---|---|---|
| 1 | 業務規則拒絕 | 422 | ❌ |
| 2 | 併發/冪等衝突 | 409 | 部分可 |
| 3 | 狀態機衝突 | 409 | ❌ |
| 4 | 請求與資源 | 400 / 404 / 405 / 409 | ❌ |
| 5 | 外部與基礎設施 | 503 / 500 | ✅(冪等者) |
3.3.1 類別位 4 之語意擴充(CR-004,2026-09-19)
類別位 4 =「請求本身或其所指資源無法被受理」,涵蓋四個子語意——
皆屬請求層,尚未進入業務邏輯:
| 子語意 | HTTP | 何時使用 | 代表碼 |
|---|---|---|---|
| 請求不合法 | 400 | 必填標頭/查詢參數缺失、主體格式錯誤、參數型別或 Bean Validation 失敗 |
CC-PLT-4001/CC-PLT-4002/CC-PLT-4003
|
| 資源或路徑不存在 | 404 | 無對應路由、聚合根查無 |
CC-PLT-4040、CC-PTY-4040、CC-POST-4040
|
| 方法不支援 | 405 | HTTP 動詞與端點不符 | CC-PLT-4050 |
| 資源重複 | 409 | 業務冪等鍵已存在 |
CC-POST-4001、CC-POST-4002
|
擴充理由(實測):
CR-004於 8089 實測發現,Spring MVC 之請求層例外
(缺標頭MissingRequestHeaderException、壞 JSONHttpMessageNotReadableException、
方法不支援HttpRequestMethodNotSupportedException、路徑不存在NoResourceFoundException)
皆非DomainException子類,因而全數落GlobalExceptionHandler之兜底回 500——
既違反契約(應為 400/404/405),且每個客戶端錯誤都觸發 ERROR 告警(告警疲勞)。原類別位 4 僅涵蓋「資源與重複」(404/409),無處安放 400 與 405;
若強行借用類別位 1(業務規則拒絕 422)或 5(基礎設施 5xx)皆為語意錯置。
故擴充本類別位之語意,使其成為請求層錯誤之歸屬。設計原則:類別位 4 之碼一律不得重試(客戶端須修正請求後重送);
與類別位 1(業務規則拒絕)之區別在於——類別位 4 尚未進入業務邏輯。
3.4 錯誤碼清單
| 錯誤碼 | HTTP | 訊息 | 說明 | 處理建議 |
|---|---|---|---|---|
CC-AUTH-1001 |
422 | 授權拒絕 | 檢核未通過 | 依回應碼判斷 |
CC-AUTH-1002 |
422 | 卡片狀態不允許交易 | 卡片為 LOCKED/LOST/CLOSED | 提示客戶聯繫客服 |
CC-AUTH-1003 |
422 | 超過單筆限額 | 交易金額超過單筆限額 | 拆單或調額 |
CC-AUTH-1004 |
422 | 超過日限額或日限次 | 當日累計超限 | 隔日再試 |
CC-AUTH-1005 |
422 | MCC 控管拒絕 | 風險參數拒絕 | 聯繫客服 |
CC-AUTH-1006 |
422 | EMV 驗證失敗 | ARQC 驗證不通過 | 重新插卡 |
CC-AUTH-1901 |
422 | 授權併發衝突(已重試) | 重試 3 次仍衝突 | 稍後重試 |
CC-AUTH-4040 |
404 | 資源不存在(授權/檢核明細) | 查無指定授權編號(CR-021 補實作之沖正/檢核明細端點所需;沿用 per-service 4040 慣例) |
確認授權編號 |
CC-AUTH-5001 |
503 | 發卡主機無回應 | 主機逾時 | 依 Stand-in 規則處理 |
CC-LIM-1001 |
422 | 可用額度不足 | available < amount |
調額或降低金額 |
CC-LIM-1002 |
422 | 超過可設定上限 | 超出角色/客層上限 | 送主管複核 |
CC-LIM-1003 |
422 | 專用額度不可用 | 未設定或已過期 | 先設定專用額度 |
CC-LIM-2001 |
409 | 額度併發衝突 | 樂觀鎖失敗 | 重試 |
CC-LIM-5001 |
503 | 商品參數服務無法連線 | 取參失敗(product-service 不可達/逾時/查無參數);依 CR-045 D4 之 fail-closed 降級至預設覆核門檻 | 檢查參數服務可用性 |
CC-CARD-1001 |
422 | 快發條款未勾選 | fastIssueAgreed=false |
先勾選條款 |
CC-CARD-1002 |
422 | 三要素驗證失敗 | 身分證字號/生日/核准序號不符 | 確認後重試 |
CC-CARD-1003 |
422 | 卡號區段已用盡 | 號碼池不足 | 聯繫系統管理 |
CC-CARD-3001 |
409 | 狀態轉換不允許 | 非法狀態轉換 | 依狀態機重新操作 |
CC-CARD-3002 |
409 | 終態不可再轉換 | 已 CLOSED | 不可操作 |
CC-CARD-1004 |
422 | Token 重複綁定 | 同卡同平台已有 ACTIVE 之 Token |
先解綁或改用其他平台 |
CC-CARD-4001 |
400 | 請求欄位驗證失敗 | 欄位格式或列舉值不符 | 檢視欄位 |
CC-CARD-4040 |
404 | 資源不存在(卡片/Token/申請案件) | 查無指定識別碼 | 確認識別碼 |
CC-CARD-2001 |
409 | 冪等鍵衝突 | 同鍵異指紋 | 更換冪等鍵 |
CC-CARD-1004/CC-CARD-2001/CC-CARD-4001/CC-CARD-4040之來源(HANDOVER #C-10,2026-09-20):
card-service補發卡/快發快綁/Token/申請案件四項(契約原已宣告 8 端點、實作僅 3 端點)。
既有 5 碼僅涵蓋業務規則(1001~1003)與狀態機(3001/3002),
無 400(欄位驗證)與 404(查無資源)——新增端點需此二碼以維持「所有錯誤一律套用封套」(CSP-003 §3);
1004則為 Token 重複綁定之業務規則;2001沿用既有冪等衝突碼之慣例(CC-PTY-2001/CC-POST-2001/CC-PLT-2001)。三者編碼對齊既有1000=業務規則、4001/4040=協定層之慣例。
|CC-PTY-1001| 422 | 管制碼不允許操作 | 帳戶管制中 | 先解除管制 |
|CC-PTY-2001| 409 | 冪等鍵衝突 | 同鍵異指紋 | 更換冪等鍵 |
|CC-PTY-4001| 400 | 請求欄位驗證失敗 | 欄位格式或必填不符 | 檢視欄位 |
|CC-PTY-4040| 404 | 資源不存在(歸戶/持卡人) | 查無指定識別碼 | 確認識別碼 |
|CC-PRD-1001| 422 | 產品代碼重複 | 已存在 | 更換代碼 |
|CC-PRD-1002| 422 | 參數生效日重疊 | 同期間已有參數 | 調整生效日 |
|CC-POST-1001| 422 | 授權請款比對不符 | 金額或幣別不符 | 人工確認 |
|CC-POST-1002| 422 | 授權失效或不存在 | 無對應授權 | 人工確認 |
|CC-POST-2001| 409 | 冪等鍵衝突 | 同鍵異指紋 | 更換冪等鍵 |
|CC-POST-2002| 409 | 樂觀鎖衝突 | 併發更新 | 重試 |
|CC-POST-3001| 409 | 交易狀態不允許沖正 | 已 REVERSED | 不可再沖正 |
|CC-POST-4001| 409 | 重複入帳(業務冪等鍵已存在) | 同一筆帳務 | 檢視前次結果 |
|CC-POST-4002| 409 | 整批重複請款(疑似) | 檔案指紋相同 | 人工判定 |
|CC-POST-4000| 400 | 請求驗證錯誤 | 欄位格式或必填不符 | 修正請求 |
|CC-POST-4040| 404 | 資源不存在(入帳交易/分戶餘額) | 查無指定識別碼 | 確認識別碼 |
|CC-PAY-1001| 422 | 繳款金額不符 | 與應繳金額不符 | 確認金額 |
|CC-PAY-2001| 409 | 繳款冪等衝突 | 重複送件 | 檢視前次結果 |
|CC-PAY-4000| 400 | 請求驗證錯誤(含分配不平衡) | 欄位格式、金額或分配加總不符 | 修正請求 |
|CC-PAY-4040| 404 | 資源不存在(繳款/自扣約定) | 查無指定識別碼 | 確認識別碼 |
|CC-PAY-3001| 409 | 繳款狀態不允許沖正 | 已 REVERSED | — |
|CC-STM-1001| 422 | 帳單週期不存在 | 週期代碼錯誤 | 確認週期 |
|CC-STM-5001| 503 | 批次執行中 | 同一週期已有執行中批次 | 等待完成 |
|CC-STM-2001| 409 | 週期重複執行(同帳戶同月份已產生) |UQ_STMT_MONTH衝突 | 檢視既有帳單 |
|CC-STM-4000| 400 | 請求驗證錯誤 | 欄位格式或必填不符 | 修正請求 |
|CC-STM-4040| 404 | 資源不存在(帳單/批次執行) | 查無指定識別碼 | 確認識別碼 |
|CC-PRC-1001| 422 | 無適用利率 | RBP 未評核且無產品利率 | 先完成 RBP 評核 |
|CC-PRC-1002| 422 | 計息期間重疊 | 期間已有計息 | 調整期間 |
|CC-INS-1001| 422 | 分期期數不支援 | 非 3/6/12/24 | 使用支援期數 |
|CC-INS-1002| 422 | 總約未簽署 |agreementSigned=false| 先完成簽署 |
|CC-INS-4000| 400 | 請求驗證錯誤 | 欄位格式或期數不符 | 修正請求 |
|CC-INS-4040| 404 | 資源不存在(分期計畫) | 查無指定識別碼 | 確認識別碼 |
|CC-INS-3001| 409 | 分期狀態不允許操作 | 非 ACTIVE | 依狀態機操作 |
|CC-RWD-1001| 422 | 點數不足 | 餘額不足且政策為拒絕 | 部分折抵或改設定 |
|CC-RWD-1002| 422 | 未達最低折抵金額 | 低於門檻 | 提高消費金額 |
|CC-RWD-1003| 422 | 點數帳戶已凍結 | AV2 到期 | 不可兌換 |
|CC-RWD-1005| 422 | 回饋規則代碼重複 | 規則代碼已存在 | 更換規則代碼 |
|CC-RWD-2001| 409 | 扣點併發衝突 | 樂觀鎖失敗 | 重試 |
|CC-RWD-4000| 400 | 請求驗證錯誤 | 欄位格式或必填不符 | 修正請求 |
|CC-RWD-4040| 404 | 資源不存在(點數帳戶) | 查無指定歸戶 | 確認識別碼 |
|CC-CLR-1001| 422 | 清算差異未歸零 | 存在差異 | 差異處理後重新比對 |
|CC-CLR-5001| 503 | 卡組織清算檔未到 | 檔案未取得 | 稍後重試 |
|CC-DSP-1001| 422 | 爭議類型不支援 | — | 確認類型 |
|CC-DSP-1002| 422 | 超過卡組織時限 | 已逾時限 | 不可提爭議 |
|CC-MRC-1001| 422 | 特店代碼重複 | 已存在 | 更換代碼 |
|CC-NOT-1001| 422 | 通知樣板不存在 | 樣板代碼錯誤 | 確認樣板 |
|CC-NOT-1002| 422 | 無可用發送通道 | 樣板未啟用任何請求之通道(偏好與樣板可用通道無交集) | 確認通道或樣板設定 |
|CC-NOT-1003| 422 | 樣板變數不足 |variables缺少樣板內文所引用之變數 | 補齊變數 |
|CC-NOT-4040| 404 | 資源不存在(通知) | 查無指定通知識別碼 | 確認識別碼 |
|CC-RPT-1001| 422 | 報表定義不存在 | 報表代碼錯誤 | 確認代碼 |
|CC-MIG-1001| 422 | 遷移類型不支援 | — | 確認類型 |
CC-NOT-1002/CC-NOT-1003/CC-NOT-4040之來源(CR-017 §9.4,2026-09-22):
notification-service實作(C-9)時發現既有CC-NOT-*僅 3 碼(1001樣板不存在、
3001/3002狀態機),缺「無可用通道」(業務規則)與「查無通知」(協定層)——
二者皆為契約端點所必需(POST /api/v1/notifications之 422、GET /api/v1/notifications/{id}
之 404),故依CSP-003 §2「不得自創錯誤碼」程序補登。
編碼對齊既有慣例:1000=業務規則、4040=協定層「查無資源」(同CC-CARD-4040/CC-PTY-4040)。
| CC-PLT-2001 | 409 | 冪等鍵衝突 | 同鍵異指紋 | 更換冪等鍵 |
| CC-PLT-2002 | 409 | 狀態版本衝突 | 樂觀鎖/狀態條件更新失敗(SDD-005 SM-04),如 Saga 並行協調 | 重讀最新狀態後重試 |
| CC-PLT-3001 | 409 | 非法狀態轉換 | 來源狀態未定義此事件之轉換(SDD-005 SM-01/SM-02) | 查詢當前狀態後重送合法事件 |
| CC-PLT-3002 | 409 | 終態不可轉換 | 來源狀態為終態(SDD-005 SM-05),例如 CLOSED → ACTIVE | 不得重送,須另立新單 |
| CC-PLT-3003 | 409 | 資源尚未就緒 | 目標資源尚未產生完成——如非同步作業仍在 QUEUED/RUNNING,或已 FAILED(與「狀態機非法轉換」語意不同,見 GOV-007 §4) | 待作業完成後重試;FAILED 者查作業狀態 |
| CC-PLT-4001 | 400 | 缺少必要請求標頭或參數 | 必填標頭(如 X-Channel)或查詢參數未提供 | 補上後重送 |
| CC-PLT-4002 | 400 | 請求主體格式錯誤 | 主體非合法 JSON,或欄位型別無法解析 | 修正請求主體 |
| CC-PLT-4003 | 400 | 請求參數驗證失敗 | 參數型別不符,或 Bean Validation 未通過 | 依 details 修正欄位 |
| CC-PLT-4010 | 401 | 未認證 | 無 Authorization 標頭、格式非 Bearer、權杖過期或簽章驗證失敗 | 重新取得權杖後重送 |
| CC-PLT-4030 | 403 | 無權限 | 已認證但角色/權限不足(SAD-005 §4) | 確認帳號之角色授權 |
| CC-PLT-4040 | 404 | 資源或路徑不存在 | 無對應路由,或聚合根查無 | 確認路徑與識別碼 |
| CC-PLT-4050 | 405 | HTTP 方法不支援 | 動詞與端點不符 | 改用正確方法 |
CC-PLT-4001~CC-PLT-4050之來源(CR-004,2026-09-19):
本 5 碼為請求層錯誤(類別位 4,見 §3.3.1),由common-spring之
GlobalExceptionHandler統一拋出,服務不需自行實作。
服務僅在需要更精確語意時才使用自有 4xxx 碼(如CC-PTY-4040「歸戶查無」)。
對應之 Spring 例外:
MissingRequestHeaderException/MissingServletRequestParameterException→4001;
HttpMessageNotReadableException→4002;
MethodArgumentNotValidException/HandlerMethodValidationException/
ConstraintViolationException/MethodArgumentTypeMismatchException→4003;
NoResourceFoundException→4040;HttpRequestMethodNotSupportedException→4050。
CC-PLT-4010/CC-PLT-4030之來源(SCS-022,2026-09-19):
CR-004之 5 碼僅涵蓋 400/404/405,無 401(未認證)與 403(無權限)——
因當時GlobalExceptionHandler尚未處理認證/授權(Spring Security 攔截發生在
進入控制器之前,不在@ControllerAdvice之攔截範圍)。
W-3 SSO 接線(SCS-022)後,common-spring之SecurityAutoConfiguration
需此二碼以維持「所有回應一律套用ApiResponse封套」(CSP-003 §3)。
依CSP-003 §2「不得自創錯誤碼」程序補登;
後兩碼對齊 HTTP 狀態碼(401/403),與既有4040/4050之慣例一致。
各服務之狀態機錯誤碼(SDD-005 SM-01/SM-02/SM-05;由共用狀態機引擎依服務代碼拋出)
| 代碼 | HTTP | 名稱 | 觸發條件 | 客戶端處置 |
|---|---|---|---|---|
CC-AUTH-3001 |
409 | 非法狀態轉換 | 授權相關狀態機(來源狀態未定義此事件) | 查詢當前狀態後重送合法事件 |
CC-AUTH-3002 |
409 | 終態不可轉換 | 授權相關狀態機(來源狀態為終態) | 不得重送,須另立新單 |
CC-CARD-3001 |
409 | 非法狀態轉換 | 卡片狀態機(SDD-005 §2) | 同上 |
CC-CARD-3002 |
409 | 終態不可轉換 | 卡片狀態機,例如 CLOSED → ACTIVE
|
同上 |
CC-LIM-3001 |
409 | 非法狀態轉換 | 額度佔用/額度調整狀態機(SDD-005 §3/§8) | 同上 |
CC-LIM-3002 |
409 | 終態不可轉換 | 額度調整已 REJECTED/EXPIRED/REVOKED 仍嘗試覆核 |
同上 |
CC-POST-3001 |
409 | 非法狀態轉換 | 帳務交易狀態機(SDD-005 §4) | 同上 |
CC-POST-3002 |
409 | 終態不可轉換 | 帳務交易已 REVERSED 仍嘗試入帳 |
同上 |
CC-PAY-3001 |
409 | 非法狀態轉換 | 繳款狀態機(SDD-005 §5) | 同上 |
CC-PAY-3002 |
409 | 終態不可轉換 | 繳款已沖正仍嘗試分配 | 同上 |
CC-INS-3001 |
409 | 非法狀態轉換 | 分期計畫狀態機(SDD-005 §6) | 同上 |
CC-INS-3002 |
409 | 終態不可轉換 | 分期已結清仍嘗試攤還 | 同上 |
CC-DSP-3001 |
409 | 非法狀態轉換 | 爭議案件狀態機(SDD-005 §7) | 同上 |
CC-DSP-3002 |
409 | 終態不可轉換 | 爭議已 CLOSED 仍嘗試調整 |
同上 |
CC-STM-3001 |
409 | 非法狀態轉換 | 帳單狀態機(SDD-005 §9) | 同上 |
CC-STM-3002 |
409 | 終態不可轉換 | 帳單已結清仍嘗試產製 | 同上 |
CC-RWD-3001 |
409 | 非法狀態轉換 | 點數帳戶狀態機(SDD-005 §10) | 同上 |
CC-RWD-3002 |
409 | 終態不可轉換 | 點數帳戶已 CLOSED 仍嘗試入帳 |
同上 |
CC-PTY-3001 |
409 | 非法狀態轉換 | 當事人/帳戶狀態機 | 同上 |
CC-PTY-3002 |
409 | 終態不可轉換 | 當事人/帳戶已終止 | 同上 |
CC-PRD-3001 |
409 | 非法狀態轉換 | 產品/參數狀態機 | 同上 |
CC-PRD-3002 |
409 | 終態不可轉換 | 產品已下架 | 同上 |
CC-PRC-3001 |
409 | 非法狀態轉換 | 定價/利率狀態機 | 同上 |
CC-PRC-3002 |
409 | 終態不可轉換 | 定價已失效 | 同上 |
CC-CLR-3001 |
409 | 非法狀態轉換 | 清算批次狀態機 | 同上 |
CC-CLR-3002 |
409 | 終態不可轉換 | 清算批次已結案 | 同上 |
CC-MRC-3001 |
409 | 非法狀態轉換 | 特店狀態機 | 同上 |
CC-MRC-3002 |
409 | 終態不可轉換 | 特店已終止 | 同上 |
CC-MIG-3001 |
409 | 非法狀態轉換 | 遷移作業狀態機 | 同上 |
CC-MIG-3002 |
409 | 終態不可轉換 | 遷移作業已完成 | 同上 |
CC-NOT-3001 |
409 | 非法狀態轉換 | 通知狀態機 | 同上 |
CC-NOT-3002 |
409 | 終態不可轉換 | 通知已結案 | 同上 |
CC-RPT-3001 |
409 | 非法狀態轉換 | 報表作業狀態機 | 同上 |
CC-RPT-3002 |
409 | 終態不可轉換 | 報表已歸檔 | 同上 |
CC-PLT-5000 |
500 | 未預期系統錯誤 | 未分類之伺服器例外(GlobalExceptionHandler 之兜底) |
已告警,聯繫系統管理 |
CC-PLT-5001 |
503 | 外部系統逾時 | 逾時 | 重試(冪等者) |
CC-PLT-5002 |
500 | Saga 補償失敗 | 補償步驟失敗 | 告警並人工介入 |
CC-PLT-5003 |
503 | 事件發布失敗 | Kafka 不可用 | 自動重試 |
CC-PLT-5004 |
500 | 欄位加解密失敗 | 金鑰不存在、密文損毀或格式不符 | 告警,人工介入(不得回退為明文) |
CC-PLT-5005 |
503 | 定價服務呼叫失敗 |
pricing-service 不可用、逾時或回應無法解析(CR-006 計息串接) |
不入帳,重試(計息請求帶冪等鍵,可安全重送) |
CC-GW-1001 |
422 | 缺少冪等鍵 | 寫入操作未帶標頭 | 補上標頭 |
CC-GW-1002 |
401 | 權杖無效或過期 | 簽章不符/已過期/被竄改 | 重新登入 |
CC-GW-1003 |
403 | 權限不足 | 角色或範圍(scope)不足 | 申請權限 |
CC-GW-4290 |
429 | 超過速率限制 | 限流 | 退避重試 |
CC-GW-5001 |
503 | 服務不可用 | 斷路器開啟 | 稍後重試 |
3.4.19 campaign-service(行銷活動引擎;CR-046/BRD-005)
★ 本組為
campaign-service專用。編碼對齊既有慣例:1000=業務規則、2001=冪等衝突、
3001=狀態機、4001/4040=協定層。
⚠️CC-CAM-1002/CC-CAM-1003之語意直接來自客戶 BRD(BRD-005D9 疊加必填、
例 9 利率型回沖必填)——非我方推測,故採獨立錯誤碼而非通用 400,以便業務端追溯。
|CC-CAM-1001| 422 | 活動期間不合法 | 迄日早於起日 | 調整期間 |
|CC-CAM-1002| 422 | 疊加模式未宣告 | 未提供stackingMode(BRD-005D9 明訂為必要參數、無預設值) | 補上疊加模式 |
|CC-CAM-1003| 422 | 利率型活動須宣告回沖規則 |rewardForm=RATE_CUT但未提供clawbackRule(BRD-005例 9) | 補上回沖規則 |
|CC-CAM-1004| 422 | 錄取名額已額滿 | 波次名額用罄(BRD-005BR-06) | 改選其他波次/活動 |
|CC-CAM-2001| 409 | 冪等鍵衝突 | 同鍵異指紋 | 更換冪等鍵 |
|CC-CAM-3001| 422 | 活動狀態不允許此操作 | 狀態機不允許(如非 DRAFT 欲核准) | 檢視活動狀態 |
|CC-CAM-4001| 400 | 請求欄位驗證失敗 | 欄位格式或必填不符 | 檢視欄位 |
|CC-CAM-4040| 404 | 資源不存在(活動/方案/任務) | 查無指定識別碼 | 確認識別碼 |
4. 事件契約
事件信封與命名規範見 SAD-004 §5。事件之 JSON Schema 位於
cardcore/contracts/events/<context>.<aggregate>.<verb>.v<major>.json。
4.1 事件信封(固定)
{
"eventId": "01HZX8K2M4N5P6Q7R8S9T0V1W2",
"eventType": "posting.transaction.posted.v1",
"eventVersion": 1,
"occurredAt": "2026-09-17T02:15:30.123Z",
"aggregateId": "018f2a3b-...",
"sequenceNo": 42,
"traceId": "01HZX8K2M4N5P6Q7R8S9T0V1W2",
"causationId": "01HZX8K2M4N5P6Q7R8S9T0V1W1",
"correlationId": "AUTH20260917000001",
"schemaRef": "posting.transaction.posted.v1",
"payload": { }
}
4.2 主要事件
★ 事件之唯一權威清單為
SAD-004 §5.2(CR-030 §2 #5)。本表僅列 payload 欄位。
| 事件 | payload 主要欄位 |
|---|---|
authorization.authorization.approved.v1 |
authorizationId, authNo, cardId, accountId, amount, currency, holdId, holdAmount, standinFlag, cardNoMasked*, merchantId*, decision*
|
authorization.authorization.declined.v1 |
authorizationId, respCode, failedChecks[] ⛔ 刻意未發射(見下) |
limit.limit.adjusted.v1 |
accountId, adjustType, beforeAmount, afterAmount, effectiveFrom
|
limit.hold.created.v1 |
holdId, accountId, holdAmount, holdType, instrumentId*, authorizationId*, currency* |
limit.hold.released.v1 |
holdId, accountId, releasedAmount, instrumentId* |
card.card.issued.v1 |
cardId, cardNoMasked, productCode, instantIssued
|
card.card.status-changed.v1 |
instrumentId*, cardNoHash, cardId, accountId, cardNoMasked, status, blockCode, expiryDate
|
card.token.bound.v1 |
tokenId, cardId, platform
|
product.parameter.changed.v1 |
namespace, paramKey, effectiveFrom
|
posting.transaction.posted.v1 |
postingId, txNo, accountId, instrumentId*, merchantId*, mcc*, payPath*(CR-048;支付路徑,未提供=未判定), txType, amount, currency, postingDate, subAccountEntries[]
|
campaign.reward.commanded.v1 |
commandId, campaignCode, recognition{campaignCode, postingId, txNo, accountId, instrumentId*, merchantId*, mcc*, txType, amount, currency, postingDate, applicableRate*, rateReason*} (★ CR-046 B4 首次定義:REWARD_CMDS(PENDING) 經 relay 派發;rewards 以 campcmd:<commandId> 冪等入點。★ CR-049:回沖指令同走此信封,recognition{…, originalPostingId, txType=REFUND, amount, postingDate, refund:true}——消費端以 txType=REFUND 為權威,欄位一律「recognition 優先、payload 次之」) |
posting.transaction.reversed.v1 |
postingId, reversalOf, amount, reason, postingDate*(★ CR-049:沖正交易之入帳日=業務日錨點;下游回沖分錄以此為 posting_date,不得以 occurredAt 反推——SCS-026 §7.5①。可選欄位,不升版) |
posting.exception.raised.v1 |
postingId, exceptionType, detail
|
payment.payment.posted.v1 |
paymentId, accountId, amount, allocations[], availableLimitAfter, currency* |
payment.payment.unmatched.v1 |
paymentId, accountId, amount, daysUnmatched, currency*
|
payment.payment.reversed.v1 |
paymentId, accountId, amount, reason, currency* (★ CR-030 §2 #9 首次定義) |
statement.statement.issued.v1 |
statementId, accountId, statementMonth, totalAmount, minimumDue, paymentDueDate, currency* |
installment.plan.created.v1 |
planId, accountId, originalAmount, periodCount, perPeriodAmount
|
rewards.points.accrued.v1 |
accountId, pointsType, bucket, points, afterBalance
|
rewards.points.redeemed.v1 |
redemptionId, accountId, pointsUsed, amountPaid, merchantShare
|
clearing.batch.matched.v1 |
runId, businessDate, txCount, totalAmount, differenceCount
|
dispute.case.opened.v1 |
disputeId, accountId, transactionId, disputeType, amount
|
dispute.case.closed.v1 |
disputeId, resolution, adjustAmount
|
merchant.settlement.submitted.v1 |
settlementId, merchantId, grossAmount, feeAmount, netAmount
|
notification.sent.v1 |
notificationId, channels[], sentAt
|
* 可選欄位(
SAD-004 §5.3:新增可選欄位不升版;通則見 §2.5)。
cardNoMasked/merchantId(approved.v1)——2026-10-01(#A-125),供通知樣板AUTH_APPROVED;
★cardNoMasked為遮罩值(不含 PAN),符合 PCI DSS。authorizationId(limit.hold.created.v1)——CR-028,供authorization-service之本地投影對應。currency(limit.hold.created.v1/payment.posted.v1/statement.issued.v1/
payment.unmatched.v1/payment.reversed.v1)——依 §2.5 通則(CR-030 §2 #10)。decision(approved.v1)——CR-030 §2 #8新增:值域APPROVED/STANDIN/REFERRAL。
★ 本事件之語意為「授權已佔用額度」(三者皆佔用);下游據decision分辨「核准」與「轉人工待覆核」。instrumentId(card.card.status-changed.v1)——CR-046 B2-6:交易載體最小識別;由CC_CARD.INSTRUMENTS依cardId解析。既有尚未回填載體之卡片可為null,不臆造值。instrumentId(posting.transaction.posted.v1)——CR-046B4 前置增量:直接取既有入帳交易之INSTRUMENT_ID;非載體交易(例如利息)可為null,舊事件亦可缺欄;消費端不得將cardId猜回載體。merchantId(posting.transaction.posted.v1)——CR-046AC 11 前置增量:取既有PostingRequest.merchantId/Transaction.merchantId;歷史或未提供者為null。mcc與支付路徑不在本次補入,避免臆造來源。mcc(posting.transaction.posted.v1)——CR-046AC 11 資料鏈補強:呼叫端自授權紀錄(CC_AUTH.AUTHORIZATIONS.mcc,權威)如實帶入;posting 不做 MCC 對映、不從 merchantId 推導;非授權來源交易與舊事件為null,消費端必須容忍缺欄。instrumentId(limit.hold.created.v1/limit.hold.released.v1)——CR-054P1(2026-10-17):
交易載體最小識別,供authorization-service之投影把佔用歸到正確的卡。
★ 補此欄之根因:權威表CC_LIM.LIMIT_HOLDS.INSTRUMENT_ID早於V10即有值,
但事件原本不帶 → 投影永遠拿不到(CR-054 §3.1「權威表有、事件沒有」)。
created取hold()之入參;released因入參不含載體,由同交易findHoldInstrumentId回查。
⚠️V10前之歷史佔用無載體 → 可為null(不得回填、不得以cardId猜);消費端須容忍缺欄。
⛔
authorization.authorization.declined.v1為「刻意未發射」(CR-030 §2 #7裁定):
拒絕不改變任何下游狀態(不佔額度、不產生待請款),依「事件=已發生的狀態變更」原則不應灌入事件流;
拒絕率/原因分佈改以 metrics(cardcore_authorization_declined_total{reason})涵蓋。
payload 保留於本表,供日後「拒絕率即時看板」需求時啟用(無契約破壞)。
🔧 本表之歷史修訂:
(i)CR-028:刪除authorization.hold.created.v1/.released.v1兩列(「同一事實兩個生產者」之矛盾)、
補登limit.hold.created.v1;(ii)CR-028:更正card.card.status-changed.v1之 payload——
原載fromBlockCode/toBlockCode/reason(差量),惟實作(SCS-003 §7.10,#A-118真機驗證)
發射者為變更後之完整狀態——本表原為該事件之唯一錯誤記載,已依實作更正
(CR-030 §2 #13追認以實作為準,結案)。
(iii) ★ 結構修復:本表原被上述CR-028說明從中間截斷(後半表格失去表頭)——已一併修復。
4.3 事件相容性規則
| 變更 | 相容性 | 處置 |
|---|---|---|
| 新增可選欄位 | 相容 | 不升版 |
| 新增必填欄位 | 不相容 | 升版並雙軌 ≥ 1 release |
| 移除欄位 | 不相容 | 升版並雙軌 |
| 改型別 | 不相容 | 升版並雙軌 |
| 改語意 | 不相容 | 升版並雙軌 |
| 消費者 | 須同時容忍 vN 與 vN+1
|
至少一個 release |
5. 契約測試
| 測試類型 | 說明 | 工具 |
|---|---|---|
| Provider 契約測試 | 服務實作是否符合契約 | 自動化(依 OpenAPI 驗證回應) |
| Consumer 契約測試 | 消費端對契約之期待 | 自動化 |
| 破壞性變更檢測 | 新版契約是否破壞舊版 | CI 契約 diff |
| Schema 驗證 | 回應是否符合宣告之 schema | 自動化 |
| 錯誤碼驗證 | 錯誤情境是否回傳宣告之錯誤碼 | 自動化 |
通過標準
| 項目 | 標準 |
|---|---|
| Provider 契約測試 | 100% 通過 |
| 破壞性變更 | 0 筆(或已升版雙軌) |
| 錯誤碼一致性 | 實作與本文件 100% 一致 |
| $ref 可解析 | 100% |
6. 端點清單(依服務)
| 服務 | 方法與路徑 | 說明 |
|---|---|---|
| authorization | POST /api/v1/authorizations |
取授權 |
| authorization | GET /api/v1/authorizations/{authId} |
查詢授權 |
| authorization | POST /api/v1/authorizations/{authId}/reversals |
取消/沖正 |
| authorization | POST /api/v1/authorizations/{authId}/advices |
人工/強制授權 |
| authorization | GET /api/v1/authorizations/{authId}/check-results |
檢核明細 |
| limit | GET /api/v1/accounts/{accountId}/limits |
歸戶額度 |
| limit | GET /api/v1/accounts/{accountId}/limits |
歸戶額度(★ CR-043 §3.1 #7:原載卡級路徑,更正為歸戶級) |
| limit | POST /api/v1/limit-adjustments |
額度調整 |
| limit | GET /api/v1/limit-adjustments/{adjustmentId} |
查詢調整 |
| limit | POST /api/v1/limit-holds |
建立佔用 |
| limit | POST /api/v1/limit-holds/{holdId}/releases |
釋放佔用 |
| posting | POST /api/v1/postings |
入帳 |
| posting | GET /api/v1/postings/{postingId} |
查詢入帳 |
| posting | POST /api/v1/postings/{postingId}/reversals |
錯帳沖正 |
| posting | GET /api/v1/accounts/{accountId}/balances |
帳務餘額 |
| posting | GET /api/v1/accounts/{accountId}/transactions |
交易明細 |
| posting | POST /api/v1/posting-duplicate-checks |
重複請款偵測 |
| payment | POST /api/v1/payments |
繳款受理 |
| payment | GET /api/v1/payments/{paymentId} |
查詢繳款 |
| payment | POST /api/v1/auto-debit-enrollments |
自扣設定 |
| payment | POST /api/v1/payments/{paymentId}/reversals |
繳款沖正 |
| card | POST /api/v1/cards |
發卡 |
| card | GET /api/v1/cards/{cardId} |
查詢卡片 |
| card | POST /api/v1/cards/{cardId}/lifecycle-transitions |
狀態轉換 |
| card | POST /api/v1/cards/{cardId}/instant-issuance |
快發快綁 |
| card | POST /api/v1/cards/{cardId}/tokens |
Token 綁定 |
| card | GET /api/v1/cards/{cardId}/tokens |
查詢 Token |
| card | POST /api/v1/card-applications |
申請案件 |
| party | GET /api/v1/accounts/{accountId} |
查詢歸戶 |
| party | GET /api/v1/parties/{partyId} |
查詢持卡人 |
| party | POST /api/v1/parties/{partyId}/flags |
貼標 |
| party | POST /api/v1/accounts/{accountId}/block-codes |
管制碼 |
| product | POST /api/v1/products |
建立產品 |
| product | GET /api/v1/products/{productCode} |
查詢產品 |
| product | PUT /api/v1/products/{productCode} |
更新產品 |
| product | GET /api/v1/parameters/{namespace}/{paramKey} |
查詢參數 |
| product | PUT /api/v1/parameters/{namespace}/{paramKey} |
設定參數 |
| statement | POST /api/v1/statement-cycles/{cycleCode}/runs |
帳單批次 |
| statement | GET /api/v1/statement-runs/{runId} |
批次狀態 |
| statement | GET /api/v1/accounts/{accountId}/statements |
查詢帳單 |
| pricing | POST /api/v1/pricing/interest-calculations |
利息計算 |
| pricing | GET /api/v1/pricing/interest-inquiries |
利息查詢 |
| pricing | GET /api/v1/pricing/fees |
費用設定 |
| installment | POST /api/v1/installments |
建立分期 |
| installment | GET /api/v1/installments/{planId} |
查詢分期 |
| installment | POST /api/v1/installments/{planId}/settlements |
結清/違約 |
| rewards | POST /api/v1/rewards/accruals |
點數回饋 |
| rewards | POST /api/v1/rewards/redemptions |
點數兌換 |
| rewards | GET /api/v1/rewards/accounts/{accountId} |
點數帳戶 |
| rewards | POST /api/v1/reward-rules |
回饋規則 |
| clearing | POST /api/v1/clearing/runs |
清分比對 |
| clearing | GET /api/v1/clearing/reconciliations/{runId} |
差異明細 |
| clearing | POST /api/v1/clearing/settlements |
撥款與分錄 |
| dispute | POST /api/v1/disputes |
爭議受理 |
| dispute | GET /api/v1/disputes/{disputeId} |
查詢爭議 |
| dispute | POST /api/v1/disputes/{disputeId}/chargebacks |
扣款與仲裁 |
| merchant | POST /api/v1/merchants |
建立特店 |
| merchant | GET /api/v1/merchants/{merchantId} |
查詢特店 |
| merchant | POST /api/v1/merchant-settlements |
商店請款 |
| notification | POST /api/v1/notifications |
發送通知 |
| notification | GET /api/v1/notifications/{notificationId} |
查詢通知 |
| report | POST /api/v1/report-jobs |
產生報表 |
| report | GET /api/v1/report-jobs/{jobId} |
查詢報表 |
| migration | POST /api/v1/migration-jobs |
資料轉換 |
| migration | GET /api/v1/legacy-accounts/{legacyId} |
查詢舊資料 |
| 合計 | 65 端點 |
7. 代表端點範例(請求/回應)
本節列出關鍵端點的具體 JSON 範例,使 Coder 無須反推欄位形狀。
全部 65 個端點的請求/回應example已內嵌於
cardcore/contracts/openapi/<service>.yaml,並可經契約測試直接驗證。
所有回應皆套用 §2.2 之統一信封(成功error=null;失敗data=null)。
7.1 取授權 POST /api/v1/authorizations
請求
{
"stan": "20260917000001",
"cardNo": "4111111234567890",
"txType": "PURCHASE",
"amount": 12345,
"currency": "TWD",
"mcc": "5411",
"merchantId": "MERCH001",
"merchantName": "便利商店",
"entryMode": "CHIP",
"txTime": "2026-09-17T02:15:30Z",
"emvData": { "arqc": "A1B2C3D4" }
}
成功回應(201)— data 主體
{
"authorizationId": "018f2a3b-0000-0000-0000-000000000001",
"authNo": "AUTH000123",
"decision": "APPROVED",
"respCode": "00",
"holdId": "018f2a3b-0000-0000-0000-000000000002",
"holdAmount": 12345,
"availableLimit": 876550,
"checkResults": [
{ "checkCode": "LIMIT", "checkOrder": 1, "result": "PASS", "reasonCode": null }
],
"txTime": "2026-09-17T02:15:30Z"
}
7.2 入帳 POST /api/v1/postings
請求
{
"accountId": "018f2a3b-0000-0000-0000-000000000010",
"cardId": "018f2a3b-0000-0000-0000-000000000020",
"authorizationId": "018f2a3b-0000-0000-0000-000000000001",
"txCode": "PUR",
"txType": "PURCHASE",
"amount": 12345,
"currency": "TWD",
"postingDate": "2026-09-17",
"merchantId": "MERCH001",
"businessIdempotencyKey": "4111111234567890|AUTH000123|2026-09-17|12345|TWD"
}
成功回應(201)— data 主體(含分欄位與更新後餘額)
{
"postingId": "018f2a3b-0000-0000-0000-000000000030",
"txNo": "TXN20260917000001",
"status": "POSTED",
"amount": 12345,
"currency": "TWD",
"postingDate": "2026-09-17",
"subAccountEntries": [
{ "subAccount": "PRINCIPAL", "amount": 12345, "beforeAmount": 0, "afterAmount": 12345 }
],
"releasedHoldAmount": 12345,
"balanceAfter": {
"accountId": "018f2a3b-0000-0000-0000-000000000010",
"principalAmount": 12345, "interestAmount": 0, "feeAmount": 0, "taxAmount": 0,
"totalAmount": 12345, "minimumDue": 0, "paymentDueDate": null,
"agingBucket": 0, "currency": "TWD", "asOf": "2026-09-17T02:20:00Z"
}
}
7.3 繳款受理 POST /api/v1/payments
請求
{
"accountId": "018f2a3b-0000-0000-0000-000000000010",
"amount": 50000,
"currency": "TWD",
"paymentMethod": "AUTO_DEBIT",
"channel": "BATCH",
"paymentDate": "2026-09-17",
"externalRef": "AD20260917001"
}
成功回應(201)— data 主體(即時恢復額度)
{
"paymentId": "018f2a3b-0000-0000-0000-000000000040",
"paymentNo": "PAY20260917000001",
"status": "POSTED",
"amount": 50000,
"currency": "TWD",
"allocations": [
{ "subAccount": "PRINCIPAL", "amount": 50000, "allocationOrder": 1 }
],
"availableLimitAfter": 926550,
"limitRestored": true,
"postedAt": "2026-09-17T03:00:00Z"
}
7.4 建立額度佔用 POST /api/v1/limit-holds
請求
{
"accountId": "018f2a3b-0000-0000-0000-000000000010",
"authorizationId": "018f2a3b-0000-0000-0000-000000000001",
"holdAmount": 12345,
"holdType": "GENERAL"
}
成功回應(201)— data 主體
{
"holdId": "018f2a3b-0000-0000-0000-000000000002",
"accountId": "018f2a3b-0000-0000-0000-000000000010",
"holdAmount": 12345,
"releasedAmount": 0,
"status": "HELD",
"availableAmount": 864105
}
8. 變更記錄
| 版本 | 說明 |
|---|---|
| 1.0 | 初版:端點清單、通用規範、錯誤碼清單、事件契約 |
| 1.1 | 契約強化:65 端點全部補齊欄位級 example 與每端點業務規則/不變式(OP_RULES);reward-rules 端點改用 RewardRuleRequest;詳見 cardcore/tools/gen_openapi.py
|
| 1.2 | ①修正表頭版本:本文件表頭原停留於 1.0,但變更記錄已有 1.1 條目,兩者不一致;現更正為 1.2 並同步本記錄。②新增錯誤碼 CC-PLT-5000(500 未預期系統錯誤)——CSP-003 §3 明訂「內部錯誤一律轉為 CC-PLT-5xxx + 通用訊息」,但本表原無通用之未預期錯誤碼,致 GlobalExceptionHandler 之兜底分支無碼可用。依 CSP-003 §2「不得自創錯誤碼,新增碼須先擴充本節」之程序補登。③新增錯誤碼 CC-PLT-5004(500 欄位加解密失敗)——SAD-005 §5 要求卡號等個資欄位加密儲存;金鑰不存在或密文損毀屬須告警之基礎設施失敗,與「未分類之系統錯誤」處置不同,故獨立登錄。新碼均符合既有 pattern ^CC-[A-Z]{2,4}-[0-9]{4}$,無須調整 OpenAPI 之 Error.code.pattern
|
| 1.3 |
補登 401/403 授權類錯誤碼:CC-GW-1002(401 權杖無效或過期)、CC-GW-1003(403 權限不足)。CSP-003 §2 規範「所有可用錯誤碼以本節為唯一登錄表」,但原表僅有 422 之 CC-GW-1001(缺少冪等鍵),無任何 401/403 碼;而 CSP-007 §5.3.4 之 JWT 驗證流程需要 401。依程序補登,並同步 tools/gen_error_codes.py 重新產生 ErrorCodes.java。新增碼符合既有 pattern,無須調整 OpenAPI Error.code.pattern
|
| 1.4 |
補登狀態機與樂觀鎖類錯誤碼(Sprint 3 之 common-saga 需要,依 CSP-003 §2 程序補登):CC-PLT-3001(409 非法狀態轉換)、CC-PLT-3002(409 終態不可轉換)——SDD-005 §14(1.7 起;1.4 起為 §13;原 §12)明訂此二情境之錯誤碼為 CC-<SVC>-3001/3002,但平台層(Saga 狀態機)原無對應登錄;CC-PLT-2002(409 狀態版本衝突/樂觀鎖)——SDD-005 SM-04 要求「以狀態條件更新實作並發轉換」,衝突時需有明確錯誤碼。三碼皆符合既有 pattern ^CC-[A-Z]{2,4}-[0-9]{4}$,無須調整 OpenAPI Error.code.pattern;已同步重新產生 ErrorCodes.java(63 個錯誤碼) |
| 1.5 | ①移除重複登錄之 CC-RW-*(4 碼):§3.2 服務代碼 明訂 rewards-service 之代碼為 RWD(§3.1 亦規範 SVC 即服務代碼),但 §3.4 另存在 CC-RW-1001/2001/4000/4040 一組——同一服務兩個代碼前綴,違反「一服務一服務代碼」。經查此 4 碼係 2026-09-19 為 SCS-010 補登時未察覺 CC-RWD-* 已存在所致,屬登錄重複,故予移除(非功能變更;移除前已確認全庫 Java 無任何引用)。②補登 CC-RWD-4000(400 請求驗證錯誤)、CC-RWD-4040(404 資源不存在):CC-RWD-* 原有 1001/1002/1003/2001/3001/3002,缺類別位 4 之通用驗證碼與查無資源碼,而 rewards-service 之 4 支端點(SCS-010 §6)需要此二碼。③補登 CC-RWD-1005(422 回饋規則代碼重複):POST /api/v1/reward-rules 之 rule_code 於主檔為 UNIQUE,重複設定須有明確拒絕碼,不得以「靜默覆蓋」處理(覆蓋會使已生效之回饋條件無聲改變)。新碼均符合既有 pattern,無須調整 OpenAPI Error.code.pattern;已同步重新產生 ErrorCodes.java(107 個錯誤碼) |
| 1.6 |
擴充類別位 4 語意 + 補登 5 個請求層錯誤碼(CR-004,2026-09-19 BEAM PMO 核定):①**§3.3 類別位 4 由「資源與重複」擴充為「請求與資源」(HTTP 400/404/405/409),並新增 §3.3.1 明訂四個子語意。理由:實測發現 Spring MVC 之請求層例外(缺標頭、壞 JSON、方法不支援、路徑不存在)皆非 DomainException 子類,全數落兜底回 500——既違反契約(應為 400/404/405),且每個客戶端錯誤都觸發 ERROR 告警(告警疲勞)。原類別位 4 僅有 404/409,無處安放 400 與 405。②補登 CC-PLT-4001(400 缺少必要請求標頭或參數)、CC-PLT-4002(400 請求主體格式錯誤)、CC-PLT-4003(400 請求參數驗證失敗)、CC-PLT-4040(404 資源或路徑不存在)、CC-PLT-4050(405 HTTP 方法不支援)**——由 common-spring 之 GlobalExceptionHandler 統一拋出,服務不需自行實作。③實測影響:17/17 服務受影響,自行補 @ExceptionHandler 者 0 個(6 項情境中 4 項錯誤)。新碼均符合既有 pattern ^CC-[A-Z]{2,4}-[0-9]{4}$,無須調整 OpenAPI Error.code.pattern;已同步重新產生 ErrorCodes.java(112 個錯誤碼) |
| 1.7 |
補登 CC-PLT-4010(401 未認證)、CC-PLT-4030(403 無權限)(SCS-022,2026-09-19;W-3 SSO 接線):①缺口: CR-004(本文件 1.6)之 5 碼僅涵蓋 400/404/405,無 401 與 403。當時看似無需——相關例外由 GlobalExceptionHandler 處理;但 Spring Security 之認證攔截發生在進入控制器之前,不在 @ControllerAdvice 之攔截範圍,故無碼可用。②若不補登將被迫二選一:要麼回傳空主體(違反 CSP-003 §3「所有回應一律套用 ApiResponse 封套」),要麼濫用 CC-PLT-4001(400 缺參數)回 401(碼與狀態不一致)。③依 CSP-003 §2「不得自創錯誤碼」程序補登;後兩碼對齊 HTTP 狀態碼(401/403),與既有 4040/4050 慣例一致。④實測:Keycloak 核發之真實權杖可通過、過期/偽造權杖皆回 401 且主體含封套(見 SCS-022 §9.1)。新碼符合既有 pattern,無須調整 OpenAPI Error.code.pattern;已同步重新產生 ErrorCodes.java(114 個錯誤碼) |
| 1.8 |
補登 CC-NOT-1002/CC-NOT-1003/CC-NOT-4040(C-9/CR-017 §9.4,2026-09-22)——notification-service 實作時發現既有 CC-NOT-* 僅 3 碼(1001 樣板不存在、3001/3002 狀態機),缺「無可用通道」(業務規則 422)與「查無通知」(協定層 404)→ 依 CSP-003 §2 程序補登 → 錯誤碼 114 → 122
|
| 1.9 |
補登 CC-AUTH-4040(404 查無授權)(CR-021,2026-09-22)——authorization-service 之授權明細/檢核明細端點需要;併修訂 check-results 契約之陣列性(原誤宣告單一物件 → AuthCheckResult[])→ 錯誤碼 122 → 123
|
| 1.10 |
補登 CC-PLT-3003(409 資源尚未就緒)(GOV-007 §4,2026-10-01)——供報表下載之「作業未就緒(QUEUED/RUNNING/FAILED)」情境;原暫用 CC-PLT-3001(非法狀態轉換)語意不符,故另立。與「狀態機非法轉換」語意區隔 → 錯誤碼 123 → 124;已重新產生 ErrorCodes.java(--force) |