Tracking in Shopify (Customer Event)

If you want to use the revenue tracking goal (and others) within a Shopify project, you can use the following code.
Just use it in Shopify as a Customer event.

More info from Shopify's side here: https://help.shopify.com/en/manual/promoting-marketing/pixels

On this page

How to set it up

Please follow the following steps:

  1. Go to Settings > Customer Pixels
  2. Click Add Custom Pixel > Name it "ABlyft Tracking" (for example)
  3. Edit to Project ID within the code (important!)
  4. Edit the eventName on each goal if they differ from the API names in the platform
  5. Add pixel > Save > Connect it

Please mind If you want to track any purchase (and other events) check the "Customer privacy" settings.

shopify-customer-event-costumer-privacy

Code to copy

/*
IMPORTANT: Edit the Project ID
*/

const projectId = 123456789; // ← Your project id

//Further options (do not change if not necessary)
const trackingCurrency  = "EUR"; // ← currency you want to track in
const maxOrderValueFallback = false; // ← if a number (i.e., 250.50) is set, a revenue is capped to that

analytics.subscribe("all_events", async ({ name, data, context }) => {
    if (!(name.startsWith("checkout_") || name === "product_added_to_cart")) {
        return;
    }
    console.log("ABlyft - Shopify pixel event received:", name);

    const snippetVersion = 2.10;
    const d = document;
    const w = window;

    w.insideShopifyPixel = true;
    w.ablyftAllowIframe = true;
    w.shopifyEventContext = context;
    let j = d.createElement("script");
    j.async = true;
    j.src = `https://cdn.ablyft.com/s/${projectId}.js`;

    j.onload = async () => {
        const url = context.window.location.href;
        const { checkout } = data;
        let events = [{ eventType:"custom", eventName:`shopify_event_${name}`}];

        //currency conversion
        try {
            await new Promise((resolve, reject) => {
                const timeout = setTimeout(() => {
                    reject(new Error("ABlyft - Timeout loading currency script"));
                }, 1000); // 1 sec timeout
                const fxScript = document.createElement("script");
                fxScript.src = "https://cdn.shopify.com/s/javascripts/currencies.js";
                fxScript.onload = () => {
                    clearTimeout(timeout);
                    if (typeof Currency?.convert === "function") {
                        resolve();
                    } else {
                        reject(new Error("ABlyft - Currency.convert impossible"));
                    }
                };
                fxScript.onerror = () => {
                    clearTimeout(timeout);
                    reject(new Error("ABlyft - Currency script could not be loaded"));
                };
                document.head.appendChild(fxScript);
            });
        } catch (error) {
            console.warn("ABlyft - Currency conversion not available:", error);
        }

        //event handling
        if(name === "checkout_completed"){
            const { amount: rawAmount, currencyCode: sourceCurrency } = checkout.totalPrice;
            const count = checkout.lineItems.reduce((sum, {quantity}) => sum + quantity, 0);
            let revenue = rawAmount;
            let conversionFactor = 1;
            if(sourceCurrency !== trackingCurrency) {
                try {
                    if (typeof Currency?.convert === "function") {
                        revenue = Currency.convert(rawAmount, sourceCurrency, trackingCurrency);
                        conversionFactor = revenue / rawAmount;
                        console.log(`ABlyft - Currency conversion successful: from ${sourceCurrency} ${rawAmount} to ${trackingCurrency} ${parseFloat(revenue.toFixed(2))}`);
                    } else {
                        throw new Error("ABlyft - Currency.convert not available");
                    }
                } catch (e) {
                    console.warn("ABlyft - Currency conversion failed – using rawAmount", e);
                    revenue = rawAmount;
                }
            }
            if(typeof maxOrderValueFallback === "number"){
                if(revenue > maxOrderValueFallback) revenue = maxOrderValueFallback;
            }
            //use your own eventNames here
            events = events.concat([
                { eventType:"revenue", eventName:"revenue", eventValue: parseFloat(revenue.toFixed(2)) },
                { eventType:"custom",  eventName:"products-count", eventValue: count },
                { eventType:"custom",  eventName:"purchase" }
            ]);
            //custom product-related goals
            let lineItemsMap = {};
            checkout.lineItems.forEach(function(item) {
                const productId = item.variant?.product?.id || item.id;
                const revenue = item.finalLinePrice.amount;
                if (!lineItemsMap[productId]) {
                    lineItemsMap[productId] = { productId, revenue: 0 };
                }
                lineItemsMap[productId].revenue += revenue;
            });
            let lineItemsArray = Object.values(lineItemsMap);
            lineItemsArray.forEach(function(item) {
                events.push({ eventType: "revenue", eventName: "revenue-product-" + item.productId, eventValue: parseFloat((item.revenue*conversionFactor).toFixed(2)) });
                events.push({ eventType: "custom", eventName: "purchase-product-" + item.productId });
            });
        }
        console.log("ABlyft - Events to send:", events);

        let userData = await browser.cookie.get("ablyft_uvs");
        if(!userData){
            userData = await browser.localStorage.getItem("ablyft_uvs");
        }

        const user = JSON.parse(userData);
        let visitor_type = "new";
        if (user && user.sessions && user.sessions > 1) {
            visitor_type = "returning";
        }

        const attributes = {
            device_type: w.ablyft.get("deviceType"),
            visitor_type,
            url,
            user_agent: w.navigator.userAgent
        };

        let experimentsBucketData = await browser.cookie.get("ablyft_exps")
        if(!experimentsBucketData){
            experimentsBucketData = await browser.localStorage.getItem("ablyft_exps");
        }
        if(!experimentsBucketData){
            console.log("ABlyft - Empty user bucketing - aborting");
            return;
        }
        const experimentsBucket = JSON.parse(experimentsBucketData);

        const experimentIds = Object.keys(experimentsBucket);
        let variationIds = [];
        for (let i = 0; i < experimentIds.length; i++) { variationIds.push(experimentsBucket[experimentIds[i]]); }

        function createLockId() {
            const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            let text = "";
            for (let i = 0; i < 10; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); }
            return text;
        }

        const goals = w.ablyft.get("goals");

        const postObj = {
            projectId: projectId,
            source: "shopify-webpixel",
            snippetVersion,
            user: {
                attributes,
                experimentsBucket
            },
            events: events.filter(event => goals.find(goal => goal.api_name === event.eventName)).map(event => {
                const goalObject = goals.find(goal => goal.api_name === event.eventName);
                return {
                    type: event.eventType,
                    value: event.eventValue,
                    timestamp: Math.floor(Date.now() / 1000),
                    locked: Math.floor(Date.now() / 1000),
                    lockId: createLockId(),
                    goal: {
                        id: goalObject.id,
                        variations: variationIds
                    }
                };
            })
        }

        if(postObj.events.length === 0) return;
        await sendWithRetry(postObj);

        async function sendWithRetry(data, maxRetries = 3) {
            const payload = JSON.stringify(data);
            for (let attempt = 1; attempt <= maxRetries; attempt++) {
                try {
                    const response = await fetch("https://log.ablyft.com", {
                        method: "POST",
                        headers: {
                            "Content-Type": "application/json"
                        },
                        body: payload
                    });
                    if (response.ok) {
                        console.log(`ABlyft - Request successful on attempt ${attempt}`);
                        return true;
                    } else if (response.status >= 400 && response.status < 500) {
                        console.warn(`ABlyft - Client error (${response.status}) – no retry`);
                        return false;
                    } else {
                        console.warn(`ABlyft - Server error (${response.status}) – retrying...`);
                    }
                } catch (error) {
                    console.warn(`ABlyft - Network error on attempt ${attempt}:`, error);
                }
                if (attempt < maxRetries) {
                    await new Promise((res) => setTimeout(res, 500)); //delay in ms
                }
            }

            console.error("ABlyft - All retry attempts failed.");
            return false;
        }
    };

    d.head.appendChild(j);
});

List of other events

You can use this goals by creating a custom goal and taking care that one of the following events is used as the goal's api name:

  • shopify_event_product_added_to_cart
  • shopify_event_checkout_started
  • shopify_event_checkout_shipping_info_submitted
  • shopify_event_checkout_contact_info_submitted
  • shopify_event_checkout_address_info_submitted
  • shopify_event_checkout_completed (or purchase or revenue)

Product-Level Events

ABlyft automatically provides two event types that allow you to measure performance on a per-product level:

  • revenue-product-[productId]
  • purchase-product-[productId]

To track a specific product, simply create an event using the product’s Shopify product ID in the placeholder. For example:

revenue-product-1234567890

This event will capture the revenue generated by the product with ID 1234567890.

Please note that the aggregation always happens on the product level, not the variant level. If a product has multiple variants, all associated revenues are summed up and reported under the same product ID. The same principle applies to the purchase-product-[productId] event, which counts the number of purchases per product.