Tracking in Shopify

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_")) return;
    console.log("ABlyft - Shopify pixel event received:", name);

    const snippetVersion = 2.00;
    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;
            if(sourceCurrency !== trackingCurrency) {
                try {
                    if (typeof Currency?.convert === "function") {
                        revenue = Currency.convert(rawAmount, sourceCurrency, trackingCurrency);
                        console.log(`ABlyft - Currency conversion successful: from ${sourceCurrency} ${rawAmount} to ${trackingCurrency} ${revenue}`);
                    } 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: revenue },
                { eventType:"custom",  eventName:"products-count", eventValue: count },
                { eventType:"custom",  eventName:"purchase" }
            ]);
        }

        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 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_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)