543a54c606
单个固定收款地址 + 每单唯一金额(base+微尾数≤0.01U)+ 精确==匹配 + 时间戳防迟到误配 + 孤儿人工对账。归集=1地址(激活一次/扫一笔)最省。前端契约不变(POST /order 返 address+amount), 以后升多地址/GasFree 纯后端切。删除已被取代的地址池 plan。双产物 md+html,登记 index。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.6 KiB
4.6 KiB
pangolin-pay 单地址 + 唯一金额收款模型(定稿)
#34/34A 收款模型定稿。取代"每单唯一 HD 地址"(激活/归集随订单数线性涨)与"地址池"(仍多地址)。 最终选:单个固定收款地址 + 每单唯一金额。归集 = 1 个地址(激活一次、扫一笔),成本最省; 订单靠金额区分,不靠地址。
前端契约不变:
POST /order仍返回{address, expect_amount},client 只用每单返回值 → 以后要升 多地址/GasFree 是纯后端换实现,前端零改动(见下"契约约定")。已实现的 wallet 派生 / tron(TronGrid 读到账、建交易、签名)/ cmd/sweep 复用;主要改 store / pay(建单)/ watcher(匹配)。现有代码是"每单派生新址",本轮改为"单地址 + 唯一金额"。
决策(已定)
- 收款地址:钱包 A 的地址 0(
m/44'/195'/0'/0/0),从配置注入或由 xpub 派生。所有订单收到这一个地址。 - 唯一金额 ⭐:
expect_amount = base + tail。base = 价格 ×1e6(micro-USDT);tail 取方案 A(微尾数):tail ∈ [1, 9999]micro(偏差 ≤ 0.009999 USDT,价格几乎不变)。tail 在迟到窗口内不复用。 - 匹配 = 精确
==:watcher 找"到收款地址、value == expect_amount、block_ts > order.created"的 TRC20 转入 → paid。 支付页显示可一键复制的精确金额 + "请付精确金额"提示。 - 15min 超时 → expired → 提示重建订单(该 tail 一段时间内不复用)。
- 同用户单订单:同时只能一个活跃(pending)订单。
- 孤儿付款:到该地址但
value不匹配任何活跃订单(付错/迟到抹了尾数)→ 记orphan_payments,人工对账/补发。 - 归集:定期扫这一个地址余额 → 冷钱包(能量租赁 ~$0.1–1/笔)。激活一次、归集一笔。
契约约定(钉死,保证以后单↔多地址纯后端)
client(下单页/独角数卡)只使用每单
POST /order返回的address+expect_amount,绝不硬编码/缓存地址。 只要守住这条,单地址 ↔ 多地址 ↔ GasFree 都是后端内部换实现,前端与 VPN app 都不改。
Phase A — 数据模型
pay_orders改:加user_ref、expect_amount(唯一金额,micro-USDT)、matched_tx_id;address恒为收款地址。- tail 分配支撑:记录当前活跃订单已用 tail(避免撞)+ 近期已用 tail 冷却(迟到窗口内不复用);或全局计数器 + 去重校验。
orphan_payments表:tx_id(唯一) / value / block_ts / created_at / handled(bool)。- 配置:
PAY_RECEIVE_ADDRESS(或从PAY_ACCOUNT_XPUB派生 index 0,与钱包 A 地址 0 一致)。
Phase B — 建单(pay 服务)
CreateOrder(userRef, sku, priceMicro):① 校验同用户无活跃单(有则返回现有/拒);② 分配唯一金额(base + 未占用 tail);③ 写pending单(TTL 15min),address= 收款地址。- tail 分配:与当前所有活跃订单不撞 + 迟到窗口内不复用(记录+回收)。
GetOrder。
Phase C — watcher 改造(匹配 / 超时 / 孤儿)
- 匹配:对每个
pending单,查收款地址的 TRC20 转入,筛value == expect_amount且block_ts > order.created→paid+ 记matched_tx_id。 - 超时:
pending过期 →expired。 - 孤儿:收款地址的到账 tx 匹配不到任何活跃订单 → 记
orphan_payments(按 tx_id 去重)。 - 幂等(tx_id 全局去重,含 orphan)+ 崩溃恢复;TronGrid 需取
block_timestamp供时间过滤。
Phase D — 归集(复用 cmd/sweep)
- 扫收款地址余额 → 冷钱包(能量租赁,先手动 runbook);一地址一笔,与订单状态解耦。
Phase E — 测试 + 真链验证
- 单测:唯一金额分配不撞、精确匹配、付错/迟到金额成孤儿、超时、同用户单订单、幂等、时间戳过滤(旧到账不误配新单)、崩溃恢复。
- 真链:两并发订单不同唯一金额、同一地址各自付款到 paid;故意付错金额 → 成孤儿不误配;归集该地址到冷钱包。
Verification / 判据
go test ./...(store/pay/watcher 新逻辑全绿,重点覆盖唯一金额/精确匹配/孤儿/时间戳)。- 真链:并发不同金额 + 付错成孤儿 两场景正确;归集一笔搞定。
- 成本:激活一次、归集 O(1);小额高频不再被激活/归集费拖累。
不在本轮 / 以后(纯后端可切)
- 多地址 / 地址池(容错更好但更贵,以后纯后端切,前端不动)。
- GasFree(#35,免 TRX 但每笔固定 1 USDT,适合少地址批量)。
- 能量租赁自动化;发货侧(独角数卡 + /internal/codes/mint)另排。