Skip to content

02 · 货币与钱包系统

0. 文档说明

内容
版本V1.0

资金底座。03 充值、04 提现、07 俸禄、08 返水、09 活动、10 任务、11 返佣、12 游戏聚合的任何金额进出都经本系统记账。本篇确立**「一租户一本位币站」**的记账与展示宪法。


一、定位与职责

  • 定义平台支持的币种目录 currencyCatalog(法币 / 加密)及其展示符号、位置与精度;
  • 定义站型:一租户 = 一「本位币站」,站内全链路单币种、单账户;
  • 定义用户的钱包账户模型:本位币单账户、可用/彩金/冻结三个余额桶;
  • 定义账变操作枚举(全平台资金进出的规范动作集,13 账本直接继承);
  • 定义两个钱包边界换算点(充值锁价折入、提现锁价折出)与「站内不提供币币兑换」;
  • 对上层提供统一的记账、余额查询、金额表达接口。

核心产品决策:

  • 一站一本位币:每个租户只有一个本位币 baseCurrency,站内自始至终只见本位币符号,站内单账户、无第二币种、无站内换算、只显示本位币。一期两种站型:CNY 站USDT 站
  • 换算只发生在两个钱包边界(充值 / 提现),均锁价 + 快照 + 审计;站内展示与账本永不换算。
  • 一个商户可开多个站服务多国市场(一站一国、无限开站,包网引擎卖点);平台跨站层面是多币种(不同站不同本位币),平台看板 / 跨租户对账「按币种分列」;单站内部为单币种口径。

二、核心概念与数据模型

2.1 币种目录 currencyCatalog(平台级)

平台维护统一币种目录,某站型的本位币与其可收付通道币种均引用它:

字段说明示例
code币种代码CNY / USDT / ETH / BTC
kindFIAT 法币 / CRYPTO 加密决定走法币通道还是链上
symbol展示符号¥ / USDT / ETH / BTC
position符号位置CNY 前缀 ¥8,888.88;USDT/ETH/BTC 后缀 1,688.88 USDT
decimals展示精度CNY 2 / USDT 2 / ETH 6 / BTC 8
networks加密专属:可用链TRC-20 / ERC-20(USDT)、Ethereum(ETH)、Bitcoin(BTC)
icon图标资源金 ¥ 徽标 / usdt.svg / eth.svg / btc.svg

标准精度与符号(一期):

codekind符号/位置decimals网络
CNYFIAT¥ 前缀2
USDTCRYPTOUSDT 后缀2TRC-20 / ERC-20
ETHCRYPTOETH 后缀6Ethereum(ERC-20)
BTCCRYPTOBTC 后缀8Bitcoin 主网

币种目录属平台级;某站的本位币wallet.baseCurrency 指定(必 ∈ 目录),可收付的通道币种集wallet.depositChannelCurrencies / wallet.withdrawChannelCurrencies 指定(§四)。

2.2 钱包账户 WalletAccount(本位币单账户核心)

用户在一个站内只有一个钱包账户,币种恒为该站本位币,三个余额桶:

字段说明
userId / tenantId归属(按租户强隔离)
currency恒 = 站的 baseCurrency(单账户,无第二币种)
available可用余额(可投注/可提现)
bonus彩金余额(受打码约束,解锁后转可用)
locked仅提现审核占用(申请提现即从 available 平移至 locked,通过后扣减、拒绝回滚)。投注不产生 locked——投注即时扣减 available,结算再入账
version乐观锁版本号(§3.6)
updatedAt最后账变时间

余额展示口径:界面「余额」默认 = available + bonus(可由租户配置 wallet.balanceFormula 改口径);可用与彩金在钱包详情分列。原型:WBAL(总额)、WCASH(可用)、WBONUS(彩金)。金额恒带本位币符号(§七展示铁律)。

2.3 账变操作枚举(全平台规范,13 账本直接继承)

操作码触发方余额桶方向幂等键
DEPOSIT_CREDIT03 充值(锁价折入本位币)available+充值单号
WITHDRAW_LOCK04 提现申请available → locked平移提现单号
WITHDRAW_CONFIRM04 审核通过locked提现单号
WITHDRAW_ROLLBACK04 拒绝/失败locked → available平移提现单号
BET_DEBIT12 投注available注单号
PAYOUT_CREDIT12 结算派彩available+注单号+结算序号
REWARD_CREDIT07/08/09/10/11 领取(目标=可用)available+claimId
BONUS_GRANT07/09/10 发放(目标=彩金)bonus+claimId
BONUS_UNLOCK10 打码引擎(达标)bonus → available平移grantId
BONUS_FORFEIT09 违规没收 / 14 风控bonusgrantId+原因
BONUS_EXPIRE定时任务(过期)bonusgrantId
ADJUST_MANUAL15 后台人工(双人复核)任意±工单号

规则:

  • 一切金额均以本位币记账,每条流水逐笔带 currency(= 本位币,出生币种);
  • 领取入 available 还是 bonus活动配置 rewardTarget 决定(09/07/08/10 各自声明),对应 REWARD_CREDIT / BONUS_GRANT 两个操作码;
  • 所有操作必须带幂等键,库层 UK(tenant_id,op_code,biz_key) 兜底;
  • 新增资金动作必须先在本表登记操作码,禁止私造。

2.4 彩金授予 BonusGrant(状态机)

每笔发放到 bonus 的奖励是一条独立授予记录,打码进度按本位币单口径核算(细则归 10 任务打码)。

打码进度权威(全库口径):彩金打码进度的唯一权威账本 = 10 wagering_obligations(source=BONUS_UNLOCK,10 订阅 BetSettled 推进);本篇 bonus_grants.wagering_progress(§八)为冗余展示快照,以 grantId 关联,供钱包/彩金详情直读,不作对账权威。达标判定与解锁触发均由 10 执行(下表)。

      BONUS_GRANT(claimId)
  ● ─────────────────► WAGERING(打码中) ──打码达标──► UNLOCKED(已解锁)
                          │        │                    · BONUS_UNLOCK: bonus→available
                          │        │
                 到期未达标│        │违规/主动放弃/按配置提现清空
                          ▼        ▼
                      EXPIRED    FORFEITED
                      · BONUS_EXPIRE      · BONUS_FORFEIT
流转触发账变
→ WAGERING领取发放(rewardTarget=bonus)BONUS_GRANT +bonus
WAGERING → UNLOCKED10 判定 wagering_obligations 达标(10 订阅 BetSettled 推进,进度权威 = 10 wagering_obligations)10 调 02 执行 BONUS_UNLOCK bonus→available;由 10BonusUnlocked 事件(幂等键 grantId,→ 00 §4.6)
WAGERING → EXPIREDexpire_at 到期BONUS_EXPIRE −bonus
WAGERING → FORFEITED风控判违规 / 用户放弃 / 未打码先提现(按租户配置)BONUS_FORFEIT −bonus
  • UNLOCKED 为终态、不回退:彩金一旦解锁(资金已放),后续注单 BetCancelled 不回退该解锁——BetCancelled 只冲正返水(08)与成长值 growthBase(06),不追回已解锁彩金及随之的可提现资格。防套利靠 validTurnover 取整(§3.7)+ 风控事后画像(→ 14),不靠回退资金。

2.5 账变流水

任何余额桶变化写一条不可变流水(操作码、幂等键、变动金额、变动后三桶快照),用于对账与「交易记录」展示,归 13 记录与账变,表结构见 §九。


三、钱包模型与边界换算(单账户制)

3.1 站内单币种,永不换算(无汇率铁律)

  • 引擎内金额一律 { amount, currency },currency 恒为本位币;站内不出现第二币种、不换算、不比较跨币、不加总跨币;
  • API 金额一律字符串(禁 float,→ 00 §4.8);前端只做格式化,禁止写死汇率,禁止展示「≈ ¥xxx」参考小字;
  • 各类活动奖金、返水、俸禄额都是本位币的数值,单口径直配,无「同基数按汇率折算」问题;
  • 无汇率铁律的边界:「站内永不换算」约束的是站内展示与账本(余额/奖励/统计/等级进度)。换算只发生在两个钱包边界(§3.2),边界事件用「该单已锁价」快照——该快照仅作该笔记账/统计,不构成站内可复用汇率,站内不缓存、不复用、不对外展示为币对牌价。

3.2 两个钱包边界换算点(锁价 + 快照 + 审计)

边界方向规则归属
充值通道币种 → 本位币站可收非本位币通道(CNY 站收 USDT/ETH/BTC;USDT 站收 CNY 等);下单时点锁价折入本位币入账(credited = paid × lockedRate)。通道币种与账户币种解耦(全站型全通道)03 充值
提现本位币 → 通道币种本位币按提现时点锁价折出到目标通道币种;提交前明示到账数,快照进提现单04 提现
  • lockedRate:边界事件发生时点从平台汇率源取的快照汇率,存单据、可审计;订单有效期内以此结算,关单后失效,重新下单重新询价;同币种(通道币种 = 本位币)lockedRate = 1,无换算行。
  • 账本只记本位币的最终入账/出账结果;逐笔流水带本位币符号。

3.3 站内不提供币币兑换(合规 + 敞口,明确「不做」)

站内不提供钱包互换 / 币币兑换 / 站内换汇任何入口。理由:合规风险 + 汇率敞口。用户改变持币结构只能经「提现到目标币种通道 / 用目标币种通道充值」两个边界完成,均锁价留痕。

3.4 三个余额桶的资金动线

  • 充值(锁价折入本位币)、派彩、rewardTarget=available 的领取 → 入 available;
  • rewardTarget=bonus 的发放 → 入 bonus,按 §2.4 状态机走到解锁/没收/过期;
  • 投注即时扣减 available(BET_DEBIT),结算入账(PAYOUT_CREDIT);不经过 locked;
  • 提现只动 available:申请即平移入 locked,审核通过扣减、拒绝回滚;bonus 不可提。

3.5 幂等记账

一切账变携带幂等键(§2.3),重复回调/重复点击不重复记账;库层 UK(tenant_id,op_code,biz_key) 兜底(→ 00 §4.4)。

3.6 并发与负余额

  • 每账户 version 乐观锁(或按账户串行队列),并发账变冲突即重试;
  • 任何扣减前校验余额充足,任何余额桶不得为负;不足即拒绝(投注失败/提现驳回),不允许透支。

3.7 精度与舍入

  • 存储精度 DECIMAL(32,8) 高于展示精度(本位币 decimals);
  • 派发类计算(返水/俸禄/佣金)统一向下取整(截断)到本位币精度,避免超发;差额留存不累计给用户;
  • 展示只格式化不改值(不四舍五入进位)。

四、★ 租户可配置项(wallet 命名空间)

jsonc
"wallet": {
  "baseCurrency": "CNY",         // 站型 = 本位币:站内唯一记账与展示币种(须 ∈ 平台币种目录)
                                 //   CNY 站 → "CNY";USDT 站 → "USDT"(一期两种站型)
  "balanceFormula": "available+bonus", // 「余额」展示口径(默认 available+bonus)
  // ↓ 通道币种集:充值/提现通道对外收付的币种,可 ≠ baseCurrency(边界锁价,→ 03/04)
  "depositChannelCurrencies": ["CNY", "USDT", "ETH", "BTC"],   // 该站充值可收的通道币种
  "withdrawChannelCurrencies": ["CNY", "USDT"],                // 该站提现可付的通道币种
  "catalogOverride": {           // 各币种展示覆盖(可选);icon=币种图标(余额类展示=图标+纯数值,符号身份由图标承担)
    "CNY":  { "symbol": "¥",    "position": "prefix", "decimals": 2, "icon": "coin-cny" },
    "USDT": { "symbol": "USDT",  "position": "suffix", "decimals": 2, "icon": "usdt", "networks": ["TRC-20","ERC-20"] },
    "ETH":  { "symbol": "ETH",   "position": "suffix", "decimals": 6, "icon": "eth" },
    "BTC":  { "symbol": "BTC",   "position": "suffix", "decimals": 8, "icon": "btc" }
  }
}

一期样例站型(与 admin 样例一致):

  • global = CNY 站:baseCurrency:"CNY",收 USDT/ETH/BTC 充值(边界锁价折入 CNY)。
  • sea01 = USDT 站:baseCurrency:"USDT";场馆链路 route 由平台超管按申报口径配置(原生 / CNY 映射,商户不可见不可改,→ 12 场馆链路);账本恒记本位币真值。
  • gold88 = CNY 站(SETUP)。

Schema 校验(由本系统提供,发布前强制,→ 01 §4.3):baseCurrency ∈ 平台目录;depositChannelCurrencies / withdrawChannelCurrencies ⊆ 平台目录(可含 ≠ baseCurrency 的币种,边界锁价合法);合规层可强制剔除某币种。baseCurrency 运营中途变更属高危(牵动等级 growthBase 重放,→ 06),后台加二次确认 + 平台审批,首版建议锁定不可改。


五、与其他系统的关系 / 接口

交互方关系
03 充值 / 04 提现成功入账 / 锁定-扣减-回滚(§2.3 对应操作码);边界锁价折算(§3.2)
07/08/09/10/11领取派发按 rewardTargetREWARD_CREDITBONUS_GRANT,均本位币
10 打码引擎推进 BonusGrant 打码进度,达标触发 BONUS_UNLOCK
12 游戏聚合BET_DEBIT / PAYOUT_CREDIT;场馆链路 route 由 12 定义,账本恒记本位币真实值
13 账变消费本系统全部账变流水
14 风控入账前置校验;违规触发 BONUS_FORFEIT / 账户冻结
06 等级订阅 DepositSucceeded(跨币充值按该单 lockedRate 折入 growthBase)/ BetSettled
17 看板平台跨站按币种分列(单站内部单币种)

对外接口(引擎级):

接口调用方幂等键说明
getWallet(userId)展示层 /me本位币单账户三桶余额(字符串金额)
credit / debit(userId, bucket, amount, opCode, bizKey)03/04/07–12/15bizKey 必填唯一记账入口(币种恒本位币),写流水+发事件
transfer(bucket→bucket)(锁定/解锁/回滚)04/10同上locked 平移与 bonus 解锁
format({amount})各处按本位币目录+租户覆盖格式化

站内单账户,故无 switchActive 之类接口。边界锁价接口(询价/入账/出账)归 03/04。


六、运营后台能力(→ 15 汇总)

能力
平台控制台币种目录维护(新增币种/网络/精度);跨站(跨租户)资金总览按币种分列
商户控制台查用户钱包三桶余额与账变流水;人工调整(发起工单→双人复核→执行 ADJUST_MANUAL,全程留痕);冻结/解冻用户钱包;本站站型(baseCurrency)与通道币种集配置(白名单内,改 wallet.*;baseCurrency 变更走平台红区审批)

提现审核归 04;对账报表归 13/17。


七、展示契约(→ 原型映射)

展示铁律(用户端 + 后台通用):

  • 站内所有金额恒本位币符号;禁止「数字不变只换符号」的渲染(原型 applyCur 只格式化本位币,不做符号替换)。
  • 已发生金额永远带出生币种(= 本位币),不重标、不换算(逐笔流水带币种)。
  • 等级 / 俸禄 / 任务进度 / 签到 / 里程碑:本位币单口径(单币种站天然无歧义,金额与进度同为本位币数值,→ 06)。
  • 边界换算的「参考到账/到手」仅在充值/提现页出现,且明示为该单锁价快照(非站内牌价)。
UI 元素数据原型载体(app.js)系统契约
顶部余额胶囊本位币余额 + 图标renderBalance().balance-pill(自管币种,排除 wrapAmounts)数据 = /me.wallet + bootstrap.wallet;单账户单币种
全站金额文本{amount} 按本位币渲染money(base)=fmtIn(base);wrapAmounts¥.cur-amt[data-base]接口返回字符串金额,前端只 format(),不算汇率、不换符号
我的·钱包区三桶余额/统计,.no-cur 自管WBAL/WCASH/WBONUS/WSTAT,renderMe()/me.wallet 单账户返回
充值/提现入口按站型的通道币种集切通道renderWallet() 按币种通道切法币/加密通道列表来自 03/04 配置(通道币种可 ≠ 本位币,边界锁价)

关键实现约定(标准化):

  • .balance-pill.no-cur 排除wrapAmounts(自管币种,避免被整站渲染逻辑覆写);
  • 金额渲染幂等,applyCur 时机在 i18n 之后(setTimeout(0) + window load);applyCur 只负责格式化本位币,不做符号替换;
  • 顶栏为单账户单币种形态,无币种选择

八、数据库设计(全表含 tenant_id;金额 DECIMAL(32,8))

关键字段约束/索引
currencies(平台级)code PK, kind, symbol, position, decimals, networks JSON, icon
wallet_accountsid PK, tenant_id, user_id, currency(= 站本位币), available, bonus, locked, version, updated_atUK(tenant_id,user_id)(一租户一用户一账户,币种恒本位币)
wallet_ledgerid PK, tenant_id, user_id, currency, op_code, biz_key, bucket, amount, available_after, bonus_after, locked_after, event_id, remark, created_atUK(tenant_id,op_code,biz_key);IDX(tenant_id,user_id,created_at)
bonus_grantsgrant_id PK, tenant_id, user_id, currency, source(系统/活动ID), claim_id, amount, wagering_multiple, wagering_required(解锁门槛快照), wagering_progress(冗余展示快照,权威在 10 wagering_obligations,按 grant_id 关联), status(WAGERING/UNLOCKED/EXPIRED/FORFEITED), granted_at, expire_at, finished_atUK(tenant_id,claim_id);IDX(tenant_id,user_id,status)
  • 站内单账户:wallet_accounts(tenant_id,user_id) 唯一,currency 列冗余存本位币便于流水对齐,不作分桶键;
  • wallet_ledger 不可变(只插不改);三个 _after 快照列用于对账与「交易记录」直读;
  • 边界锁价的通道币种/lockedRate充值单 / 提现单(03/04 表),wallet_ledger 只记本位币结果;
  • 「交易记录/领取记录」等展示由 13 基于 wallet_ledger + 各业务单据组织。

单账户无选中币种持久化:不设 user_wallet_prefs(last_active_wallet)一类选中币种持久化表——币种恒为本位币。


九、扩展点与开放问题

  • 新增币种:平台目录加定义 → 站型可选它作本位币,或加入充/提通道币种集。
  • 稳定币多网络:同一 USDT 跨 TRC-20/ERC-20,充提选网络、费率不同,归 03/04。
  • 通道币种全景:各通道币种的路由、限额、手续费、锁价询价在 03/04 展开。
  • 余额口径:默认 available+bonus;部分站只显可用 —— wallet.balanceFormula 控制。
  • 未打码先提现的处置:清空彩金(FORFEIT)还是拒绝提现,按租户配置,细则归 04/10。
  • 本位币变更:高危,牵动等级 growthBase 重放(→ 06),首版锁定;开放后走平台红区审批。

十、验收要点

  • [ ] 全链路(接口/前端/DB)搜不到任何站内汇率常量;站内无第二币种符号、无换符号渲染;
  • [ ] 钱包账户对 (tenant_id,user_id) 唯一(单账户);全站单账户单币种;
  • [ ] API 金额均为字符串;前端仅格式化,精度按本位币 decimals(CNY2/USDT2/ETH6/BTC8);
  • [ ] 同一 biz_key 重复回调/重复点击只入账一次(UK 生效);
  • [ ] 投注即扣 available,提现申请入 locked、拒绝原额回滚;任何桶不出现负数;
  • [ ] 彩金达标自动 bonus→available,bonus_grants.status 与两条账变(GRANT/UNLOCK)对得上;
  • [ ] 充值可收 ≠ 本位币通道(锁价折入本位币),提现可付 ≠ 本位币通道(锁价折出);账本恒记本位币,lockedRate 存单据可审计;
  • [ ] 站内无币币兑换入口;边界快照不作站内牌价复用。