客户在支付平台看到付款成功,Magento 后台订单却一直停在 Pending Payment。不要先手工改为 Processing;必须确认 Magento 是否收到并验证了服务端通知,否则可能造成重复发货、重复扣款或后续退款无法关联。
把一次支付拆成四个时间点
| 时间点 | 证据 |
|---|---|
| 创建支付 | 订单号、网关支付 ID、请求时间 |
| 客户跳转 | return URL 的 HTTP 状态和参数 |
| 服务端通知 | Webhook 请求 ID、签名验证结果 |
| 订单更新 | 状态历史、invoice、transaction |
SELECT entity_id, increment_id, state, status,
grand_total, total_paid, base_total_paid, created_at, updated_at
FROM sales_order
WHERE increment_id='000000123';
SELECT transaction_id, txn_id, txn_type, is_closed, created_at
FROM sales_payment_transaction
WHERE order_id=789
ORDER BY transaction_id;
有 capture transaction 但 total_paid 为 0,检查模块写交易与更新订单是否处在同一事务;完全没有 transaction,则通知可能没进入 Magento。
Webhook 是否真正到达应用
grep -Rni 'gateway_payment_id_123|000000123' var/log | tail -n 100
grep -E 'POST .*webhook|POST .*callback' /var/log/nginx/access.log | tail -n 50
curl -skI https://shop.example.com/payment/webhook
Webhook 通常要求 POST,curl -I 返回 405 不一定是故障;这里主要确认 DNS、TLS 与路由是否到达站点。WAF、Basic Auth、维护模式或强制地区跳转都可能阻止支付平台调用。
签名失败不能直接跳过验证
对照支付平台控制台的通知时间、事件 ID 和签名版本。检查 raw body 是否在验证前被 JSON 重新编码,代理是否解压或修改了请求体,以及生产/测试密钥是否混用。不要通过“暂时关闭签名”来恢复订单。
幂等处理决定能否安全重放
SELECT parent_id, status, comment, created_at
FROM sales_order_status_history
WHERE parent_id=789
ORDER BY entity_id;
同一 Webhook 可能被发送多次。处理器应以事件 ID 或交易 ID 去重:已完成的 capture 再次到达时返回成功,但不能再次开票。修复后优先从支付平台重放原事件,在测试订单上验证订单状态、invoice、total_paid 和 transaction 四处一致,再处理真实订单。

