深色模式
出金生命周期
本页说明
讲什么:跟着一笔钱走完出金全程——从用户点击"提交"到钱到银行卡,中间每一步发生了什么 适合谁:需要理解出金完整路径的产品经理 前置阅读:出金方式总览预计阅读:5 分钟 负责人:出金产品经理
核心要点:一笔出金从提交到完成经历六大阶段:前置检查→任务创建→风控检测→三步审批→自动/人工执行→结果确认。大部分出金在 Confirm→Remittance 两步内自动完成。
全程概览
一笔出金从头到尾经历这些阶段:
下面按时间顺序,完整叙述每一步。
第一步:用户提交 + 前置检查
用户在 App 点击"提交出金"后,在出金任务创建之前,系统执行一系列前置检查。检查不通过的出金请求不会创建出金任务——用户在 App 端直接看到错误提示。
最大可提金额
系统首先计算用户最大可提金额:
最大可提金额 = min(实时净资产, Min_ELV, 最大授信额)如果出金金额超过最大可提金额,直接拒绝。融资账户和现金账户的计算方式不同,融资账户还需扣除保证金要求。
前置检查清单
| 顺序 | 检查项 | 不通过结果 | 触发时机 |
|---|---|---|---|
| 1 | 出金限制标记(风控) | 直接拒绝("账户暂时无法出金") | 进入资金页面 / 点击确认 |
| 2 | 出金黑名单 | 直接拒绝("账户被限制提取资金") | 进入资金页面 |
| 3 | 不动账户 / 休眠户 | 直接拒绝("休眠户不支持出金") | 进入资金页面 / 点击确认 |
| 4 | 证券账户状态 | 拒绝("未开通证券账户/已销户") | 进入页面 |
| 5 | 穿仓校验 | 拒绝("账户存在欠款") | 点击确认出金 |
| 6 | GDCA 认证 | 拒绝("未完成 GDCA") | 点击确认出金 |
| 7 | NSS 问卷 | 拒绝("未完成 NSS 问卷") | 点击确认出金 |
| 8 | 银行卡有效性 | 提示重新绑卡 | 选择收款账户 |
| 9 | 银行账户认证状态 | 拒绝("银行账户未通过认证") | 选择收款账户 |
| 10 | 在线开户入金门槛 | 拒绝(绑卡须转账 ≥ 10,000 HKD 或 1,500 USD) | 选择收款账户 / 点击确认 |
| 11 | 可用出金方式 | 限制可选通道 | 选择收款账户 |
| 12 | 币种-市场一致性 | 拒绝 | 点击确认 |
| 13 | 费用计算 | 展示给用户确认(CHATS/RTGS 有弹窗) | 点击确认 |
| 14 | 通道路由 | 确定 method 值 | 点击确认 |
更多限制场景(低频但需注意)
| 检查项 | 不通过结果 | 说明 |
|---|---|---|
| 大陆银行卡 | 拒绝("不支持提取到中国大陆银行账户") | 系统直接拦截 |
| A 股通人民币限制 | 拒绝("A 股通 CNH 仅支持提取到香港银行") | 仅 CNH + 非港银行 |
| Payoneer 账户 | 拒绝("Payoneer 账户不支持出金") | 业务限制 |
| 波多黎各地区 | 拒绝 | 地区限制 |
| 融资可提金额 | 拒绝("超出现金可提金额") | 融资账户专属 |
| 银行币种不匹配 | 拒绝("银行账户币种与出金币种不一致") | 如港元账户提美元 |
| 天星银证未授权 | 拒绝("未授权银证") | 仅银行发起出金场景 |
| 机构账户未授权 | 拒绝 | 机构账户专属 |
这些错误码归属 140670xxx 系列,完整列表见 出金数据字典 § 前置校验错误码。
如果需求变更:修改前置检查
- 新增检查项 →
withdraw/src/app/Business/CreatorBase.php的check*()方法链中添加 - 调整黑名单 →
hk-withdraw-blacklist-go服务管理接口(数据库配置,即时生效) - 修改通道路由逻辑 →
withdraw/src/app/Business/CreatorBase.php→calcMethod()方法 - 修改费用计算 →
CreatorBase.php→getFee()方法
第二步:任务创建 + 通道路由
检查全部通过后,系统创建出金任务(写入 tasks 表),同时确定出金通道。
通道路由由 calcMethod() 自动决定:
关键边界:即使银行卡支持 BST,如果是招行/民生 + 离岸人民币 CNH,系统不选 BST,而是留给运营手动选择。因为招行/民生的 BST 对 CNH 有额外限制。天星不受此影响。
当 method = null 时,运营在 Confirm 步骤从 9 种可选通道中手动选择。
任务创建后状态为 PENDING(0),同时入队多个异步事件(风控检测、银行卡状态更新等),开始后续处理。
第三步:风控检测
任务创建后几秒内,系统自动执行高风险检测(HighRiskCheck),用 6 个因子判断这笔出金是否异常。
6 个因子中只有 4 个会触发额外审核(USER、AREA、FRAUDULENT、SWIFT),命中后任务模板从 default 升级为 unusual——审批从 2 步变成 3 步。
FREQUENCY 和 AMOUNT 只做记录,不触发额外审核——因为它们已有独立的安全机制(每日 10 笔限制、三层限额)。
详细的 6 因子说明和 bitmask 规则 → 出金规则手册 § 高风险判定
第四步:审批三步
风控检测完成后,任务状态变为 PROCESSING(1),进入审批流程。最多三步:
Step 1: Audit(高危审核)— 仅 unusual 模板
只有被标记为异常的出金才需要。运营人员审核该出金是否存在风险——检查用户账户状态、出金目的地、金额是否异常。
大部分正常出金跳过此步。
Step 2: Confirm(确认指示)— 所有出金
所有出金都经过此步。运营确认:
- 用户银行卡状态正常
- 出金方式已正确设置
- 对于
method = null的任务,手动选择出金通道
BST 额外校验:Confirm 会验证银证授权(Mandate)状态是否为 OPEN。如果不是 OPEN,出金无法推进——运营需要引导用户先完成银证授权。
重要:Confirm 不执行实际转账,这个动作保留给 Remittance。
Step 3: Remittance(汇出资金)— 所有出金
这是资金真正离开的一步。系统首先检查自动出金条件(6 个条件详见 规则手册):
- 全部通过 → 自动调用
startTransfer(),进入下一节的自动执行路径 - 任一不通过 → 降级为人工,运营确认后手动触发转账
审批可以全自动
对于 BST 通道的普通出金(非 unusual),如果自动出金条件全部满足,三步审批中 Confirm 和 Remittance 都是系统自动推进的。用户感知上就是"提交后几分钟到账"。
如果需求变更:修改审批流程
- 修改审批步骤 →
withdraw/src/app/Business/Task.php→$stepTemplates数组 - 新增审批步骤 → 新建 Step 类(实现
IFStep接口)+ 加到模板数组 - 修改自动出金条件 →
withdraw/src/app/Business/AutoSetting.php - 详细变更指南 → 出金变更指南
第五步(A):BST 全自动执行
如果通道是 BST(auto_bs),Remittance 调用 startTransfer() 后,进入全自动路径:
招行/民生:通过 Socket 双向链路实时获取结果,通常秒级完成。
天星:通过 REST API 分两阶段轮询获取结果——快速轮询(AsbBstTransfer 每 5 秒,最多 10 次,~50 秒)和兜底同步(SyncAsbBstWithdraw 2 小时窗口内持续同步)。兜底同步后仍未完成则标记异常,需运营人工查询天星 API 确认银行侧状态。
余额扣减注意:Remittance 执行时先从 auto_settings 扣减出金金额。如果余额低于 alarm 阈值 → 发飞书告警;低于 stop 阈值 → 自动关闭该币种的自动出金。这个扣减发生在银行转账之前——即使银行拒绝,余额已扣减,需运营手动恢复。
通道技术细节 → 通道执行手册 § BST
第五步(B):人工通道执行
所有非 BST 通道(网银、FPS、传统)都是运营驱动的:
关键区别:非 BST 通道的 startTransfer() 在 Confirm 步骤就调用了(不是 Remittance),因为是同步调用——SBA 立刻返回 transfer_manual。然后运营在银行完成转账后,在 Remittance 步骤点击确认。
部分通道在 Remittance 有前置要求:
| 通道 | 前置要求 |
|---|---|
| 广发 FPS | FPS 批量提交完成 |
| CHATS/RTGS | 文件导出完成 |
| 中银 FPS | FPS 提交完成 |
通道技术细节 → 通道执行手册
第六步:结果处理
无论 BST 还是人工通道,最终结果只有三种:
| 结果 | 任务状态 | 后续 |
|---|---|---|
| 成功 | DONE(2) | 通知用户,出金完成 |
| 失败 | 保持 PROCESSING(1) | 运营介入,可能换通道重试 |
| 超时 | 保持 PROCESSING(1) | 运营查询银行状态,手动确认或重试 |
BST 回调码参考:
- 0 = 成功 → Task DONE
- -5 = 超时 → 自动切换备用服务器重试
- -6 = 银行拒绝 → 标记失败,需人工处理
异常场景的详细排查 → 出金排障
其他触发方式
上面描述的是"用户在 moomoo App 发起出金"的标准路径。还有几种非标准触发方式:
银行发起出金
招行、民生、天星支持银行端发起出金——用户在银行 App 操作转出,moomoo 被动接收:
系统每分钟通过队列消费者拉取银行流水,发现新的银行端出金后自动创建任务,method 固定为 auto_bs,进入标准审批流程。
cmb_list / ms_list 记录状态:0=待处理 → 1=处理中 → 2=成功(任务已创建) / 3=失败
如果需求变更:支持新银行的银行端发起
- 新建
xxx_list表(参考cmb_list结构) - 新建两个队列事件:
SyncXxxWithdraw(拉取流水)+XxxWithdrawCreate(创建任务) - 在
Queue.php的$_enableEvent中注册 - 对接银行侧流水查询 API
基金赎回出金
基金赎回不是用户直接发起出金,而是赎回成功后系统自动创建出金任务:
基金赎回有独立的 5 个事件组成等待链路:
| 事件 | 做什么 |
|---|---|
| FundWithdrawCreate | 接收赎回完成通知 |
| FundWithdrawWait | 等待赎回资金到达证券账户 |
| FundWithdrawArrivalTime | 检查预计到账时间 |
| FundWithdrawSuccess | 资金到账 → 创建 fund 模板的出金任务 |
| FundWithdrawFailed | 到账失败 → 需人工处理 |
这条链路可能需要 T+1 到 T+3 天,取决于基金赎回的到账时间。
基金赎回 ≠ 用户主动出金
触发方式不同(系统自动 vs 用户手动),前置流程不同(需等资金从基金账户转入证券账户),但风控处理相同(都经过 HighRiskCheck)。
现金宝赎回出金
现金宝(活期理财)赎回与基金赎回类似,但回调方式不同——通过 SrvPush(服务推送)回调,而非队列轮询。
| 事件 | 做什么 |
|---|---|
| CurrentDepositRedeemSuccess | 赎回成功 → 推进出金任务 |
| CurrentDepositRedeemRejected | 赎回被拒 → 出金任务标记失败 |
| CurrentDepositRedeemSbaCreateRetry | SBA 创建失败时重试 |
CRM 代发出金
运营可在 CRM 系统中直接帮用户发起出金(无需用户在 App 操作)。适用场景:
- 用户无法登录 App(如设备故障)
- 批量退款/补偿操作
- 特殊情况下的运营协助
CRM 代发出金与用户自发出金走相同的审批流程和风控检测,但跳过部分 App 端前置检查(如 GDCA)。
OM 账户出金
OM(Omnibus)账户是合并持仓账户,出金时 Remittance 步骤会先执行 OmWithdrawDeduct(OM 扣款),从 OM 子账户扣除资金。如果 OM 扣款失败,出金任务卡在 Remittance 步骤,需人工处理。
OM 出金也支持 BST 自动出金,但前提是 OM 扣款已完成。
冲正
出金已完成(DONE)后需要把资金追回时,运营发起冲正(REVERSE):
- 运营在 CRM 发起冲正操作
- 系统检查状态——只有 DONE 的任务才能冲正
- 更新任务状态为 REVERSE(5)
- SBA 执行反向资金操作
- 通知用户出金已撤回
触发原因:银行退回、错误出金、风控事后拦截。
异步驱动:为什么有时候要等
你可能注意到,出金流程中很多步骤之间有几秒到几分钟的间隔。这是因为出金系统采用事件驱动队列——每个异步操作是一个"事件",放入数据库队列表中,由后台进程逐个处理。
11 点截止规则与 8:30 批量处理
出金任务有一个关键的日切时间点:
| 时间 | 系统行为 |
|---|---|
| 每日 11:00 | 截止线——此时间之后提交的非自动出金,标记为"次日处理" |
| 次日 08:30 | 系统自动将前一天 11:00 后提交的任务从"次日处理"转为"处理中",运营开始处理 |
这意味着用户在 11:00 之后提交的非自动出金(非 BST 通道),实际要等到次日 08:30 才开始处理。BST 自动出金不受此规则影响(在服务时段内随时处理)。
NSS 名单筛查
跨境电汇(tele_transfer)在执行前需通过 NSS(Name Screening Service) 检查收款人姓名,确认不在制裁名单上。NSS 不通过的出金需人工审核,运营联系合规团队判断。
| 你观察到的 | 实际发生了什么 |
|---|---|
| "提交后几秒才开始审批" | 风控检测事件在排队等待执行 |
| "BST 出金等了几分钟" | 系统正在轮询银行结果(每 5~60 秒一次) |
| "广发 FPS 出金等了很久" | 广发轮询最多 1000 次(可达数小时) |
| "基金赎回出金等了好几天" | 在等赎回资金从基金账户到证券账户 |
队列消费者每分钟由 cron 启动,基于数据库锁避免重复消费。如果某类事件的专用消费者进程挂了,该类事件会卡住。
这不是 bug
间隔是异步设计的正常表现。如果出金异常地长时间不推进,排查方向是:对应的队列消费者进程是否在运行。
常见误解
| 误解 | 事实 |
|---|---|
| "用户点了提交,出金就开始了" | 还没有。系统先执行 14 项前置检查,任一不通过连出金任务都不会创建。用户在 App 端直接看到错误提示 |
| "处理中 = 银行正在转钱" | 不一定。"处理中"包含了 Audit → Confirm → Remittance 三步审批,可能卡在运营确认环节,银行还没收到指令 |
| "BST 出金秒到" | 招行/民生通常秒级,但天星最长可能等 2 小时(快速轮询 + 兜底同步)。即使招行,也需要 SBA 冻结/扣款/发指令的处理时间 |
| "11 点后不能出金" | 可以出金。BST 自动出金不受此限制。11 点截止规则只影响非 BST 通道——这些任务会推迟到次日 08:30 开始处理 |
| "CRM 代发出金跳过风控" | 不跳过。CRM 代发走相同的审批流程和风控检测(HighRiskCheck),只是跳过部分 App 端前置检查(如 GDCA) |
读完之后
| 我想... | 去看 |
|---|---|
| 了解某条规则为什么存在、能不能改 | 出金规则手册 |
| 看某种通道从 Remittance 到银行的技术细节 | 通道执行手册 |
| 出金出了问题,按症状排查 | 出金排障 |
| 推动一个出金相关的需求变更 | 出金变更指南 |
| 查某个状态码/字段是什么意思 | 出金数据字典 |
这个页面有帮助吗?