> ## 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 en un píxel personalizado de Shopify (gtag.js)

### Resumen

Algunas tiendas cargan Google Analytics 4 (`G-`) o Google Ads (`AW-`) directamente con `gtag.js` desde un **Shopify custom pixel** (**Settings > Customer events**). Este artículo te da código de píxel listo para pegar que aplica Google Consent Mode Advanced a esas propiedades, usando el consentimiento que tus visitantes dan en el banner de Pandectes.

Si tus propiedades de Google las carga un contenedor de Google Tag Manager, usa en su lugar [Google Consent Mode en un píxel personalizado de Shopify (Google Tag Manager)](https://help.pandectes.io/es/article/google-consent-mode-in-a-shopify-custom-pixel-google-tag-manager-4ov23k/).

Esto es una guía de implementación. Para información de base sobre los comandos `default` y `update` y sobre el modo Advanced, consulta [Consent Mode con gtag.js](https://developers.google.com/tag-platform/security/guides/consent?consentmode=advanced#gtag.js_2) de Google.

### Antes de empezar

#### Activa primero la integración de Google Consent Mode

Este píxel es una mitad de una configuración en dos partes. En Shopify Admin, ve a **Apps > Pandectes GDPR > Integrations**, haz clic en **Configure** en la tarjeta **Google Consent Mode** y activa la integración. Consulta [Usar la integración de Google Consent Mode](https://help.pandectes.io/es/article/usar-la-integracion-de-google-consent-mode-1xnngxj/). Google Consent Mode funciona en el plan **Plus** y superiores.

Un píxel personalizado de Shopify se ejecuta en un iframe aislado con su propio `window`, `dataLayer` y copia de `gtag.js`. Está separado de la página de la tienda, y el consentimiento configurado en uno es invisible para el otro. Por eso cada uno necesita su propia configuración:

| Dónde | Qué se ejecuta allí | Qué configura el modo de consentimiento |
| --- | --- | --- |
| Página de la tienda | Etiquetas de Google en `theme.liquid`, tu contenedor de GTM, etiquetas de otras apps | La integración de Pandectes **Google Consent Mode** |
| Sandbox del píxel | La `gtag.js` que cargas en el píxel y sus propiedades | El código de este artículo |

||| El píxel no sustituye a la integración. Nada en el píxel puede llegar a la página de la tienda, así que sin la integración tu tienda no tiene ningún modo de consentimiento.

#### ¿Usas Google Tag Manager?

Si tu tienda tiene un contenedor de GTM, comprueba si tus propiedades de Google ya se cargan ahí. Normalmente sí. Añadir un píxel que vuelve a cargar la misma propiedad de GA4 o Google Ads duplica tus visualizaciones de página, sesiones, compras y conversiones, y crea dos estados de consentimiento en competencia.

| Tu configuración | Sigue
| --- | --- |
| GA4 o Google Ads cargados por un contenedor de GTM | [Google Consent Mode en un píxel personalizado de Shopify (Google Tag Manager)](https://help.pandectes.io/es/article/google-consent-mode-in-a-shopify-custom-pixel-google-tag-manager-4ov23k/) |
| Sin GTM, propiedades cargadas con `gtag.js` en un píxel personalizado | Este artículo |

### Qué cubre este código

El código configura **el estado de consentimiento y nada más**. Envía a Google la política por defecto de la tienda, aplica la elección del visitante, la mantiene sincronizada con el banner y carga la etiqueta de Google para que las señales de consentimiento tengan adónde ir. No envía datos de analítica ni de conversiones por sí mismo.

||| **Pandectes solo proporciona la capa de consentimiento.** Qué eventos envías, qué datos llevan, cómo se asignan a tu propiedad de GA4 o a tus conversiones de Google Ads y si tus informes son precisos son responsabilidad tuya. Pandectes no da soporte ni verifica el seguimiento construido sobre este código.

#### Tus suscripciones a eventos van en el mismo píxel

Enviar los eventos de clientes de Shopify a Google es el verdadero propósito del píxel, y esas suscripciones deben estar en el **mismo píxel**, debajo del código de consentimiento. Los ejemplos de cada fragmento están comentados a propósito. Descoméntalos o sustitúyelos por los tuyos.

#### Cada píxel que cargue una propiedad de Google necesita su propia copia

No puedes poner el código de consentimiento en un píxel personalizado y tus eventos en otro. Cada píxel es un sandbox separado. Un píxel que carga GA4 o Google Ads sin el código de consentimiento dispara todo con los valores por defecto de Google, como si no existiera ningún modo de consentimiento, y Shopify no te avisa.

* Un píxel carga tus propiedades: una copia del código, con los eventos de ese píxel debajo.
* Tres píxeles cargan una propiedad de Google cada uno: tres copias, una en cada píxel.
* Si varios píxeles cargan la **misma** propiedad, corrige primero esa duplicación.

### Modo Advanced y permiso del píxel

Este código te ofrece **Google Consent Mode Advanced**: la etiqueta de Google se carga en cada visualización de página, envía pings sin cookies mientras el consentimiento está denegado y pasa a medición completa cuando se concede. Así es como Google puede modelar las conversiones de los visitantes que no dan su consentimiento.

Para eso, el píxel debe poder ejecutarse antes del consentimiento. En Shopify Admin, ve a **Settings > Customer events**, abre tu píxel personalizado y, en **Customer privacy**, configura **Permission** como **Not required**. Si lo configuras como **Required**, Shopify retiene el píxel hasta que el visitante acepta, así que la etiqueta de Google nunca se carga antes del consentimiento y no se envía ningún ping sin cookies.

Si quieres **Basic mode** (nada se carga hasta que se da el consentimiento), no necesitas este código. Configura **Permission** como **Required**, selecciona las categorías que necesites y deja que Shopify retenga el píxel.

### Cómo funciona el código

Shopify da al píxel el consentimiento del visitante en `init.customerPrivacy`, pero no indica si es el valor por defecto de la tienda o una elección que el visitante ya había hecho. Por eso el código siempre ejecuta estos pasos, en este orden:

1. `consent` `default`: la política de la tienda, escrita en el código.
2. `consent` `update`: lo que contenga `init.customerPrivacy`. Si solo era el valor por defecto, no cambia nada. Si era una elección guardada, se aplica antes del primer envío.
3. Carga `gtag.js` y configura tus propiedades de Google.
4. Escucha `visitorConsentCollected` y envía un `update` cada vez que el visitante cambie su elección.

En un píxel personalizado, `analytics`, `browser` y `init` están disponibles directamente, pero `customerPrivacy` se accede a través de `api`. Llamar a `customerPrivacy.subscribe(...)` sin `api.` genera un `ReferenceError`.

Los valores de consentimiento de Shopify se asignan a los tipos de consentimiento de Google así:

| Shopify | Google |
| --- | --- |
| `analyticsProcessingAllowed` | `analytics_storage` |
| `marketingAllowed` | `ad_storage`, `ad_user_data`, `ad_personalization` |
| `preferencesProcessingAllowed` | `functionality_storage`, `personalization_storage` |
| `saleOfDataAllowed` | No tiene equivalente en Consent Mode. Es una señal de venta o compartición en estados de EE. UU. (ver `restricted_data_processing`). |
| (siempre) | `security_storage` es siempre `granted` |

### ¿Qué fragmento necesito?

| La política de tu tienda | Usa |
| --- | --- |
| Misma política en todo el mundo: nada se ejecuta hasta que el visitante da su consentimiento | **Snippet 1** (un comando por defecto) |
| Consentimiento recogido solo en algunas regiones, permitido en el resto | **Snippet 2** (comandos por defecto según región) |

Ambos fragmentos admiten GA4, Google Ads o los dos.

1. En Shopify Admin, ve a **Settings > Customer events** y haz clic en **Add custom pixel**.
2. Dale un nombre al píxel y pega uno de los fragmentos.
3. Escribe tus IDs entre `START_MODIFY_FOR_YOUR_STORE` y `END_MODIFY_FOR_YOUR_STORE`.
4. Añade tus propias suscripciones a eventos al final.
5. Configura el permiso como se describe arriba, haz clic en **Save** y luego en **Connect**.

### Fragmento 1: un comando por defecto

Un comando `default` sin región, así que se aplica a todos los visitantes. Todo se deniega excepto `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,
//   });
// });
```

### Fragmento 2: comandos por defecto según región

Para tiendas que recogen el consentimiento solo donde la ley lo exige. Hay dos comandos `default`: uno global que concede todo, y otro para las regiones indicadas que deniega todo excepto `security_storage`. Cuando ambos coinciden con un visitante, gana la región más específica.

El resto es idéntico al Fragmento 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. */
```

### Comprueba que funciona

#### Tag Assistant no puede ver el píxel

Google Tag Assistant solo inspecciona la página de la tienda. Las `gtag`, `dataLayer` y propiedades del píxel viven en un iframe aislado al que no llega, así que no muestra nada (o solo las etiquetas propias de la tienda) aunque el píxel funcione correctamente. Esto es lo esperado y no es un error.

#### Revisa el dataLayer en el frame del píxel

1. Abre las DevTools de Chrome y ve a la pestaña **Console**.
2. En la barra de herramientas de la consola, abre el desplegable de frames (normalmente muestra `top`) y selecciona el frame del sandbox del píxel (su nombre empieza por `web-pixel`). Shopify crea un frame por cada píxel personalizado, así que elige el correcto si tienes varios.
3. Ejecuta las comprobaciones de abajo.

En el frame `top`, `dataLayer` es el dataLayer de la tienda, que no te dice nada sobre el píxel.

* **¿Van primero los valores por defecto?** En `dataLayer`, las entradas `consent` deben ir antes que las entradas `config`.
* **¿Llega el consentimiento a la etiqueta?** Configura `DEBUG = true`, descomenta un evento para que se envíe algo y abre **DebugView** de GA4. Con `analytics_storage` denegado verás un ping sin cookies en lugar de un envío normal.
* **¿Se carga el píxel antes del consentimiento?** Debe hacerlo para el modo Advanced. Si la etiqueta de Google solo aparece después de que el visitante acepte, el permiso del píxel está en **Required** y estás en modo Basic.
* **¿Funciona la lógica por regiones?** Prueba con una VPN dentro y fuera de un país de la lista.
* **¿Se dispara la suscripción?** Añade un `console.log` temporal dentro del callback `visitorConsentCollected` y cambia tu elección en el banner.

### Errores habituales

| Error | Qué ocurre |
| --- | --- |
| Código de consentimiento en un píxel, eventos en otro | El píxel de eventos no tiene `gtag` ni estado de consentimiento. Sus eventos no llegan a ningún sitio. |
| Varios píxeles cargan una propiedad de Google, pero solo uno tiene el código de consentimiento | Los demás se disparan sin modo de consentimiento y sin aviso. |
| Píxel **Permission** configurado en **Required** | La etiqueta nunca se carga antes del consentimiento: sin pings sin cookies ni modelado. Eso es modo Basic. |
| Evento de `conversion` de Ads enviado mientras la compra de GA4 también se importa como conversión | Cada pedido se cuenta dos veces en Google Ads. |
| `customerPrivacy.subscribe(...)` sin `api.` | `ReferenceError`. El consentimiento nunca se actualiza tras el banner. |
| Omitir `update` porque `init.customerPrivacy` parece el valor por defecto | Los visitantes que vuelven con consentimiento guardado reciben etiquetas denegadas en su primera visualización de página. |
| `default` enviado después de `config` | Consent Mode no se aplica a los primeros envíos. |
| La integración de Google Consent Mode no está activada en Pandectes | Solo las propiedades del píxel reciben el modo de consentimiento. Las etiquetas de Google de la tienda se ejecutan sin él. |
| Píxel creado mientras GTM ya carga la misma propiedad | Visualizaciones de página, sesiones, compras y conversiones duplicadas, además de dos estados de consentimiento. |
| Probar con Tag Assistant o leer `dataLayer` en el frame `top` | Ninguno mira dentro del sandbox del píxel. |
| `security_storage` denegado | Rompe las señales de prevención de fraude sin ningún beneficio de cumplimiento. |
| `RESTRICTED_REGIONS` no coincide con las regiones del banner | El valor por defecto y la actualización no coinciden. Gana la actualización y se ignora la política por regiones. |

### Ajustes que este artículo no cubre

Los fragmentos no incluyen los ajustes de cuenta de Google, así que se aplican sus valores por defecto:

| Ajuste | Qué controla |
| --- | --- |
| `url_passthrough` | Si los ID de clic de anuncios, como GCLID, se pasan en la URL entre páginas cuando no se pueden establecer cookies |
| `ads_data_redaction` | Si los ID de clic de anuncios se eliminan de los pings sin cookies mientras `ad_storage` está denegado |
| `allow_enhanced_conversions` | Si se pueden enviar datos de cliente hasheados con las conversiones |
| `restricted_data_processing` | La restricción de venta o compartición en estados de EE. UU., relacionada con `saleOfDataAllowed` de Shopify |
| `wait_for_update` | Cuánto tiempo esperan las etiquetas una actualización de consentimiento antes de dispararse |

Pandectes no puede asesorar sobre estos ajustes. Los valores correctos dependen de tus cuentas de Google, del modelado en el que confíes y de la situación legal de tu negocio. Coméntalo con quien gestione tu configuración de Google o con tu asesor legal si hay una cuestión de privacidad.

Si decides configurar alguno, añádelo como una llamada `gtag("set", ...)` **antes** de los comandos `consent` `default`:

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

`wait_for_update` es la excepción: va dentro de un comando `default`. Estos fragmentos no lo necesitan, porque `init.customerPrivacy` está disponible de inmediato y `update` se envía justo después de los valores por defecto.