• Monitoraggio in tempo reale Varify

    Gli account creati a partire dal 17 settembre 2026 possono attivare la funzione „Varify Realtime Tracking“ direttamente nella sezione „Tracking Setup“. I clienti esistenti possono richiedere la funzione inviando una richiesta a [email protected].

    Indice dei contenuti

    Breve e dolce

    Attiva il monitoraggio in tempo reale di Varify e analizza i tuoi esperimenti direttamente e in tempo reale all’interno di Varify. Un servizio di monitoraggio dedicato di Varify misura gli obiettivi da te definiti e li visualizza nel reporting di Varify.

    Ecco come utilizzare il monitoraggio in tempo reale di Varify

    Passo 1: Configura il tuo sistema di tracciamento

    Vai su „Tracking Setup“ e seleziona „Varify Realtime Tracking“. Successivamente potrai decidere se integrare il tracciamento Varify con o senza „consenso dell’utente“. Questa pagina può aiutarti a prendere una decisione: https://varify.io/en/varify-tracking-gdpr-compliance/

    Fase 2: Attivazione del tracciamento in tempo reale di Varify

    Integrazione senza consenso dell'utente

    Se hai deciso di caricare il tracciamento in tempo reale di Varify senza il consenso dell'utente, la configurazione di base del tracciamento è già pronta e puoi passare al punto 3. 

    Integrazione con il consenso degli utenti

    Per l'integrazione con il consenso dell'utente ti viene messo a disposizione uno script di attivazione. Non appena lo script viene caricato, il tracciamento viene attivato. Integra lo script seguendo le indicazioni della tua piattaforma di gestione del consenso (CSP). A tal fine è possibile anche l'integrazione tramite GTM in base alla visualizzazione della pagina e alla presenza di un segnale di consenso valido.

    Fase 3: Configurazione dei tuoi obiettivi Varify

    Per analizzare i tuoi esperimenti, devi creare degli obiettivi. La sezione "Obiettivi" si trova nella barra laterale a sinistra.

    Nella sezione "Obiettivi" puoi scegliere tra i 3 tipi di obiettivo: "Obiettivo clic", "Obiettivo visualizzazione pagina" e "Obiettivo personalizzato".

    Configurazione degli obiettivi di clic

    Con Click Goals puoi misurare la frequenza con cui i partecipanti all’esperimento cliccano sugli elementi. A tal fine, devi definire un selettore personalizzato per l’elemento. Per identificare un selettore personalizzato per un elemento, puoi ad esempio utilizzare l’editor visivo di Varify. Aprilo e seleziona l’elemento che desideri monitorare. Copia quindi il selettore e inseriscilo nel campo “Selettore” del Click Goal.

    Configurazione degli obiettivi di visualizzazione della pagina

    Con gli obiettivi di visualizzazione delle pagine puoi misurare la frequenza con cui i partecipanti al tuo esperimento visualizzano determinate pagine o tipi di pagine. A tal fine, puoi semplicemente definire le tue regole personalizzate per misurare le visualizzazioni delle pagine secondo i tuoi criteri.

    Configurazione degli obiettivi personalizzati

    Grazie agli obiettivi personalizzati puoi creare obiettivi su misura. Ad esempio, se desideri misurare il valore del fatturato del tuo ordine. Gli obiettivi personalizzati vengono attivati tramite JavaScript nel tuo esperimento. 

    Conteggio delle conversioni

    Con questa impostazione puoi stabilire se un obiettivo debba essere conteggiato una sola volta o più volte. Per impostazione predefinita, nei test A/B ogni obiettivo viene conteggiato una sola volta, poiché in genere interessa solo sapere se un utente ha effettivamente raggiunto l’obiettivo. Una misurazione multipla può invece rivelarsi utile nel caso degli obiettivi di fatturato: in questo caso, il valore dell’ordine di un utente può essere cumulato nel corso di più sessioni.

    Monitoraggio degli obiettivi di checkout di Shopify

    Gli obiettivi di evento di Shopify come „Acquisto“ o „Avvia checkout“ non possono essere misurati direttamente tramite il tuo sito web. A partire dal 2025, Shopify ha reso obbligatorio che questi possano essere misurati solo tramite il pixel personalizzato sul tuo sito web. Per poter misurare questi obiettivi, vai su „Impostazioni > Eventi dei clienti“ e clicca sul pulsante „Aggiungi pixel personalizzato“. Imposta il nome del pixel su „Varify Realtime Tracking“ e clicca su «Aggiungi pixel».

    Dopo aver creato il pixel, apri innanzitutto la sezione „Privacy dei clienti“ e, nella voce “Vendita dei dati”, seleziona l’opzione “I dati raccolti non costituiscono una vendita di dati”. Varify non vende dati e monitora esclusivamente valori aggregati per le tue analisi degli esperimenti.

    Per quanto riguarda l’opzione „Permission“, dovresti selezionare „Not required“ se desideri effettuare le misurazioni senza il consenso dell’utente, come specificato in precedenza nella configurazione del tracciamento. Se invece effettui le misurazioni con il consenso dell’utente, puoi lasciare l’impostazione su “Required”.

    Copia quindi il codice evidenziato qui sotto il paragrafo e incollalo nella sezione "Codice". 

    				
    					/**
     * Varify Shopify Web Pixel — links experiments/variations a customer saw to
     * whether they completed a purchase.
     */
    
    let storage;
    
    // Store's currency, used to normalize order totals before sending them.
    const SHOP_CURRENCY = init.data?.shop?.paymentSettings?.currencyCode || 'EUR';
    
    // Free FX API, used only when an order's currency differs from the store's.
    const FX_API = 'https://api.frankfurter.dev/v1/latest';
    const FX_TTL_MS = 6 * 60 * 60 * 1000;
    const FX_TIMEOUT_MS = 2000;
    
    const VARIFY_INGESTION_ENDPOINT = 'https://w.varify.io/events';
    
    // One id per pixel session, reused for every event this pixel sends.
    const pixelSessionId = crypto.randomUUID();
    
    // Reads this pixel's saved data (experiments seen, anonymous id, etc.),
    // cached in-memory after the first read.
    async function getVarifyData() {
      if (storage) {
        return storage;
      }
      storage = (async () => {
        try {
          const raw = await browser.localStorage.getItem('varify-data');
          return raw ? JSON.parse(raw) : {};
        } catch (error) {
          // Don't cache a broken read — retry from scratch next time.
          storage = undefined;
          console.error('Varify: error reading local storage', error);
          return {};
        }
      })();
      return storage;
    }
    
    // Units of `currency` per 1 unit of the store's currency, or null if
    // unavailable — callers should skip sending rather than guess a rate.
    async function getFxRate(currency) {
      const cacheKey = `varify-fx-${SHOP_CURRENCY}-${currency}`;
    
      try {
        const cached = await browser.localStorage.getItem(cacheKey);
        if (cached) {
          const { rate, ts } = JSON.parse(cached);
          if (rate && ts && Date.now() - ts < FX_TTL_MS) {
            return rate;
          }
        }
      } catch (error) {
        // Fall through and fetch fresh below.
      }
    
      try {
        const url = `${FX_API}?from=${encodeURIComponent(SHOP_CURRENCY)}&to=${encodeURIComponent(currency)}`;
    
        let res;
        if (typeof AbortController === 'function') {
          const controller = new AbortController();
          const timer = setTimeout(() => controller.abort(), FX_TIMEOUT_MS);
          try {
            res = await fetch(url, { signal: controller.signal });
          } finally {
            clearTimeout(timer);
          }
        } else {
          res = await fetch(url);
        }
    
        if (!res.ok) {
          console.warn(`Varify: FX API returned ${res.status} for ${currency}.`);
          return null;
        }
    
        const json = await res.json();
        const rate = json?.rates?.[currency];
    
        if (typeof rate !== 'number' || !isFinite(rate) || rate <= 0) {
          console.warn(`Varify: FX API gave no usable rate for ${currency}.`, json);
          return null;
        }
    
        await browser.localStorage.setItem(
          cacheKey,
          JSON.stringify({ rate, ts: Date.now() }),
        );
        return rate;
      } catch (error) {
        console.warn(`Varify: FX lookup failed for ${currency}.`, error);
        return null;
      }
    }
    
    // Varify's endpoint only accepts flat text/number values, so a list of
    // experiments becomes two comma-separated strings (ids and variation ids).
    function flattenExperiments(entries) {
      return {
        experiment_ids: entries.map((entry) => entry.experimentId).join(','),
        variation_ids: entries
          .map((entry) =>
            entry.variationId === null ? 'original' : entry.variationId,
          )
          .join(','),
      };
    }
    
    // Edge's identifier also contains "Chrome/", so it must be checked first.
    function detectBrowser(userAgent) {
      if (userAgent.includes('Firefox/')) return 'Firefox';
      if (userAgent.includes('Edg/')) return 'Edge';
      if (userAgent.includes('Chrome/')) return 'Chrome';
      if (userAgent.includes('Safari/')) return 'Safari';
      return 'Unknown';
    }
    
    function detectDevice(userAgent) {
      if (/Tablet|iPad/i.test(userAgent)) return 'tablet';
      if (/Mobi|Android/i.test(userAgent)) return 'mobile';
      return 'desktop';
    }
    
    function detectOs(userAgent) {
      if (/iPhone|iPad|iPod/i.test(userAgent)) return 'iOS';
      if (userAgent.includes('Android')) return 'Android';
      if (userAgent.includes('Win')) return 'Windows';
      if (userAgent.includes('Mac')) return 'macOS';
      if (userAgent.includes('Linux')) return 'Linux';
      return 'Unknown';
    }
    
    // Browser timezone, e.g. "Europe/Berlin" — not GPS or IP-based location.
    function detectLocation() {
      try {
        return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;
      } catch {
        return undefined;
      }
    }
    
    // Non-identifying browser/page details, added to every event. The pixel
    // sandbox may not expose all of these — each lookup is best-effort and
    // simply omitted if unavailable.
    function collectBrowserInfo() {
      const info = {};
    
      try {
        const userAgent =
          typeof navigator !== 'undefined' ? navigator.userAgent : undefined;
        if (typeof userAgent === 'string') {
          info.userAgent = userAgent;
          info.browser = detectBrowser(userAgent);
          info.device = detectDevice(userAgent);
          info.os = detectOs(userAgent);
        }
      } catch (error) {
        // Not available in this environment.
      }
    
      try {
        if (typeof window !== 'undefined' && window.location) {
          info.path = window.location.pathname;
          info.domain = window.location.hostname;
        }
      } catch (error) {
        // Not available in this environment.
      }
    
      const location = detectLocation();
      if (location) {
        info.location = location;
      }
    
      return info;
    }
    
    // Sends one event to Varify. Never throws — failures are logged and
    // swallowed so the pixel/checkout is never affected. Falls back to reading
    // the anonymous id from local storage if one wasn't passed in, and returns
    // whichever id was actually used so the caller can persist it.
    async function sendVarifyEvent(name, teamSqid, anonymousId, properties) {
      const anonymousIdWithFallback =
        anonymousId || localStorage.getItem('varify_anonymous_id');
    
      if (!teamSqid || !anonymousIdWithFallback) {
        console.warn(
          `Varify: skipping "${name}" event — missing teamSqid or anonymousId.`,
          'Team Sqid',
          teamSqid,
          'Anonymous ID',
          anonymousIdWithFallback,
        );
        return anonymousIdWithFallback;
      }
    
      try {
        await fetch(VARIFY_INGESTION_ENDPOINT, {
          method: 'POST',
          keepalive: true,
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            events: [
              {
                id: crypto.randomUUID(),
                name,
                teamId: String(teamSqid),
                anonymousId: anonymousIdWithFallback,
                sessionId: pixelSessionId,
                properties: { ...collectBrowserInfo(), ...properties },
              },
            ],
          }),
        });
      } catch (error) {
        console.error(`Varify: error sending "${name}" event`, error);
      }
    
      return anonymousIdWithFallback;
    }
    
    // Fired by Varify's tracking script when a customer is shown an experiment.
    // Remembers it locally so it can be attached to a later checkout event.
    analytics.subscribe('varify', async (event) => {
      const {
        varify_experimentId,
        varify_variationId,
        varify_anonymousId,
        teamId,
        teamSqid,
        storageType = 'localStorage',
      } = event.customData || {};
    
      if (!varify_experimentId || !teamId || !teamSqid) {
        console.warn('Varify: event missing experimentId or teamId. Event:', event);
        return;
      }
    
      const validVariationId =
        varify_variationId !== undefined ? varify_variationId : null;
    
      const existingData = await getVarifyData();
    
      if (!existingData.data) {
        existingData.data = [];
      }
    
      const experimentEntry = {
        experimentId: varify_experimentId,
        variationId: validVariationId,
        timestamp: Date.now(),
      };
    
      existingData.data = existingData.data.filter(
        (entry) => entry.experimentId !== varify_experimentId,
      );
    
      existingData.data.push(experimentEntry);
      existingData.teamId = teamId;
      existingData.teamSqid = teamSqid;
      existingData.storageType = storageType;
    
      // Only present once Varify's tracking script has finished loading —
      // don't overwrite a previously-saved id with nothing.
      if (varify_anonymousId) {
        existingData.anonymousId = varify_anonymousId;
      }
    
      await browser.localStorage.setItem(
        'varify-data',
        JSON.stringify(existingData),
      );
    });
    
    // How long an experiment exposure counts toward a later purchase.
    const WINDOW_MS = 28 * 24 * 60 * 60 * 1000;
    
    const pruneEntries = (entries) => {
      const threshold = Date.now() - WINDOW_MS;
      return (entries || []).filter(
        (entry) =>
          entry.timestamp && entry.experimentId && entry.timestamp >= threshold,
      );
    };
    
    analytics.subscribe('checkout_started', async (event) => {
      const existingData = await getVarifyData();
      if (!existingData.data || existingData.data.length === 0) {
        return;
      }
    
      existingData.data = pruneEntries(existingData.data);
    
      await browser.localStorage.setItem(
        'varify-data',
        JSON.stringify(existingData),
      );
    
      if (existingData.data.length > 0) {
        const resolvedAnonymousId = await sendVarifyEvent(
          'checkout_started',
          existingData.teamSqid,
          existingData.anonymousId,
          flattenExperiments(existingData.data),
        );
    
        // Persist the fallback id, if used, so it doesn't need re-deriving.
        if (
          resolvedAnonymousId &&
          resolvedAnonymousId !== existingData.anonymousId
        ) {
          existingData.anonymousId = resolvedAnonymousId;
          await browser.localStorage.setItem(
            'varify-data',
            JSON.stringify(existingData),
          );
        }
      }
    
      // Warm the rate while the customer is still typing.
      const currency = event.data?.checkout?.currencyCode;
      if (currency && currency !== SHOP_CURRENCY) {
        getFxRate(currency);
      }
    });
    
    analytics.subscribe('checkout_completed', async (event) => {
      const orderId = event.data?.checkout?.order?.id;
      const money = event.data?.checkout?.subtotalPrice;
      let orderRevenue = money?.amount;
      const orderCurrency = money?.currencyCode;
    
      // 0 is a valid revenue (100% discount) and must not count as missing.
      if (!orderId || orderRevenue == null || !orderCurrency) {
        console.warn(
          'Varify: checkout_completed missing orderId, revenue or currency.',
        );
        return;
      }
    
      // subtotalPrice is presentment currency and the event carries no rate or
      // shop-money amount. Convert to shop currency here, or skip.
      if (orderCurrency !== SHOP_CURRENCY) {
        const rate = await getFxRate(orderCurrency);
        if (!rate) {
          console.warn(
            `Varify: skipping order ${orderId} — ${orderRevenue} ${orderCurrency}, no FX rate available.`,
          );
          return;
        }
        const converted = +(orderRevenue / rate).toFixed(2);
        console.info(
          `Varify: converted order ${orderId} — ${orderRevenue} ${orderCurrency} @ ${rate} -> ${converted} ${SHOP_CURRENCY}.`,
        );
        orderRevenue = converted;
      }
    
      const parsedData = await getVarifyData();
      if (!parsedData.data || parsedData.data.length === 0 || !parsedData.teamId) {
        console.warn('Varify: no valid data in local storage.');
        return;
      }
    
      // The order-status page refires checkout_completed on every visit.
      if (!Array.isArray(parsedData.sentOrders)) {
        parsedData.sentOrders = [];
      }
      if (parsedData.sentOrders.includes(orderId)) {
        return;
      }
    
      parsedData.data = pruneEntries(parsedData.data);
      if (parsedData.data.length === 0) {
        return;
      }
    
      const lineItems = event.data?.checkout?.lineItems;
      let cartProducts = [];
    
      if (lineItems && Array.isArray(lineItems)) {
        cartProducts = lineItems
          .map((item) => {
            const productId = item.variant?.product?.id?.split('/').pop();
            const variantId = item.variant?.id?.split('/').pop();
    
            if (productId && variantId) {
              return {
                productId,
                variantId,
              };
            }
            return null;
          })
          .filter((p) => p !== null);
      }
    
      const resolvedAnonymousId = await sendVarifyEvent(
        'checkout_completed',
        parsedData.teamSqid,
        parsedData.anonymousId,
        {
          ...flattenExperiments(parsedData.data),
          order_id: String(orderId),
          value: orderRevenue,
          currency: SHOP_CURRENCY,
          cart_product_ids: cartProducts.map((p) => p.productId).join(','),
          cart_variant_ids: cartProducts.map((p) => p.variantId).join(','),
        },
      );
    
      // Persist the fallback id, if used, so it doesn't need re-deriving.
      if (resolvedAnonymousId) {
        parsedData.anonymousId = resolvedAnonymousId;
      }
    
      parsedData.sentOrders = [...parsedData.sentOrders, orderId].slice(-50);
      await browser.localStorage.setItem('varify-data', JSON.stringify(parsedData));
    });
    
    				
    			

    Dopo aver aggiunto il codice, clicca su “connect” nel menu in alto. A questo punto, il tracciamento Shopify è configurato.

  • Primi passi