Additional product data in checkout_completed standard event

I’m working with a platform that needs additional product identifiers included for its pixel. Specifically, they need a GTIN (variant barcode) and an MPN (variant metafield).

The event.data available with the checkout_completed event contains a very limited set of product data.

Is there any way to access additional product data within a custom web pixel? Specifically, product tags, variant barcodes, and variant metafields.

Here’s a snippet that demonstrates what I’m trying to accomplish.

analytics.subscribe('checkout_completed', (event) => {

  // 1. Check product tags to filter line items.
  const lineItems = event.data.checkout.lineItems.filter((item) => {
    const product = item.variant.product;
    return product.tags.indexOf('tagName') > -1; // Tags not actually available
  });

  // 2. Gather important identifiers from line items.
  payload = lineItems.map((item) => {
    const variant = item.variant;
    return {
      sku: variant.sku,
      gtin: variant.barcode, // Barcode not actually available
      mpn: variant.metafields.custom.mpn // Metafields not actually available
    }
  });

  fetch('https://example.com/pixel', {
    method: 'POST',
    body: JSON.stringify(payload),
    keepalive: true,
  });
});

Could add it as line_item attributes when adding the products to cart. Should be able to access that in CheckoutLineItem object.

I like your outside-the-sandbox thinking. :wink:

Keeping that in our pocket as a last resort.

1 Like

That’s necessary working with Liquid, and customer requirements, on a daily basis :rofl:

I found a clean way to accomplish this by creating a Checkout UI extension for the Thank You page. :tada:

The extension broadcasts an analytics event with all the necessary line item data. Custom web pixels can listen for that event and do what they need.

It requires a custom app, but fortunately it’s extension-only app and can be hosted by Shopify. :raised_hands:

Component.js from the extension

import {extension} from "@shopify/ui-extensions/checkout";

export default extension("purchase.thank-you.block.render", (root, api) => {
  const {lines, query, analytics} = api;

  // Callback for GraphQL query.
  const handleQueryResult = function(result) {
    const customFields = {};

    result.data.nodes.forEach(node => {
      customFields[node.id] = {
        gtin: node.barcode,
        mpn: node.mpn?.value
      }
    });

    // Merge queried fields into line items.
    const lineItems = lines.current.map(line => {
      line.customFields = customFields[line.merchandise.id] || {};
      return line;
    });

    // Broadcast a custom event with all necessary data.
    analytics.publish("custom_checkout_completed", { lineItems });
  }

  // Query for fields that normally aren't available to web pixels.
  query(
    `query ($variantIds: [ID!]!) {
      nodes(ids: $variantIds) {
        ... on ProductVariant {
          id
          barcode
          mpn: metafield(namespace: "custom", key: "part_number") {
            value
          }
        }
      }
    }`,
    {
      variables: {
        variantIds: lines.current.map(line => line.merchandise.id)
      }
    },
  )
  .then(handleQueryResult)
  .catch(console.error);
});
1 Like

Great idea.

I’m usually a big advocate for “Liquid over JS”-solutions due to performance, however, specifically regarding this case of tracking, this makes way better sense than cluttering the line item properties and HTML with extra data.

You can still just pipe in customer hidden input fields along side your add-to-cart forms in liquid:

<input id="product-brand" type="hidden" type="text" name="properties[_brand]" value="{{product.metafields.custom.brand}}" form="{{ product_form_id }}">

note: the “_” before the property name makes it hidden on the front-end

it will output in checkout event like this:

lineItems[... 
"properties": [
                        {
                            "key": "_brand",
                            "value": "Brand-2"
                        }
                    ]
...