购物车显示 199.99,订单或支付网关收到 200.00,这类问题不能靠在最后一步强行减 0.01。Magento 的金额同时存在基础币种和订单币种,并在 item、address、quote 与 order 多层累计;自定义 Totals 在错误层级舍入,会把一分钱放大成对账差异。

先锁定差异第一次出现的位置

SELECT entity_id, quote_currency_code, base_currency_code,
       subtotal, base_subtotal, discount_amount, base_discount_amount,
       tax_amount, base_tax_amount, grand_total, base_grand_total
FROM quote WHERE entity_id=456;

SELECT entity_id, increment_id, order_currency_code, base_currency_code,
       subtotal, base_subtotal, discount_amount, base_discount_amount,
       tax_amount, base_tax_amount, grand_total, base_grand_total
FROM sales_order WHERE quote_id=456;

Quote 已经错,查看 collectTotals 和价格规则;Quote 正确、Order 错,检查 quote 转 order 的扩展字段与 observer;Order 正确、支付请求错,则查支付模块把 major unit 转 minor unit 的代码。

逐个商品比较,而不是只看总计

SELECT item_id, sku, qty, row_total, base_row_total,
       tax_amount, base_tax_amount, discount_amount, base_discount_amount,
       row_total_incl_tax, base_row_total_incl_tax
FROM quote_item WHERE quote_id=456 AND parent_item_id IS NULL;

SELECT item_id, sku, qty_ordered, row_total, base_row_total,
       tax_amount, base_tax_amount, discount_amount, base_discount_amount,
       row_total_incl_tax, base_row_total_incl_tax
FROM sales_order_item WHERE order_id=789 AND parent_item_id IS NULL;

组合商品要排除 parent/child 重复累计。差额只出现在某个商品时,检查该商品税类、目录价含税配置和折扣分摊;差额集中在运费,查看 shipping tax class 与免费配送规则。

检查自定义 Total 是否过早 round

// 错误倾向:每个中间步骤都 round
$amount = round($unit * $qty, 2);
$discount = round($amount * $rate, 2);

// 更稳妥:沿用 Magento PriceCurrency 的舍入边界
$amount = $this->priceCurrency->round($calculatedAmount);

不是说任何地方都不能舍入,而是同一金额只能按业务定义的边界舍入。尤其不要一会儿用 PHP round,一会儿用字符串格式化,再让网关乘以 100。

构造能暴露问题的测试组合

用 3 件单价 0.33、含税价、百分比优惠券和运费税组成最小测试,分别记录购物车、结账 review、订单、发票和网关请求金额。若系统支持多币种,再加入非整数汇率。测试目标是找出第一个发生差异的对象,而不是让最终页面“看起来一样”。