Chasing the Missing Shopify Attribution Behind Shop Pay
I chased a missing third of a store's shopify attribution to Google Ads for two weeks. Shop Pay was the trapdoor. Here is what finally caught it.

Do this before you finish reading. Open Shopify Analytics, look at orders for last month, and note what share came in through Shop Pay and PayPal. Now open Google Ads and pull conversions for the same range. If "direct" or "unknown source" owns a fat slice of your orders while the rest of your site clearly runs on paid traffic, you probably have the same shopify attribution leak I chased for a merchant on a call I did not want to be on. It is not a broken tag. It is a hop most people never look at.
What I checked first
The client, a specialty coffee subscription store doing roughly 900 orders a month, opened the call with one line. "Google Ads says we did 41 conversions last month. Shopify says we did 640 orders. Something is off." Around 60 percent of those orders came in through Shop Pay. About a third of those Shop Pay orders were flagged in Google Ads as direct.
My first move was the obvious one. I opened Tag Assistant, walked through a cart-to-checkout flow with a fake ad click, and watched every tag fire. The Google Ads conversion tag on the thank-you page fired cleanly. The event ID was there. The purchase value was there. If anything, the tag was almost too clean. It just was not receiving the click identifier it needed.
Second move was GA4. In DebugView I could see the purchase events landing with the right transaction IDs. But the traffic source on those events was "(direct) / (none)" for a group of orders I was pretty sure had come in from paid ads. I filtered to those transactions and looked at the session preceding each one. They all had the same fingerprint. The last event was checkout_completed, and the referrer was shop.app.
That was the fingerprint I did not fully understand yet.
Why the tag was not actually broken
I want to say this out loud, because it slowed me down for three days. When Google Ads reports fewer conversions than Shopify, the first instinct is to blame the tag. The client's previous developer had rebuilt the pixel setup twice in six months. I was about to be the third. I almost was.
But the tag was fine. It fired. It fired with the right data. Every conversion it fired for was a real, matched conversion in Google Ads. The tag was not the problem. It was firing without the one piece of data Google Ads needed to attribute the conversion back to a specific click.
That piece is the GCLID, the Google click identifier. It is a URL parameter Google appends to the landing page when a visitor clicks an ad. If you capture it, store it, and pass it along when you fire the purchase event, Google can trace the sale back to the campaign. If you do not, Google logs the sale as direct and your ROAS numbers collapse.
Most shopify attribution investigations start there, and most of them stop there when the tag looks healthy. Mine did. I assumed the GCLID was getting captured on landing (I could see it in the URL) and then getting lost somewhere between the cart and the thank-you page. I spent an afternoon writing a script that logged the GCLID on every page in the funnel. It was there on the product page. It was there on the cart page. It was there at the start of checkout. And then, on the thank-you page, it was gone.
I had misdiagnosed which handoff was breaking. I thought the checkout was eating the click. The checkout was not eating the click.
Where the trail went cold
To understand why, you have to know that Shopify's checkout is a black box below Shopify Plus. You cannot inject JavaScript into the checkout pages. You cannot read cookies from those pages using your own code. You can fire a Custom Pixel on checkout_completed, but that pixel runs in a sandboxed context that does not have access to the merchant domain's cookies. Everything you know about the visitor - GCLID, UTMs, session ID - has to be either passed into the checkout as an order attribute or reconstructed later from server-side data.
That was my working theory. The GCLID was making it to the checkout entry point, but the sandbox on the other side could not read it back out. So I built a workaround. In the cart template I extracted the GCLID from the current URL, and any cookie I had already set, and stored it in a Shopify cart attribute. Cart attributes survive into the order. From the Custom Pixel, I could then read the order attribute and pass the GCLID to the Google Ads conversion event.
I rolled this out on a Wednesday. By Friday, the client's Google Ads conversions were up from 41 to 208 for the week. I told myself the case was closed and moved on to the next client.
Except it was not closed. Two weeks later the client emailed me. "Conversions dropped again over the weekend, we did about 40 percent fewer than the week before." I pulled up the data. Shopify orders were up. Google Ads conversions were down. My fix had worked for some orders. It was systematically failing for a specific subset.
The clue I almost missed
I went back to GA4 and looked at the referrer on the failing sessions. It was shop.app. Every single one.
I have to admit I had noticed this on day one and dismissed it. My assumption was that shop.app was just how Shop Pay labels itself internally and that the checkout stayed on the merchant domain. It does not. When a customer clicks the Shop Pay Express button, the one that shows up if they have used Shop Pay anywhere before, they are redirected to shop.app to authenticate and complete their purchase. When they return, they arrive on a thank-you page in a fresh browser context. The GCLID that was carefully stored in a cart attribute is now a cart attribute of a cart the customer never came back to. The order that fires from the shop.app flow has no memory of my attribute.
The same pattern plays out with PayPal Express. The customer leaves the merchant domain to authenticate on paypal.com and comes back to a thank-you page. In some flows, especially when Shop Pay upsells are enabled, the thank-you page itself is hosted on shop.app/thankyou and never touches the merchant domain at all. My Custom Pixel does not fire in a context where it can even see the merchant's cookies.
So my fix was recovering the roughly 60 percent of orders that went through the standard checkout. It was invisibly missing the 40 percent that used Shop Pay Express or PayPal Express. That was the drop the client kept seeing week over week. It correlated with how many of their customers happened to click Shop Pay Express on a given day instead of the plain Check Out button.
I felt embarrassed about this for about an hour. Then I went back to the r/PPC threads on this topic and realised the exact failure mode is documented in half a dozen places, always described in slightly different words, always with the same conclusion. The accelerated payment methods break the browser session in a way the standard tag setup cannot survive.
What we actually did about it
The shopify attribution recovery has three parts. Only the first is a script the merchant can drop in themselves. The other two need someone comfortable wiring up a webhook and a small server-side endpoint, or an app that does the wiring for you.
-
Capture the GCLID on the very first pageview and persist it in three places. On the merchant domain, extract the
gclidURL parameter and write it to (a) a first-party cookie with a 90-day expiration, (b) a Shopify cart attribute the moment the visitor adds anything to cart, and (c) a customer metafield if the visitor is logged in. Belt, braces, and a piece of string. The cookie handles the standard flow. The cart attribute handles the sandboxed Custom Pixel case for merchants who stay on the merchant domain. The metafield handles the case where a returning customer completes checkout in a browser session different from the one they clicked the ad in. -
Fire the conversion from a webhook, not from the browser. Set up a small endpoint that listens for Shopify's
order_paidwebhook. When an order arrives, look up the associated GCLID from the cart attribute if present, from a customer metafield if the order has a linked customer, or by matching the customer's hashed email against a store of recent GCLIDs the endpoint has seen. Send the conversion to Google Ads using the offline conversions API. This bypasses the browser entirely, which means Shop Pay's domain hop cannot break it. -
Reconcile weekly. Shopify's own order log is the source of truth. Every Friday, export the week's orders, cross-reference the Google Ads conversion export, and count the delta. The delta will not be zero, and that is fine. You are looking for a stable ratio. If your delta was 15 percent last month and it is 15 percent this month, you are in a good place. If it moves to 40, something structural has changed. Usually a new consent banner, a theme update that broke the GCLID capture, or a Shopify migration that changed the thank-you URL pattern.
There are apps that will do all three for you if you would rather not run a webhook. Elevar is the most complete and the most expensive; it starts around $200 a month at the volume this client was doing. Littledata is cheaper and focuses more on GA4 than on Google Ads specifically. Stape lets you run the whole thing yourself for infrastructure cost. I have used all three. None of them are magic. All three still benefit from the cart-attribute capture in step one running upstream of whatever they do.
If your recovered numbers move the same way this client's did, somewhere between doubling and tripling reported ad conversions, your bidding will drift for a week or two while Google's Smart Bidding recalibrates on the new baseline. Expect noise on Performance Max especially. If you want a second pair of eyes on the wiring before you push it against a live campaign, the first fifteen minutes with a help1 expert is free and that is usually enough to spot the misconfiguration. We keep a walkthrough of the app conflicts that quietly burn Google Ads spend in the archive too, because the two failure modes get confused with each other constantly.
The piece I still am not sure about
There is one part I have not tested to my own satisfaction. When Shop Pay Express redirects a customer to shop.app, does Shopify itself store the referring GCLID anywhere on the order object? The Order API has landing_site and referring_site fields, and in a normal checkout those carry the initial referrer. I have not been able to confirm that either survives the Shop Pay Express hop for a checkout that started with the Express button instead of the regular flow. If those fields do preserve the GCLID for you, the entire webhook step above becomes unnecessary and you can just read the value off the order.
I have asked in the Shopify developer Discord and gotten inconsistent answers. If you have measured this end-to-end on a real store, I would genuinely like to know. Until I have, I am running the webhook fallback and treating it as cheap insurance.
So here is the question I want you to answer for yourself, tonight, before you open Google Ads tomorrow morning. What share of your orders came in through Shop Pay Express or PayPal Express last week, and how many of those would you find sitting in your Google Ads conversion report if you looked? If you cannot answer both, you are the client I described at the top of this post, and the shopify attribution number you have been optimising against is not the number you think it is.
Still stuck? Talk to an expert.
Our vetted Shopify experts can fix this issue for you in a live session. $39 per session. Your first 15 minutes are free.