Shop Pay, Apple Pay, PayPal: Why GA4 Misses Revenue
Show article contentsHide article contents
- What we found in 27 stores (and why 78% are in the gap)
- How we ran the 27-store audit
- Shop Pay: where the purchase event goes to die
- Apple Pay: the OS modal your tracker can't see into
- PayPal Express: redirects, popups, and the 15% who never come back
- The consent layer that compounds every gap
- Why the $450/month server-side pipe barely moved the numbers
- Revenue Gap Calculator
- The two jobs you're accidentally asking one tool to do
- Decoupling attribution from the payment sheet: the architecture
- What this looks like in Clickport (and what it doesn't)
- Frequently asked questions
- Keep reading
Shop Pay processes 41% of Shopify's payment volume. We crawled 27 top DTC Shopify brands in April 2026 and found 78% of them routing checkout through shop.app, pay.shopify.com, or checkout.shopify.com. These are domains where the merchant's own analytics cannot execute. Only one store in the sample had a third-party server-side tracking pipe installed. The rest are in the gap. This is why your Shopify admin and GA4 never match, and the fix every vendor sells you does not actually close it.
- Shop Pay represented 41% of Shopify's Q4 2024 gross payment volume. Its express path does not reliably fire GA4's purchase event, a behavior Shopify staff confirmed as expected in the developer forums.
- In our April 2026 audit of 27 top DTC Shopify brands, 78% route checkout through shop.app, pay.shopify.com, or checkout.shopify.com, where the merchant's own JavaScript cannot execute.
- Only 1 of 27 audited brands visibly installed a third-party server-side tracking pipe. The $200 to $990 per month rescue ecosystem is ignored by 96% of the sample.
- Server-side Google Tag Manager relays events that already fired. It cannot create events that never fired. The express checkout gap is an event origination problem, not a transport problem.
- Shopify's orders/paid webhook fires reliably for every completed order including Shop Pay, Apple Pay, and PayPal, regardless of ad blockers, consent state, or payment-sheet JavaScript context.
What we found in 27 stores (and why 78% are in the gap)
Before the mechanics, the audit. On April 14, 2026 we crawled public HTML across 27 well-known DTC Shopify brands spanning apparel, beauty and personal care, home goods, and food and beverage. Each is a recognizable name in its category with substantial traffic and an active paid-social presence. We are not publishing the store list to avoid inviting brand-level arguments about a platform-level finding. The shape of the sample is what matters: it is the kind of store an agency or a DTC founder would hold up as best-in-class.
The 78% is the one to sit with. For 21 of the 27 brands, the cart page itself already references a Shopify-controlled checkout domain. We manually spot-checked four of the most-trafficked brands in the sample. All four embed shop.app/checkouts/internal/preloads directly in their cart HTML. This is not an edge case triggered only when a specific buyer taps Shop Pay. Shopify is actively pre-warming the shop.app checkout experience as the default path for these brands.
The 4% is the more uncomfortable one. Every ecommerce tracking article ranking on Google recommends a server-side pipe: Elevar, Stape, Littledata, Analyzify. We found one store out of 27 using any of them. Zero Elevar, zero Stape, zero Analyzify, one Littledata. The entire $200 to $990 per month rescue ecosystem is being ignored by 96% of the sample, and the reason is not that these brands do not care about their data. The reason is that the pipe does not fix the problem people think it fixes. More on that in section six.
For a deeper view of where Shopify's native analytics fall short, see Shopify Analytics: The Store Owner's Guide to Better Data.
How we ran the 27-store audit
The full method, so the results above are checkable and rerunnable.
Sample. 27 publicly known DTC Shopify brands, selected from commonly cited Shopify case studies and industry lists, filtered to those we could confirm were running on Shopify (by detecting cdn.shopify.com, Shopify.theme, or shopify-section markers in the homepage HTML). The mix spans apparel (9 stores), beauty and personal care (8), food and beverage (7), home goods (2), and personal care adjacent categories (1). No store with under 50k monthly visits was included; we biased toward brands with existing industry reporting, so the sample reflects best-in-class DTC implementation, not long-tail merchants. We are withholding the store list from publication to keep the finding focused on the platform-level gap rather than inviting brand-level disputes over individual data points.
What we fetched. For each store, three public endpoints:
- The homepage (
/) - The cart page (
/cart) - The Shopify-default
/products.json?limit=1to get one product handle, then the product page (/products/{handle}) where the JSON endpoint returned data
No authentication. No cart additions. No checkout initiation. No PII involved. Every request used a Safari 17 desktop user-agent string, a 15 to 25 second timeout, and a 1.5 second delay between stores.
What we looked for. The crawler grepped each store's combined HTML for regex signatures of the tracking stack, express checkout path, and third-party pipes:
- GA4 measurement ID:
G-[A-Z0-9]{6,12},gtag/js?id=G-,"accountID":"G-",google-analytics.com/g/collect,"measurementId":"G-" - Google Tag Manager:
GTM-[A-Z0-9]{4,10},googletagmanager.com/(gtm|gtag).js - Meta Pixel:
connect.facebook.net,fbevents,fbq(,facebook.com/tr?,"pixelId":followed by 10+ digits,facebook-domain-verification - TikTok Pixel:
analytics.tiktok.com,ttq.load,ttq.page,tiktok.com/i18n/pixel - Klaviyo:
static.klaviyo.com,klaviyo.com/onsite,_learnq,klaviyoOnsite - Shopify native analytics:
shopify-analytics,monorail-edge.shopifysvc,trekkie.storefront - Web Pixel sandbox active:
web-pixels-manager,webPixelsManager,web_pixels_manager - Third-party server-side pipes:
getelevar.com,shopify.elevar.io,littledata.io,analyzify.com,stape.io,gtm.stape,.stape.net - Express checkout domains:
shop.app,pay.shopify.com,checkout.shopify.com - Express checkout buttons:
shopify-payment-button,shop-pay-button,shop_pay,apple-pay-button,ApplePaySession,paypal.com/sdk,paypal-button - Headless detection:
/products.json?limit=1returning non-JSON was treated as headless or JSON endpoint blocked
Limits, stated up front. Three deliberate limitations matter:
- Sandboxed pixel IDs do not always appear in SSR HTML. Shopify's Web Pixel sandbox renders tracking configuration inside iframed JSON that is hydrated client-side. A store running GA4 via the sandbox may show
web-pixels-managersignatures without a visibleG-XXXXXXmeasurement ID in the static HTML. This means our GA4 / Meta Pixel / TikTok counts are floors, not ceilings. - Headless storefronts shift tracking further client-side. Four stores in our sample (15%) are Next.js-fronted on top of a Shopify backend. Their marketing HTML is hydrated by React, which often loads tracking asynchronously. For these stores, SSR signal is weakest.
- Third-party server-side pipes can be invisible when the merchant routes sGTM through a custom subdomain. We searched for vendor-owned domains (stape.io, elevar.io, littledata.io, analyzify.com) and for common sGTM patterns. A merchant who has cleanly CNAMEd their sGTM container to a fully custom subdomain will not match these signatures. The 1-of-27 figure is therefore a floor for third-party sGTM adoption; the true number could be higher, but not by much given that most implementations leave at least one vendor-identifiable string in the HTML.
What the crawl reliably tells us. Which domain the checkout references (shop.app, pay.shopify.com, checkout.shopify.com), whether the Web Pixel sandbox is active (a strong binary signal), whether a third-party pipe is visibly branded in the markup, and which express checkout buttons are declared. These are the signals our headline findings rest on.
| Signal detected in public HTML | Count | % |
|---|---|---|
| References shop.app / pay.shopify.com / checkout.shopify.com | 21 | 78% |
| Web Pixel sandbox active | 20 | 74% |
| Shopify native analytics (monorail / trekkie) | 20 | 74% |
| PayPal integration detected | 18 | 67% |
| Klaviyo | 17 | 63% |
| GTM container | 15 | 56% |
| Meta Pixel visible in SSR HTML | 14 | 52% |
| GA4 measurement ID visible in SSR HTML | 11 | 41% |
| Apple Pay button detected | 6 | 22% |
| Shop Pay button detected | 6 | 22% |
| Headless storefront (products.json blocked) | 4 | 15% |
| TikTok Pixel | 1 | 4% |
| Third-party server-side pipe (Elevar / Stape / Analyzify / Littledata) | 1 | 4% |
Spot-check. We manually opened the cart page HTML of the four most-trafficked brands in the sample to verify the 78% figure was not an artifact of the regex. All four embed the string shop.app/checkouts/internal/preloads directly in their cart markup. Shopify is actively pre-warming the off-domain checkout experience as the default path for these brands. It is not a corner case triggered only when a specific buyer taps Shop Pay.
Reproducibility. The entire audit is 80 lines of bash and Python: curl each store's three URLs, pipe the combined HTML through grep -E for each regex above, write a row to CSV, aggregate with value_counts(). Anyone with a developer laptop and two hours can rerun it against their own sample and compare.
What we will rerun. October 2026, same 27 stores, same regexes, published as a diff. Two numbers we expect to move: the percentage of stores in full headless mode (likely up), and the percentage with the Web Pixel sandbox hiding pixel IDs (also likely up as more merchants migrate off checkout.liquid).
Shop Pay: where the purchase event goes to die
Shop Pay was 38% of Shopify's full-year 2024 gross payment volume, peaking at 41% in Q4 2024, per PYMNTS coverage of Shopify's Q4 2024 earnings call. Shopify's own BFCM 2024 report adds that Shop Pay sales grew 58% year over year across the BFCM weekend. This is the most-used accelerated checkout in ecommerce, and it is the one that breaks your analytics the hardest.
Here is what happens when a buyer taps the Shop Pay button on a product page. Shopify redirects the session to shop.app or pay.shopify.com, depending on the flow. The merchant's JavaScript does not run there. GA4's tag, Meta Pixel, any GTM container, the Klaviyo onsite tracker, all of them stop at the domain boundary. The checkout steps run in a domain the merchant does not control and cannot inject scripts into. When the buyer completes, Shopify redirects them back to the merchant's own thank-you page. That redirect is the one thing that was supposed to save client-side tracking. It does not.
A Shopify staff member, Alan_G, confirmed in the Shopify developer community that the checkout funnel events, checkout_started, checkout_shipping_info_submitted, payment_info_submitted, do not fire in the Shop Pay view. His phrasing was "at the moment, it's expected behaviour that the events don't fire in the Shop Pay view," with the team putting the fix "on our radar to enable tracking in the medium-term." No ETA. The thread dates to June 2025. As of this writing, no fix has shipped.
The checkout_completed event is the one that maps to GA4's purchase event and Meta's Purchase. It fires only when the buyer lands on the merchant's thank-you page and the merchant's pixel gets a chance to execute. For Shop Pay, this has been inconsistent. Merchants in the same forum thread have reported either the event firing with an order total of zero, or not firing at all, or firing for some orders and silently dropping others. There is no single number for how often it works, because the failure mode is non-deterministic.
There is a secondary failure mode that is worse because it is silent. In October 2025, a merchant named Jason on the Shopify community reported that enabling Shop Pay Installments (Pay in 3) silently switched his checkout domain from brownieheaven.co.uk to shop.app. His Meta Pixel had been domain-verified on brownieheaven.co.uk, so the verified-domain check now failed. Purchase events stopped firing to Meta's CAPI. He lost £175 in ad spend over five days before he diagnosed it. Shopify did not warn him before making the change. He eventually disabled Pay in 3 to get his attribution back.
This is not a configuration problem any merchant could have prevented. The domain switch was a product decision by Shopify that demolished the merchant's domain verification chain. A Shop Pay feature update can break your Meta tracking tomorrow and you will find out from your ROAS dashboard, not from a Shopify email.
Apple Pay: the OS modal your tracker can't see into
Apple Pay on the web is not an iframe. It is not a pop-up. It is a browser-native modal rendered by Safari and the OS, outside the merchant page's document entirely. Apple's ApplePaySession documentation describes the flow in terms of "the system" showing the payment sheet, not the merchant page. The merchant page never owns the sheet.
This means your JavaScript event listeners cannot observe what the buyer does inside it. The ApplePaySession API exposes events for merchant validation, shipping changes, and the final paymentauthorized callback, but these fire at specific hand-off points. The biometric authentication (Face ID, Touch ID, passcode) is inside the Secure Enclave. The tap on "Pay" is inside the sheet. The page JavaScript gets paymentauthorized after the Secure Enclave says the payment is good, with the encrypted payment token. Everything else is opaque.
For a visitor who tapped Apple Pay, completed the biometric check, and then closed the tab or lost their connection before the thank-you page loaded, the merchant's pixel sees exactly nothing. Not an add_payment_info event, not a purchase event, not a checkout_completed. Shopify's official docs confirm that accelerated checkout buttons can be placed directly on product pages, bypassing the cart entirely. The button HTML is rendered inside a closed shadow DOM. Any event tracking attached to the DOM fails silently.
Stacked on top of this, Safari's Intelligent Tracking Prevention (ITP) caps JavaScript-set first-party cookies at 7 days, and additionally to 24 hours if the landing URL carried any link-decoration parameter like fbclid or gclid. The GA4 client ID cookie and Meta's _fbp cookie are both script-set first-party. A returning buyer after 8 days looks like a new visitor to your analytics. The Apple Pay flow does not cause this, but it combines with it: the cohort most likely to use Apple Pay is mobile Safari users, which is where ITP hits hardest. Per StatCounter (March 2026), Safari holds about 55% of US mobile browser share and 43.86% of UK mobile share.
Worth flagging: the commonly cited "53% Safari UK mobile" figure is wrong. StatCounter's direct dashboard shows 43.86%. Use the real number.
PayPal Express: redirects, popups, and the 15% who never come back
PayPal's Smart Buttons use three different flows depending on device and browser state. On desktop with popups allowed, the checkout opens in a pop-up window. On mobile or when the popup is blocked, PayPal falls back to a full-page redirect to paypal.com. Inside an iframe context, there is a third iframe-fallback path. PayPal's own developer documentation describes the popup as the primary mode and the redirect as "older," but in practice every mobile Shopify checkout takes the redirect.
When a mobile buyer completes on paypal.com and PayPal 302-redirects back to the merchant's thank-you page, the browser sends Referer: https://www.paypal.com/ to the thank-you page. GA4's cross-domain linker parameter (_gl) is routinely stripped by the HTTP-level redirect — Google's own cross-domain documentation acknowledges the parameter "may end up being removed from the URL." Seresa.io documented the mechanism plainly: "JavaScript cannot intercept an HTTP server redirect. That's the fatal gap." Without _gl, GA4 starts a new session attributed to paypal.com as the referrer, overwriting whatever Meta, Google, or email campaign brought the buyer in.
Then there is the 10 to 15% who never return at all. MarketLytics quantified this based on their GA implementations: "In about 85-90% of cases, user returns to the site successfully and we can trigger a transaction. But those 10-15% of times a user closes the page after completing the order, is where the trouble lies." The payment cleared. The order is in Shopify's admin. The purchase event never fires, because there was no thank-you page load for it to fire on.
PayPal's PPCP integration makes Venmo checkout worse. Braintree's own Venmo documentation states the Mobile Web Fallback flow "currently requires a full page redirect." On mobile the standard flow switches to the Venmo app, then returns, and Braintree notes that "some mobile browsers can return customers to the same tab, while others relaunch your checkout page in a new tab" — which in practice means the merchant has to handle session restoration on return. Every Venmo payment through a PPCP integration involves either a full-page redirect or an app-switch. Your session cookie, linker parameter, and client ID have to survive a path that crosses either a server redirect or a native app context switch.
The consent layer that compounds every gap
Before the checkout even happens, a meaningful share of your visitors have already declined cookies. This is GDPR in practice, and it is not going away.
The USENIX Security 2024 paper by Bielova et al. ran controlled banner experiments on French users. Equal-prominence accept and reject options produced a 34% rejection rate. Banners that highlighted the consequences of data sharing reached 47%. etracker's 2025 consent benchmark for Germany measured 60% data loss under legally compliant banner design. Once you weight for the EU share of traffic in a DTC brand's typical audience, the GA4 and Meta Pixel coverage hit is already 15 to 30% before a single express checkout enters the picture.
The SHEIN fine makes the legal stakes concrete. CNIL issued a €150 million penalty on September 1, 2025, announced September 3. SHEIN's violation was not a missing reject button. The reject button existed. It was functionally broken: cookies still got placed after "Refuse all," and pre-existing ones continued reading. "Has a reject button" is now the baseline. The enforcement bar is "does the reject button work."
On a Shopify store, consent banner rejection does not just hide the analytics data. Shopify's Web Pixels API documentation specifies that the pixel manager will not load a pixel that declares marketing or analytics consent as required when that consent is absent. Because the pixel never loads, the cookieless modeling pings Google Consent Mode v2 uses to fill gaps cannot fire from inside the sandbox either. Architecturally, the visitor is not a 50% signal. They are a zero.
For more on why cookie-banner-free tracking matters architecturally, see What Cookie-Banner-Free Analytics Actually Means. For the full picture on GA4 gaps beyond checkout, see Why GA4 Is Not Showing Data.
Why the $450/month server-side pipe barely moved the numbers
Server-side tracking is the dominant answer that every article ranking for this topic recommends. The mechanism is: sGTM (server-side Google Tag Manager) runs on infrastructure you control, receives hits from the browser or from Shopify webhooks, and forwards them to GA4, Meta CAPI, and other destinations. In theory it bypasses ad blockers, resets cookie expiration, and recovers lost events.
In practice it does some of this and not all of it, and its pricing is not what the blogs mention. Elevar charges $200/month for the 1,000-order tier, $450/month for 10,000 orders, $950/month for 50,000 orders. Their Expert Installation add-on starts at $1,000 one-time. Littledata starts at $199/month for 1,500 orders, climbs to $990/month at 10,000 orders. Analyzify lists $109 to $206/month, with a server-side tagging add-on from $1,490 to $2,790 one-time depending on whether you self-host on Google Cloud or use a Stape-hosted container. Stape is the infrastructure-only option at $17 to $167/month (annual billing), plus Google Cloud Run hosting costs that realistically add $60-$240/month depending on traffic volume.
Against this, Stape's own published case study recovered 20.71% of previously lost requests over a 10-day test, with purchase events specifically at 30.67%. These are impressive numbers for what sGTM actually does: recover requests that the browser made but that got blocked in transit. They are not numbers for what sGTM cannot do.
sGTM cannot create events that never fired. If Shop Pay does not fire checkout_completed because the Shopify staff engineer said it is not expected to fire, there is no hit for sGTM to relay. The Shopify Web Pixel sandbox controls what events come out of the checkout domain. sGTM sits downstream of the sandbox. It is a pipe with better bypass characteristics, not a power source that generates missing events.
Our audit of 27 brands found 1 store with a visible third-party server-side pipe installed. Everybody else is on Shopify's native Meta, Google, and TikTok channel integrations, which also run inside the same Web Pixel sandbox the sGTM pipes are trying to work around. The conclusion is not that these tools are useless. They genuinely recover 20-30% of otherwise-lost events. It is that 96% of the sample has decided the recovery is not worth $200-$990/month, and they have a point.
| Problem | sGTM / CAPI |
|---|---|
| Ad blocker blocks browser request | Fixed (routes via first-party domain) |
| Safari ITP caps cookie to 7 days | Partially fixed (server-set cookies can be longer-lived) |
| Shop Pay `checkout_completed` event never fires | Not fixed (nothing to relay) |
| Apple Pay sheet, buyer closes tab pre-thank-you | Not fixed (no client hit) |
| PayPal mobile redirect, `_gl` stripped | Not fixed (attribution lost before server relay) |
| Consent banner rejection (EU) | Not fixed (sandbox suppresses firing entirely) |
| Shop App upsell on `shop.app/thankyou` | Not fixed (order never recorded on merchant domain) |
| Pixel + CAPI event deduplication | Works if `event_id` matched on both sides; frequently broken |
Revenue Gap Calculator
Plug in your own numbers. The math is transparent and the source stats are linked below the widget.
The two jobs you're accidentally asking one tool to do
Here is the reframe. Your Shopify admin is the revenue source of truth. Every completed order is there. The orders/paid webhook fires for every one of them, whether the buyer used Shop Pay, Apple Pay, PayPal, or the standard checkout. Shopify does not lose orders. Shopify loses analytics events.
Your analytics, done right, is the source of truth for which channel earned each order. Not whether the order happened. Not how much revenue it generated. Those are Shopify's job. The analytics job is: when an order ID appears in Shopify, which session started it, and where did that session come from.
GA4 tries to do both. It counts orders and attributes channels in the same system, using the same client-side purchase event as the join between the two. When the purchase event fails to fire at the payment sheet, both jobs fail at once. You lose the revenue count and the channel attribution simultaneously, because the event that would have carried both is gone.
The decoupling argument is simple. Count orders where orders are counted: Shopify admin. Attribute channels where channel data is captured: at landing, before the payment sheet exists. Join the two by order ID at the moment the webhook fires. The payment sheet is no longer in the critical path.
Edward Upton, Littledata's CEO, has been writing about this gap for years without framing it this way. Brad Redding of Elevar said on the Honest Ecommerce podcast in October 2022: "I don't think the vast majority of any site out there is going to say, 'Yes, my Google Analytics is 100% accurate.'" Rand Fishkin put the broader version of it on the SparkToro blog in July 2024: "If you try to prove acquisition via traffic referral data you will become Google's fool." The industry has the diagnosis. The prescription is still "ship more events faster," which is exactly what the payment sheet refuses to cooperate with.
Decoupling attribution from the payment sheet: the architecture
What this looks like in practice, on a Shopify store, with any analytics tool that has a server-side custom event API.
Step one: the session source is captured at landing. When a visitor lands on the store, a first-party script reads the URL's UTM parameters, the referrer, and any click IDs (gclid, fbclid). It writes these into a session identifier on the server, keyed by a short-lived first-party ID. If the tool is cookieless, the session ID lives in sessionStorage (tab-lifetime only) or is regenerated on each visit. If it is cookie-based, a first-party cookie handles it. Either way, the channel is locked in before anything about checkout matters.
Step two: during the add-to-cart or begin-checkout step, the storefront writes the session's context into Shopify's note_attributes on the cart. This is the critical move. note_attributes is a key-value array that Shopify persists with the order. Whatever you write there survives the Shop Pay redirect, the Apple Pay sheet, the PayPal popup, and the return trip. When the order lands in Shopify's admin, your session context is in order.note_attributes.
Step three: Shopify fires the orders/paid webhook. Not orders/create — that fires for unpaid orders too. orders/paid is the definitive "money received" event. Your webhook handler verifies the HMAC, reads the payload, pulls the session context out of note_attributes, and fires a server-side custom event to your analytics tool with the full revenue amount, currency, order ID, and channel attribution.
import crypto from 'crypto';
export async function handleShopifyWebhook(req) {
const rawBody = await req.text();
const hmacHeader = req.headers.get('x-shopify-hmac-sha256');
const digest = crypto
.createHmac('sha256', process.env.SHOPIFY_CLIENT_SECRET)
.update(rawBody)
.digest('base64');
if (!crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(hmacHeader))) {
return new Response('Unauthorized', { status: 401 });
}
const order = JSON.parse(rawBody);
const ctx = Object.fromEntries(
(order.note_attributes || []).map(a => [a.name, a.value])
);
await fetch('https://clickport.io/api/event', {
method: 'POST',
headers: {
'X-API-Key': process.env.CLICKPORT_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'custom',
name: 'Purchase',
url: order.landing_site || `https://${order.domain}/orders/${order.id}`,
revenue_amount: parseFloat(order.total_price),
revenue_currency: order.currency,
meta_keys: ['order_id', 'utm_source', 'utm_medium', 'utm_campaign', 'payment_method'],
meta_values: [
String(order.id),
ctx.utm_source || '',
ctx.utm_medium || '',
ctx.utm_campaign || '',
order.payment_gateway_names?.[0] || ''
]
})
});
return new Response('OK', { status: 200 });
}
This is 30 lines including the HMAC verification. It runs as a Cloudflare Worker, a Vercel function, a Shopify App's webhook handler, or a basic Express route. The /api/event endpoint details are in Clickport's custom events documentation. The Shopify webhook registration and field reference are in Shopify's Admin API docs.
What the architecture gets you that server-side GTM does not: the server-side event does not depend on any client-side event having fired. If Shop Pay fires nothing, if Apple Pay suspended the tab before thank-you, if PayPal never returned from paypal.com, none of it matters. Shopify delivered the order to its admin. The webhook fired. The server-to-server custom event carried the session context across. The join happened at order ID, not at cookie.
What it does not get you: it does not fix Meta Ads Manager's ROAS dashboard. Meta's optimization engine still wants Pixel and CAPI events with proper deduplication. That is a separate problem with a separate fix. What the architecture gets you is a correct internal picture of which channel earned which revenue, which is the thing you actually make business decisions on.
What this looks like in Clickport (and what it doesn't)
Clickport is a cookieless analytics product. The tracker is 5.6 KB, loads from a first-party domain, and writes a session ID to sessionStorage (tab-lifetime only, clears on close). No persistent cookies. No consent banner required. The 16-channel classifier runs server-side and catches AI Search, social referrers, paid click IDs, and affiliate parameters that GA4 routinely collapses into "Direct." The custom events API accepts the webhook payload above directly.
What the architecture above gets you inside Clickport: every completed Shopify order appears as a Purchase event with the original session's channel attached. You can segment revenue by channel and see that the $47,000 GA4 marked as "Direct" last month was actually 31% Meta paid social, 22% Google organic, 18% email, and 11% AI Search referrals from ChatGPT and Perplexity. The data is honest because the channel was captured before the payment sheet broke anything.
What it does not do, and where I want to be honest with you:
- Clickport does not replace Shopify's admin as the revenue ledger. We read your orders. Shopify still owns them. If Shopify says $120,000 this month and Clickport says $119,800, Shopify wins. Always.
- Shop App upsell orders completed on shop.app/thankyou are structurally unrecoverable for every analytics vendor including us. The buyer's session is inside Shopify's domain, the upsell happens entirely there, the merchant's webhook fires with the order but the upsell's channel context is lost before it can be captured. This is a known platform-level limit, not a tool problem.
- We do not provide Meta CAPI forwarding or pixel deduplication out of the box. If you need your Meta Ads Manager ROAS to reflect the recovered events, you need a separate CAPI implementation. Shopify's native Meta channel is the lowest-friction option there; Stape's sGTM is the more robust one. Clickport gives you your internal attribution picture. It does not manage Meta's bidding signal.
- Our Shopify integration is a documented pattern, not an installed app. The code snippet above is how real customers wire it up. There is no one-click Shopify app store install today. If you are a technical team or agency, this takes an afternoon. If you need a click-install, we are not there yet.
The honest summary: Clickport solves the part of the problem the existing ecosystem is worst at, which is attributing revenue to the correct channel when the payment sheet has broken the client-side event chain. It does not solve the parts the ecosystem is already good at, like Meta ROAS dashboards. Different tools for different jobs, which is the whole point of the decoupling argument.
Start a 30-day free trial. No credit card. The tracker installs in 60 seconds, and the webhook handler above is the second thing you ship.
Frequently asked questions
Why is my GA4 showing fewer purchases than Shopify?
Shopify's admin counts every server-confirmed order. GA4's purchase event only fires when the thank-you page loads in the buyer's browser and the client-side script gets a chance to execute. Ad blockers, consent banner rejections, Safari ITP, Shop Pay domain hops, Apple Pay's OS-level sheet, and PayPal's mobile redirect all break this chain. A 10-20% gap is industry baseline. Above 20% signals a configuration problem. Above 30% usually means express checkouts are a large share of your payment mix.
Why is Shop Pay not tracking in GA4?
Shop Pay's express path completes on shop.app or pay.shopify.com, domains where your GA4 tag does not run. Shopify's developer staff confirmed in June 2025 that checkout_started, checkout_shipping_info_submitted, and payment_info_submitted events "don't fire in the Shop Pay view" as "expected behaviour." The thank-you page can receive a checkout_completed event if the buyer returns to the merchant's domain, but this is non-deterministic. Add shopify.com and shop.app to your GA4 referral exclusion list to prevent the worst attribution damage, and use a server-side event fired from the orders/paid webhook for reliable revenue counting.
Does Apple Pay break GA4 tracking?
Apple Pay's payment sheet is an OS-level modal rendered by Safari and the operating system, outside the merchant page's document. Your JavaScript cannot observe the biometric authentication or the tap on "Pay." If the buyer closes the tab before the thank-you page loads, the purchase event never fires. ITP's 7-day cookie cap compounds this for returning visitors. Apple Pay on mobile Safari is one of the highest-volume, least-trackable payment paths in ecommerce.
Why does GA4 show Direct for purchases that came from my Meta ads?
Two compounding causes. First, the checkout flow hops through payment.shopify.com or paypal.com, which become the last referrer on the thank-you page and overwrite your original Meta attribution. Second, GA4's cross-domain _gl linker parameter is stripped by HTTP-level server redirects, so session continuity breaks at the domain hop. Any session that lacks both UTMs and a valid referrer defaults to direct / (none). Fixes: add shopify.com, shop.app, paypal.com to your GA4 referral exclusions, and capture UTMs into Shopify's note_attributes before the buyer leaves your storefront.
What does pay.shopify.com mean in my GA4 referral sources?
When a buyer authenticates Shop Pay via the SMS code modal on pay.shopify.com, that domain becomes the last touchpoint before your thank-you page. Brad Redding of Elevar documented this pattern years ago: pay.shopify.com shows up as its own referral channel, stealing credit from the original campaign. Fix: GA4 Admin → Data Streams → Configure tag settings → List unwanted referrals → add shopify.com and shop.app.
Is it normal for GA4 to be 20% short?
Littledata's February 2025 benchmark says 20 of every 100 Shopify orders fail to appear in GA4 on average. That is with a standard client-side setup. Individual merchants have reported gaps as high as 84% (Shopify community thread, January 2025). A 10-20% gap is tolerable. A 30%+ gap means express checkouts are a meaningful share of your payment volume and you need a server-side purchase event fired from the Shopify webhook.
Keep reading
For the broader Shopify tracking picture, start with Shopify Analytics: The Store Owner's Guide to Better Data. For a deeper look at what GA4 hides beyond checkout, Why GA4 Is Not Showing Data is the companion piece. If you want to understand the channel classification problem across all traffic (not just ecommerce), Attribution Modeling: The Six Models with Actual Math shows why the "Direct" bucket is the worst lie in your dashboard. And for the cookie-banner argument that sits underneath all of this, What Cookie-Banner-Free Analytics Actually Means frames the regulatory math.
We will rerun the 27-store audit in October 2026 and publish the diff.

Comments
Loading comments...
Leave a comment