Yihui’s Blog

同一订单在微信和支付宝同时支付,会怎么样?

日期:2026-07-11
标签:#面试 #八股 #后端 #订单 #支付 #场景题

一句话答案

不能只靠本地订单状态阻止两家渠道都扣款;应以支付尝试单隔离渠道,并通过订单状态原子确认唯一成功支付,迟到的另一笔成功支付自动退款。

面试口语版

订单可以有多个支付尝试单,每次选择渠道生成独立 paymentAttempt,但业务订单最终只能确认一笔支付。创建新尝试时应尽量关闭旧渠道支付单,不过关闭和付款仍可能并发,所以两个渠道都有可能真实扣款。回调到达后用数据库条件更新把订单从待支付改为已支付,更新成功的一笔成为生效支付;另一笔即使回调时发现订单已支付,也要记录渠道流水,然后幂等发起原路退款。最后通过主动查询和账单对账处理回调丢失。

数据关系

Order 1 --- N PaymentAttempt
PaymentAttempt: channel, outTradeNo, channelTradeNo, amount, status
Order: paidAttemptId, status, paidAmount

关键细节

  • 同一个渠道商户订单号应唯一,不同支付尝试不能复用。
  • 本地 CAS 只能决定哪笔支付生效,无法撤销已在第三方完成的扣款。
  • 退款失败要进入重试、告警和人工处理,状态不能假装成功。
  • 金额、币种、商户号和订单号都必须在回调验签后核对。

面试官追问

  1. 两个回调同时到达如何保证唯一?
  2. 回调丢失怎么办?
  3. 自动退款一直失败怎么办?
  4. 如何进行支付渠道日终对账?

面试官追问参考答案

1. 两个回调同时到达如何保证唯一?

订单表执行 UPDATE ... SET paid_attempt_id=? WHERE status='UNPAID',只有一个事务受影响行数为 1,成为生效支付。每个渠道回调仍要独立落支付流水;失败竞争的已成功渠道进入退款流程,而不是丢弃回调。

2. 回调丢失怎么办?

支付单创建后有定时任务主动查询未知或处理中状态,用户回到结果页也可触发受限查询。再结合渠道日账单对账补齐;回调、查询和对账都调用同一个幂等状态机。

3. 自动退款一直失败怎么办?

保持待退款/退款异常状态,按同一退款号查询后重试,区分可重试与永久错误。超过阈值告警并生成带 SLA 的人工工单,冻结相应资金和业务操作,日终对账持续跟踪,直到渠道确认成功或人工结案。

4. 如何进行支付渠道日终对账?

下载渠道账单,按商户订单号和渠道流水号与本地支付/退款流水做全量或增量比对,分类为金额不一致、渠道有本地无、本地有渠道无和状态不一致。差异进入自动补单、退款或人工处理,并生成可审计的对账批次和结果。

学习清单

  • 区分业务订单和支付尝试单。
  • 能解释原子确认、迟到退款与对账兜底。
维护与整理 · Yihui在 GitHub 上编辑

继续阅读

浏览全部文章