网关在未及时收到2xx时重试Webhook是正常设计。真正的问题是Magento处理端把两次同一事件都当成首次成功,重复调用registerCapture或生成两张Invoice。
先对齐三类ID
- Webhook event_id:每次网关事件唯一;
- payment transaction_id:一次授权或扣款唯一;
- Magento order increment_id:业务订单号。
把原始event_id和transaction_id写入独立事件表,分别加唯一索引。不要只把event_id塞进日志,因为多进程并发时两个请求都可能在看到日志前继续执行。
处理流程应是原子操作
BEGIN; INSERT INTO vendor_webhook_event(event_id,order_id,status) VALUES(?,?, 'processing'); SELECT entity_id,state,status FROM sales_order WHERE entity_id=? FOR UPDATE; -- 校验金额、币种、网关账户和transaction_id -- 仅在未处理时创建capture或invoice UPDATE vendor_webhook_event SET status='done' WHERE event_id=?; COMMIT;
event_id必须有UNIQUE索引。唯一键冲突时读取旧事件状态:done直接返回200;processing进入受控核对,不要再次Capture。
为什么只判断订单状态不够
两个Webhook可同时读到Processing,然后都进入Capture;订单状态变更也可能晚于支付记录。数据库唯一键与行锁才能挡住并发。还要给sales_payment_transaction使用稳定的网关交易号。
上线前回放测试
- 同一payload连续发送两次;
- 两个并发请求同时发送;
- 先收到capture再收到authorize;
- 金额或币种不一致;
- 处理一半进程崩溃后再次投递。
每种场景核对订单只产生一笔Capture和一张Invoice。若线上已重复扣款,先按支付平台流程退款并人工核账,不要仅删除Magento交易记录掩盖问题。

