通知重复到达,并不代表用户重复付款。网络重试、处理延迟和主动补单都可能让同一笔订单被再次处理。幂等的目标,是让这些重复输入仍然只产生一份正确的业务结果。

每次通知都先完成校验

按照签名规则验证来源,再核对商户号、业务订单、金额和支付成功状态。即使订单已经标记为已支付,也不能在未验签的情况下信任后续通知里的其他字段。

校验通过后,判断这条通知是否对应已经处理的同一交易。对于重复的合法成功通知,可以按支付通知文档返回成功应答;未知订单、金额不匹配或签名错误应进入异常处理,不执行履约。

“查过一次”挡不住并发

两个请求可能同时读到“待支付”,随后各自发货。仅仅先查询状态、再更新状态,无法保证核心动作只执行一次。应使用数据库事务中的条件更新、必要的锁或唯一业务约束,让只有一个请求获得本次处理资格。

例如,将订单从待支付变为已支付时限定原状态,检查实际变更结果;交付记录再以业务订单号建立唯一约束。具体数据库语句由项目技术栈实现,但约束必须落在共享的数据层,不能只放在单台服务器的内存里。

支付确认与外部交付分开恢复

订单状态与待交付任务在同一可靠提交中保存,然后由任务执行器调用发货、会员或额度服务。外部调用失败时,保留任务并重试,不要为了重试而把已支付订单改回未支付。

任务接收方也要按稳定业务标识识别重复执行。仅有发送方去重,仍无法覆盖“对方已经执行成功,但网络返回丢失”的情况;这时应先查询已有结果,再决定如何恢复。

应答和补偿按真实处理结果

成功应答应在必要记录持久化后返回,而不是刚收到请求就返回。业务若采用异步交付,至少要保证订单确认及交付任务已经可靠保存;后台任务可继续完成后续步骤。

定期核对已支付但未交付的订单,使用同一套处理入口补偿。日志记录订单号、通知时间、验签结果、状态变更和任务标识,避免输出私钥。验收时同时模拟串行重复、并发重复和提交后中断,核对业务结果确实只有一份。