Skip to content

GuidesShopify7 min read

Fix the GA4 Purchase Event on Shopify (Step by Step)

How the Shopify purchase event fires today, why Additional Scripts no longer works, and the limits of sending a GA4 purchase from a custom pixel.

Published
Reviewed
Repair the purchase event and verify the completed order.

Daniil MaximkinProduct & Solutions Engineer

Short answer

Shopify publishes checkout_completed through Customer Events, isolated from your theme. I start with the supported Google & YouTube app for GA4 purchase measurement: Google does not support Google tags inside Shopify custom pixels. Use one purchase path, check transaction_id, value, currency and items, then reconcile against Shopify orders.

— Daniil

Key takeaways

  • Shopify fires the purchase through Customer Events (web pixels) in a sandbox — code in Additional Scripts or checkout.liquid can no longer send it to GA4.
  • August 28, 2025 was the Plus sunset; non-Plus stores had to upgrade their Thank you and Order status pages by August 26, 2026. Additional Scripts became view-only on August 28, 2025.
  • Google does not support Google tags inside Shopify custom pixels. Check the Google & YouTube app before maintaining an experimental custom implementation.
  • Deduplication depends on a consistent transaction_id and, above all, on firing the purchase from exactly one path — never the native GA4 channel and a custom pixel at once.
  • Verify within the sandbox's limits: DebugView and Realtime for the live event, then order reconciliation after reports have processed for coverage.
In this guide

If your Shopify store used to send clean purchases to GA4 and now sends nothing, or sends them twice, check where the purchase event is collected. This guide covers Shopify’s current event path, the retired script locations, and the limits of a custom pixel.

How does Shopify fire the purchase event today?

Shopify moved checkout into an extensible model, and tracking moved with it. Instead of injecting scripts into checkout pages, you register a pixel that subscribes to the events Shopify emits — page views, product views, add-to-cart, and, for the completed order, checkout_completed.

The important architectural fact is the sandbox. Custom pixels do not run on your storefront page. They run in a separate, restricted context with their own window, isolated from your theme’s scripts. This is deliberate: it stops third-party pixels from reading everything on the page. But it has two consequences that trip up almost every broken setup I see:

  • A GTM container or gtag snippet loaded by your theme cannot see checkout events, because the pixel and the theme live in different contexts.
  • Anything you want to send from the checkout — your GA4 tag included — must be loaded inside the pixel itself, or forwarded from Shopify to a server.

Once you internalize that, the old symptoms make sense: the reason your theme’s GTM stopped catching purchases is not a bug, it is the sandbox doing its job.

What happened to Additional Scripts and checkout.liquid?

Two older methods still appear in guides and inherited stores, and both are dead ends.

  • checkout.liquid customizations were how Plus stores injected tracking into the checkout. That path has been retired in favour of checkout extensibility.
  • Additional Scripts on the Thank you and Order status pages was a legacy tracking location. August 28, 2025 was the Plus sunset. For non-Plus stores, Shopify made Additional Scripts view-only on that date, then required the page upgrade by August 26, 2026. Order-status script tags had the same Plus/non-Plus retirement dates. Check the store’s page version and order data rather than assuming one date describes every store.

The practical takeaway: if a store still lists its purchase tag in Additional Scripts, audit whether that tag actually runs and whether another integration sends the purchase. If it no longer runs, migrate the missing event to a supported pixel and verify it with a test order and order reconciliation.

The limits of a custom pixel implementation

Google does not support Google tags inside Shopify custom pixels and recommends the Google & YouTube app. A sandboxed tag can send data while other features fail. I check the supported app before choosing a custom implementation.

The following is a hypothetical custom-pixel example, not a supported Google setup or a tested production implementation. It shows the event mapping for an upgraded checkout with line-level discounts. Order-level discounts and tax-inclusive prices need separate checks against the order; this example does not establish those adjustments. The sandbox loads gtag itself rather than using the theme’s tag.

// Shopify admin → Settings → Customer events → Add custom pixel
// This code runs in Shopify's pixel sandbox: it has its own window,
// isolated from your theme. A GTM/gtag snippet on the theme is NOT
// reachable here, so load gtag inside the pixel and fire from here.

const MEASUREMENT_ID = 'G-XXXXXXXXXX';

// 1) Load gtag.js into the sandbox and configure GA4
const s = document.createElement('script');
s.src = 'https://www.googletagmanager.com/gtag/js?id=' + MEASUREMENT_ID;
s.async = true;
document.head.appendChild(s);

window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', MEASUREMENT_ID, { send_page_view: false });

// 2) Fire the purchase exactly once, when the checkout completes
analytics.subscribe('checkout_completed', (event) => {
  const checkout = event.data.checkout;
  if (!checkout.order?.id || !checkout.currencyCode || !checkout.lineItems?.length) return;

  const items = (checkout.lineItems || []).map((line) => ({
    item_id: line.variant && (line.variant.sku || line.variant.id),
    item_name: line.title,
    item_variant: line.variant && line.variant.title,
    price: line.finalLinePrice.amount / line.quantity,
    quantity: line.quantity,
  }));

  gtag('event', 'purchase', {
    transaction_id: checkout.order.id,
    value: items.reduce((sum, item) => sum + item.price * item.quantity, 0),
    currency: checkout.currencyCode,
    tax: checkout.totalTax && checkout.totalTax.amount,
    shipping:
      checkout.shippingLine &&
      checkout.shippingLine.price &&
      checkout.shippingLine.price.amount,
    items: items,
  });
});

If you route through Google Tag Manager instead, the same analytics.subscribe block pushes an equivalent object to a dataLayer that a container loaded inside the pixel reads. The rule does not change: the destination tag has to live in the sandbox with the subscription, not on the theme. For the mechanics of that object, see the note on the dataLayer.

What does the GA4 items array require?

Google lists items and transaction_id as required purchase parameters. An event arriving without them does not establish that the ecommerce implementation meets the documented schema.

Per Google’s ecommerce reference, each item needs at least one of item_id or item_name; sending both makes it easier to inspect. price, quantity, and item_variant make the item reports usable. A few rules that save debugging time:

  • value is the sum of item price × quantity, excluding tax and shipping. Use discounted unit prices and send tax and shipping in their separate parameters. Reconcile the same value definition against Shopify.
  • currency is required and must be a three-letter ISO code. In multi-currency stores, send the currency the customer actually transacted in, and make sure value is in that same currency.
  • item_id must be stable. Use the SKU or variant ID consistently, because that is the key GA4 uses to join items across events.

How does GA4 deduplicate Shopify purchases?

GA4 deduplicates purchases with the same transaction_id in web streams. Google describes this for transactions from the same user; it is not a guarantee across different user contexts or app streams. Two rules matter:

  1. Fire the purchase from exactly one path. Shopify’s native GA4 sales channel and a custom pixel can both send the order in different user contexts. A matching transaction ID alone does not prove deduplication worked. Choose one path and verify the received event.
  2. Never send an empty transaction_id. GA4 deduplicates all purchases with an empty transaction ID together, so a blank value does not just fail to help — it actively collapses genuine orders into one. Always send the real order ID.

If you have already seen GA4 running higher than Shopify, deduplication is where to look first. The companion article on why GA4 revenue does not match Shopify walks through catching those duplicates in reconciliation.

Verification protocol

Verifying inside the sandbox is different from verifying a normal page, and the usual first instinct — open GTM Preview or Tag Assistant — largely does not work here. This is the sequence I trust.

  1. DebugView, on a real order. Put a test device into debug mode and complete an actual checkout. Watch the purchase land in DebugView and inspect its parameters: is transaction_id present and non-empty, is value right, is currency correct, does items contain the products?
  2. Realtime as a sanity check. The purchase should appear in the Realtime report within moments. Realtime confirms the event is arriving even when DebugView attachment is fiddly.
  3. Accept the tooling limits. Tag Assistant and GTM Preview cannot hook into the pixel sandbox the way they hook into a page. Not seeing the event there is expected; it is not evidence the pixel failed. Judge success by DebugView and Realtime.
  4. Every payment path, not one. Test standard card, Shop Pay, express or wallet checkout, and post-purchase upsells. Shopify emits checkout_completed once: usually on Thank you, but on the first upsell offer page for post-purchase flows. If that page does not load, the event is not emitted.
  5. Reconcile after processing. Compare GA4 purchases against Shopify orders by transaction ID after the reports have processed. Google says processing can take 24–48 hours. Reconciliation measures recorded coverage; it cannot prove browser collection captured every order regardless of consent or a failed page load.

Common failure modes

  • Double pixels. The native GA4 sales channel and a custom pixel can send duplicate purchases. Keep one purchase path and check IDs and user context before concluding that revenue doubled.
  • Missing express paths. The purchase is bound to a flow that Shop Pay or express checkout skips, so wallet orders never send a purchase and go missing during reconciliation. This is the same class of fault covered in Shopify checkout tracking broken.
  • Consent blocking. Under a consent banner, analytics storage may be denied, so the pixel is not permitted to send for those visitors. That is a legitimate loss, not a bug to code around. This covers technical implementation, not legal advice — for the mechanism, see consent mode v2.
  • Stale Additional Scripts tag. The purchase is configured in a retired script location. Check the store’s page version and whether a replacement pixel or app already sends the event before changing the setup.
  • Empty or mismatched transaction_id. A blank ID collapses orders; an ID that differs between paths defeats deduplication entirely. See the related breakdown of GA4 not tracking purchases.

Limitations

A custom pixel inherits the limits of client-side collection and is not Google’s supported Shopify integration. Its configured privacy requirements can block it while consent is denied. A failed page load or browser blocking can also prevent collection.

The sandbox also constrains debugging, so verification leans on DebugView, Realtime, and reconciliation rather than the usual page-level inspectors. And a pixel fixes the purchase; it does not, on its own, repair upstream events like add-to-cart if those were broken by the same checkout migration.

Alternatives

There are three broad ways to send the Shopify purchase, and they are not mutually exclusive.

  • Native Shopify integration. The Google & YouTube app is Google’s supported setup. Check for an existing custom purchase tag before adding it, so that two paths do not send the same order.
  • Custom pixel with GTM or gtag (the hypothetical example above). You own the code, but Google does not support this setup. Sending one event successfully does not prove other Google tag features work.
  • Server-side. A server-side setup can forward Shopify order webhooks while browser events supply session and consent context. Webhooks do not contain browser cookies. The setup needs hosting, monitoring and a consent design; it does not remove the consent requirement.

If you want the purchase path chosen, verified and reconciled for you rather than maintaining it yourself, that is the scope of my GA4 implementation work; the verification method behind it is order reconciliation.

If the guide did not settle itUSD 395

I run this reconciliation on your store read-only, for USD 395 — written verdict two working days after the kickoff.

Request a Health Check

Questions

Questions this guide answers

Do I still need the native GA4 sales channel if I use a custom pixel?

Pick one purchase path. Two integrations can send duplicate requests; whether GA4 counts both depends on the transaction IDs and user context. Google's supported Shopify path is the Google & YouTube app, not Google tags inside a custom pixel. Remove duplicate purchase tags and verify one order against the received event.

Can I use my theme's GTM container to fire the Shopify purchase?

Not for the new checkout. A theme-level GTM container cannot see checkout or Thank you events, because they run in a separate sandbox. Google's supported path is the Google & YouTube app. Loading gtag or GTM inside a custom pixel can send data, but Google does not support it; the other option is sending the purchase server-side.

Why doesn't Tag Assistant or GTM Preview show my purchase event?

The custom pixel runs in a sandboxed context that Tag Assistant and GTM Preview cannot attach to the way they attach to a normal page. That is a limitation of the tools, not proof the event failed. Verify with GA4 DebugView and Realtime instead, then check coverage after reports have processed.

The purchase fires on card checkout but not on Shop Pay or express checkout. Why?

Test every payment path individually. Shopify triggers checkout_completed once, usually on the Thank you page, or on the first upsell offer page for post-purchase flows. If that page fails to load, the event does not fire. A missing event is not enough to identify a subscription or app fault.

What should I use as the transaction_id?

Use the Shopify order ID or order name, the same value on every path, unique per order. Never send an empty string: GA4 deduplicates purchases with an empty transaction_id together, which silently collapses your revenue. A consistent, non-empty ID is what makes deduplication work.

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