docs: pay v2 P2(收款管线)+ P7(codes 共享库)实现计划
P2(pay 仓,7 任务):Provider 抽象/render_type + 收款管线 + 统一入账 + webhook v2。 P7(新仓 ~/code/codes,8 任务):零依赖 codes 内核 + Redeem[T] 泛型事务骨架。 并行起草(均只依赖已完成 P1),登记 index。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -57,11 +57,21 @@
|
||||
<div class="desc">阅读版;执行真相源 <code>docs/superpowers/plans/2026-07-10-pay-v2-p1-core-model.md</code>(含 checkbox)。<a href="./pay-v2-unified-gateway-design.html">pay v2 设计</a>的首个落地阶段(8 阶段之 P1)。4 个 TDD 任务:money 包(int64 最小单位+币种)→ Order/Attempt/Account/Refund 模型+状态机+<code>:memory:</code> 测试约定 → OrderStore(幂等标付/取消/列表,条件UPDATE+RowsAffected)→ 账户配置注册表(凭证走 env)。沿用 GORM AutoMigrate/glebarez sqlite 惯例;金额从 string 元改 int64。P2-P8(Provider/收款管线/渠道 adapter/退款/路由/对账/codes 库/订阅)各自成计划、落地前细化。</div>
|
||||
<div class="meta">P1 · 2026-07-10 · Go(Gin+GORM+sqlite) · 待执行</div>
|
||||
</a>
|
||||
<a class="doc" href="./superpowers/plans/2026-07-10-pay-v2-p2-pipeline.md">
|
||||
<div class="title">pay v2 · P2 收款管线 + Provider 抽象 + webhook v2(计划)</div>
|
||||
<div class="desc">7 个 TDD 任务:Provider 接口(create/verify_callback/query)+6 render_type+PaidEvent+注册表 → fake provider → OrderStore 扩展 → 一次性收款管线 gateway → 统一入账/开通(复用 P1 MarkAttemptPaid 幂等)→ webhook v2 outbox+Notifier(event_type+HMAC)→ HTTP /v1 接线。复用 P1 全部类型、免 docker。/v1 与 v1 /api/v1 并存。</div>
|
||||
<div class="meta">P2 · 2026-07-10 · 待执行 · 真相源 .md</div>
|
||||
</a>
|
||||
<a class="doc" href="./pay-v2-roadmap-dag.html">
|
||||
<div class="title">pay v2 · P2-P8 依赖 DAG 与并行执行</div>
|
||||
<div class="desc">P1 已完成。P2-P8 的依赖关系图(SVG DAG)+ 关键路径 P2→P3→P4/P6 + 波次(P7 全程并行/P5∥P3/P4∥P6)+ 同仓并行的真实边界(先计划后实现、worktree 隔离防冲突)。</div>
|
||||
<div class="meta">roadmap · 2026-07-10 · 8 阶段依赖分析</div>
|
||||
</a>
|
||||
<a class="doc" href="./pay-v2-p7-codes-lib.html">
|
||||
<div class="title">pay v2 · P7 codes 共享库(激活码兑换内核)(阅读版)</div>
|
||||
<div class="desc">阅读版;执行真相源 <code>docs/superpowers/plans/2026-07-10-pay-v2-p7-codes-lib.md</code>(含 checkbox)。把 pangolin 现有码逻辑抽成独立仓库 <code>github.com/wangjia/codes</code>(与 pay 不同部署,方案 A:共享库嵌入各产品,不改 pay/pangolin/jiu 现有代码)。8 个 TDD 任务:通用权益描述符 Entitlement(duration/quota,不硬编码 plan+天数)→ Crockford Base32 生成器(移植 pangolin idgen)→ Dialect+内嵌 mysql/sqlite migrations+Store CRUD → Mint 批次生成 → <b>Redeem[T] 兑换事务骨架</b>(宿主本地事务+锁+CAS+幂等+grant 回调)→ RateLimiter/NonceChecker 接口+GuardedRedeem → redisx 可选子包(唯一 import Redis 处)→ webhook 灌码(pay-contract 式通用 HMAC 签名)。核心包零第三方依赖。</div>
|
||||
<div class="meta">P7 · 2026-07-10 · Go(stdlib database/sql,可选 redis) · 待执行</div>
|
||||
</a>
|
||||
<p class="empty">运行与联调步骤见仓库根 README.md</p>
|
||||
|
||||
<h2>🔧 排障 Runbook</h2>
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>pay v2 · P7 codes 共享库(阅读版)</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}
|
||||
.wrap{max-width:960px;margin:0 auto;padding:44px 24px 96px}
|
||||
.back{display:inline-block;margin-bottom:18px;font-size:13px}
|
||||
a{color:var(--accent);text-decoration:none}
|
||||
h1{font-size:27px;margin:6px 0 8px}
|
||||
.lead{color:var(--fg-soft);margin:0 0 22px}
|
||||
h2{font-size:18px;margin:34px 0 10px;color:var(--accent);border-bottom:1px solid var(--border);padding-bottom:8px}
|
||||
h3{font-size:15px;margin:20px 0 8px;color:var(--fg)}
|
||||
code{font-family:var(--mono);font-size:.85em;background:var(--card-2);padding:1px 6px;border-radius:5px;color:#cdd9e5}
|
||||
b{color:#fff}
|
||||
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:13px}
|
||||
table.fields td:first-child{font-family:var(--mono);font-size:12px;color:#cdd9e5;white-space:nowrap;width:1%}
|
||||
.card{background:var(--card);border:1px solid var(--border);border-radius:var(--radius);padding:14px 18px;margin:12px 0}
|
||||
.card h3{margin:0 0 6px;color:var(--accent);font-size:15px}
|
||||
.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}
|
||||
.tag{display:inline-block;font-size:12px;font-weight:600;padding:2px 9px;border-radius:999px}
|
||||
.tag.ok{background:rgba(63,185,80,.16);color:var(--ok)}
|
||||
.tag.info{background:rgba(88,166,255,.16);color:var(--accent)}
|
||||
.tag.warn{background:rgba(210,153,34,.16);color:var(--warn)}
|
||||
.tag.bad{background:rgba(248,81,73,.16);color:var(--bad)}
|
||||
.path{color:var(--muted);font-size:12px;font-family:var(--mono);margin-top:6px}
|
||||
ul,ol{padding-left:22px;margin:8px 0}
|
||||
li{margin:5px 0}
|
||||
.small{color:var(--muted);font-size:13px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<a class="back" href="index.html">← 返回文档索引</a>
|
||||
<h1>pay v2 · P7 codes 共享库(激活码兑换内核)<span style="font-size:14px"> (阅读版)</span></h1>
|
||||
<p class="lead">执行真相源 <code>docs/superpowers/plans/2026-07-10-pay-v2-p7-codes-lib.md</code>(含 <code>- [ ]</code> checkbox)。设计见 <a href="pay-v2-unified-gateway-design.html">pay v2 统一支付网关设计</a> §9/§9.1/§9.2/§12。参考实现:<code>pangolin/server/internal/codes/</code>(只读,移植 canonical 部分)。</p>
|
||||
|
||||
<div class="lead-box">
|
||||
<b>P7 目标</b>:把 pangolin 现有码逻辑(哈希存储/状态机/生成器/兑换事务/webhook 灌码)抽成一个与 pay <b>不同部署、不同仓库</b>的独立 Go 共享库,供 pangolin(订阅)/jiu(门店 license)/未来 dudu(额度)等产品各自 import 嵌入。库 <b>entitlement-agnostic</b>——码携带通用「权益描述符」而非硬编码 plan+天数;兑换的"最终开通落库"通过宿主注入的回调函数完成,兑换是<b>宿主本地事务</b>(码表与宿主业务表同库)。本计划<b>只搭这个新仓库</b>,不改 pay/pangolin/jiu 现有代码。
|
||||
</div>
|
||||
|
||||
<h2>部署与模块选型(已决策)</h2>
|
||||
<table class="fields">
|
||||
<thead><tr><th>选项</th><th>结论</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>pay 仓内 <code>pkg/codes</code></td><td><span class="tag bad">不采用</span> —— pay 依赖很重(gin/GORM/alipay/wechatpay-go),codes 与支付管线正交,放进 pay 逻辑/依赖两维度都错</td></tr>
|
||||
<tr><td>pangolin 仓内保留、jiu 抄一份</td><td><span class="tag bad">不采用</span> —— 违反"共享库"目标,退化两份漂移代码</td></tr>
|
||||
<tr><td><b>独立仓库 + 独立 module</b></td><td><span class="tag ok">已定</span> —— 新仓 <code>~/code/codes</code>,module <code>github.com/wangjia/codes</code>;核心包零第三方依赖(仅 stdlib),可选 Redis 能力隔离进 <code>codes/redisx</code> 子包</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="small">落地约定:源码 <code>~/code/codes</code>,remote <code>ssh://git@git.51yanmei.com:2222/wangjia/codes.git</code>(需先在 Gitea 建仓)。pangolin/jiu 迁移到 import 本库是<b>后续独立工作</b>,不在本计划内。</p>
|
||||
|
||||
<h2>8 个 TDD 任务</h2>
|
||||
<div class="card"><h3>Task 1 · 模块脚手架 + Entitlement + 状态机</h3><code>Entitlement{Kind,Payload}</code> 通用权益描述符(<code>duration</code>={plan,days} / <code>quota</code>={resource,amount}),替代硬编码 <code>plan_id+duration_days</code>;<code>Status</code> 三态 unused/redeemed/void;哨兵错误。</div>
|
||||
<div class="card"><h3>Task 2 · Crockford Base32 生成器</h3>移植 pangolin <code>internal/idgen</code> 的 Crockford 部分(15 数据字符+1 mod-37 校验字符,<code>crypto/rand</code> 防偏抽样),根包薄封装 <code>GenerateCode/Canonicalize/Hash</code>。</div>
|
||||
<div class="card"><h3>Task 3 · Dialect + 内嵌 migrations + Store CRUD</h3>mysql/sqlite 双 migration(<code>embed.FS</code> + 零依赖 <code>ApplyMigrations</code>,也可接 golang-migrate iofs);<code>Store</code> 的 <code>CreateBatch/CreateCode/FindByHash(ForUpdate)/MarkRedeemed/Void/WriteAudit</code>,哈希唯一约束防重码。</div>
|
||||
<div class="card"><h3>Task 4 · Mint 批次生成</h3>碰撞重试(移植 pangolin <code>CreateBatch</code> 逻辑),明文码<b>只在返回值出现一次</b>,从不落库/落日志。</div>
|
||||
<div class="card"><h3>Task 5 · Redeem[T] 兑换事务骨架(核心)</h3><code>Redeem[T any](ctx, store, tx, codeHash, redeemerRef, grant GrantFunc[T])</code>——宿主开事务传入、锁(<code>FindByHashForUpdate</code>+dialect)+CAS(<code>MarkRedeemed</code> 条件 UPDATE)+幂等(同 redeemerRef 短路)+审计,grant 回调在<b>同一 tx</b> 内执行宿主的权益写入,失败整体回滚——落地"码表与宿主表同库=本地事务"的方案 A 前提。另有 <code>VoidCode</code>。</div>
|
||||
<div class="card"><h3>Task 6 · RateLimiter/NonceChecker 接口 + GuardedRedeem</h3>零依赖默认实现(<code>NoopRateLimiter</code>/<code>InMemoryNonceChecker</code>);<code>GuardedRedeem[T]</code> 包一层失败锁定,失败计数、成功清零。</div>
|
||||
<div class="card"><h3>Task 7 · redisx 子包(可选)</h3>唯一 import Redis 的地方——不引用就不产生依赖。移植 pangolin 的失败计数器+TTL 锁定、<code>SET NX</code> 原子去重,miniredis 测试免 docker。</div>
|
||||
<div class="card"><h3>Task 8 · webhook 灌码</h3>通用 HMAC 签名(改用 pay-contract 既有 <code>system+timestamp+nonce+body</code> 一并入 MAC 的双向签名惯例,而非 pangolin 原版 body-only HMAC)+ 去重 + 通用权益负载 <code>MintPayload</code> → 调 <code>Mint</code>。</div>
|
||||
|
||||
<h2>关键设计决策</h2>
|
||||
<table class="fields">
|
||||
<thead><tr><th>决策</th><th>说明</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>同库本地事务</td><td><code>Redeem[T]</code> 接收宿主已开的 <code>*sql.Tx</code>——码状态翻转与宿主 grant 回调写同一事务,要求宿主权益表与 codes 表在同一个 <code>*sql.DB</code> 下(设计文档 §9.1 方案 A 前提)</td></tr>
|
||||
<tr><td>通用权益描述符</td><td><code>Entitlement{Kind,Payload}</code> 库只做信封校验,从不解释业务字段——宿主 <code>GrantFunc</code> 才解释,对应设计文档 §12 可扩展性验证</td></tr>
|
||||
<tr><td>Redis 可选</td><td>核心包零 Redis 依赖;<code>codes/redisx</code> 子包才 import go-redis,不 import 就不产生依赖</td></tr>
|
||||
<tr><td>webhook 签名升级</td><td>从 pangolin 的 body-only HMAC 改为 pay-contract 的 system+timestamp+nonce+body 一并入 MAC,防头部篡改,统一多产品 webhook 验签心智模型</td></tr>
|
||||
<tr><td>redeemerRef 不透明</td><td>字符串(<code>"user:123"</code>/<code>"shop:9"</code>),库不关心归属维度——设计文档 §9.1"归属维度留给各产品"的落地</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>范围之外(有意排除)</h2>
|
||||
<ul>
|
||||
<li>admin 批次列表/CSV 导出(pangolin 已有 <code>admin_support.go</code>/<code>export.go</code>,宿主可自行在 <code>Store</code> 基础方法上拼)</li>
|
||||
<li>pangolin/jiu 迁移到 import 本库(独立后续任务,brain todo)</li>
|
||||
<li>独立服务化方案 B、reseller 门户、优惠券变体(设计文档标注 later)</li>
|
||||
</ul>
|
||||
|
||||
<p class="path">相关:<a href="pay-v2-unified-gateway-design.html">pay v2 设计</a> · <a href="pay-v2-p1-plan.html">P1 核心数据模型</a> · <a href="pay-v2-roadmap-dag.html">P2-P8 依赖 DAG</a> · 真相源 docs/superpowers/plans/2026-07-10-pay-v2-p7-codes-lib.md</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user