Tracciamento Shopify
In breve
Collega Shopify direttamente a Varify e analizza i tuoi esperimenti direttamente con i dati di conversione e fatturato di Shopify. L'integrazione funziona solo insieme all'utilizzo di GA4, poiché il numero di visitatori viene importato da GA4.
Come utilizzare i dati di tracciamento Shopify
Passo 1: Attiva l'importazione dei dati Shopify
Vai su „Tracking Setup“ e clicca sulla scheda „Advanced Setup“. Qui, attiva l'impostazione „Use Shopify Data“.
Passo 2: Crea un custom pixel con il codice di tracciamento Varify
Clicca su "Settings" nel tuo negozio Shopify e apri "Customer events". Poi clicca su "Add custom pixel" e assegna al pixel il nome: "varify-checkout". Quindi inserisci il seguente codice:
Codice Standard
Usa questo codice se il tuo negozio Shopify accetta 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 Multi-Valuta
Usa questo codice se il tuo negozio Shopify supporta 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, clicca su salva. Controlla anche che i tuoi permessi siano impostati correttamente, altrimenti lo script non verrà eseguito se manca un permesso utente. Poi clicca su «Connect». L'integrazione è ora installata con successo.
Passo 3: Aggiungi Shopify Goals al tuo esperimento
Assicurati che il tuo tracking setup sia configurato e di aver selezionato GA4 come tracking provider e che la voce «Test Evaluation» sia impostata su «In Varify and GA4». Se è così, apparirà ora un «Results Link» per gli esperimenti «appena creati» in alto a destra dell'esperimento avviato.
Facendo clic su "Results Link" si apre il relativo report dei risultati dell'esperimento. Ora puoi aggiungere "Conversions" o "Revenue" come obiettivo cliccando su "Add Shopify Goal". I visitatori dell'obiettivo vengono presi da GA4. Il valore di conversione e il valore di revenue provengono direttamente da Shopify.
Importante: i dati di GA4 vengono aggiornati solo dopo circa 24 ore. I dati di Shopify vengono aggiornati in tempo reale.
Filtro prodotti nel report dei risultati
Analizza le performance di specifici prodotti nel tuo A/B test. Puoi osservare singoli prodotti o varianti di prodotto. Per farlo, inserisci un ID prodotto o più ID nel campo, separati da virgole.