Files
pay/docs/payment-channels.html
T
wangjia e7f989588e docs(pay): 支付渠道公开文档(哪吒/支付宝 + stripe/crypto 框架)+ 登记 index
单一入口登记各支付渠道接入信息(类型/结算币种/官方文档/provider 位置/
Method 名/接入形态/凭证位置指针/状态),新渠道后续往这加。不含任何
商户号/密钥/私钥/地址等敏感值,凭证一律指向 Bitwarden 条目名。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-11 12:06:27 +08:00

168 lines
15 KiB
HTML
Raw 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>pay · 支付渠道接入登记</title>
<style>
:root{--bg:#0d1117;--card:#161b22;--card-2:#1c2330;--border:#283041;--fg:#e6edf3;--fg-soft:#aeb9c7;--muted:#7d8896;--accent:#58a6ff;--ok:#3fb950;--warn:#d29922;--bad:#f85149;--radius:14px;--mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace}
*{box-sizing:border-box}
body{margin:0;background:radial-gradient(1200px 600px at 80% -10%,rgba(88,166,255,.08),transparent 60%),var(--bg);color:var(--fg);font:15px/1.7 -apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;-webkit-font-smoothing:antialiased}
.wrap{max-width:1040px;margin:0 auto;padding:44px 24px 96px}
.eyebrow{color:var(--accent);font-weight:600;letter-spacing:.12em;font-size:12px;text-transform:uppercase}
h1{font-size:28px;margin:8px 0 8px}
.lead{color:var(--fg-soft);margin:0 0 20px}
h2{font-size:19px;margin:38px 0 12px;color:var(--accent);border-bottom:1px solid var(--border);padding-bottom:8px}
h3{font-size:16px;margin:22px 0 8px;color:var(--fg)}
p{color:var(--fg-soft)}
a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}
code{font-family:var(--mono);font-size:.85em;background:var(--card-2);padding:1px 6px;border-radius:5px;color:#cdd9e5}
b{color:#fff}
ul,ol{color:var(--fg-soft);padding-left:20px}
li{margin:3px 0}
table{width:100%;border-collapse:collapse;margin:14px 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(--fg-soft);font-weight:600;font-size:12.5px;white-space:nowrap}
td{color:var(--fg-soft)}
table.fields td:first-child{font-family:var(--mono);font-size:12px;color:#cdd9e5;white-space:nowrap;width:1%}
.lead-box{background:linear-gradient(180deg,rgba(88,166,255,.10),transparent);border:1px solid var(--border);border-radius:var(--radius);padding:16px 20px;margin:0 0 8px}
.notice{border-left:3px solid var(--warn);background:var(--card-2);border-radius:0 8px 8px 0;padding:12px 16px;margin:14px 0;color:var(--fg-soft)}
.notice b{color:var(--warn)}
.tag{display:inline-block;font-size:11.5px;font-weight:600;padding:2px 9px;border-radius:999px;white-space:nowrap}
.tag.ok{background:rgba(63,185,80,.16);color:var(--ok)}
.tag.dev{background:rgba(210,153,34,.16);color:var(--warn)}
.tag.plan{background:rgba(125,136,150,.18);color:var(--muted)}
.card{background:var(--card);border:1px solid var(--border);border-radius:var(--radius);padding:18px 22px;margin:16px 0}
.card h3{margin-top:0;color:var(--accent);display:flex;align-items:center;gap:10px}
.card h3 .tag{font-size:11px}
.kv{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;font-size:13.5px;margin:10px 0}
.kv dt{color:var(--muted)}
.kv dd{margin:0;color:var(--fg-soft)}
.svgwrap{overflow-x:auto;background:#0a0e14;border:1px solid var(--border);border-radius:12px;padding:10px 6px;margin:14px 0}
.stub{color:var(--muted);font-size:13.5px}
</style>
</head>
<body>
<div class="wrap">
<div class="eyebrow">pay · Channel Registry</div>
<h1>支付渠道接入登记</h1>
<p class="lead">pay v2 支付渠道接入的<b>单一登记入口</b>——每接一个新渠道,在本文档追加一节。汇总各渠道的官方文档链接、pay 侧 provider 位置、接入形态概要、凭证存放位置指针。</p>
<div class="lead-box">
<b>公开性声明</b>:本文档<b>公开可读</b>,不含任何商户号 / API 密钥 / 私钥 / 证书 / 真实收款地址 / Bitwarden 具体取值。凭证一律只写「走 Bitwarden 某条目,经 rbw 灌 env」这类<b>位置指针</b>,实际值不出现在本文档或 git 仓库任何地方。
</div>
<h2>pay v2 Provider 接口</h2>
<p>每个渠道在 <code>internal/provider/&lt;name&gt;/</code> 下实现统一的 <code>provider.Provider</code> 接口(定义于 <code>internal/provider/provider.go</code>),网关侧不感知渠道差异:</p>
<table class="fields">
<thead><tr><th>方法</th><th>作用</th></tr></thead>
<tbody>
<tr><td>Method()</td><td>渠道注册名(如 <code>alipay</code>/<code>stripe</code>/<code>crypto</code>),也是配置里 <code>method</code> 字段的值</td></tr>
<tr><td>Capabilities()</td><td>渠道能力自述:支持的 render_type、是否支持退款/续订、结算币种、可服务区域</td></tr>
<tr><td>Create(ctx, CreateRequest)</td><td>拿 pay 已定金额/币种/账户去渠道建单,产出 <code>Session{ProviderRef, RenderType, Payload}</code></td></tr>
<tr><td>VerifyCallback(ctx, CallbackInput)</td><td>解析并验签渠道异步回调(HTTP body/头/query),产出归一化 <code>PaidEvent</code></td></tr>
<tr><td>Query(ctx, QueryRequest)</td><td>主动查单兜底(回调丢失时的定时轮询/对账)</td></tr>
</tbody>
</table>
<p>可选扩展接口:<code>RefundingProvider</code>(退款)、<code>RecurringProvider</code> / <code>SubscriptionProvider</code>(续订)、<code>OrphanScanner</code>(自托管渠道的孤儿到账扫描)。</p>
<p><b>render_type</b>(客户端只认这 6 种付款意图形态,不下发任何 UI,样式归业务方自绘):</p>
<table class="fields">
<thead><tr><th></th><th>含义</th><th>当前用者</th></tr></thead>
<tbody>
<tr><td>redirect</td><td>跳转到渠道收银台 URL</td><td>alipaypage/wap)、stripeCheckout</td></tr>
<tr><td>qr</td><td>渠道二维码内容,前端自行渲染</td><td>alipay(当面付 precreate</td></tr>
<tr><td>crypto_address</td><td>收款地址 + 精确到账金额</td><td>crypto</td></tr>
<tr><td>native_pay</td><td>原生支付组件拉起(如小程序/App SDK)</td><td>暂无渠道使用</td></tr>
<tr><td>sdk_handoff</td><td>交由客户端 SDK 处理支付流程</td><td>暂无渠道使用</td></tr>
<tr><td>iap_receipt</td><td>App Store/Play 内购收据校验</td><td>暂无渠道使用</td></tr>
</tbody>
</table>
<h2>渠道总览</h2>
<table class="fields">
<thead><tr><th>渠道</th><th>类型</th><th>结算币种</th><th>Method()</th><th>状态</th></tr></thead>
<tbody>
<tr><td>哪吒支付</td><td>第三方 RSA 聚合支付</td><td>CNY</td><td>nezha</td><td><span class="tag dev">开发中</span></td></tr>
<tr><td>支付宝</td><td>自有商户直连</td><td>CNY</td><td>alipay</td><td><span class="tag ok">已接入</span></td></tr>
<tr><td>Stripe</td><td>自有商户直连(海外网关)</td><td>USD</td><td>stripe</td><td><span class="tag ok">已接入</span></td></tr>
<tr><td>CryptoUSDT-TRC20</td><td>链上自托管</td><td>USDT</td><td>crypto</td><td><span class="tag ok">已接入</span></td></tr>
</tbody>
</table>
<h2>① 哪吒支付(nezha</h2>
<div class="card">
<h3>哪吒支付 <span class="tag dev">开发中</span></h3>
<div class="kv">
<dt>类型</dt><dd>第三方 RSA 聚合支付</dd>
<dt>结算币种</dt><dd>CNY</dd>
<dt>官方文档</dt><dd><a href="https://nzzf.org/doc/index.html" target="_blank" rel="noopener">https://nzzf.org/doc/index.html</a></dd>
<dt>provider 位置</dt><dd><code>internal/provider/nezha/</code>(待建)</dd>
<dt>Method() 名</dt><dd><code>nezha</code></dd>
<dt>凭证管理</dt><dd>Bitwarden 条目 <code>nzzf</code>(商户号 <code>pid</code> + 商户私钥 + 平台公钥),仅按需 <code>rbw get</code> 取用,不入文档/git</dd>
</div>
<p><b>接入形态概要</b>(据官方文档):</p>
<ul>
<li><b>签名</b><code>SHA256WithRSA</code> + Base64。签名串 = 非空参数(排除 <code>sign</code>/<code>sign_type</code>/数组)按参数名 ASCII 升序拼 <code>key=value&amp;...</code>(不拼密钥);商户私钥签、平台公钥验。</li>
<li><b>下单</b><code>POST https://nzzf.org/api/pay/create</code>form-urlencoded)。字段:<code>pid</code> / <code>type</code> / <code>out_trade_no</code> / <code>notify_url</code> / <code>return_url</code> / <code>name</code> / <code>money</code>(元)/ <code>timestamp</code> / <code>sign</code> / <code>sign_type</code>;响应 JSON 含 <code>payurl</code>(收银台跳转,对应 render_type=<code>redirect</code>+ <code>qrcode</code>(二维码,对应 <code>qr</code>)。</li>
<li><b>异步通知</b>GET 回调,字段含 <code>trade_no</code> / <code>out_trade_no</code> / <code>money</code> / <code>trade_status</code> / <code>sign</code>;平台公钥验签;<code>trade_status=TRADE_SUCCESS</code> 为成功;商户须回纯文本 <code>success</code>(对应 <code>VerifyCallback</code>)。</li>
<li><b>查单</b><code>POST https://nzzf.org/api/pay/query</code><code>status=1</code> 表示已支付(对应 <code>Query</code>)。</li>
<li><b>支持的支付方式</b>:支付宝等(由 <code>type=alipay</code> 等参数选择,聚合渠道内部路由)。</li>
</ul>
</div>
<h2>② 支付宝(alipay</h2>
<div class="card">
<h3>支付宝 <span class="tag ok">已接入</span></h3>
<div class="kv">
<dt>类型</dt><dd>自有商户直连</dd>
<dt>结算币种</dt><dd>CNY</dd>
<dt>官方文档</dt><dd><a href="https://opendocs.alipay.com/" target="_blank" rel="noopener">https://opendocs.alipay.com/</a>(当面付 / 手机网站支付 / 电脑网站支付)</dd>
<dt>provider 位置</dt><dd><code>internal/provider/alipay/alipay.go</code></dd>
<dt>Method() 名</dt><dd><code>alipay</code></dd>
<dt>凭证管理</dt><dd>Bitwarden 条目 <code>ali pay key</code>(字段 <code>app_private_key</code> / <code>alipay_public_key</code> / <code>appid</code>),经 <code>rbw get</code> 灌服务器 env(见 <a href="./上线状态与部署Runbook.html">上线状态与部署 Runbook</a></dd>
</div>
<p><b>接入形态概要</b>(源自 <code>internal/provider/alipay/alipay.go</code> + <a href="./支付宝密钥申请与备案依赖.html">支付宝密钥申请与备案依赖</a> / <a href="./支付宝收款页时序图与架构.html">支付宝收款页时序图与架构</a>):</p>
<ul>
<li><b>签名</b>RSA2<code>SHA256WithRSA</code>),商户应用私钥签、支付宝公钥验(<code>smartwalle/alipay/v3</code> SDK 封装)。</li>
<li><b>下单</b>:按 <code>metadata["render"]</code> 与端型三选一——当面付 <code>alipay.trade.precreate</code><code>render=qr</code>,出二维码,render_type=<code>qr</code>);手机端 <code>alipay.trade.wap.pay</code><code>TradeWapPay</code><code>QUICK_WAP_WAY</code>);PC 端 <code>alipay.trade.page.pay</code><code>TradePagePay</code><code>FAST_INSTANT_TRADE_PAY</code>),两者均产出跳转 URLrender_type=<code>redirect</code>)。渠道以 <code>out_trade_no</code> 归位,仅支持 CNY。</li>
<li><b>异步通知</b><code>DecodeNotification</code> 用支付宝公钥验签;交易状态归一化——<code>WAIT_BUYER_PAY</code>→pending<code>TRADE_SUCCESS</code>/<code>TRADE_FINISHED</code>→succeeded<code>TRADE_CLOSED</code>→failed;商户须回 <code>success</code></li>
<li><b>查单兜底</b><code>alipay.trade.query</code><code>TradeQuery</code>),用于回调丢失/超时时的定时轮询。</li>
<li><b>退款</b><code>alipay.trade.refund</code>(同步接口,<code>TradeRefund</code>),<code>out_request_no</code> 作幂等键支持部分退款。</li>
</ul>
</div>
<h2>③ Stripestripe</h2>
<div class="card">
<h3>Stripe <span class="tag ok">已接入</span></h3>
<div class="kv">
<dt>类型</dt><dd>自有商户直连(海外网关)</dd>
<dt>结算币种</dt><dd>USD</dd>
<dt>官方文档</dt><dd><a href="https://docs.stripe.com/" target="_blank" rel="noopener">https://docs.stripe.com/</a>Checkout Sessions / Webhooks / Subscriptions</dd>
<dt>provider 位置</dt><dd><code>internal/provider/stripe/stripe.go</code></dd>
<dt>Method() 名</dt><dd><code>stripe</code></dd>
<dt>凭证管理</dt><dd>BitwardenStripe API Secret Key + Webhook Signing Secret,账户级 <code>CredentialEnvPrefix</code> 对应 env 后缀 <code>SECRET_KEY</code> / <code>WEBHOOK_SECRET</code>),经 rbw 灌 env;具体条目名待生产上线时在 Bitwarden 建立,不写入本文档</dd>
</div>
<p><b>接入形态概要</b>:托管 Checkouthosted Checkout Session),一次性支付走 <code>mode=payment</code>、订阅走 <code>mode=subscription</code>,均产出跳转 URLrender_type=<code>redirect</code>)。Webhook 验签用官方 SDK <code>webhook.ConstructEvent</code>HMAC + 时间容差);处理 <code>checkout.session.completed</code>(入账/订阅激活)、<code>invoice.paid</code>(续费)、<code>customer.subscription.deleted</code>(订阅取消)、<code>invoice.payment_failed</code>(逾期)、<code>charge.dispute.created</code>(拒付)。退款走 <code>/v1/refunds</code><code>Idempotency-Key</code> 幂等);订阅续费由 Stripe 网关自身调度(<code>RecurringKindGatewayScheduled</code>),pay 不主动发起扣款。</p>
</div>
<h2>④ Crypto · USDT-TRC20crypto</h2>
<div class="card">
<h3>CryptoUSDT-TRC20 <span class="tag ok">已接入</span></h3>
<div class="kv">
<dt>类型</dt><dd>链上自托管</dd>
<dt>结算币种</dt><dd>USDT</dd>
<dt>官方文档</dt><dd><a href="https://developers.tron.network/" target="_blank" rel="noopener">https://developers.tron.network/</a>TronGrid API,用于扫链查询)</dd>
<dt>provider 位置</dt><dd><code>internal/provider/crypto/crypto.go</code></dd>
<dt>Method() 名</dt><dd><code>crypto</code></dd>
<dt>凭证管理</dt><dd>Bitwarden(收款地址 watch-only + 可选 TronGrid API Key,账户级 env 后缀 <code>ADDRESS</code> / <code>TRONGRID_KEY</code>),私钥不由 pay 持有;具体条目名不写入本文档</dd>
</div>
<p><b>接入形态概要</b>:每账户一个固定收款地址(watch-only,pay 从不持有私钥),每单在基础价上叠加随机唯一尾数(微 USDT 级)区分订单身份,冷却窗防金额复用误配。无渠道异步回调(<code>VerifyCallback</code> 恒返回 <code>ErrNotSupported</code>),到账完全靠<b>查单</b>:定时扫 TronGrid <code>only_confirmed</code> 已确认交易,按「收款地址 + 精确金额 + 块时晚于建单时间」匹配(render_type=<code>crypto_address</code>)。另实现 <code>OrphanScanner</code>:扫描到账但不匹配任何在途订单的转账,供对账人工核对。资金归集(sweep 到冷存储)是独立的线下操作,不在 pay 范围内。</p>
</div>
<h2>凭证管理约定</h2>
<p class="stub">所有渠道凭证统一走 <b>Bitwarden</b>,按需 <code>rbw get</code> 取出灌入服务器环境变量(<code>CredentialEnvPrefix</code> + 字段名,如 <code>ALIPAY_APP_PRIVATE_KEY</code>),不落配置文件、不落 git 仓库、不进聊天记录。已上线渠道的具体 Bitwarden 条目名见各渠道小节;未上线渠道的条目在生产接入前另建,同样不预先写入本文档。部署细节参见 <a href="./上线状态与部署Runbook.html">上线状态与部署 Runbook</a></p>
</div>
</body>
</html>