日历系统上线后,我开始了下一个项目——支付系统。这是一个更复杂的领域,涉及资金安全、分布式事务、对账等挑战。这篇先梳理整体架构。
为什么做支付系统
公司之前的支付逻辑散落在各个业务系统里,每个业务线自己对接支付渠道,代码重复且难以维护。我的任务是把支付能力抽离出来,做成统一的支付中台,供各业务线调用。
核心模块
一个完整的支付系统包含以下模块:
收银台
面向用户的支付界面,负责展示可用支付方式、收集支付信息。可以是 H5 页面、原生 APP 页面或 SDK 弹窗。收银台本身不处理支付逻辑,只是前端入口。
支付网关
系统的核心入口,负责:
- 接收业务系统的支付请求
- 参数校验与签名验证
- 风控规则检查
- 路由到对应的渠道适配器
- 统一的响应格式
渠道适配层
对接各个三方支付渠道(支付宝、微信支付、银联等),每个渠道一个适配器,把我们的统一接口转换为渠道的私有协议。这一层屏蔽了渠道差异,上层不需要感知具体渠道的 API 细节。
账务系统
记录每一笔资金变动,核心是复式记账。每笔交易产生至少两条账务记录(借方和贷方),保证资金总额平衡。账务数据是最终审计的依据。
对账系统
每天从各渠道下载交易明细,与我方账务记录逐笔比对,找出差异(长款、短款、金额不符)。对账是支付系统中最枯燥但最重要的部分——它是发现问题的最后一道防线。
风控系统
交易前的风险评估:
- 频率限制(同一用户短时间内多次支付)
- 金额限制(单笔/日累计)
- 黑名单检查
- 异常行为检测
支付流程
一笔典型的支付流程:
- 创建支付单:业务系统调用支付网关,传入订单号、金额、商品信息等
- 风控检查:网关调用风控系统评估风险
- 渠道选择:根据用户选择或路由规则确定支付渠道
- 调用三方:渠道适配器组装请求,调用三方支付接口
- 等待支付:用户在三方页面完成支付(跳转/扫码/密码输入)
- 异步回调:三方支付完成后回调我方接口,更新支付状态
- 通知业务:支付成功后通知业务系统(异步消息 + 同步查询兜底)
幂等性设计
支付场景中幂等性至关重要——网络超时、用户重复点击、回调重复投递都会导致重复请求。
我的方案是每笔支付单有一个全局唯一的 payment_id,所有操作都基于这个 ID 做幂等:
- 创建支付单:相同
biz_order_id+biz_system幂等 - 调用三方:相同
payment_id对应相同的三方订单号 - 回调处理:通过
payment_id查询当前状态,已成功的直接返回成功 - 通知业务:消息带
payment_id,业务方自行去重
数据库层面,关键表都有唯一索引保底。
分布式事务考虑
支付系统天然涉及分布式事务——我们的系统要和三方支付渠道之间保持状态一致。严格的分布式事务(2PC)性能太差,实际采用的是最终一致性方案:
- 本地事务 + 消息表:支付状态变更和消息发送在同一个本地事务里,消息表定期扫描确保投递
- 补偿机制:对于超时未收到回调的支付单,定时任务主动去三方查询状态
- 对账兜底:T+1 对账发现任何不一致,生成差异单人工或自动处理
这套方案不追求实时一致,但能保证最终一致。对于支付场景,这是业界通行的做法。
技术选型
- 语言:Go(团队技术栈)
- 数据库:MySQL(支付数据对事务要求高,不用 PostgreSQL)
- 缓存:Redis
- 消息队列:RocketMQ(支持事务消息)
- 框架:Gin + Wire(依赖注入)
下一步
接下来的文章会深入每个模块的实现细节。下一篇从渠道适配层开始,先接入支付宝和微信支付。