支付系统系列第二篇。上一篇讲了整体架构和对账,这篇专注于支付网关层的设计。支付网关是所有支付请求的入口,负责路由、协议转换、安全校验等。设计好了后面对接再多渠道也不慌。
支付网关的职责
支付网关夹在业务系统和支付渠道之间,核心职责:
- 请求路由:根据支付方式(微信/支付宝/银联)把请求分发到对应渠道
- 协议转换:业务侧统一接口,网关负责转成各渠道的协议(微信用JSON+RSA,支付宝用表单+RSA2)
- 安全校验:请求签名验签、防重放、参数校验
- 限流熔断:保护下游渠道,渠道挂了不能把整个系统拖垮
- 重试补偿:网络超时自动重试,但要注意幂等
- 日志审计:每一笔支付请求都要完整记录
渠道抽象接口
这是网关最核心的抽象。不管对接多少家渠道,业务侧只看到一个统一接口:
PayChannel 接口:
+-- unifiedOrder(request) -> OrderResult # 统一下单
+-- queryOrder(outTradeNo) -> QueryResult # 订单查询
+-- closeOrder(outTradeNo) # 关闭订单
+-- refund(request) -> RefundResult # 退款
+-- queryRefund(refundNo) -> RefundResult # 退款查询
+-- handleCallback(rawData) -> CallbackResult # 回调处理
+-- downloadBill(date) -> BillData # 账单下载
每个支付渠道实现这个接口。新增渠道只需要加一个实现类,不影响已有代码。
接口设计时有几个原则:
- 入参出参全部用业务模型,不暴露渠道特有的字段
- 金额统一用分(long型),渠道实现内部做转换
- 错误码统一映射,不把微信的errcode直接抛给业务层
策略模式选择渠道
用策略模式做渠道路由:
PayChannelFactory
+-- 根据 channelType 选择实现
+-- WECHAT_JSAPI -> WechatJsapiChannel
+-- WECHAT_NATIVE -> WechatNativeChannel
+-- WECHAT_H5 -> WechatH5Channel
+-- ALIPAY_PC -> AlipayPcChannel
+-- ALIPAY_WAP -> AlipayWapChannel
+-- ALIPAY_APP -> AlipayAppChannel
+-- 支持运行时注册新渠道
渠道选择不仅看类型,有时还要看业务规则:
- 同一个商户可能配了多个微信支付商户号(主/备)
- A/B测试:部分流量走新渠道
- 不同金额区间走不同渠道(大额走银联)
这些规则抽象成ChannelRouter:
ChannelRouter:
+-- 输入:支付请求(金额、渠道类型、商户ID、用户标签等)
+-- 规则引擎:
+-- 渠道可用性检查
+-- 金额区间匹配
+-- 商户渠道配置
+-- 灰度/AB规则
+-- 输出:具体的渠道实例 + 商户配置
请求签名与验签
每笔支付请求都要签名,防篡改、防重放。
业务侧签名(内部通信)
业务系统调用支付网关时也需要签名,不能裸调。签名流程:
- 所有请求参数按key字母序排列
- 拼接成
key1=value1&key2=value2的格式 - 末尾追加
&secret=xxx(商户密钥) - 对整个字符串做SHA256
- 请求头带上商户ID、时间戳、随机数、签名值
验签时额外检查:
- 时间戳与服务器时间差不超过5分钟(防重放)
- nonce在Redis中不存在(防重放,nonce过期时间10分钟)
渠道侧签名
每个渠道有自己的签名方式:
- 微信V3:SHA256withRSA,签名串包含HTTP方法+URL+时间戳+随机串+请求体
- 支付宝:RSA2(SHA256WithRSA),签名串是排序后的请求参数
- 银联:SHA256,证书签名
这些差异全部封装在渠道实现内部,网关框架只关心签名是否通过。
渠道降级方案
支付渠道不是100%可靠的。微信支付偶尔会超时,支付宝也有过全国性的故障。降级策略:
健康检查
每个渠道维护一个健康状态:
渠道健康状态:
+-- HEALTHY:正常,成功率 > 95%
+-- DEGRADED:亚健康,成功率 80%-95%,告警
+-- UNHEALTHY:不可用,成功率 < 80%
+-- CIRCUIT_OPEN:熔断,暂停请求
状态转换:
+-- 滑动窗口统计最近5分钟的请求成功率
+-- 连续3次超时 -> 进入DEGRADED
+-- DEGRADED持续2分钟 -> 进入UNHEALTHY
+-- UNHEALTHY下每30秒放一个探测请求
+-- 成功 -> 回到DEGRADED -> 观察期
+-- 失败 -> 保持UNHEALTHY
+-- 熔断恢复后进入半开状态,逐步放量
自动降级
当某个渠道不可用时:
- 渠道切换:微信JSAPI挂了,引导用户用支付宝(如果业务允许)
- 排队等待:把请求放入队列,渠道恢复后自动重试
- 限流保护:对不健康的渠道降低QPS,避免雪崩
告警
- 渠道状态变更:钉钉/企微推送
- 掉单率超阈值:电话告警
- 对账差异:邮件通知 + 工单
幂等设计
支付最怕重复扣款。幂等要做到多层:
- 接口层:相同的outTradeNo在一定时间内只处理一次(Redis分布式锁)
- 数据层:outTradeNo做唯一索引,重复插入直接报错
- 渠道层:大部分渠道本身也支持幂等(相同outTradeNo不会重复扣款)
三层兜底,确保即使网关收到重复请求也不会出问题。
总结
支付网关的设计核心就三个字:可靠性。每一个环节都要考虑失败场景——网络超时、渠道故障、重复请求、数据不一致。把这些异常场景都处理好了,支付系统才算靠谱。
下一篇进入实际渠道对接的代码实现。