TRACKING SPECIALIST — BARIS ASA · LONDON
Synctag
Start the check → TRACKING SPECIALIST — BARIS ASA · LONDON

READING ROOM › SYMPTOM FILES › SYM-012

SYMPTOM FILE SYM-012 FILED 04 NOV 2026 · REVISED 04 NOV 2026

GA4 purchase event fires twice: how to find the second sender and fix it.

ALSO FILED AS
  • “My purchase event fires twice in GA4, how do I fix it”
  • “GA4 purchase event firing twice”
  • “GA4 duplicate purchase events”
  • “purchase event triggered twice in GTM”
  • “GA4 transaction ID (not set) purchases”
  • “GA4 counting the same order twice”
  • “thank you page reload sends purchase again”
  • “GA4 duplicate transactions”
  • “GA4 purchase firing twice on Shopify”

Baris Asa, tracking consultant, London · Last checked: November 2026

A GA4 purchase fires twice because two things send it for one order. Usually that is two tags, one tag on two triggers, GTM plus a hardcoded tag, a server copy, or a reload of the thank-you page. On a website, GA4 drops a repeat that carries the same transaction_id, the order reference it uses to spot repeats. So the copies that reach your reports have no ID or a different one. The fix: one test order, find every sender, keep one, and send the order number as transaction_id.

On file as F-101 . If the doubling shows up as GA4 revenue above Shopify, start with GA4 revenue higher than Shopify, which separates refunds and currency from this fault. This note is the hunt for the second sender, on any platform.

A check you can do yourself

Take a full week that ended at least two days ago.

  1. In GA4, open Reports and find the Transactions report (under Monetisation, or under Drive online sales). Note the Ecommerce purchases total.
  2. In the same report, look down the Transaction ID column. A (not set) row with purchases on it, or two formats side by side, such as 1042 next to a long random string, means a second sender.
  3. Count the orders in your shop’s admin for the same week. Leave test orders out of both.

More GA4 purchases than orders is the fault. GA4 usually reads a little below the shop: some buyers block scripts, and purchases without analytics consent are only partly modelled back.

With hundreds of orders, build a free-form exploration instead: Transaction ID as the row, Ecommerce purchases as the value. The (not set) row and any odd format are easier to spot there.

Why does the purchase fire twice?

Each send is its own event. Google’s help says GA4 deduplicates purchases registered with the same transaction ID, and publishes no time window. Apps send the ID through the Firebase SDK; this note is about websites. Anything without that shared ID counts twice.

That is why a double can hide for months. Two senders that both read the order number cancel out in GA4. The overcount starts when one of them sends no ID, or a different one, such as a checkout token instead of the order number. Often the second sender is a tag added for another job and never checked against the first.

t+msSent byep.transaction_idGA4 keeps it?
0GTM · GA4 purchase tag1042yes
+310platform app · gtagc1a9…7f3eyes, different ID
+4,800 (reload)GTM · GA4 purchase tag1042no, same ID
NETWORK · en=purchase, one order · worked example, illustrative, not a client account

One order, three purchases sent, two counted. The reload repeats the order number, so GA4 drops it. The app sends a checkout token, so GA4 keeps it. Remove the app’s purchase and the count is right.

Which sender is it?

These are the senders I look for, roughly in this order. The middle column is what one test order shows.

Second senderOne test order showsFix
Tag on two triggers, or one broad triggerPreview: tag fired on two messagesOne trigger
Two purchase tags in GTMPreview: two GA4 tags on one messageDelete the copy
GTM plus a hardcoded gtag, plugin or app (e.g. Shopify’s Google & YouTube app)Network: two purchases, Preview shows oneKeep one route
GTM container installed twiceEvery event doubledRemove one install
dataLayer push runs twicePreview: purchase message twiceFix the site code
GA4 Create event rule outputs purchaseNetwork: extra purchase no GTM tag sentDelete the rule
Server copy (Measurement Protocol, server-side GTM)Server-side Preview or the Transactions report, not the browserOne sender, or one shared ID
Thank-you page reloaded or revisitedA second purchase on reloadSame ID, or a guard

Three of these need a word more.

Hardcoded tag plus GTM. WordPress plugins, Shopify’s Google & YouTube app and theme code can all send GA4 without passing through GTM. Preview shows only what GTM sends, so this one is invisible there. The Network panel shows both. The Google tag setting “Ignore duplicate instances of on-page configuration” stops a repeated config command. Google doesn’t say it removes a repeated event. Don’t rely on it.

Create event rules. In GA4, Admin, Data display, Events, Create event. A rule that makes purchase from a page_view on the thank-you page adds a second purchase. Google says these rules are calculated client side, before data is sent, and that the original event stays. So the extra en=purchase does show in the Network panel, often in the same request as the page view, but no GTM tag in Preview sent it. Rule changes don’t apply to historical data.

Reloads. On Shopify, checkout_completed fires once per checkout, on the Thank you page or on the first upsell page, and Shopify says it isn’t triggered again on the Thank you page. So a Shopify double is usually two senders, not a reload. Elsewhere, a reload, the back button or a link in the confirmation email can replay the tag. With the same transaction_id, GA4 drops the replay. Without one, add a guard in GTM:

// Custom JavaScript variable "Purchase already sent"
function() {
  var id = encodeURIComponent({{DLV - transaction_id}});
  return document.cookie.indexOf('ga4_sent_' + id + '=') > -1;
}

Add an exception trigger to the purchase tag: the purchase event, where “Purchase already sent” equals true. Then a Custom HTML tag, set to fire after the purchase tag with tag sequencing, writes the cookie: document.cookie = 'ga4_sent_' + encodeURIComponent({{DLV - transaction_id}}) + '=1; path=/; max-age=2592000';. The exception only blocks the tags it is added to. Add it to the Meta and Google Ads purchase tags as well if they fire on the same event.

How do I find it with one test order?

From here the work needs publish access to GTM. If that isn’t you, send this section to whoever has it.

Use a real or test order, with three windows open.

  1. GTM Preview. Connect Preview, place the order, then click the purchase message on the left. Count the GA4 tags that fired on it. Click the tag and check which messages it fired on.
  2. The browser Network panel. Open developer tools, tick Preserve log so the hits survive the redirect, and filter on collect. Count purchase events, not requests: one request can carry several events, some in the request body (the Payload tab). Each event carries its name as en and the order reference as ep.transaction_id. Check the measurement ID (tid) is yours. With server-side GTM the requests go to your own tagging domain, not google-analytics.com. Then repeat with Preview closed, so Preview’s own debug script isn’t in the count.
  3. GA4 DebugView. Admin, Data display, DebugView, to confirm what reached GA4 from your test session. A server copy is harder to see: a Measurement Protocol purchase only shows here if it carries debug_mode. For server-side GTM, open its own Preview and count the GA4 purchase requests going out. For a backend sender, look the test order’s ID up in the Transactions report the next day: two purchases, or a second row under another ID, is the double.

On Shopify, GTM on the checkout pages runs inside a custom pixel, and Preview usually can’t connect to it there. Use the Network panel and DebugView for the checkout.

Write down each sender and the transaction_id it sent. That list is the diagnosis.

How do I fix it?

  1. One sender per purchase. Pick the route that stays, usually GTM, and remove the purchase from every other one. Leave the other route’s page views alone unless they are doubled too.
  2. Order number as transaction_id. Read it from the order, the same string the shop admin shows. Never empty: Google warns GA4 then treats every empty-ID purchase as one. Never fixed or reused: the same ID on different orders undercounts.
  3. A server copy on purpose. If a server copy of the purchase must stay, it sends the same order number as the browser copy. Meta keys on event_id, not transaction_id, which is F-102 : Meta CAPI and Pixel double counting purchases.
  4. Publish and record the date. Past data stays as it is. Compare from the first full week after the fix.

How do I know it’s fixed?

  • One test order sends one en=purchase in the Network panel with Preview closed, carrying the order number.
  • DebugView shows one purchase for that order.
  • From the fix date, no new (not set) rows in the Transactions report, and one ID format.
  • A closed week of GA4 ecommerce purchases sits at or a little below the shop’s orders.

If Google Ads doubled too, the tag may be fine and the counting not. One purchase can reach Ads twice, through the Ads tag and a GA4 import both set as primary. That one is on file as F-104 : GA4 and Google Ads numbers don’t match.

Common questions

Why does my GA4 purchase event fire twice?

Two things send it for one order: two tags or two triggers in GTM, GTM plus a hardcoded gtag or a plugin, the container installed twice, a dataLayer push that runs twice, a GA4 Create event rule that outputs purchase, a server copy, or a reload of the thank-you page. One test order with GTM Preview and the browser Network panel open shows which.

Doesn’t GA4 remove duplicate purchases on its own?

Only when both copies carry the same transaction_id. Google says GA4 deduplicates purchases registered with the same transaction ID, and publishes no time window. This note covers websites; apps send the ID through the Firebase SDK. A copy with no ID, or a different one, is kept. An empty string is worse: GA4 treats every purchase with an empty ID as one, and you undercount.

How do I check for duplicate purchases in GA4 without a developer?

Use the Transactions report for a closed week. A (not set) row with purchases on it, or two ID formats in the Transaction ID column, points to a second sender. Then count the shop’s orders for the same week. More GA4 purchases than orders is the fault.

GTM Preview shows the purchase tag fired twice. What do I change?

Check which two messages it fired on. Two different events means the trigger is too broad, or a second trigger is attached: give the tag one trigger, on the purchase event only. The same purchase message twice means the dataLayer push runs twice, which is fixed in the site code.

Will fixing it correct my past GA4 data?

No. Purchases already recorded stay in the reports, and Google says Create event changes don’t apply to historical data. Note the fix date and compare from the first full week after it.

Who should I hire to fix a purchase event firing twice?

Someone who can read GTM Preview, the browser Network panel and GA4 DebugView together, with publish access to the container and Editor access in GA4. The job is done when one test order sends one purchase with the order number as transaction_id, and no new (not set) rows appear in the Transactions report.

Sources

Read on 4 November 2026:

← back to the reading room