Shopify: GA4 “purchase” is being counted twice – find the causes and fix them properly.
Direct solution coming soon
- Check if GA4 is integrated multiple times (Theme code, Google & YouTube, Google Tag Manager, Custom Pixel, Apps).
- Choose exactly one source to send "purchase" (recommended: either GTM or Shopify Custom Pixel or Google & YouTube).
- Make sure that only one trigger fires on "checkout_completed"/Thank-You.
- Use a stable transaction_id (order number/order ID) and optionally deduping via localStorage.
When does this occur?
- GA4 displays 2 (or more) "purchase" events for individual orders.
- Revenue/conversions in GA4 are higher than in Shopify (Admin → Analytics/Reports).
- In GA4 DebugView, several "purchase" events appear in quick succession when an order is completed.
- In the browser network, you can see multiple requests to GA4 (e.g.
collect?v=2) with identical/similar shopping carts.
Technical background: Why does this happen?
Double counting almost always occurs because more than one tracking setup is active simultaneously and sending the "purchase" event. In Shopify, typical sources for this include:
-
Theme integration (e.g., gtag.js or GTM snippet in
theme.liquid). - Google & YouTube channel/app that independently connects GA4 and broadcasts events.
-
Shopify Customer Events → Custom Pixel , which populates the GA4/gtag or
dataLayer. - Apps that inject their own GA4/GTM or conversion tags.
Additionally, an event can be triggered twice if it is triggered both on a generic page view (e.g., "thank you page viewed") and on a checkout-specific completion event ("checkout_completed").
Step-by-step: Here's how to implement it
-
Inventory the current state (find all GA4 sources)
- Shopify Admin → Settings → Customer events : Check if a custom pixel is active that addresses GA4/gtag/GTM.
- Shopify Admin → Online Store → Themes → … → Edit Code : Search for
GTM-,googletagmanager,gtag(,G-(Measurement ID),GA_MEASUREMENT_ID. - Shopify Admin → Apps : Check tracking/consent/marketing apps (these often inject additional tags).
- Shopify Admin → Sales channels or installed channels: Check if Google & YouTube are connected and GA4 tracking is active.
-
Verify in GA4 that it is indeed a duplicate.
- Open GA4 → Admin (Administration) → (Property) and use DebugView for a test purchase (or a test order).
- Alternatively, in the browser: DevTools → Network and filter for
collect?v=2. If two requests occur around the time of checkout completion, it's a true double trigger.
-
Identify a "Source of Truth" (only one entity may send "purchase")
- Option A (common in agency setups): Use GTM as the central instance → remove/pause GA4-Purchase from Custom Pixel and from Google & YouTube, or remove GTM from the theme if Google & YouTube are to be used.
- Option B: Shopify Custom Pixel sends GA4 → remove GA4/gtag and GTM from the theme and disable duplicate app integrations.
- Option C: Google & YouTube manages GA4 → do not run an additional GA4/GTM implementation for "purchase" in parallel.
Important: It is okay to use multiple tools (e.g., Consent + GTM), but not multiple parallel "purchase" senders.
-
Remove duplicate triggers (typical locations)
- If you are using GTM: Make sure that “purchase” only fires via one trigger (e.g., only on a dedicated checkout completion signal, not additionally on page view/history change).
- If you are using Custom Pixel: Only send “purchase” on a completion event (e.g.
checkout_completed) and not additionally onpage_viewedon the order confirmation page.
-
Optional: Deduping in the custom pixel (additional security)
If, for technical reasons, you cannot guarantee that an event is triggered only once, you can add simple deduping based on an order ID/order number to the Shopify Custom Pixel .
Shopify Admin → Settings → Customer events → Add custom pixel (or edit existing pixel) and only use this if you are actually populating GA4/Datalayer there:
// Beispiel: Deduping für purchase im Shopify Custom Pixel // Ziel: pro Bestellung nur einmal senden (clientseitig) analytics.subscribe("checkout_completed", (event) => { // Defensive Checks if (!event || !event.data || !event.data.checkout) return; const checkout = event.data.checkout; // Versuche eine stabile ID zu finden (je nach Datenverfügbarkeit) const transactionId = (checkout.order && (checkout.order.id || checkout.order.name)) || checkout.order_id || checkout.id; if (!transactionId) return; const key = "ga4_purchase_sent_" + String(transactionId); try { if (localStorage.getItem(key) === "1") return; localStorage.setItem(key, "1"); } catch (e) { // Wenn localStorage blockiert ist, kein Hard-Fail } // GA4 über dataLayer (nur wenn GTM vorhanden/gewünscht)window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: "purchase", transaction_id: String(transactionId) }); });Note: This example pushes to the
dataLayer. If you are usinggtagdirectly, check beforehand whetherwindow.gtagexists, and only send if it does. -
Test result
- Perform a test order.
- GA4 → DebugView: Only one “purchase” should appear.
- Samples: Shopify orders vs. GA4 conversions/revenue (consider timely results and the same time zone).
Common mistakes
- Cause: GA4 is active in the theme (gtag) and also as a custom pixel. Fix: Completely remove one implementation (theme code or custom pixel), then test again.
- Cause: Google and YouTube are sending purchase notifications, while GTM is also sending "purchase" notifications. Fix: Choose one source; disable the duplicate purchase sender (depending on your setup: channel/app or GTM tag/trigger).
- Cause: The "purchase" event is triggered on both "Thank-You Page View" and "checkout_completed". Fix: Keep only one trigger (preferably the unique checkout event, not a generic pageview).
-
Cause: Different
transaction_idfor each trigger (e.g., one order ID, one checkout ID) → appears as two purchases. Fix: Use a uniformtransaction_id(order number/order ID) and set it identically in all tags. - Cause: Apps inject additional GA4/Ads tags that also send purchase events. Fix: Disable purchase tracking in the app configuration or remove the app; then recheck the DebugView/Network.
Best Practices
- One responsibility per event: Determine which system sends "purchase" (GTM or Custom Pixel or Google & YouTube) and document it.
- Stable transaction_id: Use a unique order identifier and use it identically in all purchase events.
- Changes in only one layer: If GTM is the source, do not add any additional gtag/pixel snippets to the theme.
- DebugView as a release criterion: No release without 1:1 validation (one checkout = one purchase).
- Deduping as a safety net: If technically necessary, add client-side deduping (e.g., localStorage) – but do not replace the cleanup of duplicate sources.
Brief summary
- Double counting almost always comes from multiple active GA4 implementations in Shopify.
- Check theme code, customer events (custom pixel), Google & YouTube, and apps.
- Only one source may send "purchase"; consistently remove duplicate triggers.
- Use a stable transaction_id (order ID/order number) for clean assignment.
- Test with GA4 DebugView and network requests until only one purchase arrives per order.

