GA4 purchase event fires twice: how to find the second sender and fix it.
- “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 sametransaction_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.
- In GA4, open Reports and find the Transactions report (under Monetisation, or under Drive online sales). Note the Ecommerce purchases total.
- 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 as1042next to a long random string, means a second sender. - 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+ms | Sent by | ep.transaction_id | GA4 keeps it? |
|---|---|---|---|
| 0 | GTM · GA4 purchase tag | 1042 | yes |
| +310 | platform app · gtag | c1a9…7f3e | yes, different ID |
| +4,800 (reload) | GTM · GA4 purchase tag | 1042 | no, same ID |
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 sender | One test order shows | Fix |
|---|---|---|
| Tag on two triggers, or one broad trigger | Preview: tag fired on two messages | One trigger |
| Two purchase tags in GTM | Preview: two GA4 tags on one message | Delete the copy |
| GTM plus a hardcoded gtag, plugin or app (e.g. Shopify’s Google & YouTube app) | Network: two purchases, Preview shows one | Keep one route |
| GTM container installed twice | Every event doubled | Remove one install |
| dataLayer push runs twice | Preview: purchase message twice | Fix the site code |
GA4 Create event rule outputs purchase | Network: extra purchase no GTM tag sent | Delete the rule |
| Server copy (Measurement Protocol, server-side GTM) | Server-side Preview or the Transactions report, not the browser | One sender, or one shared ID |
| Thank-you page reloaded or revisited | A second purchase on reload | Same 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.
- 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.
- 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 asenand the order reference asep.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. - 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?
- 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.
- 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. - 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, nottransaction_id, which is F-102 : Meta CAPI and Pixel double counting purchases. - 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=purchasein 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:
- Google Analytics Help, minimise duplicate key events with transaction IDs (same transaction ID deduplicated, empty string, unique dynamic value, app streams through the Firebase SDK), Transactions report (Monetisation, Transaction ID, ecommerce purchases), modify and create events (Admin, Data display, Events; calculated client side before data is sent; the original event remains; not applied to historical data), DebugView (Admin, Data display, DebugView; Tag Assistant and preview mode) and manage your Google tag (ignore duplicate instances of on-page configuration)
- Google Tag Manager Help, preview and debug containers
- Shopify developer docs, checkout_completed (once per checkout, Thank you page or first upsell page, not triggered again)
- Analytics Mania, duplicate events in Google Analytics 4 and how to fix them (batched requests, server-side containers, duplicate pushes, container installed twice, Measurement Protocol)