Ask any e-commerce founder or marketing manager about their data accuracy, and they will likely sigh. The discrepancy between sales recorded in the Shopify dashboard and conversions reported inside **Google Analytics 4 (GA4)** is one of the most frustrating problems in modern digital marketing.
Typically, GA4 underreports sales by 10% to 20%, or attributes a massive chunk of conversions to the source paypal.com / referral or stripe.com / referral instead of Google Ads, Meta Ads, or organic search.
When ad platforms are stripped of conversion credit, budgeting and optimization fall apart.
In this technical guide, we will dissect why payment gateways hijack your attribution credit and show you the exact configurations needed to fix session splitting, unwanted referrals, and purchase discrepancies in GA4.
1. The Root Cause: The Gateway Referral Loop
To understand how GA4 handles traffic, you have to understand the definition of a **session**. A session represents a period of user interaction with your website. In GA4, a session is automatically reset and a new one starts when:
- The user is inactive for more than 30 minutes.
- The acquisition campaign sources change mid-journey.
This second rule is what breaks e-commerce tracking during checkout. Here is the lifecycle of a hijacked conversion:
The Hijack Loop:
1. User clicks an Instagram Ad (session source is recorded as instagram.com / cpc).
2. User adds an item to their cart and initiates checkout.
3. When completing payment, they select PayPal, Stripe 3D Secure, or Klarna. The browser redirects them away from your site to the payment processor's domain.
4. Once authorized, the gateway redirects them back to your store's thank you page.
5. Because the user returned from an external domain, GA4 registers this as a new session from a new traffic source: paypal.com / referral. The original campaign credit is erased.
2. The Checklist: Fixing Referral Splitting in GA4
To prevent payment gateways from resetting user sessions, you must add them to your Unwanted Referrals list inside your Google Tag configurations. This forces GA4 to ignore the domain change, keeping the active session open and preserving the original traffic source.
Here is the step-by-step setup:
Step 1: Navigate to Web Stream Tag Settings
Go to your GA4 property Admin page, select Data Streams, click your primary web stream, and scroll down to select Configure Tag Settings.
Step 2: Define Unwanted Referrals
Click **Show All** to expand the list of configuration settings, and select **List Unwanted Referrals**. Add conditions for every gateway domain that redirects users during checkout.
Use the following matching patterns (set Match Type to "Referral domain contains"):
paypal.com(covers standard PayPal Express checkouts)stripe.com(covers Stripe payment sessions)connect.stripe.com(covers Stripe authentication redirects)checkout.shopify.com(if using Shopify's shared checkouts)pay.shopify.com(covers Shop Pay redirects)klarna.com(covers Klarna checkouts)adyen.com(covers Adyen transaction screens)
Important Note: Setting up referral exclusions in GA4 is **not retroactive**. Once configured, it will only prevent session splits on future conversions. Past conversions will remain assigned to the referral source.
3. The Thank-You Page Mismatch: Client-Side vs Webhook
Even with referral exclusions, client-side GA4 tags frequently drop 10% to 15% of transactions. Why? Because client-side tags rely on the user loading the final purchase confirmation page.
If a buyer completes payment on PayPal and closes their tab before they are redirected back to your thank-you page, the client-side JavaScript purchase tag **never fires**. The revenue is completely lost from GA4.
To achieve a 100% match rate, you must combine client-side tags with a **server-side webhook listener** mapped via GTM Server-Side.
// Intercepting Shopify Paid Webhook and posting directly to GA4 API
const ga4MeasurementId = "G-V9VM0MWLVE";
const ga4ApiSecret = "q-4X_793J291nS...";
const webhookBody = JSON.parse(request.body);
const clientClientId = getGaClientIdFromSession(webhookBody.customer_id);
const measurementProtocolPayload = {
"client_id": clientClientId || generateFallbackId(),
"events": [{
"name": "purchase",
"params": {
"transaction_id": webhookBody.order_number,
"value": parseFloat(webhookBody.total_price),
"currency": webhookBody.currency,
"shipping": parseFloat(webhookBody.total_shipping),
"tax": parseFloat(webhookBody.total_tax),
"items": formatShopifyItems(webhookBody.line_items)
}
}]
};
postToGA4MP(ga4MeasurementId, ga4ApiSecret, measurementProtocolPayload);
Because this server-side event fires directly from the backend Shopify webhook container the second the payment is cleared, it guarantees 100% purchase logging, even if the customer drops off or closes their browser instantly.
4. What Success Looks Like: A Reconciled Dashboard
Once these structural issues are resolved, the variance between your Shopify sales records and GA4 transactions should shrink to **less than 2%**.
More importantly, you will see a significant drop in referral traffic conversions and a proportional rise in organic, paid search, and social campaign revenues. Your multi-touch and first-click attribution paths will represent the actual customer acquisition journey, giving your media buyers the accurate data they need to scale campaign budgets.
Tired of Missing E-commerce Purchases?
We specialize in setting up server-side Measurement Protocol configurations and fixing broken referral sources for 8-figure e-commerce brands. Claim a free account audit.
Get a Free Attribution Audit