恒生 Hang Seng Pull 模式
本頁說明
講什麼:恒生的入金流水匹配、eDDA/eDDI 入金代扣、企業網銀出金的完整業務規則,以及與匯豐的關鍵差異 適合誰:需要深入了解恒生對接細節的產品經理 前置閱讀:銀行能力矩陣、匯豐 HSBC(推薦先看匯豐,恒生以差異對比為主) 預計閱讀:4 分鐘 負責人:入金產品經理
核心要點:恒生與匯豐共用 FPS 體系但實現差異大——恒生用 HASE Protocol(非標 JSON),匯豐用標準 FPS XML。建議先看匯豐頁面,再看恒生差異。
能力總覽
| 能力 | 支持情況 | 協議/通道 |
|---|---|---|
| 入金流水匹配 | ✅ | 多類型流水(ATM/GT/WY/ZP/BP) |
| eDDA 授權簽約 | ✅ | eDDA API(內部 SRPC,銀行側 HTTPS REST) |
| eDDI 入金代扣 | ✅ | eDDI API(內部 SRPC,銀行側 HTTPS REST) |
| 出金 | ✅ | 企業網銀(method=hase) |
| FPS | ❌ | — |
| 銀證 BST | ❌ | — |
恒生是 moomoo 首個接入 eDDI 的銀行——項目設計文檔明確提到"首家券商接入恒生 eDDI 服務"。
與匯豐的關鍵差異
恒生和匯豐都支持 eDDA/eDDI,但實現細節有顯著差異:
| 維度 | 匯豐 HSBC | 恒生 Hang Seng |
|---|---|---|
| eDDA 模型 | Mandate 授權 | 簡化模型(直接 payer/payee) |
| 授權需要 OTP | 是(短信驗證碼) | 否(數字證書代替) |
| 認證方式 | Token + 基礎認證 | P12 數字證書 + SHA256-RSA 簽名 |
| 並發控制 | 令牌桶(樂觀鎖,4/秒) | 串行化(悲觀鎖,一次一個) |
| 銀行返回狀態 | ACSC/RJCT/ACCP/ACSP | S(成功)/F(失敗)/P(處理中) |
| 交易時段感知 | 無 | 設計有但當前已禁用 |
| 灰度部署 | — | 支持(雙 API + 雙憑證) |
| 證書來源 | — | 香港郵政 e-Cert (USB 交付) |
入金:流水類型與匹配規則
恒生入金流水分為 5 種類型,每種類型有不同的匹配策略:
| 流水類型 | 含義 | 匹配條件 | 可自動入賬 |
|---|---|---|---|
| WY | 網銀轉賬 | 金額相似 + 姓名精確,日期窗口 -3~+2 天 | ✅ 唯一可自動 |
| ATM | ATM 存款 | 金額精確,按批次時間 | ❌ |
| GT | 櫃台存款 | 金額精確,按批次時間 | ❌ |
| ZP | 支票存入 | 金額精確 + 姓名可選,日期窗口 -3~+2 天 | ❌ |
| BP | 賬單支付 | 金額精確 + 賬單賬號 | ❌ |
| 默認 | 其他 | 金額相似,日期窗口 -3~+2 天 | ❌ |
WY 類型自動入賬條件
只有 WY(網銀轉賬)類型可以自動入賬,需同時滿足 4 個條件:
- 入金通知類型為普通入金(
NOTICE_TYPE_NORMAL) - 幣種完全匹配
- 金額精確相等
- 英文姓名精確匹配
不滿足任一條件,WY 流水回退到"相似匹配",需要人工審核。
ATM/GT 批次時間匹配
ATM 和 GT 類型的日期匹配不是看流水日期,而是看批次導入時間(atm_date)——同一秒導入的記錄視為同一批次。這是因為 ATM/櫃台存款的銀行流水到達時間與實際存款時間可能有較大偏差。
金額容差
| 幣種 | 容差 | 適用類型 |
|---|---|---|
| HKD | CRM - 20 ≤ 流水 ≤ CRM | WY/默認(相似匹配時) |
| USD | CRM - 3 ≤ 流水 ≤ CRM | WY/默認(相似匹配時) |
ATM、GT、ZP、BP 類型要求金額精確相等,不適用容差。
eDDA/eDDI 入金代扣
eDDA 授權:無需 OTP,數字證書代替
與匯豐不同,恒生的 eDDA 不需要用戶輸入 OTP 短信驗證碼,而是通過數字證書簽名來認證請求的合法性。這意味著:
- 用戶授權流程更短(無需等待和輸入短信碼)
- 但安全性依賴證書管理——證書過期或丟失會影響整個 eDDI 服務
數字證書詳情
- 來源:香港郵政 e-Cert(ecert.gov.hk)
- 格式:P12 (PKCS#12),USB 交付
- 簽名算法:SHA256 + RSA-PKCS1_v1_5
- 簽名步驟:
- 對請求內容做 SHA256 哈希
- 將哈希值轉為大寫十六進制字符串
- 對十六進制字符串再做 SHA256 哈希
- 用 RSA 私鑰簽名
- Base64 編碼結果放入
msgSignature請求頭
eDDI 請求參數
恒生 eDDI 比匯豐更簡化,不需要 mandate_identification:
| 參數 | 說明 | 示例 |
|---|---|---|
| payer_bank | 付款銀行代碼 | "024"(恒生) |
| payer_id | 付款人賬號 | "123456789001" |
| payer_name | 付款人名稱 | 用戶姓名 |
| payee_bank | 收款銀行代碼 | "024"(恒生) |
| payee_account_number | 收款賬號 | 往來賬戶(3-6-3)或儲蓄賬戶(3-1-6) |
| transaction_amount | 金額(11位,含2位小數) | "00000012345" = 123.45 |
| currency | 幣種 | "HKD" |
| payment_purpose_code | 用途代碼 | 01=費用, 07=電商, 08=其他 |
eDDI 狀態機
功能已禁用
new_blank 交易時段感知功能在代碼中已被註釋掉(handles.py L37-42),當前所有請求無論時段均直接進入 wait_process 狀態。以下描述為原始設計,非當前運行狀態。
交易時段感知(原始設計):如果在非港股交易時段(如晚上)創建 eDDI 指令,不會立即發送到銀行,而是進入 new_blank 狀態等待交易時段開始後再處理。這是恒生獨有的特性,匯豐沒有此限制。
銀行返回狀態碼
| 狀態碼 | 含義 | 系統動作 |
|---|---|---|
| S | 扣款成功 | → end_ok,創建入金流水 |
| F | 扣款失敗 | → end_reject,記錄失敗原因 |
| P | 處理中 | → pending,繼續查詢 |
渠道接口概覽
| 維度 | 說明 |
|---|---|
| 協議類型 | HTTPS REST + P12 數字證書簽名 |
| 認證方式 | 香港郵政 e-Cert (P12 PKCS#12 + SHA256-RSA 簽名) |
| 銀行代碼 | "024" |
| 並發模型 | 串行化(悲觀鎖,一次一個請求) |
| 交易時段感知 | 設計有但當前已禁用(代碼已註釋) |
eDDI 請求字段完整表
EDDIProcedure(恒生 eDDI 格式)完整字段如下:
| 字段 | 類型 | 描述 | 取值範圍/格式 |
|---|---|---|---|
payer_bank | string | 付款銀行代碼 | "024"=恒生,或其他銀行代碼 |
payer_id | string | 付款人 ID | AccountNumber(如 "123456789001")或 CustomerID(如 "012N18020900000202") |
payer_name | string | 付款人姓名 | 如 "Logistic Company A" |
debtor_reference | string | 付款方參考 | 如 "Shipping Fee" |
transaction_amount | string(11) | 金額(11 位補零,最後 2 位為分) | "00000012345" = 123.45 HKD |
payee_bank | string | 收款銀行代碼 | "024" |
payee_account_number | string | 收款賬號 | 支票賬戶: 3-6-3 格式 "333666666333",儲蓄賬戶: 3-1-6 格式 "3331666666" |
payer_type | string | 付款人類型 | "01"=AccountNumber, "02"=CustomerID |
currency | string | 幣種 | "HKD" |
payment_purpose_code | string | 付款用途代碼 | "01"=收費, "02"=費用, "03"=捐贈, "04"=租金, "05"=保險, "06"=公用事業, "07"=電商, "08"=其他 |
client_transaction_id | string | 客戶交易 ID | 系統自動生成,如 "ABC000123456789" |
響應字段:
| 字段 | 描述 |
|---|---|
transaction_id | 恒生交易 ID |
acknowledgement_id | 恒生確認 ID |
error_code | "000"=成功,其他需查開發文檔 |
error_reason | 錯誤描述 |
reject_code | 拒絕碼 |
金額格式說明
恒生 eDDI 使用 11 位補零字符串,最後 2 位是分:
| 實際金額 | 傳輸格式 | 說明 |
|---|---|---|
| 123.45 HKD | "00000012345" | 123.45 × 100 = 12345,補零到 11 位 |
| 10,000.00 HKD | "00001000000" | 10000.00 × 100 = 1000000,補零到 11 位 |
| 0.50 HKD | "00000000050" | 0.50 × 100 = 50,補零到 11 位 |
轉換規則:將 decimal 金額 × 100,轉為整數後左側補零到 11 位字符串。
賬號格式說明
恒生銀行賬號有兩種格式,取決於賬戶類型:
| 賬戶類型 | 格式 | 結構 | 示例 |
|---|---|---|---|
| 支票賬戶 (Current) | 3-6-3 | BBB-AAAAAA-CCC | "333666666333" |
| 儲蓄賬戶 (Saving) | 3-1-6 | BBB-T-AAAAAA | "3331666666" |
系統需根據傳入的賬號長度自動判斷賬戶類型。
銀行錯誤碼(完整表)
| 錯誤碼 | 含義 | 類型 | 處理方式 |
|---|---|---|---|
000 | 成功 | — | 正常處理 |
BRC_8I1 | 餘額不足 | 業務 | 通知用戶充值 |
BRC_8RW | 授權問題 | 業務 | 重新授權 |
BRC_8RZ | 賬戶異常 | 業務 | 聯繫銀行 |
FP2414 | 電子授權未找到 | 授權 | 重新簽約 |
FP2415 | eDDA 未啟用 | 授權 | 等待啟用 |
FP2416 | eDDA 已過期 | 授權 | 重新簽約 |
FP2417 | 超過 eDDA 限額 | 授權 | 提升限額 |
CAC_018 | 銀行服務不可用 | 系統 | 自動重試 |
CAC_021 | 銀行服務不可用 | 系統 | 自動重試 |
CAC_022 | 重複交易 | 冪等 | 轉 pending 跟蹤 |
CAC_998 | 銀行超時 | 系統 | 自動重試 |
CAC_999 | 銀行超時 | 系統 | 自動重試 |
BRC_8RK | 銀行服務不可用 | 系統 | 自動重試 |
BRC_8RM | 銀行服務不可用 | 系統 | 自動重試 |
其他 CAC_* | 其他拒絕 | 業務 | 立即拒絕 |
錯誤碼分類
- BRC 開頭:銀行業務返回碼(Business Return Code),分為可重試(
8RK/8RM)和不可重試(8I1/8RW/8RZ) - FP 開頭:eDDA 授權相關錯誤,通常需要用戶重新簽約或等待啟用
- CAC 開頭:通道通信錯誤(Channel Access Code),大部分可自動重試,
CAC_022為重複交易需特殊處理
moomoo 匹配規則 (HangSengMatch) — 按流水類型分支
系統根據恒生流水的類型碼進入不同的匹配分支,每種類型的匹配嚴格程度不同:
| 流水類型 | 代碼 | 匹配規則(金額 / 姓名 / 日期) | 可自動入賬 |
|---|---|---|---|
| 網銀 (WY) | WY | 金額精確+容差,姓名精確匹配,標準窗口 (-3~+2 天) | ✅ 唯一可自動 |
| ATM | ATM | 金額精確,批次時間 (atm_date) | ❌ |
| 櫃台 (GT) | GT | 金額精確,批次時間 (atm_date) | ❌ |
| 支票 (ZP) | ZP | 金額精確,日期窗口 (-3~+2 天) | ❌ |
| 賬單 (BP) | BP | 金額精確,賬單賬號校驗 | ❌ |
WY 自動入賬 4 條件(全部滿足才能自動入賬):
notice_type=NOTICE_TYPE_NORMAL(普通入金通知)- 幣種完全匹配
- 金額精確相等(比輔助匹配更嚴格,不走容差邏輯)
- 英文姓名精確匹配
ATM/GT "批次時間匹配"說明:不看流水日期字段,而是看 atm_date——同一秒導入的記錄視為同一批次。這是因為 ATM/櫃台存款的銀行流水到達時間與實際存款時間可能有較大偏差,使用導入批次時間更可靠。
串行化控制
恒生要求一次只能處理一個 eDDI 請求,不能並發。系統使用 InnoDB 行鎖實現串行化:
| 參數 | 說明 |
|---|---|
| 鎖類型 | InnoDB 行級悲觀鎖(UserLock 表) |
| 鎖粒度 | 一次一個請求 |
| 鎖標識 | selnum=886(默認) |
| 灰度鎖 | selnum_gray / selnum_formal 分別鎖 |
為什麼恒生需要串行化而匯豐不需要? 恒生銀行的 eDDI 接口不支持並發請求——如果兩個扣款同時到達銀行,銀行會返回錯誤。所以系統必須排隊一個一個發。匯豐則支持一定並發度(令牌桶控制速率)。
灰度部署
恒生 eDDI 支持灰度發佈,可以讓部分用戶走新版本接口:
| 維度 | 灰度環境 | 正式環境 |
|---|---|---|
| API 端點 | ddi_url_gray | ddi_url |
| 憑證 | gray_usrname / gray_psw | username / password |
| 證書 | gray_profile_id | profile_id |
| 串行鎖 | selnum_gray | selnum_formal |
| 路由規則 | 灰度用戶列表 + user_id 取模 | 默認 |
出金:企業網銀
恒生出金通過企業網銀批量匯款,方法碼為 hase。
| 維度 | 說明 |
|---|---|
| 方法碼 | TRANSFER_METHOD_HASE = 'hase' |
| 分類 | 電子銀行方法(allEBankMethod) |
| 中文名 | 恒生網銀出金 |
| 自動化程度 | 半自動(需企業網銀操作) |
與匯豐一樣,恒生出金走企業網銀通道,不是 eDDI(eDDA/eDDI 僅用於入金代扣)。
需求變更指引
| 變更需求 | 改動位置 | 說明 |
|---|---|---|
| 修改 WY 自動入賬條件 | HangSengMatch.php → WY 分支 | 調整姓名/金額/幣種匹配條件 |
| 新增流水類型 | HangSengMatch.php → switch 分支 | 添加新 type 的匹配規則 |
| 修改金額容差 | MatchRule.php → amountSimilar() | 調整 HKD -20 / USD -3 閾值 |
| eDDI 新增用途代碼 | sba_hase_eddi 請求參數 | 添加 payment_purpose_code |
| 修改串行化策略 | sba_hase_eddi → table_model.py | 調整 UserLock 邏輯 |
| 更換數字證書 | sba_hase_eddi/conf/ | 替換 P12 文件和密碼 |
| 修改灰度比例 | sba_hase_eddi → gray_control.py | 調整用戶列表/取模規則 |
| 新增可重試錯誤碼 | sba_hase_eddi → handles.py | 修改 hase_bank_service_not_available_map |
| 修改出金審批 | Task.php → $stepTemplates | 調整恒生出金審批模板 |
| 證書續期 | 香港郵政 e-Cert 網站 | 申請新證書,替換 P12 文件 |
常見客訴 Top 3
| # | 用戶反饋 | 原因 | 客服話術 |
|---|---|---|---|
| 1 | "eDDA 扣款失敗" | BRC_8I1 餘額不足 | "請確認恒生銀行賬戶餘額充足後重試" |
| 2 | "eDDA 突然不能用了" | 連續餘額不足→銀行自動取消授權 | "您的 eDDA 授權可能已被銀行取消,請重新簽署 eDDA 授權" |
| 3 | "扣的金額和申請的不一樣" | 銀行手續費扣減 | "跨行轉賬可能產生手續費,實際扣款以銀行收費為準" |
監控與告警
| 告警項 | 觸發條件 | 嚴重度 | 處理步驟 |
|---|---|---|---|
| eDDI 並發鎖衝突 | InnoDB 行鎖等待超時 | 🟡 中 | 檢查 sba_hase_eddi 並發配置,降低同時扣款數 |
| BRC_8I1 連續告警 | 同一用戶多次餘額不足 | 🟡 中 | 提前提醒用戶充值,連續多次可能導致授權自動取消 |
| Web 通知接收異常 | 恒生 Web 通知回調失敗 | 🔴 高 | 檢查回調接口可用性,查看 HTTP 狀態碼 |
| 銀行服務不可用 | CAC_018/021/998/999 | 🟡 中 | 系統自動延遲重試,持續異常聯繫恒生技術支持 |
讀完之後
| 我想... | 去看 |
|---|---|
| 對比匯豐的 eDDA/eDDI 差異 | 匯豐 HSBC |
| 了解匹配引擎的完整邏輯 | 匹配與自動入賬 |
| 查 eDDA/eDDI 錯誤碼 | 統一錯誤碼中心 |
| 看恒生在各銀行中的位置 | 銀行能力矩陣 |
| 了解 eDDA/eDDI 在架構中的角色 | 系統架構與數據流 |