> For the complete documentation index, see [llms.txt](https://helps.ptengine.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://helps.ptengine.com/en/start-guide/othertag/tag-shopify.md).

# Installing Tags in Shopify

## Installing Tags in Shopify

> For Shopify app integration setup, see also [Shopify Integration](https://gitlab.com/PtmindDev/ptx/ptengine-helps/-/tree/main/DOCS/en/start-guide/integrations/shopify.md).

## Basic Tag Installation

### 1. Find the theme.liquid template

In the Shopify admin dashboard, select "Online Store" → "Themes", then select "Edit code" from the "…" button

### 2. Install the Ptengine basic tag under

Please paste the Ptengine basic tag in the red box area on the right side of the image below.

#### **Example of Basic Tag**

```js
<!-- Ptengine Tag -->
<script src="https://js.ptengine.com/xxxxxxxx.js"></script>
<!-- End Ptengine Tag -->
```

> **DANGER**
>
> **⚠️ Caution**
>
> The xxxxxxxx part above is your project ID, which differs for each Ptengine project.
>
> Please replace it with your own project's ID.
>
> For more details, please refer to [this article](/en/start-guide/othertag/tag-shopify.md).

#### **How to Obtain the Ptengine Basic Tag**

## Event Installation

Simply installing the basic tag above is not enough to aggregate behavioral data such as **purchase completion and cart addition**, which are common on e-commerce sites.

Therefore, if you also want to check event data and conversion data on Ptengine, we also recommend setting up events.

### Implementing Event Collection Tags

#### 1. Select "Settings" in the Shopify Admin Dashboard

#### 2. Select "Customer events"

#### 3. Set a Pixel Name

Click the black "Add custom pixel" button in the top right, and set the pixel name in the popup. (In the figure below, we have set it to "pt tracking on checkouts" as an example)

Then click "Add pixel".

#### 4. Save

Paste the code below in the Code section, and save by clicking "save" in the top right.

> **DANGER**
>
> **⚠️ Caution**
>
> **Be sure to replace** `const sid` **with your own Ptengine project ID!!!**

```js
// Code to paste
function trackEvent(eventType, eventProperties, cb) {
  const sid = "Enter your profile ID";
  // Enter your project ID!
  const area = "jp";
  const options = eventProperties || {};

  loadPtengineScript(sid, area)
    .then(() => {
      console.log("Ptengine script loaded successfully");
      window.ptengine && window.ptengine.track(eventType, options);
      cb && typeof cb === "function" && cb();
    })
    .catch((error) => {
      console.error("Ptengine script loading failed:", error);
    });
}

function loadPtengineScript(sid, area) {
  return new Promise((resolve, reject) => {
    if (window.ptengine) {
      // Resolve immediately if ptengine already exists
      resolve();
    } else {
      // Support both /wpm@.../web-pixel-... and /web-pixels@.../custom/web-pixel-... sandbox paths
      const url = window.location.href.replace(
        /\/(?:wpm|web-pixels)@[^/]+\/(?:custom\/)?web-pixel-[^/]+\/sandbox\/modern/,
        ""
      );
      window._pt_sp_2 = [];
      _pt_sp_2.push(`setAccount, ${sid}`);
      _pt_sp_2.push(`setPVTag,${url},replace`);

      const script = document.createElement("script");
      const isCheckoutPage = window.location.href.includes("checkouts");
      const sandboxQuery = isCheckoutPage ? "" : "?sandbox";
      script.src = `https://js.ptengine.${area}/${sid}.js${sandboxQuery}`;
      script.onload = () => resolve();
      script.onerror = () => reject(new Error("Script loading failed"));
      document.head.appendChild(script);
    }
  });
}

// Remove empty properties to avoid sending null/undefined/'' fields
function removeEmptyKeys(obj) {
  for (let key in obj) {
    if (
      obj.hasOwnProperty(key) &&
      (obj[key] === null || obj[key] === undefined || obj[key] === "")
    ) {
      delete obj[key];
    }
  }
  return obj;
}

// Order completed: report checkout_completed only once
analytics.subscribe("checkout_completed", (event) => {
  const checkout = event?.data?.checkout;
  if (!checkout) return;

  // Extract the numeric ID from the order customer ID (gid://shopify/Customer/xxx) as uid
  const rawCustomerId = checkout?.order?.customer?.id;
  const numericCustomerId = rawCustomerId ? rawCustomerId.split("/").pop() : null;
  const uid = numericCustomerId;
  const totalPrice = checkout?.totalPrice?.amount;
  const currencyCode = checkout?.totalPrice?.currencyCode;
  const orderId = checkout?.order?.id;
  const items = checkout?.lineItems || [];

  // Collect line items into an array and send with the same event
  const products = items.map((item) =>
    removeEmptyKeys({
      sku: item?.variant?.sku,
      name: item?.title,
      spu: item?.variant?.product?.type,
      id: item?.id,
      quantity: item?.quantity,
      price: item?.finalLinePrice?.amount,
    })
  );

  // 1) identify: once per order, write order info to the user profile
  if (uid) {
    window.ptengine &&
      ptengine.identify(uid, {
        totalPrice: totalPrice,
        totalorderid: orderId,
      });
  }

  // 2) track: send checkout_completed only once
  const eventProperties = removeEmptyKeys({
    totalPrice: totalPrice,
    currencyCode: currencyCode,
    totalorderid: orderId,
    products: products,
  });
  trackEvent("checkout_completed", eventProperties);
});

analytics.subscribe("search_submitted", (event) => {
  const eventProperties = removeEmptyKeys({
    keyword: event?.data?.searchResult?.query,
    resultCount: event?.data?.searchResult?.productVariants?.length,
  });
  trackEvent("search_submitted", eventProperties);
});

analytics.subscribe("collection_viewed", (event) => {
  const eventProperties = removeEmptyKeys({
    id: event?.data?.collection?.id,
    name: event?.data?.collection?.title,
    productCount: event?.data?.collection?.productVariants?.length,
  });
  trackEvent("collection_viewed", eventProperties);
});

analytics.subscribe("product_viewed", (event) => {
  const origin = event?.context?.window?.origin || window.location.origin;
  const variant = event?.data?.productVariant;
  const eventProperties = removeEmptyKeys({
    sku: variant?.sku,
    name: variant?.product?.title,
    type: variant?.product?.type,
    spu: variant?.product?.type,
    price: variant?.price?.amount,
    currencyCode: variant?.price?.currencyCode,
    url: variant?.product?.url ? `${origin}${variant.product.url}` : undefined,
    imageUrl: variant?.image?.src,
  });
  trackEvent("product_viewed", eventProperties);
});

analytics.subscribe("product_added_to_cart", (event) => {
  const origin = event?.context?.window?.origin || window.location.origin;
  const merchandise = event?.data?.cartLine?.merchandise;
  const eventProperties = removeEmptyKeys({
    url: merchandise?.product?.url ? `${origin}${merchandise.product.url}` : undefined,
    name: merchandise?.product?.title,
    sku: merchandise?.sku,
    quantity: event?.data?.cartLine?.quantity,
    price: merchandise?.price?.amount,
    currencyCode: merchandise?.price?.currencyCode,
    spu: merchandise?.product?.type,
  });
  trackEvent("product_added_to_cart", eventProperties);
});

analytics.subscribe("product_removed_from_cart", (event) => {
  const origin = event?.context?.window?.origin || window.location.origin;
  const merchandise = event?.data?.cartLine?.merchandise;
  const eventProperties = removeEmptyKeys({
    url: merchandise?.product?.url ? `${origin}${merchandise.product.url}` : undefined,
    name: merchandise?.product?.title,
    sku: merchandise?.sku,
    quantity: event?.data?.cartLine?.quantity,
    price: merchandise?.price?.amount,
    currencyCode: merchandise?.price?.currencyCode,
    spu: merchandise?.product?.type,
  });
  trackEvent("product_removed_from_cart", eventProperties);
});

analytics.subscribe("cart_viewed", (event) => {
  const cart = event?.data?.cart;
  if (!cart) {
    trackEvent("cart_viewed");
    return;
  }
  const origin = event?.context?.window?.origin || window.location.origin;
  const lines = cart?.lines || [];
  if (lines.length) {
    lines.forEach((line) => {
      const merchandise = line?.merchandise;
      const eventProperties = removeEmptyKeys({
        totalQuantity: cart?.totalQuantity,
        url: merchandise?.product?.url ? `${origin}${merchandise.product.url}` : undefined,
        name: merchandise?.product?.title,
        sku: merchandise?.sku,
        spu: merchandise?.product?.type,
        quantity: line?.quantity,
        price: line?.cost?.totalAmount?.amount,
        currencyCode: line?.cost?.totalAmount?.currencyCode,
      });
      trackEvent("cart_viewed", eventProperties);
    });
  } else {
    trackEvent("cart_viewed", removeEmptyKeys({ totalQuantity: cart?.totalQuantity }));
  }
});

analytics.subscribe("checkout_started", (event) => {
  const checkout = event?.data?.checkout;
  const origin = event?.context?.window?.origin || window.location.origin;
  const checkoutItems = checkout?.lineItems;
  const totalPrice = checkout?.subtotalPrice?.amount;
  const currencyCode = checkout?.subtotalPrice?.currencyCode;

  if (checkoutItems && checkoutItems.length) {
    checkoutItems.forEach((item) => {
      const eventProperties = removeEmptyKeys({
        totalPrice: totalPrice,
        currencyCode: currencyCode,
        url: item?.variant?.product?.url ? `${origin}${item.variant.product.url}` : undefined,
        name: item?.variant?.product?.title,
        sku: item?.variant?.sku,
        spu: item?.variant?.product?.type,
        quantity: item?.quantity,
        price: item?.finalLinePrice?.amount,
      });
      trackEvent("checkout_started", eventProperties);
    });
  } else {
    trackEvent("checkout_started", removeEmptyKeys({ totalPrice, currencyCode }));
  }
});

analytics.subscribe("checkout_contact_info_submitted", () => {
  trackEvent("checkout_contact_info_submitted");
});

analytics.subscribe("checkout_address_info_submitted", () => {
  trackEvent("checkout_address_info_submitted");
});

analytics.subscribe("checkout_shipping_info_submitted", () => {
  trackEvent("checkout_shipping_info_submitted");
});

analytics.subscribe("payment_info_submitted", (event) => {
  const checkout = event?.data?.checkout;
  const eventProperties = removeEmptyKeys({
    totalPrice: checkout?.subtotalPrice?.amount,
    currencyCode: checkout?.subtotalPrice?.currencyCode,
  });
  trackEvent("payment_info_submitted", eventProperties);
});

```

#### 5. Publish

Finally, click the "Connect" button in the top right to publish the code set in Step 4.

### About Event Tag Firing Timing

## Important Notes

When using Ptengine with Shopify, please be sure to check the following points.

### **1. Please install the Ptengine basic tag directly in "Shopify themes"**

Please install the Ptengine basic tag directly in Shopify themes (theme.liquid).

Installing via Google Tag Manager may not allow event measurement due to Shopify's specifications.

Therefore, we do not recommend installing through tag managers such as GTM, and it is outside the scope of support.

### **2. Do not add page-by-page transmission restrictions or conditions**

We do not recommend modifying the Ptengine basic script to fire only on specific pages or restricting firing based on URL conditions.

Please understand that we cannot investigate the cause or provide support for any data measurement issues that occur in such cases.

### **3. If you change the theme, you need to reinstall the tags**

In Shopify, when you change a theme, the contents of theme.liquid are reset.

Therefore, if you switch themes or apply a new theme, please reinstall the Ptengine basic tag in the new theme as well.

Please note that if you do not reinstall the tag, Ptengine measurement will not be performed.
