Why GA4 shows fewer purchases than Shopify, and how to fix it

A gap between GA4 and Shopify is normal. A gap you can't explain is not. Here are the eight causes, how to measure each one by order ID, and what to fix first.

How big should the gap be?

GA4 will never count every Shopify order. Some orders never happen in a browser, and some visitors decline tracking. The goal isn’t a perfect match. It’s a gap you can explain line by line.

The quickest sanity check: compare the gap with your cookie banner’s decline rate. If about 20% of visitors decline and GA4 misses about 20% of online orders, your setup is probably fine. If GA4 misses 40%, something is broken.

Compare like with like

Only compare Shopify orders from the Online Store channel, excluding test orders, over a full week or month, in the same timezone as your GA4 property.

The eight causes

Most gaps come from a combination of these. The first three are the most common in audits.

CauseEffectHow to spot it
No purchase event from checkoutFewerLarge gap that started after old Additional Scripts or checkout.liquid code stopped running
Consent declined or pendingFewerGap close to your banner’s decline rate, or tags that wait for consent that never comes
Ad blockers and browser protectionsFewerGap higher on desktop Safari and Firefox than on mobile apps
Orders outside the browserFewerPOS, draft, manual and subscription renewal orders in the Shopify Source column
Purchase sent without currencyRevenue missingPurchases counted in GA4 but revenue shows zero
Timezone mismatchShifts by dayDaily totals disagree but weekly totals match
Test, cancelled and refunded ordersEither wayOrders tagged as test or cancelled still counted on one side
Duplicate purchase eventsMoreThe same transaction_id appears two or more times in GA4

Reconcile by order ID

Totals hide problems. Matching orders one by one shows exactly which ones are missing and what they have in common.

  1. In Shopify, export last month’s orders. Keep the Id, Name, Source, Total and Created at columns.
  2. In GA4, list every transaction_id for the same dates, with an Exploration or the BigQuery export.
  3. Match on the field your pixel sends. Some setups send the order ID, others the order name like #1042.
  4. Group the unmatched orders by source, device, payment method and date. The pattern points to the cause.

With the BigQuery export, this query returns one row per tracked order and flags duplicates:

SELECT
  ecommerce.transaction_id,
  COUNT(*) AS purchase_events,
  MIN(TIMESTAMP_MICROS(event_timestamp)) AS first_seen
FROM `your-project.analytics_123456789.events_*`
WHERE event_name = 'purchase'
  AND _TABLE_SUFFIX BETWEEN '20261001' AND '20261031'
GROUP BY 1
ORDER BY purchase_events DESC

Rather have it fixed for you?

Shopify packages from 1300€ rebuild your tracking in four weeks, with GA4, Google Ads and Meta aligned on the same orders.

Fix the checkout event

Since Shopify moved checkout tracking to Customer Events, the reliable way to send purchases is a custom pixel subscribed to checkout_completed. Add it in Settings → Customer events → Add custom pixel.

analytics.subscribe("checkout_completed", (event) => {
  const c = event.data.checkout;
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({
    event: "purchase",
    ecommerce: {
      transaction_id: c.order?.id,
      value: c.totalPrice?.amount,
      tax: c.totalTax?.amount,
      shipping: c.shippingLine?.price?.amount,
      currency: c.currencyCode,
      items: c.lineItems.map((li) => ({
        item_id: li.variant?.sku || li.variant?.id,
        item_name: li.title,
        item_variant: li.variant?.title,
        price: li.variant?.price?.amount,
        quantity: li.quantity
      }))
    }
  });
});

This pushes to a dataLayer inside the pixel sandbox. Load GTM inside the same custom pixel so it can read it, and connect consent through the Customer Privacy API before any tag fires.

Check for duplicates first

If the Google & YouTube app already sends purchases to the same GA4 property, adding this pixel will double-count. Keep one source per destination.

What to do next

  • Run the reconciliation on last month’s orders.
  • Fix missing or duplicate purchase events before touching anything else.
  • Then connect consent, so the remaining gap matches your decline rate.
  • Recheck after every theme update or new checkout app.
Elliot Latil

Freelance web analyst specialised in GA4, GTM and Shopify tracking. About me