Files
pangolin/docs/payment-clean-usdt-plan.html
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

217 lines
16 KiB
HTML
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.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>收款闭环 · 独角数卡 + 自托管 USDT + webhook JIT 实现计划</title>
<style>
:root{
--bg:#0f1117; --panel:#171a22; --panel2:#1d2129; --fg:#e6e8ee; --fg2:#a8afbd;
--accent:#e0884f; --accent2:#5fb0c9; --ok:#5ec27a; --bad:#e06a6a; --warn:#e0b84f;
--border:#272c36; --mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
--sans:-apple-system,"PingFang SC","Helvetica Neue",Arial,sans-serif;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.7;font-size:15px}
.wrap{max-width:960px;margin:0 auto;padding:48px 24px 96px}
h1{font-size:29px;line-height:1.3;margin:0 0 8px;letter-spacing:-.01em}
.sub{color:var(--fg2);font-size:15px;margin:0 0 32px}
h2{font-size:21px;margin:44px 0 14px;padding-bottom:8px;border-bottom:1px solid var(--border)}
h3{font-size:16px;margin:24px 0 8px;color:var(--fg)}
p{margin:10px 0}
code{font-family:var(--mono);font-size:.85em;background:var(--panel2);padding:1px 6px;border-radius:5px;color:#f0d9c4}
pre{background:#0a0c11;border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow-x:auto;font-family:var(--mono);font-size:12.5px;line-height:1.5;color:#cdd3df}
.tag{display:inline-block;font-size:12px;font-weight:600;padding:2px 9px;border-radius:999px;vertical-align:middle}
.tag.ok{background:rgba(94,194,122,.16);color:var(--ok)}
.tag.warn{background:rgba(224,184,79,.16);color:var(--warn)}
.tag.info{background:rgba(95,176,201,.16);color:var(--accent2)}
.tag.bad{background:rgba(224,106,106,.16);color:var(--bad)}
.card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:16px 20px;margin:14px 0}
.card h3{margin-top:0;color:var(--accent2)}
table{width:100%;border-collapse:collapse;margin:16px 0;font-size:13.5px}
th,td{text-align:left;padding:9px 12px;border-bottom:1px solid var(--border);vertical-align:top}
th{color:var(--fg2);font-weight:600;font-size:13px}
ul,ol{padding-left:22px;margin:8px 0}
li{margin:6px 0}
.lead{background:linear-gradient(180deg,rgba(224,136,79,.10),transparent);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:0 0 8px}
.small{color:var(--fg2);font-size:13px}
a{color:var(--accent2)}
.back{display:inline-block;margin-bottom:24px;font-size:13px}
b{color:#fff}
.phase{font-size:18px;margin:38px 0 6px;color:var(--accent);font-weight:700}
.ck{list-style:none;padding-left:4px}
.ck li{position:relative;padding-left:26px}
.ck li::before{content:"☐";position:absolute;left:0;color:var(--fg2)}
.diagram{overflow-x:auto;background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:18px;margin:16px 0}
.diagram pre{background:transparent;border:none;padding:0;color:#cdd3df;font-size:12px}
.warnbox{background:rgba(224,106,106,.08);border:1px solid rgba(224,106,106,.35);border-radius:12px;padding:14px 18px;margin:14px 0}
.warnbox b{color:var(--bad)}
.okbox{background:rgba(94,194,122,.07);border:1px solid rgba(94,194,122,.32);border-radius:12px;padding:14px 18px;margin:14px 0}
</style>
</head>
<body>
<div class="wrap">
<a class="back" href="index.html">← 文档索引</a>
<h1>收款闭环 · 独角数卡 + 自托管 USDT(TRC20) + webhook JIT 发码</h1>
<p class="sub">2026-07-09 · <span class="tag info">阅读版</span> · 执行真相源 <code>docs/superpowers/plans/2026-07-09-payment-clean-usdt-loop.md</code>(带 checkbox)· #34tier-1</p>
<div class="lead">
<b>目标:</b>把「下单 → USDT 付款 → 自动发激活码 → 客户端/用户中心兑换 → 订阅生效」端到端打通,<b>全程自托管、售出才发码、无跑分、无第三方支付主体</b><br>
<b>关键前提(已摸底):</b>激活码后端大半已就绪——<code>POST /v1/redeem</code>(兑换,幂等+限频)、批次生成/导出、发卡店回调 <code>/webhook/store/codes</code>HMAC+时间戳+nonce)都在。本方案<b>新增核心只有一个后端端点</b>JIT 发码 <code>/internal/codes/mint</code>+ 一个独立收款服务 <code>pangolin-pay</code>TronGrid watcher + epay 支付口)。
</div>
<h2>三大组件 / 数据流</h2>
<div class="diagram">
<pre>买家 ──下单──▶ 独角数卡(门面, 独立 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 ──▶ 订阅生效</pre>
</div>
<div class="warnbox">
<b>安全红线(贯穿全程):</b>① 热服务 <code>pangolin-pay</code> <b>只持 xpub 派生收款地址、绝不持私钥</b>(能收不能转,脱库也转不走钱);② 私钥/助记词冷备 + Bitwarden,不入 git、不上热机;③ 收款终点=自托管钱包,变现经交易所出金 US LLC,记账走 accounting;④ 全程不碰跑分/中国支付。<b>部署在独立海外小 VPS,不放 pangolin1</b>(内存紧 + 风险隔离),只经 HTTPS+HMAC 与控制面 <code>api.yanmeiai.com</code> 通信。
</div>
<h2>已就绪 vs 本轮新增</h2>
<table>
<thead><tr><th>能力</th><th>状态</th></tr></thead>
<tbody>
<tr><td><code>POST /v1/redeem</code> 兑换(幂等+限频+账户锁)</td><td><span class="tag ok">已实现</span></td></tr>
<tr><td>批次生成/导出(Crockford Base32 75-bit 带校验位,只存 hash</td><td><span class="tag ok">已实现</span></td></tr>
<tr><td>发卡店回调 <code>/webhook/store/codes</code>HMAC+时间戳+nonce</td><td><span class="tag ok">已实现</span>(留给未来第三方发卡店)</td></tr>
<tr><td><code>POST /internal/codes/mint</code> JIT 发码</td><td><span class="tag warn">本轮新增</span></td></tr>
<tr><td><code>pangolin-pay</code>TronGrid watcher + epay 支付口</td><td><span class="tag warn">本轮新增</span></td></tr>
<tr><td>自托管 TRC20 HD 钱包 + 归集/变现 runbook</td><td><span class="tag warn">本轮新增</span></td></tr>
</tbody>
</table>
<h2>Phase 0.5 — 钱包选型(用哪个钱包)</h2>
<p><b>先厘清:这套架构的「钱包」是两件事,别当成装一个 app。</b></p>
<ul>
<li><b>主种子(钱在哪)</b>:一套 BIP39 助记词,冷存。资产本体。</li>
<li><b>watcher 收款</b>:热服务用<b></b>从 account <b>xpub(公钥)</b>派生每单地址——<b>watch-only,不是钱包 app</b>,拿不到私钥。</li>
<li><b>归集签名</b>:动钱时在冷端用私钥离线签 sweep 交易。</li>
</ul>
<p>所以「用哪个钱包」= 选 ① 主种子怎么冷存 + ② watcher 用哪个派生库。</p>
<h3>① 主种子 / 冷存 候选</h3>
<table>
<thead><tr><th>方案</th><th>冷存安全</th><th>xpub 导出</th><th>归集签名</th><th>结论</th></tr></thead>
<tbody>
<tr><td><b>离线生成助记词 + 库派生</b> <span class="tag ok">推荐</span></td><td>高(纸/钢板离线,永不联网)</td><td>从种子导 account xpub</td><td>离线机用库签</td><td>最贴合 watch-only 架构、最灵活</td></tr>
<tr><td>Ledger 硬件钱包(可叠加)</td><td>最高(私钥不出设备)</td><td>可导 account 公钥(需工具)</td><td>插 Ledger 签</td><td>想再加一层硬件冷存就用它兜底</td></tr>
<tr><td>TronLink / imToken / Trust(手机热钱包)</td><td>低(热钱包)</td><td>不便批量派生</td><td>app 内签</td><td><span class="tag bad">不推荐</span>当收款主钱包</td></tr>
</tbody>
</table>
<h3>② watcher 派生库 候选(给热服务 watch-only 派生,非钱包)</h3>
<table>
<thead><tr><th>语言</th><th></th></tr></thead>
<tbody>
<tr><td>Go(与 server 同栈,推荐)</td><td><code>tyler-smith/go-bip39</code> + <code>btcd/btcutil/hdkeychain</code>BIP32)→ 公钥转 TRON base58 地址;或成熟 TRON Go SDK</td></tr>
<tr><td>Python</td><td><code>tronpy</code> + <code>bip_utils</code>(一步派生 TRON 地址)</td></tr>
</tbody>
</table>
<div class="okbox">
<b>推荐:</b>主种子用<b>离线生成 BIP39 助记词</b>(想再稳叠一层 <b>Ledger 冷存</b>);watcher 用 <b>Go 库从 account xpub watch-only 派生</b>(与 server 同栈、好集成、热机无私钥)。<b>收款钱包 = 你自己的 HD 种子,不是某个 app。</b>
</div>
<ul class="ck">
<li><b>0.5.1</b> 定主种子方案:离线助记词(推荐)/ 是否叠加 Ledger 冷存;记录选型。</li>
<li><b>0.5.2</b> 定 watcher 派生库(Go bip39+hdkeychain→TRON / Python tronpy+bip_utils),跑通派生并与钱包 app 交叉核对同一地址(防派生不一致收不到钱)。</li>
<li><b>0.5.3</b> 定归集冷签工具(离线机脚本 / Ledger),确认能对 sweep 交易离线签名。</li>
</ul>
<h2>Phase 1 — 自托管钱包(TRC20 HDwatch-only 收款)</h2>
<p>一套助记词派生无限个收款地址;热服务只拿 xpub 派生地址收款、拿不到私钥;私钥冷备用于归集。</p>
<ul class="ck">
<li><b>1.1</b> <b>离线</b>生成 BIP39 助记词(24 词),TRON 派生路径 <code>m/44'/195'/0'/0/i</code>195=TRON)。工具:离线机 tronpy/tronweb 或硬件钱包(Ledger 导出 xpub),<b>生成不联网</b></li>
<li><b>1.2</b> 密钥分级:助记词/私钥→冷备+Bitwarden(永不上热机);account xpub→给 pangolin-pay watch-only 派生;HMAC secret→Bitwarden。</li>
<li><b>1.3</b> 定冷钱包归集地址 + 记 USDT-TRC20 合约地址(主网 <code>TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t</code>,核对无误再写死)。</li>
<li><b>1.4</b> 备少量 <b>TRX</b> 作归集 gasTRON 转账吃 energy/bandwidth)。</li>
<li><b>1.5</b> 验证:xpub 离线派生前 5 地址;往 index 0 打 1 USDT 测试,确认 watch-only 查得到。</li>
</ul>
<h2>Phase 2 — pangolin-payTronGrid watcher(自建收款侦测)</h2>
<ul class="ck">
<li><b>2.1</b> 建 Go 服务 <code>pangolin-pay</code>(自带轻量 SQLite)。</li>
<li><b>2.2</b> 数据模型:<code>pay_orders(order_no, sku, plan, duration_days, addr_index, address, expect_amount, status, tx_id, confirmations, created_at, expires_at)</code> + <code>addr_cursor(next_index)</code></li>
<li><b>2.3</b> <b>每单唯一地址</b>:建单时从 xpub 派生 <code>m/.../0/next_index</code>(最准,免金额撞车)。次选单地址+唯一金额。</li>
<li><b>2.4</b> TronGrid 轮询:<code>GET /v1/accounts/{addr}/transactions/trc20?only_confirmed=true&amp;contract_address=&lt;USDT&gt;</code>,比对 to/token/金额(含容差)。</li>
<li><b>2.5</b> 确认:只认已固化交易或等 ≥N 区块(约 1 分钟)→ <code>status=paid</code> + 记 tx_id。</li>
<li><b>2.6</b> 到账触发发货;<b>幂等</b>:同 tx_id 只认一次、一单只发一次。</li>
<li><b>2.7</b> 超时(默认 15min)→ <code>expired</code>、释放地址(不复用,避免串单)。</li>
<li><b>2.8</b> TronGrid key + 限流退避 + 重试;崩溃可从 DB 未决单恢复。</li>
<li><b>2.9</b> 测试:mock TronGrid → 金额匹配/确认门槛/超时/幂等/恢复。</li>
</ul>
<h2>Phase 3 — 控制面:JIT 发码端点 <code>POST /internal/codes/mint</code></h2>
<p>付款确认时<b>即时生成一个合法码</b>——这才是 webhook JIT 的落点:售出才产码、才有效。自建场景由 pangolin 产码(保证格式+校验位单源),比让外部产码更干净。</p>
<pre>POST /internal/codes/mint # /internal 组, 无 JWT, HMAC 同 webhook
Headers: X-Pangolin-Signature: sha256=&lt;hmac&gt; · X-Pangolin-Timestamp(±5min) · X-Pangolin-Nonce
Body: { "plan":"pro", "duration_days":365, "order_ref":"&lt;独角数卡订单号&gt;" }
→ 200 { "code":"PGL-XXXX-...", "expires_at":"..." } # 以 order_ref 幂等, 同单返同码</pre>
<ul class="ck">
<li><b>3.1</b> 新增端点,HMAC+时间戳+nonce(复用 webhook 那套验签,抽公共中间件)。</li>
<li><b>3.2</b> 复用 <code>codes.GenerateCode()</code> 产码 + <code>Store.CreateCode</code> 存 unusedchannel=ChannelStore)→ 返回明文。</li>
<li><b>3.3</b> <b>幂等</b><code>order_ref</code> 为幂等键(存 order_ref→code_hash),同单重放返回同一码。</li>
<li><b>3.4</b> secret → Bitwarden → <code>server.env</code><code>CODES_MINT_HMAC_SECRET</code>);<code>main.go</code> 挂路由。</li>
<li><b>3.5</b> 保留现有 <code>/webhook/store/codes</code> 供未来第三方发卡店。</li>
<li><b>3.6</b> 测试:验签/时间戳/nonce/order_ref 幂等/产码格式。</li>
</ul>
<h2>Phase 4 — 独角数卡对接(门面 + 自动发货)</h2>
<ul class="ck">
<li><b>4.1</b>(研究·先做)摸清所用独角数卡版本的 <b>epay 支付接口</b> + <b>API-提货/自动发货商品</b>机制(版本相关,实测出对接点清单再实现)。</li>
<li><b>4.2</b> pangolin-pay 实现 <b>epay 兼容支付接口</b>:独角数卡建单 → 跳我方支付页(USDT 地址+应付金额+倒计时+TRC20 强提示)。</li>
<li><b>4.3</b> 到账 → 按 epay 规范 <b>notify 独角数卡</b>(签名)→ 标「已支付」。</li>
<li><b>4.4</b> 商品设 <b>API-提货</b>:发货时调 pangolin-pay <code>/issue</code> → 内部 HMAC 调 <code>/internal/codes/mint</code> 取码 → 交付买家。</li>
<li><b>4.5</b> <b>SKU 映射表</b>(配置化):独角数卡商品 → plan+duration_daysPro 月/年、Team 月/年;对齐官网 Pricing)。</li>
<li><b>4.6</b> 幂等贯通:独角数卡 order_no 作为 order_ref 一路透传到 mint,一单一码。</li>
</ul>
<h2>Phase 5 — 部署 · 运维 · 安全</h2>
<ul class="ck">
<li><b>5.1</b> 独立海外小 VPS(2C/2G)部署:独角数卡(Docker+ pangolin-pay + 域名 + HTTPS<b>不放 pangolin1</b>)。</li>
<li><b>5.2</b> 密钥落位核对:pangolin-pay 只有 xpub + TronGrid key + epay 签名密钥 + mint HMAC secret<b>无私钥/助记词</b>(冷备)。全 Bitwarden。</li>
<li><b>5.3</b> <b>归集 runbook</b>:定期把各收款地址 USDT sweep 到冷钱包(<b>离线签名</b>),备 TRX gas。</li>
<li><b>5.4</b> <b>变现 runbook</b>:冷钱包 → 交易所(能出金 US LLC 的)→ 卖 USDT→USD → 银行;记账交 accounting agent。</li>
<li><b>5.5</b> 监控告警(接现有 Telegram):watcher 存活、TronGrid 配额、到账延迟、mint 失败、订单堆积。</li>
<li><b>5.6</b> <b>对账</b>:独角数卡订单 ↔ pay_orders ↔ codesmint/redeem)三方日结,差异告警。</li>
</ul>
<h2>Phase 6 — 端到端验证</h2>
<ul class="ck">
<li><b>6.1</b> 真单闭环:下单→USDT付款→watcher确认→mint码→独角数卡交付→<code>/v1/redeem</code>→订阅生效、全端同步。</li>
<li><b>6.2</b> 异常路径:超时未付/金额不足/重复付/确认期抖动/mint 重放。</li>
<li><b>6.3</b> 安全核对:热服务无私钥、密钥不入 git、每单唯一地址不复用、redeem 幂等+限频。</li>
</ul>
<div class="okbox">
<b>验收判据:</b>后端 <code>go test ./internal/codes/...</code> 含 mint 验签/幂等/格式全绿;pangolin-pay <code>go test</code> 含匹配/确认/超时/幂等/恢复;真机一笔真 USDT 小额单全通 + 异常逐条演练;安全逐项核对。
</div>
<h2>不在本轮</h2>
<ul>
<li>支付宝/微信第三方通道(E 层,灰产,另议且当可替换插件)。</li>
<li>HD 自动 sweep 归集(先人工离线归集)。</li>
<li>多币种、watcher 高可用、加密网关备选路子(B 方案)。</li>
<li>Telegram Stars / Apple IAP 独立收款口(海外补充,另排)。</li>
</ul>
<p class="small" style="margin-top:32px">相关:<a href="payment-channels-overview.html">支付渠道选型总览</a> · <a href="payment-a-selfhosted-store-plan.html">方案A 自建发卡网细化</a> · <a href="payment-reseller-fulfillment-design.html">发卡+发货架构</a></p>
</div>
</body>
</html>