📌 Series: GA4 × SFCC Reconciliation — Part 5 of 5

This article is part of a technical series on reconciling e-commerce order data between Salesforce Commerce Cloud (SFCC) and Google Analytics 4 (GA4). It assumes familiarity with BigQuery, LookML, and the reconciliation table architecture covered in Parts 1–4. If you are arriving here directly, the section below covers the essential context.

Context: What Is SFCC and Why Do We Compare It to GA4?

Salesforce Commerce Cloud (SFCC) is an enterprise e-commerce platform used by mid-to-large retailers to manage their online storefront, product catalogue, orders, customer accounts, and fulfilment. When a customer places an order, SFCC records it server-side — regardless of what the customer’s browser does.

Google Analytics 4 (GA4), by contrast, tracks behaviour client-side — via a JavaScript tag running in the customer’s browser. When a purchase completes, GA4 fires a purchase event from the browser, sending transaction data to Google’s servers.

In theory, both systems should record the same set of orders. In practice, they never do. GA4 systematically reports fewer orders than SFCC. The gap typically ranges from 10% to 35% of web orders.

Why GA4 and SFCC Order Counts Never MatchCustomerclicks Buy NowOrder placedSFCC records orderServer-side. Always.✓ 100% of orders capturedGA4 purchase eventClient-side. Conditional.⚠ 65–90% captured (varies)GA4 misses orders when:• Consent not granted (GDPR)• Safari ITP erases cookie• Ad blocker blocks tag• In-app browser blocks JS• Tag hits API quota• Order via phone/store/B2B• GTM/tag implementation bug
Figure 0 — SFCC records every order server-side with 100% coverage. GA4 records orders client-side and misses a structural fraction. The goal of reconciliation is to understand exactly which orders are missing, why, and what to do about it.

What is a match rate?

The match rate is the percentage of SFCC orders for which a corresponding GA4 session and purchase event can be found. A match rate of 88% means that for 100 web orders in SFCC, 88 have a corresponding GA4 purchase event. The remaining 12 are invisible to your standard analytics reports.

What is an orphan order?

An orphan order is an SFCC order that has no matching GA4 session. From GA4’s perspective, it does not exist. The customer paid. The revenue is real. The attribution is simply missing.

The critical mistake is treating all orphan orders the same. Some are structurally untraceable (offline, B2B). Others are a genuine tracking failure that can — and should — be fixed.


You have normalised your transaction IDs. Your match rate is now 88% on web orders. But 12% of web orders and all offline orders still show no GA4 session. The correct approach is to classify every order in your data model, measure each category separately, and use that information to make better decisions.


The Five Categories of Unmatched Orders

Five Categories of Unmatched SFCC OrdersOFFLINE(expected, not fixable)In-store POSCall centre ordersRep-assisted salesNo browser ever.Not a bug.Classify + measureseparately.Action: classify onlyDo not fixB2B / API(expected, not fixable)API order creationWholesale portalBack-office entryNo browser.Higher AOV.Action: segment outfrom consumer KPIsITP / CONSENT(structural, partial fix)Safari 7-day ITPConsent declinedLong cycle buyersCookie lost.Attributed to direct.30% of mobile.Partial fix: CAPIserver-side trackingWEBVIEW(partially fixable)Instagram WebViewTikTok / GmailWhatsApp linksIn-app browserblocks GA4 tags.20-35% of socialad traffic.Fix: server-side GTMor web-to-app bridgeIMPL. GAP(fixable)Tag not firingConsent misconfiguredAd blocker (15-25%)Should be tracked.Isn’t. Quantifyand fix.Action: audit + fixtag implementationCategories 1-2: Structural, never fixable · Category 3: Structural but improvable · Categories 4-5: Largely fixable with investment
Figure 1 — The five categories of unmatched SFCC orders. Each requires a different response. Lumping them together as “dark traffic” leads to incorrect prioritisation.

Why Ignoring Unmatched Orders Creates Compounding Errors

When you exclude unmatched orders from your analysis, you do not make them disappear. You redistribute their impact across your remaining metrics in ways that are invisible and cumulative.

The “direct” inflation effect

In GA4, any session or conversion without a recognised source is attributed to “direct / none”. If 30% of your SFCC orders are unmatched and include orders where channel attribution was lost (ITP on Safari, WebView), those orders are inflating “direct” in your attribution reports. A channel that appears to drive 25% revenue via direct may actually be driving 10% direct and 15% through attribution-lost paid channels.

The ROAS underestimation cascade

User A clicks your Google Ads search ad on June 1st. They return on June 10th via Safari. ITP has erased their cookie. GA4 sees them as a new “direct” visitor. They convert. SFCC records the order. GA4 attributes it to direct. Google Ads does not receive credit. Your paid search ROAS looks lower than it is. You reduce your budget based on systematically incorrect data.

This is the structural reality for any business selling products with a consideration cycle longer than 7 days to Safari users — which in 2024 Western Europe includes 28–32% of desktop and 50–60% of mobile users.

The conversion rate distortion

If 15% of actual purchases generated no GA4 event, your conversion rate is systematically understated. Every A/B test, funnel analysis, and cohort comparison is biased downward. If you use this rate to calculate expected revenue per visitor for bidding, you are systematically underbidding.


The LookML Classification System

The solution is to classify every unmatched order before it reaches any dashboard.

Tier 1: Origin classification

dimension: order_origin {
  label: "Order Origin"
  type: string
  sql: CASE
    WHEN ${TABLE}.ga4_session_id IS NOT NULL
      THEN 'tracked_web'
    WHEN ${TABLE}.ga4_session_id IS NULL
         AND ${TABLE}.sfcc_channel IN ('store', 'pos', 'kiosk', 'retail')
      THEN 'offline_store'
    WHEN ${TABLE}.ga4_session_id IS NULL
         AND ${TABLE}.sfcc_channel IN ('phone', 'callcenter', 'agent', 'fax')
      THEN 'offline_callcenter'
    WHEN ${TABLE}.ga4_session_id IS NULL
         AND ${TABLE}.sfcc_customer_type IN ('b2b', 'wholesale', 'partner', 'reseller')
      THEN 'b2b_wholesale'
    WHEN ${TABLE}.ga4_session_id IS NULL
         AND ${TABLE}.sfcc_order_type IN ('subscription', 'renewal', 'auto_reorder')
      THEN 'subscription_automated'
    WHEN ${TABLE}.ga4_session_id IS NULL
         AND ${TABLE}.sfcc_order_source = 'mobile_app'
      THEN 'mobile_app_untracked'
    WHEN ${TABLE}.ga4_session_id IS NULL
         AND ${TABLE}.sfcc_channel IN ('web', 'storefront', 'website')
      THEN 'dark_traffic_web'
    ELSE 'unknown'
  END ;;
}

Tier 2: Tracking confidence

dimension: tracking_confidence {
  label: "Tracking Confidence"
  type: string
  sql: CASE
    WHEN ${TABLE}.order_origin = 'tracked_web'
      THEN '1. High'
    WHEN ${TABLE}.order_origin IN ('offline_store','offline_callcenter','b2b_wholesale','subscription_automated')
      THEN '2. Structural gap'
    WHEN ${TABLE}.order_origin IN ('dark_traffic_web','mobile_app_untracked')
      THEN '3. Fixable gap'
    ELSE '4. Unknown'
  END ;;
}

The Complete Metrics Library

measure: tracked_revenue {
  label: "Tracked Revenue (€)"
  type: sum
  sql: CASE WHEN ${TABLE}.order_origin = 'tracked_web'
            THEN ${TABLE}.sfcc_net_revenue END ;;
  value_format_name: decimal_2
  description: "Net revenue attributable to digital channels."
}

measure: fixable_gap_revenue {
  label: "Fixable Gap Revenue (€)"
  type: sum
  sql: CASE WHEN ${TABLE}.tracking_confidence LIKE '3.%'
            THEN ${TABLE}.sfcc_net_revenue END ;;
  value_format_name: decimal_2
  description: "Revenue from web orders with no GA4 match. Business case for tracking investment."
}

measure: web_dark_traffic_rate {
  label: "Web Dark Traffic Rate"
  type: number
  sql: COUNT(DISTINCT CASE WHEN ${TABLE}.order_origin = 'dark_traffic_web' THEN ${TABLE}.order_id END)
       / NULLIF(COUNT(DISTINCT CASE WHEN ${TABLE}.sfcc_channel IN ('web','storefront') THEN ${TABLE}.order_id END), 0)
  ;;
  value_format_name: percent_1
  description: "Target: below 15%. Above 20% indicates tracking issues."
}

measure: revenue_coverage_ratio {
  label: "Revenue Coverage Ratio"
  type: number
  sql: ${tracked_revenue} / NULLIF(${sfcc_net_revenue}, 0) ;;
  value_format_name: percent_1
}

The Complete Reconciliation Dashboard Architecture

Dashboard 1: Executive revenue picture

KPI tiles: SFCC total net revenue, tracked revenue, structural gap revenue, fixable gap revenue, revenue coverage ratio. Five numbers that give a complete picture of revenue by attribution status.

Dashboard 2: Finance reconciliation

Line chart: SFCC net revenue vs GA4 reported revenue by month with delta. Answers the Monday morning question: “why are the numbers different?”

Dashboard 3: Tracking implementation health

Trend line: web dark traffic rate over the last 90 days. Spikes = deployments that broke something. Gradual increase = systemic degradation (consent mode drift, ad blocker growth).

Dashboard 4: Business mix by channel

Breakdown of orders and revenue by order_origin, by month. Shows the true contribution of offline, B2B, and subscription channels independent of what appears in GA4.


The Minimum Viable Starting Point

  1. Run the diagnostic queries from Part 3 to understand your current match rate
  2. Create the two normalised BigQuery views (norm_sfcc_orders and norm_ga4_purchases)
  3. Create a reconciliation table in BigQuery with order_id, order_origin, sfcc_net_revenue, ga4_revenue, revenue_delta, channel, created_at
  4. Connect to Looker and define your core governed measures
  5. Build the four KPI tiles and the dark traffic trend line

Sources and Further Reading