Отправка цели "Заказ оплачен" в Яндекс Метрику, если пользователь не вернулся на сайт из платёжной системы
Приветствую, друзья.
Расскажу, как я решил задачу с отправкой цели «Заказ оплачен» в Яндекс.Метрику, точнее статуса Заказа на «Оплачен». Что здесь такого, можете подумать вы? Вот и я так же подумал, но всё оказалось несколько сложнее, и готовых решений на этот счёт тоже нет.
Представьте себе сценарий: покупатель приходит на сайт, оформляет заказ и после оформления переходит на страницу платёжной системы для оплаты заказа. Всё, вроде бы, просто, но как отправить цель в Метрику, если пользователь не вернулся из платежной системы, и просто закрыл страницу? Ведь счётчик Метрики представляет собой JavaScript API, а значит работает в браузере и пользователь должен вернуться обратно на сайт на какую-то страницу, где мы выполним код отправки цели.
Т.к. мы не можем на это повлиять, то решать этот вопрос мы будем с помощью API Метрики, офлайн-конверсий, плагина для MODX, а также нам придется расширять поля заказа.
В моём случае для онлайн-платежей использовался компонент mspYooKassa, главное, чтобы после оплаты заказа было автоматическое изменение статуса заказа, это всё, что нужно для корректной работы.
Начнём с простого и создадим цель JavaScript событие в Метрике. Для работы нам понадобится только идентификатор цели, который вы придумаете.

Теперь нам необходимо получить OAuth-токен для работы с API Метрики. Я не буду на этом останавливаться. Подробно о том, как это сделать можно найти в документации к API Метрики.
Далее необходимо расширить поля заказа. Зачем это нужно я расскажу чуть позже. Для этого конкретно в своём случае я воспользовался компонентом msOrderFields, но вы можете сделать это и другими удобными для вас способами. В интернете можно найти и бесплатные решения.
Создаем новое поле с ключом _ym_uid

Я добавил его на вкладке Адрес, не суть важно где оно будет, можете сделать для него отдельную вкладку, нам главное записать в него ClientID, который пользователю присваивает Метрика (значение хранится в cookie) и в дальнейшем по этому ID будет осуществляться привязка офлайн-конверсии. Подробно об этом можно почитать также в документации.
Добавляем плагин на 2 события: msOnBeforeCreateOrder и msOnChangeOrderStatus.
Что делает плагин?
Во-первых, при оформлении заказа мы получаем тот самый ClientID пользователя и записываем его в созданное поле заказа, чтобы в дальнейшем связать заказ и Посетителя в Метрике. Всё это делалось по документации, хотя там есть и другие варианты, но привязка по ClientID является рекомендуемой Яндексом.

Теперь, когда мы имеем ClientID — нам уже всё равно вернётся посетитель из платежной системы или нет, т.к. основная часть плагина будет срабатывать на изменение статуса заказа по id, а именно, статус «Оплачен».
Согласно документации того же Яндекса рекомендуемым способом загрузки офлайн конверсий является csv файл, хотя можно и напрямую отправить данные, я всё же решил сделать это рекомендуемым способом через CSV. При каждой отправке заказа мы будем создавать временный файл по пути assets/tmp
Лучше создать директорию assets/tmp заранее, чтобы не было проблем.
Здесь же стоит проверка на наличие в заказе ClientID, чтобы не отправлять в Метрику, например, менеджерские заказы, когда заказ создаётся менеджером в админке и ClientID там пустой. Помимо этого, без ClientID мы просто не сможем привязать оплату заказа к посетителю и Метрика вернёт нам ошибку. Для таких случаев потребуется применять другие идентификаторы, но это уже отдельный вопрос.
Если посетитель оформил заказ на сайте без оплаты, и пришел забирать заказ в офис или на пункт выдачи с оплатой на месте, например, наличными, то при изменении статуса заказа в админке менеджером конверсия будет засчитана, т.к. у заказа есть ClientID.
У офлайн-конверсий есть ограничение:
Для всех офлайн-данных (офлайн-конверсий, звонков, заказов из CRM) период дополнения визитов составляет 21 день. Данные будут добавлены к визиту, если между последним визитом посетителя на сайт и моментом обработки файла с данными о конверсиях прошло не больше 21 дня.
То есть, по истечении этого времени цель оплаты заказа не будет засчитана и привязана к посетителю. Это просто нужно иметь ввиду.
По итогу в Метрике мы будем наблюдать такую картину:

Здесь пример как успешной привязки офлайн-конверсии посетителю, так и ошибка, для наглядности.
В разделе «Конверсии» всё будет стандартно:


Вот такое нетривиальное решение оказалось. Традиционно, если вы найдёте ошибки — прошу меня поправить.
Спасибо за внимание!
Расскажу, как я решил задачу с отправкой цели «Заказ оплачен» в Яндекс.Метрику, точнее статуса Заказа на «Оплачен». Что здесь такого, можете подумать вы? Вот и я так же подумал, но всё оказалось несколько сложнее, и готовых решений на этот счёт тоже нет.
Представьте себе сценарий: покупатель приходит на сайт, оформляет заказ и после оформления переходит на страницу платёжной системы для оплаты заказа. Всё, вроде бы, просто, но как отправить цель в Метрику, если пользователь не вернулся из платежной системы, и просто закрыл страницу? Ведь счётчик Метрики представляет собой JavaScript API, а значит работает в браузере и пользователь должен вернуться обратно на сайт на какую-то страницу, где мы выполним код отправки цели.
Т.к. мы не можем на это повлиять, то решать этот вопрос мы будем с помощью API Метрики, офлайн-конверсий, плагина для MODX, а также нам придется расширять поля заказа.
Начнём с простого и создадим цель JavaScript событие в Метрике. Для работы нам понадобится только идентификатор цели, который вы придумаете.

Теперь нам необходимо получить OAuth-токен для работы с API Метрики. Я не буду на этом останавливаться. Подробно о том, как это сделать можно найти в документации к API Метрики.
Далее необходимо расширить поля заказа. Зачем это нужно я расскажу чуть позже. Для этого конкретно в своём случае я воспользовался компонентом msOrderFields, но вы можете сделать это и другими удобными для вас способами. В интернете можно найти и бесплатные решения.
Создаем новое поле с ключом _ym_uid

Я добавил его на вкладке Адрес, не суть важно где оно будет, можете сделать для него отдельную вкладку, нам главное записать в него ClientID, который пользователю присваивает Метрика (значение хранится в cookie) и в дальнейшем по этому ID будет осуществляться привязка офлайн-конверсии. Подробно об этом можно почитать также в документации.
Добавляем плагин на 2 события: msOnBeforeCreateOrder и msOnChangeOrderStatus.
<?php
switch ($modx->event->name) {
case 'msOnChangeOrderStatus':
if ($status == 2) {
$counterId = 'ваш код счётчика'; // ID счетчика
$apiToken = 'полученный OAuth-токен'; // OAuth-токен
$goal = 'название цели какое вы придумали'; // Название цели
$timestamp = time(); // Время в формате Unix Timestamp
// Проверяем наличие объекта заказа
if (!isset($order) || !is_object($order)) {
$modx->log(modX::LOG_LEVEL_ERROR, "[YandexMetrika] Объект заказа не доступен");
return;
}
$clientId = $order->Address->get('_ym_uid');
//$modx->log(modX::LOG_LEVEL_ERROR, "[YandexMetrika] ID клиента {$clientId}");
if (empty($clientId)) {
$modx->log(modX::LOG_LEVEL_ERROR, "[YandexMetrika] ID клиента не задан");
return;
}
$url = "https://api-metrika.yandex.net/management/v1/counter/{$counterId}/offline_conversions/upload";
// Генерируем уникальное имя CSV-файла
$randomNumber = mt_rand(100000, 999999);
$csvFilePath = MODX_BASE_PATH . "assets/tmp/conversions_{$timestamp}_{$randomNumber}.csv";
// Генерируем CSV-данные
$csvData = "ClientID,Target,DateTime\n"; // Заголовки CSV
$csvData .= "{$clientId},{$goal},{$timestamp}\n"; // Данные конверсии
if (file_put_contents($csvFilePath, $csvData) === false) {
$modx->log(modX::LOG_LEVEL_ERROR, "[YandexMetrika] Не удалось создать CSV-файл: {$csvFilePath}");
return;
}
// Формируем POST-данные
$postFields = [
'file' => new CURLFile($csvFilePath, 'text/csv', basename($csvFilePath)),
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: OAuth {$apiToken}",
"Content-Type: multipart/form-data"
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
// Удаляем временный CSV-файл после отправки
if (file_exists($csvFilePath)) {
unlink($csvFilePath);
}
// Проверяем результат
if ($response === false) {
$modx->log(modX::LOG_LEVEL_ERROR, "[YandexMetrika] Ошибка выполнения cURL: {$error}");
return;
}
$responseData = json_decode($response, true);
if ($httpCode == 200) {
$modx->log(modX::LOG_LEVEL_ERROR, "[YandexMetrika] Цель '{$goal}' успешно отправлена для посетителя с ID {$clientId}");
} else {
$modx->log(modX::LOG_LEVEL_ERROR,
"[YandexMetrika] Ошибка при отправке цели '{$goal}' для посетителя с ID {$clientId}. " .
"Код ответа: {$httpCode}. " .
"Ошибка cURL: " . ($error ? $error : 'нет') . ". " .
"Ответ API: " . print_r($responseData, true)
);
}
}
break;
case 'msOnBeforeCreateOrder':
$_ym_uid = $_COOKIE['_ym_uid'];
if(!empty($_ym_uid)){
$msOrder->Address->set('_ym_uid', $_ym_uid);
}
break;
}
Что делает плагин?
Во-первых, при оформлении заказа мы получаем тот самый ClientID пользователя и записываем его в созданное поле заказа, чтобы в дальнейшем связать заказ и Посетителя в Метрике. Всё это делалось по документации, хотя там есть и другие варианты, но привязка по ClientID является рекомендуемой Яндексом.

Теперь, когда мы имеем ClientID — нам уже всё равно вернётся посетитель из платежной системы или нет, т.к. основная часть плагина будет срабатывать на изменение статуса заказа по id, а именно, статус «Оплачен».
Согласно документации того же Яндекса рекомендуемым способом загрузки офлайн конверсий является csv файл, хотя можно и напрямую отправить данные, я всё же решил сделать это рекомендуемым способом через CSV. При каждой отправке заказа мы будем создавать временный файл по пути assets/tmp
Лучше создать директорию assets/tmp заранее, чтобы не было проблем.
Здесь же стоит проверка на наличие в заказе ClientID, чтобы не отправлять в Метрику, например, менеджерские заказы, когда заказ создаётся менеджером в админке и ClientID там пустой. Помимо этого, без ClientID мы просто не сможем привязать оплату заказа к посетителю и Метрика вернёт нам ошибку. Для таких случаев потребуется применять другие идентификаторы, но это уже отдельный вопрос.
Если посетитель оформил заказ на сайте без оплаты, и пришел забирать заказ в офис или на пункт выдачи с оплатой на месте, например, наличными, то при изменении статуса заказа в админке менеджером конверсия будет засчитана, т.к. у заказа есть ClientID.
У офлайн-конверсий есть ограничение:
Для всех офлайн-данных (офлайн-конверсий, звонков, заказов из CRM) период дополнения визитов составляет 21 день. Данные будут добавлены к визиту, если между последним визитом посетителя на сайт и моментом обработки файла с данными о конверсиях прошло не больше 21 дня.
То есть, по истечении этого времени цель оплаты заказа не будет засчитана и привязана к посетителю. Это просто нужно иметь ввиду.
По итогу в Метрике мы будем наблюдать такую картину:

Здесь пример как успешной привязки офлайн-конверсии посетителю, так и ошибка, для наглядности.
В разделе «Конверсии» всё будет стандартно:


Вот такое нетривиальное решение оказалось. Традиционно, если вы найдёте ошибки — прошу меня поправить.
Спасибо за внимание!
Комментарии: 1
Большое спасибо за качественное и подробное описание!
Вообще считаю, что в нынешние времена, веб-аналитика в минишопе должна быть если не из коробки, то хотя бы модулем каким-то готовым. На самом деле конверсии полезно отправлять не только в Метрику, но и в другие счетчики (GA, VK, TMR и даже в тикток пиксель иногда, и другие)
Описанный способ подойдёт в принципе для всех систем по аналогии, т.к. ключевое здесь, именно clietID выцепить и сохранить. Очень полезно!
Вообще считаю, что в нынешние времена, веб-аналитика в минишопе должна быть если не из коробки, то хотя бы модулем каким-то готовым. На самом деле конверсии полезно отправлять не только в Метрику, но и в другие счетчики (GA, VK, TMR и даже в тикток пиксель иногда, и другие)
Описанный способ подойдёт в принципе для всех систем по аналогии, т.к. ключевое здесь, именно clietID выцепить и сохранить. Очень полезно!
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.