Blog
Événements e-commerce sans plateforme de boutique : faire remonter un tunnel de paiement sur mesure dans GA4
Publié · Mis à jour · 6 min de lecture
Une plateforme de boutique vous donne au moins un plugin avec lequel vous disputer. Un tunnel de paiement que vous avez construit vous-même — une application React, un parcours de réservation, une inscription SaaS avec Stripe derrière — ne vous donne rien : aucun événement ne se déclenche tant que votre propre code ne le pousse pas. La bonne nouvelle, c’est que le contrat est court : une poignée de noms, une forme d’objet, et quelques règles sur la place de chaque push. Le voici en entier, y compris la partie que la plupart des guides laissent de côté : les achats qui ont lieu quand personne n’est sur votre page.
Le contrat : un nom et un objet ecommerce
Tout ici parvient à Analytics de la même façon. Votre page pousse un événement dans window.dataLayer sous son nom GA4, portant un objet ecommerce à la forme GA4. Un déclencheur Tag Manager reconnaît le nom, et une balise d’événement GA4 lit l’objet et le transmet à votre conteneur serveur. Chaque conteneur SignalHost a déjà ces déclencheurs et ces balises dans son conteneur web, nommés avec le préfixe SH — pour les événements de boutique ci-dessous, et pour les inscriptions, les essais et les prospects. Reste le push.
Deux règles évitent la plupart des erreurs. Videz d’abord l’objet ecommerce précédent, sinon un view_item d’il y a trois pages voyage en passager clandestin dans l’événement suivant. Et sur chaque événement qui porte de l’argent, envoyez value sous forme de nombre et currency sous forme de code ISO : une value en chaîne de caractères ou une currency absente est acceptée sans broncher et discrètement exclue des revenus.
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: 'begin_checkout',
ecommerce: {
currency: 'EUR',
value: 49.00,
items: [
{ item_id: 'plan-pro', item_name: 'Pro', price: 49.00, quantity: 1 }
]
}
});Quel événement, et où il a sa place
Un parcours sur mesure a les mêmes moments qu’une plateforme de boutique. Il vous faut seulement les trouver dans votre propre code.
- view_item_list — une page ou un composant qui affiche plusieurs produits ou formules. Une fois quand la liste s’affiche, pas à chaque défilement.
- view_item — la page d’un seul produit ou d’une seule formule.
- add_to_cart et remove_from_cart — dans le gestionnaire du bouton, une fois que votre panier a réellement changé.
- view_cart — quand le panier s’affiche : sa page, ou l’ouverture du panier latéral.
- begin_checkout — le moment où le visiteur entre dans le paiement : le premier rendu de la page de paiement, ou le clic qui ouvre une fenêtre de paiement.
- add_shipping_info — quand un mode de livraison est choisi. Laissez-le de côté si vous n’expédiez rien.
- add_payment_info — quand les informations de paiement sont acceptées, pas quand le formulaire apparaît.
- purchase — une fois, quand la commande est confirmée. L’essentiel de cet article porte sur la façon de réussir celui-ci.
- sign_up et start_trial — pour une activité par abonnement, les conversions qui précèdent tout échange d’argent. SignalHost construit des balises pour les deux, à côté de celles des événements de boutique, et aussi pour les prospects : generate_lead, contact, schedule, submit_application et subscribe.
Poussez depuis le gestionnaire qui a provoqué le changement, pas depuis un rendu. Une application monopage refait ses rendus librement, et React exécute les effets deux fois en développement : un push placé dans un effet est doublé dans le build de développement et se répète à chaque nouveau montage d’un composant.
L’achat sur votre page de confirmation : lire la commande depuis votre serveur
Quand le visiteur revient sur l’une de vos pages après avoir payé, c’est sur cette page que purchase a sa place — à deux conditions. Prenez la commande dans votre propre backend, pas dans l’URL : les paramètres de requête peuvent être modifiés, se perdent dans les redirections et arrivent aussi lors des paiements échoués. Stripe, par exemple, renvoie à la même adresse que le paiement ait abouti ou non, et l’indique dans un paramètre qui lui est propre. Et poussez-le une seule fois : un rechargement, le bouton Retour ou une page de reçu enregistrée en favori y ramènent tous.
// On your confirmation page. The order comes from YOUR server, by an id the
// page already knows — never from price or status parameters in the URL.
const order = await fetch(`/api/orders/${orderId}`).then((r) => r.json());
const key = `purchase-sent-${order.id}`;
if (order.status === 'paid' && !localStorage.getItem(key)) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: order.id,
value: order.total,
tax: order.tax,
currency: order.currency,
items: order.items.map((i) => ({
item_id: i.sku, item_name: i.name, price: i.price, quantity: i.quantity
}))
}
});
localStorage.setItem(key, '1');
}transaction_id est un filet de sécurité, pas une stratégie. Analytics écarte un transaction_id répété au sein d’une même session, mais pas un rechargement de la page de reçu le lendemain matin.
Le paiement sur la page de quelqu’un d’autre : Stripe Checkout, PayPal, une redirection bancaire
Une page de paiement hébergée est le choix habituel pour un développement sur mesure, et elle a un écueil : le visiteur peut ne jamais revenir. Il paie, ferme l’onglet, et votre page de confirmation ne s’affiche jamais. Avec les cartes, cela représente quelques pour cent des achats. Avec les virements, les factures et les solutions de paiement différé, cela peut en être la majorité, parce que l’argent arrive des heures ou des jours plus tard.
Décidez donc, moyen de paiement par moyen de paiement, où se trouve la vérité. Si votre backend apprend le paiement par un webhook, le webhook est l’endroit fiable pour le signaler — voir la section suivante — et la page de confirmation ne doit pas pousser purchase en plus, sinon chaque paiement par carte est compté deux fois. Si le seul endroit où vous l’apprenez est la page de retour, signalez-le là et acceptez l’écart.
Les achats auxquels aucun navigateur n’assiste : envoyez-les depuis votre serveur
Les abonnements rendent cela inévitable. Un essai se convertit au bout de sept jours, un renouvellement a lieu chaque mois, une facture est réglée par virement la semaine prochaine. Chacun est un achat, et aucun n’a lieu devant un navigateur. La solution consiste à envoyer l’événement de serveur à serveur, depuis le webhook qui apprend le paiement, vers le même point de terminaison de tagging que vos pages — votre nom d’hôte SignalHost ou votre propre domaine — pour qu’il traverse votre conteneur serveur comme n’importe quel autre hit.
Pour les achats transmis côté serveur, enregistrez les choix réels de consentement du visiteur avec la commande. L’absence d’un cookie `_ga` ne prouve pas un refus. Cet exemple envoie des données uniquement à Analytics, avec une autorisation enregistrée et un ID client existant ; la publicité reste refusée. L’envoi aux plateformes publicitaires nécessite ses propres autorisations enregistrées.
// At checkout: save the visitor's actual banner choice with the order.
// Cookie presence alone is NOT consent. This example is Analytics-only.
const ga = req.cookies._ga;
order.gaClientId = ga ? ga.split('.').slice(-2).join('.') : null;
order.analyticsGranted = req.body.consent?.analytics === true;
// Later, in the payment webhook: only send with recorded permission and id.
if (order.analyticsGranted === true && order.gaClientId) {
const params = new URLSearchParams({
v: '2',
tid: 'G-XXXXXXXXXX',
cid: order.gaClientId,
gcs: 'G101', // Analytics allowed; advertising stays denied.
en: 'purchase',
'ep.transaction_id': invoice.id,
'epn.value': '49.00',
cu: 'EUR',
pr1: 'idplan-pro~nmPro~pr49.00~qt1',
});
await fetch(`https://metrics.example.com/g/collect?${params}`, { method: 'POST' });
}Choisissez une seule source par événement et tenez-vous-y. C’est exactement ainsi que signalhost.io mesure ses propres ventes : le navigateur pousse sign_up, begin_checkout et add_payment_info ; notre serveur envoie start_trial une fois qu’un essai a réellement commencé, si bien qu’un onglet refermé ne peut pas le perdre ; et le webhook Stripe envoie purchase quand la première facture est payée. Aucun événement n’a deux sources. Le Measurement Protocol de Google fonctionnerait aussi, mais il va directement chez Google et contourne votre conteneur serveur, si bien qu’aucune de vos balises côté serveur ne voit jamais la vente.
Les achats tardifs sont l’une des quatre raisons pour lesquelles GA4 compte moins que votre liste de commandes : les trois autres, et celles que vous pouvez corriger.
Configurer votre bannière de cookies
Choisissez le mode adapté à la politique de votre organisation. Ne désactivez pas un blocage volontaire uniquement pour faire apparaître des événements.
**Basic :** bloquez l’extrait de suivi jusqu’au consentement correspondant. Aucun événement de suivi n’est attendu dans SignalHost avant ce consentement.
**Advanced :** les balises Google chargent avec des valeurs initiales `denied`. Google peut recevoir des mesures sans cookies avant le choix ou après un refus. Une mesure ne vaut pas consentement.
Avant le début du suivi, votre bannière doit définir les autorisations sur refusé. Après le choix du visiteur, mettez à jour uniquement les finalités qu’il a autorisées, mémorisez son choix et gérez son retrait.
[Guide de configuration de la bannière de cookies](/fr/blog/banniere-cookies-consent-mode-serveur)
Vérifier que tout fonctionne
- Ouvrez le conteneur dans SignalHost. La carte « Événements » liste ce qui est arrivé, par nom, avec la part qui portait un consentement. Un nom que vous avez poussé et que vous n’y voyez pas n’a jamais atteint votre serveur de tagging.
- Parcourez votre tunnel de paiement dans le mode Aperçu de Tag Manager. Il montre chaque push, la balise qui s’est déclenchée et l’objet ecommerce qui a été envoyé.
- Dans Analytics, DebugView affiche les événements à mesure qu’ils arrivent. Les rapports standard peuvent prendre une journée.
- Rechargez deux fois la page de confirmation et comptez les achats. Il ne doit toujours y en avoir qu’un.
Les échecs ici sont tous silencieux : un nom avec la mauvaise casse, value envoyé en chaîne de caractères, items imbriqué un niveau trop bas. Aucun ne produit d’erreur nulle part. Chacun produit un rapport avec un trou dedans.

