用户连续点两次确认支付,如何保证不会重复扣款

核心考点:接口幂等性,同一个请求执行 N 次,业务结果和执行 1 次完全一致。

误区:只做前端按钮置灰,前端防护只能防普通用户,攻击者可以绕过前端直接发 HTTP 请求,不能作为兜底。

一、问题根源(两次点击带来的并发场景)

用户快速双击,产生两个几乎同时到达后端的支付请求:

  1. 请求 A、请求 B 同时查询订单状态,都查到「待支付」;
  2. 两个线程都进入扣款逻辑,同时调用支付渠道,造成重复扣款;
  3. 衍生同类问题:网关超时自动重试、第三方支付回调重复推送、MQ 消息重复消费,本质都是幂等问题。

二、多层防御体系(面试标准回答,由浅到深)

1)前端层(第一层,软防护)

点击支付按钮后,立即置灰 + loading,禁用二次点击; 缺点:可绕过,仅优化用户体验,不能保障资金安全。

2)服务端前置防护:Token 防重提交(Redis)

  • 用户进入支付页,后端生成支付 Token存入 Redis,设置过期时间(如 5min);
  • 前端提交支付必须带上这个 Token;
  • 后端用Lua 脚本原子执行:查询 + 删除 Token;
    • Token 存在:执行业务;
    • Token 不存在:返回「请勿重复提交」;

坑:不能分成 get + del 两条命令,并发下两个请求都 get 到存在,同时进入业务,失效。 缺点:Redis 故障时该方案失效,只能做前置挡板,不能做最终兜底。

3)核心方案:订单状态机 + 数据库乐观锁(支付系统真正兜底)

订单状态:待支付(INIT) → 支付中(PAYING) → 已支付(SUCCESS) / 支付失败(FAIL) 状态只能单向流转,禁止非法跳转。

SQL 原子更新(条件更新,原子性):

update order set status=1,version=version+1 
where order_no='ORDER001' and status=0 and version=1;
  • 两个并发请求进来,只有 1 条 SQL 影响行数 = 1,执行成功;
  • 第二条请求 update 返回 0 行,直接拒绝,不执行扣款;
  • 本质:数据库行锁 + 条件更新,是最终保障,不依赖 Redis。

也可以单独建一张幂等记录表,幂等号加唯一索引,业务和幂等记录在同一个事务写入;插入冲突则捕获异常,代表重复请求。

4)Redis 分布式锁(并发控制,辅助方案)

key = 订单号,value = 请求唯一标识,设置过期时间; 用 Lua 脚本原子加锁;业务执行完,Lua 校验 value 是自己的锁才释放。

经典坑(面试官追问重点): 锁过期时间 < 业务执行时间 → 锁提前释放,第二个请求拿到锁,双线程并发扣款; 不能单纯依靠分布式锁做幂等;分布式锁只用来削峰控并发,最终兜底一定是数据库。

5)支付渠道幂等(对接微信 / 支付宝)

调用第三方支付接口时,传入商户侧唯一支付流水号(out_trade_no)。 微信支付宝原生支持:同一个 out_trade_no 多次调用,只会扣一次钱,返回上次结果。

这是对接支付渠道必须做的一层。

6)异步回调幂等 + 对账兜底

  • 支付渠道回调通知会重复推送,回调接口必须用订单 / 支付流水号做幂等校验;已处理直接返回成功,不再更新订单;
  • 定时对账:每日拉取渠道账单,和本地订单流水比对,发现单边账(渠道扣款本地未更新、重复扣款)自动冲正告警。

三、面试精简回答版本(直接背)

分多层防护:

  1. 前端:点击后置灰按钮,仅做体验优化,不可靠;
  2. 服务端前置:支付页面下发 Redis Token,Lua 脚本原子校验删除,拦截重复提交;
  3. 数据库兜底:采用订单状态机 + 乐观锁,通过条件 update 原子修改订单状态,只有待支付订单才允许进入支付流程;也可以使用幂等表唯一索引兜底;
  4. 调用支付渠道时,传递唯一商户流水号,利用支付渠道自身幂等;
  5. 回调接口做幂等校验,再加上定时对账,作为资金最后兜底。 Redis 分布式锁可以用来控制并发,但不能作为最终幂等保障,因为存在锁超时风险,数据库才是真相源。

四、面试官连环追问(高频)

  1. 只靠 Redis 分布式锁行不行? 不行。锁超时、Redis 主从切换丢锁都会失效,Redis 只是缓存,数据库才是最终数据源。
  2. 乐观锁和悲观锁怎么选? 支付场景并发不算极高,优先乐观锁;高并发争抢严重时才考虑悲观锁select ... for update,悲观锁会带来数据库行锁等待,性能差。
  3. 如果第一次扣款成功,更新订单状态时服务宕机怎么办? 第三方支付回调会重试,回调接口做幂等,恢复订单状态;对账任务发现不一致,自动补偿。
THE END
抢沙发

请登录后发表评论

    暂无评论内容