Как исключить дублирование транзакций в Google Analytics с помощью customTask

Недавно в своём телеграм-канале Дмитрий Осиюк дал ссылку на статью Simo Ahava о том, как исключить дублирование транзакций в Google Analytics с помощью customTask. Мы решили перевести данное руководство, чтобы даже у тех, у кого нет времени разбираться с английским, была возможность решить данный вопрос.

Одной из наибольших проблем в модели данных Google Analytics является неизменность исторических данных. При записи информации в таблицу данных она остается там практически навсегда.

Это явление особенно раздражает в двух случаях: при спаме и фиктивных заказах электронной коммерции.

Первый случай является общепризнанной проблемой с открытым и общедоступным протоколом сбора данных. Во втором случае попросту возникает раздражение, которое может привести к полномасштабному саботажу (например, вы можете использовать Measurement Protocol для отправки сотен транзакций в Google Analytics вашего конкурента).

Проблема заключается в дублировании транзакций. На вашем сайте, как и на большинстве других, скорее всего есть страница оплаты, которую можно просмотреть через кеш или историю браузера. При повторном посещении страницы оплаты часто случается, что детали транзакции снова записываются в dataLayer(или отправляются с помощью метода ga()), что приводит к дублированию данных вашей электронной коммерции.

Исключение может быть в случаях, когда вы обновляете данные о транзакциях, например:

Я принял решение об использовании customTask, поскольку это позволяет избежать использования лишних триггеров или логической переменной. Мы выполним настройку с помощью Google Tag Manager, однако ничто не мешает вам сделать то же самое прямо в коде analytics.js.

Чтобы проверить есть ли в вашем аккаунте такая проблема:

1) перейдите в Google Query Explorer по этой ссылке;
2) авторизуйтесь в свой Google аккаунт;
3) выберите представление данных, которое хотите проверить;
4) нажмите кнопку Run Query.

Если в полученном отчете будет 0 результатов — у вас не было замечено такой проблемы за последние 30 дней. Если же в отчете есть данные — у вас обнаружены дублирующиеся транзакции в выбранном представлении данных.

Оглавление

Как это работает

Когда вы добавляете переменную customTaskв свои теги, она активируется каждый раз, когда тег пытается выполнить отправку в Google Analytics.

Во время активации метод ищет ключ &ti, соответствующий значению идентификатора транзакции.

Затем он ищет в вашем браузере уже отправленные идентификаторы транзакций.

Важно! Эта функция автоматической блокировки работает только с расширенной версией Электронной торговли. В стандартной электронной торговле customTaskобновляет только хранилище браузера, но при этом ничего не блокирует. В этом случае вам придется использовать дополнительные триггеры. О том, как их настроить, я расскажу дальше.

Переменная customTask

Скопируйте и вставьте приведенный ниже код в переменную Собственный код JavaScript (ниже приведена подробная инструкция). Переменная Собственный код JavaScript должна выглядеть следующим образом:

function() {
  // customTask Builder by Simo Ahava
  //
  // More information about customTask: https://www.simoahava.com/analytics/customtask-the-guide/
  //
  // Change the default values for the settings below.

  // transactionDeduper: Configuration object for preventing duplicate transactions from being recorded.
  // https://bit.ly/2AvSZ2Y
  var transactionDeduper = {
    keyName: '_transaction_ids',
    cookieExpiresDays: 365
  };

  // DO NOT EDIT ANYTHING BELOW THIS LINE
  var readFromStorage = function(key) {
    if (!window.Storage) {
      // From: https://stackoverflow.com/a/15724300/2367037
      var value = '; ' + document.cookie;
      var parts = value.split('; ' + key + '=');
      if (parts.length === 2) return parts.pop().split(';').shift();
    } else {
      return window.localStorage.getItem(key);
    }
  };

  var writeToStorage = function(key, value, expireDays) {
    if (!window.Storage) {
      var expiresDate = new Date();
      expiresDate.setDate(expiresDate.getDate() + expireDays);
      document.cookie = key + '=' + value + ';expires=' + expiresDate.toUTCString();
    } else {
      window.localStorage.setItem(key, value);
    }
  };

  var globalSendHitTaskName   = '_ga_originalSendHitTask';

  return function(customTaskModel) {

    window[globalSendHitTaskName] = window[globalSendHitTaskName] || customTaskModel.get('sendHitTask');
    var tempFieldObject, dimensionIndex, count, ga, tracker, decorateTimer, decorateIframe, iframe;

    customTaskModel.set('sendHitTask', function(sendHitTaskModel) {

      var originalSendHitTaskModel = sendHitTaskModel,
          originalSendHitTask      = window[globalSendHitTaskName],
          canSendHit               = true;

      var hitPayload, hitPayloadParts, param, val, regexI, trackingId, snowplowVendor, snowplowVersion, snowplowPath, request, originalTrackingId, hitType, nonInteraction, d, transactionId, storedIds;

      try {

        // transactionDeduper
        if (typeof transactionDeduper === 'object' && transactionDeduper.hasOwnProperty('keyName') && transactionDeduper.hasOwnProperty('cookieExpiresDays') && typeof sendHitTaskModel.get('&ti') !== 'undefined') {
          transactionId = sendHitTaskModel.get('&ti');
          storedIds = JSON.parse(readFromStorage(transactionDeduper.keyName) || '[]');
          if (storedIds.indexOf(transactionId) > -1 && ['transaction', 'item'].indexOf(sendHitTaskModel.get('hitType')) === -1) {
            canSendHit = false;
          } else if (storedIds.indexOf(transactionId) === -1) {
            storedIds.push(transactionId);
            writeToStorage(transactionDeduper.keyName, JSON.stringify(storedIds), transactionDeduper.cookieExpiresDays);
          }
        }
        // /transactionDeduper

        if (canSendHit) {
          originalSendHitTask(sendHitTaskModel);
        }

      } catch(e) {
        originalSendHitTask(originalSendHitTaskModel);
      }

    });

  };
}

В переменной transactionDeduper есть объект конфигурации, который при необходимости может быть изменен. Он должен иметь как keyName, так и cookieExpiresDays кодом.

Задайте необходимое keyNameдля файла cookie или ключа localStorage. Значением по умолчанию является _transaction_ids

Установите для cookieExpiresDays кодом необходимое количество дней существования cookie.

Реализация основной логики происходит ближе к концу блока кода, где я поместил решение с помощью // transactionDeduperи // /transactionDeduper.

Все действительно довольно просто, и наблюдается определенное соответствие процессу, который я описал выше.

В случае использования расширенной электронной торговли, customTaskзаботится обо всем за вас. Если ID транзакции будет найден в списке сохраненных идентификаторов, он просто заблокирует повторную отправку информации в Google Analytics.

При использовании стандартной электронной торговли customTaskкод будет записывать в хранилище только идентификатор, и вам придется самостоятельно обрабатывать логику блокировки.

В любом случае вам нужно добавить эту переменную customTaskво все теги расширенной электронной коммерции и/или Теги транзакций, которые могут отправлять информацию о покупках в Google Analytics. 

Как добавить customTask в ваши тэги электронной торговли?

Чтобы добавить customTask в Google Tag Manager вам необходимо создать пользовательскую переменную:

Тип переменной “Собственный код JavaScript”:

В появившееся поля ввода необходимо скопировать код, приведённый выше и дать переменной имя customTask.

Должно получиться так:

Сохраните переменную.

Теперь необходимо подключить во всех тэгах Электронной торговли данную переменную.
Перейдите в раздел “Тэги” и откройте тэг Электронной торговли:

В настройках необходимо проделать следующие операции:

1. Включить переопределение настроек
2. Раскрыть поля “Дополнительные настройки” и “Поля, которые необходимо задать”
3. Ввести название поля customTask
4. Дать ссылку на переменную customTask, которую мы создали ранее.
В нашем случае это {{customTask}}

Сохраните изменения в тэге.

Если код Universal Analytics (analytics.js) установлен непосредственно на сайте, то необходимо проделать следующие операции:

1. Определить функцию JavaScript _customTask — её код приведён выше.

Начало кода выглядит так:

function() {

 // customTask Builder by Simo Ahava
...

Необходимо в начале дописать:

var _customTask = function() {

 // customTask Builder by Simo Ahava
...

2. Установить customTask

ga('create', 'UA-XXXXX-Y');
ga('require', 'ec');

ga('ec:addProduct', {
 'id': 'P12345',
 'name': 'Android Warhol T-Shirt',
 'category': 'Apparel',
 'brand': 'Google',
 'variant': 'black',
 'price': '29.20',
 'quantity': 1
});

ga('ec:setAction', 'purchase', {
 'id': 'T12345',
 'affiliation': 'Google Store - Online',
 'revenue': '37.39',
 'tax': '2.85',
 'shipping': '5.34',
 'coupon': 'SUMMER2013'    
});

ga('set', 'customTask', _customTask()); //С помощью данной функции запускаем customTask
ga('send', 'pageview');

Триггеры и переменные для Стандартной электронной торговли

Т. к. в стандартной электронной торговле передача информации о покупке делится на хит транзакции и хит информации о товаре, логику блокировки было бы крайне сложно автоматизировать в customTask. Вот почему вам необходимо создать триггер исключения для вашего тега транзакции: он будет блокировать запуск тега, если ID транзакции найден в списке сохраненных идентификаторов.

Переменная уровня данных для идентификатора транзакции

Создайте переменную уровня данных для транзакции, например:

1st Party Cookie для списка идентификаторов транзакций

Создайте переменную 1st Party Cookie для списка ID транзакций, используя keyName, которое вы указали в customTask. Вот как будет выглядеть настройка по умолчанию:

Пользовательская переменная JavaScript для проверки наличия идентификатора в списке

Наконец, создайте переменную Собственный код JavaScript, которая возвращает true, если идентификатор транзакции найден в списке.

function() {
  // Change this to match the keyName you added to customTask:
  var keyName = '_transaction_ids';
  
  var ids = JSON.parse((!!window.Storage ? window.localStorage.getItem(keyName) : {{Cookie - _transaction_ids}}) || '[]');
  return ids.indexOf({{DLV - transactionId}}) > -1;
}

Назовите переменную. Например, {{JS — transactionId sent}}.

Триггер исключения

Последний шаг создать триггер, который блокирует срабатывание тега транзакции. Триггер должен использовать то же событие, что и тег транзакции. Если тег транзакции срабатывает в триггере просмотра страницы, триггер исключения также должен являться триггером просмотра страницы (подробнее об исключениях читайте здесь).

В условиях триггера проверьте, является ли {{JS — transactionId sent}} равным значению true. Таким образом, триггер исключения заблокирует тег, если ID транзакции найден в списке уже записанных идентификаторов.

Вот пример того, как выглядит исключение с триггером пользовательского события, и как оно добавляется в тег:

Выводы

Поиски по усовершенствованию качества данных Google Analytics с помощью customTaskпродолжаются до сих пор.

Предотвращение отправки информации при возникновении определенных условий подходит для запуска через customTask, так как в этом случае вам не нужно возиться со сложными триггерами, загромождающими интерфейс GTM.

Однако каждый дополнительный customTaskснижает прозрачность вашей настройки, что не позволит сказать с первого взгляда, какую задачу решает тот или иной тег.

Понравилась статья? Делитесь ей в соцсетях!

Оставляйте ваши вопросы на нашей странице в Facebook