返回博客列表
后端工程2026年8月20日2 分钟阅读
支付与订单系统中的幂等、回调和最终一致性
从预下单、支付通知、主动查单到退款,梳理一条不能依赖前端成功页的交易状态链路。
#Go#MySQL#Redis#支付系统

支付链路的困难不在于发起一次 HTTP 请求,而在于任何一步都可能重复、延迟或乱序。用户重复点击、网关超时、回调重试和服务重启,都不能制造第二笔订单或让已支付订单回到待支付。
幂等键放在哪里
客户端为一次业务意图生成 Idempotency-Key,服务端以租户、接口和幂等键建立唯一约束。相同请求返回原结果,参数不同但幂等键相同则明确报冲突。
UNIQUE KEY uk_tenant_idempotency (tenant_id, endpoint, idempotency_key)状态机只能单向推进
CREATED → PAYING:生成支付参数
PAYING → PAID:验签通知或主动查单确认
PAID → REFUNDING:创建退款请求
REFUNDING → REFUNDED:退款通知或查询确认
任何终态都不接受旧事件回退
回调处理顺序
读取原始请求体并验证签名
校验商户、订单号、金额和币种
在事务内锁定订单并判断当前状态
写入支付记录和订单新状态
记录待发送领域事件
事务提交后快速返回成功
为什么还要主动查单
回调可能因为网络、网关或配置问题无法到达。订单长时间处于 PAYING 时,后台任务可以主动查询渠道状态。客户端轮询也只查询自己的服务端,不能直接把渠道响应当作本地订单状态。
退款不是反向支付
退款需要独立记录申请金额、渠道退款号、原因和状态。一笔支付可能发生部分退款,多次退款总额不能超过实付金额,所有状态变化必须可审计。
测试重点
相同幂等键并发请求
重复支付通知
旧通知晚于新通知到达
事务提交成功但事件发送失败
退款金额累计越界
渠道 401、429 与超时