网关在未及时收到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使用稳定的网关交易号。

上线前回放测试

  1. 同一payload连续发送两次;
  2. 两个并发请求同时发送;
  3. 先收到capture再收到authorize;
  4. 金额或币种不一致;
  5. 处理一半进程崩溃后再次投递。

每种场景核对订单只产生一笔Capture和一张Invoice。若线上已重复扣款,先按支付平台流程退款并人工核账,不要仅删除Magento交易记录掩盖问题。