Laravel支付回调幂等保障有三种方法:一、订单状态+唯一索引双校验事务法,通过联合唯一索引拦截重复插入并自动回滚;二、“SELECT FOR UPDATE + 状态跃迁”行锁事务法,用行锁与状态条件更新阻断并发重复处理;三、Redis原子锁前置守卫+数据库事务组合法,利用分布式锁确保高并发下仅一请求执行。

如果您在Laravel中处理微信或支付宝支付回调时,遭遇重复通知导致订单状态被多次更新、金额被重复扣减或卡密被重复发放,则说明回调缺乏幂等保障。以下是确保支付回调幂等执行的多种事务处理方法:
一、订单状态+唯一索引双校验事务法
该方法通过数据库唯一约束拦截重复写入,并利用事务包裹状态变更与业务操作,使重复回调在插入阶段即失败并自动回滚,避免脏数据产生。适用于订单表已存在业务主键(如order_no)且需强一致写入的场景。
1、为订单记录表添加联合唯一索引:ALTER TABLE orders ADD UNIQUE INDEX uk_order_no_status (order_no, status);
2、在回调控制器中开启事务并尝试插入幂等标记记录:DB::transaction(function () use ($request) { DB::table('callback_logs')->insert(['order_no' => $request->get('order_no'), 'notify_id' => $request->get('notify_id'), 'created_at' => now()]); });
3、若插入因唯一索引冲突抛出Illuminate\Database\QueryException,捕获后直接返回成功响应,不执行后续逻辑。
二、“SELECT FOR UPDATE + 状态跃迁”行锁事务法
该方法先锁定待处理订单的对应行,再判断其当前状态是否允许继续处理;仅当状态为“待确认”时才更新为“处理中”,从而阻断并发或重试导致的重复执行。适用于需严格控制状态流转顺序的核心支付流程。
1、确保订单表包含状态字段:status ENUM('pending', 'processing', 'paid', 'failed') DEFAULT 'pending',且引擎为InnoDB。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
2、在回调处理闭包内执行带锁查询与条件更新:DB::transaction(function () use ($orderNo) { $order = DB::table('orders')->where('order_no', $orderNo)->where('status', 'pending')->lockForUpdate()->first(); if (!$order) { return; } DB::table('orders')->where('order_no', $orderNo)->update(['status' => 'processing', 'updated_at' => now()]); });
3、完成支付验证与业务更新后,在同一事务中将状态更新为paid或failed。
三、Redis原子锁前置守卫 + 数据库事务组合法
该方法不依赖数据库锁,而是利用Redis的SETNX命令抢占回调处理权,成功获取锁后才进入数据库事务;即使多个请求同时到达,也仅有一个能执行后续逻辑。适合多实例部署、高并发回调场景,且可避免数据库长事务阻塞。
1、生成基于订单号的分布式锁键:$lockKey = 'pay:callback:lock:' . $request->get('order_no');
2、使用Laravel Cache门面尝试加锁(30秒过期):if (!Cache::lock($lockKey, 30)->get()) { return response('Locked', 423); }
3、在加锁成功后立即开启数据库事务执行核心逻辑:DB::transaction(function () use ($request) { /* 验签、查单、更新状态、发卡等 */ });
4、事务完成后主动释放锁:Cache::lock($lockKey)->release();

















