PWA GraphQL Checkout Shipping Glitch: Currency Rounding & Tax Overbilling in Magento 2.4.9

As e-commerce platforms evolve, particularly with the adoption of headless architectures like Magento PWA Studio, new complexities can arise. A recent GitHub issue (#41074) has brought to light a critical shipping calculation bug affecting Magento Open Source 2.4.9, specifically within the PWA GraphQL checkout flow. This issue, initially reported as a minor currency rounding discrepancy, has been found to have more severe implications for merchants dealing with tax-inclusive shipping prices.

The Core Issue: Unintended Shipping Recalculation During Place Order

The problem manifests when a customer places an order via the GraphQL checkout. The system, unexpectedly, recalculates the shipping amount during the placeOrder mutation. This leads to a slight but persistent difference in the recorded shipping amount between the initial quote and the final order.

The original reporter, AbdulRahim97, detailed how a flat rate shipping of USD 5.00, converted to EUR 3.53 on the quote, would inexplicably update to EUR 3.5335 in both the quote_address and sales_order tables during the checkout finalization. This behavior was observed only with the PWA (GraphQL) checkout, while the traditional Luma checkout remained unaffected.

Technical Deep Dive: The Culprit Plugin

The root cause was identified within the Magento\QuoteGraphQl\Plugin\ShippingMethodValidationRulePlugin. This plugin, during the placeOrder flow, invokes requestShippingRates(). This method, rather than preserving the already calculated and potentially rounded shipping amount, recollects and recalculates the rates. The snippet below illustrates the logic:

 if ($this->getShippingMethod() == $rate->getCode()) {
                    if ($item) {
                        $item->setBaseShippingAmount($rate->getPrice());
                    } else {

                        /** @var StoreInterface */
                        $store = $this->storeManager->getStore();
                        $amountPrice = $store->getBaseCurrency()
                            ->convert($rate->getPrice(), $store->getCurrentCurrencyCode());
                        $this->setBaseShippingAmount($rate->getPrice());
                        $this->setShippingAmount($amountPrice);
                    }

                    $found = true;
                }

This recalculation introduces the minor rounding error, which then gets persisted, creating a discrepancy between what the customer saw and what was recorded.

Beyond Rounding: Structural Overbilling with Tax-Inclusive Shipping

A crucial follow-up from community member 'paales' revealed a more critical manifestation of this bug. For stores configured with tax-inclusive shipping prices (common in EU regions), this recalculation leads to structural overbilling on invoices. When a flat rate shipping of, for example, €4.99 (incl. 21% VAT) is used:

  • The quote address correctly shows shipping_amount = 4.13 (excl. tax) and shipping_incl_tax = 4.99.
  • During placeOrder, the ShippingMethodValidationRulePlugin::afterValidate() calls Address::requestShippingRates().
  • This call overwrites shipping_amount with the raw, tax-inclusive carrier rate (€4.99), while shipping_incl_tax and other tax values remain unchanged.
  • Crucially, no collectTotals() runs afterward. This corrupted address is then converted into the order, resulting in sales_order.shipping_amount = 4.99.
  • The invoice generation process (Magento\Sales\Model\Order\Invoice\Total\Shipping::collect()) then adds order->getShippingAmount() on top of the already correct tax total, effectively double-counting the shipping VAT.

The outcome is an invoice grand total that is higher than what the customer actually paid (e.g., €45.83 invoiced for a €44.97 payment), creating significant accounting and compliance issues for merchants.

Community-Driven Solution and Impact

The issue has been confirmed as reproducible on the latest 2.4-develop branch by the Magento engineering team, highlighting its severity. The community's contribution, particularly 'paales' detailed analysis, not only escalated the understanding of the bug's impact but also proposed a viable workaround:

"Suggested fix: the validation should not rely on requestShippingRates() (which mutates the address) — or it should snapshot and restore shipping_amount/base_shipping_amount around the call."

This bug underscores the importance of rigorous testing, especially for GraphQL APIs and PWA implementations, and highlights how subtle changes in calculation logic can lead to major financial discrepancies. Merchants and developers leveraging Magento 2.4.9 (or potentially other 2.4.x versions) with PWA GraphQL checkout and multi-currency/tax-inclusive shipping configurations should be aware of this issue and consider implementing the suggested patch to ensure accurate order totals and invoicing.

Start with the tools

Explore migration tools

See options, compare methods, and pick the path that fits your store.

Explore migration tools