Client-Side Tracking with Google Tag Manager

How does it work?

When a user clicks on one of your products on moebel.de, meubles.fr, meubelo.nl, moebel24.at, moebel24.ch, or mobi24.es, our server generates a unique clickID (moeclid) which is appended as a query parameter to your landingpage URL. With the integration of our client-side script, the clickID is written into the local storage for 90 days. Our server does not get any requests from you unless the user converts.

If the user makes a purchase on your site and has an existing moeclid value in the local storage of the browser, a conversion event is sent to our server including the clickID and further purchase-related data.

Prerequisites

Before you proceed with this integration, make sure the following conditions are met:

  • You have received your Partner ID or dedicated Sales Tracking Key.

  • You have set up a Google Tag Manager web container.

  • Your have configured UA/GA4 eCommerce tracking.

Integration

The client-side salestracking for Google Tag Manager consists of two tags - the base code tag and the conversion tag.

1. Base Code

The base code tag writes the appended moeclid parameter value of the landing page URL into the local storage of the browser. The trigger of this tag is configured in a way that the tag only fires if the user reached your site through one of our portals. It doesn’t affect your traffic from other marketing channels.

  1. Add a new tag and select “Custom HTML” as tag type

  2. Copy the code snippet of the initialization script and paste it. This script is same for all portals.

cfc8764f 52bc 4a4e a646 1cab4801dc26

Initialization Script per Portal

All Portals / Countries

<script>
"use strict";

!function(){"use strict";var t;JSON.parse({}.VITE_ENDPOINTS||"{}"),(t=function(){var t=window.location.search.replace("?","").split("&").find((function(t){return t.startsWith("moeclid")}));return t&&t.split("=")[1]||null}())&&function(t){var n={date:(new Date).toISOString(),clickId:t};localStorage.setItem("MOEBEL_CLICKOUT_ID",JSON.stringify(n))}(t)}();
</script>
  1. Create a new “Page View” trigger where the URL of the page contains “moeclid=”. This way, the base code tag is only fired when a user reached your site through a moebel.de product click.

26beab09 b30a 4f8d a8da 4a9e32566446
  1. Add the trigger to the tag and save the tag.

Since local storage cannot be shared across domains/subdomains, saving the clickID (moeclid) to use at checkout will not work if the actual checkout page is on a different domain/subdomain than the product page the user was sent from us.

2. Conversion

The conversion tag enables sending us a purchase event at the time the user converts. If there is no clickID from the initialization script in the local storage of the browser, calling sales function will do nothing. Moreover, this script removes obsolete clickIDs (older than 90 days) from local storage.

  1. Add a new tag and select “Custom HTML” as tag type

  2. Copy the code snippet of the conversion script and paste it. Please be aware that the MARKET variable in script must be adjusted for your market. (moebel.de - de, meubles.fr - fr, moebel24.at - at, moebel24.ch - ch, mobi24.es - es)

21ff6680 32b7 4247 98f9 06ab70d5e9af

Conversion Script per Portal

All Portals / Countries

<script>
"use strict";
var PARTNER_KEY = "INSERT-YOUR-PARTNER-KEY";  // REPLACE ME
var MARKET = "de"; // REPLACE ME "de","fr","nl","at","ch","es"

function _regeneratorRuntime(){/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */_regeneratorRuntime=function(){return t};var t={},e=Object.prototype,r=e.hasOwnProperty,n=Object.defineProperty||function(t,e,r){t[e]=r.value},o="function"==typeof Symbol?Symbol:{},i=o.iterator||"@@iterator",a=o.asyncIterator||"@@asyncIterator",c=o.toStringTag||"@@toStringTag";function u(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{u({},"")}catch(t){u=function(t,e,r){return t[e]=r}}function s(t,e,r,o){var i=e&&e.prototype instanceof p?e:p,a=Object.create(i.prototype),c=new S(o||[]);return n(a,"_invoke",{value:_(t,r,c)}),a}function f(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}t.wrap=s;var l={};function p(){}function y(){}function d(){}var h={};u(h,i,(function(){return this}));var m=Object.getPrototypeOf,v=m&&m(m(x([])));v&&v!==e&&r.call(v,i)&&(h=v);var g=d.prototype=p.prototype=Object.create(h);function b(t){["next","throw","return"].forEach((function(e){u(t,e,(function(t){return this._invoke(e,t)}))}))}function w(t,e){function o(n,i,a,c){var u=f(t[n],t,i);if("throw"!==u.type){var s=u.arg,l=s.value;return l&&"object"==_typeof(l)&&r.call(l,"__await")?e.resolve(l.__await).then((function(t){o("next",t,a,c)}),(function(t){o("throw",t,a,c)})):e.resolve(l).then((function(t){s.value=t,a(s)}),(function(t){return o("throw",t,a,c)}))}c(u.arg)}var i;n(this,"_invoke",{value:function(t,r){function n(){return new e((function(e,n){o(t,r,e,n)}))}return i=i?i.then(n,n):n()}})}function _(t,e,r){var n="suspendedStart";return function(o,i){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===o)throw i;return N()}for(r.method=o,r.arg=i;;){var a=r.delegate;if(a){var c=O(a,r);if(c){if(c===l)continue;return c}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var u=f(t,e,r);if("normal"===u.type){if(n=r.done?"completed":"suspendedYield",u.arg===l)continue;return{value:u.arg,done:r.done}}"throw"===u.type&&(n="completed",r.method="throw",r.arg=u.arg)}}}function O(t,e){var r=e.method,n=t.iterator[r];if(void 0===n)return e.delegate=null,"throw"===r&&t.iterator.return&&(e.method="return",e.arg=void 0,O(t,e),"throw"===e.method)||"return"!==r&&(e.method="throw",e.arg=new TypeError("The iterator does not provide a '"+r+"' method")),l;var o=f(n,t.iterator,e.arg);if("throw"===o.type)return e.method="throw",e.arg=o.arg,e.delegate=null,l;var i=o.arg;return i?i.done?(e[t.resultName]=i.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=void 0),e.delegate=null,l):i:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,l)}function E(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function j(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function S(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(E,this),this.reset(!0)}function x(t){if(t){var e=t[i];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var n=-1,o=function e(){for(;++n<t.length;)if(r.call(t,n))return e.value=t[n],e.done=!1,e;return e.value=void 0,e.done=!0,e};return o.next=o}}return{next:N}}function N(){return{value:void 0,done:!0}}return y.prototype=d,n(g,"constructor",{value:d,configurable:!0}),n(d,"constructor",{value:y,configurable:!0}),y.displayName=u(d,c,"GeneratorFunction"),t.isGeneratorFunction=function(t){var e="function"==typeof t&&t.constructor;return!!e&&(e===y||"GeneratorFunction"===(e.displayName||e.name))},t.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,d):(t.__proto__=d,u(t,c,"GeneratorFunction")),t.prototype=Object.create(g),t},t.awrap=function(t){return{__await:t}},b(w.prototype),u(w.prototype,a,(function(){return this})),t.AsyncIterator=w,t.async=function(e,r,n,o,i){void 0===i&&(i=Promise);var a=new w(s(e,r,n,o),i);return t.isGeneratorFunction(r)?a:a.next().then((function(t){return t.done?t.value:a.next()}))},b(g),u(g,c,"Generator"),u(g,i,(function(){return this})),u(g,"toString",(function(){return"[object Generator]"})),t.keys=function(t){var e=Object(t),r=[];for(var n in e)r.push(n);return r.reverse(),function t(){for(;r.length;){var n=r.pop();if(n in e)return t.value=n,t.done=!1,t}return t.done=!0,t}},t.values=x,S.prototype={constructor:S,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=void 0,this.done=!1,this.delegate=null,this.method="next",this.arg=void 0,this.tryEntries.forEach(j),!t)for(var e in this)"t"===e.charAt(0)&&r.call(this,e)&&!isNaN(+e.slice(1))&&(this[e]=void 0)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if("throw"===t.type)throw t.arg;return this.rval},dispatchException:function(t){if(this.done)throw t;var e=this;function n(r,n){return a.type="throw",a.arg=t,e.next=r,n&&(e.method="next",e.arg=void 0),!!n}for(var o=this.tryEntries.length-1;o>=0;--o){var i=this.tryEntries[o],a=i.completion;if("root"===i.tryLoc)return n("end");if(i.tryLoc<=this.prev){var c=r.call(i,"catchLoc"),u=r.call(i,"finallyLoc");if(c&&u){if(this.prev<i.catchLoc)return n(i.catchLoc,!0);if(this.prev<i.finallyLoc)return n(i.finallyLoc)}else if(c){if(this.prev<i.catchLoc)return n(i.catchLoc,!0)}else{if(!u)throw new Error("try statement without catch or finally");if(this.prev<i.finallyLoc)return n(i.finallyLoc)}}}},abrupt:function(t,e){for(var n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&r.call(o,"finallyLoc")&&this.prev<o.finallyLoc){var i=o;break}}i&&("break"===t||"continue"===t)&&i.tryLoc<=e&&e<=i.finallyLoc&&(i=null);var a=i?i.completion:{};return a.type=t,a.arg=e,i?(this.method="next",this.next=i.finallyLoc,l):this.complete(a)},complete:function(t,e){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&e&&(this.next=e),l},finish:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),j(r),l}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;j(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:x(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=void 0),l}},t}function asyncGeneratorStep(t,e,r,n,o,i,a){try{var c=t[i](a),u=c.value}catch(t){return void r(t)}c.done?e(u):Promise.resolve(u).then(n,o)}function _asyncToGenerator(t){return function(){var e=this,r=arguments;return new Promise((function(n,o){var i=t.apply(e,r);function a(t){asyncGeneratorStep(i,n,o,a,c,"next",t)}function c(t){asyncGeneratorStep(i,n,o,a,c,"throw",t)}a(void 0)}))}}function ownKeys(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function _objectSpread(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?ownKeys(Object(r),!0).forEach((function(e){_defineProperty(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):ownKeys(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function _defineProperty(t,e,r){return(e=_toPropertyKey(e))in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function _toPropertyKey(t){var e=_toPrimitive(t,"string");return"symbol"===_typeof(e)?e:String(e)}function _toPrimitive(t,e){if("object"!==_typeof(t)||null===t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var n=r.call(t,e||"default");if("object"!==_typeof(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}function _typeof(t){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},_typeof(t)}!function(){var t="MOEBEL_CLICKOUT_ID",e="data-partner-key",r="data-market",n=JSON.parse('{"at":"https://redirect.moebel24.at/api/1.0/moebel/at/sales","ch":"https://redirect.moebel24.ch/api/1.0/moebel/ch/sales","de":"https://redirect.moebel.de/api/1.0/moebel/de/sales","fr":"https://redirect.meubles.fr/api/1.0/moebel/fr/sales","nl":"https://redirect.meubelo.nl/api/1.0/moebel/nl/sales","es":"https://redirect.mobi24.es/api/1.0/moebel/es/sales"}');function o(t){return isNaN(t)||!isFinite(t)?new Error("Bad numeric value ".concat(t)):null}function i(t){return o(t)?o(t):Number.isInteger(t)?null:new Error("Bad integer value ".concat(t))}function a(t){switch(_typeof(t)){case"number":return o(t)||t;case"string":return o(parseFloat(t))||parseFloat(t);default:return new Error("Can not convert ".concat(_typeof(t)," to number (float)"))}}function c(t){switch(_typeof(t)){case"string":return t;case"number":return o(t)||t.toString();default:return new Error("Can not convert ".concat(_typeof(t)," to string"))}}function u(t){var e=_objectSpread({},t),r=a(t.price);r instanceof Error&&console.error("Item price conversion error: ".concat(r.message)),e.price=r;var n=function(t){switch(_typeof(t)){case"number":return i(t)||t;case"string":return i(parseFloat(t))||parseInt(t,10);default:return new Error("Can not convert ".concat(_typeof(t)," to number (float)"))}}(t.quantity);if(n instanceof Error&&console.error("Item quantity conversion error: ".concat(n.message)),e.quantity=n,-1===Object.keys(t).indexOf("item_category")&&-1===Object.keys(t).indexOf("category")||(delete e.item_category,e.category=-1!==Object.keys(t).indexOf("item_category")?t.item_category:t.category),t.id){var o=c(t.id);if(o instanceof Error)return console.error("Item id can not be converted to string: ".concat(o.message)),o;e.id=o}if(t.item_id){var u=c(t.item_id);if(u instanceof Error)return console.error("Item item_id can not be converted to string: ".concat(u.message)),u;e.id=u}return e}function s(t){console.error("Received sales object: ".concat(JSON.stringify(t)))}var f=function(o){return function(i){if(!i)return console.error("No sales object provided"),void s(i);var f;try{f=function(t){var o=PARTNER_KEY;if(!o)throw new Error("".concat(e," not defined on push script"));var i="".concat(o).replace(/ /g,"").split(","),a=t.getAttribute(r)?"".concat(t.getAttribute(r)).replace(/ /g,"").toLowerCase().split(","):[MARKET];return a.forEach((function(t){if(-1===Object.keys(n).indexOf(t))throw new Error("Unsupported value in ".concat(r,": ").concat(t))})),{partnerKeys:i,useMarkets:a}}(o)}catch(t){return void console.error(t)}var l,p=function(t){try{for(var e=_objectSpread({},t),r=0,n=["total","shipping"];r<n.length;r++){var o=n[r],i=a(t[o]);if(i instanceof Error)return console.error("Can not convert ".concat(o," to number ").concat(i.message)),i;e[o]=i}if(e.orderId){var s=c(t.orderId);if(s instanceof Error)return console.error("Can not convert orderId to string ".concat(s.message)),s;e.orderId=s}if(t.items&&Array.isArray(t.items)){var f=t.items.map(u),l=f.find((function(t){return t instanceof Error}));if(l)return console.error("Can not convert items: ".concat(l.message)),l;e.items=f}return e}catch(t){return t}}(i);if(p instanceof Error)return console.error("Error sanitizing sale ".concat(p.message)),void s(i);if(function(t){if(!t)return console.error("No sales object provided to the API"),!1;if(!function(t){return t&&Array.isArray(t)&&0!==t.length?!t.some((function(t){return!function(t){if(!t)return console.error("Sales object item ".concat(t," should be object instead")),!1;if(Object.keys(t).filter((function(t){return"category"===t||"item_category"===t})).map((function(e){return t[e]})).filter((function(t){return"string"!=typeof t})).length)return console.error("item.category OR item.item_category must be a string if present"),!1;var e=t.category||t.item_category||"";if(Object.keys(t).find((function(t){return"category"===t||"item_category"===t}))&&"string"!=typeof e)return console.error("Sales object item ".concat(JSON.stringify(t)," item.category OR item.item_category is missing")),!1;var r=t.item_id||t.id;return!r||"string"!=typeof r&&a(r)instanceof Error?(console.error("Sales object item ".concat(JSON.stringify(t)," is missing item.id OR item.item_id field, or it is not a non-empty string/number")),!1):Number.isInteger(t.quantity)?!(Number.isNaN(t.price)||!Number.isFinite(t.price))||(console.error("Sales object item price for item ".concat(JSON.stringify(t)," is not a number")),!1):(console.error("Sales object item quantity for item ".concat(JSON.stringify(t)," is not an integer number")),!1)}(t)})):(console.error("Items must be a non-empty array, got ".concat(JSON.stringify(t)," instead")),!1)}(t.items))return!1;if(t.hasOwnProperty("currency")&&"string"!=typeof t.currency)return console.error("Currency shold be string"),!1;for(var e=0,r=["total","shipping"];e<r.length;e++){var n=r[e];if(Number.isNaN(t[n])||!Number.isFinite(t[n]))return console.error("Sales object ".concat(n," does not exist or is not a number")),!1}return!0}(l=p)){var y=function(){var e=localStorage.getItem(t);if(!e)return null;try{var r=JSON.parse(e);if(!(null==r?void 0:r.clickId)||!(null==r?void 0:r.date)||Number.isNaN(new Date(null==r?void 0:r.date).getTime()))return console.error("Click id has corrupted data"),null;var n=new Date(r.date),o=new Date;return n.getTime()+7776e6<o.getTime()?(console.log("Deleting expired click id"),localStorage.removeItem(t),null):r}catch(t){return console.error(t),null}}();if(y){var d=[];return f.partnerKeys.forEach((function(t){f.useMarkets.forEach((function(e){return d.push((r=_asyncToGenerator(_regeneratorRuntime().mark((function t(e,r,o){var i;return _regeneratorRuntime().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.prev=0,i=n[e],t.next=4,fetch("".concat(i,"?key=").concat(o),{method:"POST",headers:{"Content-type":"application/json"},mode:"no-cors",body:JSON.stringify(r)});case 4:t.next=9;break;case 6:t.prev=6,t.t0=t.catch(0),console.error("Error submitting sale to API: ".concat(t.t0.message));case 9:case"end":return t.stop()}}),t,null,[[0,6]])}))),function(t,e,n){return r.apply(this,arguments)})(e,function(t,e){var r={currency:t.currency||"EUR",items:t.items.map((function(t){var e=t.category,r=t.id,n=t.item_id,o=t.item_category;return _objectSpread({item_id:r||n,price:t.price,quantity:t.quantity},e||o||""===e||""===o?{item_category:e||o||""}:{})})),moeclid:e,value:t.total,shipping:t.shipping,type:"c2s"};return t.orderId&&(r.order_id=t.orderId),r}(l,y.clickId),t));var r}))})),d}}else s(i)}};PARTNER_KEY?function(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"MOEBEL_SALES",n={sale:f(e)};t[r]=n}(window,document.currentScript):console.error("Can not define moebel clickout API - partner key missing")}();

MOEBEL_SALES.sale({
    total: {{ecommerce.value}}, // Can be float or integer, required
    shipping: {{ecommerce.shipping}}, // Can be float or integer, required
    currency: "EUR", // Optional, default: EUR
    orderId: "{{ecommerce.transaction_id}}", // Optional
    items: {{ecommerce.items}} // Array of Items, required
  });
</script>
  1. Enter your partner key (provided by your account manager) in line 3

  2. Enter your market in line 4, should be one of "de","fr","nl","at","ch","es".

  3. Add all required variables in the sendSale() function. If you have UA/GA4 eCommerce tracking enabled, all required variables should already be set up with the “purchase” event.

A description of all parameters with example values you can find in the table below:

parameter description mandatory field data type example comment

total

gross total basket value, without shipping costs, including tax

yes

float

1499.97

2 digits, "." as decimal delimiter, no thousand delimiters

shipping

shipping costs

yes

float

29.99

2 digits, "." as decimal delimiter, no thousand delimiters

items

array of unique items in order. elements:

- item_id (string), alternatively: id (string),

- quantity (integer),

- price (float),

- item_category (string) alternatively: category (string)

yes

array

[{ "item_id": "47886359", "quantity": "1", "price": "899.99", "item_category": "boxspringbetten" }, { "item_id": "31118801", "quantity": "2", "price": "299.99", "item_category": "heimtextilien" }]

currency

currency

yes

string

EUR

DIN norm

orderid

your identifier for the order

no

string

1172210481407

  1. Add the "purchase" event as trigger and save the tag

344f51e6 06c6 4a8f 9ad6 f17b9cdc8acb
  1. Publish your container version