Logo

Add tracker to one page checkout with custom pixels

How to add SlideRule's GTM tracker code to your single-page checkout or checkout extensibilities using custom pixels.

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.

Steps

  1. In your Shopify admin, navigate to "Settings" > "Custom events."
  2. Click Add custom pixel
  3. Name the SlideRule Google Tag Manager
  4. Add the code below and update the GTM-XXXXX with your GTM Container ID.
  5. When you are ready to go live with the new checkout click 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);
});