Passa al contenuto principale

Varify Realtime Tracking

Ultimo aggiornamento il 09 ott 20263 min di lettura
important

Gli account creati a partire dal 17 settembre 2026 possono abilitare „Varify Realtime Tracking“ direttamente in „Tracking Setup.“ I clienti esistenti possono richiedere questa funzione contattando [email protected].

In breve​

Abilita Varify Realtime Tracking e analizza i tuoi esperimenti direttamente in Varify senza ritardi. Un servizio di tracciamento Varify dedicato misura i tuoi obiettivi definiti e li mostra in Varify Reporting.

La scelta tra „Varify Realtime Tracking“ e „Google Analytics 4“ come analytics che valuta gli esperimenti, ciascuno con il proprio pulsante.

Come usare Varify Realtime Tracking​

Passo 1: Configura il tuo sistema di tracciamento​

Vai su „Tracking Setup“ e seleziona „Varify Realtime Tracking.“ Puoi quindi decidere se integrare Varify Realtime Tracking con o senza „User Consent.“ Questa pagina può aiutarti a prendere questa decisione: https://varify.io/varify-tracking-gdpr-compliance/

Varify Tracking Setup per „Varify Realtime Tracking“, passo „User consent“: scegli tra „Without user consent“ e „With user consent“

Passo 2: Abilitare Varify Realtime Tracking​

Integrazione senza consenso dell'utente​

Se hai deciso di caricare Varify Realtime Tracking senza il consenso dell'utente, la configurazione di base del tracciamento è già in atto e puoi procedere al Passo 3.

Integrazione con consenso dell'utente​

Viene fornito uno script di attivazione per l'integrazione con il consenso dell'utente. Il tracciamento viene attivato non appena questo script viene caricato. Integra lo script secondo le specifiche della tua piattaforma di gestione del consenso (CMP). Puoi anche utilizzare Google Tag Manager (GTM) per l'integrazione basata sulle visualizzazioni di pagina e su un segnale di consenso valido.

Varify Tracking Setup, passo „Activation tag“: una freccia indica lo script di attivazione che richiama window.varify.setTracking(true)

Passo 3: Configurare i tuoi obiettivi Varify​

Per analizzare i tuoi esperimenti, crea degli obiettivi. Troverai la sezione "Goals" nella barra laterale a sinistra.

Nella sezione "Goals", puoi scegliere tra tre tipi di obiettivi: Click Goal, Page View Goal e Custom Goal.

Dialogo Varify „Create goal“ di tipo „Page view“: nome „Product Detail Page - Page views“ e una corrispondenza URL che contiene „/products/“

Configurazione dei Click Goal​

Con Click Goals, puoi misurare quante volte i partecipanti all'esperimento fanno clic sugli elementi. Per farlo, definisci un selettore personalizzato per l'elemento. Per identificare un selettore personalizzato per un elemento, puoi utilizzare ad esempio l'editor visivo di Varify. Aprilo e seleziona l'elemento che vuoi tracciare. Poi copia il selettore e inseriscilo nel campo "Selector" sotto "Click Goal."

Configurazione dei Page View Goal​

Con i Page View Goal puoi misurare quanto spesso i partecipanti all'esperimento visualizzano pagine o tipi di pagina specifici. Per farlo, puoi semplicemente definire regole personalizzate per tracciare le visualizzazioni di pagina che soddisfano i tuoi criteri.

Configurazione dei Custom Goal​

Puoi usare i Custom Goal per creare obiettivi personalizzati. Ad esempio, questo può essere il caso quando vuoi misurare il valore del fatturato di un ordine. I Custom Goal vengono attivati tramite JavaScript nel tuo esperimento.

Conteggio delle conversioni​

Con questa impostazione puoi specificare se un obiettivo debba essere tracciato una o più volte. Per impostazione predefinita, nell'A/B testing ogni obiettivo viene tracciato una sola volta, poiché di solito interessa solo sapere se un utente ha raggiunto l'obiettivo o meno. Il tracciamento multiplo, tuttavia, può essere utile per gli obiettivi legati al fatturato: in questo caso, il valore dell'ordine di un utente può essere accumulato su più sessioni.

Click goal di Varify „Button Click - Register“ con il selettore CSS „.register“ e le opzioni di conversion counting „Count unique“ (selezionata) e „Count multiple“

Tracciamento degli obiettivi di checkout Shopify​

Gli obiettivi di evento Shopify come «Purchase» o «Start Checkout» non possono essere tracciati direttamente tramite il tuo sito web. A partire dal 2025, Shopify ha reso obbligatorio che questi possano essere tracciati solo tramite il pixel personalizzato sul tuo sito web. Per tracciare questi obiettivi, vai su «Settings > Customer Events» e clicca sul pulsante «Add Custom Pixel». Imposta il nome del pixel su «Varify Realtime Tracking» e clicca su «Add Pixel.»

Impostazioni Shopify, Customer events: le frecce indicano „Customer events“ e „Add custom pixel“; la finestra di dialogo „Add custom pixel“ ha il nome del pixel „Varify Realtime Tracking“

Dopo aver creato il pixel, apri prima la sezione „Customer Privacy“ e imposta "Data Sale" su "Data collected does not qualify as a data sale." Varify non vende dati e traccia solo valori aggregati per le tue analisi dell'esperimento.

Per «Permission», dovresti selezionare «Not required» se vuoi tracciare senza il consenso dell'utente—come specificato in precedenza nella configurazione del tracciamento. Se invece stai tracciando con il consenso dell'utente, puoi lasciare l'impostazione su "Required."

Customer privacy del pixel Shopify „Varify Realtime Tracking“: Permission „Not required“ e Data sale „Data collected does not qualify as data sale“

Successivamente, copia il codice evidenziato sotto questo paragrafo e incollalo sotto "Code."

Pixel personalizzato Shopify „Varify Realtime Tracking“: il campo „Code“, evidenziato in rosso, con l'inizio dello script Varify Shopify Web Pixel
/**
* 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. Il tuo tracking Shopify è ora impostato.

Pixel personalizzato Shopify „Varify Realtime Tracking“ con Customer privacy impostato su „Not required“ e il pulsante „Connect“ in alto a destra