Why GA4 does not match your Shopify or WooCommerce orders (and how to find out why)

If you have ever put your GA4 purchase numbers next to your Shopify or WooCommerce orders and felt slightly sick, this post is for you.

I am going to assume you know what GA4 is, that you have some kind of purchase tracking set up, and that the numbers do not agree. I am not going to assume you know how that tracking was built, because in my experience most store owners do not, and that is completely fine. Some of this gets technical. I would rather explain it properly than give you a checklist that only works on a perfect setup.

The short version

GA4 will not match your store, and it should not be expected to. A healthy setup usually records most of your orders but not all of them, because some orders never happen in a browser and some browsers do not let tracking through.

 

The useful question is not "why does it not match". It is "which orders are missing, which ones are extra, and is there a pattern". The only reliable way to answer that is to compare the two order by order using the transaction ID, which is what most of this post is about.

 

One thing worth knowing before you start: the total can look fine while the tracking is wrong in two directions at once. I will show you an example of that further down.

What a normal gap looks like

On an ecommerce audit I finished recently, GA4 recorded 88% of the orders the store held, on two separate stores, with values matching order for order. That was a good result, and when we broke the remaining 12% down, almost all of it was explainable.

 

I would not treat 88% as a benchmark, though. The right number for you depends on how many of your orders are subscriptions, how many of your customers use iPhones, which payment methods you offer, and where your customers are. A store with lots of subscription renewals and an Apple-heavy audience might be perfectly healthy at 80%. A store selling one-off products mostly to Android users might have a problem at 90%.

 

That is why the percentage on its own does not tell you much. The breakdown does.

Why there is always a gap

The easiest way to think about this is that Shopify and WooCommerce are the till. Every order goes through them, because that is how the money is taken. It does not matter what browser the customer used, whether they accepted cookies or whether they run an ad blocker. If they paid, the till has the order.

 

GA4 is something else. It is more like someone standing by the door with a clicker, counting what they can see. Most of the time they see most things. But GA4 only knows about an order if the customer's browser loads the tracking code and lets it send the purchase to Google, and there are lots of perfectly normal reasons that does not happen.

Consent

In the UK and Europe, GA4 should only run after a visitor has agreed to analytics cookies. How much you lose when someone declines depends on how Consent Mode is set up. In basic mode, nothing is sent to Google at all for that visitor, so their order simply is not in GA4. In advanced mode, Google receives limited cookieless signals and can use modelling to estimate some of what declined visitors did. That helps with the overall picture, but modelled data is an estimate based on similar visitors who did consent. It does not give you a transaction ID you can reconcile, and it is not included in the BigQuery export.

 

Your store still records every one of those orders, because taking payment has nothing to do with cookie consent.

Ad blockers and privacy browsers

A lot of ad blockers and privacy extensions block requests to Google Analytics outright, and some browsers, Brave for example, do it by default. When that happens, the purchase event never reaches Google. There is no error and nothing to see in GA4, the order is just not there.

Browser tracking prevention

Safari in particular limits how long cookies set by scripts can last, which is how GA4 recognises a returning visitor. This tends to affect attribution more than order counts (more on that below), but in practice Apple devices are often where most of the missing orders turn up. On the audit I mentioned above, roughly one in six UK orders from iPhones, iPads and Windows was missing from GA4, against about one in 55 from Android.

Orders with no browser at all

Subscription renewals, point of sale orders, draft and manual orders, and orders from marketplaces or other sales channels all go into the till without a customer ever loading your confirmation page. GA4 cannot see any of them, and it is not supposed to.

The confirmation page never loads

Most purchase tracking fires on the order confirmation page. If the customer pays through a provider that sends them to another site and they close the tab before coming back, or the page fails to load, the till has the order and GA4 does not.

Your store's analytics are not the till either

One thing worth being clear on: it is only the order count in Shopify or WooCommerce that works like a till. Shopify's own sessions, conversion rate and marketing attribution still rely on what the browser tells it, so they are affected by consent, blockers and cookie limits too, just in their own way. So when I say your store is the source of truth, I mean for how many orders and how much money. Not for where the customers came from.

Step one: make sure you are comparing the same thing

Before you look at a single transaction ID, you need to be sure the two reports are counting the same orders over the same period. A surprising number of "discrepancies" are not tracking problems at all. They are two reports with different rules.

 

These are the ones I check first:

  • Time zones. Your store and your GA4 property can be set to different time zones. If they are, every order placed near midnight lands on a different day in each, and a monthly comparison drifts at both ends.

  • Which orders the store is counting. Shopify and WooCommerce reports can include orders that never went through your website at all: point of sale, draft orders, manual orders, orders from marketplaces or the Shop app, and subscription renewals. GA4 will never see those, because no browser was involved.

  • Order status. WooCommerce in particular creates orders before payment is confirmed. Pending, failed and cancelled orders may or may not be in the report you are looking at, and may or may not have triggered a purchase event.

  • What "revenue" means. GA4 reports whatever value your tracking sends. That might include tax and shipping, or it might not, and it might be before or after discounts. Your store report has its own definition. If the order counts match but the revenue does not, this is usually why.

  • Refunds. Your store takes refunds off. GA4 only does if your setup sends refund events, and most setups do not.

  • Currency. If you sell in more than one currency, GA4 converts everything into the property currency using its own exchange rates, which will not match your payment provider's to the penny.

  • Sessions and conversion rate, if you compare those too. Shopify changed how its own analytics counts sessions on 21 September 2026, filtering out more bot traffic. Orders and sales were not affected, but sessions and conversion rate in Shopify are on a new baseline from that date, so comparing them with GA4 across that date will not tell you much.

 

If you get to the end of that list and the numbers suddenly look closer, that is good news, and it means your tracking may be fine. If not, it is time to go order by order.

Step two: reconcile by transaction ID

Every purchase event sent to GA4 should carry a transaction ID, which should be the same order number your store uses. That ID is what lets you line the two systems up and see exactly which orders are where.

 

In principle, it is simple. You export your orders from the store, you export your transaction IDs from GA4, and you match them. In practice, each of those three steps has its own catches.

Getting the GA4 side

The most accessible route is a free form exploration with Transaction ID as the dimension and purchases and purchase revenue as the metrics. That is fine for a first look, but explorations can be affected by data thresholds and row limits, and if your GA4 property has thresholding applied because of Google signals, you might not see every row. For anything more than a few hundred orders a month, I use the BigQuery export, because it gives you every individual purchase event with its exact timestamp, its parameters and the device and browser it came from. If BigQuery is not already linked, it only starts collecting from the day you link it, so it is worth doing now even if you do not need it yet.

Getting the store side

Export your orders with as much detail as you can: order number, date and time, total, payment method, order status, sales channel, and anything about the customer's device if your platform holds it.

Making them match

This is where most DIY attempts fall over, because the IDs often do not look the same in both systems. On Shopify, an order has a name (the #1001 your customers see), a long numeric order ID, and a checkout token, and depending on how your tracking was built, GA4 could be receiving any of the three. On WooCommerce, if you use a plugin that changes order numbers, GA4 might be receiving the internal post ID while your export shows the display number. You have to work out which one your tracking sends before you can join anything.

 

Once they line up, every order falls into one of three groups:

  • In both. Tracked properly. Check the values match.

  • In the store only. Missing from GA4.

  • In GA4 only. Extra, which usually means duplicates, tests or orders that did not complete.

A worked example

Here is an illustrative month for a store, based on the kind of pattern I see regularly:

Orders
Store orders1,000
GA4 transactions940
Headline match94%
In both852
Store only (missing from GA4)148
GA4 only (extra)88

 

On the headline number, 94% looks great. But only 852 orders actually matched, which is 85%, and GA4 is carrying 88 transactions that should not be there. The extras are hiding the missing ones. If you only ever compare totals, you would never know.

 

When I broke down the missing 148 in this example:

ReasonOrders
Customer paid through PayPal and never came back to the confirmation page41
Subscription renewals (no browser involved)37
iPhone and iPad customers, lost to tracking prevention and blockers44
Point of sale and manual orders9
No obvious pattern17

   And the extra 88:

ReasonOrders
Same order sent twice with slightly different IDs, from two tracking setups running side by side61
Cancelled or failed orders that still reached the confirmation page18
Test orders9


Some of those are just the cost of doing business. The subscription renewals will never be in GA4, and that is fine. Others are fixable, and some of them (the duplicates especially) are actively distorting your channel reports, because GA4 thinks certain channels are selling more than they are.

Step three: read the patterns

Once you have the three groups, the job is to look for what the missing and extra orders have in common. This is the part that takes experience, because the cause is rarely labelled.

Patterns in the missing orders

  • Payment method. If most of your missing orders are from one gateway, the customer is probably being sent off to pay and not coming back to the page your purchase event fires on. PayPal, Klarna and some bank transfer options are the usual suspects.

  • Device and browser. Split the matched and missing groups by device. On the audit I mentioned earlier, roughly one in six UK orders from iPhones, iPads and Windows was missing, against about one in 55 from Android. That is browser tracking prevention and blockers, and you can reduce it but not remove it.

  • Date. If the missing orders start on a specific date, something changed that day. A theme update, a new plugin, a checkout change. For Shopify stores, this one is very current. Shopify has been retiring the old ways of adding tracking to the Thank you and Order status pages (Additional Scripts, checkout.liquid and apps that use script tags). Plus stores lost them on 28 August 2025, and script tags stopped running on the Order status page for all remaining stores on 26 August 2026. Any purchase tracking that still relied on them stopped recording from that date unless it had already been moved to Shopify's customer events.

  • Post-purchase upsells. On Shopify, if you use a post-purchase upsell app, the purchase event fires on the first upsell offer page instead of the Thank you page. If that page fails to load, Shopify does not send the purchase event at all.

  • Shopify pausing a pixel. Since January 2026, Shopify has a setting switched on by default that pauses data sharing with marketing pixels it decides are not driving traffic or sales. If you send purchases through a pixel that has gone quiet for a while, it can be switched off without anyone touching it. You can check this, and set a pixel to always on, under Settings, Customer events.

  • Country. If you have a consent banner that only applies in some regions, missing orders will cluster there. Worth saying that on the audit above it was not consent, even though that is the first thing everybody assumes.

  • Sales channel. Anything that did not happen on your website will be missing, and should be.

Patterns in the extra orders

  • More than one tracking setup. This is the most common one I find. A Shopify store might have the Google and YouTube app sending purchases, a custom pixel sending them again through Tag Manager, and an old setup still hanging around. If they each send a slightly different ID (one sends #1001, one sends 1001, one sends the long numeric ID), GA4 cannot tell they are the same order and counts each one.

  • Page reloads. On WooCommerce, if the purchase event fires every time the order confirmation page loads, a customer who refreshes or comes back to that page later creates another purchase. Shopify's customer events are designed to send the purchase once per checkout, so on Shopify this is usually a sign that older code is still running alongside them. GA4 does deduplicate purchases with the same transaction ID, but only if the ID is identical every time, and only on web data.

  • Empty transaction IDs. This one catches people out. If your tracking sends an empty transaction ID, GA4 treats every purchase with an empty ID as the same purchase and throws all but one away. So a broken ID does not just lose information, it can wipe out most of your orders.

  • Orders that never completed. Failed and cancelled orders that still reached the confirmation page.

  • Test orders. Everyone forgets these.

Step four: check the values, not just the count

For the orders that appear in both, compare the values. If GA4 is consistently higher, your tracking probably includes tax or shipping that your store report excludes. If it is consistently lower, it might be sending the subtotal before shipping, or after a discount the store report does not apply. If it is inconsistent, with some orders right and some wrong, that usually points to a currency issue or to two setups sending different values for the same order.

 

This matters more than it sounds, because whatever value GA4 receives is often what Google Ads is optimising towards as well.

When the orders match but the channels do not: attribution

Even if your order counts line up perfectly, you will still see different answers to "which channel sold this". That is not a tracking fault. It is because Shopify and GA4 use different rules for deciding who gets the credit, and GA4 actually uses more than one set of rules depending on which report you are in.

How Shopify does it

Shopify gives each order to one channel. In its marketing reports the default is last non-direct click, which means the credit goes to the last channel the customer came through before buying, ignoring direct visits unless direct is all there is. Some custom reports default to plain last click instead, which does count direct. You can switch a report to first click, linear (credit split evenly) or any click (every channel gets full credit), but whichever you choose, it is based on the visits Shopify itself could see on your store.

How GA4 does it

GA4 is where this gets confusing, because it has two different approaches running side by side.

 

The standard Traffic acquisition report uses the session source. That is the channel that started the visit in which the purchase happened, worked out on a last non-direct click basis. It is the closest thing in GA4 to Shopify's default view.

 

The Advertising section, and any exploration that uses event-scoped dimensions like Source, Medium or Default channel group, use the property's reporting attribution model. By default that is data-driven attribution. Instead of giving the whole order to one channel, it looks at the paths customers took across your property and splits the credit between the touchpoints it thinks contributed, over a lookback window that is 90 days by default for purchases. So one order might be credited 0.6 to paid search and 0.4 to email. Data-driven attribution also needs enough data to learn from, so on smaller properties it may not be doing much beyond what last click would.

 

If you change the attribution model in GA4, it changes your historical reports as well, not just future ones. So if a number in a report you saved last month looks different now, check whether somebody changed the setting.

A simple example

Somebody clicks a Meta ad on Monday and has a look around. On Wednesday they search for you on Google and click an organic result. On Friday they click a link in your newsletter and buy.

  • Shopify, last non-direct click: the whole order goes to email.

  • GA4 Traffic acquisition: the whole order goes to email, because that is the session the purchase happened in.

  • GA4 data-driven: the order is split, perhaps some to paid social, some to organic search and most to email, depending on what GA4 has learned from your data.

  • Meta Ads: may well count it as a Meta conversion, because the customer clicked a Meta ad within Meta's own window.

 

All four are internally consistent. None of them is lying. They are answering different questions.

 

And on a real account, it looks like this. On an audit last month, the store held 2,830 orders in August. Google Ads claimed 769. GA4 credited 520 to Google paid. Google Ads counts orders it believes it influenced using its own rules and windows, and GA4 hands a lot of those to other channels instead.

What the browser hides from both

There is one more thing that affects both Shopify and GA4. If a customer declined cookies, or Safari has cleared the cookie that recognised them, their return visit looks like a brand new visitor. The ad they clicked a fortnight ago is invisible, and the order tends to land in Direct or Unassigned. That is why Direct often looks like a much bigger channel than it really is.

Comparing like with like

If you want to compare channels across Shopify and GA4, the fairest comparison is Shopify's last non-direct click against GA4's Traffic acquisition report. Even then they will not match exactly, because the two systems define a visit differently, use different lookback windows and can see different visitors. What I would avoid is putting GA4's data-driven numbers next to Shopify's channel report, or adding up what every ad platform claims, because the total will always be more orders than you actually took.

Step five: check the tracking itself, not just Tag Manager

The reconciliation tells you what is wrong. To find out why, you have to look at what the site is actually sending, which is not always what Tag Manager says it should be sending.

 

On Shopify, purchase tracking now runs through customer events, which are sandboxed. That means Tag Manager's preview mode does not connect the way it does on the rest of your site, and a tag can look perfectly configured in the container while never firing on the checkout. You need to test with a real order and watch the network requests the checkout actually makes.

 

It is also worth knowing that Google's own position is that running Tag Manager inside a Shopify custom pixel is not a supported setup. Preview mode, many trigger types and many variables do not work properly in there, and consent can behave differently too. Google recommends the Google and YouTube app for GA4 on Shopify instead. Plenty of stores still run Tag Manager through a custom pixel, and it can work, but if yours does, it is one of the first places I would look.

 

On WooCommerce, purchase tracking usually comes from a plugin, and it is common to find two (one from an analytics plugin, one from a Google plugin, and sometimes a third from a theme) all sending purchases independently. Caching can also break the confirmation page in ways that only show up on some orders.

 

In both cases, the things I look at are the purchase requests leaving the browser (what event name, which transaction ID, what value, which GA4 property), whether anything is hard coded on the site as well as in Tag Manager, whether more than one GA4 property is being sent to, and whether any server-side or Measurement Protocol setup is sending purchases without the client and session IDs GA4 needs to attribute them. Purchases sent from a server without those tend to turn up in GA4 with no source, which looks like direct traffic and makes every other channel look worse.

When the gap is a problem and when it is not

Once you know what makes up the gap, you can decide what to do about it. Subscription renewals, point of sale orders and a share of iPhone customers will always be missing, and the right response is to take your revenue figures from the store and use GA4 for what it is good at: which channels, which pages and which journeys are driving sales.

 

Duplicates, broken IDs, missing gateways and tracking that stopped on a specific date are a different matter. Those are worth fixing, because they do not just make GA4 inaccurate, they make it misleading, and anything downstream (Google Ads bidding, Meta optimisation, your monthly reports) inherits the problem.

If this all sounds like a lot

It is, to be honest. The reconciliation itself is a few hours of careful work, and the hard part is not the spreadsheet, it is knowing what you are looking at when the patterns come out. Most of the time, the answer is a combination of three or four small causes, each of which looks like nothing on its own.

 

This is a big part of what I do in a tracking audit: reconcile your orders against GA4 by transaction ID, find the patterns, and tell you which parts of the gap are fixable, which are not, and what fixing them will do to your numbers. If you would like me to take a look, please feel free to contact me here.

Next
Next

What Makes a Cookie Banner Actually Compliant? (And Which CMPs Get It Right)