首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >支付请求幂等性设计:从原理到落地,杜绝重复扣款

支付请求幂等性设计:从原理到落地,杜绝重复扣款

原创
作者头像
tcilay
发布2025-11-26 18:02:05
发布2025-11-26 18:02:05
1.3K0
举报

支付请求幂等性设计:从原理到落地,杜绝重复扣款

支付场景中,“重复支付” 是最致命的问题之一 —— 用户点击两次支付按钮、网络延迟导致系统重试、第三方支付回调重复通知,都可能导致 “一笔订单扣两次款”。一旦发生,不仅会引发用户投诉,还可能造成资金损失与合规风险。而解决这一问题的核心,就是保证支付请求的幂等性

本文结合电商、金融支付的实战经验,从 “为什么需要幂等性”“重复请求的来源”“具体实现方案” 到 “落地避坑”,完整拆解支付幂等性的设计逻辑,提供可直接复用的技术方案。

一、先搞懂:支付场景的幂等性是什么?

1. 幂等性的定义

在计算机领域,幂等性是指:同一操作无论执行多少次,最终结果都是一致的

对于支付请求,幂等性的核心要求是:

  • 同一笔订单,无论用户发起多少次支付请求,都只扣一次款;
  • 同一笔支付回调,无论第三方(如微信支付、支付宝)重复通知多少次,都只更新一次订单状态;
  • 重复操作不会产生额外的业务影响(如重复生成支付记录、重复发送扣款短信)。

2. 为什么支付场景必须保证幂等性?

支付的核心是 “资金安全”,幂等性是资金安全的底线:

  • 若不保证幂等性:用户因网络卡顿重复点击支付,可能导致 “一笔订单扣两次款”(资损风险);
  • 若不保证幂等性:第三方支付回调重复通知,可能导致 “订单已支付但重复回调更新状态”(业务混乱);
  • 合规要求:金融支付场景需满足 “一笔交易一次清算”,幂等性是合规的基础。

二、支付请求重复的 4 个常见场景

在设计方案前,先明确 “重复请求从哪来”,才能针对性解决:

重复场景

具体描述

典型案例

用户重复操作

用户点击支付按钮后,因页面无响应再次点击(或连续点击)

电商下单后,用户快速点击 “微信支付” 按钮 3 次

网络延迟 / 超时重试

支付请求发送后,因网络延迟未收到响应,客户端 / 服务端触发重试

移动端支付请求超时,APP 自动重试 2 次

系统故障重发

服务端处理支付时宕机,重启后重新接收未完成的请求

支付服务处理中数据库宕机,恢复后重放请求

第三方支付回调重复通知

微信支付 / 支付宝的支付结果回调,因网络波动重复发送

支付宝回调通知超时,重复发送 3 次支付成功通知

关键结论:重复请求无法避免(用户操作、网络、系统都可能导致),必须通过技术手段让重复请求 “无害”。

三、核心方案:支付幂等性的 5 种实现方式(按易落地排序)

支付幂等性的实现核心是 “唯一标识 + 状态校验”—— 用唯一标识区分请求,用状态校验判断是否允许执行,以下是 5 种主流方案,覆盖不同场景:

方案 1:唯一订单号(幂等号)—— 最基础、最常用

原理

全局唯一的订单号作为幂等标识,支付请求必须携带该订单号,服务端通过订单号判断是否已处理:

  1. 下单时生成唯一订单号(如ORDER202511210001),关联用户、金额等核心信息;
  2. 用户支付时,请求携带该订单号;
  3. 服务端接收请求后,先查询 “该订单号是否已支付 / 处理中”;
  4. 若已处理:直接返回成功结果;若未处理:执行支付逻辑;若处理中:返回 “处理中”(避免并发重复)。
实现步骤(Java+MySQL)
  1. 订单表设计(核心字段):
代码语言:javascript
复制
CREATE TABLE `payment_order` (  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',  `order_no` varchar(64) NOT NULL COMMENT '唯一订单号(幂等标识)',  `user_id` bigint NOT NULL COMMENT '用户ID',  `amount` decimal(10,2) NOT NULL COMMENT '支付金额',  `status` tinyint NOT NULL COMMENT '支付状态:0=待支付,1=支付中,2=已支付,3=支付失败',  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  PRIMARY KEY (`id`),  UNIQUE KEY `uk_order_no` (`order_no`) COMMENT '订单号唯一索引(确保幂等)') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '支付订单表';
  • 关键:order_no加唯一索引,避免重复创建订单;status字段用于状态校验。
  1. 服务端支付接口实现
代码语言:javascript
复制
@Servicepublic class PaymentService {    @Autowired    private PaymentOrderMapper orderMapper;    @Autowired    private PayGatewayClient payGateway; // 调用第三方支付网关(如微信支付)        /**     * 支付接口(保证幂等性)     * @param orderNo 唯一订单号(幂等标识)     * @param userId 用户ID     * @param amount 支付金额     * @return 支付结果     */    @Transactional    public PaymentResultDTO pay(String orderNo, Long userId, BigDecimal amount) {        // 步骤1:查询订单(通过唯一订单号)        PaymentOrder order = orderMapper.selectByOrderNo(orderNo);        if (order != null) {            // 步骤2:已处理过,直接返回结果            if (order.getStatus() == 2) { // 已支付                return PaymentResultDTO.success("支付成功", orderNo);            } else if (order.getStatus() == 1) { // 支付中                return PaymentResultDTO.processing("支付处理中,请稍后查询", orderNo);            } else if (order.getStatus() == 3) { // 支付失败,允许重试                return processPayment(orderNo, userId, amount);            }        }        // 步骤3:未处理,执行支付逻辑        return processPayment(orderNo, userId, amount);    }        /**     * 核心支付逻辑(抽离复用)     */    private PaymentResultDTO processPayment(String orderNo, Long userId, BigDecimal amount) {        // 步骤1:创建订单(唯一索引保证不会重复创建)        PaymentOrder newOrder = new PaymentOrder();        newOrder.setOrderNo(orderNo);        newOrder.setUserId(userId);        newOrder.setAmount(amount);        newOrder.setStatus(1); // 状态设为“支付中”(防止并发重复)        try {            orderMapper.insert(newOrder);        } catch (DuplicateKeyException e) {            // 并发场景下,其他线程已创建订单,直接查询返回            return pay(orderNo, userId, amount);        }                try {            // 步骤2:调用第三方支付网关(如微信支付统一下单接口)            PayGatewayResponse gatewayResp = payGateway.unifiedOrder(orderNo, amount);            if (gatewayResp.isSuccess()) {                // 步骤3:支付成功,更新状态为“已支付”                orderMapper.updateStatusByOrderNo(orderNo, 2);                return PaymentResultDTO.success("支付成功", orderNo);            } else {                // 步骤4:支付失败,更新状态为“支付失败”                orderMapper.updateStatusByOrderNo(orderNo, 3);                return PaymentResultDTO.fail("支付失败:" + gatewayResp.getMsg(), orderNo);            }        } catch (Exception e) {            // 步骤5:异常情况,更新状态为“支付失败”            orderMapper.updateStatusByOrderNo(orderNo, 3);            log.error("支付异常,订单号:{}", orderNo, e);            return PaymentResultDTO.fail("支付异常,请重试", orderNo);        }    }}
适用场景与优缺点
  • 适用场景:单体应用、订单驱动的支付(如电商订单支付)、同步支付请求;
  • 优点:实现简单,无额外依赖(仅用订单表),兼容性强;
  • 缺点:依赖订单号的唯一性,若订单号生成规则泄露可能被恶意利用(需配合签名验证)。

方案 2:令牌机制(Token)—— 适合前端重复提交

原理

针对 “用户重复点击” 场景,通过 “预生成令牌” 控制支付请求的唯一性:

  1. 前端发起支付前,先调用 “获取支付令牌” 接口,服务端生成唯一 Token(如TOKEN_123456),存入 Redis(设置 15 分钟过期);
  2. 前端携带该 Token 发起支付请求;
  3. 服务端校验 Token:若 Token 不存在(已使用 / 过期),拒绝请求;若存在,执行支付逻辑并删除 Token(或标记为已使用)。
实现步骤(Java+Redis)
  1. 获取令牌接口
代码语言:javascript
复制
@RestController@RequestMapping("/api/pay")public class PaymentController {    @Autowired    private StringRedisTemplate redisTemplate;    private static final String TOKEN_PREFIX = "pay:token:";        /**     * 获取支付令牌(前端支付前调用)     * @param userId 用户ID     * @return 唯一Token     */    @GetMapping("/get-token")    public ResultDTO getPayToken(Long userId) {        // 生成唯一Token(UUID+用户ID,确保唯一性)        String token = "TOKEN_" + UUID.randomUUID().toString().replace("-", "") + "_" + userId;        // 存入Redis,过期时间15分钟(避免Token长期有效)        redisTemplate.opsForValue().set(TOKEN_PREFIX + token, userId.toString(), 15, TimeUnit.MINUTES);        return ResultDTO.success(token);    }}
  1. 支付接口(结合 Token 校验)
代码语言:javascript
复制
@Servicepublic class PaymentService {    @Autowired    private StringRedisTemplate redisTemplate;    private static final String TOKEN_PREFIX = "pay:token:";        @Transactional    public PaymentResultDTO payWithToken(String token, String orderNo, Long userId, BigDecimal amount) {        // 步骤1:Token校验(核心幂等逻辑)        String redisKey = TOKEN_PREFIX + token;        String redisUserId = redisTemplate.opsForValue().get(redisKey);        if (redisUserId == null || !redisUserId.equals(userId.toString())) {            // Token不存在/已使用/用户不匹配,拒绝请求            return PaymentResultDTO.fail("无效的支付请求,请刷新页面重试", orderNo);        }                // 步骤2:订单校验(同方案1,双重保障)        PaymentOrder order = orderMapper.selectByOrderNo(orderNo);        if (order != null && order.getStatus() == 2) {            return PaymentResultDTO.success("支付成功", orderNo);        }                try {            // 步骤3:执行支付逻辑(同方案1)            PaymentResultDTO result = processPayment(orderNo, userId, amount);                        // 步骤4:支付完成后,删除Token(标记为已使用)            redisTemplate.delete(redisKey);                        return result;        } catch (Exception e) {            // 异常时不删除Token,允许重试(避免因系统异常导致无法支付)            log.error("支付异常,Token:{},订单号:{}", token, orderNo, e);            return PaymentResultDTO.fail("支付异常,请重试", orderNo);        }    }}
适用场景与优缺点
  • 适用场景:前端重复提交(如按钮连续点击)、移动端支付、需要防止恶意请求的场景;
  • 优点:Token 一次性有效,安全性高,能有效防止无 Token 的恶意支付请求;
  • 缺点:多一步 “获取 Token” 接口调用,增加前端开发成本;依赖 Redis(分布式场景需 Redis 集群)。

方案 3:支付状态机校验 —— 防止状态回退

原理

支付订单的状态变更遵循固定流程(如 “待支付→支付中→已支付”),通过状态机限制 “不允许的状态变更”,避免重复处理:

  • 状态流转规则:0=待支付 → 1=支付中 → 2=已支付 或 3=支付失败;
  • 幂等逻辑:若当前状态是 “已支付” 或 “支付中”,拒绝重复支付请求;只有 “待支付” 或 “支付失败” 状态允许执行支付。
实现示例(状态机校验)
代码语言:javascript
复制
/** * 支付状态机校验(工具类) */public class PaymentStateMachine {    // 状态流转规则:key=当前状态,value=允许流转的目标状态    private static final Map<Integer, List<Integer>> STATE_RULES = new HashMap<>();        static {        // 待支付(0)可流转到:支付中(1)        STATE_RULES.put(0, Collections.singletonList(1));        // 支付中(1)可流转到:已支付(2)、支付失败(3)        STATE_RULES.put(1, Arrays.asList(2, 3));        // 支付失败(3)可流转到:支付中(1)(允许重试)        STATE_RULES.put(3, Collections.singletonList(1));        // 已支付(2)不可流转到任何状态(不允许重复支付)        STATE_RULES.put(2, Collections.emptyList());    }        /**     * 校验状态是否允许流转     * @param currentState 当前状态     * @param targetState 目标状态     * @return true=允许,false=不允许     */    public static boolean checkStateTransition(int currentState, int targetState) {        List<Integer> allowedStates = STATE_RULES.getOrDefault(currentState, Collections.emptyList());        return allowedStates.contains(targetState);    }}// 支付接口中使用状态机校验@Transactionalpublic PaymentResultDTO payWithStateMachine(String orderNo, Long userId, BigDecimal amount) {    PaymentOrder order = orderMapper.selectByOrderNo(orderNo);    if (order != null) {        // 状态机校验:若当前状态不允许流转到“支付中”,直接返回        if (!PaymentStateMachine.checkStateTransition(order.getStatus(), 1)) {            if (order.getStatus() == 2) {                return PaymentResultDTO.success("支付成功", orderNo);            } else {                return PaymentResultDTO.fail("当前订单状态不允许支付", orderNo);            }        }    }    // 后续执行支付逻辑(同方案1)    return processPayment(orderNo, userId, amount);}
适用场景与优缺点
  • 适用场景:复杂支付流程(如分期支付、预授权支付)、需要严格控制状态流转的场景;
  • 优点:状态校验严谨,能防止 “已支付订单重复支付”“支付中订单被重复处理”;
  • 缺点:需维护状态机规则,适合状态流转清晰的场景。

方案 4:防重表 —— 分布式场景的强一致性保障

原理

针对分布式系统(多服务实例),用 “防重表” 记录已处理的支付请求,通过数据库唯一索引保证幂等:

  1. 防重表仅存储 “幂等标识”(如订单号、支付流水号),无其他业务字段;
  2. 支付请求到达后,先向防重表插入幂等标识;
  3. 若插入成功(无重复),执行支付逻辑;若插入失败(已存在),直接返回成功结果。
实现步骤
  1. 防重表设计
代码语言:javascript
复制
CREATE TABLE `payment_idempotent` (  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',  `idempotent_key` varchar(64) NOT NULL COMMENT '幂等标识(订单号/支付流水号)',  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  PRIMARY KEY (`id`),  UNIQUE KEY `uk_idempotent_key` (`idempotent_key`) COMMENT '幂等标识唯一索引') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '支付防重表';
  • 核心:idempotent_key加唯一索引,确保同一标识只能插入一次。
  1. 分布式支付接口实现
代码语言:javascript
复制
@Servicepublic class DistributedPaymentService {    @Autowired    private PaymentIdempotentMapper idempotentMapper;    @Autowired    private PaymentOrderMapper orderMapper;        @Transactional    public PaymentResultDTO distributedPay(String orderNo, Long userId, BigDecimal amount) {        // 步骤1:插入防重表(核心幂等逻辑)        PaymentIdempotent idempotent = new PaymentIdempotent();        idempotent.setIdempotentKey(orderNo); // 用订单号作为幂等标识        try {            idempotentMapper.insert(idempotent);        } catch (DuplicateKeyException e) {            // 插入失败,说明已处理过,查询订单状态返回            PaymentOrder order = orderMapper.selectByOrderNo(orderNo);            if (order != null && order.getStatus() == 2) {                return PaymentResultDTO.success("支付成功", orderNo);            }            return PaymentResultDTO.fail("该订单已提交支付,请稍后查询结果", orderNo);        }                // 步骤2:执行支付逻辑(同方案1)        try {            return processPayment(orderNo, userId, amount);        } catch (Exception e) {            // 步骤3:支付失败,删除防重表记录(允许重试)            idempotentMapper.deleteByIdempotentKey(orderNo);            log.error("分布式支付异常,订单号:{}", orderNo, e);            return PaymentResultDTO.fail("支付异常,请重试", orderNo);        }    }}
适用场景与优缺点
  • 适用场景:分布式系统(多服务实例)、高并发支付(如秒杀支付)、需要强一致性的场景;
  • 优点:不依赖缓存,数据库唯一索引保证强一致性,适合分布式环境;
  • 缺点:多一张防重表,增加数据库存储成本;插入删除操作增加数据库压力(可通过分表优化)。

方案 5:第三方支付回调幂等 —— 处理重复通知

原理

第三方支付平台(微信支付、支付宝)的支付结果回调可能重复发送,需单独处理回调的幂等性:

  1. 第三方回调会携带唯一的 “支付流水号”(如微信的transaction_id、支付宝的trade_no);
  2. 服务端接收回调后,先校验该流水号是否已处理;
  3. 若已处理:直接返回第三方 “成功” 标识(避免第三方重复通知);若未处理:更新订单状态并记录流水号。
实现步骤(以微信支付回调为例)
  1. 回调记录表设计(存储已处理的回调流水号):
代码语言:javascript
复制
CREATE TABLE `payment_callback_record` (  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',  `third_party_no` varchar(64) NOT NULL COMMENT '第三方支付流水号(如微信transaction_id)',  `order_no` varchar(64) NOT NULL COMMENT '关联订单号',  `callback_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '回调时间',  PRIMARY KEY (`id`),  UNIQUE KEY `uk_third_party_no` (`third_party_no`) COMMENT '第三方流水号唯一索引') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '支付回调记录表';
  1. 微信支付回调接口实现
代码语言:javascript
复制
@RestController@RequestMapping("/api/pay/callback")public class WechatPayCallbackController {    @Autowired    private PaymentCallbackRecordMapper callbackMapper;    @Autowired    private PaymentOrderMapper orderMapper;        /**     * 微信支付回调接口(保证幂等性)     */    @PostMapping("/wechat")    public String wechatPayCallback(@RequestBody String xmlData) {        // 步骤1:解析微信回调数据(获取transaction_id、order_no、result_code等)        WechatCallbackDTO callbackDTO = parseWechatCallback(xmlData);        String thirdPartyNo = callbackDTO.getTransactionId(); // 第三方唯一流水号        String orderNo = callbackDTO.getOutTradeNo(); // 商户订单号                // 步骤2:校验回调是否已处理(幂等核心)        PaymentCallbackRecord record = callbackMapper.selectByThirdPartyNo(thirdPartyNo);        if (record != null) {            // 已处理,返回微信“成功”标识(避免重复回调)            return buildWechatSuccessXml();        }                // 步骤3:校验支付结果(仅处理支付成功的回调)        if (!"SUCCESS".equals(callbackDTO.getResultCode())) {            log.error("微信支付回调失败,订单号:{},原因:{}", orderNo, callbackDTO.getErrCodeDes());            return buildWechatFailXml("支付失败");        }                try {            // 步骤4:更新订单状态为“已支付”            int updateCount = orderMapper.updateStatusByOrderNo(orderNo, 2);            if (updateCount == 0) {                log.error("订单不存在或已处理,订单号:{}", orderNo);                return buildWechatSuccessXml(); // 仍返回成功,避免重复回调            }                        // 步骤5:记录回调流水号(标记为已处理)            PaymentCallbackRecord newRecord = new PaymentCallbackRecord();            newRecord.setThirdPartyNo(thirdPartyNo);            newRecord.setOrderNo(orderNo);            callbackMapper.insert(newRecord);                        // 步骤6:返回成功标识            return buildWechatSuccessXml();        } catch (Exception e) {            log.error("微信支付回调处理异常,订单号:{}", orderNo, e);            // 回调处理失败,返回失败标识,微信会重试(需做好重试机制)            return buildWechatFailXml("处理异常");        }    }        // 工具方法:解析微信回调XML、构建返回XML(省略)    private WechatCallbackDTO parseWechatCallback(String xmlData) { /* ... */ }    private String buildWechatSuccessXml() { /* ... */ }    private String buildWechatFailXml(String msg) { /* ... */ }}
关键注意点
  • 必须用第三方提供的 “唯一流水号” 作为幂等标识,而非商户订单号(避免同一订单多次支付的回调冲突);
  • 回调处理成功后,无论订单状态是否更新成功,都需返回第三方 “成功” 标识(否则第三方会持续重复回调);
  • 回调接口需支持幂等重试(如订单不存在时仍返回成功,避免第三方重复通知)。

四、实战案例:电商支付幂等性完整流程

结合以上方案,以电商支付为例,梳理完整的幂等性保障流程:

  1. 下单阶段:生成唯一订单号ORDER202511210001,订单状态设为 “待支付”;
  2. 前端支付前:调用 “获取 Token” 接口,获取 TokenTOKEN_XXX;
  3. 支付请求阶段:前端携带 Token + 订单号发起支付,服务端执行:
    • Token 校验(方案 2)→ 订单状态校验(方案 3)→ 防重表插入(方案 4)→ 调用第三方支付;
  4. 第三方回调阶段:微信支付回调携带transaction_id,服务端执行:
    • 回调流水号校验(方案 5)→ 更新订单状态→ 记录回调记录;
  5. 重复请求处理:用户重复点击支付时,服务端通过 Token / 防重表 / 订单状态直接返回结果,不重复扣款。

五、避坑指南:支付幂等性的 6 个关键注意事项

  1. 幂等标识必须全局唯一:订单号、Token、第三方流水号等幂等标识,需保证全局唯一(如订单号用 “时间戳 + 随机数 + 用户 ID” 生成);
  2. 并发场景加锁:高并发支付时,需用分布式锁(如 Redis 锁)锁定订单号,避免 “同时插入防重表” 导致的 DuplicateKeyException 过多;
  3. 异常处理需谨慎:支付失败 / 系统异常时,需释放幂等标识(如删除 Token、防重表记录),允许用户重试;
  4. 避免状态回退:已支付的订单不允许再次支付(状态机限制),防止 “已支付订单重复扣款”;
  5. 第三方回调必须幂等:第三方回调的幂等性独立处理,不依赖订单状态(避免同一订单多次支付的回调冲突);
  6. 日志与监控:记录每笔支付的幂等校验结果、状态变更日志,监控重复请求次数,异常时触发告警(如重复支付请求突增)。

六、方案选型建议

业务场景

推荐方案组合

核心原因

中小电商(单体应用)

唯一订单号(方案 1)+ 状态机(方案 3)

实现简单,无额外依赖,满足基础幂等需求

移动端支付(防重复点击)

令牌机制(方案 2)+ 唯一订单号(方案 1)

Token 防止重复点击,订单号双重保障

分布式电商(多服务)

防重表(方案 4)+ 状态机(方案 3)

分布式强一致性,支持高并发

第三方支付回调

回调流水号校验(方案 5)

针对性处理第三方重复通知

秒杀支付(高并发)

防重表(方案 4)+ Redis 分布式锁

兼顾高并发与强一致性

总结:支付幂等性的核心逻辑

支付幂等性的本质是 “用唯一标识锁定操作,用状态校验控制流程”—— 无论重复请求来自用户、网络还是系统,只要通过 “唯一标识判断是否已处理”,就能保证结果一致。

设计时需遵循 “简单优先,兼顾场景”:

  • 中小团队 / 单体应用:优先用 “唯一订单号 + 状态机”,低成本落地;
  • 分布式 / 高并发场景:叠加 “防重表 + 分布式锁”,保证强一致性;
  • 第三方回调:单独用 “第三方流水号” 做幂等,避免重复通知。

最终,支付幂等性是资金安全的底线,没有 “最优方案”,只有 “最适合业务场景的方案”—— 结合自身业务规模、技术架构选择组合方案,同时做好监控与日志,才能杜绝重复扣款,保障用户与平台的资金安全。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 支付请求幂等性设计:从原理到落地,杜绝重复扣款
    • 一、先搞懂:支付场景的幂等性是什么?
      • 1. 幂等性的定义
      • 2. 为什么支付场景必须保证幂等性?
    • 二、支付请求重复的 4 个常见场景
    • 三、核心方案:支付幂等性的 5 种实现方式(按易落地排序)
      • 方案 1:唯一订单号(幂等号)—— 最基础、最常用
      • 方案 2:令牌机制(Token)—— 适合前端重复提交
      • 方案 3:支付状态机校验 —— 防止状态回退
      • 方案 4:防重表 —— 分布式场景的强一致性保障
      • 方案 5:第三方支付回调幂等 —— 处理重复通知
    • 四、实战案例:电商支付幂等性完整流程
    • 五、避坑指南:支付幂等性的 6 个关键注意事项
    • 六、方案选型建议
    • 总结:支付幂等性的核心逻辑
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档