Where your Shopify plan supports it, move checkout onto the primary domain to remove the cross-domain problem entirely. Where checkout must stay on myshopify.com or shop.app, capture the GA4 client id and any ad-click id on the storefront before the shopper reaches checkout, pass them forward explicitly (for example via a query parameter your custom pixel reads), and send the purchase event using that explicit client id rather than relying on gtag.js's automatic, cookie-based identity on the checkout domain.
Why this happens
Cookies are scoped per registrable domain. myshopify.com and shop.app are entirely different registrable domains from a merchant's custom domain, so a first-party analytics cookie set while browsing the storefront is simply not visible to code running on checkout's domain. This is standard browser cookie-scoping behavior, not a Shopify-specific defect - any platform whose checkout is hosted cross-domain runs into the identical problem, and it's the same class of issue Universal Analytics' old cross-domain linker parameter existed to solve for other multi-domain setups.
Fix it
- Check whether "checkout on your domain" is available on your Shopify plan under Settings > Domains - Shopify has been consolidating checkout onto the primary domain for most plans, and enabling it removes the cross-domain cookie problem outright.
- If checkout must stay cross-domain, capture the GA4 client id (the
_gacookie value) and session id on the storefront side before the shopper reaches checkout. - Pass that client id forward using a mechanism the checkout's custom pixel can actually read - since the pixel can't read the storefront's separate cookie jar, common approaches are encoding the id into the cart/checkout URL as a query parameter, or relying on Shopify's own customer/session identifiers to correlate the two sides after the fact.
- On the checkout_completed custom pixel, send the purchase event to GA4 via the Measurement Protocol directly (server-side, or as a fetch call from inside the pixel) using the explicit client id you captured - not gtag.js's automatically-generated one, which will simply be a fresh, unrelated id in the checkout's separate cookie context.
- Apply the same forwarding pattern to ad-platform click ids (gclid, fbclid): capture them on the storefront, pass them through to checkout, and attach them to the CAPI or server-side event rather than assuming a browser pixel on checkout has access to them.
Branch: if "checkout on your domain" is available on your plan but hasn't been enabled, do that first - it's a one-time settings change that eliminates every downstream workaround in this fix, rather than a partial mitigation.
How to verify it worked
With DevTools open, browse the storefront through to checkout on a real, cookies-allowed session (not incognito). In Application > Storage, note the _ga cookie's client id value while still on the storefront. Then, in the Network tab at checkout completion, filter for requests to google-analytics.com/g/collect or /mp/collect and inspect the cid parameter on the purchase hit. If the two values match, continuity worked. If the checkout-side hit shows a freshly generated client id instead, the session wasn't carried through, and GA4 will record that purchase as a brand-new, unattributed user rather than the same session that clicked the original ad.