5fd0d3c148
pay-server 自带 /_test 测试页:创建订单→显示收款地址+精确金额(可复制)
+过期倒计时→轮询状态到 paid。同源、无 CORS/CSP 摩擦,不碰营销站安全头;
纯调 POST /order + GET /order/{id},无私钥无密钥。生产收银台后续走独角数卡门面。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
217 lines
16 KiB
HTML
217 lines
16 KiB
HTML
<!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)· #34(tier-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 HD,watch-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> 作归集 gas(TRON 转账吃 energy/bandwidth)。</li>
|
||
<li><b>1.5</b> 验证:xpub 离线派生前 5 地址;往 index 0 打 1 USDT 测试,确认 watch-only 查得到。</li>
|
||
</ul>
|
||
|
||
<h2>Phase 2 — pangolin-pay:TronGrid 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&contract_address=<USDT></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=<hmac> · X-Pangolin-Timestamp(±5min) · X-Pangolin-Nonce
|
||
Body: { "plan":"pro", "duration_days":365, "order_ref":"<独角数卡订单号>" }
|
||
→ 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> 存 unused(channel=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_days(Pro 月/年、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 ↔ codes(mint/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>
|