Zum Hauptinhalt springen

Shopify-Tracking

Zuletzt aktualisiert am 16. Juli 20262 Min. Lesezeit

Kurz gefasst​

Verbinde Shopify direkt mit Varify und analysiere deine Experimente direkt mit den Conversion- und Umsatzdaten aus Shopify. Die Integration funktioniert nur in Verbindung mit GA4, da die Besucherzahl aus GA4 importiert wird.

Varify-Zielauswertung mit zwei Zielen: „Conversions“ verbessert sich um 9,49 % und ist signifikant, „Revenue“ um 9,73 % bei 94,28 % Konfidenz und ist nicht

So verwendest du Shopify-Tracking-Daten​

Schritt 1: Shopify-Datenimport aktivieren​

Gehe zu „Tracking Setup“ und klicke auf den Tab „Advanced Setup“. Aktiviere hier die Einstellung „Use Shopify Data“.

Varify Advanced Setup mit aktiviertem „Start Tracking on Activation Event“ und „Use Shopify Data“

Schritt 2: Einen Custom Pixel mit Varify-Tracking-Code erstellen​

Shopify „Customer events“ mit einem Pfeil, der auf die Schaltfläche „Add custom pixel“ über der Pixelliste zeigt

Klicke in deinem Shopify-Store auf „Einstellungen“ und öffne „Kundenereignisse“. Klicke dann auf „Benutzerdefinierten Pixel hinzufügen“ und benenne den Pixel: „varify-checkout“. Füge anschließend den folgenden Code ein:

Standard-Code​

Verwende diesen Code, wenn dein Shopify-Store Zahlungen nur in einer Währung akzeptiert.

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);
}
});

Multi-Währungs-Code​

Verwende diesen Code, wenn dein Shopify-Store Zahlungen in mehreren Währungen unterstützt.

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);
}
});

Nachdem du den Code hinzugefügt hast, klicke auf Speichern. Überprüfe außerdem, ob deine Berechtigungen korrekt gesetzt sind, da das Skript sonst nicht ausgeführt wird, wenn eine Nutzerberechtigung fehlt. Klicke anschließend auf „Connect“. Die Integration ist nun erfolgreich installiert.

Der erstellte Pixel „varify-checkout“ mit seinem Code, der das „varify“-Event abonniert und die Variationsdaten ausliest

Schritt 3: Shopify-Ziele zu deinem Experiment hinzufügen​

Stelle sicher, dass dein Tracking-Setup eingerichtet ist, dass du GA4 als Tracking-Anbieter ausgewählt hast und dass der Punkt „Test Evaluation“ auf „In Varify and GA4“ gesetzt ist. Ist dies der Fall, erscheint nun oben rechts im gestarteten Experiment ein „Results Link“ für „neu erstellte“ Experimente.

Ein Klick auf den „Results Link“ öffnet den entsprechenden Ergebnisbericht des Experiments. Jetzt kannst du entweder „Conversions“ oder „Revenue“ als Ziel hinzufügen, indem du auf „Add Shopify Goal“ klickst. Die Besucher des Ziels stammen aus GA4. Der Conversion-Wert und der Revenue-Wert kommen direkt von Shopify.

Wichtig: GA4-Daten werden erst nach ca. 24 Stunden aktualisiert. Die Daten von Shopify werden in Echtzeit aktualisiert.

Varify-Zielauswertung mit zwei Zielen: „Conversions“ verbessert sich um 9,49 % und ist signifikant, „Revenue“ um 9,73 % bei 94,28 % Konfidenz und ist es nicht

Produktfilter im Ergebnisbericht​

Analysiere die Performance bestimmter Produkte in deinem A/B-Test. Du kannst dir entweder einzelne Produkte oder Produktvarianten ansehen. Trage dazu eine Produkt-ID oder mehrere IDs, durch Kommas getrennt, in das Feld ein.