GA4 recorded purchases for years, then one day the purchase line drops close to zero and stays there. Orders keep coming in. Shopify looks normal. Nothing in GA4 warns you. For most stores in 2025 and 2026, the cause is the same: Shopify's move to checkout extensibility removed the place where the GA4 purchase code used to live.
This guide explains what changed, why it broke GA4, the three ways to rebuild the purchase event, a working custom pixel you can adapt, and how to test it. For the full diagnosis of missing purchases beyond this one cause, see GA4 missing Shopify purchases.
What changed in Shopify checkout
Checkout extensibility is Shopify's locked-down checkout. Instead of editing checkout templates and pasting scripts, merchants extend checkout with apps, UI extensions and pixels that run in a sandbox. It rolled out in stages:
| Date | What stopped working |
|---|---|
| August 2024 | checkout.liquid on the information, shipping and payment pages for Shopify Plus |
| August 28, 2025 | Thank you and Order status page scripts for Shopify Plus |
| August 26, 2026 | Additional Scripts on the Thank you and Order status pages for all other stores |
The last change happened automatically. Stores were upgraded whether or not they had moved their tracking, which is why so many GA4 properties lost purchases at the end of August 2026.
Why the GA4 purchase stopped
On older setups, the purchase usually came from one of these, and all of them are now gone:
- A gtag snippet in Additional Scripts that sent the purchase when the Thank you page loaded.
- A GTM data layer push on the Thank you page that a GA4 purchase tag listened for.
- The theme GTM container. Checkout pages never ran theme code, and with the old scripts gone there is nothing left to load it.
Everything before checkout, like product views and add to cart, often still works because it runs in the theme. That is why the drop shows up in purchases first.
Three ways to rebuild it
| Option | Best for | Trade-off |
|---|---|---|
| Google and YouTube app | Standard GA4 events and Google Ads conversions, quickly | Little control over parameters and revenue definition |
| Tracking app | Server-side delivery, many destinations, less maintenance | Monthly fee based on orders |
| Custom pixel | Full control, no extra cost | You maintain the code and test it after changes |
Whichever you choose, use only one. A common mistake after the upgrade is adding the Google and YouTube app while a custom pixel also sends purchases, which swaps missing purchases for duplicated ones.
A GA4 purchase custom pixel
Custom pixels listen for Shopify's standard events. The one that matters here is checkout_completed, which fires once per order. This example sends a GA4 purchase with items, reuses the storefront's client ID so the order keeps its traffic source, and sets the real page URL instead of the sandbox address:
// Shopify admin: Settings > Customer events > Add custom pixel
// Replace G-XXXXXXXXXX with your GA4 measurement ID.
const GA4_ID = 'G-XXXXXXXXXX';
const s = document.createElement('script');
s.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA4_ID;
s.async = true;
document.head.appendChild(s);
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
analytics.subscribe('checkout_completed', async (event) => {
const c = event.data.checkout;
// Reuse the storefront's GA4 client ID so the purchase keeps its source.
const ga = await browser.cookie.get('_ga'); // GA1.1.1234567890.1700000000
const clientId = ga ? ga.split('.').slice(-2).join('.') : undefined;
gtag('config', GA4_ID, {
send_page_view: false,
client_id: clientId,
page_location: event.context.document.location.href,
});
gtag('event', 'purchase', {
transaction_id: c.order?.id,
value: Number(c.totalPrice?.amount),
tax: Number(c.totalTax?.amount || 0),
shipping: Number(c.shippingLine?.price?.amount || 0),
currency: c.currencyCode,
items: c.lineItems.map((li) => ({
item_id: li.variant?.sku || li.variant?.id,
item_name: li.title,
item_variant: li.variant?.title,
price: Number(li.variant?.price?.amount),
quantity: li.quantity,
})),
});
});A few things to adjust before you rely on it:
- Revenue definition.
totalPriceincludes tax and shipping. If your reports use subtotal, sendsubtotalPriceinstead, and use the same definition everywhere. - Transaction ID. Use the same order identifier your other tools use, so you can match GA4 to Shopify later.
- Customer privacy. In the pixel's settings, set the permission to require analytics consent if your store shows a cookie banner. Our Consent Mode v2 guide covers the Google side.
- Upsell apps. With a post-purchase upsell,
checkout_completedfires on the first upsell page instead of the Thank you page. Listening for the event rather than a page view keeps it working. - Multi-currency stores. With Shopify Markets, checkout amounts are in the currency the customer paid in. Send that currency code with those amounts and GA4 converts them to your reporting currency. Mixing the store currency code with customer currency amounts is a common source of wrong revenue.
- Connect the pixel. A saved custom pixel does nothing until you click Connect. Customer events shows each pixel's status, so check it says Connected after every edit.
Our free tracking check shows exactly what your GA4 purchase setup is missing since the checkout upgrade, and what to rebuild first. Read-only, within 3 business days.
Get a free tracking checkRebuild the rest of the checkout funnel
The purchase is the event everyone notices, but the checkout steps before it broke at the same time. Without them, GA4's checkout funnel report goes blank and you can't see where buyers drop off. Shopify's standard events map to GA4's recommended ecommerce events like this:
| Shopify event | GA4 event |
|---|---|
checkout_started | begin_checkout |
checkout_shipping_info_submitted | add_shipping_info |
payment_info_submitted | add_payment_info |
checkout_completed | purchase |
Each one is another analytics.subscribe call in the same pixel, sending the same items array. Keep product views and add to cart wherever they run today, usually the theme or the Google and YouTube app, so nothing fires twice. Once all four checkout events arrive, GA4's funnel exploration shows exactly which step loses the most buyers, which is often more useful than the purchase count itself.
Test it end to end
- Turn on DebugView. In GA4, open Admin, DebugView. Add
debug_mode: trueto the purchase event while testing, then remove it. - Place a real order with a 100% discount code and free shipping.
- Watch the network. In your browser's developer tools, filter the Network tab for
collectand find the request withen=purchase. Check the value, currency and transaction ID. - Check the source. Arrive on the store through a URL with UTM parameters before ordering. The purchase in GA4 should keep that source instead of showing as direct.
- Watch for a week. Compare GA4 purchases with Shopify online orders each day. A healthy setup captures 90% or more. The GA4 vs Shopify revenue guide explains how to read the gap that remains.
Common questions
Why did my GA4 purchase event stop after Shopify checkout extensibility?
Purchase tracking used to run from code on the Thank you page, usually in the Additional Scripts box or checkout.liquid. Checkout extensibility removed both. Shopify Plus stores lost Thank you page scripts on August 28, 2025, and other stores were upgraded on August 26, 2026. Tracking now has to run through Settings, Customer events.
Does Google Tag Manager still work on Shopify checkout?
Not from the theme. The theme GTM container does not load on checkout pages. You can load GTM inside a custom pixel and push Shopify events into its data layer, but GTM preview mode does not connect to the pixel sandbox, so test with GA4 DebugView or the network tab.
Should I use the Google and YouTube app or a custom pixel?
Use the Google and YouTube app if you only need standard GA4 ecommerce events and Google Ads conversions. Use a custom pixel or a tracking app when you need control over parameters, client IDs, extra tags or a specific revenue definition.
Why do purchases from my custom pixel show as direct traffic?
Custom pixels run in a sandbox that cannot see the storefront cookies, so gtag creates a new client ID unless you pass the existing one. Read the _ga cookie with browser.cookie.get and send it as the client_id, as in the example in this guide.
