用户支付完成回到站点,右上角购物车仍显示原来的商品。刷新后还能进 checkout,严重时同一批商品又下了一单。这个问题要立刻分级:如果只是 minicart 的旧缓存,影响展示;如果数据库里的 Quote 仍是 active,就有重复下单风险。
十分钟内先确认是哪一种
从订单找到 quote_id,然后只读查询:
SELECT entity_id, increment_id, quote_id, state, status, created_at
FROM sales_order
WHERE increment_id = :order_number;
SELECT entity_id, is_active, reserved_order_id, items_count, customer_id, updated_at
FROM quote
WHERE entity_id = :quote_id;如果 Quote 已经 is_active = 0,重新调用后端 cart API 通常拿不到这辆活动购物车,问题更偏向前端 customer-data、PWA store 或 localStorage 没清。若 Quote 仍为 1,则继续查订单提交链路。
数据库已经正确:清的是前端“身份”,不是服务器缓存
Luma/Hyvä/PWA 的处理方式不同,但共同点是下单成功后要让当前 cart 标识失效,并重新加载购物车区块。只做 cache:clean 解决不了某个浏览器 localStorage 里的旧 cart_id。
我会在成功页打开 Network,确认页面没有继续查询旧 cart;再开一个新标签页,看购物车状态是否从持久化存储恢复。若只有支付网关回跳路径失败,而货到付款正常,通常是回跳页面绕过了标准成功处理。
Quote 真的仍 active:沿“创建订单”边界查
标准提交过程中,Quote 会与订单建立关系并结束活动状态。定制支付模块常见的错误是:
- 先创建订单,捕获后续异常时又把 Quote 设回 active,却没有判断订单已存在;
- 为了失败后恢复购物车,在支付 callback 和用户 return URL 两条路径都执行恢复;
- 插件包住 place order,提交成功后抛异常,补偿逻辑把状态回滚了一半;
- 异步下单时,前端把“已接收”当“失败”,继续沿旧 cart 操作。
把订单号、quote_id、payment transaction ID 和请求 ID 串成时间线,通常能看到是哪一个回调最后修改 Quote。不要只搜用户看到成功页的那次请求,支付平台的服务器 callback 可能更早或更晚到达。
为什么不能直接批量把这些 Quote 改成 0
对“已经有唯一有效订单”的单条 Quote,停用看起来合理,但批量脚本若没有处理并发、重复订单和失败支付,会把仍需恢复的购物车也关掉。先找出每个 active Quote 对应的订单数量和状态;存在两张订单时,重点已不是清购物车,而是支付、库存和取消哪张订单。
修复后我会测试四条路径:普通支付成功、用户关闭回跳页、支付成功但 return 超时、支付失败后重试。每条都要求一辆 Quote 最多对应一个预期订单,成功后旧 cart 不再可写,失败时则能明确创建新 cart 或受控恢复。购物车图标变成 0 只是表面,幂等性才是最后的验收标准。

