> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://help.pandectes.io/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# Google Consent Mode dans un pixel personnalisé Shopify (gtag.js)

### Vue d’ensemble

Certaines boutiques chargent Google Analytics 4 (`G-`) ou Google Ads (`AW-`) directement avec `gtag.js` depuis un **Shopify custom pixel** (**Settings > Customer events**). Cet article vous fournit un code de pixel prêt à coller, qui applique Google Consent Mode Advanced à ces propriétés en utilisant le consentement que vos visiteurs donnent sur la bannière Pandectes.

Si vos propriétés Google sont chargées par un conteneur Google Tag Manager, utilisez plutôt [Google Consent Mode dans un pixel personnalisé Shopify (Google Tag Manager)](https://help.pandectes.io/fr/article/google-consent-mode-in-a-shopify-custom-pixel-google-tag-manager-324pq3/).

Il s'agit d'un guide de mise en œuvre. Pour le contexte sur les commandes `default` et `update` et sur le mode Advanced, consultez [Consent Mode avec gtag.js](https://developers.google.com/tag-platform/security/guides/consent?consentmode=advanced#gtag.js_2) dans la documentation de Google.

### Avant de commencer

#### Activez d'abord l'intégration Google Consent Mode

Ce pixel est l'une des deux moitiés d'une configuration en deux parties. Dans Shopify Admin, allez dans **Apps > Pandectes GDPR > Integrations**, cliquez sur **Configure** sur la carte **Google Consent Mode** et activez l'intégration. Voir [Utiliser l'intégration Google Consent Mode](/en/article/using-google-consent-mode-integration-1xkfv6o/). Google Consent Mode fonctionne à partir du forfait **Plus**.

Un pixel personnalisé Shopify s'exécute dans une iframe isolée, avec ses propres `window`, `dataLayer` et une copie de `gtag.js`. Il est séparé de la page de la boutique, et le consentement défini dans l'un est invisible pour l'autre. Chacun a donc besoin de sa propre configuration :

| Emplacement | Ce qui s'y exécute | Ce qui définit le mode de consentement
| --- | --- | --- |
| Page de la boutique | Balises Google dans `theme.liquid`, votre conteneur GTM, balises d'autres applications | L'intégration Pandectes **Google Consent Mode**
| Environnement isolé du pixel | Le `gtag.js` que vous chargez dans le pixel et ses propriétés | Le code de cet article

||| Le pixel ne remplace pas l'intégration. Rien dans le pixel ne peut atteindre la page de la boutique : sans l'intégration, votre boutique n'a donc aucun mode de consentement.

#### Utilisez-vous Google Tag Manager ?

Si votre boutique a un conteneur GTM, vérifiez si vos propriétés Google y sont déjà chargées. C'est généralement le cas. Ajouter un pixel qui charge à nouveau la même propriété GA4 ou Google Ads duplique vos pages vues, sessions, achats et conversions, et crée deux états de consentement concurrents.

| Votre configuration | Suivez
| --- | --- |
| GA4 ou Google Ads chargés par un conteneur GTM | [Google Consent Mode dans un pixel personnalisé Shopify (Google Tag Manager)](https://help.pandectes.io/fr/article/google-consent-mode-in-a-shopify-custom-pixel-google-tag-manager-324pq3/) |
| Pas de GTM, propriétés chargées avec `gtag.js` dans un pixel personnalisé | Cet article

### Ce que couvre ce code

Le code définit **uniquement l'état du consentement**. Il envoie à Google la politique par défaut de la boutique, applique le choix du visiteur, le maintient synchronisé avec la bannière et charge la balise Google pour que les signaux de consentement aient une destination. Il n'envoie pas à lui seul de données d'analyse ou de conversion.

||| **Pandectes fournit uniquement la couche de consentement.** Les événements que vous envoyez, les données qu'ils contiennent, leur correspondance avec votre propriété GA4 ou vos conversions Google Ads, et l'exactitude de vos rapports relèvent de votre responsabilité. Pandectes ne prend pas en charge et ne vérifie pas le suivi construit au-dessus de ce code.

#### Vos abonnements aux événements vont dans le même pixel

Envoyer les événements clients de Shopify à Google est la véritable raison d'être du pixel, et ces abonnements doivent se trouver dans le **même pixel**, sous le code de consentement. Les exemples de chaque snippet sont volontairement commentés. Décommentez-les ou remplacez-les par les vôtres.

#### Chaque pixel qui charge une propriété Google a besoin de sa propre copie

Vous ne pouvez pas mettre le code de consentement dans un pixel personnalisé et vos événements dans un autre. Chaque pixel est un environnement isolé. Un pixel qui charge GA4 ou Google Ads sans le code de consentement envoie tout avec les valeurs par défaut de Google, comme si aucun mode de consentement n'existait, et Shopify ne vous en avertit pas.

* Un seul pixel charge vos propriétés : une copie du code, avec les événements de ce pixel en dessous.
* Trois pixels chargent chacun une propriété Google : trois copies, une dans chaque pixel.
* Si plusieurs pixels chargent la **même** propriété, corrigez d'abord ce doublon.

### Mode Advanced et autorisation du pixel

Ce code vous donne **Google Consent Mode Advanced** : la balise Google se charge à chaque affichage de page, envoie des pings sans cookie tant que le consentement est refusé, et passe à une mesure complète dès que le consentement est accordé. C'est ce qui permet à Google de modéliser les conversions des visiteurs qui ne consentent pas.

Pour cela, le pixel doit pouvoir s'exécuter avant le consentement. Dans Shopify Admin, allez dans **Settings > Customer events**, ouvrez votre pixel personnalisé et, sous **Customer privacy**, réglez **Permission** sur **Not required**. Si vous le réglez sur **Required**, Shopify retient le pixel jusqu'à l'acceptation du visiteur : la balise Google ne se charge donc jamais avant le consentement et aucun ping sans cookie n'est envoyé.

Si vous voulez **Basic mode** (rien ne se charge tant que le consentement n'est pas donné), vous n'avez pas besoin de ce code. Réglez **Permission** sur **Required**, sélectionnez les catégories dont vous avez besoin et laissez Shopify retenir le pixel.

### Fonctionnement du code

Shopify transmet au pixel le consentement du visiteur dans `init.customerPrivacy`, mais n'indique pas s'il s'agit de la valeur par défaut de la boutique ou d'un choix déjà fait par le visiteur. Le code exécute donc toujours ces étapes, dans cet ordre :

1. `consent` `default` : la politique de la boutique, inscrite dans le code.
2. `consent` `update` : ce que contient `init.customerPrivacy`. S'il ne s'agit que de la valeur par défaut, rien ne change. S'il s'agit d'un choix enregistré, il s'applique avant le premier envoi.
3. Charger `gtag.js` et configurer vos propriétés Google.
4. Écouter `visitorConsentCollected` et envoyer un `update` chaque fois que le visiteur modifie son choix.

Dans un pixel personnalisé, `analytics`, `browser` et `init` sont disponibles directement, mais `customerPrivacy` s'obtient via `api`. Appeler `customerPrivacy.subscribe(...)` sans `api.` génère une `ReferenceError`.

Les valeurs de consentement de Shopify correspondent aux types de consentement de Google comme suit :

| Shopify | Google |
| --- | --- |
| `analyticsProcessingAllowed` | `analytics_storage` |
| `marketingAllowed` | `ad_storage`, `ad_user_data`, `ad_personalization` |
| `preferencesProcessingAllowed` | `functionality_storage`, `personalization_storage` |
| `saleOfDataAllowed` | Pas d'équivalent dans Consent Mode. Il s'agit d'un signal de vente ou de partage pour les États américains (voir `restricted_data_processing`). |
| (toujours) | `security_storage` est toujours `granted` |

### Quel snippet me faut-il ?

| Politique de votre boutique | Utiliser |
| --- | --- |
| Même politique partout : rien ne s'exécute tant que le visiteur n'a pas consenti | **Snippet 1** (une seule commande par défaut) |
| Consentement recueilli seulement dans certaines régions, autorisé ailleurs | **Snippet 2** (commandes par défaut par région) |

Les deux snippets prennent en charge GA4, Google Ads, ou les deux.

1. Dans Shopify Admin, allez dans **Settings > Customer events** et cliquez sur **Add custom pixel**.
2. Donnez un nom au pixel et collez l'un des snippets.
3. Indiquez vos identifiants entre `START_MODIFY_FOR_YOUR_STORE` et `END_MODIFY_FOR_YOUR_STORE`.
4. Ajoutez vos propres abonnements aux événements en bas.
5. Réglez l'autorisation comme décrit plus haut, cliquez sur **Save**, puis sur **Connect**.

### Snippet 1 : une commande par défaut

Une seule commande `default` sans région, qui s'applique donc à tous les visiteurs. Tout est refusé, sauf `security_storage`.

```javascript
/* START_MODIFY_FOR_YOUR_STORE */
const GA4_ID = ""; // e.g. "G-XXXXXXXXXX", leave empty if not used
const AW_ID = ""; // e.g. "AW-XXXXXXXXXX", leave empty if not used
const DEBUG = false; // true sends GA4 hits to DebugView while testing
/* END_MODIFY_FOR_YOUR_STORE */

const GRANTED = "granted";
const DENIED = "denied";
const GOOGLE_URL = "https://www.googletagmanager.com/gtag/js?id=";
const DEVELOPER_ID = "dMTZkMj";

/* Shopify customerPrivacy -> Google consent state */
const toConsentState = (cp) => ({
  ad_storage: cp.marketingAllowed ? GRANTED : DENIED,
  ad_user_data: cp.marketingAllowed ? GRANTED : DENIED,
  ad_personalization: cp.marketingAllowed ? GRANTED : DENIED,
  analytics_storage: cp.analyticsProcessingAllowed ? GRANTED : DENIED,
  functionality_storage: cp.preferencesProcessingAllowed ? GRANTED : DENIED,
  personalization_storage: cp.preferencesProcessingAllowed ? GRANTED : DENIED,
  security_storage: GRANTED,
});

/* The pixel sandbox has its own dataLayer. */
window.dataLayer = window.dataLayer || [];
function gtag() {
  dataLayer.push(arguments);
}

/* Pandectes developer ID. Identifies the implementation to Google,
 * has no effect on your data. Leave it as it is. */
gtag("set", `developer_id.${DEVELOPER_ID}`, true);

/* STEP 1: store policy, before gtag.js loads. Applies to everyone. */
gtag("consent", "default", {
  ad_storage: DENIED,
  ad_user_data: DENIED,
  ad_personalization: DENIED,
  analytics_storage: DENIED,
  functionality_storage: DENIED,
  personalization_storage: DENIED,
  security_storage: GRANTED,
});

/* STEP 2: visitor state, always sent. If it is only the default,
 * nothing changes. If it is a saved choice, it lands before the first hit. */
gtag("consent", "update", toConsentState(init.customerPrivacy));

/* STEP 3: load the Google tag once and configure each property. */
const TAG_IDS = [GA4_ID, AW_ID].filter(Boolean);

if (TAG_IDS.length) {
  gtag("js", new Date());

  if (GA4_ID) {
    gtag("config", GA4_ID, {
      send_page_view: false,
      ...(DEBUG ? { debug_mode: true } : {}),
    });
  }

  if (AW_ID) {
    gtag("config", AW_ID);
  }

  const script = document.createElement("script");
  script.async = true;
  script.src = GOOGLE_URL + TAG_IDS[0];
  document.head.appendChild(script);
}

/* STEP 4: follow the visitor's choices. Note the api. prefix. */
api.customerPrivacy.subscribe("visitorConsentCollected", (event) => {
  gtag("consent", "update", toConsentState(event.customerPrivacy));
});

/* YOUR EVENT SUBSCRIPTIONS GO HERE, IN THIS SAME PIXEL.
 * The examples are commented out on purpose. What you send is yours
 * to own and verify. */

/* Page view: only if the storefront does not already send page_view
 * for the same property, or it is counted twice. */
// analytics.subscribe("page_viewed", (event) => {
//   gtag("event", "page_view", {
//     page_path: event.context.document.location.pathname,
//     page_title: event.context.document.title,
//     page_location: event.context.window.location.href,
//   });
// });

/* Purchase: if GA4 and Google Ads are linked, import the GA4 purchase
 * as a conversion in Google Ads and delete the conversion block below,
 * or every order is counted twice. */
// analytics.subscribe("checkout_completed", (event) => {
//   const checkout = event.data.checkout;
//   const items = checkout.lineItems.map((line) => ({
//     item_id: "shopify_" + line.variant.product.id + "_" + line.variant.id,
//     item_name: line.title,
//     item_variant: line.variant.title,
//     price: line.variant.price.amount,
//     quantity: line.quantity,
//   }));
//
//   gtag("event", "purchase", {
//     transaction_id: checkout.token,
//     currency: checkout.currencyCode,
//     value: checkout.totalPrice.amount,
//     tax: checkout.totalTax.amount,
//     shipping: checkout.shippingLine ? checkout.shippingLine.price.amount : 0,
//     items: items,
//   });
//
//   // Google Ads conversion, ONLY when GA4 and Google Ads are NOT linked.
//   // Use the conversion label from your Google Ads conversion action.
//   gtag("event", "conversion", {
//     send_to: "AW-XXXXXXXXXX/AbC-D_efGhIjKlMnOp",
//     transaction_id: checkout.token,
//     currency: checkout.currencyCode,
//     value: checkout.totalPrice.amount,
//   });
// });
```

### Snippet 2 : commandes par défaut par région

Pour les boutiques qui ne recueillent le consentement que là où la loi l'exige. Il y a deux commandes `default` : une globale qui accorde tout, et une pour les régions listées qui refuse tout sauf `security_storage`. Quand les deux correspondent à un visiteur, la région la plus précise l'emporte.

Le reste est identique au Snippet 1.

```javascript
/* START_MODIFY_FOR_YOUR_STORE */
const GA4_ID = ""; // e.g. "G-XXXXXXXXXX", leave empty if not used
const AW_ID = ""; // e.g. "AW-XXXXXXXXXX", leave empty if not used

/* ISO 3166-2 codes where the store collects consent: EEA + UK below.
 * Add US states ("US-CA", "US-CO", ...) if you treat them as opt-in.
 * This list MUST match the regions where your banner collects consent. */
const RESTRICTED_REGIONS = [
  "AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR",
  "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL",
  "PL", "PT", "RO", "SK", "SI", "ES", "SE", "GB", "LI", "NO", "IS",
];

const DEBUG = false; // true sends GA4 hits to DebugView while testing
/* END_MODIFY_FOR_YOUR_STORE */

const GRANTED = "granted";
const DENIED = "denied";
const GOOGLE_URL = "https://www.googletagmanager.com/gtag/js?id=";
const DEVELOPER_ID = "dMTZkMj";

/* Shopify customerPrivacy -> Google consent state */
const toConsentState = (cp) => ({
  ad_storage: cp.marketingAllowed ? GRANTED : DENIED,
  ad_user_data: cp.marketingAllowed ? GRANTED : DENIED,
  ad_personalization: cp.marketingAllowed ? GRANTED : DENIED,
  analytics_storage: cp.analyticsProcessingAllowed ? GRANTED : DENIED,
  functionality_storage: cp.preferencesProcessingAllowed ? GRANTED : DENIED,
  personalization_storage: cp.preferencesProcessingAllowed ? GRANTED : DENIED,
  security_storage: GRANTED,
});

/* The pixel sandbox has its own dataLayer. */
window.dataLayer = window.dataLayer || [];
function gtag() {
  dataLayer.push(arguments);
}

/* Pandectes developer ID. Identifies the implementation to Google,
 * has no effect on your data. Leave it as it is. */
gtag("set", `developer_id.${DEVELOPER_ID}`, true);

/* STEP 1a: policy for everyone outside the listed regions. */
gtag("consent", "default", {
  ad_storage: GRANTED,
  ad_user_data: GRANTED,
  ad_personalization: GRANTED,
  analytics_storage: GRANTED,
  functionality_storage: GRANTED,
  personalization_storage: GRANTED,
  security_storage: GRANTED,
});

/* STEP 1b: policy for the listed regions. Google resolves the region
 * from the IP address, so test it with a VPN. */
gtag("consent", "default", {
  ad_storage: DENIED,
  ad_user_data: DENIED,
  ad_personalization: DENIED,
  analytics_storage: DENIED,
  functionality_storage: DENIED,
  personalization_storage: DENIED,
  security_storage: GRANTED,
  region: RESTRICTED_REGIONS,
});

/* STEP 2: visitor state, always sent. Shopify has already applied your
 * regional settings to init.customerPrivacy, so this matches 1a or 1b. */
gtag("consent", "update", toConsentState(init.customerPrivacy));

/* STEP 3: load the Google tag once and configure each property. */
const TAG_IDS = [GA4_ID, AW_ID].filter(Boolean);

if (TAG_IDS.length) {
  gtag("js", new Date());

  if (GA4_ID) {
    gtag("config", GA4_ID, {
      send_page_view: false,
      ...(DEBUG ? { debug_mode: true } : {}),
    });
  }

  if (AW_ID) {
    gtag("config", AW_ID);
  }

  const script = document.createElement("script");
  script.async = true;
  script.src = GOOGLE_URL + TAG_IDS[0];
  document.head.appendChild(script);
}

/* STEP 4: follow the visitor's choices. Note the api. prefix. */
api.customerPrivacy.subscribe("visitorConsentCollected", (event) => {
  gtag("consent", "update", toConsentState(event.customerPrivacy));
});

/* YOUR EVENT SUBSCRIPTIONS GO HERE, IN THIS SAME PIXEL.
 * Use the same page view and purchase examples as in Snippet 1. */
```

### Vérifiez que cela fonctionne

#### Tag Assistant ne voit pas le pixel

Google Tag Assistant n'inspecte que la page de la boutique. Le `gtag`, le `dataLayer` et les propriétés du pixel se trouvent dans une iframe isolée qu'il n'atteint pas. Il ne signale donc rien (ou seulement les balises propres à la boutique), même lorsque le pixel fonctionne correctement. C'est normal et ce n'est pas une erreur.

#### Vérifier le dataLayer dans le cadre du pixel

1. Ouvrez les outils de développement de Chrome et allez dans l'onglet **Console**.
2. Dans la barre d'outils de la console, ouvrez la liste déroulante des cadres (elle affiche normalement `top`) et sélectionnez le cadre de l'environnement isolé du pixel (son nom commence par `web-pixel`). Shopify crée un cadre par pixel personnalisé. Choisissez donc le bon si vous en avez plusieurs.
3. Effectuez les vérifications ci-dessous.

Dans le cadre `top`, `dataLayer` est le dataLayer de la boutique, qui ne vous renseigne pas sur le pixel.

* **Les valeurs par défaut viennent-elles en premier ?** Dans `dataLayer`, les entrées `consent` doivent apparaître avant les entrées `config`.
* **Le consentement atteint-il la balise ?** Réglez `DEBUG = true`, décommentez un événement pour qu'il soit envoyé, puis ouvrez GA4 **DebugView**. Avec `analytics_storage` refusé, vous voyez un ping sans cookie au lieu d'un hit normal.
* **Le pixel se charge-t-il avant le consentement ?** Il le doit en mode Advanced. Si la balise Google n'apparaît qu'après l'acceptation du visiteur, l'autorisation du pixel est réglée sur **Required** et vous êtes en mode Basic.
* **La logique par région fonctionne-t-elle ?** Testez avec un VPN à l'intérieur et à l'extérieur d'un pays listé.
* **L'abonnement se déclenche-t-il ?** Ajoutez un `console.log` temporaire dans le callback `visitorConsentCollected` et modifiez votre choix sur la bannière.

### Erreurs courantes

| Erreur | Ce qui se passe |
| --- | --- |
| Code de consentement dans un pixel, événements dans un autre | Le pixel des événements n'a pas de `gtag` ni d'état de consentement. Ses événements n'aboutissent nulle part. |
| Plusieurs pixels chargent une propriété Google, un seul a le code de consentement | Les autres s'exécutent sans mode de consentement, sans avertissement. |
| Pixel **Permission** réglé sur **Required** | La balise ne se charge jamais avant le consentement : pas de pings sans cookie et pas de modélisation. C'est le mode Basic. |
| Événement Ads `conversion` envoyé alors que l'achat GA4 est aussi importé comme conversion | Chaque commande est comptée deux fois dans Google Ads. |
| `customerPrivacy.subscribe(...)` sans `api.` | `ReferenceError`. Le consentement ne se met jamais à jour après la bannière. |
| Omettre le `update` parce que `init.customerPrivacy` ressemble à la valeur par défaut | Les visiteurs de retour dont le consentement est enregistré reçoivent des balises refusées dès leur première page vue. |
| `default` envoyé après `config` | Consent Mode ne s'applique pas aux premiers hits. |
| Intégration Google Consent Mode non activée dans Pandectes | Seules les propriétés du pixel bénéficient du mode de consentement. Les balises Google de la boutique fonctionnent sans lui. |
| Pixel créé alors que GTM charge déjà la même propriété | Pages vues, sessions, achats et conversions en double, plus deux états de consentement. |
| Tester avec Tag Assistant ou lire `dataLayer` dans le cadre `top` | Aucun des deux ne regarde à l'intérieur de l'environnement isolé du pixel. |
| `security_storage` refusé | Affaiblit les signaux de prévention de la fraude sans aucun bénéfice de conformité. |
| `RESTRICTED_REGIONS` ne correspond pas aux régions de la bannière | La valeur par défaut et la mise à jour ne concordent pas. La mise à jour l'emporte et la politique par région est ignorée. |

### Réglages non couverts par cet article

Les snippets ne couvrent pas les réglages au niveau du compte Google : ce sont donc les valeurs par défaut de Google qui s'appliquent :

| Réglage | Ce qu'il contrôle
| --- | --- |
| `url_passthrough` | Si les identifiants de clic publicitaire, comme le GCLID, sont transmis dans l'URL d'une page à l'autre lorsque les cookies ne peuvent pas être définis |
| `ads_data_redaction` | Si les identifiants de clic publicitaire sont supprimés des pings sans cookie lorsque `ad_storage` est refusé |
| `allow_enhanced_conversions` | Si des données clients hachées peuvent être envoyées avec les conversions |
| `restricted_data_processing` | La restriction de vente ou de partage des États américains, liée au `saleOfDataAllowed` de Shopify |
| `wait_for_update` | Combien de temps les balises attendent une mise à jour du consentement avant de se déclencher |

Pandectes ne peut pas vous conseiller sur ces points. Les bonnes valeurs dépendent de vos comptes Google, de la modélisation sur laquelle vous vous appuyez et de la position juridique de votre entreprise. Discutez-en avec la personne qui gère votre configuration Google, ou avec votre conseiller juridique en cas de question de confidentialité.

Si vous décidez d'en définir un, ajoutez-le comme un appel `gtag("set", ...)` **avant** les commandes `consent` `default` :

```javascript
// gtag("set", "url_passthrough", true);
// gtag("set", "ads_data_redaction", true);
```

`wait_for_update` fait exception : il va dans une commande `default`. Ces snippets n'en ont pas besoin, car `init.customerPrivacy` est disponible immédiatement et l'envoi `update` part juste après les valeurs par défaut.