Files
pangolin/docs/superpowers/plans/2026-07-09-payment-clean-usdt-loop.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

186 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 收款闭环 · 独角数卡 + 自托管 USDT(TRC20)+ webhook JIT 发码(最干净长期方案)
> #34。目标:把「下单 → USDT 付款 → 自动发激活码 → 客户端/用户中心兑换 → 订阅生效」端到端打通,
> 全程自托管、售出才发码、无跑分、无第三方支付主体。
>
> **关键前提(已摸底)**:激活码后端大半已就绪——`POST /v1/redeem`JWT 兑换,含幂等+限频+账户锁)、
> 批次生成/导出(`server/cmd/codegen` + `server/internal/codes`,码为 Crockford Base32 75-bit 带校验位、只存
> hash)、发卡店回调 `POST /webhook/store/codes`HMAC+时间戳+nonce 防重放)都在。本方案**新增的核心只有一个
> 后端端点**JIT 发码 `/internal/codes/mint`+ 一个独立收款服务(`pangolin-pay`TronGrid watcher + epay 支付口)。
>
> **三大组件 / 数据流**
> ```
> 买家 ──下单──▶ 独角数卡(门面, 独立VPS)
> │ 建单 + epay 跳转
>
> pangolin-pay(自建收款服务, 同VPS)
> ├ 派生每单唯一 TRC20 收款地址(watch-only, 只持 xpub)
> ├ TronGrid watcher 轮询到账 + 确认
> └ 到账 ──epay notify──▶ 独角数卡「已支付」
>
> ▼ 独角数卡 API-提货
> pangolin-pay /issue ──HMAC──▶ pangolin 控制面 POST /internal/codes/mint
> └ 生成合法码 + 存 unused + 返回明文
>
> ▼ 独角数卡把码交付买家
> 买家 ──输码──▶ 客户端/用户中心 POST /v1/redeem ──▶ 订阅生效
> ```
>
> **安全红线(贯穿全程)**:① 热服务 `pangolin-pay` **只持 xpub 派生收款地址、绝不持私钥**(能收不能转,
> 被脱库也转不走钱);② 私钥/助记词冷备 + Bitwarden,不入 git、不上热机;③ 收款终点=自托管钱包,变现经
> 交易所出金 US LLC,记账走 accounting agent;④ 全程不碰跑分/中国支付。
>
> **主体归属**`pangolin-pay` + 独角数卡部署在**独立海外小 VPS**(不放 pangolin1:内存紧 + 风险隔离),
> 只经 HTTPS + HMAC 与 pangolin 控制面(`api.yanmeiai.com`)通信。
>
> 参考文档:`docs/payment-a-selfhosted-store-plan.html` / `payment-channels-overview.html` /
> `payment-reseller-fulfillment-design.html`。
---
## Phase 0 — 计划落库 + todo
- [ ] 0.1 本 `.md` 定稿 + 生成 HTML 阅读版 `docs/payment-clean-usdt-plan.html`,登记进 `docs/index.html`「实现计划」
- [ ] 0.2 `/todo`#34 propose 为「待确认」(tier-1`--ref` 指向本 `.md`),等用户 approve 再进 Phase 1
---
## Phase 0.5 — 钱包选型(用哪个钱包)
**先厘清:这套架构的「钱包」是两件事,别当成装一个 app。**
- **主种子(钱在哪)**:一套 BIP39 助记词,冷存。这是资产本体。
- **watcher 收款(派生地址)**:热服务用**库**从 account **xpub(公钥)**派生每单地址——**watch-only,不是钱包 app**,也拿不到私钥。
- **归集签名**:需要动钱时,在冷端用私钥离线签 sweep 交易。
所以「用哪个钱包」= 选①主种子怎么冷存 + ②watcher 用哪个派生库。
**① 主种子 / 冷存 候选**
| 方案 | 冷存安全 | xpub 导出(给 watcher) | 归集签名 | 结论 |
|---|---|---|---|---|
| **离线生成助记词 + 库派生**(推荐) | 高(纸/钢板离线保存,永不联网) | 直接从种子导 account xpub | 离线机用库签 | **最贴合 watch-only 架构、最灵活** |
| **Ledger 硬件钱包**(可叠加) | 最高(私钥永不出设备) | 可导 account 公钥(需配套工具) | 插 Ledger 签 | 想再加一层硬件冷存就用它兜底 |
| TronLink / imToken / Trust(手机热钱包) | 低(热钱包) | 不便批量 per-order 派生 | app 内签 | **不推荐**当收款主钱包(热 + 难集成) |
**② watcher 派生库 候选**(给热服务 watch-only 派生 TRON 地址,非钱包)
| 语言 | 库 |
|---|---|
| Go(与 server 同栈,推荐) | `tyler-smith/go-bip39`(助记词)+ `btcsuite/btcd/btcutil/hdkeychain`BIP32 派生)→ 公钥转 TRON base58 地址(`0x41` 前缀 + Keccak);或成熟 TRON Go SDK |
| Python | `tronpy` + `bip_utils`(一步到位派生 TRON 地址) |
**推荐:** 主种子用 **离线生成 BIP39 助记词**(想再稳就叠一层 **Ledger 冷存**);watcher 用 **Go 库从 account xpub watch-only 派生**(与 server 同栈、好集成、热机无私钥)。**收款钱包 = 你自己的 HD 种子,不是某个 app。**
- [ ] 0.5.1 定主种子方案:离线助记词(推荐)/ 是否叠加 Ledger 冷存;记录选型入本计划
- [ ] 0.5.2 定 watcher 派生库(Go bip39+hdkeychain→TRON 地址 / Python tronpy+bip_utils),跑通派生并与钱包 app 交叉核对同一地址(防派生实现不一致收不到钱)
- [ ] 0.5.3 定归集冷签工具(离线机脚本 / Ledger),确认能对 sweep 交易离线签名
---
## Phase 1 — 自托管钱包(TRC20 HDwatch-only 收款)
目标:一套助记词派生**无限个收款地址**,热服务只拿 xpub(公钥)派生地址收款、拿不到私钥;私钥冷备用于归集。
- [ ] 1.1 **离线**生成 BIP39 助记词(24 词)。TRON 派生路径 `m/44'/195'/0'/0/i`195=TRON coin type)。
工具:离线机器用 `tronpy` / `tronweb` 或硬件钱包(Ledger,导出 account xpub)。**生成过程不联网。**
- [ ] 1.2 密钥分级存放:
- **助记词/私钥** → 冷备(纸/钢板离线)+ Bitwarden 记录,**永不上热机、不入 git**
- **account xpub(公钥)** → 给 `pangolin-pay` 用来 watch-only 派生收款地址(无私钥)
- **HMAC secret**Phase 3/4 用)→ Bitwarden
- [ ] 1.3 定**冷钱包归集地址**cold sweep 目标)+ 记录 USDT-TRC20 合约地址(主网 `TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t`**核对无误再写死**
- [ ] 1.4 备少量 **TRX** 作归集 gasTRON 转账吃 energy/bandwidth,账户无 energy 时烧 TRX);记 energy 租赁备选
- [ ] 1.5 验证:用 xpub 离线派生前 5 个地址;往 index 0 打 1 USDT 小额测试,确认 watch-only 能查到该地址收款
---
## Phase 2 — pangolin-payTronGrid watcher(自建收款侦测)
目标:一个独立 Go 服务,派生每单唯一地址、轮询 TronGrid 侦测到账、确认后置单已付。
- [ ] 2.1 建服务骨架 `pangolin-pay`Go,复用 server 的 DB 方言层思路;独立仓/独立 cmd 均可)。自带轻量 DB(SQLite 即可)
- [ ] 2.2 数据模型:
- `pay_orders(order_no PK, sku, plan, duration_days, addr_index, address, expect_amount, status, tx_id, confirmations, created_at, expires_at)`
- `addr_cursor(next_index)` — HD 派生游标,单调递增
- [ ] 2.3 **每单唯一地址**:建单时从 xpub 派生 `m/.../0/next_index` 分配给该单(最准,免金额撞车)。次选:单地址+唯一金额(备选降级)
- [ ] 2.4 TronGrid 轮询:对每个 pending 单地址查 TRC20 转入
- endpoint`GET https://api.trongrid.io/v1/accounts/{addr}/transactions/trc20?only_confirmed=true&contract_address=<USDT>`
- 比对:`to==address``token==USDT``value≥expect_amount`(含小额容差处理)
- [ ] 2.5 **确认数**:只认 `only_confirmed`(已固化)交易,或等 ≥N 区块(TRON 约 1 分钟)。到账+确认 → `status=paid`、记 `tx_id`
- [ ] 2.6 到账后触发发货(Phase 4 的 epay notify);**幂等**:同 `tx_id` 只认一次,一个 order 只发一次货
- [ ] 2.7 订单超时(默认 15min)未付 → `status=expired`、释放地址(地址不复用,避免旧付款串单)
- [ ] 2.8 TronGrid API key + 限流退避 + 失败重试;watcher 崩溃可恢复(从 DB 未决单继续轮询)
- [ ] 2.9 测试:mock TronGrid → 单测 金额匹配 / 确认门槛 / 超时 / 幂等(同 tx 重放)/ 崩溃恢复
---
## Phase 3 — pangolin 控制面:JIT 发码端点 `POST /internal/codes/mint`
目标:付款确认时**即时生成一个合法激活码**(这才是 webhook JIT 的落点:售出才产码、才有效)。
现有 `/webhook/store/codes` 要求调用方自带合法码;自建场景由 pangolin 产码更干净(保证格式+校验位单源)。
- [ ] 3.1 新增 `POST /internal/codes/mint`(挂 `/internal` 组,**无 JWT**HMAC 同 webhook
`X-Pangolin-Signature: sha256=<hmac>` + `X-Pangolin-Timestamp`(±5min) + `X-Pangolin-Nonce`(Redis 去重)
- [ ] 3.2 body `{plan: free|pro|team, duration_days, order_ref}` → 复用 `codes.GenerateCode()` 产码 +
`Store.CreateCode(hash, planID, duration, batchID)` 存 unusedchannel=ChannelStore)→ 返回 `{code, expires_at}`
- [ ] 3.3 **幂等**:以 `order_ref` 为幂等键(存 `order_ref→code_hash` 映射);同单重放返回**同一个码**,绝不重复发
- [ ] 3.4 secret 走 Bitwarden → `server.env``CODES_MINT_HMAC_SECRET`);路由挂载(`main.go`),仅 HMAC 保护、不经公网 JWT
- [ ] 3.5 复用现有 webhook 的验签/时间戳/nonce 逻辑(抽公共中间件,避免重复实现)
- [ ] 3.6 测试:验签通过/失败、时间戳越界、nonce 重放、`order_ref` 幂等、产码格式(Canonicalize 可还原+校验位对)
---
## Phase 4 — 独角数卡对接(门面 + 自动发货)
目标:独角数卡做门面/订单/交付,`pangolin-pay` 做 USDT 支付口 + 发货时取码。
- [ ] 4.1 (研究·先做)摸清所用独角数卡版本的**支付接口(epay 兼容规范)**与**API-提货/自动发货商品**机制
(版本相关,需实测;产出对接点清单再实现)
- [ ] 4.2 `pangolin-pay` 实现 **epay 兼容支付接口**:独角数卡建单 → 跳我方支付页(展示该单 USDT 地址 + 应付金额 +
倒计时 + 网络必须 TRC20 强提示)
- [ ] 4.3 到账(Phase 2.5)→ 按 epay 规范 **notify 独角数卡**(带签名)→ 独角数卡标「已支付」
- [ ] 4.4 商品设为 **API-提货**:独角数卡发货时调 `pangolin-pay /issue` → 内部以 HMAC 调 pangolin
`/internal/codes/mint` 取合法码 → 返回独角数卡 → 交付买家(页面/邮件/站内)
- [ ] 4.5 **SKU 映射表**(配置化,不散在代码):独角数卡商品 → `plan + duration_days`
Pro 月=pro/30、Pro 年=pro/365、Team 月/年=team/30|365;对齐 `design/CLAUDE.md §7` 与官网 Pricing
- [ ] 4.6 幂等贯通:独角数卡 order_no 作为 `order_ref` 一路透传到 mint,保证一单一码
---
## Phase 5 — 部署 · 运维 · 安全
- [ ] 5.1 独立海外小 VPS(2C/2G 起)部署:独角数卡(DockerPHP+MySQL+Redis+ `pangolin-pay` + 域名 + HTTPS
**不放 pangolin1**
- [ ] 5.2 密钥落位核对:`pangolin-pay` 只有 **xpub + TronGrid key + epay 签名密钥 + mint HMAC secret**
**无私钥/助记词**(冷备)。全部 Bitwarden,不入 git
- [ ] 5.3 **归集 runbook**:定期把各收款地址 USDT sweep 到冷钱包(**离线签名**,热机不碰私钥);备 TRX gas
- [ ] 5.4 **变现 runbook**:冷钱包 → 交易所(能出金到 US LLC 的,自行核实)→ 卖 USDT→USD → US LLC 银行;
入账交 `code/accounting` agent(本项目不做费用台账)
- [ ] 5.5 监控告警(接现有 Telegram 告警):watcher 存活、TronGrid 配额、到账延迟、mint 失败、订单异常堆积
- [ ] 5.6 **对账**:独角数卡订单 ↔ `pay_orders` ↔ codesmint 条数/redeem 条数)三方日结,差异告警
---
## Phase 6 — 端到端验证
- [ ] 6.1 真单闭环:下单 → USDT 付款(TRC20)→ watcher 确认 → mint 码 → 独角数卡交付 →
客户端/用户中心 `POST /v1/redeem` → 订阅/时长生效、全端同步
- [ ] 6.2 异常路径:超时未付 / 金额不足 / 重复付款 / 确认期网络抖动 / mint 重放 → 各自表现符合预期
- [ ] 6.3 安全核对:热服务 `otool`/进程无私钥;密钥不在 git(红线扫描);redeem 幂等+限频生效;
每单唯一地址不复用
---
## Verification(端到端判据)
- 后端:`cd server && go test ./internal/codes/...`(新增 mint 端点验签/幂等/格式测试全绿)。
- pangolin-pay`go test ./...`watcher 匹配/确认/超时/幂等/恢复)。
- 真机:一笔真 USDT 小额单,从下单到订阅生效全通;异常路径逐条演练。
- 安全:热服务无私钥、密钥不入 git、每单唯一地址、redeem 幂等限频——逐项核对。
## 不在本轮
- 支付宝/微信第三方通道(E 层,灰产,另议且当可替换插件)。
- HD 自动 sweep 归集(先人工离线归集,量大再自动化)。
- 多币种(先只 USDT-TRC20)、watcher 高可用/多实例、加密网关备选路子(B 方案)。
- Telegram Stars / Apple IAP 独立收款口(海外补充,另排)。