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).
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 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. 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 | La integración de Pandectes Google Consent Mode |
Sandbox del píxel | La | El código de este artículo |
¿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.
GA4 o Google Ads cargados por un contenedor de GTM | Google Consent Mode en un píxel personalizado de Shopify (Google Tag Manager) |
|---|---|
Sin GTM, propiedades cargadas con | 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.
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:
consentdefault: la política de la tienda, escrita en el código.consentupdate: lo que contengainit.customerPrivacy. Si solo era el valor por defecto, no cambia nada. Si era una elección guardada, se aplica antes del primer envío.- Carga
gtag.jsy configura tus propiedades de Google. - Escucha
visitorConsentCollectedy envía unupdatecada 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 | |
|---|---|
|
|
|
|
|
|
| No tiene equivalente en Consent Mode. Es una señal de venta o compartición en estados de EE. UU. (ver |
(siempre) |
|
¿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.
- En Shopify Admin, ve a Settings > Customer events y haz clic en Add custom pixel.
- Dale un nombre al píxel y pega uno de los fragmentos.
- Escribe tus IDs entre
START_MODIFY_FOR_YOUR_STOREyEND_MODIFY_FOR_YOUR_STORE. - Añade tus propias suscripciones a eventos al final.
- 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.
/* 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.
/* 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
- Abre las DevTools de Chrome y ve a la pestaña Console.
- 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 porweb-pixel). Shopify crea un frame por cada píxel personalizado, así que elige el correcto si tienes varios. - 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 entradasconsentdeben ir antes que las entradasconfig. - ¿Llega el consentimiento a la etiqueta? Configura
DEBUG = true, descomenta un evento para que se envíe algo y abre DebugView de GA4. Conanalytics_storagedenegado 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.logtemporal dentro del callbackvisitorConsentCollectedy 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 |
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 | Cada pedido se cuenta dos veces en Google Ads. |
|
|
Omitir | Los visitantes que vuelven con consentimiento guardado reciben etiquetas denegadas en su primera visualización de página. |
| 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 | Ninguno mira dentro del sandbox del píxel. |
| Rompe las señales de prevención de fraude sin ningún beneficio de cumplimiento. |
| 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 |
|---|---|
| Si los ID de clic de anuncios, como GCLID, se pasan en la URL entre páginas cuando no se pueden establecer cookies |
| Si los ID de clic de anuncios se eliminan de los pings sin cookies mientras |
| Si se pueden enviar datos de cliente hasheados con las conversiones |
| La restricción de venta o compartición en estados de EE. UU., relacionada con |
| 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:
// 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.
Actualizado el: 30/10/2026
¡Gracias!
