GA4 and Magento sales reports don't match: taxes, shipping, refunds, statuses

|Dan Giura
GA4 and Magento sales reports don't match: taxes, shipping, refunds, statuses

TL;DR

A revenue difference between the Magento sales report and GA4 is usually four separate differences stacked on top of each other, and they do not all point the same direction. Tax and shipping settings move the number up or down depending on how you configured them. Refunds move it down, but only if you are sending them. Excluded order statuses hold orders back until the cron pushes them. Currency configuration quietly rescales everything. Work through those four before you touch a tag, then accept that a residual gap remains and is not a bug you can configure away.

Key Takeaways

  • The GA4 extension has settings that decide whether the grand total sent to GA4 includes tax and shipping [1]. If nobody has looked at them since install, that is the first thing to check, because it changes every transaction rather than a subset.
  • Refund is one of the events the extension sends through the Measurement Protocol, alongside Purchase, Sign Up, Login, View Item, View Item List, Select Item, Search, Add to Wishlist, Add to Cart, View Cart, Begin Checkout, Add Payment Info, and Add Shipping Info [1].
  • Order-status exclusion means some orders are deliberately not tracked yet. A cron job pushes orders server-side once they leave an excluded status, so a same-day comparison will show a shortfall that resolves later [1].
  • The order grid gets a column marking which orders were sent via the Measurement Protocol [1]. That column, not a GA4 report, is the fastest way to find out whether a specific missing order ever left Magento.
  • Compare like for like or the exercise is meaningless: same date range, same timezone, same order statuses, same currency, and a GA4 window inside the data retention period you are actually on, which is 2 or 14 months on the free tier [3].
  • Some of the gap is structural. Browser blocking, consent choices, and cross-device behaviour all remove browser-side data, and we do not publish a recovery percentage for what server-side sending returns.
  • Composer is the only supported install path since v1.16.0 (January 7, 2026), and the current documented version is v1.17.3, released August 3, 2026 [1].

Start with the arithmetic, not the tracking code

Most reconciliation sessions go wrong in the first five minutes, because someone opens GA4, opens the Magento sales report, sees two numbers that differ, and starts debugging the tag.

The two reports are not measuring the same object. Magento's sales report counts orders in your store, filtered by status, in your base currency, over a date range in your store's timezone. GA4 counts purchase events it received, attributed to sessions, in the property's reporting currency, over a date range in the property's timezone. Every one of those clauses is a place where the two can legitimately disagree while both are working correctly.

So the first task is not "why is GA4 wrong". It is "what exactly is each of these two numbers counting". Once you have written that down for both sides, the remaining difference is small enough to attribute to specific causes, and the four settings below are where those causes live.

Does your GA4 revenue include tax and shipping?

This is the setting that produces the largest consistent offset, and it is the one most often left at whatever it was on the day the extension was installed.

The extension exposes grand-total settings that control whether tax and shipping are included in the value sent to GA4 [1]. The same block repeats once per social platform integration and covers both its pixel and its API addon, defaulting to the GA4-level setting, so a platform that has been changed can disagree with GA4 [1]. That is worth checking even if GA4 itself reconciles, because it explains why your ad platforms report a different revenue number than your analytics does.

There is no single correct configuration. The correct configuration is the one that matches what you are comparing GA4 against. If your finance team reconciles on net product revenue, exclude tax and shipping and stop comparing to a grand-total report. If your marketing team judges ROAS on what the customer actually paid, include them. Pick one, write it down somewhere your successor will find it, and make the Magento-side report you compare against match.

Currency belongs in this same pass. GA4 PRO adds a choice of which currency the value is reported in [1]. On a single-currency store this setting is invisible. On a store with multiple presentment currencies it is the difference between a report that reconciles and one that never will, and the symptom looks like random noise rather than a systematic offset, which is why it survives so many reconciliation attempts.

Refunds are the one that compounds

Purchase events are sent once and then are done. Refunds keep arriving for weeks after the order, which means an unsent refund does not produce a fixed error. It produces an error that grows every month you leave it.

Refund is in the Measurement Protocol event list the extension sends server-side, switched on at Admin > WeltPixel > GA4 PRO > Enable Measurement Protocol Tracking Configuration [1]. If it is not switched on, GA4 holds the full original revenue for orders you have partly or fully refunded, and the divergence between GA4 and your sales report widens continuously while every individual transaction looks correct. You have probably seen this shape before without naming it: the gap was fine at launch and is now embarrassing, and nothing in the setup changed.

Server-side is the natural place for refunds to be sent from, because a refund happens in the admin or through an API call, not in a browser session. There is no page for a browser tag to fire on. That is also the reason refund completeness is one of the differences between the STANDARD and PRO tiers of the extension [1].

Our older write-up on GA4 events not being tracked when using Measurement Protocol covers the failure modes when server-side events are configured but not arriving, which is the next thing to check once you know refunds are enabled.

Which order statuses are you actually tracking?

The extension supports excluding specific order statuses from tracking [1]. Most stores that use it exclude something like pending payment or fraud review, so that orders which never complete do not inflate GA4.

The consequence is a timing gap. An order sitting in an excluded status has not been sent. When it leaves that status, a cron job pushes it server-side, using a Measurement Protocol purchase payload that was stored from the frontend event at the time of the order [1]. Admin-created orders are handled the same way, through the server rather than a browser.

Two practical implications follow. First, a comparison run on today's data will show GA4 behind Magento by roughly the volume of orders currently sitting in excluded statuses. That is the extension working as configured. Give it the settlement time your payment flow needs before you compare. Second, if orders are leaving an excluded status and still not appearing, the cron is the suspect, not the tracking configuration. Check that Magento cron is running at all before you check anything else. On a surprising number of stores it is not.

The order grid column is the audit tool

Everything above is a hypothesis about why a category of orders is missing. The order grid column is how you test the hypothesis on one specific order.

GA4 PRO adds a column to the Magento order grid marking which orders were sent via the Measurement Protocol [1]. That converts an unanswerable question into a two-minute one. A customer emails about an order, the order does not appear in GA4, and instead of reasoning about it you filter the grid and read the column.

If the column says the order was sent and GA4 does not show it, the problem is downstream: the property, the date range, the timezone, a filter in the report, or the reporting currency. If the column says it was not sent, the problem is upstream and one of the earlier sections applies. That one question decides where you look next.

Two more artifacts sit alongside it. The extension writes a ga4.log file for Measurement Protocol activity, and there is a Debug Collect endpoint option for inspecting what is being sent [1]. Consent state is logged alongside server-side events for debugging, which is useful when you are trying to reconstruct what a specific session did rather than guessing [1]. The GTM container and the frontend pixels honour whichever consent method you configured.

For a full list of what the extension emits on the browser side, see which ecommerce events the GA4 extension tracks.

How do you compare the two reports like for like?

Build the comparison once, deliberately, and keep it. Reconciliation done ad hoc from memory produces a different answer every time, which is how a store ends up with three people holding three opinions about the size of the gap.

Fix these before you compare anything:

Dimension What to align
Date range Same start and end, and check the timezone on both sides
Order status Magento report filtered to the same statuses the extension tracks
Value basis Grand total with or without tax and shipping, matched to the extension setting
Currency Magento base or presentment currency versus the GA4 reporting currency
Refunds Decide whether both sides are net of refunds, or neither
Window age Inside GA4 data retention, which is 2 or 14 months on the free tier [3]

Run it on a closed month rather than a live one. Orders in flight, refunds not yet issued, and statuses not yet settled all move during the current month, so the number you are chasing keeps changing while you chase it.

One dated change worth knowing if you also compare conversion counts rather than revenue: GA4 added configurable conversion lookback windows on August 11, 2026 [4]. Changing that setting changes which conversions land in which reporting period, so a comparison that spans the day you changed it is comparing two different definitions.

Some of the gap is structural

Here is the part most reconciliation guides skip, and it is the part worth being honest about.

Browser-side tracking loses data. Blockers, browser privacy restrictions, and visitors who decline cookies all remove events before they reach anything. The extension's own documentation states that server-side events are not affected by ad blockers or browser restrictions, and that server-side sending avoids missing and duplicate orders [1]. Both of those are true and both are scoped. We do not publish a percentage for how much data server-side sending recovers, because the honest answer varies by store, by traffic mix, and by what your consent configuration does, and any single number would be a made-up one.

Platform changes contribute too. Magento 2.4.9 shipped on May 12, 2026 and made the Google Analytics module's Content Security Policy whitelist independent of the Adwords module [2]. Version 2.4.8 had already allowed region1.analytics.google.com when Google Analytics is enabled, which had been producing CSP errors for EU visitors [5], and payment pages have run CSP in restrict mode since 2.4.7 for PCI DSS 4.0, with other pages in report-only mode [6]. If a browser-side gap appeared right after an upgrade, that is the sequence to check, and our guide on updating Magento 2 and installing security patches covers the mechanics.

If what you are chasing is missing transactions specifically rather than a revenue offset, the dedicated write-up on missing transactions and revenue in GTM and GA4 works through that case in more detail.

FAQ

Why is GA4 revenue higher than my Magento sales report?

Usually because the value being sent includes tax and shipping while the Magento report you are comparing against does not, or because refunds are not being sent to GA4 so the original transaction value stays in place [1]. Check the grand-total settings first, then whether Refund events are enabled.

Why is GA4 revenue lower than Magento?

Two common causes. Orders sitting in excluded statuses have not been pushed yet, and a cron job sends them once they leave those statuses [1]. Beyond that, browser-side events are lost to blockers, privacy restrictions, and declined consent. That part of the gap is structural.

How do I measure the gap on my own store?

Run the comparison on a closed month, not the current one, so nothing moves underneath you. The per-order detail lives in the Magento order grid, in the column GA4 PRO adds showing which orders were sent via the Measurement Protocol [1], so you can go from a total to the individual orders behind it in the same screen.

Does the extension send refunds automatically?

Refund is one of the Measurement Protocol events available server-side, so it needs the Measurement Protocol configuration enabled rather than being on by default in every setup [1]. Refund completeness is one of the PRO-tier differences.

Which Magento versions does this apply to?

Check what you are on before you check anything else: run bin/magento --version from the console on the server, then match that against the supported range in the extension's user guide [1]. Installation is Composer-only, so composer show will also tell you which extension version you have.


If you are running Magento 2 and want refunds, order-status handling, and the order grid audit column handled inside the admin, the Google Analytics 4 PRO extension with server-side tracking is where those settings live.

Before you do anything else, open the order grid, sort by the Measurement Protocol column, and look at yesterday. That takes a minute and tells you which half of this article you need.

Sources

  1. WeltPixel Google Analytics 4 User Guide, v1.17.3, August 3, 2026. https://docs.weltpixel.com/GA4/User-Guide-WeltPixel-Google-Analytics-4.html
  2. Adobe Experience League, Adobe Commerce 2.4.9 release notes. https://experienceleague.adobe.com/en/docs/commerce-operations/release/notes/adobe-commerce/2-4-9
  3. Google Analytics Help, data retention settings (2 or 14 months on the free tier; up to 50 months on Analytics 360). https://support.google.com/analytics/answer/7667196
  4. Google Analytics Help, "What's new in Analytics", configurable conversion lookback windows, released August 11, 2026. https://support.google.com/analytics/answer/9164320
  5. Adobe Experience League, Adobe Commerce 2.4.8 release notes. https://experienceleague.adobe.com/en/docs/commerce-operations/release/notes/adobe-commerce/2-4-8
  6. Adobe Experience League, Adobe Commerce 2.4.7 release notes. https://experienceleague.adobe.com/en/docs/commerce-operations/release/notes/adobe-commerce/2-4-7

Ready to upgrade your tracking?

Server-side tracking for Magento and Shopify — accurate data, better attribution, full privacy compliance.