购物车显示 99.00,进入支付页却变成 98.99 或 109.00,不能只在前端格式化金额。Magento 的总额会经过 subtotal、discount、shipping、tax、gift card 和 grand_total 等 collector,多币种站点还会同时保存 base 金额与显示币种金额。先确定偏差首次出现在哪一层。

固定一个可重复的测试购物车

只保留一个简单商品,记录 SKU、数量、客户组、配送国家、优惠码和币种。浏览器 Network 中保存 totals-information、shipping-information 和 payment-information 三个请求的响应,比较 grand_total 与 base_grand_total。

SELECT entity_id, store_id, is_active, items_count, subtotal, base_subtotal,
       grand_total, base_grand_total, quote_currency_code, base_currency_code
FROM quote WHERE entity_id=12345G

SELECT address_type, subtotal, discount_amount, shipping_amount,
       tax_amount, grand_total, base_grand_total
FROM quote_address WHERE quote_id=12345;

quote 已错误,检查 collector;quote 正确而支付请求错误,则检查支付模块转换和取值。不要直接改 grand_total,下一次 collectTotals 会覆盖它。

检查自定义 totals collector

grep -R --line-number "sales.xml|sort_order|collect(" app/code/*/*/etc app/code/*/*/Model 2>/dev/null
grep -R --line-number "setGrandTotal|setBaseGrandTotal" app/code 2>/dev/null

自定义费用必须同时维护 base 与非 base 金额,并使用加法而不是覆盖已有总额。collector 的 sort_order 若在 tax 或 discount 前后放错,会让含税口径发生变化。临时禁用模块只能在预发布环境做 A/B 对照。

排除四舍五入和币种换算

php bin/magento config:show tax/calculation/algorithm
php bin/magento config:show currency/options/base
php bin/magento config:show currency/options/default
SELECT * FROM directory_currency_rate WHERE currency_from='USD';

一分钱差异常由逐项四舍五入与总额四舍五入混用造成。支付请求应使用订单币种最小单位,并由精确 decimal 金额转换,不能先经过浮点运算。

验收

依次测试无优惠、百分比优惠、固定额优惠、含运费税和切换币种。购物车、结账 totals、订单、发票与网关后台金额必须一致;失败支付重新下单也不能复用旧 quote 总额。

生产环境操作前的检查

针对“Magento 2 购物车金额与支付页面不一致”进行处理时,先记录 Magento 版本、部署模式、问题 Store View、复现时间和最近一次发布。所有 SQL 默认先执行 SELECT;需要 UPDATE、DELETE、补偿命令或目录删除时,必须先确认命中范围并保留可恢复备份。多 Web 节点环境还要比较代码版本、app/etc/env.php 摘要和当前流量节点,避免只修复其中一台。

php bin/magento --version
php bin/magento deploy:mode:show
php bin/magento maintenance:status
php bin/magento indexer:status
php bin/magento cache:status

命令应由 Magento 文件所有者在项目根目录执行。生产站不要同时运行多个全量索引或静态部署任务;若涉及数据库结构、库存、支付或订单,先在脱敏的生产数据副本验证,再安排维护窗口。

建立可比较的排查记录

每次实验只改变一个变量,并保存“操作前值、执行命令、开始时间、结束时间、结果、回滚方式”。至少准备一个正常对象和一个异常对象作对照,例如两个 SKU、两张订单或访客与登录客户。若修复后仅当前对象恢复,而新建对象仍会复现,说明根因尚未消除。

检查点需要记录通过标准
数据层 主键、Store/Website、更新时间、关联行数 关系完整且无孤儿记录
应用层 异常堆栈、模块、Cron/消费者状态 无新异常并可重复执行
缓存与索引 索引模式、版本、源站和公网响应 按预期周期自动更新
业务回归 正常路径、失败路径、重复请求 结果一致且没有重复副作用

修复后的观察窗口

上线后不要只刷新一次页面就结束。至少观察一轮 Cron、队列消费和索引周期,并在两台节点、无痕浏览器及目标 Store View 重复验证。对日志、数据库行数、错误率和响应时间设置临时观察项;确认它们稳定后再移除调试日志。调试日志可能包含客户、订单或令牌信息,采集时要脱敏,问题结束后及时关闭。