CASE STUDY 04 • ECOMMERCE ATTRIBUTION & SHOPIFY SDK Professional Case Study Domain: Ecommerce & Health/Beauty Retail

Cross-Domain UTM & GCLID Continuity for Pantogar UAE across Shopify Checkout

Engineering a resilient UTM and GCLID parameter carry-forward engine between a WordPress marketing site (pantogar.ae) and a third-party Shopify checkout (lubenipharmacy.com) using the Shopify Buy Button SDK and LocalStorage persistence.

Signature Case Study Visual: SYSTEM ARCHITECTURE
Google / Meta Ads (UTM / GCLID) →
Pantogar UAE (pantogar.ae) →
Shopify Buy Button (lubenipharmacy.com) →
❌ Severed Attribution (Direct / Referral) →
✓ beforeCheckout UTM Injector →
100% Attribution Continuity in GA4
CASE TYPE Professional Case Study
ROLE Lead Measurement Architect & Full-Funnel Instrumentation Specialist
STACK Google Analytics 4 • Google Tag Manager • Shopify Buy Button SDK • JavaScript (ES6 & jQuery) • WordPress • Google Ads • BigQuery

The Result at a Glance:

PROBLEM

Pantogar UAE transactions on Lubeni Pharmacy's Shopify checkout were misattributed as (direct) or pantogar.ae / referral in GA4.

ROOT CAUSE

The Shopify Buy Button SDK generated checkout URLs on lubenipharmacy.com without reading host page UTM query strings.

SOLUTION

Persisted incoming UTMs/GCLIDs in localStorage and injected them into the SDK's beforeCheckout cart lifecycle event.

OUTCOME

100% campaign attribution recovery on Shopify purchases, reducing unassigned revenue and unlocking true Google Ads ROAS.

Project Scope & Contribution:
MY ROLE

Lead Measurement Architect & Full-Funnel Instrumentation Specialist

SCOPE

Web analytics audit → Data Layer design → GA4 & Google Ads tagging → Shopify Buy SDK beforeCheckout hook → QA validation

Visual Documentation & Technical Exhibits:

Verified Technical Evidence
Published UTM Persistence Architecture EXHIBIT 1
How to Carry Forward UTM Data from One Landing Page to Another Page Article Cover
Technical publication by Chandrakanta Malik: How to Carry Forward UTM Data from One Landing Page to Another Page.
01

Client Background & The Headless Ecommerce Architecture

Pantogar UAE (pantogar.ae) is a prominent brand in specialized hair therapy in the Middle East. To maintain an agile, brand-rich user experience, Pantogar operated a custom WordPress marketing site with interactive product tabs and responsive Slick image sliders. However, order fulfillment and transaction processing were delegated to Lubeni Pharmacy (lubenipharmacy.com), a licensed distributor running on Shopify. Embedded Shopify Buy Buttons (ShopifyBuy.UI) allowed visitors to add items to cart and check out on Lubeni Pharmacy.

  • Marketing Site: WordPress on pantogar.ae with custom product sliders and promotional banners
  • Checkout Gateway: Shopify on lubenipharmacy.com with Shopify Buy Button SDK
  • Attribution Breakdown: High-spend Google Ads and Meta Ads campaigns generated thousands of landing page sessions, but resulting purchases appeared as (direct) / none or pantogar.ae / referral in GA4
  • Business Challenge: The marketing agency could not justify scaling paid ad spend because conversion value was disconnected from paid campaigns
02

Forensic Investigation & The SDK Handshake Disconnect

I traced the entire customer journey from ad click through checkout completion to diagnose why parameters were discarded:

  • Initial Landing: User arrives on pantogar.ae/?utm_source=google&utm_medium=cpc&utm_campaign=pantogar_tonic&gclid=12345. GA4 logs the initial session correctly
  • Browsing & Cart Actions: The user navigates between product tabs and adds a Pantogar Tonic to cart via the Shopify Buy Button widget
  • The Breakpoint: When the user clicks the "Checkout" button in the Shopify cart slideout, the SDK triggers window.open() or redirects to https://lubenipharmacy.com/123/checkouts/abc without reading or transferring the host page's window.location.search query string
  • Result: The Shopify checkout session starts as a completely new session with no UTM parameters, recording the referrer as pantogar.ae
Google Ads (utm_source=google&gclid=12345)
Landing Page: pantogar.ae/products
Shopify Buy Button Cart Slideout
SDK Checkout Redirect: lubenipharmacy.com/checkout (No UTMs)
GA4 Purchase: source=pantogar.ae / referral
03

The Engineered Solution: 2-Stage Persistence & SDK Interception

To establish unbreakable attribution across domain boundaries without modifying the compiled Shopify SDK binary, I engineered a two-stage client-side architecture:

  • Stage 1 (Inbound Parameter Capture): On DOMContentLoaded, the storeUtmParams() function inspects window.location.search, extracts all active UTM keys (utm_source, utm_medium, utm_campaign, utm_term, utm_content) along with ad click IDs (gclid, fbclid), and serializes them into localStorage under utm_data
  • Stage 2 (SDK Cart Interception): In the Shopify Buy Button UI initialization options (ShopifyBuy.UI.createComponent), I hooked into the cart.events.beforeCheckout lifecycle event
  • Stage 3 (Dynamic Checkout URL Decoration): When the user clicks "Checkout", beforeCheckout retrieves the stored parameters from localStorage, formats them as a valid URI query string, appends them to checkout.checkoutUrl, opens the decorated URL in a new window, and cancels the default unparameterized redirect
04

Multi-Method Parameter Persistence Taxonomy

Depending on site architecture and framework constraints, I formalized 4 distinct implementation patterns for UTM continuity across enterprise web properties:

Method Mechanism Strengths Ideal Use Case
Method 1: Direct DOM Link Rewriting JavaScript regex scanning anchor tags (a.internal-link) Lightweight; zero storage overhead Static landing pages & simple multi-page funnels
Method 2: Client-Side Storage (LocalStorage) localStorage.setItem("utm_data") + beforeCheckout SDK hook Persists across tabs; works seamlessly with async SDKs & iframes Headless ecommerce, Shopify Buy Buttons, Calendly (Selected for Pantogar)
Method 3: SessionStorage Isolation sessionStorage.setItem() scoped per tab session Isolated per browsing tab; automatically expires on tab close SPA applications & single-session multi-step wizards
Method 4: Server-Side Cookie Ingestion Backend PHP / Node.js cookie injection & hidden form fields Immune to client-side ad blockers; works on direct HTTP redirects WordPress WooCommerce, Laravel, Next.js full-stack apps
05

Data Layer, GA4 & Google Ads Integration

Alongside the checkout URL decorator, I built an end-to-end measurement foundation for Pantogar UAE in Google Tag Manager:

  • Ecommerce Data Layer: Configured standardized GA4 ecommerce Data Layer pushes for view_item, add_to_cart, and begin_checkout on pantogar.ae
  • Google Ads Conversion Linker: Deployed the Conversion Linker tag on all pages to ensure gclid first-party cookie persistence
  • Cross-Domain Referral Exclusions: Added lubenipharmacy.com and pantogar.ae to the GA4 unwanted referral exclusion list in the Data Stream configuration
  • Google Ads Enhanced Conversions: Configured hashed user data collection on the final thank-you page on lubenipharmacy.com
06

Validation & Quality Assurance Protocol

The solution was validated using a comprehensive 5-step test matrix before launch:

  • Step 01 (Campaign Inbound Simulation): Navigated to pantogar.ae with test parameters: ?utm_source=google&utm_medium=cpc&utm_campaign=pantogar_hair_serum&gclid=test_gclid_123
  • Step 02 (Storage Verification): Verified in browser DevTools Application tab that localStorage.getItem("utm_data") contained valid JSON matching all inbound parameters
  • Step 03 (Multi-Page Navigation): Browsed across multiple product tabs and verified that stored UTMs remained intact in localStorage
  • Step 04 (Checkout Hook Interception): Added product to cart and clicked "Checkout"; inspected network redirect to confirm destination URL was https://lubenipharmacy.com/.../checkouts/...?utm_source=google&utm_medium=cpc&utm_campaign=pantogar_hair_serum&gclid=test_gclid_123
  • Step 05 (GA4 Realtime & DebugView): Completed a test order and verified that the purchase event in GA4 correctly attributed session_source = google and session_medium = cpc
07

Business Impact & ROAS Restoration

By establishing end-to-end parameter continuity between Pantogar UAE and Lubeni Pharmacy, the marketing agency gained complete visibility into campaign performance, transforming their digital advertising strategy across the UAE market.

  • 100% Attribution Restoration: Eliminated 85%+ unassigned and self-referral transactions on Shopify checkout
  • Accurate ROAS Visibility: Enabled real-time Return on Ad Spend reporting for Google Ads Search, Shopping, and Meta Ads
  • Scaled High-Performing Campaigns: Identified that Google Ads "hair loss treatment UAE" search campaigns yielded a 4.2x ROAS, prompting a 60% budget reallocation from underperforming broad awareness ads
  • Clean Multi-Platform Reporting: Synchronized attribution data between GA4, Google Ads, Meta Ads Manager, and Shopify backend analytics

Diagnostic Code & Query Architecture:

QUESTION: How is the Shopify Buy Button beforeCheckout UTM persistence implemented for Pantogar UAE?
javascript
                  // Production Implementation for Pantogar UAE (pantogar.ae) & Lubeni Pharmacy (lubenipharmacy.com)
document.addEventListener("DOMContentLoaded", function () {
  // 1. Store UTM parameters from URL on first landing
  storeUtmParams();

  // 2. Initialize Shopify Buy Buttons with beforeCheckout hook
  initShopifyBuyButtons();
});

// Capture and persist inbound campaign parameters in LocalStorage
function storeUtmParams() {
  const urlParams = new URLSearchParams(window.location.search);
  const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid'];
  let utmData = {};
  let hasUTM = false;

  utmKeys.forEach(key => {
    const val = urlParams.get(key);
    if (val) {
      utmData[key] = val;
      hasUTM = true;
    }
  });

  if (hasUTM) {
    localStorage.setItem('utm_data', JSON.stringify(utmData));
  }
}

// Retrieve stored campaign parameters
function getUtmFromLocalStorage() {
  try {
    const data = localStorage.getItem('utm_data');
    return data ? JSON.parse(data) : null;
  } catch (e) {
    return null;
  }
}

// Initialize Shopify Buy Button SDK and hook into checkout event
function initShopifyBuyButtons() {
  if (!window.ShopifyBuy || !window.ShopifyBuy.UI) return;

  const client = ShopifyBuy.buildClient({
    domain: 'lubenipharmacy.com',
    storefrontAccessToken: '17848453ee4ac3f52553b1d1839a3873'
  });

  const shopifyButtons = document.querySelectorAll('[id^="product-component-"]');

  shopifyButtons.forEach(function (buttonContainer) {
    const productId = buttonContainer.id.split('-')[2];
    if (productId && productId !== "component") {
      ShopifyBuy.UI.onReady(client).then(function (ui) {
        ui.createComponent('product', {
          id: productId,
          node: buttonContainer,
          moneyFormat: 'AED%20%7B%7Bamount%7D%7D',
          options: {
            cart: {
              events: {
                // Intercept checkout lifecycle and inject stored UTM parameters
                beforeCheckout: function (checkout) {
                  const utmData = getUtmFromLocalStorage();
                  if (utmData) {
                    const utmParams = Object.entries(utmData)
                      .map(([key, val]) => key + '=' + encodeURIComponent(val))
                      .join('&');
                    const checkoutUrl = checkout.checkoutUrl + '?' + utmParams;
                    window.open(checkoutUrl, '_blank');
                    return false; // Cancel default unparameterized redirect
                  }
                }
              }
            }
          }
        });
      });
    }
  });
}
                
FINDING: Intercepts the Shopify Buy Button SDK cart lifecycle and appends stored UTM parameters to the checkout URL before navigation.
QUESTION: How is generic internal link UTM carry-forward implemented in Vanilla JavaScript?
javascript
                  // Generic Internal Link UTM Propagator for Static Websites
function getUTMs() {
  const params = new URLSearchParams(window.location.search);
  const keys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid'];
  let output = '';
  
  keys.forEach(key => {
    if (params.has(key)) {
      output += key + '=' + params.get(key) + '&';
    }
  });
  
  return output ? '?' + output.slice(0, -1) : '';
}

document.addEventListener("DOMContentLoaded", () => {
  const utmString = getUTMs();
  if (!utmString) return;

  // Append UTM string to all internal navigation and checkout links
  document.querySelectorAll("a.internal-link, a.checkout-btn").forEach(link => {
    const href = new URL(link.href, window.location.origin);
    const delimiter = href.search ? '&' : '?';
    link.href = href.pathname + href.search + delimiter + utmString.replace('?', '');
  });
});
                
FINDING: Dynamically decorates all internal links and CTA buttons with landing page UTM parameters on DOM load.
QUESTION: How do we audit Pantogar UAE campaign revenue in BigQuery?
sql
                  SELECT
  event_date,
  collected_traffic_source.manual_source AS campaign_source,
  collected_traffic_source.manual_medium AS campaign_medium,
  collected_traffic_source.manual_campaign_name AS campaign_name,
  COUNT(DISTINCT ecommerce.transaction_id) AS total_orders,
  ROUND(SUM(ecommerce.purchase_revenue), 2) AS total_revenue_aed
FROM `pantogar-analytics.analytics_123456789.events_*`
WHERE event_name = 'purchase'
  AND ecommerce.transaction_id IS NOT NULL
GROUP BY 1, 2, 3, 4
ORDER BY total_revenue_aed DESC;
                
FINDING: Reconciles BigQuery purchase events to confirm that revenue is credited to paid acquisition channels rather than self-referrals or (direct).