执行真相源 docs/superpowers/plans/2026-07-10-pay-v2-p2-pipeline.md(含 checkbox + 每步完整代码/测试)。设计见 pay v2 设计,DAG 见 依赖图。
event_type)。用一个 fake provider 端到端验证管线,真渠道在 P3。全程复用 P1、免 docker。
文件 internal/provider/provider.go。核心契约,P3 所有渠道 adapter 依赖它:
type Provider interface { Method() string Capabilities() Capabilities Create(ctx, CreateRequest) (*Session, error) VerifyCallback(ctx, CallbackInput) (*PaidEvent, error) Query(ctx, providerRef string) (*PaidEvent, error) } // 可选子接口:RefundingProvider(P4)、RecurringProvider(P8) type Session struct { ProviderRef string; RenderType string; Payload map[string]any; ExpiresAt *time.Time } type PaidEvent struct { ProviderRef string; Status PaidStatus; PaidAmountMinor int64; PaidCurrency, Raw string }
6 个 render_type 常量 + PaidPending/Succeeded/Failed;Registry(Register/Get/Methods + ErrUnknownMethod)。
internal/provider/fake/fake.go —— 实现 Provider,Method()=="fake"、render=crypto_address;带 SetQueryResult 测试 seam。P2 用它端到端验管线,不接真渠道。
internal/store/order_query.go:GetOrder / AttemptByProviderRef / ListAttemptsByStatus / ExpirePendingAttempts + 哨兵错误。为管线定位订单/尝试用。
internal/gateway/gateway.go:CreateOrder(选 provider→create→落 Order+Attempt→返回 {order_no, session:{render_type,payload}})/ GetOrder / RetryOrder / CancelOrder。接口 ProductResolver.Resolve(sku)(取权威价/币种/subject/bizCode)、WebhookEnqueuer.Enqueue(...)。
internal/gateway/settle.go:Settle(ctx, *PaidEvent) = 归一 → 定位(provider_ref→attempt→order)→ 币种/金额核对 → 复用 P1 MarkAttemptPaid 幂等 → 仅翻转时入队 payment.succeeded。HandleCallback(webhook 入口)、SyncPendingAttempts(query 兜底)都汇入这条。SettleResult:ignored/not_found/amount_mismatch/duplicate/processed。
model.WebhookDelivery(uniqueIndex(out_trade_no,event_type) 幂等)+ store.WebhookStore(Enqueue/ListUndelivered/MarkDelivered/MarkFailed)+ webhook.Notifier(DeliverPending/Start,HMAC 头 X-Pay-System/Event/Timestamp/Nonce/Sign,失败重试)。event_type 从 payment.succeeded 起。
router.SetupV2 挂 POST /v1/orders、GET /v1/orders/:no、.../retry、.../cancel、POST /v1/callback/:method;main.go 装配 provider 注册表 + gateway + notifier,AutoMigrate 加 WebhookDelivery。
| 取舍 | 说明 |
|---|---|
| fake provider_ref | 需纳秒唯一化——P1 Attempt 有 uniqueIndex(channel,provider_ref),retry 同 method 会撞键(真渠道天然不同 ref) |
| webhook 无 biz_code | P1 OrderV2 无 biz_code 列,不改已落地 schema;业务方暂用 biz_ref 映射,P3 补 |
| 路由取首个账户 | P2 取"首个 enabled 账户",完整路由策略在 P5 |
| 币种走部署默认 | 多币种在 P3+ |
| /v1 与 /api/v1 并存 | v2 新端点与 v1 旧端点并存,收口在 pay 定稿后 |
(out_trade_no,event_type) 是否合理(Task6);④ 上面 5 条取舍是否认可(尤其"路由取首个账户""币种默认"延后)。相关:pay v2 设计 · 真相源 docs/superpowers/plans/2026-07-10-pay-v2-p2-pipeline.md