The cart isn't active 不是一个模糊的系统异常,它其实说得很具体:这个 cart 对应的 Quote 找到了,但 is_active 已经不是 1。Magento 在购物车成功转成订单后会把原 Quote 置为非活动,这是正常生命周期,不应该再拿它加商品、改地址或重复下单。

麻烦在于,前端经常把这句正常保护机制展示成“结账失败”。用户看到红色提示又点一次,实际上第一笔订单可能已经创建了。

先回答最重要的问题:订单到底有没有生成

不要立刻创建新购物车重试支付。先用服务端日志中的 masked cart ID 找到 Quote,再查它和订单的关系:

SELECT entity_id, is_active, reserved_order_id, customer_id,       created_at, updated_at
FROM quote
WHERE entity_id = :quote_id;
SELECT entity_id, increment_id, status, state, quote_id,       created_at, grand_total
FROM sales_order
WHERE quote_id = :quote_id;

如果已经有订单,前端应该进入成功页或订单查询流程,而不是继续对原 cart 执行 mutation。尤其是支付接口超时、浏览器提前断开时,客户端没收到成功响应,不代表服务端事务没有完成。

旧 cart_id 通常藏在三个地方

第一处是 localStorage 或 IndexedDB。下单成功后前端只清了内存状态,没有删除持久化的 cart token;用户刷新页面,又把它装回来了。

第二处是登录前后的购物车合并。游客 cart 被合并进客户 cart 后,组件树里仍有请求持有游客 ID。你会看到同一秒内两个 cart_id 交替出现。

第三处是重复提交。按钮虽然变灰,但双击、移动端重触或网络重试已经发出了两个 placeOrder。第一个请求将 Quote 变为 inactive,第二个请求随后得到这句报错。

用时间线判断是哪一种

日志现象更像什么问题修复方向
placeOrder 成功后数秒又 query 同一 cart成功回调未清理持久化 ID成功态原子地清 cart,并跳转订单结果
两个 placeOrder 几乎同时到达按钮防重只做在视觉层前端互斥 + 后端幂等确认
登录瞬间两个 cart_id 交替合并后的引用没有统一更新集中管理当前 cart,取消旧请求
没有订单,Quote 却被停用支付扩展或异常恢复逻辑沿 Quote 提交日志查谁修改 is_active

GraphQL 官方错误里也把 CART_NOT_ACTIVE 与 CART_NOT_FOUND 分开了。前者意味着对象存在但生命周期已结束;后者才更像 ID 错误或无权限。因此,遇到 inactive 就无条件新建购物车,会丢掉“订单其实已经生成”的关键信号。

不要把 is_active 手工改回 1

这条 UPDATE 看起来很诱人,但如果订单已经存在,相当于把已结算的商品重新开放下单。库存、优惠券、礼品卡和支付授权都可能重复。正确处理是:

  • 有订单:恢复订单结果展示,不复活 Quote;
  • 没有订单且提交明确失败:由受控的业务流程创建新 cart 或恢复结账内容;
  • 状态来源不明:先记录修改 Quote 的调用栈或插件,再决定补偿。

最后我会用人为制造超时来验收:让 placeOrder 在服务端完成、客户端超时,然后刷新页面。页面必须能识别已有订单,而不是再次提交。这个场景通过了,才说明问题真的解决。