深色模式
通道执行手册
本页说明
讲什么:每种出金通道从 Remittance 之后到银行确认的完整技术执行过程 适合谁:需要理解"钱是怎么从系统到银行"的产品经理,以及评估通道改造影响的人 前置阅读:出金生命周期预计阅读:5 分钟 负责人:出金产品经理
核心要点:所有出金通道最终通过 SBA 编排执行。BST 通道全自动(秒~分钟级到账),非 BST 通道需运营在银行网银手动操作后确认完成。
SBA 编排:资金的中枢调度
所有出金通道最终都通过 SBA(Server Bank Account) 执行。SBA 是内部资金编排系统——负责冻结、扣款、转账指令的编排和状态追踪。
SBA Procedure 状态机
每笔出金在 SBA 中对应一个 Procedure(编排流程):
SBA 子状态(ext_status)
主状态只有 5 个,但子状态才是精确追踪进度的关键。以下列出最常用的子状态,完整列表见 出金数据字典 § SBA 子状态。
| 主状态 | 子状态 | PM 视角 |
|---|---|---|
| new | waiting | 用户此时还可以在 App 撤回出金 |
| new | manual_confirm | 需要运营在 CRM 确认 |
| pending | deduct_done | 钱已扣,等待发起转账 |
| pending | transfer_auto | BST:系统正在向银行发指令 |
| pending | transfer_manual | 非 BST:等运营在银行网银操作 |
| pending | transfer_done | 银行确认收到指令 |
| pending | transfer_reject | 银行拒绝了出金指令 |
| end_ok | transfer_done | 出金完成 |
用户可取消的窗口
只有 ext_status = waiting 时用户才能在 App 撤回出金。一旦进入 deduct_done,资金已扣除,用户无法自行取消——只能由运营发起冲正。
如果需求变更:扩展 SBA 子状态
代码位置:withdraw/src/app/Business/SOA/Srpc/WithdrawRPC.php 中定义了所有 EXT_STATUS_* 常量
子状态是 SBA(Python 服务)和出金服务(PHP)之间的协议。修改子状态需要两端同步。
BST 银证通道
BST 是唯一走全自动路径的通道。
银证授权前提
用户必须先完成银证授权(Mandate) 才能使用 BST:
| 银行 | 授权模型 | 特点 |
|---|---|---|
| 招行/民生 | 传统银证签约 | 只有"已签约/未签约"两个状态 |
| 天星 | Mandate 授权 | 6 个状态;一次授权绑定 3 个市场(HK/US/HKCC) |
授权状态必须为 OPEN 才能出入金。Confirm 步骤会校验。
执行方式
| 银行 | 服务 | 协议 | 结果获取 |
|---|---|---|---|
| 招行 | cmb_stock_trans | Socket 二进制 | 实时推送 |
| 民生 | ms_stock_bank_transaction | Socket 二进制 | 实时推送 |
| 天星 | Airstar BST API | REST + JSON | 轮询 |
BST 出金遵循冻结-转账-释放三步:
- SBA 冻结用户可用余额中等额资金
- 向银行发送出金指令
- 收到结果后,成功则扣款释放冻结,失败则回滚释放
结果获取方式
招行/民生和天星获取银行结果的方式完全不同:
招行/民生:通过 Socket 双向链路实时推送结果,通常秒级完成。超时(回调码 -5)时自动切换备用 exit_server 重试。
天星:通过 REST API 分两阶段轮询获取结果:
| 阶段 | 事件 | 策略 | 累计耗时 |
|---|---|---|---|
| 快速轮询 | AsbBstTransfer | 每 5 秒查询,最多 10 次 | ~50 秒 |
| 兜底同步 | SyncAsbBstWithdraw | 2 小时内持续同步未完成指令 | 最长 2 小时 |
| 超时 | — | 标记异常,需运营人工查询天星 API | — |
exit_server 是什么
exit_server 是招行/民生 BST 的 Socket 通信地址。每家银行配置了主备两个地址,主地址超时时系统自动切换到备用地址重试。
余额扣减
Remittance 执行时首先调用 autoOut() 扣减 auto_settings 表中的出金余额:
- 低于
alarm_amount→ 发飞书告警 - 低于
stop_amount→ 自动关闭该币种自动出金,后续全部降级人工
扣减发生在银行转账之前——即使银行拒绝,余额已扣。需运营手动恢复。
BST 异常处理
| 异常 | 触发 | 系统行为 | 运营操作 |
|---|---|---|---|
| 招行/民生超时(-5) | Socket 超时 | 切换备用 exit_server 重试 | 通常不需干预 |
| 招行/民生拒绝(-6) | 银行拒绝 | 标记失败 | 联系银行确认原因 |
| 天星轮询超时 | 10 次轮询后仍 PENDING | 进入 2 小时兜底同步 | 等待,关注告警 |
| 天星同步超时 | 2 小时后仍 PENDING | 标记异常 | 人工查询天星 API |
| 天星指令失败 | 返回 FAILED | 释放冻结资金 | 查看 140630xxx 错误码 |
| 授权异常 | Mandate 非 OPEN | Confirm 步骤阻止推进 | 引导用户重新授权 |
网银通道(汇丰 / 恒生)
运营通过银行企业网银转账,CRM 确认完成。
eDDI ≠ 出金
eDDA/eDDI 是入金协议(代扣入金)。汇丰/恒生的出金走企业网银,不走 eDDI。
流程:
- Confirm 步骤调用
startTransfer()→ SBA 返回transfer_manual - 运营在 CRM 看到"待汇款"任务
- 运营登录银行企业网银操作转账
- 转账成功后在 CRM Remittance 步骤点击确认
- Task → DONE
FPS 通道
中银 / 渣打 FPS
标准半自动流程,与网银通道类似——运营触发后 CRM 确认。
FPS 限额:HKD / CNH 金额 < 100 万才走 FPS。≥ 100 万升级为 CHATS/RTGS。
收款人名要求:FPS 通道必须使用全名(与 CHATS/TT 可用简称不同)。
广发 FPS(批量提交 + 异步轮询)
广发 FPS 是半自动通道中最接近全自动的——有 API 接口,但需运营触发:
轮询间隔(递增):
| 次数范围 | 间隔 |
|---|---|
| 1~100 | 10 秒 |
| 101~200 | 20 秒 |
| 201~300 | 30 秒 |
| 301~1000 | 40 秒 |
最长轮询可达数小时。数据存储在 task_cgb_fps 表。
如果需求变更:接入新的 FPS 通道
- 创建 Go 服务对接银行 FPS API
- 在 PHP 新建
XxxFpsBiz.php(参考CgbFpsBiz.php) - 新建轮询事件
SyncXxxFpsResult - 在
Method.php添加通道常量 - 新建
task_xxx_fps表存储 API 状态
传统通道
CHATS/RTGS(文件导出)
通过香港银行间清算系统出金,运营导出文件后上传银行系统。
触发条件:
- 虚拟银行 + USD 出金
- 虚拟银行 + HKD/CNH 金额 ≥ 100 万
- 非 BST 银行 + FPS 不可用时的 fall-through
费用:USD $8 / HKD $25 / CNY $25(用户确认出金时弹窗提示,需用户明确同意)。
流程:
- 运营在 CRM 选择待汇款任务
- 系统生成 RTGS/CHATS 格式文件
- 运营上传文件到银行系统(线下操作)
- 银行处理完成后 CRM 确认
文件注意事项:
- 收款人名:可用简称
FUTU SECURITIES INTL (HK) LTD(不需要全名) - 收款人姓名限制 140 字符(超过截断并警告)
- 金额单位为分(乘以 100)
- 姓名从
nick_list表获取,优先英文名
如果需求变更:调整文件格式
代码位置:withdraw/src/app/Business/Withdraw/ExportRTGSCHATSFile.php
文件格式是银行要求的固定格式,修改需和银行确认。
跨境电汇(tele_transfer / ewb)
纯人工通道。运营在银行网银操作跨境汇款,完成后 CRM 确认。setTransferDone() 时传 method = null(由 SBA 按配置路由)。
NSS 名单筛查:跨境电汇在执行前需通过 NSS(Name Screening Service)检查收款人姓名,确认不在制裁名单上。NSS 不通过的出金需人工升级,运营联系合规团队判断。
收款人名规则:跨境电汇/TT 可用简称 FUTU SECURITIES INTL (HK) LTD。
支票(check)
最传统的通道,到账最慢。
操作流程:
- 运营根据出金任务信息开具支票
- 需 2 位授权人签名(双签制度)
- 每日下午由专人送到银行存入
- 记录支票编号(
check_extra表) - CRM 确认完成
适用场景:当同行转账、FPS、CHATS/RTGS 均不可用时的最终 fall-through 方案。
通道对比总结
| 维度 | BST 银证 | 广发 FPS | 其他 FPS/网银 | 传统通道 |
|---|---|---|---|---|
| startTransfer 时机 | Remittance | Confirm | Confirm | Confirm |
| SBA ext_status | transfer_auto | transfer_manual | transfer_manual | transfer_manual |
| 结果获取 | 推送(招行/民生) / 轮询(天星) | 轮询(1000次) | CRM 手动确认 | CRM 手动确认 |
| 完成触发 | 自动完成 | 轮询自动完成 | 运营确认 | 运营确认 |
| 最大等待 | 秒级(招行/民生) / ~2小时(天星) | 数小时 | 取决于运营 | 取决于运营 |
| 需要运营 | 否(全自动) | 触发提交 | 银行操作+确认 | 银行操作+确认 |
如果需求变更:让某个手工通道变为半自动
核心改造:把 CRM 手动确认替换为 API 轮询。参考广发 FPS 的模式:
- 对接银行 API(提交转账 + 查询状态)
- 新建轮询队列事件
- Remittance 步骤中针对该通道走异步路径
startTransfer()时机和 SBA ext_status 不需要改
通道方法分组
代码中的方法分组决定了 CRM 的展示和操作方式:
| 分组 | 包含的通道 | 用途 |
|---|---|---|
| allEBankMethod() | manual, boc, hase, hsbc, boc_fps, cgb_fps_api, chats_rtgs, sc | 网银类(8 种) |
| allTeleMethod() | tele_transfer, ewb | 跨境电汇类(2 种) |
| allSetMethod() | 网银类 + check(9 种) | Confirm 步骤可选通道 |
| allTabMethod() | allSetMethod + ewb + tele_transfer(11 种) | CRM Tab 页 |
| all() | 全部 12 种 | 所有出金通道 |
为什么 auto_bs 不在 allSetMethod 里
BST 是系统自动路由的,不需要运营手动选择。只有 method = null 时运营才需要从 allSetMethod 中选通道。
常见误解
| 误解 | 事实 |
|---|---|
| "SBA 是一家银行" | 不是。SBA(Server Bank Account)是内部资金编排系统,负责冻结、扣款、转账指令的编排和状态追踪。所有出金通道最终都通过 SBA 执行 |
| "所有 BST 银行用同样的技术" | 不是。招行/民生走 Socket 双向链路(SM2 加密,实时推送结果),天星走 REST API(JSON,轮询获取结果)。超时处理、轮询策略完全不同 |
| "汇丰/恒生出金走 eDDA" | 不走。eDDA/eDDI 仅用于入金代扣。汇丰/恒生的出金走企业网银转账,与 eDDA 无关 |
| "FPS 出金是全自动的" | 不是。广发 FPS 有 API 接口但仍需运营触发提交,中银/渣打 FPS 完全靠运营手动操作。只有 BST 是全自动 |
读完之后
| 我想... | 去看 |
|---|---|
| 了解审批到执行的完整流程 | 出金生命周期 |
| 看自动出金条件和限额规则 | 出金规则手册 |
| 排查某通道的异常 | 出金排障 |
| 推动通道相关的需求变更 | 出金变更指南 |
| 查 SBA 状态码、回调码 | 出金数据字典 |
| 深入了解三家 BST 银行 | 内银系 BST 总览 |
这个页面有帮助吗?