Files
pangolin/docs/superpowers/plans/2026-07-09-crypto-tx-engine.md
wangjia 5fd0d3c148 feat(pay): 同源收款测试页 GET /_test(Phase E 手动闭环验证)
pay-server 自带 /_test 测试页:创建订单→显示收款地址+精确金额(可复制)
+过期倒计时→轮询状态到 paid。同源、无 CORS/CSP 摩擦,不碰营销站安全头;
纯调 POST /order + GET /order/{id},无私钥无密钥。生产收银台后续走独角数卡门面。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 15:51:10 +08:00

5.2 KiB

pangolin-pay 加密货币交易引擎(钱包 + watcher + 收款侦测 + 归集)

#34 的第一块、可独立执行、能用真实小额 USDT 端到端验证的基础件。只做加密货币的"交易"核心—— 生成/派生地址、侦测链上到账、归集(sweep)——不含独角数卡对接、不含 /internal/codes/mint 发码 (那是"发货侧",本引擎跑通后再接)。

成果物:一个独立 Go 服务 pangolin-pay(部署在独立海外 VPS),对外提供"建单→给收款地址→侦测到账→ 标记已付"的 HTTP 接口 + 一套离线归集工具。跑通后,任何门面(独角数卡 / 自建下单页)都能挂上来。

安全铁律(贯穿):热服务只持 xpub(watch-only 派生地址)+ TronGrid key + 金库收款地址,绝不持私钥; 私钥/助记词冷存,归集在离线端签名。参考知识:brain notes/dev/crypto-hd-wallet-basics.html

关键常量(开工核对无误再写死):USDT-TRC20 合约 TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t(6 位小数); TRON 派生 m/44'/195'/0'/0/i;TronGrid https://api.trongrid.io


Phase A — 钱包与密钥(全程离线)

  • A.1 离线生成两套助记词(断网 Mac 跑 Ian Coleman standalone / bip_utils,选 Coin=TRX、24 词、English):
    • 钱包 A · 运营收款;钱包 B · 冷备金库(另一套,独立冷存)
  • A.2 导出钱包 A 的 account xpub(m/44'/195'/0' 的 Account Extended Public Key);取钱包 B 的地址0(T...)当归集目标
  • A.3 冷存:助记词 A/B 分开手抄/钢板 + Bitwarden;只把 xpub_A + sweep 地址_B 放进 pangolin-pay 配置(Bitwarden 管密钥)
  • A.4 交叉核对:B 阶段的 Go 派生实现算出的地址 0..4,必须与 Ian Coleman 显示的逐个一致(防派生实现不一致 → 收款地址对不上 → 收不到钱)

Phase B — 服务骨架 + HD 派生

  • B.1 建 Go 服务 pangolin-pay(独立 cmd 或独立仓)。配置:xpub_A / sweep_addr_B / TRONGRID_API_KEY / 确认数 / 订单超时。自带 SQLite
  • B.2 HD 派生模块:tyler-smith/go-bip39 + btcsuite/.../hdkeychainxpub_A watch-only 派生 .../0/i 公钥 → TRON 地址(公钥 → Keccak-256 取后 20 字节 → 前缀 0x41 → Base58Check → T...)。单测:派生地址对齐 Ian Coleman(A.4)
  • B.3 数据模型:
    • pay_orders(order_no PK, sku, expect_amount, addr_index, address, status, tx_id, confirmations, created_at, expires_at)
    • addr_cursor(next_index) — HD 派生游标,单调递增,地址不复用

Phase C — 收款(建单 + 侦测到账)

  • C.1 POST /order:入参 sku/基准金额 → cursor++ 派生新收款地址 → 写单(expect_amount唯一尾数去重、expires=+15min)→ 返回 {order_no, address, expect_amount, expires_at}
  • C.2 GET /order/{order_no}:返回 status(pending/paid/expired)+ 到账信息
  • C.3 TronGrid watcher(轮询):对每个 pending 单地址查 TRC20 转入
    • GET /v1/accounts/{addr}/transactions/trc20?only_confirmed=true&contract_address=<USDT>&limit=...(头带 TRON-PRO-API-KEY)
    • 匹配:to==addresstoken==USDTvalue≥expect_amount(USDT 6 位小数,注意单位换算)且已确认 → status=paid、记 tx_id
  • C.4 幂等(同 tx_id 只认一次)+ 超时 pending→expired(地址不复用)+ 崩溃恢复(从 DB 未决单继续轮询)
  • C.5 TronGrid 限流退避 + 重试;测试用 mock TronGrid 覆盖:金额匹配 / 确认门槛 / 超时 / 幂等 / 恢复

Phase D — 归集(sweep,离线签名)

  • D.1 归集清单:watcher(xpub)列出有余额地址 [(index, address, balance)]
  • D.2 gas:从一个 gas 钱包给每个待归集地址打少量 TRX(TRC20 转账吃 energy;收款地址无 TRX);量大改能量租赁降本
  • D.3 离线签名(离线机,导入钱包 A 助记词):派生 address_i 私钥 → 构造 TRC20 transfer(sweep_addr_B, balance)离线签名
  • D.4 广播:把签好的交易经 TronGrid POST /wallet/broadcasttransaction 上链(只有签名后的交易出门,私钥不出门)
  • D.5 半自动脚本(离线端)+ 归集 runbook;触发=阈值(攒够 X)或定时;热派生地址不长期囤大额

Phase E — 端到端验证(真实小额 USDT)

  • E.1 真单:POST /order → 往返回的地址转 1 USDT(TRC20) → watcher 侦测 + 确认 → GET /orderpaid
  • E.2 归集:对该地址执行 D 流程 → USDT 到达钱包 B 冷地址
  • E.3 安全核对:pangolin-pay 进程/配置无任何私钥/助记词(只有 xpub);密钥不入 git(红线扫描);地址不复用

Verification(判据)

  • go test ./...:HD 派生对齐 Ian Coleman、watcher 匹配/确认/超时/幂等/恢复全绿。
  • 真机:一笔真 1 USDT 从建单→侦测→确认→归集到冷地址全通。
  • 安全:热服务无私钥、密钥不入 git、每单唯一地址不复用——逐项核对。

不在本轮

  • 独角数卡门面对接 + epay 支付口(下一块:接门面)。
  • /internal/codes/mint JIT 发码 + 兑换闭环(发货侧,#34 后续)。
  • HD 自动 sweep(先人工离线归集)、多币种(先只 USDT-TRC20)、watcher 高可用。