Metric Discrepancies & Attribution

Why Shopify, GA4 and Meta Report Three Different Revenue Numbers for the Same Day

By Chinmay Raibagkar·August 28, 2026·10 min read·Some SQL

The 60-second version

Three systems, one day, three revenue totals — and none of them is broken. Timezones, refunds, tax and shipping explain most of the gap. Here is how to decompose it line by line.

  • What happened, in one line
  • What to do about it this week
  • What you can safely ignore

It is 9am on the 5th. You open Shopify: yesterday did $48,210. You open GA4: yesterday did $44,905. You open Meta Ads Manager: it claims $31,400 in purchase value, and Google Ads claims another $14,800 on top.

Four numbers. One day. Nobody is wrong.

This post is not about attribution — that is a different disagreement, about which channel deserves credit. This one is narrower and more fixable: even before you ask who caused a sale, the three systems do not agree on what a sale is worth or which day it happened on. Timezones, refunds, tax and shipping account for most of the gap, and every one of them is a decomposable, checkable line item.

The distinction that matters: attribution disagreements are about credit and can never be fully reconciled. Revenue-definition disagreements are about arithmetic and can be reconciled to within a rounding error. Fix the second one first — otherwise you will spend weeks debugging attribution while the real gap was a timezone.


The four systems, and what each one is counting

Same Day, Four Definitions of Revenue

Data Journey
Stage 1Order-time
Shopify / Your Store

Records the order at the moment checkout completes, in the store's configured timezone, at gross value including tax and shipping.

$48,210 — gross, store TZ
Stage 2Tag-time
GA4

Records a purchase event only if the tag fired on the thank-you page, at whatever value the tag was configured to send.

$44,905 — tagged only
Stage 3Click-date
Ad Platforms

Records purchase value against the day the ad was clicked, not the day the order was placed, in the ad account's timezone.

$46,200 — combined, back-dated

Shopify (or your commerce backend)

Counts an order the instant checkout succeeds. Stamps it in the store timezone. Reports gross value by default — including tax collected and shipping charged. Refunds show up on the day the refund was processed, not the day the order was placed, so a report you ran yesterday and re-run today can legitimately show different numbers for the same historical date.

GA4

Counts a purchase event only if the tag fired. Its value is whatever your tag was told to send — which may or may not include tax and shipping, depending on how someone configured it eighteen months ago. Its day boundary is the GA4 property timezone, a separate setting from the Shopify one. GA4 never sees refunds unless you explicitly send a refund event, which almost nobody does.

Meta and Google Ads

Count purchase value against the day of the ad interaction, not the day of the purchase. This is the one that surprises people most: a purchase on the 4th, from a click on the 1st, appears in Meta's row for the 1st. Both platforms also stamp days in the ad account timezone, which is a third independent setting.


Decomposing the gap, in order

Work through these in sequence. Each one is a subtraction you can actually perform, and by the end the residual should be small enough to stop caring about.

The Reconciliation Sequence

Process Flow
1

Normalise the day boundary

Get all four systems onto one timezone before comparing anything. Until this is done, every other number you compute is noise.

2

Strip tax and shipping to a common basis

Decide whether you are comparing gross or net revenue, then adjust every source to that basis.

3

Align the refund treatment

Order-date-net or refund-date-net — pick one convention and apply it everywhere.

4

Account for cancelled and unpaid orders

Draft orders, failed payments and fraud cancellations sit in your store but never in GA4.

5

Measure the tag gap

Whatever remains between store and GA4 after 1–4 is tracking loss. Now you know its real size.

Step 1: The timezone, which is three settings, not one

This is the single largest contributor and the one most often missed, because each system has its own setting and nothing warns you when they disagree.

SystemSetting locationCommon default
ShopifySettings → General → Store timezoneWhatever the store was created in
GA4Admin → Property Settings → Reporting time zoneOften left as America/Los_Angeles
Meta AdsAccount Settings → Time zone (immutable after creation)Account creation locale
Google AdsAccount Settings → Time zone (immutable after creation)Account creation locale
Your warehouseWhatever the ingestion wroteAlmost always UTC

An Asia/Kolkata store against a America/Los_Angeles GA4 property is a 12.5-hour offset. That does not shift revenue slightly — it means roughly half of each Indian evening's orders land on the previous calendar day in GA4. On a business with an evening purchase peak, that is a 20–40% daily discrepancy that looks exactly like broken tracking.

Ad account timezones cannot be changed after creation. If yours is wrong, you cannot fix it in place — you would have to create a new ad account and lose the campaign history and learning. The practical answer is to normalise in your reporting layer instead, which is why every serious reconciliation happens in SQL rather than by clicking around dashboards.

The fix is to convert everything to a single business timezone at query time:

Normalising Order Timestamps to One Business Day Boundary

Show query

Step 2: Tax and shipping

Three systems, three defaults, and the gap compounds:

  • Shopify's "Total sales" in the default Sales report is net of discounts and returns but the "Gross sales" figure is not — and the dashboard headline number is a different metric again. Read the column header, every time.
  • GA4's purchase event sends whatever value your tag passes. Shopify's native GA4 integration sends the order subtotal excluding tax and shipping. A manually-built GTM tag very often sends the full checkout total, including both.
  • Meta's pixel sends value from the same tag configuration, and the Conversions API may send a different value if it was implemented separately — which is how one system ends up double-counting against itself.

At an 18% GST rate plus ₹80 shipping on a ₹2,000 order, gross-versus-net is a 20% difference. That alone explains most gaps people spend a week debugging.

The 20% gap that was one checkbox

Worked example
Shopify (gross)$48,210Includes $6,900 tax and $1,340 shipping
GA4 (net)$44,905Tag sends subtotal only, excludes tax and shipping
Apparent gap$3,305 (6.9%)Looks like a tracking problem
Actual gap$935 (2.3%)After putting both on a net basis
Same-basis comparison: $48,210 − $6,900 − $1,340 = $39,970 net in Shopify. Against GA4's $44,905, GA4 is now *higher* — which points at the real bug: the GA4 tag is firing on thank-you-page reloads, double-counting some orders. That bug was completely invisible while the two numbers were on different bases.

Step 3: Refunds, and the date you assign them to

A refund processed on the 12th, for an order placed on the 5th, can be booked two ways:

  • Order-date net — subtract it from the 5th. Your history restates as refunds come in. The 5th's revenue is not final until your refund window closes.
  • Refund-date net — subtract it from the 12th. History never restates, but any single day mixes new sales with old reversals.

Neither is wrong. What is wrong is using both in different reports and then comparing them. Ad platforms use neither: they report gross conversion value and do not subtract refunds at all unless you actively send refund events back through the Conversions API or offline conversion import.

Pick order-date net if you are measuring campaign performance (you want the refund charged against the campaign that produced it). Pick refund-date net if you are reconciling to accounting (that is how the cash actually moved).

Step 4: Orders that exist but never happened

Cancelled orders, failed payments, fraud-flagged holds, and — in an Indian D2C context — COD orders that will be returned undelivered are all present in your store's order table and absent from every downstream system, because the customer completed checkout and then the order died.

Splitting Order Revenue by What Actually Settled

Show query

Step 5: What is left is the tag gap

After steps 1–4, whatever still separates your store from GA4 is genuine tracking loss: ad blockers, consent declines, JavaScript errors on the thank-you page, users who close the tab before the tag fires, and Safari's privacy restrictions.

A 3–10% residual is normal and healthy. Above 15%, go looking for a specific cause — a consent banner that blocks by default, a single-page checkout that navigates before the tag fires, or a payment method that redirects to a bank page and never returns to your thank-you URL.


The reconciliation query

Once every source is in a warehouse, one query per day tells you which of the five causes is moving:

Daily Four-Way Revenue Reconciliation

Show query

ga4_capture_rate should sit stable in the 0.90–0.97 band. platform_overclaim will exceed 1.0 — that is double-counting between platforms plus back-dating, and it is expected. What matters is that both ratios are stable. A ratio that moves week to week is a broken tag or a changed setting; a ratio that is merely high is just how attribution works.

Alert on the ratio, not the difference. A $3,000 gap on a $48,000 day and a $6,000 gap on a $96,000 day are the same health. Teams that alert on absolute differences page themselves every time revenue grows.


What to report to whom

One Revenue Number Per Audience

Reporting Hierarchy
Tier 1
Finance and the board

Store net revenue, refund-date basis, converted to the reporting timezone. This is the only number that ties to the bank.

Store net, refund-date
Tier 2
Marketing leadership

Store net revenue over total marketing spend — MER. Attribution-free, so no platform disagreement can distort it.

Store net ÷ total spend
Tier 3
Media buyers

Platform-reported value, inside the platform, used only for bidding. Never summed across platforms, never compared to finance.

Platform dashboard value

The failure mode is not having three numbers. It is having three numbers and pretending they are one — putting Meta's back-dated, gross, double-counted purchase value into a slide next to Shopify's net revenue and asking why they differ.


Frequently asked questions

Why does Meta show revenue on a day my store had none?

Because Meta books conversion value against the ad click date, not the purchase date. A purchase today from a click nine days ago appears in Meta's row for nine days ago. Over a stable period this washes out; on a day-by-day comparison it never will.

My GA4 revenue is higher than Shopify's. How is that possible?

Almost always double-firing: the purchase tag firing on a thank-you page reload, a browser back-navigation, or being installed both natively and through GTM. Check whether GA4's distinct transaction_id count exceeds your store's order count for the same day — if it does, the value is inflated by duplicates, not by an accounting difference.

Should I fix my GA4 timezone to match my store?

Yes, but understand what it does: GA4 does not reprocess history when you change the reporting timezone. You get a discontinuity at the change date, with old data on the old boundary and new data on the new one. Change it, document the date, and treat comparisons across that line with care.

Is a 10% gap between store and GA4 a problem?

No. That is a normal, healthy capture rate once consent declines and ad blockers are accounted for. Correct for the known rate and move on. Chasing the last 10% costs more engineering time than the decisions it would change are worth.

Which number belongs in a ROAS calculation?

Store net revenue, in the denominator's timezone, on a consistent refund basis. Using platform-reported revenue in the numerator and real spend in the denominator produces a ratio where the two halves come from different universes.


The summary

Four systems disagreeing about one day is not four bugs. It is five known, decomposable causes, in this order of typical size:

  1. Timezone boundaries — three independent settings, up to 12.5 hours apart. Largest and most invisible.
  2. Tax and shipping basis — a 15–25% swing on the same orders.
  3. Refund date convention — order-date versus refund-date net.
  4. Cancelled, failed and undelivered orders — present upstream, absent downstream.
  5. Tag loss — the genuine 3–10% residual, and the only one that is actually a tracking problem.

Work them in that order, put the result in one query, and alert on the ratio rather than the difference. What remains is attribution — a real disagreement, and a different post.

Related reading: MER is the number that sidesteps all of this by refusing to attribute anything, and ROAS is the one that cannot.

Free tool

MER Calculator

Total revenue divided by total marketing spend — the attribution-agnostic efficiency number, plus its contribution-margin-adjusted variant.

CR

Chinmay Raibagkar

About author →

Founder of DataLens AI. He helps non-technical teams read their ad and database numbers with confidence — which number to trust, what to do next, and what to ignore.