支付接入:回调、对账与退款这三件事决定成败

支付功能跑通很快,跑稳很难。本文说明为什么不能信前端的支付成功、回调幂等怎么做、以及对账和退款该怎么设计。

要点速览
  • 前端跳转可被伪造也可能不发生,订单状态只能由服务端回调变更。
  • 回调必须同时做验签、金额校验与幂等,三者缺一分别对应漏洞、资损与重复发货。
  • 支付平台会重复发送回调直到收到成功响应,幂等不是可选项。
  • 必须有每日对账任务,找出平台已收款未入账与本地已入账平台无记录两类差异。
  • 退款是异步的,订单应先进中间状态,直接跳终态会导致失败时回不去。

支付是电商系统里出错代价最高的部分 —— 少收钱是亏损,多收钱是纠纷,状态不一致是客服灾难。而演示环境里它通常一次就跑通了,真正的问题要到有真实流量之后才暴露。

永远以服务端回调为准

用户付款后浏览器跳回来的那个页面,只是一个前端跳转,它可以被伪造、可以不发生、也可以在付款失败时照样触发。把它当作入账依据,等于把收款逻辑交给了客户端。

正确的流程是:前端跳转只用于展示「正在确认」,真正的订单状态变更由支付平台的服务端回调触发,前端轮询我们自己的接口查询状态。两条路分开,少任何一条都不行。

回调必须做三件事

  1. 验签:确认这条回调确实来自支付平台,而不是伪造的请求;
  2. 校验金额:回调里的金额必须与下单时记录的一致,不一致一律拒绝入账并告警。这一条挡的是改金额攻击;
  3. 幂等:同一笔支付的回调会被重复发送,直到收到成功响应为止。用订单号做唯一键,重复到达时直接返回成功而不重复处理。

三者缺一都会出事,而且表现形式各不相同:缺验签是安全漏洞,缺金额校验是直接的资金损失,缺幂等是重复发货或重复充值。

对账不是可选项

回调可能丢失、可能超时、我们这边也可能处理失败。所以必须有一个定期任务,拉取支付平台的交易流水与本地订单逐笔比对,找出「平台已收款但本地未入账」和「本地已入账但平台无记录」两类差异。

前一类要补单,后一类要查清楚原因 —— 它通常意味着有订单被错误地标记为已付。对账频率至少每天一次,差异要有人处理而不只是记一条日志。

退款同样要幂等

退款接口的重试、运营的重复点击,都可能导致退两次。做法与支付一致:用退款单号做唯一键,先查询再发起,并且退款成功的回调也要幂等处理。

另外要注意退款与订单状态的联动:退款发起后订单应当进入中间状态,而不是立刻变成已退款 —— 支付平台的退款是异步的,可能失败。状态直接跳到终态,失败时就回不去了。

沙箱测不出来的几件事

并发下单同一库存、回调与前端查询的竞态、以及真实网络下的超时重试。这些建议在上线前用小额真实交易验证一轮,电商网站 / 商城定制项目里我们会把这一轮列进验收,具体安排见合作流程。

常见问题

支付成功后订单状态什么时候更新?

以支付平台的服务端回调为准。浏览器跳回的页面只是前端行为,可以被伪造、可以不发生,也可能在付款失败时照样触发。前端跳转只用于展示确认中,真实状态通过轮询自己的接口获取。

支付回调重复收到怎么处理?

用订单号作为唯一键做幂等:已处理过的回调直接返回成功而不重复执行业务逻辑。支付平台会持续重发直到收到成功响应,不做幂等会导致重复发货或重复充值。同时每次回调都要验签并校验金额。

为什么还需要对账?

回调可能丢失或超时,本地处理也可能失败。对账任务逐笔比对支付平台流水与本地订单,找出平台已收款但本地未入账、以及本地已入账但平台无记录两类差异。频率至少每天一次,且差异必须有人跟进处理。

参考资料

  1. 支付宝开放平台文档 · 支付宝开放平台
  2. RFC 9110: HTTP Semantics · IETF
# 支付# 微信支付# 支付宝# 电商系统
免费咨询

聊聊你的项目

留下联系方式,我们一个工作日内联系你。先沟通需求和现状,再给方案;报价当面沟通。

  • 一个工作日内回复
  • 需求沟通免费,不强推方案
  • 交付管理后台与文档,可自行维护
仅用于本次咨询联系,不会用于其他用途。