Shopify and Stripe are records of money. GA4 is a record of what browsers told it. The two are built differently, so they will always disagree a little.
The useful question isn't "why don't they match?" but "is the gap the normal kind, or is something broken?" This guide covers both, then gives a way to reconcile them properly.
The gaps that are normal.
Some of the difference is built into how GA4 collects data. You can reduce it, but you can't remove it.
- Consent refusals. If a customer rejects analytics cookies, GA4 doesn't get a normal purchase event. With Consent Mode in advanced mode, Google receives cookieless pings and may model some of the missing activity, but modelled data never gives you individual orders with transaction IDs.
- Ad blockers and browser protections. Some blockers stop GA4 requests entirely. Those orders exist in Shopify and Stripe and nowhere in GA4.
- Timezone. GA4 reports in the property's timezone. Shopify uses the store's timezone and Stripe the account's. If they differ, orders near midnight land on different days.
- Currency. GA4 converts every purchase into the property's reporting currency using its own daily rates. Shopify and Stripe may convert differently, or report in the presentment currency.
- Reporting latency. GA4 can take a day or two to finish processing. Comparing yesterday's numbers is comparing against incomplete data.
- Sampling and thresholding. Explorations over large date ranges can be sampled, and GA4 may withhold rows when numbers are small or Google signals is on. A small warning icon at the top of the exploration tells you when either applies.
Definitions: what "revenue" means in each system.
Before assuming a fault, check that you're comparing the same thing. This causes more false alarms than anything else.
| Item | GA4 | Shopify and Stripe |
|---|---|---|
| Tax and shipping | Whatever your implementation puts in the purchase value. tax and shipping are separate parameters. | Shopify's sales reports separate gross sales, discounts, returns, tax and shipping. Stripe shows the amount charged. |
| Discounts | Depends on whether value is sent before or after discounts. | Shopify reports discounts as their own line. |
| Refunds | Purchase revenue is only reduced if a refund event is sent. Gross purchase revenue ignores refunds. | Refunds come off net figures, often on the day of the refund. |
| Metric | Gross purchase revenue, Purchase revenue (after refunds) and Total revenue (which can also include subscription and ad revenue) are three different numbers. | Each report has its own definition, so pick one and stick to it. |
Write down which GA4 metric you compare with which Shopify or Stripe figure, and keep using the same pair. Switching between them week to week makes every comparison meaningless.
Fault 1: Purchase events missing or duplicated.
If the purchase tag doesn't fire on some orders, GA4 is short. If it fires twice, GA4 is over. Both can happen at once and cancel out in the totals, which is why totals alone can mislead.
How to diagnose it: Compare transaction IDs, not totals (method below). Missing IDs point to lost events. The same ID counted twice, or purchases with no ID at all, point to duplicates.
How to fix it: Send a unique transaction_id with every purchase, taken from the real order or payment ID. GA4 uses it to deduplicate repeat purchases, though it shouldn't be your only defence. See the duplicate key events guide for the common causes.
Fault 2: The thank-you page doesn't always load.
Most setups fire the purchase on the order confirmation page. That only works if every customer reaches it, and many don't.
Offsite payment methods (PayPal, Klarna, some bank transfers) and hosted checkouts such as Stripe Checkout take the customer away to pay, then redirect back. If the customer closes the tab after paying, or the redirect fails, the payment succeeds and the purchase event never fires.
Wallets such as Shop Pay usually return to the normal confirmation page, but any flow that ends elsewhere is worth testing.
How to diagnose it: Break your reconciliation down by payment method. If orders paid by one method go missing from GA4 far more often than others, that method's redirect is the leak.
How to fix it: Test every payment method end to end and confirm the purchase fires. Where a redirect can't be relied on, send the purchase from the server instead (Fault 4).
Fault 3: Shopify checkout extensibility and customer events.
Shopify's checkout, thank-you and order status pages no longer run scripts added through checkout.liquid or the old additional scripts box. Tracking there has to go through customer events: either an app pixel, such as the Google & YouTube app, or a custom pixel you write yourself.
Custom pixels run in a sandbox, separate from the storefront. GTM on your theme can't see the checkout, and page URLs and referrers inside the sandbox don't always look like normal pages unless the pixel sets them explicitly.
Common results: no purchase at all after a migration, purchases sent twice because both the app pixel and a custom pixel send one, or purchases that arrive without the session they belong to, so attribution breaks.
How to diagnose it: In Shopify, go to Settings, then Customer events, and list every pixel that sends GA4 data. More than one sending purchases to the same property is a duplicate. None means no purchases.
How to fix it: Choose one source for the purchase event. If you use a custom pixel, subscribe to checkout_completed, map the order ID to transaction_id and pass the page location through. The Shopify GTM and GA4 setup page describes how this should be built.
Fault 4: Payments that never touch the browser.
Subscription renewals, invoice payments, payment links, phone orders, draft orders, point-of-sale sales and marketplace orders all create revenue in Shopify or Stripe without a customer loading your site. Browser-based GA4 can't record them.
This is the usual reason Stripe totals run well ahead of GA4 for subscription businesses. Every renewal is revenue in Stripe and invisible to GA4.
How to diagnose it: Filter Shopify orders by sales channel and Stripe payments by source. Exclude anything that isn't an online checkout, then compare again.
How to fix it: Decide whether GA4 should hold that revenue. If so, send the purchase from your server with the Measurement Protocol, triggered by a Shopify webhook or a Stripe webhook such as invoice.paid. Include the customer's GA4 client ID where you have it, so the purchase joins their history instead of creating a new user.
A server-side GTM container can do the same job.
Fault 5: Test orders and multiple currencies.
Test orders placed on the live site through Shopify's test gateway, or with Stripe in test mode, reach GA4 as real purchases. Shopify and Stripe keep them separate. GA4 doesn't.
On multi-currency stores, a purchase sent with the wrong currency, or with no currency at all, is either converted wrongly or not counted in revenue.
How to fix it: Mark test orders so they can be filtered out, or run tests against a separate GA4 property. Always send the currency the customer actually paid in, and let GA4 convert it.
A practical reconciliation method.
Work through this in order. Each step rules out a whole class of problem.
- Compare order counts before revenue. If counts match and revenue doesn't, it's a definitions or currency problem. If counts don't match, it's a tracking problem.
- Pick a settled date range. Use a full week or month ending at least three days ago, and line up the timezones.
- Compare day by day. A gap that appears on one date points to a release, a theme change or a new payment method.
- Compare by transaction ID. In GA4, build a free-form exploration with the Transaction ID dimension and the Ecommerce purchases and Purchase revenue metrics. Export it, export orders from Shopify or payments from Stripe, and match them in a spreadsheet.
- Look at the unmatched orders. Group them by payment method, device, browser, sales channel and whether they're new or renewal orders. The pattern usually names the cause.
The Monetisation reports are fine for trends, but reconciliation needs the transaction-level exploration.
How big a gap is acceptable.
There's no official figure, and it depends on your consent rate and audience. As a working judgement, a gap of a few per cent on online orders is common and not worth chasing. A double-figure gap, or one that changes suddenly, usually means something is broken.
Duplicate transaction IDs, test orders or GA4 running ahead of Shopify are always faults, whatever the size.
If you'd rather have it checked, the GA4 mini audit covers purchase tracking, transaction IDs and the Shopify or Stripe setup, and says which gaps are normal and which need fixing.