• Tracciamento di Shopify

    Indice dei contenuti

    Breve e dolce

    Collegate Shopify direttamente con Varify e analizzate i vostri esperimenti direttamente con i dati di conversione e di fatturato di Shopify. L'integrazione funziona solo con l'utilizzo di GA4, poiché il numero di visitatori viene importato da GA4.

    Come utilizzare i dati di tracciamento di Shopify

    Passo 1: Attivare l'importazione dei dati di Shopify

    Andare su "Tracking Setup" e fare clic sulla scheda "Advanced Setup". Attivare l'impostazione "Usa dati Shopify".

    Passo 2: Creare un pixel personalizzato con il codice di tracciamento Varify

    Cliccate su "Impostazioni" nel vostro negozio Shopify e aprite "Eventi cliente". Cliccate quindi su "Aggiungi pixel personalizzato" e assegnate al pixel il nome "varify-checkout". Inserite quindi il seguente codice:

    Codice standard

    Utilizza questo codice se nel tuo negozio Shopify è possibile effettuare pagamenti in una sola valuta.

    				
    					let storage;
    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) {
          // Cache nicht dauerhaft vergiften: nach Fehler zuruecksetzen,
          // damit der naechste Aufruf erneut versucht statt still tot zu bleiben.
          storage = undefined;
          console.error('Varify: error reading local storage', error);
          return {};
        }
      })();
      return storage;
    }
    
    analytics.subscribe('varify', async (event) => {
      const {
        varify_experimentId,
        varify_variationId,
        teamId,
        storageType = 'localStorage',
      } = event.customData || {};
    
      if (!varify_experimentId || !teamId) {
        console.warn('Varify: event missing experimentId or teamId.');
        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.storageType = storageType;
    
      await browser.localStorage.setItem(
        'varify-data',
        JSON.stringify(existingData)
      );
    });
    
    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)
      );
    });
    
    analytics.subscribe('checkout_completed', async (event) => {
      const orderId = event.data?.checkout?.order?.id;
      const orderRevenue = event.data?.checkout?.subtotalPrice?.amount;
    
      // 0 is a valid revenue (100% discount) and must not count as missing.
      if (!orderId || orderRevenue == null) {
        console.warn('Varify: checkout_completed missing orderId or revenue.');
        return;
      }
    
      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 storedAccountId = parsedData.teamId;
      const experiments = parsedData.data.map((entry) => {
        return [entry.experimentId, entry.variationId];
      });
    
      const storageType = parsedData.storageType;
    
      const storeHandle = init.data?.shop?.myshopifyDomain?.replace('.myshopify.com', '');
    
      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);
      }
    
      try {
        await fetch('https://ecommerce.varify.io/store_data', {
          method: 'POST',
          keepalive: true,
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            experiments,
            orderId,
            orderRevenue,
            accountId: storedAccountId,
            storageType: storageType,
            storeHandle,
            cartProducts,
          }),
        });
    
        parsedData.sentOrders = [...parsedData.sentOrders, orderId].slice(-50);
        await browser.localStorage.setItem('varify-data', JSON.stringify(parsedData));
      } catch (error) {
        console.error('Varify: error sending data', error);
      }
    });
    				
    			

    Codice multivaluta

    Utilizza questo codice se nel tuo negozio Shopify è possibile effettuare pagamenti in più valute.

    				
    					let storage;
    
    const SHOP_CURRENCY = init.data?.shop?.paymentSettings?.currencyCode || 'EUR';
    
    const FX_API = 'https://api.frankfurter.dev/v1/latest';
    const FX_TTL_MS = 6 * 60 * 60 * 1000;
    const FX_TIMEOUT_MS = 2000;
    
    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) {
          // Reset so the next call retries instead of caching a poisoned promise.
          storage = undefined;
          console.error('Varify: error reading local storage', error);
          return {};
        }
      })();
      return storage;
    }
    
    // Returns units of <currency> per 1 <SHOP_CURRENCY>, or null. null => do not send.
    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) {
        // Broken cache -> 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;
      }
    }
    
    analytics.subscribe('varify', async (event) => {
      const {
        varify_experimentId,
        varify_variationId,
        teamId,
        storageType = 'localStorage',
      } = event.customData || {};
    
      if (!varify_experimentId || !teamId) {
        console.warn('Varify: event missing experimentId or teamId.');
        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.storageType = storageType;
    
      await browser.localStorage.setItem(
        'varify-data',
        JSON.stringify(existingData)
      );
    });
    
    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)
      );
    
      // 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; the event carries no rate or shop-money
      // amount, and /store_data books the number as shop currency. Convert 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 storedAccountId = parsedData.teamId;
      const experiments = parsedData.data.map((entry) => {
        return [entry.experimentId, entry.variationId];
      });
    
      const storageType = parsedData.storageType;
    
      const storeHandle = init.data?.shop?.myshopifyDomain?.replace('.myshopify.com', '');
    
      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);
      }
    
      try {
        await fetch('https://ecommerce.varify.io/store_data', {
          method: 'POST',
          keepalive: true,
          headers: {
            'Content-Type': 'application/json',
          },
    
          body: JSON.stringify({
            experiments,
            orderId,
            orderRevenue,
            // Already converted above; always shop currency so a future server-side
            // conversion is a no-op, not a double conversion.
            orderCurrency: SHOP_CURRENCY,
            accountId: storedAccountId,
            storageType: storageType,
            storeHandle,
            cartProducts,
          }),
        });
    
        parsedData.sentOrders = [...parsedData.sentOrders, orderId].slice(-50);
        await browser.localStorage.setItem('varify-data', JSON.stringify(parsedData));
      } catch (error) {
        console.error('Varify: error sending data', error);
      }
    });
    				
    			

    Dopo aver aggiunto il codice, fare clic su Salva. Controllate anche che le autorizzazioni siano impostate correttamente, altrimenti lo script non verrà eseguito se manca un'autorizzazione dell'utente. Fare quindi clic su "Connetti". L'integrazione è ora installata con successo.

    Passo 3: Aggiungere gli obiettivi di Shopify all'esperimento

    Assicurarsi che la configurazione del tracciamento sia impostata e che sia stato selezionato GA4 come fornitore di tracciamento e che la voce "Valutazione del test" sia impostata su "In Varify e GA4". In questo caso, per gli esperimenti "appena creati" apparirà un "link ai risultati" in alto a destra dell'esperimento avviato. 

    Facendo clic sul "Link ai risultati" si apre il rapporto sui risultati dell'esperimento. Ora è possibile fare clic su "Aggiungi obiettivo Shopify" per aggiungere "Conversioni" o "Ricavi" come obiettivo. I visitatori dell'obiettivo sono presi da GA4. Il valore di conversione e il valore delle entrate provengono direttamente da Shopify.

    Importante: i dati di GA4 vengono aggiornati solo dopo circa 24 ore. I dati di Shopify sono aggiornati in tempo reale.

    Filtro prodotto nel report dei risultati

    Analizzare le prestazioni di determinati prodotti nel test A/B. È possibile analizzare singoli prodotti o varianti di prodotti. A tal fine, inserire nel campo un ID prodotto o più ID, separati da virgole.

  • Primi passi