多渠道支付系统设计文档
一、系统目标
设计一个支持微信支付、支付宝支付、苹果支付的统一支付系统,满足以下要求:
- 支持虚拟商品支付(如金币、会员等)
- 安全可靠(签名验证、幂等处理、防止伪造)
- 支持支付状态回调、补单
- 易扩展(未来支持 PayPal、Google Pay)
二、系统架构
三、主要模块
1. 统一支付订单模块(payment_order)
- 字段:order_id、user_id、amount、channel、status、product_id、created_at、updated_at
- 状态流转:待支付 → 已支付 → 发货完成 / 取消 / 关闭
2. 支付渠道适配器(adapter)
- WechatAdapter:生成预支付单、处理回调、验签
- AlipayAdapter:生成跳转参数包、处理回调、验签
- AppleAdapter:接收客户端 receipt,调用 Apple Server 验证接口
3. 回调处理器(callback_handler)
- 接收渠道异步回调
- 验签、查订单、状态更新
- 发货逻辑(通知业务系统)
- 返回 success/failed 给渠道
4. 发货服务(deliver_service)
- 发放金币、商品等虚拟物品
- 保证幂等(基于 order_id 唯一发货)
5. 补单服务(补偿任务)
- 定时任务查询超时未支付订单
- 主动调用渠道查询接口确认订单状态
- 状态回写到支付订单表
6. 退款处理模块(refund_service)
- 支持用户主动申请退款(如支付后未发货)或平台发起退款
- 流程:校验退款条件 → 创建退款单 → 调用支付渠道接口 → 更新状态
- 支持查询退款状态(微信/支付宝接口支持)
- 字段:refund_id、order_id、amount、reason、status、channel_refund_id
- 退款状态流转:待处理 → 处理中 → 已退款 / 失败
7. 对账系统(reconcile_service)
- 定期拉取渠道账单(如微信对账单、支付宝对账单)
- 与本地订单进行核对:金额、状态、交易号
- 输出差异报告,支持自动处理(如补发货、标记异常订单)
- 提供人工复查接口和日志记录
8. 风控系统(risk_engine)
- 检测异常支付行为(如频繁支付、支付失败过多、设备异常)
- 接入用户画像、IP 黑名单、设备指纹等信息
- 在发起支付前、回调处理时进行风控判断,可阻止发货或标记高风险
- 风控结果可推送至运营后台用于人工介入处理
四、支付流程图
客户端请求支付
↓
服务端创建订单,调用渠道下单接口
↓
返回客户端调起支付 SDK 参数
↓
客户端拉起微信/支付宝/苹果支付
↓
支付完成后 → 渠道回调服务端
↓
服务端验签、确认支付成功、修改订单状态
↓
发货 / 通知业务模块
五、接口设计(RESTful)
1. 创建订单(统一接口)
POST /api/pay/create
{
"product_id": "coin_100",
"amount": 100,
"channel": "wechat"
}
2. 苹果支付验证
POST /api/pay/apple/verify
{
"receipt_data": "base64...",
"order_id": "202506110001"
}
3. 渠道回调接口(服务端)
POST /api/pay/callback/wechat
POST /api/pay/callback/alipay
4. 查询订单状态
GET /api/pay/status?order_id=xxx
5. 发起退款
POST /api/pay/refund
{
"order_id": "202506110001",
"reason": "user_cancel",
"amount": 100
}
6. 查询退款状态
GET /api/pay/refund/status?refund_id=xxx
六、安全设计
- 微信/支付宝:严格验签 + IP 白名单
- 苹果:通过 Apple 官方服务验证 receipt
- 所有接口防重放、幂等处理
- 所有交易记录审计日志
七、扩展与优化
- 自动补单机制:定时查询未完成订单,自动确认状态
- 抽象支付网关接口,便于添加更多渠道
- 支持渠道回调重试与失败告警
- 接入 Prometheus / Grafana 做监控
八、数据库表结构(简化)
CREATE TABLE payment_order (
order_id VARCHAR(32) PRIMARY KEY,
user_id BIGINT NOT NULL,
channel VARCHAR(20),
amount INT,
status VARCHAR(20),
product_id VARCHAR(32),
channel_txn VARCHAR(64),
created_at DATETIME,
updated_at DATETIME
);
九、系统整体设计要点
1.统一支付抽象接口
- 抽象统一接口(如:PayService)屏蔽支付渠道差异。
- 每个支付渠道实现一个独立的适配器或驱动(如 WeChatPayService, AliPayService, ApplePayService)。
type PayService interface {
CreateOrder(params OrderRequest) (PayResponse, error)
HandleCallback(data []byte) (CallbackResult, error)
QueryOrder(orderID string) (OrderStatus, error)
}
2.幂等性保证
- 每个订单生成后不可重复处理(防止回调多次触发、客户端重试多次创建等)。
- 推荐使用 Redis + 唯一事务号做幂等控制。
- 回调处理中要 先检查订单状态是否已处理。
3.统一订单系统
- 建立统一订单表(例如:payment_orders),结构统一管理,不区分渠道。
- 字段示例:
- order_id、user_id
- amount、channel(wechat/alipay/apple)
- status(未支付/已支付/失败)
- external_txn_id(微信/支付宝交易号)
- created_at、paid_at
4.安全性设计
- 支付请求签名、参数校验。
- 微信/支付宝有 RSA 或 MD5 签名;苹果 IAP 要验证 receipt。
- 服务器之间通信需要做 IP 白名单、Token 或密钥校验。
- 防止金额被客户端伪造,价格应服务端为准。
5.异步通知 + 补单机制
- 所有支付渠道都使用 异步回调 通知支付成功。
- 系统必须支持 回调重试、补单查询机制(如订单超时但实际支付了)。
- 每个订单应支持后台主动查询(Query API)校验状态。
6.Apple Pay 特殊处理
- 客户端先拿到 receipt,传给服务端;
- 服务端请求 Apple 验证服务器(生产/沙箱)验证该 receipt;
- 需要处理多次验证、试用期、自动续费等复杂场景;
- 推荐:对 Apple 订单建立额外的验证任务队列 + 异常监控。
十、模块划分建议
/pay
├── controller
│ └── pay_controller.go // 下单、回调入口
├── service
│ ├── pay_service.go // 调度统一接口
│ ├── wechat_pay.go // 微信实现
│ ├── alipay.go // 支付宝实现
│ └── apple_pay.go // 苹果支付实现
├── model
│ └── payment_order.go // 订单模型定义
├── repository
│ └── payment_repo.go // 订单读写
└── util
└── signature.go // 签名生成/验证
整体流程总览图
[客户端请求支付]
│
▼
┌── Step 1:创建支付订单 ───────────────┐
│ │
│ 服务端生成统一订单(支付单) │
│ 并调用各渠道下单接口 │
└────── 返回支付凭证/跳转链接 ──────┘
│
▼
┌── Step 2:客户端发起支付 ─────────────┐
│ 调起微信/支付宝/苹果支付 SDK │
└──────────────────────────────────────┘
│
▼
┌── Step 3:支付渠道异步回调 ───────────┐
│ 微信/支付宝服务器 → 通知服务端 │
│ Apple → 客户端拿到 receipt, │
│ 传给服务端验证 │
└──────────────────────────────────────┘
│
▼
┌── Step 4:服务端确认支付状态 ────────┐
│ - 更新订单状态 │
│ - 通知业务模块发货 / 送金币 │
└──────────────────────────────────────┘
Step 1:创建订单(统一接口)
1、客户端发起支付请求:
{
"amount": 100,
"pay_channel": "wechat", // or alipay / apple
"product_id": "coin_100"
}
2、服务端处理逻辑
- 验证参数(用户合法、商品有效、金额正确)
- 生成统一支付订单(payment_order)
- 调用渠道支付服务:
- 微信:生成预支付单,返回 prepay_id
- 支付宝:生成跳转链接或参数包
- 苹果:等待客户端提供 receipt
3、返回结果给客户端(供 SDK 使用):
{
"pay_params": { ... }, // SDK 调起参数
"order_id": "202506110001"
}
Step 2:客户端调起支付(前端负责)
- 微信 / 支付宝使用 SDK 或 Web 页面调起;
- 苹果 IAP 使用 StoreKit 发起支付流程;
- 苹果支付完成后,客户端上传 receipt 给服务端。
Step 3:支付回调 & 验签处理
所有渠道都通过“服务端通知”最终确认支付是否成功。
微信 / 支付宝
- 渠道服务器回调服务端 URL(POST 请求),携带订单号、金额、签名等;
- 服务端验签、查订单、对比金额;
- 修改订单状态:待支付 → 已支付;
- 回调业务逻辑(发货、金币到账等);
- 返回 success 响应,防止多次回调。
苹果支付
- 客户端上传 receipt;
- 服务端调用 Apple 验证接口;
- 解析验证结果,确认支付成功;
- 更新订单、发货。
Step 4:订单确认 & 发货
- 更新订单为 已支付,记录交易号;
- 通知业务层执行发货逻辑:
- 发放虚拟物品、金币;
- 记录流水;
- 给客户端推送支付成功消息;
- 支持幂等调用,确保只发货一次。
异常处理流程(补单、回调失败)
场景 | 处理方案 |
回调失败 | 支付平台会重试(最多 3~5 次),应幂等处理 |
用户支付成功但未收到货 | 后台支持查询订单状态接口,或通过补单机制查询 |
苹果支付未及时验证 | 可以将 receipt 放入异步队列,后续处理 |
用户多次点击付款 | 前端应防重 + 服务端幂等检查(order_id 唯一) |
支付回调被攻击伪造 | 严格验签(微信/支付宝签名、Apple receipt) |
支付订单状态流转图
[待支付] ──→ [支付中] ──→ [已支付]
│ │
│ └──→ [发货成功]
│
└──→ [已取消] / [超时关闭]
接口示意(建议暴露的 API)
接口 | 说明 |
POST /pay/create | 创建支付订单 |
POST /pay/apple | 苹果支付 receipt 验证接口 |
POST /pay/callback/wechat | 微信回调接口 |
POST /pay/callback/alipay | 支付宝回调接口 |
GET /pay/query | 查询订单状态 |
十一、安全性设计
一、支付请求签名与验签
微信 / 支付宝签名机制(RSA / MD5 / HMAC)
1. 微信支付(HMAC-SHA256 / MD5)
- 签名字段:appid、mch_id、nonce_str、body、total_fee 等所有参与签名字段(不含 sign)
- 服务端生成签名:
func GenerateWechatSign(params map[string]string, apiKey string) string {
// 1. 排序
keys := make([]string, 0, len(params))
for k := range params {
if k != "sign" && params[k] != "" {
keys = append(keys, k)
}
}
sort.Strings(keys)
// 2. 拼接参数字符串
var buf strings.Builder
for _, k := range keys {
buf.WriteString(k + "=" + params[k] + "&")
}
buf.WriteString("key=" + apiKey)
// 3. 签名
h := md5.Sum([]byte(buf.String()))
return strings.ToUpper(hex.EncodeToString(h[:]))
}
- 微信回调时服务端需使用相同算法进行签名校验
2. 支付宝支付(RSA2)
- 服务器需保留支付宝公钥,本地进行验签:
func VerifyAlipaySign(params map[string]string, sign, publicKey string) bool {
// 1. 参数排序并拼接
// 2. 使用 RSA-SHA256 验签
// 推荐使用官方 SDK(Java / Go)
}
3.苹果 IAP 凭证校验
func VerifyAppleReceipt(receipt string, isSandbox bool) (bool, error) {
url := "https://buy.itunes.apple.com/verifyReceipt"
if isSandbox {
url = "https://sandbox.itunes.apple.com/verifyReceipt"
}
body := map[string]string{"receipt-data": receipt}
payload, _ := json.Marshal(body)
resp, err := http.Post(url, "application/json", bytes.NewBuffer(payload))
// 解析 JSON 返回 {"status": 0, "receipt": {...}}
}
二、服务通信安全(API 保护)
1. IP 白名单(网关层或接口层实现)
- 配置反向代理(如 Nginx)或微服务网关(Traefik、Kong)限制来源 IP:
allow 192.168.1.0/24;
deny all;
2. Access Token 校验(更灵活)
服务 A 调用服务 B 时,需携带签名头:
POST /internal/order/notify
Authorization: Bearer {HMAC(api_key, timestamp, nonce)}
X-Timestamp: 1680000000
X-Nonce: abc123
服务 B 验签流程:
func VerifyToken(apiKey, receivedSignature, timestamp, nonce string) bool {
base := fmt.Sprintf("%s|%s|%s", apiKey, timestamp, nonce)
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(base))
return hex.EncodeToString(h.Sum(nil)) == receivedSignature
}
三、客户端伪造金额的防范
原则:价格以服务端为准
- 客户端只能提交商品 ID(如:coin_100)
- 服务端通过商品配置表确认价格与商品内容:
// 后端查表获取金额
product := db.FindProductByID("coin_100") // price = 10.00
不允许客户端直接传入金额,否则极易被修改
四、幂等性保证(防止多次支付 / 发货)
// 发货前先判断状态
if order.Status == "paid" && !order.Delivered {
// 发货逻辑 + 设置 delivered = true
}
五、日志 + 监控 + 告警
- 所有验签失败、异常凭证、退款失败都需日志记录
- 接入 Prometheus + Grafana 做指标监控
- 回调失败、对账失败等支持钉钉/飞书告警