Skip to content

TroubleshootingGA4

GA4 Purchase Event Fires Twice on Shopify: Diagnosis

GA4 counting two purchases for one Shopify order? Find the second sender — app, pixel, snippet or server hit — and check the transaction_id.

Published
Reviewed

Diagnosis

  1. Platform GA4
  2. Usual cause Two senders fire a purchase for the same order without a matching transaction_id.
  3. Causes, ranked 6
  4. How to diagnose it 7
Go to the checks

First answer free, within one working day. Describe your case

Sounds familiar?

The symptom

Symptom report GA4

Real orders are coming in, but GA4’s ecommerce report shows more purchases than the store actually took — sometimes close to twice as many. Shopify’s order count does not move; GA4’s purchase count does.

The drift is rarely clean: some orders are counted once and others twice, so the totals separate gradually. The test that settles it is small — group purchases by transaction_id and look for one order number on two rows. That separates duplication from every other reason a purchase count looks high.

One order producing two identical purchase events that land in the counter as two instead of one
Tick what you see

Tick the lines that match your store.

Describe my case

The short version

Direct answer

GA4 counts one Shopify order twice when two independent senders each fire a purchase for it and GA4 cannot match them. Matching needs the same non-empty transaction_id on both. The likely pair is the Google & YouTube app and a custom web pixel — but any second path has the same effect.

GA4 deduplicates purchase events sharing the same non-empty transaction_id, and merges those sharing an empty one. A missing ID is not neutral — it collapses unrelated orders. Two senders generating their own ID, or leaving it blank, count separately.

Where to look

Causes, ranked

GA4 counts orders twice6 causes

  1. The Google & YouTube app and a custom web pixel both send purchase

    Both map Shopify’s checkout_completed event to a GA4 purchase, so one order produces two events. Each chooses its own transaction_id, and when the strings differ GA4 cannot match them.
  2. A second sender you did not count

    A plain theme-file snippet cannot see the new checkout, but an app block, a theme app extension or an app-injected tag can still run on the order-status page, and pre-migration code may still be live. Two pixels in Customer Events do the same.
  3. The order-status page firing again on reload or revisit

    If the purchase fires on page load rather than once per order, refreshing the page or reopening the order-status link from an email sends another event. Without an “already sent” guard, every visit looks like a new purchase.
  4. transaction_id mismatch, or none at all

    One sender may use the order ID, another the order name or checkout token — GA4 matches on the exact string, so those are two orders. An empty ID is worse: GA4 deduplicates all purchases that share it.
  5. Two measurement IDs or two GTM containers

    A second Google tag or container can route the same purchase to the same property twice, or forward it from inside the pixel alongside one on the storefront.
  6. Server-side forwarding plus the browser hit, without shared deduplication

    An order webhook forwarded through a server container or Measurement Protocol sends a second purchase for orders the browser reported — fine until it carries a different transaction_id, or none.

7 steps

How to diagnose it

Work from the event back to the sender: count how many purchases GA4 received for one known order, read the transaction_id on each, then find every system that could have sent one. One test order proves duplication; the sender list tells you what to switch off.

  1. List every pixel that can send a purchase. In Shopify Admin, open Settings → Customer events and read the registered pixels. Two pixels that both subscribe to checkout_completed and send purchase are your first answer.
  2. Check whether the Google & YouTube app is one of them. In Settings → Apps and sales channels, check whether the Google & YouTube channel is installed. If it is, and a custom pixel also sends a purchase, one of the two must be switched off.
  3. Place a test order in debug mode. In GA4, open Admin → DebugView, complete a real checkout on a debug device. Two purchase rows for one order is the confirmation; open both and note each transaction_id. Reports → Realtime shows the same arrivals without debug mode.
  4. Compare the two IDs, then reload the page. Expand both events in DebugView and compare transaction_id. Identical and non-empty means deduplication should have collapsed them and something else is wrong; different or empty values mean GA4 was given two orders. Then reload the thank-you page and navigate back: a new purchase on each visit points to a page-load trigger with no “already sent” guard.
  5. Count purchases per transaction_id in an Exploration. In GA4, open Explore → Free-form, set the dimension to Transaction ID, the metric to Event count, and filter event name equals purchase. Any transaction ID with a count above one is duplicated.
  6. Look for a second tag or container. Search the storefront source for the Google tag and container identifiers; more than one is worth naming, and GA4 → Admin → Data Streams shows the legitimate destination. GTM Preview sees storefront tags but cannot attach inside the pixel sandbox, so judge the pixel by DebugView.
  7. Reconcile a closed day of orders. Export a day from Shopify → Orders → Export and pull GA4 event-level data with event name and transaction_id from an Exploration export or BigQuery. Match by order: a few doubled orders a day is the same bug as a store-wide doubling.

Signal and fix

Decision table

Match the signal from DebugView to the sender it implies, then make the smallest change that leaves one purchase path.

CauseSignal you’ll seeFix
Google & YouTube app plus a custom pixelTwo purchase events for one order, with different transaction_id valuesKeep one path: disable the app’s purchase or the pixel’s, then re-test
Leftover snippet or extra pixelMore than one pixel in Customer Events, or an app-injected tag on the order-status pageRemove the redundant sender; keep one subscription firing purchase
Order-status page reloadA new purchase each time the thank-you or order-status page loadsFire once from checkout_completed and record that the order reported
transaction_id mismatch or emptyDifferent IDs on the two events, or one blankSend the same non-empty value — the Shopify order ID — on every path
Two measurement IDs or containersMore than one Google tag or container in the storefront sourceRemove the stale tag or container; keep one destination per event
Server hit plus browser hitA server purchase carries a different or absent transaction_idForward the browser’s transaction_id, or stop the duplicate server send

A note from Daniilbefore you decide

Should you fix it yourself or bring in help?

Finding the second sender is usually admin work: the Customer Events list plus DebugView is enough to name the cause in most stores. Removing it is where code, and the risk of breaking the path you keep, begins.

Deciding which sender to keep is a judgement call — usually the one whose parameters and consent behaviour you trust, disabled rather than deleted so it can be restored. Deleting a redundant pixel, rewriting a subscription so it cannot fire twice, or threading a shared transaction_id to a server container is development work.

Do it in that order: stop the duplicate, re-run one test order in DebugView to confirm a single purchase, then reconcile against Shopify the next day. If duplication continues after you have confirmed exactly one path fires, the ID logic spans several systems — that is GA4 implementation work, not a settings toggle.

— Daniil

Next

If the self-check does not settle it.

The checks above find the cause in most stores. When two or more overlap, an order-level reconciliation is faster than another afternoon spent comparing dashboards.

Tracking Health Check

Price
USD 395
Access
Read-only
Timeline
verdict two working days after the kickoff

What the reconciliation shows for this symptom.

A Health Check reconciles this symptom order by order against your Shopify orders, so the cause is a fact from your own data, not a guess from a dashboard. Real numbers for this symptom are not published yet — ask for the sample report.

Request a Health Check · USD 395

Follow the order. Then compare the evidence.

  1. Real orderOrder ID, amount, currency
  2. Browser / server eventsConsent, event ID, delivery
  3. GA4 / advertising platformsReceived events and reporting
  4. Reconcile the recordsMatch IDs, time windows and definitions
A completed order and a reported conversion are different records. Check the path and compare like-for-like before treating a difference as missing data.

Questions

Questions this symptom raises

Why does GA4 show more purchases than Shopify has orders?

Because two systems are reporting the same order and GA4 cannot tell they are the same event. GA4 deduplicates purchase events that share the same non-empty transaction_id, so when the two senders choose different values — or one sends an empty string — both are counted. Check how many purchase events arrived for one known order, not the totals for the month.

Doesn't GA4 deduplicate purchases automatically?

It does, but only under a condition you have to satisfy: the purchase events must carry the same non-empty transaction_id. Automatic deduplication is a safety net for the same ID arriving twice, not a fix for two systems that generate their IDs independently. An empty transaction_id is worse than no ID at all, because GA4 merges unrelated purchases that share it.

Can I leave both senders running and rely on deduplication?

Only if every path sends exactly the same transaction_id every time, which is rare when one sender is a vendor-managed app whose ID you cannot control. Running one purchase path is the reliable answer; a second path is a permanent chance for the two IDs to drift apart after an update.

Will Tag Assistant or GTM Preview show me the duplicate?

Not reliably. GTM Preview cannot attach inside Shopify's pixel sandbox, so it only shows tags running on the page and the pixel path stays invisible to it. Treat DebugView and the Realtime report as the primary evidence, and use Preview only for the containers it can actually see.

Read more, or check a neighbouring symptom.

All symptoms
Daniil Maximkin

Hi, I’m Daniil.

I work with you from defining the problem to implementation and handover. You talk to the person who does the work. I work in English and Russian.

Tried it and still stuck?

Describe your task

The first answer is free, within one working day. Or write directly: next@taskfordaniel.com