To install Google Tag Manager and track checkout correctly on Shopify's new one page checkout and/or checkout extensibilities, you must add custom pixels.
Add custom pixel
SlideRule Google Tag Manager
Connect
to enable the custom pixel.Do not not enable the custom pixel until you are ready to go live with the new checkout and the Order status page is disabled. You will have duplicates if both the Order status page and custom pixel are enabled.
Read our blog post on using custom pixels here.
If you need help, email us. We will request access to your store from our partner account and then you will have to grant us custom pixel access.
const loadGTM = async () => {
if (!window.dataLayer) {
const CONTAINER_ID = "GTM-XXXXX";
(function (w, d, s, l, i) {
w[l] = w[l] || [];
w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" });
var f = d.getElementsByTagName(s)[0],
j = d.createElement(s),
dl = l != "dataLayer" ? "&l=" + l : "";
j.async = true;
j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;
f.parentNode.insertBefore(j, f);
})(window, document, "script", "dataLayer", CONTAINER_ID);
window.dataLayer = window.dataLayer || [];
return await debugLog("GTM Loaded " + CONTAINER_ID);
} else {
return debugLog("GTM Already Loaded");
}
};
function formatLineItems(lineItems) {
return lineItems.map((lineItem) => {
const {
variant: {
id: item_id,
product: { id: item_product_id, title: item_name, vendor: item_brand },
price: { amount: price, currencyCode: currency },
sku: sku,
},
quantity,
} = lineItem;
return {
item_id: parseInt(item_id),
item_name,
affiliation: "Shopify Store",
currency,
item_brand,
item_product_id: parseInt(item_product_id),
price,
quantity,
sku,
};
});
}
function destructureAddress(address) {
return {
address1: address.address1,
address2: address.address2,
city: address.city,
country: address.country,
countryCode: address.countryCode,
province: address.province,
provinceCode: address.provinceCode,
zip: address.zip,
};
}
async function formatUserProperties(shopifyPixelPayload) {
const { data } = shopifyPixelPayload;
const { checkout } = data;
const { billingAddress, email, shippingAddress } = checkout;
const email_sha256 = await sha256(email);
const user_properties = {
billing_address: destructureAddress(billingAddress),
shipping_address: destructureAddress(shippingAddress),
default_address: destructureAddress(shippingAddress),
email,
email_sha256,
first_name: shippingAddress.firstName,
customer_id: checkout.order.customer.id,
last_name: shippingAddress.lastName,
name: `${shippingAddress.firstName} ${shippingAddress.lastName}`,
};
return user_properties;
}
async function sha256(message) {
const msgBuffer = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
return hashHex;
}
// Define the dataLayerPush function to abstract away direct calls to window.dataLayer.push and always add fired_from and fired_via parameters
function dataLayerPush(event, data) {
data.fired_from = "SlideRule Analytics";
data.fired_via = "Custom Pixels";
const dataLayer = window.dataLayer || [];
dataLayer.push({ event, ...data });
debugLog({ dataLayer: data });
}
async function pushEcommerceEvent(eventName, checkout, shopifyPixelPayload) {
const data = {
event: eventName,
ecommerce: {
currency: checkout.totalPrice.currencyCode,
value: checkout.totalPrice.amount,
items: formatLineItems(checkout.lineItems),
},
shopifY_pixel_payload: shopifyPixelPayload,
};
if (eventName !== "checkout_started") {
data.user_properties = await formatUserProperties(shopifyPixelPayload);
}
await dataLayerPush(eventName, data);
}
const debugLog = async (message) => {
// eslint-disable-next-line no-undef
const debug = await browser.cookie.get("_sra_debug");
if (debug) {
console.log("SlideRule Analytics - Custom Pixels:", message);
}
};
const eventHandler = async (
formattedEventName,
shopifyPixelPayload,
isEcommerce
) => {
await loadGTM();
debugLog({ shopifyPixelPayload });
const checkout = shopifyPixelPayload.data.checkout;
if (isEcommerce) {
pushEcommerceEvent(formattedEventName, checkout, shopifyPixelPayload);
}
};
// eslint-disable-next-line no-undef
analytics.subscribe("page_viewed", (shopifyPixelPayload) => {
if (
shopifyPixelPayload.context.document.location.pathname.includes("orders")
) {
debugLog({ shopifyPixelPayload });
loadGTM();
}
});
// eslint-disable-next-line no-undef
analytics.subscribe("checkout_started", (shopifyPixelPayload) => {
eventHandler("begin_checkout", shopifyPixelPayload, true);
});
// eslint-disable-next-line no-undef
analytics.subscribe("contact_info_submitted", (shopifyPixelPayload) => {
eventHandler("add_contact_info", shopifyPixelPayload, true);
});
// eslint-disable-next-line no-undef
analytics.subscribe(
"checkout_address_info_submitted",
(shopifyPixelPayload) => {
eventHandler("add_address_info", shopifyPixelPayload, true);
}
);
// eslint-disable-next-line no-undef
analytics.subscribe(
"checkout_shipping_info_submitted",
(shopifyPixelPayload) => {
eventHandler("add_shipping_info", shopifyPixelPayload, true);
}
);
// eslint-disable-next-line no-undef
analytics.subscribe("payment_info_submitted", (shopifyPixelPayload) => {
eventHandler("add_payment_info", shopifyPixelPayload, true);
});
// eslint-disable-next-line no-undef
analytics.subscribe("checkout_completed", async (shopifyPixelPayload) => {
eventHandler("purchase", shopifyPixelPayload, true);
});