Last active
April 3, 2026 21:05
-
-
Save ceaksan/b30eade05d83b3e44a7e62924ac8ac2e to your computer and use it in GitHub Desktop.
Shopify Market Bridge: Consent-gated market cookie writer for Custom Pixels
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Shopify Market Bridge - Consent-Gated Market Cookie Writer | |
| * | |
| * Writes Shopify Markets localization data (country, language, currency, | |
| * market handle) to a cookie via storageHelper. This data is not available | |
| * in the pixel sandbox init event, so the cookie acts as a bridge between | |
| * the storefront (Theme App Extension) and Custom Pixel. | |
| * | |
| * Requires: | |
| * 1. storageHelper (see companion gist) | |
| * 2. Liquid snippet in theme (see below) | |
| * | |
| * Liquid snippet (Theme App Extension / App Embed Block): | |
| * | |
| * <script type="application/json" id="shopify-market-data"> | |
| * { | |
| * "market": { | |
| * "id": "{{ localization.market.id }}", | |
| * "handle": "{{ localization.market.handle }}" | |
| * }, | |
| * "country": "{{ localization.country.iso_code }}", | |
| * "language": "{{ localization.language.iso_code }}", | |
| * "locale": "{{ request.locale }}", | |
| * "currency": "{{ cart.currency.iso_code }}", | |
| * "shop": "{{ shop.permanent_domain }}" | |
| * } | |
| * </script> | |
| * | |
| * Note: localization.market cannot be serialized with | json filter | |
| * ("json not allowed for this object"). Fields must be listed individually. | |
| * | |
| * Pixel-side reading (Custom Pixel, lax sandbox): | |
| * | |
| * var consentGranted = init.customerPrivacy.analyticsProcessingAllowed; | |
| * | |
| * analytics.subscribe("page_viewed", async function (event) { | |
| * if (!consentGranted) return; | |
| * var raw = await browser.cookie.get("_shopify_m"); | |
| * if (!raw) return; | |
| * var market = JSON.parse(decodeURIComponent(raw)); | |
| * // market.country, market.currency, market.language, market.market.handle | |
| * }); | |
| * | |
| * Note: visitorConsentCollected is a storefront-only DOM event. | |
| * It is NOT available in the pixel sandbox. Pixel reads consent | |
| * from init.customerPrivacy which is fixed for the page lifetime. | |
| * | |
| * @see https://ceaksan.com/tr/shopify-markets-cookie-bridge-consent-aware-veri-koprusu | |
| * @license MIT | |
| */ | |
| (function () { | |
| var MARKET_KEY = "_shopify_m"; | |
| // --- Market Data --- | |
| function getMarketData() { | |
| var el = document.getElementById("shopify-market-data"); | |
| if (el) { | |
| try { | |
| return JSON.parse(el.textContent); | |
| } catch (e) { | |
| console.warn("[marketBridge] parse error:", e); | |
| } | |
| } | |
| // Fallback: window.Shopify global (less reliable than Liquid) | |
| if (typeof Shopify !== "undefined") { | |
| return { | |
| market: null, | |
| country: Shopify.country || null, | |
| language: Shopify.locale || null, | |
| locale: Shopify.locale || null, | |
| currency: Shopify.currency ? Shopify.currency.active : null, | |
| shop: Shopify.shop || null, | |
| }; | |
| } | |
| return null; | |
| } | |
| // --- Consent-Gated Writer --- | |
| function writeMarketData() { | |
| var data = getMarketData(); | |
| if (!data) return; | |
| if (!storageHelper.hasChanged(MARKET_KEY, data)) return; | |
| storageHelper.set(MARKET_KEY, data, { maxAge: 2592000 }); | |
| } | |
| function deleteMarketData() { | |
| storageHelper.remove(MARKET_KEY); | |
| } | |
| // --- Consent Handler --- | |
| var listenerAttached = false; | |
| function onConsentChange() { | |
| if ( | |
| typeof Shopify !== "undefined" && | |
| typeof Shopify.customerPrivacy !== "undefined" && | |
| Shopify.customerPrivacy.analyticsProcessingAllowed() | |
| ) { | |
| writeMarketData(); | |
| } else { | |
| deleteMarketData(); | |
| } | |
| } | |
| function attachConsentListener() { | |
| if (listenerAttached) return; | |
| // visitorConsentCollected: storefront-only DOM event | |
| document.addEventListener("visitorConsentCollected", onConsentChange); | |
| listenerAttached = true; | |
| } | |
| function handleConsent() { | |
| if ( | |
| typeof Shopify === "undefined" || | |
| typeof Shopify.customerPrivacy === "undefined" | |
| ) { | |
| attachConsentListener(); | |
| // Polling: customerPrivacy may load asynchronously | |
| var attempts = 0; | |
| var poll = setInterval(function () { | |
| attempts++; | |
| if ( | |
| typeof Shopify !== "undefined" && | |
| typeof Shopify.customerPrivacy !== "undefined" | |
| ) { | |
| clearInterval(poll); | |
| onConsentChange(); | |
| } | |
| if (attempts >= 10) clearInterval(poll); | |
| }, 500); | |
| return; | |
| } | |
| onConsentChange(); | |
| attachConsentListener(); | |
| } | |
| handleConsent(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment