/** * Meta Advanced Tracking — injete na página de vendas. * * Até InitiateCheckout: Meta Pixel (browser) + Conversions API (servidor), mesmo event_id. * AddPaymentInfo / Purchase: só CAPI (checkout + backend). * * Eventos na landing: * 1. PageView → ao carregar * 2. ViewContent → na primeira rolagem * 3. InitiateCheckout → clique em .checkout-button * * Links .checkout-button recebem pixelID, advanced_tracking=true, external_id gerado (+ UTMs). * * Uso: * * * data-meta-pixel-id (opcional): ID numérico Meta. Se omitido, busca via API pelo pixelID. * data-browser-pixel="false": desliga fbq (só CAPI na landing). */ (function () { "use strict"; var COOKIE_DAYS = 90; var EXTERNAL_ID_COOKIE = "_meta_external_id"; var sent = { pageView: false, viewContent: false }; var icDebounceUntil = 0; var IC_DEBOUNCE_MS = 2000; var PARAMS_TO_PROPAGATE = [ "utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term", "utm_id", "src", "sck", "external_id", "ip", "fbclid", "fbc", "fbp", "gclid", "gclsrc", ]; function currentScript() { return document.currentScript || document.querySelector('script[src*="meta-advanced-tracking"]'); } function readConfig() { var el = currentScript(); var ds = (el && el.dataset) || {}; var win = window.META_ADVANCED_TRACKING || {}; function pick(key, dataKey) { if (win[key] != null && win[key] !== "") return win[key]; if (ds[dataKey] != null && ds[dataKey] !== "") return ds[dataKey]; return ""; } var contentIdsRaw = pick("contentIds", "contentIds") || pick("content_ids", "contentIds"); var contentIds = []; if (contentIdsRaw) { contentIds = String(contentIdsRaw) .split(",") .map(function (s) { return s.trim(); }) .filter(Boolean); } var valueRaw = pick("value", "value"); var value = valueRaw === "" || valueRaw == null ? null : parseFloat(valueRaw); var browserPixelRaw = pick("browserPixel", "browserPixel"); var browserPixelEnabled = browserPixelRaw === "" || browserPixelRaw == null ? true : String(browserPixelRaw).trim().toLowerCase() !== "false"; return { apiBaseUrl: String(pick("apiBaseUrl", "apiBase") || "https://api.adesivodissolvex.site").replace(/\/$/, ""), pixelID: String(pick("pixelID", "pixelId") || pick("pixelId", "pixelId") || ""), metaPixelId: String(pick("metaPixelId", "metaPixelId") || ""), browserPixelEnabled: browserPixelEnabled, eventSourceUrl: String(pick("eventSourceUrl", "eventSourceUrl") || ""), contentIds: contentIds, contentName: String(pick("contentName", "contentName") || document.title || ""), contentType: String(pick("contentType", "contentType") || "product"), value: isNaN(value) ? null : value, currency: String(pick("currency", "currency") || "BRL"), testEventCode: String(pick("testEventCode", "testEventCode") || ""), }; } function getQueryParam(name) { try { return new URLSearchParams(window.location.search).get(name) || ""; } catch (e) { return ""; } } function trackingParamValue(key) { if (key === "external_id") return ensureExternalId(); if (key === "fbp") return getQueryParam("fbp") || getCookie("_fbp"); if (key === "fbc") { ensureFbc(); return getQueryParam("fbc") || getCookie("_fbc") || ""; } return getQueryParam(key); } function getCookie(name) { var match = document.cookie.match(new RegExp("(?:^|; )" + name.replace(/[.$?*|{}()[\]\\/+^]/g, "\\$&") + "=([^;]*)")); return match ? decodeURIComponent(match[1]) : ""; } function setCookie(name, value, days) { if (!value) return; var maxAge = (days || COOKIE_DAYS) * 24 * 60 * 60; var secure = window.location.protocol === "https:" ? "; Secure" : ""; document.cookie = name + "=" + encodeURIComponent(value) + "; path=/; max-age=" + maxAge + "; SameSite=Lax" + secure; } function fbcCreationTimeMs(fbc) { if (!fbc || fbc.indexOf("fb.1.") !== 0) return 0; var parts = fbc.split("."); if (parts.length < 4) return 0; var ts = parseInt(parts[2], 10); // Meta exige creationTime em milissegundos (13 dígitos), não segundos. return ts >= 1e12 ? ts : 0; } function ensureFbc() { var fbc = getCookie("_fbc"); if (fbcCreationTimeMs(fbc)) return fbc; var fbclid = getQueryParam("fbclid"); if (!fbclid) return fbc || ""; fbc = "fb.1." + Date.now() + "." + fbclid; setCookie("_fbc", fbc, COOKIE_DAYS); return fbc; } function fbpCreationTimeMs(fbp) { if (!fbp || fbp.indexOf("fb.1.") !== 0) return 0; var parts = fbp.split("."); if (parts.length < 4) return 0; var ts = parseInt(parts[2], 10); return ts >= 1e12 ? ts : 0; } function ensureFbp() { var fbp = getCookie("_fbp"); if (fbpCreationTimeMs(fbp)) return fbp; fbp = "fb.1." + Date.now() + "." + String(Math.floor(Math.random() * 10000000000)).padStart(10, "0"); setCookie("_fbp", fbp, COOKIE_DAYS); return fbp; } function newUuid() { if (window.crypto && crypto.randomUUID) { return crypto.randomUUID(); } return ( Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 11) + Math.random().toString(36).slice(2, 11) ); } function ensureExternalId() { var existing = getCookie(EXTERNAL_ID_COOKIE); if (existing) return existing; var id = newUuid(); setCookie(EXTERNAL_ID_COOKIE, id, COOKIE_DAYS); return id; } function newEventId(prefix) { if (window.crypto && crypto.randomUUID) { return prefix + "-" + crypto.randomUUID(); } return prefix + "-" + Date.now() + "-" + Math.random().toString(36).slice(2, 11); } function userDataPayload() { ensureFbc(); ensureFbp(); var ud = { fbp: getCookie("_fbp") || undefined, fbc: getCookie("_fbc") || undefined, fbclid: getQueryParam("fbclid") || undefined, external_id: ensureExternalId(), client_user_agent: navigator.userAgent, }; Object.keys(ud).forEach(function (k) { if (ud[k] == null || ud[k] === "") delete ud[k]; }); return ud; } function contentCustomData(cfg) { var cd = { content_type: cfg.contentType }; if (cfg.contentIds.length) cd.content_ids = cfg.contentIds; if (cfg.contentName) cd.content_name = cfg.contentName; return cd; } function purchaseCustomData(cfg, overrides) { overrides = overrides || {}; var cd = contentCustomData(cfg); var value = overrides.value != null && overrides.value !== "" ? parseFloat(overrides.value) : cfg.value; if (value == null || isNaN(value)) return cd; cd.value = value; cd.currency = String(overrides.currency || cfg.currency || "BRL"); return cd; } function buildTrackingPayload() { var out = {}; PARAMS_TO_PROPAGATE.forEach(function (key) { var val = trackingParamValue(key); if (val) out[key] = val; }); return out; } function sendCapi(cfg, eventName, eventId, customData) { if (!cfg.apiBaseUrl || !cfg.pixelID) return Promise.resolve(); var body = { pixelID: cfg.pixelID, event_name: eventName, event_id: eventId, event_source_url: cfg.eventSourceUrl || window.location.href, action_source: "website", user_data: userDataPayload(), tracking: buildTrackingPayload(), }; if (cfg.testEventCode) body.test_event_code = cfg.testEventCode; if (customData && Object.keys(customData).length) body.custom_data = customData; return fetch(cfg.apiBaseUrl + "/api/pix/meta-capi/events/", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, body: JSON.stringify(body), credentials: "omit", keepalive: true, }).catch(function (err) { console.warn("[meta-advanced-tracking] CAPI error", eventName, err); }); } function loadFbevents() { if (window.fbq) return Promise.resolve(); return new Promise(function (resolve) { var done = false; function finish() { if (done) return; done = true; resolve(); } !(function (f, b, e, v, n, t, s) { if (f.fbq) return finish(); n = f.fbq = function () { n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments); }; if (!f._fbq) f._fbq = n; n.push = n; n.loaded = !0; n.version = "2.0"; n.queue = []; t = b.createElement(e); t.async = !0; t.src = v; t.onload = finish; t.onerror = finish; s = b.getElementsByTagName(e)[0]; s.parentNode.insertBefore(t, s); })(window, document, "script", "https://connect.facebook.net/en_US/fbevents.js"); setTimeout(finish, 4000); }); } function fetchMetaPixelId(cfg) { if (cfg.metaPixelId) return Promise.resolve(String(cfg.metaPixelId).trim()); if (!cfg.apiBaseUrl || !cfg.pixelID) return Promise.resolve(""); return fetch( cfg.apiBaseUrl + "/api/pix/meta-capi/pixel/?pixelID=" + encodeURIComponent(cfg.pixelID), { credentials: "omit" } ) .then(function (r) { return r.json(); }) .then(function (data) { return data && data.success && data.meta_pixel_id ? String(data.meta_pixel_id).trim() : ""; }) .catch(function () { return ""; }); } function initBrowserPixel(metaPixelId) { if (!metaPixelId || !window.fbq) return false; window.fbq("init", metaPixelId); return true; } function sendBrowserEvent(cfg, eventName, eventId, customData) { if (!cfg.browserPixelEnabled || !cfg.metaPixelId || !window.fbq) return; var params = customData ? Object.assign({}, customData) : {}; try { window.fbq("track", eventName, params, { eventID: eventId }); } catch (err) { console.warn("[meta-advanced-tracking] Pixel error", eventName, err); } } function fireEvent(cfg, eventName, prefix, customData, options) { options = options || {}; var eventId = newEventId(prefix); if (options.browser !== false) { sendBrowserEvent(cfg, eventName, eventId, customData); } return sendCapi(cfg, eventName, eventId, customData); } function onPageView(cfg) { if (sent.pageView) return; sent.pageView = true; fireEvent(cfg, "PageView", "pv", undefined); } function onViewContent(cfg) { if (sent.viewContent) return; sent.viewContent = true; fireEvent(cfg, "ViewContent", "vc", contentCustomData(cfg)); } function onInitiateCheckout(cfg) { var now = Date.now(); if (now < icDebounceUntil) return; icDebounceUntil = now + IC_DEBOUNCE_MS; fireEvent(cfg, "InitiateCheckout", "ic", contentCustomData(cfg)); } function onPurchase(cfg, overrides) { var eventId = newEventId("pu"); sendCapi(cfg, "Purchase", eventId, purchaseCustomData(cfg, overrides)); } function augmentLinkHref(href, cfg) { if (!href || href.charAt(0) === "#") return href; var lower = href.toLowerCase(); if (lower.indexOf("javascript:") === 0 || lower.indexOf("mailto:") === 0 || lower.indexOf("tel:") === 0) { return href; } try { var url = new URL(href, window.location.href); if (cfg.pixelID) { url.searchParams.set("pixelID", cfg.pixelID); url.searchParams.set("advanced_tracking", "true"); } url.searchParams.set("external_id", ensureExternalId()); PARAMS_TO_PROPAGATE.forEach(function (key) { if (key === "external_id") return; var val = trackingParamValue(key); if (val && !url.searchParams.has(key)) { url.searchParams.set(key, val); } }); return url.toString(); } catch (e) { return href; } } function augmentCheckoutButtonLinks(cfg) { if (!cfg.pixelID) return; document.querySelectorAll("a.checkout-button[href], a[href].checkout-button").forEach(function (anchor) { var href = anchor.getAttribute("href"); if (!href) return; anchor.setAttribute("href", augmentLinkHref(href, cfg)); }); } function bindScrollOnce(cfg) { var fired = false; function trigger() { if (fired) return; fired = true; onViewContent(cfg); window.removeEventListener("scroll", trigger, scrollOpts); window.removeEventListener("wheel", trigger, scrollOpts); window.removeEventListener("touchmove", trigger, scrollOpts); } var scrollOpts = { passive: true, capture: false }; window.addEventListener("scroll", trigger, scrollOpts); window.addEventListener("wheel", trigger, scrollOpts); window.addEventListener("touchmove", trigger, scrollOpts); } function bindCheckoutClicks(cfg) { document.addEventListener( "click", function (ev) { if (ev.metaAdvancedIcSent) return; var target = ev.target; if (!target || !target.closest) return; var el = target.closest(".checkout-button"); if (!el) return; ev.metaAdvancedIcSent = true; onInitiateCheckout(cfg); }, true ); } function bootTracking(cfg) { if (window.__metaAdvancedTrackingBooted) return; window.__metaAdvancedTrackingBooted = true; ensureFbc(); ensureFbp(); ensureExternalId(); augmentCheckoutButtonLinks(cfg); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", function () { augmentCheckoutButtonLinks(cfg); onPageView(cfg); }); } else { onPageView(cfg); } bindScrollOnce(cfg); bindCheckoutClicks(cfg); window.MetaAdvancedTracking = { getExternalId: function () { return ensureExternalId(); }, trackPurchase: function (overrides) { onPurchase(cfg, overrides); }, augmentLinks: function () { augmentCheckoutButtonLinks(cfg); }, }; } function start() { if (window.__metaAdvancedTrackingStarted) return; window.__metaAdvancedTrackingStarted = true; var cfg = readConfig(); if (!cfg.pixelID) { console.warn("[meta-advanced-tracking] data-pixel-id / pixelID ausente — tracking desligado."); return; } var boot = function () { bootTracking(cfg); }; if (!cfg.browserPixelEnabled) { boot(); return; } fetchMetaPixelId(cfg) .then(function (metaPixelId) { cfg.metaPixelId = metaPixelId; if (!metaPixelId) { console.warn("[meta-advanced-tracking] meta_pixel_id não encontrado — só CAPI na landing."); boot(); return; } return loadFbevents().then(function () { initBrowserPixel(metaPixelId); boot(); }); }) .catch(function () { boot(); }); } start(); })();