新人導讀:一筆錢的旅程
本頁說明
講什麼:以 FPS 入金、eDDA 入金、銀證出金三個核心場景,端到端走一遍完整鏈路,串聯全書知識點 適合誰:剛接手出入金業務、需要快速建立全局認知的產品經理 前置閱讀:無,這是起點 預計閱讀:8 分鐘 負責人:出入金產品團隊
核心要點:三個核心場景串聯全書知識點——FPS 入金走 Push 匹配鏈路,eDDA 入金走 Pull 代扣鏈路,BST 出金走自動審批+銀證轉賬鏈路。理解這三條鏈路就掌握了系統全貌。
30 秒全景
用戶發起申請 → 銀行流水/指令對接 → 自動匹配/審批 → 資金到賬/匯出 → 對賬
出入金系統是 moomoo 證券交易平台的資金通道層——連接用戶銀行帳戶與證券帳戶的橋樑。
| 維度 | 數量 | 說明 |
|---|---|---|
| 入金方式 | 10 種 | BST、FPS、eDDA(恒生/匯豐)、網銀、ATM、支票、繳費、海外、天星 |
| 出金通道 | 12 種 | 銀證、中銀銀企/FPS、恒生/匯豐網銀、渣打/廣發 FPS、CHATS、電匯、支票 |
| 支持銀行 | 16 家 | 中銀、匯豐、恒生、工銀、渣打、廣發、招行、民生、天星、EWB 等 |
| 貨幣 | 5 種 | HKD、USD、CNH、JPY、SGD |
| 邊界 | 系統負責 | 系統不負責 |
|---|---|---|
| 入口 | 接收用戶入金/出金請求 | 用戶註冊、KYC、開戶 |
| 銀行對接 | 流水解析、匯款指令、eDDA/eDDI | 銀行內部處理、SWIFT 中轉 |
| 資金操作 | 通過 SBA 創建 procedure 操作賬本 | 賬本餘額管理本身(SBA 負責) |
| 風控 | 調用黑白名單、高風險檢查 | 風控規則引擎本身 |
| 通知 | 觸發入金/出金結果通知 | 推送通道管理 |
入金和出金是兩套完全獨立的服務——入金核心是「匹配」(錢到了,找到是誰的),出金核心是「審批」(錢要走,能不能放行)。
按角色選擇閱讀路徑
不同角色關注的重點不同。先看完下面三個場景建立全局認知,然後按你的角色深入:
下面用三個真實場景,帶你走完入金和出金的每一步。FPS 和 eDDA 是兩種截然不同的入金模式——前者是用戶主動轉賬(Push),後者是系統從用戶銀行帳戶扣款(Pull)。理解這個區別是理解整個入金體系的關鍵。
入金之旅:一筆 HKD 50,000 的 FPS 入金
場景:用戶在 moomoo App 選擇「FPS 轉數快」,向證券帳戶轉入 HKD 50,000。
Step 1: 用戶發起申請
用戶在 App 點擊「入金」,選擇入金方式 fps(方式代碼 3),填入金額 50,000,選擇幣種 HKD。
系統調用入金服務的 ApplyController.applyAction(),經過以下檢查:
| 檢查項 | 說明 |
|---|---|
| 用戶權限 | 驗證 WEB/OA 簽名,確認用戶身份 |
| 入金限制 | 調用 AccountLockBusiness::isDepositRestricted() 檢查帳戶是否被限制入金 |
檢查通過後,創建一條 Apply 記錄,寫入 applys_{uid % 100} 分表:
| 字段 | 值 | 說明 |
|---|---|---|
status | 0(PENDING) | 用戶已申請,等待匹配 |
deposit_method | fps | FPS 轉數快 |
amount | 50000 | 申請金額 |
currency | HKD | 港幣 |
export_bank_id | 用戶選擇的銀行 | 付款銀行(用戶側) |
import_bank_id | 系統分配 | 收款銀行(公司側) |
深入了解
全部 10 種入金方式 → 入金方式總覽 Apply 表每個字段的含義 → 入金規則速查 § 核心字段
Step 2: 用戶完成 FPS 轉賬
用戶切換到銀行 App,通過 FPS(轉數快)完成 HKD 50,000 的轉賬。
FPS 是即時到賬的支付系統,錢立刻從用戶銀行扣出,進入公司在渣打(SCB)或廣發(CGB)的子帳戶。
Step 3: 銀行流水到達系統
渣打/廣發的 FPS 閘道將交易流水推送到系統。流水寫入 flows_{bankType}_{YYYYMM} 分表,關鍵資訊:
| 字段 | 值 | 說明 |
|---|---|---|
| 流水類型 | FPS 轉賬 | FPS transfer 標識 |
| 備註 | FPS... | FPS 交易參考號 |
| 金額 | 50000.00 | 實際到賬金額 |
| 幣種 | HKD | 港幣 |
不同銀行的流水採集方式差異很大:
| 銀行 | 方式 | 時效 |
|---|---|---|
| 中銀 | B2E 主動拉取 | 每日 3 次 + 2 小時轉換 |
| 匯豐 | MT910 實時推送 | 秒級 |
| 渣打/廣發 | FPS API | 實時 |
| 招行/民生 | 銀證雙向鏈路 | 事件驅動,實時 |
| EWB | CSV + BAI2 文件 | 按需手工上傳 |
Step 4: 自動匹配引擎啟動
匹配引擎是一組定時任務,每家銀行一個,每 3 分鐘運行一次(如 php artisan match:boc)。
引擎拿到所有 status = PROCESSING 的未處理流水和 status = PENDING 的待匹配申請,執行五維比對:
| 維度 | 匹配字段 | 匹配邏輯 |
|---|---|---|
| 1. 幣種 | currency | 必須完全一致 |
| 2. 金額 | amount | 容差範圍內匹配(HKD/CNH/JPY: 0~20 差額;USD/SGD: 0~3 差額) |
| 3. 姓名 | en_name / cn_name | 精確匹配或模糊匹配 |
| 4. 日期 | date | 申請日期 ±15 天內 |
| 5. 卡號 | bank_card_number | 銀行賬號匹配 |
匹配結果有三種:
| 結果碼 | 含義 | 後續 |
|---|---|---|
| 2 (RESULT_DEPOSIT) | 完全匹配,可自動入賬 | → 進入自動入賬判定 |
| 1 (RESULT_NORMAL) | 部分匹配,需人工確認 | → 創建匹配記錄,等運營審核 |
| 0 (RESULT_NOT) | 不匹配 | → 不操作 |
我們這筆 FPS 入金:幣種 HKD 一致、金額 50,000 精確匹配、姓名一致、日期當天、卡號匹配 → RESULT_DEPOSIT(完全匹配)。
Step 5: 自動入賬判定
匹配成功不等於自動入賬。系統還需要檢查 5 個自動入賬條件:
| # | 條件 | 本筆情況 |
|---|---|---|
| 1 | 金額未超過幣種限額 | HKD 50,000 < HKD 2,000,000 限額 → 通過 |
| 2 | 流水未被人工拒絕過 | reject == 0 → 通過 |
| 3 | 在允許的自動處理時間窗口內 | 工作時間內 → 通過 |
| 4 | 一條流水只對應一個用戶 | count(uids) == 1 → 通過 |
| 5 | 當日自動入賬次數未超 10 筆 | 首筆 → 通過 |
各幣種自動入賬限額:
| 幣種 | 最大自動入賬金額 |
|---|---|
| HKD | 2,000,000 |
| USD | 300,000 |
| CNH | 2,000,000 |
| JPY | 40,000,000 |
| SGD | 350,000 |
5 個條件全部通過 → 系統判定為 NORMAL(正常模式) 入金類型。
Step 6: SBA 編排執行
系統向 SBA(Server Bank Account)發起入金編排請求,創建一個 Procedure:
入金 Procedure(NORMAL 模式)執行序列:
1. 檢查凍結 →
2. 資金入賬(加到證券帳戶餘額)→
3. 解除凍結 →
4. 完成SBA 是系統的「內部銀行」——所有涉及資金餘額變動的操作都通過 SBA Procedure 完成,確保原子性和一致性。
深入了解
SBA 是什麼 → SBA 概念與數據模型 6 種入金編排模式的觸發條件 → SBA 資金編排
Step 7: 資金到賬,用戶收到通知
Procedure 執行成功,Apply 狀態更新為 2(DONE)。
系統發送推送通知(NOTICE_TYPE_NORMAL),用戶在 App 看到「入金成功」。
從用戶提交到到賬,這筆 FPS 入金的全鏈路時間:FPS 流水實時到達 + 匹配引擎 3 分鐘週期 + SBA 執行秒級 ≈ 3~5 分鐘。
入金之旅 II:一筆 HKD 20,000 的 eDDA 入金
場景:用戶已綁定匯豐銀行卡並完成 eDDA 授權,在 moomoo App 選擇「eDDA 電子直接扣賬」,入金 HKD 20,000。
eDDA 與 FPS 的根本區別
FPS 是用戶主動轉賬(Push)——用戶在銀行 App 操作,錢到達後系統需要匹配引擎識別「這筆錢是誰的」。 eDDA 是系統主動扣款(Pull)——用戶在 moomoo App 提交後,系統直接從用戶銀行帳戶扣錢。不需要匹配引擎,因為系統自己發起的扣款,天然知道錢屬於誰。
Step 1: 前提——eDDA 授權
在發起 eDDA 入金之前,用戶必須先完成一次性的 eDDA 授權,允許富途從其銀行帳戶直接扣款。
授權狀態在 setup_eddis 表中跟蹤:
| 狀態碼 | 含義 | 說明 |
|---|---|---|
| 3 | WAITING | 等待發起授權 |
| 1 | PENDING | 授權進行中,等待銀行確認 |
| 2 | EFFECT | 授權成功,可以入金 |
| 0 | FAIL | 授權失敗 |
系統通過 SetupEddaCheckAuthorizationJob 定時檢查授權進度(每 600 秒輪詢一次),最長等待約 10 天(3000 次重試)。
常見授權失敗原因:
| 錯誤碼 | 含義 |
|---|---|
MFISAC01 | 銀行賬號錯誤 |
MPP01005~01008 | 身份證/手機號/姓名不匹配 |
MPP06001 | 銀行帳戶狀態異常 |
深入了解
匯豐 eDDA 與恒生 eDDA 的區別 → 匯豐 HSBC / 恒生 Hang Seng 哪些銀行卡可以用 eDDA → eDDA 支持範圍(匯豐通道 15 家、恒生通道 12 家)
Step 2: 用戶發起 eDDA 入金
用戶在 App 點擊「入金」,選擇入金方式 eddaHSBC(方式代碼 9),輸入金額 20,000 HKD。
系統創建 Apply 記錄後,驗證 eDDA 授權狀態:
驗證邏輯(verifyEddaBankCard):
恒生 eDDA → 必須已預授權(status = EFFECT)
匯豐 eDDA(同行)→ 必須已預授權
匯豐 eDDA(跨行 + 線上開戶)→ 允許後置授權我們的場景:匯豐同行,授權已生效 → 驗證通過。
Apply 記錄:
| 字段 | 值 | 與 FPS 的區別 |
|---|---|---|
status | 0(PENDING) | 同 |
deposit_method | eddaHSBC | FPS 是 fps |
amount | 20000 | — |
currency | HKD | 同 |
Step 3: 異步任務佇列啟動扣款
這是 eDDA 與 FPS 最大的不同——用戶不需要去銀行 App 操作。
Apply 創建後,系統自動觸發任務鏈:
ApplyFollowJob(檢測到 method = eddaHSBC)
→ HsbcEddiCreateJob(發起扣款)
→ HsbcEddiResultJob(輪詢扣款結果)HsbcEddiCreateJob 調用 Eddi::applyHsbcEddi(),構建扣款請求:
| 參數 | 說明 |
|---|---|
debtor_bank_code | 用戶的銀行代碼(匯豐 3 位) |
debtor_account_identification | 用戶銀行賬號 |
debtor_name | 用戶姓名 |
creditor_bank_code | 富途收款銀行代碼 |
creditor_account_identification | 富途收款賬號 |
instructed_amount | 20000(扣款金額) |
instructed_amount_currency | HKD |
系統通過 SBA 服務將扣款指令發往匯豐。同時寫入 hsbc_eddis 表,用 apply_id 的唯一索引防止重複扣款。
深入了解
入金方式對比 → 入金方式總覽
Step 4: 銀行執行扣款
匯豐收到 eDDI(Electronic Direct Debit Instruction)指令後:
- 驗證用戶帳戶餘額
- 從用戶帳戶扣除 HKD 20,000
- 將資金轉入富途在匯豐的帳戶
- 通過 eDDA Report 回傳執行結果
hsbc_edda_report 服務接收匯豐的執行報告,更新 Procedure 狀態:
| 報告狀態 | 含義 | 系統處理 |
|---|---|---|
| finished | 扣款成功 | 更新 Apply real_amount,標記入賬 |
| rejected | 扣款被拒 | 記錄 reject_reason_code,通知用戶 |
Step 5: SBA 入賬(跳過匹配引擎!)
關鍵區別:eDDA 入金完全跳過匹配引擎。
在匹配服務的代碼中,eDDA 被顯式排除:
HsbcMatch 匹配邏輯:
if (deposit_method == 'eddaHSBC') → return 不匹配(跳過)
HangSengMatch 匹配邏輯:
if (deposit_method == 'edda') → return 不匹配(跳過)因為系統自己發起的扣款,天然知道資金屬於哪個用戶、對應哪筆申請,不需要五維比對。
扣款成功後,SBA 直接創建入金 Procedure 完成入賬。
Step 6: 資金到賬,用戶收到通知
HsbcEddiResultJob 輪詢到扣款成功 → Apply 狀態更新為 2(DONE) → 推送通知。
從用戶提交到到賬時間:扣款指令發送秒級 + 銀行處理通常 幾分鐘到幾小時(取決於銀行處理速度)。
FPS vs eDDA:兩種入金模式對比
| 維度 | FPS(Push) | eDDA(Pull) |
|---|---|---|
| 資金方向 | 用戶主動轉入 | 系統從用戶帳戶扣款 |
| 用戶操作 | 需切換到銀行 App 操作 | 在 moomoo App 內一鍵完成 |
| 前置條件 | 無 | 需先完成 eDDA 授權 |
| 匹配引擎 | 需要五維匹配 | 完全跳過 |
| 到賬確定性 | 依賴匹配結果 | 扣款即確認 |
| 支持銀行 | 渣打、廣發、中銀等 | 匯豐通道 15 家、恒生通道 12 家(完整列表) |
| 用戶體驗 | 步驟多,需跨 App | 體驗最好,一鍵入金 |
| 數據表 | applys + flows + matches | applys + hsbc_eddis/hs_eddis + setup_eddis |
| 異步任務 | 匹配引擎定時掃描(3 分鐘) | Job 佇列主動推進 |
| 失敗處理 | 流水到了但匹配不上 → 人工 | 銀行拒絕扣款 → 通知用戶 |
為什麼 eDDA 是核心流程?
eDDA 實現了「用戶在 App 內一鍵入金」的最佳體驗——不需要切換銀行 App、不需要等待匹配。eDDA 佔入金總量約 78%(匯豐 eDDA 單通道佔 72%,恒生 eDDA 約 6%),是絕對主力。匯豐通道支持 15 家銀行的個人帳戶,恒生通道支持 12 家——並非只有匯豐和恒生自家銀行卡才能用。理解 eDDA 的授權-扣款-入賬鏈路,是理解入金產品設計的關鍵。詳見 eDDA 支持範圍。
出金之旅:一筆 HKD 30,000 的銀證出金
場景:用戶在 moomoo App 發起出金,通過招行銀證通道提取 HKD 30,000 到招行銀行卡。
Step 1: 用戶發起出金請求
用戶在 App 點擊「出金」,選擇招行銀行卡,輸入金額 30,000 HKD。
系統調用出金服務的 WithdrawCreator.apply(),進行一系列前置檢查:
| 檢查項 | 說明 |
|---|---|
| 出金限制 | 檢查用戶是否被限制出金 |
| 黑名單 | 調用 hk-withdraw-blacklist-go 檢查出金黑名單 |
| 風控狀態 | 驗證用戶風控狀態 |
| 幣種-市場一致性 | HKD 屬於 HK 市場,一致 |
| 可用出金方式 | 檢查用戶銀行卡支持的出金方式 |
| 手續費 | 計算出金手續費 |
| 銀行卡資訊 | 驗證銀行卡有效 |
檢查通過後,系統執行原子事務,一次性完成:
- 創建 SBA List 記錄
- 創建 Task 記錄(出金任務)
- 創建 Apply 記錄
- 添加 Flow 操作日誌
深入了解
出金任務每個字段的含義 → 出金數據字典 § 核心字段 全部 12 種出金通道 → 出金方式總覽
Step 2: 通道路由——系統選擇出金方式
系統通過 calcMethod() 自動判斷出金通道:
決策樹:
1. 用戶銀行卡支持 BST?
├─ 是 → 招行(CMBHK)支持銀證 → method = auto_bs
└─ 否 → 繼續判斷
2. 銀行在香港?
├─ 否 → method = tele_transfer(跨境電匯)
└─ 是 → method = null(需人工選擇)招行銀行卡支持 BST(銀證轉賬),所以系統自動設置 method = auto_bs。
Step 3: 確定審批流程模板
出金任務有三步審批,但並非每筆都要走全部三步:
| 步驟 | 名稱 | 何時需要 |
|---|---|---|
| Step 1: Audit | 高危審核 | 僅高風險/OM 帳戶需要 |
| Step 2: Confirm | 確認指示 | 所有出金都需要 |
| Step 3: Remittance | 匯出資金 | 所有出金都需要 |
這筆不是高風險,流程模板為 [Confirm, Remittance],跳過 Audit。
Step 4: Confirm——確認指示
運營人員(或自動系統)在 Confirm 步驟:
- 驗證銀行卡 — 確認招行銀行卡狀態正常
- 確認出金方式 —
auto_bs(銀證轉賬)已自動設置
對於 auto_bs 通道,Confirm 步驟不會調用 SBA startTransfer()(這個動作留給 Remittance)。
操作:點擊「提交到下一步」(NEXT) → 進入 Remittance。
Step 5: Remittance——匯出資金
這是關鍵步驟。對於 auto_bs(銀證)通道,系統檢查自動出金條件:
| # | 條件 | 說明 | 本筆情況 |
|---|---|---|---|
| 1 | 通道是 auto_bs | 只有銀證通道才支持自動出金 | 是 → 通過 |
| 2 | 非遷移數據 | 排除從舊系統遷移的歷史數據 | 新數據 → 通過 |
| 3 | 自動出金開關已開啟 | auto_settings 表中對應幣種 status = OPEN | HKD 已開啟 → 通過 |
| 4 | 金額在限額內 | amount ≤ max_amount | 30,000 ≤ 配置上限 → 通過 |
| 5 | 餘額充足 | amount ≤ available_balance | 餘額足夠 → 通過 |
| 6 | 每日次數未超限 | 當日同用戶出金 < 10 筆 | 首筆 → 通過 |
全部通過 → 系統調用 SBA startTransfer()(異步),創建出金 Procedure。
深入了解
三步審批完整流程 → 出金生命週期 出金通道路由決策樹 → 出金方式總覽 § 通道怎麼選
Step 6: 銀證指令發往招行
SBA 出金編排向招行銀證服務(cmb_stock_trans)發送出金指令:
出金指令處理鏈路:
1. 出金服務 → SBA 出金編排 → 招行銀證服務
2. 招行銀證服務用 SM2 國密算法加密請求
3. 通過 Socket 發送到招行的 exit_server
4. 招行處理後返回結果SM2 加密流程:
- 加載富途私鑰
- 加載招行公鑰證書
- 簽名 + 加密請求數據
- Base64 編碼後發送
Step 7: 銀行回調 & 任務完成
招行處理完成後,通過銀證雙向鏈路回調系統:
| 回調結果 | 處理 |
|---|---|
result = 0(成功) | Task 狀態 → 2(DONE) |
result = -5(逾時) | 自動切換到備用 exit_server 重試 |
result = -6(銀行拒絕) | Task 標記失敗,需人工處理 |
我們這筆正常成功。Task 狀態更新為 DONE,用戶收到「出金成功」推送。
從提交到到賬時間:銀證通道是事件驅動的,通常 幾秒到幾分鐘。
入金 vs 出金:關鍵差異一覽
| 維度 | 入金 | 出金 |
|---|---|---|
| 方向 | 用戶銀行 → 證券帳戶 | 證券帳戶 → 用戶銀行 |
| 方式數 | 10 種 | 12 種 |
| 核心機制 | 自動匹配引擎(五維比對) | 三步審批工作流 |
| 自動化條件 | 5 個自動入賬條件 | 6 個銀證自動出金條件 |
| 每日限制 | 10 筆/用戶(自動入賬) | 10 筆/用戶(銀證自動) |
| 狀態數 | 6 個(PENDING→DONE) | 6 個(PENDING→DONE) |
| SBA 模式 | 6 種入金編排模式 | 統一出金編排 |
| 風控檢查 | 入金黑名單 + 白名單 | 出金黑名單 + 高風險檢測 |
| 量級分佈 | eDDA 佔 ~78%,工銀 ~12%,其他 ~10% | — |
讀完之後,去哪裡?
| 我想... | 去看 |
|---|---|
| 查一個術語是什麼意思 | 術語表 |
| 看 32 個服務的分層架構 | 架構與數據流 |
| 查入金的 10 種方式 | 入金方式總覽 |
| 查出金的 12 種通道 | 出金方式總覽 |
| 看某家銀行的完整規則 | 銀行能力矩陣 |
| 理解風控怎麼攔截 | 入金排障、出金排障 |
| 了解退款和沖正機制 | 退款與沖正 |
| 理解 SBA 編排 | SBA 概念與數據模型 |