Note: This document is being actively edited with a v2 of the pixel that allows for users on multiple subdomains.
Webtrends Optimize has the ability to collect numerous events from Shopify:
Events we can collect
checkout_completed - we rename this to "purchase", and tag along revenue and units as Custom Data to this event.
page_viewed - we do not collect this by default
If you would prefer that we change what we capture, there are options to customise this in the code provided below.
Unless stated otherwise, we do not rename events to avoid confusion between ourselves and Shopify.
Preparation
If you are using cookies instead of localstorage to preserve test decisions, you will need to add this code to your preinit script:
WT.addEventHandler("pageview", function(e){
var ta = e.target.params.testAlias;
var curCookie = WT.helpers.cookie.get("_wt.entrieslist") || ""; curCookie = curCookie.split(",");
if(curCookie.includes(ta)) return;
curCookie.push(ta);
curCookie = curCookie.join(",");
WT.helpers.cookie.set("_wt.entrieslist", curCookie, 14);
});
Shopify pixels don't let you read all cookies through document.cookie, and so we use this _wt.entrieslist cookie to keep track of what to fetch.
Required values
You will need your domain ID and key token values.
You can find these in your Account Settings area
Or, if you're on your website, you can paste this code into the console:
let { s_keyToken: token, s_domainKey: domain } = WT.optimizeModule.prototype.wtConfigObj;
console.log({token, domain});
Installation
At the top of the code provided below, please update the domain and token in the relevant areas. Other than this, you can copy/paste the code as detailed below.
Pixel code
analytics.subscribe("all_standard_events", async event => {
// ---- Configure this area :: START ----
let domain = '1440308';
let keyToken = "7a0a8366623f580210fc847c6f69c3b0912b04a2bd39";
let eventsToIgnore = "page_viewed"; // comma seperated
// ---- Configure this area :: END ----
const encoder = { strToLongs: function (s) { var l = new Array(Math.ceil(s.length / 4)); for (var i = 0; i < l.length; ++i) { l[i] = s.charCodeAt(i * 4) + (s.charCodeAt(i * 4 + 1) << 8) + (s.charCodeAt(i * 4 + 2) << 16) + (s.charCodeAt(i * 4 + 3) << 24) } return l }, longsToStr: function (l) { var a = new Array(l.length); for (var i = 0; i < l.length; ++i) { a[i] = String.fromCharCode(l[i] & 255, l[i] >>> 8 & 255, l[i] >>> 16 & 255, l[i] >>> 24 & 255) } return a.join("") }, encryptTea: function (plaintext, password) { if (plaintext.length === 0) return ""; var v = this.strToLongs(this.encode_utf8(plaintext)); if (v.length <= 1) v[1] = 0; var k = this.strToLongs(this.encode_utf8(password).slice(0, 16)); var n = v.length; var z = v[n - 1], y = v[0], delta = 2654435769; var mx, e, q = Math.floor(6 + 52 / n), sum = 0; while (q-- > 0) { sum += delta; e = sum >>> 2 & 3; for (var p = 0; p < n; ++p) { y = v[(p + 1) % n]; mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z); z = v[p] += mx } } var ciphertext = this.longsToStr(v); return "WT3" + this.encode_base64(ciphertext) }, base64Code: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_~", base64Pad: "~", encode_base64: function (str, utf8encode) { utf8encode = typeof utf8encode == "undefined" ? false : utf8encode; var o1, o2, o3, bits, h1, h2, h3, h4, e = [], pad = "", c, plain, coded; var b64 = this.base64Code; plain = utf8encode ? this.encode_utf8(str) : str; c = plain.length % 3; if (c > 0) { while (c++ < 3) { pad += this.base64Pad; plain += "\0" } } for (c = 0; c < plain.length; c += 3) { o1 = plain.charCodeAt(c); o2 = plain.charCodeAt(c + 1); o3 = plain.charCodeAt(c + 2); bits = o1 << 16 | o2 << 8 | o3; h1 = bits >> 18 & 63; h2 = bits >> 12 & 63; h3 = bits >> 6 & 63; h4 = bits & 63; e[c / 3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4) } coded = e.join(""); coded = coded.slice(0, coded.length - pad.length) + pad; return coded }, encode_utf8: function (strUnicode) { var strUtf = strUnicode.replace(/[\u0080-\u07ff]/g, function (c) { var cc = c.charCodeAt(0); return String.fromCharCode(192 | cc >> 6, 128 | cc & 63) }); strUtf = strUtf.replace(/[\u0800-\uffff]/g, function (c) { var cc = c.charCodeAt(0); return String.fromCharCode(224 | cc >> 12, 128 | cc >> 6 & 63, 128 | cc & 63) }); return strUtf } };
function projectToControl(cookieVal, keyToken) {
var v = decodeURIComponent(cookieVal).split('||');
var ids = v[1].split('-').reverse();
var testid = ids.pop();
var runid = ids.pop();
var trackid = ids.reverse().join('-');
var value = {
"runid": runid,
"testGroup": "default",
"testid": testid,
"throttleResult": v[3],
"trackid": trackid,
"typeid": parseInt(v[0])
};
if (v[2] !== 'undefined') value.pageTrack = v[2] === 'true';
if (v[4] && v[4] !== 'undefined') value.targetData = JSON.parse(v[4]);
if (v[5] !== 'undefined') value.throttleRunVersion = v[5];
if (v[6] !== 'undefined') value.throttleTestVersion = v[5];
value = JSON.stringify(value);
var encvalue = encoder.encryptTea(value, keyToken);
return encvalue;
}
let name = event.name;
eventsToIgnore = eventsToIgnore.split(',');
if(eventsToIgnore.includes(event.name)) return;
let projectCookies = {};
let taList = [];
var len = await browser.localStorage.length();
for(let i=0; i<len; i++){
let o = await browser.localStorage.key(i);
if(o.match(/_wt\./i)){
let val = await browser.localStorage.getItem(o);
projectCookies[o] = val;
taList.push( o.replace(new RegExp(`_wt.(control|project)-${domain}-`, 'i'), '') );
}
if(!o) break;
}
let wto_entrylistcookie = await browser.cookie.get('_wt.entrieslist');
if(wto_entrylistcookie && wto_entrylistcookie.length){
wto_entrylistcookie = wto_entrylistcookie.split(',');
for(let ta of wto_entrylistcookie){
if(!ta) continue;
let cookieoptions = [
`_wt.control-${domain}-${ta}`,
`_wt.project-${domain}-${ta}`
];
for(let cookiename of cookieoptions){
let cookieval = await browser.cookie.get(cookiename);
if(cookieval && cookieval.length){
taList.push( ta );
if(cookiename.match(/_wt.project/i)){
cookieval = projectToControl(cookieval, keyToken);
}
projectCookies[cookiename.replace(/_wt.project/i, '_wt.control')] = cookieval;
}
}
}
}
if(!taList.length){
return; // no assignments, nothing to convert on.
}
let standardCookies = {};
let usercookie = await browser.cookie.get('_wt.user-'+domain);
if(usercookie) standardCookies['_wt.user-'+domain] = usercookie;
let modecookie = await browser.cookie.get('_wt.mode-'+domain);
if(modecookie) standardCookies['_wt.mode-'+domain] = modecookie;
if(!usercookie || !modecookie){
return;
}
let data = {};
if(name == "checkout_completed"){
name = "purchase";
data.revenue = event.data?.checkout?.totalPrice?.amount || undefined;
data.units = event.data?.checkout?.lineItems?.length || 1;
}
var WTO_CTrack2 = function(params) {
var OTS_ACTION = params.conversionPoint ? 'track' : 'control'; var OTS_GUID = domain + (params.testAlias ? '-' + params.testAlias : ''); var useTestGroupShared = false;
var OTS_URL_POST = 'https://ots.webtrends-optimize.com/ots/api/rest-1.2/' + OTS_ACTION + '/' + OTS_GUID;
var aCookies = {};
for(let key in params.cookies){
aCookies[key] = { value: params.cookies[key] };
}
var qpURL = '\x26url\x3d' + params.URL;
var qpConversion = '\x26conversionPoint\x3d' + params.conversionPoint || '';
var qpData = params.data ? '\x26data\x3d' + JSON.stringify(params.data) : '';
var offsetValue = function() {
var rightNow = new Date;
var offset = -rightNow.getTimezoneOffset() / 60; return offset * 1E3 * 60 * 60;
};
var offset = '\x26_wm_TimeOffset\x3d' + offsetValue();
var referrer = '\x26_wm_referer\x3d' + params.referrer;
var rqElSrc = 'keyToken\x3d' + keyToken + '\x26preprocessed\x3dtrue\x26_wt.encrypted\x3dtrue\x26testGroup\x3d' + (useTestGroupShared ? 'shared' : 'default') + qpURL + '\x26cookies\x3d' + JSON.stringify(aCookies) + qpConversion + qpData + offset + referrer;
if(navigator.sendBeacon && window.Blob) {
var blob = new Blob([rqElSrc], {type: 'application/x-www-form-urlencoded'});
navigator.sendBeacon(OTS_URL_POST, blob);
} else {
var xhttp = new XMLHttpRequest();
xhttp.open('POST', OTS_URL_POST, true); xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhttp.send(rqElSrc);
}
};
let url = encodeURIComponent(event.context.window.location.href);
let reqbody = {
conversionPoint: name,
testAlias: taList.join(','),
cookies: {
...standardCookies,
...projectCookies
},
URL: url,
referrer: url,
data
};
WTO_CTrack2(reqbody);
});
How to install
Head over to Settings > customer events in your admin area
That should get you to this screen:
Click "Add custom pixel" and give it a relevant name like Webtrends Optimize
Next, apply the configuration to the pixel as you feel appopriate, e.g.:
Note that Webtrends Optimize does not resell your data, nor pay for it, and so it should be a safe option to select.
Finally, find the code section just below it, which will look like:
Paste in the code above, having updated your domain and keytoken. It should look like this:
Once you're happy, click Save in the bar at the top:
That's it. All being well, you should start tracking metrics from this point onwards.
How long does it take to get working?
We have seen it take upto 1 minute for our changes to take effect, but often it's under 5 seconds.
If you don't see it take effect immediately. we recommend hard-refreshes (Ctrl+Shift+R / Cmd+Shift+R) for a minute or so, and it should come through.
Final setup in Webtrends Optimize
We recommend adding the defaults to your Automatic tracking lists in Webtrends Optimize:
If you do so:
All of these metrics will show up when building tests to remind you we're capturing that information.
All Custom Data fields will be automatically attached to your tests, so you don't need to add them manually.