102 Commits

Author SHA1 Message Date
wangjia 0912027c69 docs(v2): P4 退款 / P6 对账守护 / P8 订阅拒付 三计划(P6 收编全部路由欠账)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 16:22:44 +08:00
wangjia 5e83e104fd docs(v2): 执行总览标记 P3/P5 完成 + pangolin 集成验证状态
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 15:33:20 +08:00
wangjia c4fc962db2 chore(v2): P3 终审收尾——GetReserved 移 export_test / BuildRegistry 表测试 / 坏签名负路径 / 注释更正 2026-07-10 15:32:10 +08:00
wangjia c933657350 feat(v2): 配置驱动注册三渠道 adapter + crypto 真 adapter 全链 e2e(下单→查单→入账→webhook) 2026-07-10 15:17:43 +08:00
wangjia 79b9111a99 feat(v2): stripe adapter — Checkout Session 跳转 + webhook 验签(stripe-go/v79 pin) 2026-07-10 15:07:10 +08:00
wangjia d3050d25d8 feat(v2): alipay adapter — 迁移 v1 验签/查单,render_type=redirect(page/wap) 2026-07-10 14:53:03 +08:00
wangjia 527de1c93e fix(v2): crypto 预留键改按地址(对齐链上匹配维度)——防共享地址跨账户同额碰撞
- allocateAmount 中解析地址后改用 address+'/'+amount 作为预留键,而非 accountID
- 同一地址的不同账户不能同时占用同金额,链上唯一性保证对齐 Query 的 to==addr 维度
- 新增 GetReserved() 测试方法暴露预留表副本,验证键格式
- 新增 TestReservationKeyByAddress 测试:两个账户共享地址,验证预留键基于地址

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 14:43:21 +08:00
wangjia fbd3bd6f36 feat(v2): crypto adapter — 移植 pangolin-pay 单地址+唯一金额模型(only_confirmed 扫链,VerifyCallback 不适用) 2026-07-10 14:33:21 +08:00
wangjia 98b95dde19 fix(v2): 币种错误映射 409/503(不再误导 500 重试)+ DailyLimit 量纲注释
- handler: ErrCurrencyMismatch → 409, ErrNoSettleCurrency → 503
- test: TestV2RetryCurrencyMismatch409 验证重试不同币种渠道返回 409
- config: DailyLimit 添加量纲注释(minor units)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 14:27:39 +08:00
wangjia 585133532c feat(v2): 多币种 product(ProductPrice 分币价目 + v1 元回退)+ 结算币种由渠道能力驱动
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 14:20:12 +08:00
wangjia abf6a43b8c docs(v2): P2 计划 settle 代码段同步 T5 修复(瞬时读库错误→SettleFailed)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 14:14:01 +08:00
wangjia 7a27a6223a feat(v2): order 补 biz_code + webhook payload 带 product_biz_code(收口 P2 延后项) 2026-07-10 14:12:44 +08:00
wangjia c06f6d3a3c feat(v2): gateway 经 Picker 路由选账户 + retry 换号(ExcludeAccounts);替换首个 enabled 2026-07-10 14:02:33 +08:00
wangjia 381288e707 docs(v2): Router.Pick 注明 weighted 桶序绑定 config 账户声明顺序(reorder=repin) 2026-07-10 13:56:03 +08:00
wangjia bd50a0d561 feat(v2): limit_aware 策略(UsageSource 留位)+ Router 按配置派发 + config.Routing 2026-07-10 13:55:16 +08:00
wangjia 875093dff2 fix(v2): weighted 零权重测试改确定性断言(去抽样偏置)——套件回绿
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 13:50:16 +08:00
wangjia 689de61e94 feat(v2): weighted 路由策略(按 Weight,hash(OutTradeNo) 确定性) 2026-07-10 13:46:05 +08:00
wangjia 1fd86bc1b3 docs(v2): P3 渠道 adapter 计划(crypto 按 pangolin-pay 真源回校:单地址+唯一金额+预留冷却;alipay 迁移;stripe;biz_code/多币种收口)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 13:41:08 +08:00
wangjia d78945b09a feat(v2): 账户路由 Picker/Strategy 接口 + round_robin 策略(替换首个 enabled) 2026-07-10 13:36:31 +08:00
wangjia 9d3b3d8ed0 docs(v2): P5 多账户路由策略计划(4 任务:Picker/round_robin/weighted/limit_aware骨架+gateway接线)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 13:34:45 +08:00
wangjia 13815f1313 docs(v2): 执行总览标记 P2/P7 完成(2026-07-10 执行波收官)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 12:55:38 +08:00
wangjia 764ed558a1 chore(v2): 终审收尾——settle-sync 非常态结果落日志 + Retry 补 body 上限
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 11:51:33 +08:00
wangjia 6cbaa59db7 fix(v2): 回调按 SettleResult 分终态/可重试——amount_mismatch 回 200 停投,不再无限重投 2026-07-10 11:44:14 +08:00
wangjia a633b2ac0a feat(v2): /api/v2 网关路由 + handler(下单/查/重试/取消/回调)+ main 装配 gateway/notifier 2026-07-10 11:36:47 +08:00
wangjia 63af44bfe1 fix(v2): webhook 截断 UTF-8 安全 + 投递门禁查库失败落日志(与未付区分)
- MarkFailed: truncateUTF8 避免截断中点 UTF-8 rune, last_error 安全 <=255B
- notifier truncate: 同上, 投递失败消息截断 UTF-8 安全
- deliverOne: 拆分 orderPaid 错误分支 — DB 错误落日志 [webhook] 投递门禁查单失败,与"订单未付"(静默)区分
- test: MarkFailed with long Chinese string, 验证 stored last_error 为有效 UTF-8 且 <=255B

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 11:31:16 +08:00
wangjia 3f5e44581f feat(v2): webhook v2 outbox + Notifier(event_type + HMAC 双向 + 重试兜底) 2026-07-10 11:25:14 +08:00
wangjia 0877b01874 fix(v2): settle 定位尝试的瞬时读库错误归 SettleFailed(可重试),不再误标 not_found
AttemptByProviderRef 返回非哨兵错误(如 DB 抖动)时原先与"查无此单"终态
一并标 SettleNotFound;调用方按结果值决定 ack,会把已付订单永久丢弃。
非哨兵分支改判 SettleFailed(可重试,渠道会重投),errors.Is(ErrAttemptNotFound)
分支维持 SettleNotFound 不变。新增 TestSettleTransientReadErrorIsFailed 通过
关闭底层连接强制产出瞬时读库错误,验证结果与语义。
2026-07-10 11:20:53 +08:00
wangjia 68d162d6c4 feat(v2): 入账管线 Settle/HandleCallback/SyncPending(归一→定位→幂等→核对→标付→入队) 2026-07-10 11:15:10 +08:00
wangjia cd4782f11c docs(v2): P6 范围补订单级过期清理(兜 Task4 孤儿 pending 单 plan-mandated 缺口)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 11:13:01 +08:00
wangjia 0ab0713161 feat(v2): 一次性收款管线 CreateOrder/GetOrder/Retry/Cancel + DBProductResolver 2026-07-10 11:08:03 +08:00
wangjia 31e354e31c feat(v2): OrderStore 扩展 GetOrder/AttemptByProviderRef/ListByStatus/ExpirePending
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 11:01:42 +08:00
wangjia 20ec7b4942 feat(v2): fake provider(确定性,验证收款管线;真渠道 P3) 2026-07-10 10:57:53 +08:00
wangjia 7d05c2f655 feat(v2): Provider 渠道抽象接口 + 6 render_type + PaidEvent + 注册表 2026-07-10 10:54:22 +08:00
wangjia 85ce5aa639 docs(v2): P2 计划折入评审修订 — D3 路由统一 /api/v2;D4 settle 崩溃安全顺序(先入队后翻转+投递门禁)/ QueryRequest 上下文 / PaidEvent.PaidAt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-10 10:20:42 +08:00
wangjia add0d26963 docs: pay v2 执行总览与评审页(单页汇总 P1状态/DAG/P2·P7计划/决策点)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:55:35 +08:00
wangjia d1fd0abd73 docs: P2 计划 HTML 阅读版 + index 改指
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:52:14 +08:00
wangjia a4ae5fcb64 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>
2026-07-10 09:26:32 +08:00
wangjia d225067f29 docs: pay v2 P2-P8 依赖 DAG + 并行执行边界分析
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:08:32 +08:00
wangjia af15f4736e fix(v2): MarkAttemptPaid 按单scope+校验attempt命中 + OpenTestDB 唯一库名
- attempt UPDATE 加 out_trade_no 过滤并检查 RowsAffected,防止 order 已翻
  paid 但 providerRef 错配导致没有对应 attempt 被标记的账本背离(未命中
  时返回 error 触发事务回滚,order 保持 pending)
- 补测试覆盖 providerRef 错配场景:断言返回 error 且回滚后订单仍 pending
- OpenTestDB 改为每次调用生成唯一内存库名(atomic 计数器),避免同包多
  测试共享 file::memory:?cache=shared 导致数据串扰

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 08:35:22 +08:00
wangjia 5aecb18d7c feat(v2): 账户配置注册表(config.accounts + env 取凭证 + 渠道/区域筛选) 2026-07-10 08:25:01 +08:00
wangjia 6a53d7e008 feat(v2): OrderStore 建单/建尝试/幂等标付/取消/列表(条件UPDATE+RowsAffected) 2026-07-10 08:21:02 +08:00
wangjia 01887dd571 feat(v2): Order/Attempt/Account/Refund 模型 + 状态枚举 + :memory: 测试约定
v2 OrderStatus 三个值(Pending/Paid/Refunded)与 v1 model.OrderStatus 撞名,
统一加 V2 后缀(OrderPendingV2 等)规避包内重名;AttemptStatus/RefundStatus
无此冲突,原样保留。
2026-07-10 08:14:27 +08:00
wangjia 7da15c00a1 fix(v2): money 去 float 进制 + 溢出防护 + 补 Format/负数/溢出测试
- money.go: 用纯 int64 循环乘法替代 int64(math.Pow10(exp)) 计算进制因子
- Parse: whole*mult+frac 前加溢出检查(math.MaxInt64 仅作整型常量参与比较,无 float 参与)
- money_test.go: 补 Format 未知币种错误分支(errors.Is ErrUnknownCurrency)、
  Parse 溢出(整数部分过大)、负数往返测试
2026-07-10 08:10:36 +08:00
wangjia 8555368785 feat(v2): money 包 int64 最小单位 + 币种码(CNY/USD/USDT) 2026-07-10 08:05:14 +08:00
wangjia 2383b9d593 docs: pay v2 P1 实现计划(核心数据模型+账户注册表)
4 TDD 任务:money(int64 最小单位+币种) / Order·Attempt·Account·Refund 模型+状态机
+:memory: 测试约定 / OrderStore(幂等标付/取消/列表) / 账户配置注册表(env 凭证)。
沿用 GORM AutoMigrate + glebarez sqlite。HTML 阅读版 + 登记 index + 与设计文档互链。
P2-P8 各自成计划。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 07:56:26 +08:00
wangjia 61a049d977 docs: pay v2 多产品统一支付+履约网关设计(spec)
Order+Attempt+Account+Refund · 6 render_type(数据不下发UI,样式归业务) · Provider
adapter(verify_callback 吸收渠道差异) · webhook v2 event_type(退款/订阅/拒付) ·
退款三向 · 促销折扣归业务 · 多币种+配置驱动渠道+国内外双部署 · codes 共享库嵌入各产品(A) ·
购买激活码履约多态 · recurring 设计进暂不实现 · 流量包可扩展性验证。含 SVG 架构图。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 07:46:04 +08:00
wangjia 6ed1f68a57 feat(seed): 套餐名带品牌前缀短句式——支付宝收银台 subject 不再截断
收银台/付款盘由支付宝渲染,商户仅 subject 一行可控(2026-07-04 用户
反馈信息太少):Name 改为「岩美酒库·首月特惠30天」等 ≤11 汉字短句,
seed 幂等 upsert 重启即更新线上套餐名

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 22:49:37 +08:00
wangjia dbdadd1b10 feat(order): 下单支持显式 client_type 端型(契约 v1.1.0)
resolveIsMobile:mobile/pc 显式优先,空值回退 isMobileUA(请求 UA),非法 400;
修复业务方服务端下单被 Go client UA 误判为 PC、手机用户拿不到 wap 拉起页

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
2026-07-04 10:28:17 +08:00
wangjia e0c7baa7cb fix(sec): 安全评审加固——goroutine panic 恢复 + 下单 body 上限 + 错误脱敏
- 后台 goroutine (notifyBiz/SyncPending) 加 defer recover,防 panic 拖垮进程
- 下单请求体 64KB 上限 (MaxBytesReader),防超大 body 内存 DoS
- 下单错误不再回传原始 err.Error(),内部详情只进日志
- (另: pay nginx 加 per-IP 限流 limit_req,服务器侧配置)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UQmqWmV67sXGLrb3U1XXn
v1.0.1
2026-07-03 23:53:11 +08:00
wangjia ef0fa39ce4 chore: 加一键发布脚本 deploy.sh(编译→传ali→备份→重启→探活,失败自动回滚)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UQmqWmV67sXGLrb3U1XXn
2026-07-03 22:49:52 +08:00