refactor(payment): 渠道抽象成 Channel 接口 —— 接新渠道=加 adapter 不动骨架

PAYMENT_DESIGN §3 承诺的 internal/payment/channel.go 适配器接口此前不存在,微信硬编码在
manager/handler 里。补齐抽象:

- channel.go:Channel 接口(Name/CreatePay/QueryOrder/VerifyCallback)+ 统一 PayIntent/PayResult
  + 渠道名常量。入参用基本类型不吃 *store.PaymentOrder,payment 包不反依赖 store。
- Wechat 实现 Channel(编译期断言 var _ Channel);QueryResult 归一为 PayResult;CreatePay 返回 PayIntent。
- Manager 从「持一个 *Wechat」改为渠道注册表:Get(name)/Available()/Status(name)/ReloadWechat;
  按渠道名持有已装配实例,热重载不变。
- 回调路由收敛 /billing/callback/wechat → /billing/callback/:channel 按名路由(旧 notify URL 仍匹配);
  查单/掉单补偿据 order.Channel 路由,不再写死微信。下单支持可选 channel(缺省 wechat)。
- 支付宝/Stripe 现在真·只差一个 adapter+注册。唯一未泛化:回调 ack 应答格式(现微信态,注释标明)。
- payment 包首个测试:Manager 注册表 4 用例(空/注册摘除/空配置/配置不全)。build/vet/test/lint 全绿。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-18 13:56:24 +08:00
parent 86e79d76ac
commit 3a4e1d53a5
10 changed files with 261 additions and 80 deletions
@@ -0,0 +1,44 @@
package payment
import (
"context"
"net/http"
)
// Channel 是一个「真渠道」充值适配器(设计见 PAYMENT_DESIGN.md §3)。
// 兑换码不走这里(无「待支付」态,核销即入账);本接口面向需要下单-回调/查单确认的渠道。
// 接新渠道(支付宝/Stripe)= 实现这四个方法 + 在 Manager 注册,不动上层充值/入账骨架。
//
// 刻意用基本类型入参(orderID/amountFen)而非 *store.PaymentOrder:让 payment 包不反向依赖
// store,渠道适配层保持纯粹。金额一律以「分」为单位、服务端锁定值,绝不信任客户端。
type Channel interface {
// Name 渠道名,与 store.Channel*"wechat"/"alipay"/"stripe")一致,用于按订单渠道路由。
Name() string
// CreatePay 依据订单生成支付凭据(微信 Native 出 code_url;支付宝/Stripe 可出跳转 URL)。
CreatePay(ctx context.Context, orderID, description string, amountFen int64) (PayIntent, error)
// QueryOrder 主动查单(前端轮询确认 + 掉单补偿共用)。
QueryOrder(ctx context.Context, orderID string) (PayResult, error)
// VerifyCallback 验签并解析渠道回调 → 统一结果。验签失败必须返回 error(回调路由公开,签名是唯一门)。
VerifyCallback(req *http.Request) (PayResult, error)
}
// 渠道名常量(与 store.Channel* 取值一致;此处独立定义以免 payment 反依赖 store)。
const (
ChannelWechat = "wechat"
ChannelAlipay = "alipay" // 预留:未实现
ChannelStripe = "stripe" // 预留:未实现
)
// PayIntent 是下单后给前端的支付凭据(不同渠道形态不同,按需取用)。
type PayIntent struct {
CodeURL string // 微信 Native:二维码内容。其它渠道可放跳转 URL / client_secret。
}
// PayResult 是查单/回调验签解析后的统一结果(各渠道适配器把自家响应归一到这里)。
type PayResult struct {
OrderID string // 商户订单号(out_trade_no
ChannelTxn string // 渠道流水号(transaction_id),入账时落库备查
Paid bool // 支付成功
Closed bool // 关单/撤销/支付失败等终态
AmountFen int64 // 用户实付(分);供与订单金额核对,防金额被篡改
}