Tout projet d’analytics e-commerce arrive au même moment. Quelqu’un ouvre trois onglets (GA4, l’export des commandes SFCC et le fichier hors ligne) et dit : « Ces chiffres ne collent pas. Lequel est le bon ? »
La réponse qui met fin à la confusion ouvre aussi des questions bien plus intéressantes : les trois sont justes, et les écarts entre eux ne sont pas des erreurs à corriger, ce sont des informations à extraire.
Cet article est le deuxième d’une série sur la réconciliation GA4 / SFCC / hors ligne dans Looker. Avant tout pipeline ou modèle LookML, il faut comprendre pourquoi ces sources divergent : c’est un problème conceptuel, pas technique.
Trois systèmes, trois réalités
GA4, SFCC et vos sources hors ligne ne mesurent pas le même événement. Ils n’observent pas au même endroit, ne capturent pas de la même façon et ne définissent pas une transaction de la même manière.
GA4 : ce que le navigateur a bien voulu vous dire
GA4 est un système de collecte d’événements côté client. Toute sa vision de votre activité dépend du code JavaScript qui se déclenche dans le navigateur, de ce que ce navigateur autorise à envoyer, et d’un cookie persistant qui relie l’événement à une session précédente. Chacune de ces conditions peut échouer en silence.
Ce que GA4 capture bien : le comportement en session (pages vues, scroll, clics, ajouts au panier, étapes du checkout), les chemins d’attribution (canal, campagne, source), les achats (revenu, articles, transaction ID, quand l’événement purchase se déclenche correctement) et les signaux d’identité (client ID, user ID si implémenté, GCLID).
Ce que GA4 rate par construction :
- Toute commande passée hors navigateur : téléphone, caisse, API, back-office. GA4 n’a jamais existé dans ces flux.
- Les commandes dont la page de confirmation ne s’est pas chargée : onglet fermé, timeout du prestataire de paiement, connexion 3G lente. SFCC a confirmé la commande, GA4 ne l’a jamais vue.
- Les commandes sans consentement analytics : en Consent Mode basique, GA4 ne reçoit rien ; en mode avancé, il reçoit des pings sans cookie et, si la propriété atteint les seuils de Google, affiche des données modélisées. Dans les deux cas, aucun
transaction_idn’est joignable. - Les commandes remboursées ou annulées : GA4 a enregistré le revenu au moment du
purchase. Sans événementrefund(mêmetransaction_id, montant remboursé en valeur positive), GA4 ne sait jamais que la commande a été annulée. - Les parcours longs sur Safari : l’ITP supprime les cookies posés par JavaScript après 7 jours sans visite. Un utilisateur avec un cycle de réflexion de 10 jours est un nouveau visiteur à chaque retour. Sa session d’achat existe dans GA4, mais l’attribution d’acquisition d’origine est définitivement perdue.
Le revenu GA4 est donc : la somme des valeurs des événements purchase arrivés jusqu’au point de collecte de Google depuis une session navigateur où un cookie était présent et le tracking autorisé, à l’instant où l’événement s’est déclenché. Une définition très précise, et très bornée.
SFCC : la source de vérité comptable
Salesforce Commerce Cloud est la plateforme e-commerce où naissent les commandes web ; c’est le système de référence pour ces commandes (souvent aux côtés d’un OMS/ERP). Son rôle : enregistrer chaque transaction commerciale, suivre son cycle de vie et alimenter la logistique, la finance et le service client. Peu lui importe comment le client est arrivé. Ce qui compte : une commande a été passée, confirmée, puis livrée ou annulée.
Simplified business lifecycle (illustrative):
created → payment_pending → payment_confirmed → exported (to WMS)
↓
shipped → delivered
↓
return_requested → return_received → refunded
Alternative paths:
created → payment_failed → (abandoned)
created → fraud_review → cancelled
Dans SFCC, une commande porte des statuts séparés : commande (CREATED, NEW, OPEN, COMPLETED, CANCELLED, FAILED), paiement (NOTPAID, PARTPAID, PAID), export (NOTEXPORTED, READY, EXPORTED, FAILED) et expédition. Les retours et remboursements vivent le plus souvent dans l’OMS/ERP.
GA4 déclenche purchase à l’étape « paiement confirmé » de ce cycle simplifié, plus précisément au chargement de la page de confirmation. Mais l’enregistrement SFCC persiste pendant tout le cycle. Comparer le « revenu » GA4 (un instantané à la confirmation) au revenu net SFCC (qui intègre tous les événements ultérieurs) crée donc un écart structurel qui n’est pas un bug.
Ce que SFCC sait et que GA4 ignore : le statut final de la commande, le montant facturé exact (taxes, remises serveur, points de fidélité), le système qui a créé la commande, toutes les commandes quel que soit le canal, et l’identité au niveau du compte.
Ce que SFCC ignore : presque tout du marketing. Il peut stocker un source code (si vos campagnes en utilisent) et un type de canal (boutique, centre d’appels, marketplace…), mais pas le parcours au niveau du clic : clic Google Ads, recherche organique, e-mail. Cette couche d’attribution vit dans GA4 et les plateformes publicitaires. Il faut donc les deux systèmes dans votre modèle de réconciliation, aucun ne suffit seul.
Les cinq causes structurelles d’écart
1. Décalage temporel
GA4 se déclenche au chargement de la page de confirmation. SFCC enregistre la commande quand le paiement est confirmé côté back-end, après validation de la passerelle de paiement, contrôle antifraude et réservation du stock. Le délai se compte en général en secondes, mais peut atteindre des heures, voire des jours (revue manuelle antifraude, rappel tardif du prestataire de paiement). Sur une fenêtre de reporting en milieu de mois, c’est négligeable. Aux frontières de période, c’est un vrai écart :
Illustrative example:
User completes checkout at 23:59:57 on December 31st
→ GA4 fires purchase event: December (revenue lands in Dec)
→ Payment gateway confirms at 00:00:04 January 1st
→ SFCC records order: January (revenue lands in Jan)
Result: This order appears in GA4 December report but not SFCC December report.
Vérifiez aussi les fuseaux horaires : l’event_date de GA4 suit le fuseau de la propriété (alors que event_timestamp est en UTC), et les exports SFCC sont souvent en UTC. Cela suffit à déplacer des commandes d’un jour ou d’un mois à l’autre.
Diagnostic : comparez les commandes par jour entre GA4 et SFCC sur 30 jours. Un décalage glissant de 1 à 2 jours signale un problème de timing, qui se résorbe à l’agrégation mensuelle.
2. Définitions du revenu différentes
C’est la cause la plus souvent sous-diagnostiquée. GA4 reçoit la valeur que pousse votre dataLayer. SFCC enregistre sa propre définition financière de la valeur de commande. Sans alignement explicite, elles divergent sur chaque commande concernée :
| Composante | Comportement GA4 typique | Comportement SFCC typique | Impact si non aligné |
|---|---|---|---|
| Frais de livraison | Spec GA4 : exclus de value (envoyés dans shipping) | Inclus dans totalGrossPrice | Comparer value à adjustedMerchandizeTotalPrice, pas au total de commande |
| TVA / taxes | Spec GA4 : exclues de value (envoyées dans tax) | SFCC stocke brut et net | S’aligner sur la valeur nette des marchandises |
| Remises promotionnelles | Souvent la valeur avant remise dans le dataLayer | Le montant après remise est l’order_value | GA4 surestime le revenu de chaque commande remisée |
| Points de fidélité | Rarement déduits | Réduisent le montant facturé | GA4 surestime pour les clients du programme |
| Devise | Dépend du paramètre de devise du dataLayer | Stockée en devise d’origine + convertie | Écarts de change systématiques sur les sites multi-devises |
Correctif : avant tout travail technique, décidez ce que « revenu » veut dire : brut ou net ? Avec ou sans livraison ? Avec ou sans TVA ? Puis auditez votre dataLayer face à adjustedMerchandizeTotalPrice sur un échantillon de 50 commandes. La spec purchase de GA4 dit « Don’t include shipping or tax » dans value, et SFCC expose ces montants dans des totaux séparés. Si les valeurs diffèrent d’un pourcentage fixe, vous avez un problème de définition à corriger à la source.
3. Commandes annulées et remboursées
GA4 enregistre le revenu quand purchase se déclenche. SFCC suit la commande sur tout son cycle. Si un client demande un remboursement deux semaines plus tard, SFCC ajuste le revenu net ; GA4 non, sauf si vous envoyez explicitement un événement refund. Il faut pour cela un déclencheur côté serveur (Measurement Protocol) ou un tag GTM activé quand votre OMS traite le remboursement. La plupart des implémentations n’en ont pas : GA4 surestime alors durablement le revenu des catégories à fort taux de retour. Pour chiffrer cet écart dans BigQuery :
-- Quantify the refund gap in BigQuery
SELECT
DATE_TRUNC(created_at, MONTH) AS month,
SUM(order_value) AS sfcc_gross_revenue,
SUM(order_value - COALESCE(refund_amount, 0)) AS sfcc_net_revenue,
SUM(COALESCE(refund_amount, 0)) AS total_refunds,
ROUND(
SUM(COALESCE(refund_amount, 0))
/ NULLIF(SUM(order_value), 0) * 100, 1
) AS refund_rate_pct
FROM `project.sfcc.orders`
WHERE status NOT IN ('cancelled', 'fraud', 'test')
AND DATE(created_at) >= DATE_SUB(CURRENT_DATE(), INTERVAL 6 MONTH)
GROUP BY 1
ORDER BY 1
-- Compare refund_rate_pct with the GA4–SFCC revenue gap: if they are close,
-- missing refund events explain most of it.
4. Perte de cookies : ITP et Consent Mode
L’Intelligent Tracking Prevention (ITP) de Safari supprime les cookies posés par JavaScript après 7 jours sans visite, ou après 24 heures quand le visiteur arrive par un lien décoré venant d’un site classé comme tracker. Sans consentement (mode basique), l’achat n’atteint jamais GA4. Avec l’ITP, l’achat atteint GA4 mais souvent comme nouveau visiteur : la source d’acquisition d’origine est perdue.
Si votre cycle de réflexion moyen est inférieur à 7 jours, l’impact est minime. Pour des produits étudiés 1 à 4 semaines (voyage, électronique, luxe), c’est un problème d’attribution structurel et permanent. Regardez la part de Safari dans votre GA4 (Technologie > Navigateur).
5. Commandes passées hors navigateur
Une part importante et croissante des commandes est passée dans des environnements où les tags GA4 ne se déclenchent pas, ou mal :
- WebViews in-app (Instagram, TikTok, Gmail, WhatsApp) : le lien s’ouvre dans le navigateur interne de l’app, qui isole souvent les cookies du navigateur principal. Les sessions se scindent et l’attribution se perd (mesurer dans une WebView demande un pont côté app). Ces commandes arrivent dans SFCC sans événement GA4 correspondant. Avec de la publicité sociale active, ce trafic peut peser lourd dans les sessions mobiles ; mesurez-le via le user agent.
- Architectures headless : quand la vitrine est découplée du rendu serveur, l’injection des tags dépend du framework front. Rater le déclencheur de la page de confirmation est courant et souvent découvert tard.
- Apps mobiles natives : sans tracking Firebase/GA4 explicitement implémenté dans les apps iOS et Android, chaque achat via l’app arrive dans SFCC sans événement GA4. C’est souvent un trou d’implémentation hérité : le web était tracké, pas l’app.
- Centre d’appels et back-office : les agents créent les commandes directement dans SFCC. Ni navigateur, ni session, ni tag. C’est normal : ces commandes sont à classer en hors ligne, pas à traiter comme des échecs de tracking.
L’architecture complète du flux de données
Ce que l’écart vous dit vraiment
Une fois les causes structurelles comprises, l’écart cesse d’être un problème à résoudre et devient un instrument de mesure.
- Taux d’invisibilité du revenu. La part du revenu SFCC sans session GA4 correspondante dit exactement quelle part de votre activité est invisible pour votre outil d’analytics. À 30 %, chaque calcul de ROAS, chaque attribution de canal, chaque résultat de test A/B repose sur 70 % du revenu réel, et les 30 % invisibles se répartissent entre les canaux de façon inconnue.
- Sous-valorisation des canaux. Si une part notable du revenu vient de commandes passées en centre d’appels sous l’influence de la publicité, et n’apparaît jamais dans GA4, le ROAS média payant est sous-estimé. Vous risquez de couper des budgets sur des canaux qui performent.
- Score de qualité d’implémentation. Une fois retirées les causes hors ligne structurelles (boutiques, API B2B, centre d’appels), les commandes web SFCC sans correspondance GA4 sont de vrais échecs de tracking : tags manquants, Consent Mode mal configuré, WebViews, bloqueurs de pub. Suivre cet indicateur dans le temps donne un score de santé continu : un pic un jour précis signale une mise en production qui a cassé quelque chose, une hausse graduelle une dégradation systémique.
Quatre décisions métier avant tout travail technique
Avant d’écrire une requête SQL, répondez à ces quatre questions. Ce sont des décisions métier, pas techniques.
- Qu’est-ce que le revenu ? Valeur brute confirmée (livraison et TVA incluses, avant remboursements), montant net facturé (hors TVA, hors livraison, après remises) ou net encaissé (après remboursements et rétrofacturations) ? Il n’y a pas de réponse universelle : la finance veut le net encaissé, le marketing le brut facturé. Le dashboard doit exposer les trois, avec la définition affichée sur chaque métrique.
- Quelle est la clé de jointure ? Le
transaction_idde GA4 correspond-il à l’order_idde SFCC ? Toujours ? Après normalisation ? Jamais ? Le prochain article de la série le traite en détail : c’est le point de défaillance le plus fréquent de tout projet de réconciliation. - Quelles commandes sont dans le périmètre ? Web seul ? Tous canaux ? Avec ou sans B2B, avec ou sans annulées ? Le périmètre fixe le dénominateur de chaque indicateur d’écart : 20 % d’écart sur le web seul ne veut pas dire la même chose que 20 % tous canaux confondus.
- Comment traiter les remboursements ? Comparer le brut GA4 (qui inclut des remboursements futurs jamais renvoyés) au net SFCC (qui déduit les remboursements au fil de l’eau) revient à comparer des choses incompatibles. Soit vous alignez GA4 en implémentant les événements
refund, soit vous comparez sur le brut confirmé SFCC, qui correspond à l’instantané de GA4.
Diagnostic pratique : vos premières requêtes BigQuery
Quand l’export GA4 et les données SFCC sont dans BigQuery, lancez cette séquence avant de construire quoi que ce soit dans Looker. La première compare les volumes quotidiens (commandes web seulement) :
SELECT
date,
SUM(ga4_orders) AS ga4_orders,
SUM(sfcc_orders) AS sfcc_orders,
SUM(sfcc_orders) - SUM(ga4_orders) AS delta,
ROUND(
(SUM(sfcc_orders) - SUM(ga4_orders))
/ NULLIF(SUM(sfcc_orders), 0) * 100, 1
) AS gap_pct
FROM (
-- GA4 purchases
SELECT
PARSE_DATE('%Y%m%d', event_date) AS date, -- event_date is a YYYYMMDD string
COUNT(DISTINCT ecommerce.transaction_id) AS ga4_orders,
0 AS sfcc_orders
FROM `project.analytics_XXXX.events_*`
WHERE event_name = 'purchase'
AND _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
GROUP BY 1
UNION ALL
-- SFCC web orders only
SELECT
DATE(created_at),
0,
COUNT(DISTINCT order_id)
FROM `project.sfcc.orders`
WHERE channel IN ('web', 'storefront')
AND status NOT IN ('cancelled', 'fraud', 'test')
AND DATE(created_at) >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY 1
)
GROUP BY date
ORDER BY date;
-- Interpretation:
-- Consistent gap: structural issue (tracking quality, consent mode)
-- Spiky gap: event-based issue (deployment broke tag on specific day)
-- Rolling 1-2 day offset: timing mismatch between systems
La deuxième requête décompose l’écart de revenu par mois (commandes SFCC, revenu brut et net, commandes GA4 appariées, deltas brut et net, taux d’appariement et part de commandes sans correspondance), en joignant sur LOWER(TRIM(order_id)) = LOWER(TRIM(transaction_id)). La troisième fait la même chose par catégorie de produit. Une catégorie au taux d’appariement systématiquement bas peut avoir un tag cassé sur ses pages produit, se vendre surtout en hors ligne ou en B2B, ou suivre un tunnel de paiement dont le déclencheur manque.
La suite de la série
- Partie 3 : le problème du transaction ID, tous les modes de défaillance et la séquence SQL complète de diagnostic.
- Partie 4 : la gouvernance des métriques LookML, définir revenu et écart une fois et les appliquer partout.
- Partie 5 : trafic sombre et commandes orphelines, classer les commandes non appariées et construire un dashboard Looker complet de réconciliation.
Sources
- Documentation des événements e-commerce GA4
- GA4 Measurement Protocol (événements refund côté serveur)
- Apple ITP : webkit.org/tracking-prevention
- Guide Google Consent Mode v2
- Référence du schéma d’export GA4 BigQuery
- Salesforce B2C Commerce Script API : dw.order.Order
- Simo Ahava : ITP et web analytics
- Analytics dans une WebView
Pour aller plus loin, lisez la version complète de l’article (en anglais) : Why GA4, SFCC and Offline Orders Will Never Match — And What That Gap Actually Tells You.
Besoin d’aide sur ce sujet ? Consultant GA4 et audit analytics.