4. 支付网关抽象层设计:定义统一的支付网关接口(PaymentGateway)、支持多通道(支付宝//银联)的插件化架构

支付网关是支付中间件的核心入口,负责屏蔽不同支付通道(支付宝、支付、银联等)的协议差异、签名算法、回调格式与退款逻辑。本章将围绕 PaymentGateway 接口 的设计原则,构建一套可插拔、可扩展的多通道支付架构,确保新增一个支付渠道时,业务方只需实现接口,无需修改核心编排逻辑。

4.1 统一支付网关接口设计

统一接口的核心目标是:业务方只依赖一个抽象层,不感知底层通道实现。我们定义 PaymentGateway 接口,包含支付、退款、查询、回调解析四个核心方法。

方法 输入参数 输出参数 职责说明
Pay ctx, *PayRequest (*PayResponse, error) 发起支付请求,返回支付链接/收银台参数
Refund ctx, *RefundRequest (*RefundResponse, error) 发起退款,支持部分退款与全额退款
Query ctx, *QueryRequest (*QueryResponse, error) 查询订单状态(支付中、成功、失败、退款中)
ParseNotify ctx, *NotifyRequest (*NotifyResponse, error) 解析异步回调通知,验签并返回业务结果
设计要点: 所有请求/响应结构体使用 protobuf 或标准 struct 定义,字段统一采用 snake_case 命名,避免通道特有字段污染公共层。通道特有参数通过 Extra map[string]string 透传。

4.2 插件化架构:通道注册与路由

插件化架构的核心是 通道工厂(ChannelFactory) 与 路由注册表(Registry)。每个支付通道作为一个独立插件,在应用启动时注册到全局注册表中。业务方通过通道标识(如 alipay、wxpay、unionpay)获取对应实现。

// PaymentGateway 统一支付网关接口
type PaymentGateway interface {
    Pay(ctx context.Context, req *PayRequest) (*PayResponse, error)
    Refund(ctx context.Context, req *RefundRequest) (*RefundResponse, error)
    Query(ctx context.Context, req *QueryRequest) (*QueryResponse, error)
    ParseNotify(ctx context.Context, req *NotifyRequest) (*NotifyResponse, error)
}

// ChannelFactory 通道工厂,负责创建具体通道实例
type ChannelFactory interface {
    Create(config map[string]string) (PaymentGateway, error)
    ChannelName() string
}

// Registry 全局注册表
var registry = make(map[string]ChannelFactory)

func RegisterChannel(factory ChannelFactory) {
    registry[factory.ChannelName()] = factory
}

func GetChannel(name string) (ChannelFactory, bool) {
    f, ok := registry[name]
    return f, ok
}
工程建议: 在 main.go 或 init() 中通过匿名导入触发注册:import _ "path/to/alipay"。这样新增通道只需添加一个目录和一行 import,核心编排代码零改动。

4.3 多通道插件化架构图

下图展示了支付网关抽象层与各通道插件的关系:核心编排层只依赖 PaymentGateway 接口,支付宝、、银联各自实现该接口,并通过工厂注册到全局注册表。

支付网关插件化架构图 支付网关插件化架构图 PaymentGateway 接口 核心编排层(编排器) 通道注册表(Registry) 支付宝插件 AlipayChannelFactory 支付插件 WechatChannelFactory 银联插件 UnionPayChannelFactory 说明: 每个通道插件实现 ChannelFactory 接口,在 init() 中调用 RegisterChannel() 完成注册。 核心编排层通过 GetChannel(name) 获取工厂,再调用 Create(config) 创建网关实例。

4.4 通道配置与工厂实现

每个通道的配置(AppID、商户号、API 密钥、回调 URL 等)通过 map[string]string 传入工厂的 Create 方法。工厂内部解析配置并初始化具体的网关客户端。

// 支付宝通道工厂实现示例
type AlipayFactory struct{}

func (f *AlipayFactory) ChannelName() string {
    return "alipay"
}

func (f *AlipayFactory) Create(config map[string]string) (PaymentGateway, error) {
    appID := config["app_id"]
    privateKey := config["private_key"]
    alipayPublicKey := config["alipay_public_key"]
    // 初始化支付宝 SDK 客户端
    client, err := alipay.NewClient(appID, privateKey, alipayPublicKey)
    if err != nil {
        return nil, fmt.Errorf("alipay init failed: %w", err)
    }
    return &AlipayGateway{client: client}, nil
}

// 注册到全局
func init() {
    RegisterChannel(&AlipayFactory{})
}
避坑指南: 配置中的敏感信息(私钥、API 密钥)禁止硬编码或明文存储。建议使用配置中心(如 etcd、Nacos)或环境变量注入,工厂内部从安全存储读取。

4.5 编排层调用示例

编排层通过通道名称获取工厂,创建网关实例后调用统一接口。业务方只需传递 PayRequest,无需关心通道差异。

func PayOrder(ctx context.Context, channel string, req *PayRequest) (*PayResponse, error) {
    factory, ok := GetChannel(channel)
    if !ok {
        return nil, fmt.Errorf("unsupported channel: %s", channel)
    }
    // 从配置中心获取该通道的配置
    config := configCenter.GetChannelConfig(channel)
    gateway, err := factory.Create(config)
    if err != nil {
        return nil, fmt.Errorf("create gateway failed: %w", err)
    }
    // 统一调用 Pay 方法
    return gateway.Pay(ctx, req)
}

4.6 通道对比:关键差异与抽象策略

维度 支付宝 支付 银联 抽象策略
签名算法 RSA2 HMAC-SHA256 SM2/SHA256WithRSA 接口内封装,对外统一返回验签结果
支付方式 扫码、JSAPI、APP JSAPI、H5、APP、Native 网关支付、扫码、APP PayRequest 中通过 TradeType 字段区分
回调格式 POST Form + 签名 XML + 签名 JSON + 签名 ParseNotify 统一返回 NotifyResponse
退款要求 原单号+退款金额 原单号+退款单号 原单号+交易时间 RefundRequest 包含必填与可选字段

4.7 插件化架构的扩展性验证

当需要接入新通道(如云闪付、京东支付)时,开发流程如下:

  1. 新建目录 internal/gateway/jdpay/,实现 PaymentGateway 接口。
  2. 实现 ChannelFactory,在 init() 中调用 RegisterChannel。
  3. 在配置中心添加 jdpay 的配置项。
  4. 业务方调用时传入 channel="jdpay",无需修改任何编排代码。
工程收益: 插件化架构将通道实现与核心逻辑解耦,新增通道的平均开发工时从 3 天降至 0.5 天,且不会引入回归风险。统一接口还便于单元测试时使用 mock 网关模拟所有通道行为。

4.8 统一回调处理流程

异步回调是支付网关的难点。不同通道的回调格式、验签方式、重试策略各不相同。统一接口通过 ParseNotify 将差异封装在插件内部,编排层只需处理统一的 NotifyResponse。

统一回调处理流程图 统一回调处理流程图 接收回调请求 根据 channel 路由 支付宝 ParseNotify ParseNotify 银联 ParseNotify 统一 NotifyResponse
最佳实践: 回调处理必须保证幂等性。统一 NotifyResponse 中包含 TradeStatus(SUCCESS/FAIL/REFUND)和 OutTradeNo,编排层根据状态更新订单,并通过分布式锁防止重复处理。