Shopify

A guide to implement Criteo solutions in Shopify.

Overview

This tutorial will explain how to install the Criteo OneTag on your Shopify site.

[[disclaimer-nonplugin]]

Feed

Create API Key

For Shopify integrations, the easiest and most stable method of pulling the product feed is directly through the Shopify API. You will need to create the keys needed for our server to pull the needed product data remotely.

  1. Click on Apps in the left slider 11

  2. Scroll to the bottom of the page and select "Manage private apps" 11

  3. Create a Private app 12

  4. Title the new app "Criteo Feed Pull" 13

  5. Copy ALL of the authentication information and send them to your Criteo Contact. 14

  6. The Feed Url will look like this {{domain}}/admin/products.json?limit=250&published_status=published

Tags

I do not have to comply with GDPR

Creating a new Snippet

  1. Mouse over to the left of the Shopify Admin Dashboard and click the Globe Icon
  2. The main theme is the first one displayed in your list of themes. Edit this HTML/CSS
  3. Now under the Snippets section in the left slider, click "Add a New Snippet" .
  4. Name the snippet 'criteo-tracking' with no quotes. Shopify will automatically append the extension '.liquid' to the snippet name.

Edit the new Snippet

  1. Copy the code provided into this new Snippet.
    The criteo-tracking snippet lives inside the Shopify platform and will remain static regardless of published theme.
  2. Your account ID in the criteo-tracking script should be: {{accountid}}
  3. Save the new Snippet
<script
    type="text/javascript"
    src="//dynamic.criteo.com/js/ld/ld.js?a={{accountid}}"
    async="true"
></script>
<script type="text/javascript">
    (function(){
      var deviceType = /iPad/.test(navigator.userAgent) ? "t" : /Mobile|iP(hone|od)|Android|BlackBerry|IEMobile|Silk/.test(navigator.userAgent) ? "m" : "d";
      window.criteo_q = window.criteo_q || [];
      window.criteo_q.push(
        { event: "setAccount", account: {{accountid}} },
        { event: "setEmail", email: "{{ customer.email | remove: ' ' | strip_newlines | downcase | sha256 }}", hash_method: "sha256" },
        { event: "setEmail", email: "{{ customer.email | remove: ' ' | strip_newlines | downcase | md5 }}", hash_method: "md5" },
        { event: "setSiteType", type: deviceType },
        { event: "setZipcode", zipcode: "{{ customer.default_address.zip }}" },
          {% assign template-type = template | split: '.' | first %}
          {% if template-type == 'cart' or template-type == 'collection' or template-type == 'index' or template-type == 'product' or template-type == 'search' %}
          {% unless template-type == 'cart' and cart.item_count < 1 %}
            {% case template-type %}
              {% when 'cart' %}
                { event: "viewBasket", ecpplugin: "shopify-gd",
                item: [
                  {% for item in cart.items %}
                  {% unless forloop.index0 == 0 %}, {% endunless %}
                    {
                      id: "{{ item.product.id }}",
                      price: (typeof({{ item.price }}) != "number") ? {{ item.price }} : ({{ item.price }}/100),
                      quantity: {{ item.quantity }}
                    }
                {% endfor %}]}
              {% when 'search' %}
                { event: "viewSearchResult", ecpplugin: "shopify-gd", keyword:"{{search.terms}}",
                item: [
                  {% for product in search.results limit:3 %}
                  {% if forloop.index0 != 0 %},{% endif %}
                    "{{product.id}}"
                  {% endfor %}]}
              {% when 'collection' %}
                { event: "viewList", ecpplugin: "shopify-gd", category:"{{collection.handle}}",
                item: [
                  {% for product in collection.products limit:3 %}
                  {% if forloop.index0 != 0 %},{% endif %}
                    "{{product.id}}"
                  {% endfor %}]}
              {% when 'index' %}
                { event: "viewHome", ecpplugin: "shopify-gd" }
              {% when 'product' %}
                { event: "viewItem", ecpplugin: "shopify-gd", item: "{{ product.id }}" }
            {% endcase %}
          {% endunless %}
          {% else %}
            { event: "viewPage" , ecpplugin: "shopify-gd" }
          {% endif %}
      );

      window.addEventListener('load', function() {
        // ajax request catching
        (function(open) {
          XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
            this.addEventListener("readystatechange", function() {
              if (this.readyState == 4 && this._url.includes("/add.js") && "{{ product.id }}" != "") {
                try {
                  $.getJSON("/cart.js", function(data) {
                    var basketData = [];
                    var item;
                    if (!data.items || !data.items.length) return;
                    for (var i = 0; i < data.items.length; i++) {
                      item = data.items[i];
                      if (item.product_id == "{{ product.id }}") {
                        basketData.push({
                          id: item.product_id,
                          price: (typeof(item.price) != "number") ? item.price : (item.price/100),
                          quantity: item.quantity
                        });
                      }
                    }
                    window.criteo_q.push({
                      event: "addToCart",
                      ecpplugin: "shopify-gd",
                      product: basketData
                    });
                  });
                } catch (err) {
                  // do nothing
                }
              }
            }, false);
            open.call(this, method, url, async, user, pass);
          };
        })(XMLHttpRequest.prototype.open);

        // fetch request catching
        const crtoMock = window.fetch;
        window.fetch = function() {
          return new Promise((resolve, reject) => {
            crtoMock.apply(this, arguments)
              .then((response) => {
                if(response.url.includes("/add.js") && response.type != "cors" && "{{ product.id }}" != ""){
                  try {
                    $.getJSON("/cart.js", function(data) {
                      var basketData = [];
                      var item;
                      if (!data.items || !data.items.length) return;
                      for (var i = 0; i < data.items.length; i++) {
                        item = data.items[i];
                        if (item.product_id == "{{ product.id }}") {
                          basketData.push({
                            id: item.product_id,
                            price: (typeof(item.price) != "number") ? item.price : (item.price/100),
                            quantity: item.quantity
                          });
                        }
                      }
                      window.criteo_q.push({
                        event: "addToCart",
                        ecpplugin: "shopify-gd",
                        product: basketData
                      });
                    });
                  } catch (err) {
                    // do nothing
                  }
                }
                resolve(response);
              })
              .catch((error) => {
                reject(error);
              })
          });
        }
      }, false);
    })();
</script>

Edit Theme

  1. Copy the provided include statement into theme.liquid
    this include statement must be manually added to theme.liquid of the live theme.
  2. Paste the include statement at the bottom of your theme.liquid (before the "body" and "html" tags)
  3. Save your theme.
<!----- YOU MUST INCLUDE THIS STATEMENT OR CRITEO TAGS WILL BREAK ---------->

{% include 'criteo-tracking' %}

<!----- YOU MUST INCLUDE THIS STATEMENT OR CRITEO TAGS WILL BREAK ---------->

Adding Conversion Script

  1. Click on Settings in left slider
  2. Click Checkout in the left slider
  3. In Additional content & Scripts paste the provided code at the very bottom
  4. Your account ID in the checkout script should be: {{accountid}}
<!-- CRITEO START -->
{% if first_time_accessed %}
<script
    type="text/javascript"
    src="//dynamic.criteo.com/js/ld/ld.js?a={{accountid}}"
    async="true"
></script>
<script type="text/javascript">
    var site_type = /iPad/.test(navigator.userAgent) ? "t" : /Mobile|iP(hone|od)|Android|BlackBerry|IEMobile|Silk/.test(navigator.userAgent) ? "m" : "d";
    var criteo_items = [];
    var crto_totalprice = "{{ order.subtotal_price | money_without_currency }}";
    if ( /((\,|\.)\d{2})$/g.test(crto_totalprice) ) {
        crto_totalprice = {{ order.subtotal_price | money_without_currency | remove: "," | remove: "." }}/100;
    } else {
        crto_totalprice = {{ order.subtotal_price | money_without_currency | remove: "," | remove: "." }};
    }
    criteo_items.push({id : "ordertotal", price : crto_totalprice, quantity : 1});
    {% for item in order.line_items %}
        criteo_items.push({id : "{{item.product.id}}",price : 0, quantity : 1});
    {% endfor %}
    window.criteo_q = window.criteo_q || [];
    window.criteo_q.push(
        { event: "setAccount", account: {{accountid}} },
        { event: "setSiteType", type: site_type },
        { event: "setEmail", email: "{{ customer.email | remove: ' ' | strip_newlines | downcase | sha256 }}", hash_method: "sha256" },
        { event: "setEmail", email: "{{ customer.email | remove: ' ' | strip_newlines | downcase | md5 }}", hash_method: "md5" },
        {
          event: "trackTransaction",
          ecpplugin: "shopify",
          zipcode: "{{order.shipping_address.zip}}",
          currency: "{{order.currency}}",
          id: "{{order.order_number}}",
          product: criteo_items
        });
</script>
{% endif %}
<!-- CRITEO END -->
I have to comply with GDPR

Creating a new Snippet

  1. Mouse over to the left of the Shopify Admin Dashboard and click the Globe Icon
  2. The main theme is the first one displayed in your list of themes. Edit this HTML/CSS
  3. Now under the Snippets section in the left slider, click "Add a New Snippet" .
  4. Name the snippet 'criteo-datalayer' with no quotes. Shopify will automatically append the extension '.liquid' to the snippet name.

Edit the new Snippet

  1. Copy the code provided into this new Snippet.
    The criteo-datalayer snippet lives inside the Shopify platform and will remain static regardless of published theme.
  2. Save the new Snippet
<script>
    {% assign template-type = template | split: '.' | first %}
    window.dataLayer = window.dataLayer || [];
    switch("{{ template-type }}") {
      case "index":
        // do nothing, the ee datalayer does not populate anything on the homepage
        break;
      case "product":
          dataLayer.push({
          'ecommerce': {
            'detail': {
              'currencyCode': '{{ shop.currency }}',
              'products': [
                {
                  'name': '{{ product.title }}',
                  'id': '{{ product.id }}',
                  'variant_id':  '{{ product.selected_or_first_available_variant.id }}',
                  'variant_sku':  '{{ product.selected_or_first_available_variant.sku }}',
                  'handle': '{{ product.handle}}',
                  'price': '{{ product.price | money_without_currency }}',
                  'availability': '{{ product.available}}',
                  'product_type': '{{ product.type }}',
                  'vendor': '{{ product.vendor }}',
                }
              ]
             }
           }
        });
        break;
      case "collection":
        dataLayer.push({
          'ecommerce': {
            'currencyCode': '{{ shop.currency }}',
            'impressions': [
              {% for product in collection.products %}
                {
                  'name': '{{ product.title }}',
                  'id': '{{ product.id }}',
                  'variant_id':  '{{ product.selected_or_first_available_variant.id }}',
                  'variant_sku':  '{{ product.selected_or_first_available_variant.sku }}',
                  'handle': '{{ product.handle}}',
                  'price': '{{ product.price | money_without_currency }}',
                  'availability': '{{ product.available}}',
                  'product_type': '{{ product.type }}',
                  'vendor': '{{ product.vendor }}',
                },
              {% endfor %}
            ]}
        });
        break;
      case "search":
        dataLayer.push({
          'ecommerce': {
            'currencyCode': '{{ shop.currency }}',
            'impressions': [
              {% for product in search.results %}
                {
                  'name': '{{ product.title }}',
                  'id': '{{ product.id }}',
                  'variant_id':  '{{ product.selected_or_first_available_variant.id }}',
                  'variant_sku':  '{{ product.selected_or_first_available_variant.sku }}',
                  'handle': '{{ product.handle}}',
                  'price': '{{ product.price | money_without_currency }}',
                  'availability': '{{ product.available}}',
                  'product_type': '{{ product.type }}',
                  'vendor': '{{ product.vendor }}',
                },
              {% endfor %}
            ],
          'keyword': '{{search.terms}}',
        }
        });
     break;
      case "cart":
          dataLayer.push({
          'event': 'checkout',
          'ecommerce': {
            'currencyCode': '{{ shop.currency }}',
            'checkout': {
              'products': [
                {% for item in cart.items %}
                    {
                    'name': '{{ item.product.title }}',
                    'id': '{{ item.product.id }}',
                    'sku': '{{ item.sku }}',
                    'variant_id':  '{{ item.product.selected_or_first_available_variant.id }}',
                    'variant_sku':  '{{ item.product.selected_or_first_available_variant.sku }}',
                    'handle': '{{ item.product.handle}}',
                    'price': '{{ item.variant.price | money_without_currency }}',
                    'quantity': '{{ item.quantity }}',
                    'availability': '{{ item.product.available}}',
                    'product_type': '{{ item.product.type }}',
                    'vendor': '{{ item.product.vendor }}',
                  },
                {% endfor %}
             ]
           }
         }
        });
        break;
      default:
        // do nothing since its not a page we can get info on
    }
</script>

Edit Theme

  1. Copy the provided include statement into theme.liquid
    this include statement must be manually added to theme.liquid of the live theme.
  2. Paste the include statement at the bottom of your theme.liquid (before the "body" and "html" tags)
  3. Save your theme.
<!----- YOU MUST INCLUDE THIS STATEMENT OR CRITEO TAGS WILL BREAK ---------->

{% include 'criteo-datalayer' %}

<!----- YOU MUST INCLUDE THIS STATEMENT OR CRITEO TAGS WILL BREAK ---------->

Adding Conversion Script

  1. Click on Settings in left slider
  2. Click Checkout in the left slider
  3. In Additional content & Scripts paste the provided code at the very bottom
{% if first_time_accessed %}
<script type="text/javascript">
    window.uid = window.uid || {};
    window.uid.email = '{{ customer.email | remove: " " | strip_newlines | downcase }}' || '';
    window.uid.email_sha256 = '{{ customer.email | remove: ' ' | strip_newlines | downcase | sha256 }}' || '';
    window.uid.email_md5 = '{{ customer.email | remove: " " | strip_newlines | downcase | md5 }}' || '';
    window.uid.zipcode = '{{ order.shipping_address.zip }}' || '';
    window.dataLayer = window.dataLayer || [];
    dataLayer.push({
      'ecommerce': {
        'purchase': {
                'currencyCode': '{{ order.currency }}',
          'actionField': {
            'id': '{{ order.order_number }}',
            'revenue': '{{ order.subtotal_price | money_without_currency }}',
            'tax': '{{ order.tax | money_without_currency}}',
            'shipping': '{{order.shipping_price | money_without_currency}}',
          },
          'products': [
            {% for item in order.line_items %}
              {
                'name': '{{ item.product.title }}',
                'id': '{{ item.product.id }}',
                'sku': '{{ item.sku }}',
                'variant_id':  '{{ item.product.selected_or_first_available_variant.id }}',
                'variant_sku':  '{{ item.product.selected_or_first_available_variant.sku }}',
                'handle': '{{ item.product.handle}}',
                'price': '{{ item.variant.price | money_without_currency }}',
                'quantity': '{{ item.quantity }}',
                    'availability': '{{ item.product.available}}',
                'product_type': '{{ item.product.type }}',
                'vendor': '{{ item.product.vendor }}',
              },
            {% endfor %}
          ],
          'user': {
            'email': '{{ customer.email | remove: " " | strip_newlines | downcase }}',
            'zip_code': '{{ order.shipping_address.zip }}'
          }
        }
      }
    });
</script>
{% endif %}

Upload the GTM container



1. Download the custom container for the Enhanced Ecommerce data layer



2. Import the container on your GTM


Instructions on how to import a container

3. Add the blocking trigger


By default, the solution above does not fully comply with GDPR. You will need to create a "blocking trigger" in GTM so our tags do not fire until a certain condition is met. This condition can be a JS variable, a cookie, or anything else that indicated the user accepted the cookie warning. Since this will be custom for your site, we will not go into detail but please reach out to your Criteo representative if you are at all confused and we can help.

Verification

[[accurate]]