Appearance
04 · 提现系统
0. 文档说明
| 项 | 内容 |
|---|---|
| 版本 | V1.0 |
资金出口 = 风险出口。锁定/扣减/回滚走 02 的
WITHDRAW_LOCK / CONFIRM / ROLLBACK(账户恒本位币baseCurrency);风控前置归 14;审核工作台归 15。本篇是 02 §3.2「提现边界锁价」的落地。
一、定位与职责
- 定义提现申请的校验链(KYC / 余额 / 打码门槛 / 限额 / 收款账户);
- 定义提现订单状态机(锁定 → 风控 → 审核 → 打款 → 终态)与资金一致性;
- 落地提现边界换算:本位币按提现时点锁价
lockedRate折出到目标通道币种channelCurrency(02 §3.2),提交前明示到账数、快照进提现单; - 定义收款账户管理(法币银行卡 / 加密地址簿);
- 定义自动通过、双人复核、手续费规则。
术语(照 02):baseCurrency 本位币(账户口径)、channelCurrency 通道币种(到账方式对外付出币种,可 ≠ 本位币)、lockedRate 锁价(提现时点快照汇率)。
边界:余额三桶语义归 02(账户恒本位币,站内单账户);打码进度计算归 10;风控规则归 14;审核界面归 15。
二、核心概念与数据模型
2.1 提现订单 WithdrawOrder
| 字段 | 说明 |
|---|---|
withdrawId / tenantId / userId | 单号(幂等键)、归属 |
currency / amount / fee / actualAmount | 本位币 baseCurrency(扣款口径)、申请额、手续费、实扣额(= amount) |
channelCurrency / payoutAmount / lockedRate | 目标通道币种(可 ≠ 本位币)、到账数(通道币种)、提现时点锁价快照(同币种=1,→ §3.3bis)。到账数 = (amount − fee) × lockedRate |
accountRef | 收款账户(银行卡 ID / 地址簿 ID) |
riskResult / reviewerId / ticketId | 风控结论、审核人、关联工单(大额双审) |
channelId / txHash / externalRef | 打款通道、链上哈希/通道单号 |
state + 时间戳组 | 见状态机 |
申请(校验链通过,WITHDRAW_LOCK)
● ──► RISK_SCREENING(风控) ──► PENDING_REVIEW(待审核) ──► APPROVED ──► PAYING(打款中) ──► SUCCESS
│ │ ▲ │ (WITHDRAW_CONFIRM)
│自动拒 │驳回 │小额低风险自动通过 │打款失败
▼ ▼ │(跳过人工) ▼
REJECTED ◄──────────────────┘ │ FAILED ──重试──► PAYING
(WITHDRAW_ROLLBACK) │ │终止(回滚)
│ ▼
└─────────────────────────── REJECTED| 流转 | 触发 | 资金动作 |
|---|---|---|
| 申请 → RISK_SCREENING | 校验链全过 | WITHDRAW_LOCK(available → locked) |
| → REJECTED(任意阶段) | 风控拒 / 人工驳回 / 打款终止 | WITHDRAW_ROLLBACK(locked → available),记拒因 |
| PAYING → SUCCESS | 通道回执 / 链上确认 | WITHDRAW_CONFIRM(locked −),发 WithdrawCompleted |
| FAILED → PAYING | 可重试:打款失败次数 < payout.maxRetry 且非致命错误(通道超时/临时不可用等) → 换通道重试(幂等:同 withdrawId 仅一笔在途) | — |
| FAILED → REJECTED | 终止:达最大重试次数 payout.maxRetry(默认 3),或致命错误(账户/地址无效、通道永久拒付),或财务人工判定不可打款 | WITHDRAW_ROLLBACK(locked → available),记拒因(PAYOUT_EXHAUSTED / PAYOUT_FATAL / MANUAL_TERMINATE) |
FAILED 双出边判定:每次打款失败先判错误类别与已重试次数——可重试类且未超上限走 PAYING(自动或财务手动换通道);致命类、超上限、或财务终止走 REJECTED 并原额回滚,locked 资金不滞留 FAILED 态。payout.maxRetry 为租户可配上限(→ §四)。
2.2 收款账户(账户化)
用户从已存账户中选择到账方式,一次保存长期复用。四类 payoutKind,手续费挂在方式上:
| kind | 二级渠道 | 字段 | 校验(保存时,失败 toast) | 手续费 |
|---|---|---|---|---|
ewallet 数字钱包 | GOPAY/OKPAY/VIPPAY/波币/K豆/万币/TOPAY/808钱包… | 渠道 + 姓名 + 账号 | 账号 ≥6 位字母数字;姓名必填 | 免 |
crypto 虚拟货币 | USDT × 链(TRC-20/ERC-20) | 链 + 地址(+备注) | TRC:^T[1-9A-HJ-NP-Za-km-z]{33}$;ERC:^0x[0-9a-fA-F]{40}$ | 免 |
bank 银行卡 | —(开户行为字段) | 开户行 + 持卡人 + 卡号 | 卡号 16–19 位数字;持卡人/开户行必填 | 2% |
alipay 支付宝 | — | 姓名 + 账号 | 11 位手机号或邮箱 | 5% |
账户管理规则:默认账户自动带入(用户在 ⋮ 菜单「设为默认」,组内置顶+标);编辑仅可改账号(渠道/链/姓名/开户行锁定 🔒,变更须删除重加);删除即移出(默认/选中随之清空);姓名一致性——新增带实名账户时姓名须与已有账户一致(脱敏比对提示);账户行不展示「已验证/待验证」类标注。
三、业务规则
3.1 申请校验链(按序,任一失败即拦)
① 账户状态(05:非冻结)
→ ② KYC:默认不要求;后台对该账户下发 kycRequired 时,点「确认提现」直接打开实名认证页(05 §2.4),
认证通过后自动继续本次提现(PENDING 中提示等待)
→ ③ 收款账户有效(账户化,见 §2.2)
→ ④ 可提现金额校验:打码门槛已折入「可提现金额」口径——彩金打码达标(02)与 `WITHDRAW_GATE` 义务缺口(充值时按 `withdraw.wageringCheck` 生成、10 推进)达标后**自动计入可提现额,不单列缺口提示**(唯一实现口径,与 §四 `wageringCheck`、10 §七对齐)
→ ⑤ 彩金处置(存在未完成 BonusGrant 时按配置:拒绝 或 没收后放行)
→ ⑥ 限额:单笔 min/max(按币种);**次数不限(无日次数/日累计)**
→ ⑦ 余额充足 → WITHDRAW_LOCK展示层拦截三连即 ④⑥ 的可视化:低于最低 / 超上限 / 可提现金额不足。
3.2 审核与自动通过
- 风控(14)同步评分:高危自动拒;可疑 → 强制人工。
- 自动通过:
amount ≤ autoApprove.maxAmount且风控 PASS → 跳过人工直达 APPROVED。 - 双人复核:
amount ≥ dualThreshold→ 走 15 审批工单(申请人≠复核人)。 - 其余进入商户财务审核队列(15 工作台)。
3.3 手续费(按到账方式收,不按来源/次数)
fee = amount × payoutKinds[kind].fee(数字钱包/虚拟货币 0、银行卡 2%、支付宝 5%,租户可配);actualAmount = amount − fee。选中有费方式时展示省费引导:「改用数字钱包/虚拟货币可免手续费,本笔省 ¥x」。
3.3bis 提现边界锁价(跨币种出金,落地 02 §3.2)
目标到账方式的通道币种可 ≠ 本位币:本位币账户按提现时点锁价 lockedRate 折出到通道币种。到账数 = (amount − fee) × lockedRate;lockedRate 于提现提交时点取平台汇率源(withdraw.fxSource,与 03 deposit.fx 同一「平台汇率源」实体,→ 02 §3.2)快照,方向 = 本位币 → 通道币种,非直连币对经 USDT 中转(与 03 §四 fx 一致),取整/精度同 03(round2 防浮点);提交前在费用明细卡明示到账数,并快照进提现单(存 channelCurrency/payoutAmount/lockedRate),后续打款以此为准。该快照仅记本单、不构成站内牌价(→ 02 §3.1 无汇率铁律)。例:USDT 站账户提到 CNY 方式,费用卡显「预计到账 ¥x(锁价快照)」,反向(CNY 站提到 USDT)同理;同币种(通道币种 = 本位币)lockedRate = 1,无汇率行。crossCurrencyPayout 租户可关(关则仅允许提到本位币通道)。站内不做币币兑换——改变持币结构只经此边界(02 §3.3)。
3.4 打款
- 法币:代付通道出款,回执驱动终态;
- 加密:热钱包签名广播,
txHash回填,确认数达标 → SUCCESS; - 在途唯一:同一订单同一时刻仅一笔打款在途(通道单号幂等)。
四、★ 租户可配置项(withdraw 命名空间)
jsonc
"withdraw": {
"payoutKinds": { // 到账方式(手续费挂方式,→ §2.2/§3.3)
"ewallet": { "fee": 0, "channels": ["GOPAY", "OKPAY", "VIPPAY", "波币", "K豆", "万币", "TOPAY", "808钱包"] },
"crypto": { "fee": 0, "nets": ["TRC-20", "ERC-20"] },
"bank": { "fee": 0.02 },
"alipay": { "fee": 0.05 }
},
"limits": { "CNY": { "min": 100, "max": 100000 }, "USDT": { "min": 20, "max": 20000 } }, // 按通道币种,仅单笔;次数不限
"crossCurrencyPayout": true, // 跨币种出金(提现时点锁价折出);关=仅允许提到本位币通道
// 可付的通道币种集 = wallet.withdrawChannelCurrencies(→ 02 §四)
"fxSource": "platform-rate-svc", // 提现锁价所用汇率源引用(平台汇率源,与 03 deposit.fx 同源、同「平台汇率源」实体,→ 02 §3.2);
// 方向 = 本位币 → 通道币种,非直连币对经 USDT 中转;仅边界询价用,lockedRate 提交锁定、非站内牌价(→ 02 §3.1)
"wageringCheck": { // 提现打码门槛(充值时由 03 生成 WITHDRAW_GATE 义务,10 消费推进;唯一实现,见 §3.1④)
"enabled": true, // 关=不生成 WITHDRAW_GATE 义务、提现不看打码缺口
"multiple": 1, // required = 本次充值入账额(本位币)× multiple;10 §3.3 据此建义务
"applyScope": "allDeposit" // allDeposit(每笔成功充值)| withPromoOnly(仅带充值活动的订单)
},
"autoApprove": { "enabled": true, "maxAmount": { "CNY": "1000", "USDT": "200" } },
"dualThreshold": { "CNY": "10000", "USDT": "2000" }, // 双人复核线(→ 15 工单)
"bonusPolicy": "reject", // 未完成彩金:reject | forfeit
"payout": { "maxRetry": 3 }, // 打款失败重试上限;达上限 FAILED→REJECTED 原额回滚(→ §2.1 FAILED 双出边判定)
"account": { "nameConsistency": true, "editOnlyAccountNo": true, "addressCooldownHours": 24 }
}KYC 触发不在本命名空间:默认关闭,由后台按账户下发(→ 05 auth.kycRequired 语义,风控/合规触发 → 14/15)。
wageringCheck 说明:提现打码门槛的唯一权威实现——enabled 时,03 在 DepositSucceeded 生成 source=WITHDRAW_GATE 打码义务(required = 入账额 × multiple,本位币),由 10 订阅 BetSettled 推进;提现校验链第 ④ 步据其未达标缺口折入「可提现金额」口径(达标即自动计入,不单列缺口提示,→ §3.1④、10 §七对齐)。multiple=0 或 enabled=false 即无提现打码门槛。
五、与其他系统的关系 / 接口
| 接口 | 调用方 | 幂等键 | 说明 |
|---|---|---|---|
applyWithdraw(userId, amount, accountRef) | 展示层 | 客户端单号 | amount 为本位币扣款额;目标通道币种由 accountRef 决定;跑校验链 → 锁价折出到账数 → 锁定 → 建单(快照 lockedRate/payoutAmount) |
review(withdrawId, decision, reviewerId) | 15 后台 | withdrawId+决定 | 通过/驳回(大额经工单) |
payoutCallback(payload, sign) | 通道商 | externalRef | 打款回执 → 终态 |
listWithdraws / bindCard / addAddress / verifyAddress | 展示层 | — | 记录与收款账户管理 |
事件:产 WithdrawCompleted(→ 16/17);风控交互 riskCheck(14,同步)。
六、运营后台能力(→ 15)
| 侧 | 能力 |
|---|---|
| 平台控制台 | 代付通道目录与接入、平台级出款风控线、热钱包管理 |
| 商户控制台 | 提现审核工作台(队列、用户画像/风控信息、通过/驳回、批量);限额/手续费/自动通过阈值配置;收款账户管理(异常卡/地址标记);出款统计 |
七、展示契约(→ 原型映射)
| UI 元素 | 数据 | 原型载体 | 系统契约 |
|---|---|---|---|
提现页 #sub-withdraw | — | .sub-head.center;头部居中「提现」+ 右上「提现记录」 | 推入页形态(隐藏底导);资金出口入口 |
| 金额渐变卡 | 可提现额 / 输入额 | #wd-amt(大字输入,默认带入全额)+ 全部 pill;#wd-inline 内联金色提示 | 提示随态:超出可提现金额 / 还差 ¥x 起提;金额恒本位币;可提现额 = 04 §3.1④ 折入口径 |
| 「提现到」卡 | 选中账户 | #wd-acct:未选=引导态(请选择提现账户/四方式副题),已选=头像 + 提现到 xxx + 更换 › | 账户化(§2.2);目标通道币种由所选账户决定 |
| 账户选择器 | payoutKinds + 已存账户 | renderWdSheet:底部弹层,按方式分组(组头=方式 + 免手续费绿/费率灰),组内账户行(默认标 + ⋮ 设默认/编辑/删除 + radio),每组「+添加xxx」 | 手续费挂方式(§3.3);分组=四类 payoutKind |
| 添加/编辑账户 | 渠道/链 + 动态字段 | waShowForm / wa2Save:二级弹层,主色胶囊 → 动态字段 →「保存并使用」(禁用 40%) | 校验失败 toast;姓名一致性(§2.2);编辑仅账号可改(🔒);保存即选中关闭双层 |
| 省费提示条 | 手续费率 | #wd-savetip:选中有费方式且金额有效时显示 | 文案「改用数字钱包/虚拟货币可免手续费,本笔省 ¥x」(§3.3) |
| 费用明细卡 | 金额 / 手续费 / lockedRate / 到账数 | #wd-feecard:未选=金额(本位币)+ ⓘ「选择到账方式后计算」;已选=金额 / 手续费(红 −x 或绿 免费)/(跨币种)参考汇率行 / 预计到账(绿 19px,跨币种双行) | 手续费=amount×方式费率;跨币种参考汇率行明示本单锁价快照(非站内牌价,§3.3bis);到账数=(amount−fee)×lockedRate |
| 底部状态按钮 | 校验链结论 | #wd-go:文案随态(确认提现 / 请选择提现账户 / 请添加提现账户 / 超出可提现金额 / 单笔最低 ¥100 / 请输入提现金额,禁用 40%) | 资金密码触发点:开启资金密码时点击「确认提现」先弹资金密码校验(→ 05 §七触发清单);未选账户但有金额时点击=拉起选择器 |
| KYC 门 | KYC 状态 | wdPending:后台对该账户开启且未认证 → 打开实名认证页(05) | 通过后自动继续本次提现直达结果页(§3.1②) |
结果页 #sub-wd-result | 提现单快照 | wdSubmit:84px 绿勾 + 金额大字 + 明细卡(到账账户/处理中/预计到账/流水号)+「查看提现记录 / 完成」 | = WITHDRAW_LOCK 建单语义;跨币种明细展到账数 + 锁价快照 |
提现记录页 #sub-wtxn | WXREC.wd | 三下拉(方式 × 状态[已到账/处理中/已驳回/已撤销] × 时间[今日默认/昨日/近7日]);新单实时置顶 | 金额恒本位币扣款口径(跨币种单据可展开到账数=通道币种 + 锁价快照);详情页:处理中可撤销(原路退回,撤销为资金密码触发点 → 05 §七)、已驳回重新提交 + 联系客服 |
演示钩子:?kyc=1(该账户被要求实名)&kycst=pending|approved|reject;launcher 第六机位「多态图 · 提现触发KYC」= ?kyc=1&go=withdraw 直达本页。
八、数据库设计(全表含 tenant_id;金额 DECIMAL(32,8))
| 表 | 关键字段 | 约束/索引 |
|---|---|---|
withdraw_orders | withdraw_id PK, tenant_id, user_id, currency(本位币,扣款币种), amount(本位币申请额), fee, actual_amount(本位币实扣=amount), channel_currency(通道币种,可 ≠ 本位币), payout_amount(到账数,通道币种), locked_rate(提现时点锁价,同币种=1), account_ref, risk_result, reviewer_id, ticket_id, channel_id, tx_hash, external_ref, state, created_at, decided_at, paid_at | UK(tenant_id,withdraw_id);IDX(tenant_id,state);IDX(tenant_id,user_id,created_at) |
user_bank_cards | card_id PK, tenant_id, user_id, bank, card_no_cipher, card_no_tail, holder_name, status, created_at | UK(tenant_id,user_id,card_no_hash) |
user_crypto_addresses | addr_id PK, tenant_id, user_id, currency, network, address, remark, verify_state, cooldown_until, created_at | UK(tenant_id,user_id,currency,network,address) |
payout_attempts | id PK, tenant_id, withdraw_id, channel_id, external_ref, result, created_at | UK(tenant_id,channel_id,external_ref);IDX(withdraw_id) |
九、扩展点
- 未打码先提现:
bonusPolicy默认 reject;forfeit 模式附用户告知与确认交互。 - 地址白名单模式:高安全租户可配「仅允许提现至已验证白名单地址」开关。
- 加密找零/矿工费:链上手续费(
fee之外的网络费)由平台或用户承担,按币种配置。 - 批量出款:商户侧批量审核已含;批量打款(合并签名)属基建优化。
十、验收要点
- [ ] 申请即锁定:
available减少、locked等额增加;驳回后原额回滚,三桶总和不变; - [ ] 拦截三连可视(低于最低/超上限/可提现不足);打码折入可提现口径、无次数限制文案;
- [ ] 手续费按到账方式(切支付宝 1,000 → 费 50 → 到账 950 换算正确);省费提示出现;
- [ ] 跨币种到账按提现时点锁价
lockedRate折出、提交前明示到账数、快照进提现单(如 USDT 站账户提到 CNY 方式 显 ¥ 到账数,双行 + 本位币扣款额);锁价仅记本单、非站内牌价; - [ ] 账户口径恒本位币;
crossCurrencyPayout=false时仅允许提到本位币通道;站内无币币兑换入口; - [ ] KYC 门:后台开启时确认提现弹实名页,通过自动续提直达结果页;已认证后二次提现直接放行;
- [ ] 账户管理:设默认置顶、编辑仅账号、删除清引用、姓名一致性拦截、添加即选用;
- [ ] 撤销闭环:处理中 → 详情撤销 → 列表变已撤销(原路退回提示);
- [ ] 自动通过仅发生在阈值内且风控 PASS;≥ 双审阈值必产生审批工单且申请人≠复核人;
- [ ] 同一订单不可能产生两笔在途打款(通道单号唯一约束);
- [ ]
WithdrawCompleted仅在 SUCCESS 发一次;手续费在账变中单列可对账; - [ ]
bonusPolicy=reject时带未完成彩金的提现被拒并提示;forfeit时有BONUS_FORFEIT账变留痕。