Что такое Enhanced Ecommerce
Enhanced Ecommerce (расширенная электронная торговля) — это набор возможностей Google Tag Manager и Google Analytics, который выходит за рамки стандартных событий «покупка» и «добавление в корзину».
С помощью Enhanced Ecommerce вы сможете отслеживать:
- взаимодействие с баннерами и промоблоками;
- пошаговый процесс оформления заказа (checkout funnel);
- возвраты товаров;
- клики по внутренним промоакциям и купонам.
Зачем маркетологу и бизнесу добавлять Enhanced Ecommerce
Если стандартный e-commerce отвечает на вопрос «сколько купили?», то Enhanced Ecommerce показывает, почему купили (или не купили):
- Глубокая аналитика воронки продаж — видим, где пользователи чаще всего отпадают: при просмотре, на этапе добавления в корзину или при оформлении.
- Оценка эффективности промо — измеряем, какие баннеры реально приводят к покупкам, а какие просто занимают место.
Далее подробно рассмотрим каждое событие расширенной разметки электронной торговли с примерами.
События просмотра списков товаров (impressions, view_item_list)
Пользователь открывает страницу категории (например, «Смартфоны») или страницу с результатами поиска. В этот момент он видит список товаров, но еще не перешел в карточку. Нам важно зафиксировать этот просмотр — чтобы потом сравнивать, какие товары чаще показываются, по каким чаще кликают и как CTR влияет на продажи.
ВАЖНО! Часто забывают такие списки товаров на сайтах интернет-магазинов, а это именно списки: «с этим товаром покупают…», «кросс-сейл», «бандлы товара», товары, отложенные в раздел любимых.
Стандартный код для передачи события в dataLayer
dataLayer.push({
"event": "view_item_list",
"ecommerce": {
"item_list_id": "smartphones_category",
"item_list_name": "Смартфоны",
"items": [
{
"item_id": "12345",
"item_name": "Samsung Galaxy A34",
"price": 19990,
"currency": "RUB",
"index": 1
},
{
"item_id": "67890",
"item_name": "iPhone 13",
"price": 59990,
"currency": "RUB",
"index": 2
}
]
}
});
Комментарии к коду:
event— фиксируем событиеview_item_list.item_list_id / item_list_name— идентификатор и название списка (например, «категория_смартфоны», «поиск_по_iphone»).items[]— массив товаров, которые показаны пользователю.index— позиция товара в списке (важно для анализа, кликают ли чаще на первые позиции или скроллят вниз).
Реальная ситуация
Бывает, что фронтенд не имеет информации о товарах в списке (например, страница рендерится сервером на PHP и отдает уже готовый HTML). В таком случае события можно генерировать с бэка и сразу встраивать в страницу.
Пример PHP-кода:
<?php
// Допустим, у нас есть массив товаров, отрендеренный с бэка
$products = [
[
"id" => "12345",
"name" => "Samsung Galaxy A34",
"price" => 19990,
"currency" => "RUB"
],
[
"id" => "67890",
"name" => "iPhone 13",
"price" => 59990,
"currency" => "RUB"
]
];
// Формируем массив для dataLayer
$items = [];
$index = 1;
foreach ($products as $product) {
$items[] = [
"item_id" => $product["id"],
"item_name" => $product["name"],
"price" => $product["price"],
"currency" => $product["currency"],
"index" => $index++
];
}
?>
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: "view_item_list",
ecommerce: {
item_list_id: "smartphones_category",
item_list_name: "Смартфоны",
items: <?= json_encode($items, JSON_UNESCAPED_UNICODE) ?>
}
});
</script>
PHP собрал список товаров сразу из базы или API. Встроили готовый JSON прямо в JS-код. В итоге фронту не нужно ничего «знать» — он просто отрисовывает HTML + готовый dataLayer.push.
Что это даст аналитикам
- Понимание, какие категории и поисковые запросы реально привлекают внимание.
- Возможность рассчитать CTR товаров в списках: сколько раз товар показали vs сколько раз кликнули.
- Анализировать эффективность сортировок (например, выгодно ли ставить «Хиты продаж» первыми).
- Привязка данных к источникам трафика: разные каналы могут давать разные паттерны поведения.
Добавление в «Избранное» / в wishlist (add_to_wishlist)
Пользователь на карточке товара или в списке кликает на иконку «В Избранное» или «Добавить в wishlist». Этот жест не ведет к немедленной покупке, но показывает интерес к товару. Для бизнеса это сигнал о потенциальном спросе, а для аналитики — возможность строить прогнозы: какие товары часто лайкают, но редко покупают (может, цена кусается или товар не в наличии?).
Стандартный код для передачи события в dataLayer
dataLayer.push({
"event": "add_to_wishlist",
"ecommerce": {
"items": [{
"item_id": "12345",
"item_name": "Наушники Sony WH-1000XM5",
"price": 29990,
"currency": "RUB"
}]
}
});
Комментарии к коду:
event— указываем событие add_to_wishlist.items[]— массив товаров, которые добавили в wishlist (обычно один товар).item_id / item_name / price / currency— минимум обязательных параметров для корректной аналитики.
Реальная ситуация
Если добавление в «Избранное» происходит асинхронно (например, AJAX-запрос уходит на сервер, а UI обновляется без перезагрузки), фронт может не знать всей информации о товаре (например, актуальную цену или валюту). В таком случае бэкенд может сам сформировать dataLayer и отдать его в ответе.
Пример PHP-кода:
<?php
// Допустим, пользователь добавил товар с ID 12345 в избранное
$productId = 12345;
// Получаем данные о товаре из базы
$product = [
"id" => "12345",
"name" => "Наушники Sony WH-1000XM5",
"price" => 29990,
"currency" => "RUB"
];
// Возвращаем фронту JSON для dataLayer
$response = [
"event" => "add_to_wishlist",
"ecommerce" => [
"items" => [[
"item_id" => $product["id"],
"item_name" => $product["name"],
"price" => $product["price"],
"currency" => $product["currency"]
]]
]
];
header('Content-Type: application/json; charset=utf-8');
echo json_encode($response, JSON_UNESCAPED_UNICODE);
Пояснение:
- Фронт отправляет AJAX-запрос «добавить в Избранное».
- Бэкенд подгружает полные данные о товаре (из базы или API).
- Возвращает JSON с готовым событием
add_to_wishlist. - JS на фронте получает этот JSON и выполняет
dataLayer.push(response).
Таким образом:
- фронту не нужно знать все атрибуты товара;
- данные всегда точные (например, цена и валюта синхронизированы с CRM/ERP).
Что это даст аналитикам
- Измерение skinterest (интереса без покупки) — сколько товаров попадает в wishlist.
- Прогнозирование спроса — можно видеть, какие товары «хотят купить позже».
- Сравнение категорий — часто добавляют в «Избранное», но не покупают → возможно, нужна скидка или более заметный call-to-action.
- Сегментация покупателей — формирование аудитории для ремаркетинга: «Показать рекламу тем, кто добавил товар в wishlist, но не купил».
Шаги оформления заказа begin_checkout, add_shipping_info, add_payment_info
Пошаговый чекаут:
- Пользователь заполняет контакты и адрес доставки → фиксируем вход в чекаут
begin_checkout. - Пользователь выбирает способ доставки →
add_shipping_infoсshipping_tier(«Курьер завтра», «ПВЗ СДЭК», «Самовывоз» и т. п.). - Пользователь выбирает способ оплаты →
add_payment_infoсpayment_type(«Банковская карта», «ЮKassa», PayPal, СБП и т. д.).
Почему так? В GA4 больше нет нумерованных «шагов чекаута» из UA-EEC — их заменили отдельные события. Если хотите, можно добавить свой кастомный параметр checkout_step для удобной сегментации в отчетах.
Стандартный код для передачи события в dataLayer
<script>
// 1) Пользователь ввел контакты/адрес и нажал «Далее»
// ВАЖНО: не отправляем email/телефон/адрес в GA!
// Можно добавить безопасный флаг: contact_info_collected: true
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "begin_checkout",
ecommerce: {
value: 53980, // сумма товаров (без доставки)
currency: "RUB",
coupon: "WELCOME10", // если применен купон ко всей корзине
items: [
{ item_id: "SKU123", item_name: "Кроссовки Nike Air", price: 19990, quantity: 1 },
{ item_id: "SKU456", item_name: "Носки спортивные", price: 3990, quantity: 3 }
]
},
contact_info_collected: true // кастомный параметр БЕЗ ПДн
});
</script>
<script>
// 2) Пользователь выбрал способ доставки (радио-кнопка/селект) и подтвердил
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "add_shipping_info",
ecommerce: {
value: 53980, // та же сумма товаров; стоимость доставки уйдет в purchase
currency: "RUB",
shipping_tier: "Курьер, завтра", // рекомендованный параметр GA4
items: [
{ item_id: "SKU123", item_name: "Кроссовки Nike Air", price: 19990, quantity: 1 },
{ item_id: "SKU456", item_name: "Носки спортивные", price: 3990, quantity: 3 }
]
}
});
</script>
<script>
// 3) Пользователь выбрал метод оплаты и подтвердил (до редиректа на платежку)
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "add_payment_info",
ecommerce: {
value: 53980,
currency: "RUB",
payment_type: "Банковская карта", // рекомендованный параметр GA4
items: [
{ item_id: "SKU123", item_name: "Кроссовки Nike Air", price: 19990, quantity: 1 },
{ item_id: "SKU456", item_name: "Носки спортивные", price: 3990, quantity: 3 }
]
}
});
</script>
Комментарии:
shipping_tierиpayment_type— рекомендованные параметры, по ним строятся отчеты и (при необходимости) кастомные измерения.- Контактные данные (Ф. И. О. / email / телефон / адрес) в GA не отправляем — это PII и запрещено. Храните в CRM/бэке. В GA4 можете передавать только обезличенные флаги (например,
contact_info_collected: true).
Реальная ситуация
Частые кейсы:
- Доставка/оплата подтверждается на сервере после валидации (SSR, мультишаговый чекаут).
- Методы и стоимости доставки считаются бэком (API курьеров), фронту приходит уже «готовое» решение.
- На редирект в платежную систему уходит POST/302 — перед редиректом нужно успеть отправить
add_payment_info.
Идея: бэк после успешного сохранения шага чекаута вставляет в HTML маленький скрипт с dataLayer.push(...). Вот безопасный шаблон:
<?php
// Хелпер для вывода dataLayer-пуша с чисткой ecommerce
function dl_push(string $eventName, array $ecommerce, array $extra = []): void {
$payload = array_merge(['event' => $eventName, 'ecommerce' => $ecommerce], $extra);
echo '<script>window.dataLayer = window.dataLayer || [];</script>';
echo '<script>dataLayer.push({ecommerce: null});</script>';
echo '<script>dataLayer.push('
. json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
. ');</script>';
}
// Пример данных из сервера
$cartItems = [
['sku' => 'SKU123', 'name' => 'Кроссовки Nike Air', 'price' => 19990, 'qty' => 1],
['sku' => 'SKU456', 'name' => 'Носки спортивные', 'price' => 3990, 'qty' => 3],
];
$items = array_map(fn($i) => [
'item_id' => (string)$i['sku'],
'item_name' => $i['name'],
'price' => (float)$i['price'],
'quantity' => (int)$i['qty'],
], $cartItems);
$currency = 'RUB';
$value = array_sum(array_map(fn($i) => $i['price'] * $i['quantity'], $items));
// 1) После того, как сервер принял и сохранил контакты/адрес (без передачи PII в GA!)
dl_push('begin_checkout', [
'value' => $value,
'currency' => $currency,
'items' => $items
], [
'contact_info_collected' => true // кастомный параметр (зарегистрируйте как custom dimension)
]);
// 2) После сохранения выбранного метода доставки на бэке
$shippingTier = $order->shipping_method_label ?? 'Курьер, завтра';
dl_push('add_shipping_info', [
'value' => $value,
'currency' => $currency,
'shipping_tier' => $shippingTier,
'items' => $items
]);
// 3) Перед редиректом на платежную систему, когда на сервере известен метод оплаты
$paymentType = $order->payment_method_label ?? 'Банковская карта';
dl_push('add_payment_info', [
'value' => $value,
'currency' => $currency,
'payment_type' => $paymentType,
'items' => $items
]);
Пояснения к бэку:
- Скрипты выводим в HTML-ответ страницы/шага (или во фрагмент, который вставляет фронт). Браузер пользователя выполнит
dataLayer.push, и GTM отправит данные в GA4. - Для SPA/AJAX можно вернуть JSON с полями (
event,ecommerce,payment_typeи т. д.) и на фронте сделать единый «диспетчер» пушей; но если «фронт не знает», прямой инлайн с бэка — надежнее. - Хотите видеть
payment_type/shipping_tier в отчетах как отдельные измерения — зарегистрируйте их как event-scoped custom dimensions в интерфейсе GA4 (Admin → Custom definitions).
Две важные оговорки:
- Не передавайте PII в GA: email, телефон, Ф. И. О., полный адрес, индексы и т. п. — под запретом. Если сильно нужно контролировать качество вводимых контактов — храните и анализируйте в CRM / внутреннем DWH, а в GA4 ограничьтесь флагом «контакты введены».
- Дедупликация: добавьте свой
checkout_session_id(UUID) как кастомный параметр к каждому из трех событий и используйте его для контроля дублей в QA/BigQuery.
Если коротко: теперь примеры отражают реальный чекаут — вход, доставка, оплата — на корректных событиях GA4 с нужными параметрами и без нарушения политики PII. Это даст вам прозрачную воронку и разрезы по методам доставки/оплаты из коробки.
Что это даст аналитикам
- Четкая воронка оформления: вход в чекаут → выбор доставки → выбор оплаты → покупка (дальше
purchase). - Сравнение конверсии по методам доставки/оплаты (дропы на конкретных опциях, влияние на CR и AOV).
- Возможность строить сегменты «выбрал оплату Х, но не купил» и ретаргетировать.
Отмена заказа / возвраты (refund)
Клиент отменяет заказ или оформляет возврат (полный или частичный).
Примеры:
- Заказ отменен еще до отгрузки.
- Покупатель вернул часть товаров в течение гарантийного срока.
- В CRM зафиксирован возврат, и нужно передать его в GA4.
В GA4 для этого предусмотрено событие refund:
- полный возврат — достаточно указать
transaction_id; - частичный возврат — указываем
transaction_idи списокitems, которые вернули.
Стандартный код для передачи события в dataLayer
<script>
// 1. Полный возврат заказа
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "refund",
ecommerce: {
transaction_id: "ORDER12345" // ID заказа из purchase
}
});
</script>
<script>
// 2. Частичный возврат (например, вернули только 1 товар из заказа)
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "refund",
ecommerce: {
transaction_id: "ORDER12345",
items: [
{
item_id: "SKU456",
item_name: "Носки спортивные",
quantity: 1
}
]
}
});
</script>
Комментарии к коду:
transaction_idобязателен: он связывает возврат с конкретным заказом.- Для частичных возвратов указываем только те товары, которые реально вернули.
- Цены можно не указывать — GA подтянет данные из события
purchase.
Реальная ситуация
Обычно возврат фиксируется в CRM, а фронт о нем не узнает. В этом случае пуш события делаем на сервере (например, на PHP) в момент, когда статус заказа меняется на «Возврат» или «Отменен».
Пример бэкенд-кода на PHP:
<?php
// Допустим, у нас в CRM изменился статус заказа:
$orderId = "ORDER12345";
$returnedItems = [
[ "sku" => "SKU456", "name" => "Носки спортивные", "qty" => 1 ]
];
// Собираем массив для dataLayer
$refundEvent = [
"event" => "refund",
"ecommerce" => [
"transaction_id" => $orderId,
"items" => array_map(fn($i) => [
"item_id" => $i["sku"],
"item_name" => $i["name"],
"quantity" => $i["qty"]
], $returnedItems)
]
];
// Выводим <script> на страницу ЛК или в админку (где грузится GTM)
echo '<script>window.dataLayer = window.dataLayer || [];</script>';
echo '<script>dataLayer.push({ecommerce: null});</script>';
echo '<script>dataLayer.push('
. json_encode($refundEvent, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
. ');</script>';
Пояснение:
- Такой код чаще всего внедряется в личный кабинет клиента или «страницу статуса заказа», которую он открывает после отмены/возврата.
- Если нужно передавать возвраты без участия пользователя (автоматически, когда заказ перешел в статус «Возврат»), то лучше использовать Measurement Protocol (отправка событий напрямую из бэка в GA4 без участия фронта). Это надежнее, чем ждать захода клиента на сайт.
Что это даст аналитикам
- Чистая выручка: revenue в отчетах GA4 будет скорректирован с учетом возвратов.
- Контроль качества: можно отслеживать долю возвратов по категориям товаров или каналам трафика.
- Фрод и логистика: высокий процент возвратов по определенным SKU или источникам = сигнал для маркетинга и отдела качества.
- ROI и окупаемость: корректный учет возвратов позволяет считать реальную прибыльность рекламных кампаний.
Разметка баннеров и промокодов (promotion_click, promotion_view)
На главной странице или в каталоге показывается баннер с акцией. Мы хотим понять, сколько пользователей его реально видели (promotion_view) и сколько кликнули (promotion_click).
Стандартный код для передачи события в dataLayer
Отображение баннера (promotion_view):
<script>
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "view_promotion",
ecommerce: {
items: [{
promotion_id: "promo_blackfriday",
promotion_name: "Black Friday 50% Off",
creative_name: "banner_homepage_top",
creative_slot: "slot_1"
}]
}
});
</script>
Клик по баннеру (promotion_click):
<script>
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "select_promotion",
ecommerce: {
items: [{
promotion_id: "promo_blackfriday",
promotion_name: "Black Friday 50% Off",
creative_name: "banner_homepage_top",
creative_slot: "slot_1"
}]
}
});
</script>
Комментарии к коду:
promotion_id— уникальный ID акции/баннера (например, из CMS или CRM).promotion_name— название кампании (например, Black Friday 2025).creative_nameиcreative_slotпомогают отслеживать, какая версия баннера (A/B тесты) и в каком месте страницы сработала лучше.coupon— конкретный промокод, который пользователь применил в корзине.
Реальная ситуация
Часто бывает, что фронт не контролирует применение промокода (например, проверка делается на сервере в момент оформления заказа). Или баннеры грузятся из CMS, а фронту не всегда доступны их ID.
В этом случае данные можно передать прямо с бэкенда.
Пример бэкенд-кода на PHP:
<?php
// Предположим, баннер загружается из базы или конфигурации
$promo = [
'id' => 'promo_blackfriday',
'name' => 'Black Friday 50% Off',
'creative_name' => 'banner_homepage_top',
'creative_slot' => 'slot_1'
];
?>
<script>
window.dataLayer = window.dataLayer || [];
// Сбрасываем предыдущие ecommerce-данные
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "view_promotion",
ecommerce: {
items: [{
promotion_id: "<?php echo $promo['id']; ?>",
promotion_name: "<?php echo htmlspecialchars($promo['name'], ENT_QUOTES, 'UTF-8'); ?>",
creative_name: "<?php echo $promo['creative_name']; ?>",
creative_slot: "<?php echo $promo['creative_slot']; ?>"
}]
}
});
</script>
<?php
// Используем те же данные баннера
$promo = [
'id' => 'promo_blackfriday',
'name' => 'Black Friday 50% Off',
'creative_name' => 'banner_homepage_top',
'creative_slot' => 'slot_1'
];
// JS-код, который будет вставлен в ссылку или onClick
$js = <<<JS
window.dataLayer = window.dataLayer || [];
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "select_promotion",
ecommerce: {
items: [{
promotion_id: "{$promo['id']}",
promotion_name: "{$promo['name']}",
creative_name: "{$promo['creative_name']}",
creative_slot: "{$promo['creative_slot']}"
}]
}
});
JS;
?>
<!-- Например, вставка в ссылку -->
<a href="/action-page" onclick="<?php echo htmlspecialchars($js, ENT_QUOTES, 'UTF-8'); ?>">
Перейти к акции!
</a>
Пояснение:
onclickвставляет пуш вdataLayerперед переходом.- Если используется JavaScript-фреймворк, вставьте этот код в событие клика.
Для аналитики это дает связку: какой баннер → какой промокод → какое количество продаж.
Комбинации и лайфхаки разметки
Список + «Избранное» + Покупка
Один из самых интересных путей пользователя — когда он просматривает список товаров, добавляет что-то в «Избранное» и затем совершает покупку. Разметка событий view_item_list, add_to_wishlist и purchase позволяет аналитикам отследить этот путь.
Лайфхак: создайте сегмент пользователей, которые добавили товар в wishlist, а затем купили его через неделю. Сравнивайте с теми, кто просто просматривал список, — это показывает реальную ценность «Избранного» и помогает точнее нацеливать email‑рассылки или push‑уведомления.
Checkout_steps + Промокоды
Комбинация событий begin_checkout, add_shipping_info, add_payment_info и apply_coupon позволяет оценить влияние промокодов на прохождение каждого шага оформления заказа.
Пример: вы видите, что пользователи активно применяют промокод на шаге «выбор способа доставки», но бросают корзину перед оплатой. Это сигнал, что скидка мотивирует попробовать корзину, но есть проблемы с оплатой или UX. Можно протестировать другой вариант промокода или упрощение шага оплаты.
Лайфхак: заведите кастомный параметр coupon_applied_at_step, чтобы видеть, на каком именно шаге промокод был применен, и строить отчет по drop-off по каждому шагу.
Promotion_click + Purchase
Комбинируя события баннеров (promotion_view, promotion_click) с покупками (purchase), вы получаете полную картину эффективности промоматериалов.
Пример: баннер на странице категории показывает товар, пользователь кликает на него и в итоге покупает. Сравнивая CTR и конверсии, можно выявить баннеры, которые привлекают внимание, но не приводят к продажам, и наоборот.
Лайфхак: заведите уникальные promotion_id для A/B тестов креативов и местоположений баннеров, чтобы видеть, какая комбинация креатив + слот работает лучше всего.
Добавление/удаление из корзины + Wishlist
Сочетание событий add_to_cart, remove_from_cart и add_to_wishlist помогает понять поведение пользователей относительно конкретных товаров.
Пример: товар часто уходит в «Избранное», но почти не покупается — это сигнал, что пользователи заинтересованы, но не готовы купить из-за цены, описания или наличия. Напротив, товары, которые часто добавляются и удаляются из корзины, но редко идут в wishlist, показывают моментальные импульсные покупки.
Лайфхак: анализируйте корзины и wishlist параллельно, чтобы корректировать ценовую стратегию, предлагать персонализированные скидки и планировать маркетинговые кампании для «высокого интереса / низкой конверсии».
Выводы
Расширенная e-commerce дает бизнесу возможность видеть полный путь пользователя — от первого просмотра списка товаров до финальной покупки и даже возврата. Это позволяет точно анализировать поведение покупателей, выявлять слабые места в воронке продаж и оптимизировать маркетинговые кампании.
Правильная комбинация разметок, включающая события по просмотрам списков, wishlist, шагам оформления заказа, промокодам и баннерам, значительно повышает качество данных. Аналитики получают полноценную картину конверсий, CTR, эффективности промоакций и влияния скидок на поведение пользователей.
Следующий шаг — интеграция всех этих событий в отчетные панели и BI-системы, что позволяет автоматически строить аналитику, видеть тренды в реальном времени и принимать обоснованные решения на основе данных. Это превращает цифры в инструмент для роста продаж и оптимизации маркетинга, а не просто в статические отчеты.
В следующих материалах я покажу сложные сценарии из реальной жизни, когда у нас нет ресурса backend, а в распоряжении только ресурс фронта. Многие скажут, как это? А это реальная жизнь и вполне рабочая ситуация. Следите за статьями.