Прежде чем что-то менять, проверьте одно событие Purchase
Если Meta, судя по всему, считает покупку дважды, начните с одной известной покупки и событий, которые уже доступны в Events Manager или в логах вашей интеграции. Не отключайте работающую интеграцию и не оформляйте ещё один платный заказ только ради этой проверки.
- Составьте список отправителей. Запишите, какие нативный канал, приложения и контейнеры GTM отправляют
Purchaseи в какой dataset. - Сравните одну пару. Для этой же покупки зафиксируйте браузерный
eventID, серверныйevent_idи названия событий. Отметьте каждое поле как совпадающее, различающееся или недоступное. Пропущенное значение не угадывайте. - Не смешивайте наблюдение с диагнозом. Две входящие записи или разница между итогами Meta и Shopify сами по себе не доказывают, что покупку посчитали дважды. Сохраните данные событий и временное окно для дальнейшей проверки.
Если значения различаются или приложение их скрывает, подготовьте бриф на проверку Meta Purchase. Он переносит эти наблюдения в запрос, и вам не нужно ни отправлять данные клиентов, ни сначала менять настройки.
Как Meta сопоставляет браузерное событие с серверной парой?
Conversions API от Meta рассчитан на работу вместе с браузерным Pixel, а не вместо него. Они должны описывать одно и то же реальное действие с двух точек наблюдения: Pixel срабатывает в браузере покупателя, Conversions API отправляет то же событие с вашего сервера.
Отправка обоих событий рекомендована, потому что каждый источник закрывает слепые зоны другого: браузер передаёт события, которые мог пропустить сервер, а сервер передаёт события, потерянные в браузере из-за блокировщика рекламы или ограниченного срока cookie.
Загвоздка в том, что теперь у Meta две записи об одной покупке. Дедупликация объединяет их в одну запись. Когда она работает, вы получаете охват двух источников и учитываете одну покупку. Когда нет, покупка учитывается дважды. Это искажает число конверсий, стоимость покупки и ROAS так, что неэффективные кампании выглядят успешными.
Дедупликация не работает автоматически. Это сопоставление по ключам, которые вы сами обязаны задать правильно.
По чему Meta сопоставляет события Pixel и CAPI для дедупликации?
Основной метод дедупликации в Meta сравнивает два поля в браузерном и серверном событиях:
- event_id: уникальный идентификатор конкретного события. В Pixel он передаётся как
eventID, в теле запроса Conversions API это полеevent_id. Значение одно, написание разное. - event_name: стандартное событие, например
Purchase. Параметрeventв Pixel должен совпадать сevent_nameв Conversions API.
Если у браузерного и серверного события совпадают event_id и event_name, а Meta получает второе из них в течение 48 часов после первого, они считаются одним событием. По документации Meta, сопоставление идёт по id события и названию события вместе: одно без другого не подходит.
Вот что на самом деле происходит в каждом случае сбоя:
- event_id различается в двух источниках. Совпадения нет. Meta записывает две конверсии. Одна покупка превращается в две.
- event_id генерируется заново при каждой загрузке страницы. Браузер и сервер никогда не передают одно и то же значение одновременно, поэтому ничего не совпадает. Это самая частая ошибка, которую люди создают себе сами.
- event_id отсутствует с одной стороны. Сопоставлять не с чем. Считаются оба.
- event_name различается (
Purchaseиpurchase). Id могут совпадать идеально, но сопоставление всё равно не сработает, потому что название входит в ключ. - Второе событие приходит позже чем через 48 часов. За пределами окна Meta уже зафиксировала первое, и запоздавшая пара считается отдельно.
Бизнес-последствие всегда одно: завышенные конверсии дают завышенный ROAS. Кампания с отчётным ROAS 4,0 при двойном учёте на деле работает на 2,0. Вы масштабируете её, потому что панель показывает её успешной, и направляете бюджет в кампанию, которая в лучшем случае окупается. Дубли событий добавляют шум и ведут к ошибочным решениям.
Почему Shopify дублирует события Meta Purchase?
В Shopify дублирование редко возникает из-за одного неверно настроенного тега. Его создают две системы, каждая из которых отправляет Purchase независимо от другой. Типичные сочетания:
- Нативный канал Facebook & Instagram + CAPI через GTM или приложение. Нативный канал отправляет свои браузерные и серверные события. Добавьте второй серверный путь через GTM или приложение, и на каждый заказ будет два события Purchase без общего id.
- Приложение CAPI + пиксель темы или Customer Events. Приложение отправляет серверное Purchase, пиксель витрины отправляет браузерное Purchase, а приложение генерирует собственные id, которых пиксель не видит.
- Два приложения CAPI, установленных одновременно. Часто это остаток прежней настройки, который так и не удалили. Срабатывают оба, и друг с другом они не согласуются.
- Пиксель checkout.liquid + пиксель Customer Events при недоделанном переходе на Checkout Extensibility: оба остаются на странице «Спасибо».
Что объединяет эти случаи: каждый источник может быть верен по отдельности, а их сочетание всё равно удваивает данные, потому что дедупликация работает, только когда источники передают одинаковый event_id. Два исправных источника, которые не обмениваются id, хуже одного: они создают впечатление полного охвата и незаметно считают покупку дважды. Если вы разбираетесь, какие источники работают, диагностика дедупликации Meta CAPI проведёт по инвентаризации шаг за шагом.
Дерево решений для диагностики
Сравнительная таблица: прокрутите вбок, чтобы увидеть все столбцы
| Что вы видите в Events Manager | Вероятная причина | Что происходит | Исправление |
|---|---|---|---|
Две строки Purchase на заказ, обе «Processed», без метки «Deduplicated» | Два источника без общего id | event_id отсутствует или различается с каждой стороны | Используйте один источник id для обоих событий; передавайте одно и то же значение в Pixel и на сервер |
| Пары совпадают в Test Events, но в продакшене число по-прежнему около 2× заказов | event_id генерируется заново при каждой загрузке страницы | Id совпадают только внутри одной загрузки, но не между браузером и сервером | Берите event_id из id заказа, а не из Date.now() и не из нового UUID |
| Серверные события считаются отдельно; браузерные выглядят нормально | Несовпадение event_name | Сервер отправляет purchase или PURCHASE, Pixel отправляет Purchase | Приведите обе стороны к Purchase (регистр важен) |
| Часть заказов дедуплицируется, часть нет | Пара уходит за 48-часовое окно, или сервер повторяет отправку с новыми id | Запоздавшая пара или пара с изменённым ключом оказывается вне сопоставления | Стабилизируйте id; убедитесь, что сервер отправляет без задержки |
| Общее число выглядит верным, но EMQ низкий | Это не проблема дедупликации | Дедупликация в порядке; обогащение пользовательскими данными слабое | Отдельная проблема: см. Event Match Quality |
Как проверить дедупликацию в Events Manager?
По главной панели это не диагностируется: она показывает итоги, а итоги скрывают двойной учёт. Откройте источник событий и перейдите в Test Events:
- В Events Manager выберите свой dataset (Pixel), откройте вкладку Test Events и скопируйте тестовый код.
- Сделайте реальную тестовую покупку (или используйте тестовый код в поле
test_event_codeсерверного запроса, чтобы серверное событие тоже появилось). - Следите за лентой входящих событий. Правильно сопоставленное событие появляется с индикатором «Deduplicated», а при раскрытии видно, что оно получено и из Browser, и из Server с одним и тем же
event_id. - Если вместо этого вы видите две отдельные записи
Purchase, одну Browser и одну Server, без метки дедупликации, ваши id или названия не совпадают. - Для исторических данных откройте отдельное событие в Overview или в подробностях события и посмотрите connection method. У корректной пары указаны и браузер, и сервер; две независимые строки на заказ выдают проблему.
Искать нужно буквальную метку «Deduplicated» на паре браузер + сервер. Если вы отправляете оба события, а метки нет, это и есть ошибка.
Набросок правильной реализации
Вся починка сводится к одной идее: сгенерируйте event_id один раз, привяжите его к заказу и передайте точно такую же строку и в Pixel, и на сервер. Вот технически корректный вариант.
Браузер (пиксель Customer Events или темы), eventID передаётся четвёртым аргументом в fbq:
// One id per purchase, derived from the order so it is identical on the server.
// NOT Date.now(), NOT a fresh crypto.randomUUID() on each load.
const eventId = `purchase_${order.id}`; // e.g. "purchase_4512890"
fbq('track', 'Purchase', {
value: 129.00,
currency: 'USD',
contents: [{ id: 'SKU-123', quantity: 1 }],
content_type: 'product'
}, { eventID: eventId });
Сервер (тело запроса Conversions API), с тем же значением в поле event_id в snake_case и тем же event_name:
{
"data": [
{
"event_name": "Purchase",
"event_time": 1690000000,
"event_id": "purchase_4512890",
"action_source": "website",
"event_source_url": "https://store.example/checkout/thank-you",
"user_data": {
"em": ["<sha256 of lowercased, trimmed email>"],
"fbp": "fb.1.1690000000.1234567890",
"fbc": "fb.1.1690000000.AbCdEfGh"
},
"custom_data": { "value": 129.00, "currency": "USD" }
}
]
}
Результат зависит от трёх условий: строки event_id совпадают байт в байт, названия событий в обоих случаях ровно Purchase (регистр важен), а id берётся из заказа, поэтому перезагрузка страницы его не изменит. Поля fbp, fbc и em улучшают качество сопоставления, но дедуплицируют не они: дедупликацию обеспечивают только event_id и event_name.
Как я проверяю это в реальных внедрениях
Я разрабатываю серверные пайплайны и основал Fixel Pixel, поэтому склоняюсь к серверным схемам. Именно поэтому дедупликацию я проверяю подсчётами, а не заявлениями.
Мой порядок проверки в каждом проекте по CAPI:
- Сначала инвентаризация всех источников. Прежде чем трогать код, я перечисляю все системы, которые могут отправить
Purchase: нативный канал Facebook, любые приложения CAPI, GTM web, серверный GTM, пиксели темы. Дублирование почти всегда оказывается лишним источником, о котором никто не помнил, а не сломанным тегом. - Параллельный запуск и Test Events вместо панели. Я делаю реальную покупку с
test_event_codeна сервере, чтобы появились обе половины пары, и затем убеждаюсь в буквальной метке «Deduplicated» на совпавшей паре. - Сверка счётчиков с заказами. За фиксированный период я сравниваю число
Purchase, о котором сообщает Meta, с реальным числом заказов Shopify. Корректная настройка даёт значение, близкое к 1:1. Число событий около 2× числа заказов выдаёт пропущенную дедупликацию, даже когда отдельные тестовые события выглядят нормально. - Проверка стабильности id. Я перезагружаю страницу «Спасибо» и запускаю событие заново, чтобы убедиться, что
event_idмежду загрузками не меняется. Если он меняется, дедупликация в продакшене сломается, хотя один чистый тест прошёл.
Сверку пропускают чаще всего, и только она выявляет периодическое дублирование.
Типичные сценарии сбоев
- Id генерируются заново при каждой загрузке страницы.
event_idстроится изDate.now(),Math.random()или нового UUID при рендере. Каждая загрузка даёт новое значение, поэтому серверная пара никогда не совпадает. Берите id из заказа. - Компромисс: id из заказа или случайный UUID. Id из заказа стабилен и его легко воспроизвести на сервере, но его можно угадать, и он повторяется, если тот же заказ отрисован снова. Для дедупликации это обычно нормально. Случайный UUID не угадать, но он работает, только если вы генерируете его один раз и сохраняете, чтобы обе стороны читали одно и то же значение. Если сгенерировать его дважды, дублирование гарантировано. В большинстве настроек Shopify надёжнее id из заказа.
- Расхождения в регистре и названиях.
purchaseиPurchase, либо пользовательское событие с одной стороны и стандартное с другой. - Путаница camelCase и snake_case.
eventIDв Pixel,event_idв серверном запросе. Если передатьevent_idвfbq(илиeventIDв серверный JSON), молча ничего не произойдёт. - 48-часовое окно. Пакетные или отложенные серверные отправки, которые приходят позже чем через 48 часов после браузерного события, не дедуплицируются.
- Удалённые приложения, которые продолжают срабатывать. Удалённое приложение, у которого фрагмент пикселя или вебхук остался в теме.
Меняет ли согласие то, что показывает дедупликация?
Дедупликация может сопоставить только события, которые существуют с обеих сторон. Если покупатель отказывает в маркетинговом согласии, блокировка по согласию останавливает Pixel, а серверный путь, который следует тому же сигналу согласия, не отправляет то же событие. Ничего не отправляется, значит, нечего сопоставлять и нечего считать.
Прежде чем назвать серверный Purchase из чекаута витрины без браузерной пары сбоем дедупликации, задайте ещё один вопрос: давал ли этот покупатель согласие? Если давал, браузерное событие, скорее всего, не дошло до Meta, например из-за блокировщика рекламы, закрытой вкладки до страницы подтверждения или Pixel, который там не сработал. Тогда серверное событие считается один раз (ради этого и запускают оба), и оно не дедуплицируется, потому что сопоставить его не с чем. Если покупатель отказал, вероятная причина в серверном пайплайне, который проигнорировал отказ, учтённый браузером: это ошибка согласия, а не дедупликации. Привяжите серверную отправку к тому же сигналу согласия. Речь о технической реализации, это не юридическая консультация.
Ограничения
Дедупликация сводит только одно и то же событие, описанное дважды. Она не объединяет разные события и не спасёт настройку, где два источника описывают разное (скажем, браузер срабатывает на странице «Спасибо», а сервер при списании оплаты с другим id: это два события, и посчитаны они правильно).
Она также относится только к Meta. Если привести в порядок Meta, для GA4 и Google Ads это ничего не даст: у них свои правила подсчёта. А идеальный процент дедупликации ничего не говорит о качестве сопоставления: можно безупречно дедуплицировать и при этом отправлять события, которые Meta едва может атрибутировать. Дедупликация нужна, чтобы считать один раз, а качество сопоставления решается отдельно.
Альтернативы
Если передать общий event_id нельзя, например закрытое приложение его не раскрывает, запасной вариант Meta сопоставляет по fbp или external_id плюс event_name, но только когда браузерное событие приходит первым и обе стороны последовательно передают идентификатор. Рассматривайте это как резервный способ, а не основную схему.
Более надёжный вариант: свести всё к одному серверному пути, чтобы id задавался в одном месте, а браузерный Pixel его повторял. Обычно это часть более широкой настройки серверного трекинга или Meta Conversions API, и поэтому я, как правило, предпочитаю один пайплайн с нужными проверками вместо трёх, согласованных лишь частично. Работа такого пайплайна разобрана в гайде Серверный трекинг: преимущества, ограничения и архитектура.
Нужна помощь с проверкой настройки Meta Purchase?
Используйте этот бриф, чтобы запросить проверку источников отправки и идентификаторов событий. Следующий шаг: выяснить, что можно подтвердить, затем согласовать работы по внедрению. Выбирать новое приложение CAPI заранее не нужно.
Если гайд не решил вопрос25 000 ₽
Я провожу эту сверку на вашем магазине в режиме только чтения, за 25 000 ₽: письменный вывод через два рабочих дня после старта.