4. 支付网关抽象层:统一支付网关接口设计、适配器模式实现、多通道接入策略

支付网关是支付中间件的核心出口,直面支付宝、支付、银联等外部渠道。本章聚焦如何通过统一接口抽象与适配器模式,将多通道差异封装在网关层内部,使上层业务代码与具体支付渠道解耦。

4.1 统一支付网关接口设计

统一接口的核心目标是:一套 API 定义,对接所有支付渠道。接口需要覆盖支付全生命周期:下单、查询、退款、关闭、回调通知。

接口方法 输入参数 输出参数 说明
Pay(ctx, req) 订单号、金额、商品描述、回调地址 支付凭据、支付链接、渠道单号 发起支付,返回前端可用的支付参数
Query(ctx, req) 商户订单号、渠道单号 支付状态、渠道返回码、支付时间 主动查询订单最终状态
Refund(ctx, req) 原订单号、退款金额、退款原因 退款单号、退款状态 发起全额或部分退款
Close(ctx, req) 商户订单号 关闭结果 关闭未支付订单
VerifyNotify(ctx, req) 渠道原始回调参数 解析后的支付通知对象 验签并解析异步通知
设计原则:接口参数使用统一的 PaymentRequest 和 PaymentResponse 结构体,内部包含渠道扩展字段 map,避免接口因渠道特有参数而膨胀。

4.2 适配器模式实现

适配器模式是支付网关抽象层的核心实现手段。每个支付渠道实现一个适配器,将渠道 SDK 的差异接口转换为统一的网关接口。

// 统一支付网关接口
type PaymentGateway interface {
    Pay(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error)
    Query(ctx context.Context, req *QueryRequest) (*QueryResponse, error)
    Refund(ctx context.Context, req *RefundRequest) (*RefundResponse, error)
    Close(ctx context.Context, req *CloseRequest) (*CloseResponse, error)
    VerifyNotify(ctx context.Context, params map[string]string) (*NotifyResult, error)
}

// 支付宝适配器
type AlipayAdapter struct {
    client *alipay.Client
}

func (a *AlipayAdapter) Pay(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) {
    // 将统一请求转换为支付宝请求结构体
    alipayReq := &alipay.TradePrecreateRequest{
        OutTradeNo: req.OrderNo,
        TotalAmount: req.Amount,
        Subject: req.Subject,
    }
    // 调用支付宝 SDK
    result, err := a.client.TradePrecreate(alipayReq)
    if err != nil {
        return nil, err
    }
    // 将支付宝响应转换为统一响应
    return &PaymentResponse{
        TradeNo: result.TradeNo,
        PayUrl: result.QrCode,
        RawData: result,
    }, nil
}

// 支付适配器
type WechatPayAdapter struct {
    client *wechat.Client
}

func (w *WechatPayAdapter) Pay(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) {
    // 将统一请求转换为请求结构体
    wechatReq := &wechat.NativePayRequest{
        OutTradeNo: req.OrderNo,
        TotalFee: int64(req.Amount * 100), // 以分为单位
        Body: req.Subject,
    }
    result, err := w.client.NativePay(wechatReq)
    if err != nil {
        return nil, err
    }
    return &PaymentResponse{
        TradeNo: result.TransactionId,
        PayUrl: result.CodeUrl,
        RawData: result,
    }, nil
}
适配器职责边界:适配器只做协议转换和参数映射,不处理业务逻辑(如金额校验、订单状态机)。业务规则应放在网关上层或领域层。

4.3 多通道接入策略

多通道接入不仅仅是实现多个适配器,还需要一套通道路由与降级策略,确保系统在单通道故障时能自动切换。

4.3.1 通道注册与路由

使用工厂模式管理适配器实例,通过配置中心动态注册/注销通道。

// 通道工厂
type GatewayFactory struct {
    gateways map[string]PaymentGateway
    mu       sync.RWMutex
}

func (f *GatewayFactory) Register(name string, gateway PaymentGateway) {
    f.mu.Lock()
    defer f.mu.Unlock()
    f.gateways[name] = gateway
}

func (f *GatewayFactory) GetGateway(name string) (PaymentGateway, error) {
    f.mu.RLock()
    defer f.mu.RUnlock()
    gateway, ok := f.gateways[name]
    if !ok {
        return nil, fmt.Errorf("gateway %s not registered", name)
    }
    return gateway, nil
}

// 路由策略:按权重分配
type WeightedRouter struct {
    gateways []WeightedGateway
}

type WeightedGateway struct {
    Name   string
    Weight int
    Gateway PaymentGateway
}

func (r *WeightedRouter) Select() (PaymentGateway, error) {
    // 加权随机选择
    totalWeight := 0
    for _, gw := range r.gateways {
        totalWeight += gw.Weight
    }
    randVal := rand.Intn(totalWeight)
    for _, gw := range r.gateways {
        randVal -= gw.Weight
        if randVal < 0 {
            return gw.Gateway, nil
        }
    }
    return nil, fmt.Errorf("no gateway available")
}

4.3.2 降级与熔断策略

策略 触发条件 行为
快速失败 连续 5 次请求超时或返回错误 直接返回错误,不重试
自动降级 单通道错误率 > 50% 将该通道权重降为 0,切换到备用通道
半开恢复 降级后每 30 秒尝试一次 若成功则恢复权重,否则继续降级
手动切换 运维配置变更 通过配置中心强制指定通道
避坑提示:不要在所有支付请求上都做自动降级。对于退款和查询操作,应优先使用原支付通道,因为跨通道退款通常不被支持。

4.4 支付网关抽象层架构

下图展示了支付网关抽象层的整体架构,从统一接口到适配器再到外部渠道的完整链路。

支付网关抽象层架构 上层业务系统 统一支付网关接口 (PaymentGateway) 支付宝适配器 AlipayAdapter 支付适配器 WechatPayAdapter 银联适配器 UnionPayAdapter 支付宝开放平台 支付平台 银联支付系统 通道路由 & 降级策略 配置中心 (动态注册/权重调整)

4.5 适配器实现要点

  • 参数映射:金额单位统一(元 vs 分)、时间格式统一(ISO8601 vs 时间戳)、订单号长度适配。
  • 签名处理:每个渠道的签名算法不同(RSA2、HMAC-SHA256、MD5),适配器内部封装签名逻辑。
  • 错误码转换:将渠道返回的错误码映射为统一的内部错误码,方便上层统一处理。
  • 上下文传递:将 Go 的 context.Context 透传到 SDK 调用中,支持超时控制和链路追踪。
最佳实践:每个适配器单独一个文件,命名规范为 alipay_adapter.go、wechat_adapter.go。适配器内部不要缓存渠道配置,配置统一从工厂层注入。

4.6 多通道接入策略对比

策略 适用场景 优点 缺点
主备切换 有明确主通道,备用通道仅用于故障 实现简单,资源占用少 主通道故障时切换有延迟
权重轮询 多通道流量均衡,如支付宝 70% + 30% 流量可控,可灰度验证 需要实时监控调整权重
动态路由 按用户、金额、地域等维度路由 精细化控制,用户体验好 路由规则复杂,维护成本高
全通道并发 对成功率要求极高,如大促场景 任一通道成功即返回,成功率最高 资源消耗大,需处理重复回调

选择哪种策略取决于业务场景。对于大多数中小型系统,主备切换 + 权重轮询的组合已经足够;对于大型电商或金融系统,建议使用动态路由 + 全通道并发的组合。