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.
-
Click on Apps in the left slider
-
Scroll to the bottom of the page and select "Manage private apps"
-
Create a Private app
-
Title the new app "Criteo Feed Pull"
-
Copy ALL of the authentication information and send them to your Criteo Contact.
-
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
- Mouse over to the left of the Shopify Admin Dashboard and click the Globe Icon
- The main theme is the first one displayed in your list of themes. Edit this HTML/CSS
- Now under the Snippets section in the left slider, click "Add a New Snippet" .
- Name the snippet 'criteo-tracking' with no quotes. Shopify will automatically append the extension '.liquid' to the snippet name.
Edit the new Snippet
-
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. - Your account ID in the criteo-tracking script should be: {{accountid}}
- 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
-
Copy the provided include statement into theme.liquid
this include statement must be manually added to theme.liquid of the live theme. - Paste the include statement at the bottom of your theme.liquid (before the "body" and "html" tags)
- 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
- Click on Settings in left slider
- Click Checkout in the left slider
- In Additional content & Scripts paste the provided code at the very bottom
- 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
- Mouse over to the left of the Shopify Admin Dashboard and click the Globe Icon
- The main theme is the first one displayed in your list of themes. Edit this HTML/CSS
- Now under the Snippets section in the left slider, click "Add a New Snippet" .
- Name the snippet 'criteo-datalayer' with no quotes. Shopify will automatically append the extension '.liquid' to the snippet name.
Edit the new Snippet
-
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. - 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
-
Copy the provided include statement into theme.liquid
this include statement must be manually added to theme.liquid of the live theme. - Paste the include statement at the bottom of your theme.liquid (before the "body" and "html" tags)
- 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
- Click on Settings in left slider
- Click Checkout in the left slider
- 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
- Select the option to merge, do not overwrite
- If there are conflicts, select the option to rename, do not overwrite
3. Add the blocking trigger
Because of the vast number of solutions out there to adhere with GDPR, we have left this section intentionally vague. We will only explain the logic you need to implement and you will need to add that logic into your GTM yourself.
Verification¶
[[accurate]]