Перейти к содержимому
Назад в Академию

Meta Conversions API: две отправки, одна покупка

13.03Server-side и API-интеграции
Содержание статьи

Интернет-магазин кофе в Алматы, заказ O42 на 15 000 ₸ оплачен картой на сайте (магазин и цифры в уроке условные). Это следующий шаг после урока про шлюз Conversions API: быстрый старт со шлюзом магазин прошёл, пиксель Meta теперь срабатывает только после оплаты, а нанятый разработчик отправляет оплаченные заказы из CRM с сервера. В первой версии ID каждая сторона придумала свой, и Meta посчитала одну оплату дважды. Через неделю владелец замечает, что покупок в Ads Manager стало вдвое больше, чем до запуска сервера, хотя оплат в CRM не прибавилось, и спрашивает, каким цифрам верить.

Зачем сервер, если есть пиксель

Conversions API (CAPI) - канал, по которому ваш сервер сам сообщает Meta о событиях: POST-запрос на https://graph.facebook.com/{API_VERSION}/{PIXEL_ID}/events с токеном доступа. Пиксель работает в браузере и видит только то, что случилось, пока страница открыта. Если покупатель оплатил и закрыл вкладку, не дождавшись «Спасибо», браузер промолчит, а сервер магазина об оплате знает и может сам отправить событие: на этом и строится серверный трекинг.

Meta советует не выбирать между каналами, а отправлять одни и те же события и через пиксель, и через CAPI. В справке это называется конфигурацией с избыточными событиями: сервер подхватит то, что пиксель потерял из-за сбоя сети или ошибки при загрузке страницы (рекомендации по Conversions API). У такой схемы есть цена: каждая покупка приходит в Meta дважды, и нужно, чтобы её посчитали один раз.

Сначала найдите все источники Purchase

Прежде чем писать серверную отправку, выясните, кто уже шлёт Purchase в этот пиксель. Это может быть код в шаблоне сайта, контейнер GTM, модуль интернет-магазина, партнёрская интеграция, включённый кем-то «Conversions API с поддержкой Meta», шлюз Conversions API или чей-то самописный скрипт. Шлюз с прошлого шага у нашего магазина как раз из этого списка. Если его не отключить, сервер станет третьим отправителем, и придётся выяснять, совпадают ли ключи у всех трёх. Разработчик шлюз отключил: сервер и так знает о каждой оплате.

По каждому отправителю запишите четыре вещи: когда он срабатывает, откуда берёт сумму, откуда ID и в какой пиксель отправляет. У нашего магазина после разбора осталось два отправителя:

ОтправительКогда срабатываетСуммаIDПиксель
Пиксель на странице «Спасибо»Страница открылась после подтверждения оплаты картойИз dataLayerНомер заказаПиксель магазина
Сервер магазинаЗаказ получил в CRM статус «оплачен»Из заказа в CRMНомер заказаТот же пиксель

Покупка в этом магазине - подтверждённая полная оплата заказа. Страница «Спасибо», которая открывается без проверки статуса, покупкой не считается: на неё можно вернуться из истории браузера или попасть после отказа банка. А если браузер подтверждения не увидел, потому что вкладку закрыли, сервер отправляет покупку один, без браузерной пары. Придумывать браузерное событие для симметрии не нужно.

Одна покупка - один ID

Meta склеивает браузерную и серверную копии, когда у них совпадают две вещи: имя события (event в пикселе и event_name на сервере) и идентификатор (eventID в пикселе и event_id на сервере). Обе копии должны прийти в один и тот же пиксель, а склейка работает в пределах 48 часов от первого события с этим ID (дедупликация событий пикселя и Conversions API). Значит, браузерная и серверная отправки одной покупки несут одно имя и один ID. В пикселе идентификатор передают четвёртым аргументом:

fbq('track', 'Purchase', {value: 15000, currency: 'KZT'}, {eventID: 'O42'});

Какой ID брать, Meta подсказывает сама: номер заказа или транзакции. Если клиент оформил два заказа, у каждой пары отправок свой номер, и Meta видит две покупки, а не четыре (параметры серверного события).

У одной покупки один ключ, даже при повторе

Магазин кофе, заказ O42
ОтправкаИмяКлюч
БраузерPurchaseeventID: O42
СерверPurchaseevent_id: O42
Повтор серверной отправки после тайм-аутаPurchaseevent_id: O42
Следующий заказ того же клиентаPurchaseevent_id: O57

Meta склеит копии, если они пришли в один пиксель с тем же именем и ID не позже 48 часов после первого события с этим ID.

O42 одинаков в браузере, на сервере и при повторе; следующая покупка того же клиента получает O57.

ID привязан к покупке, а не к HTTP-запросу. Если сервер не дождался ответа и отправил O42 ещё раз, повтор идёт с тем же ключом: повторные серверные события с одинаковыми именем и ID Meta отбрасывает как дубли (о дедупликации событий), а с новым ID повтор станет второй покупкой. ID клиента из CRM на эту роль не годится, потому что он одинаковый у всех заказов человека.

Отдельно договоритесь о частичных оплатах. Допустим, кофемашину за 240 000 ₸ берут с предоплатой 50 000 ₸ и доплатой при получении. Можно отправить одну покупку O77 на 240 000 ₸, когда заказ оплачен полностью. Можно отправлять каждый платёж отдельной покупкой на его сумму и со своим ID, O77-1 и O77-2, понимая, что покупок в кабинете станет больше, чем заказов. А вот так нельзя: отправлять Purchase на полные 240 000 ₸ после каждого платежа (выручка в кабинете удвоится) или вешать O77 на оба платежа (доплату Meta может принять за копию предоплаты).

Время и источник события

event_time - момент, когда покупка произошла, в Unix-секундах, а не время отправки запроса. Meta принимает события не старше семи дней, и если в пачке окажется хотя бы одно более старое, ошибкой вернётся весь запрос (параметры серверного события). Отправлять при этом советуют сразу, в идеале в течение часа (отправка событий через API). Для очереди повторов отсюда два правила. Повтор сохраняет исходный event_time. События, которые пролежали в очереди дольше недели, из пачки убирают, иначе вместе с ними отклонят и свежие.

action_source говорит, где произошло действие, а не откуда пришёл запрос. Покупка на сайте остаётся website, даже когда её отправляет сервер. Для веб-событий Meta требует ещё event_source_url, адрес страницы, на которой всё случилось, и client_user_agent.

Вот серверная отправка заказа O42 целиком:

{
"data": [
{
"event_name": "Purchase",
"event_time": 1791790200,
"event_id": "O42",
"action_source": "website",
"event_source_url": "https://shop.example.com/checkout/success",
"user_data": {
"em": ["882a7adc5f92074a7721b6e396112855a0d57cdff5586ecac743995ad252dbcc"],
"ph": ["7c1dc35860507e0fee0dee49def2433f25534035de81deb1bbc6f4c006e4c90a"],
"fbp": "fb.1.1791789120000.1234567890",
"fbc": "fb.1.1791789000000.IwAR0exampleClickId",
"client_ip_address": "203.0.113.24",
"client_user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1"
},
"custom_data": {
"value": 15000,
"currency": "KZT",
"order_id": "O42"
}
}
]
}

event_time здесь - 12 октября 2026 года, 12:30 по Алматы, когда пришло подтверждение оплаты. value и currency для Purchase обязательны. Токен доступа создают в Events Manager, в настройках пикселя, в разделе Conversions API (Generate access token), и хранят только на сервере. Ему не место в JavaScript страницы, в dataLayer и на скриншоте для поддержки.

Что хешировать, а что нет

Контакты сначала нормализуют, потом хешируют SHA-256. Email - без пробелов по краям и в нижнем регистре. Телефон - только цифры, обязательно с кодом страны: +7 701 123-45-67 превращается в 77011234567. Так же обязательно хешируют имя, фамилию, дату рождения, пол, город, регион, индекс и страну, а external_id хешировать рекомендуют (параметры информации о клиенте).

fbp, fbc, client_ip_address и client_user_agent не хешируют никогда: Meta ждёт их в исходном виде. Поэтому прогонять весь user_data через SHA-256 одной функцией нельзя. И выясните, не хеширует ли контакты сам ваш SDK или серверный тег: хеш от хеша ни с кем не сопоставится.

fbc - идентификатор рекламного клика. Он лежит в куке _fbc, а если куки нет, его собирают из параметра fbclid в адресе страницы по формату fb.1.<время в миллисекундах>.<fbclid>. Сам fbclid копируют как есть, он чувствителен к регистру (fbp и fbc). Если человек пришёл не по рекламе и fbclid не было, у события нет и fbc. Собрать его из случайной строки или ID кампании - значит придумать клик, которого не было.

client_ip_address - IP браузера покупателя, и Meta просит передавать настоящий адрес. Серверное событие уходит, когда заказ получил в CRM статус «оплачен», и в этот момент у сервера под рукой только собственный IP или IP платёжного сервиса. Подставите его - и все покупки магазина придут будто бы с одного компьютера. Поэтому IP, user agent, _fbp и _fbc записывают в заказ при оформлении, пока покупатель на сайте, и при отправке берут оттуда.

Чего не отправлять вовсе

Хеш прячет строку, но не даёт права её передавать. Meta запрещает отправлять через свои инструменты сведения о здоровье, финансовом положении и другие чувствительные данные: диагнозы и лекарства, одобрение или отказ по кредиту, кредитный лимит, остаток на счёте (о запрещённой информации). Запрет распространяется на параметры URL и на названия собственных событий. Для МФО не годится событие «одобрен кредит на 300 000 ₸», для клиники - событие с названием услуги вроде «приём эндокринолога», даже если запрос технически проходит. Список событий и полей для таких клиентов согласуют с их юристом до разработки.

Выбор человека действует и на сервере. Meta требует, чтобы инструменты учитывали согласие там, где его требует закон, и про Conversions API пишет прямо: не отправляйте события людей, которые согласия не дали (требования к конфиденциальности). Отказ от рекламных cookies должен останавливать и немедленную отправку, и очередь повторов.

EMQ: диагноз, а не цель

В Events Manager у серверных веб-событий есть оценка «Качество сопоставления событий» (Event Match Quality, EMQ) от 0 до 10. Meta считает её по последним 48 часам: по тому, какие данные о клиенте пришли с событием, и по доле событий, которые удалось связать с аккаунтами (о качестве сопоставления событий). Там же указан приоритет полей: у email и ID клика он высокий, у телефона, внешнего ID и ID браузера средний, у имени и города низкий.

Пользуйтесь оценкой как диагнозом. Если у Purchase она заметно ниже, чем у других событий, откройте вкладку «Сопоставление событий» и посмотрите, каких полей не хватает: возможно, сервер не передаёт fbc или email, хотя они есть в заказе. А вот цель «поднять EMQ до девяти» вредна. Её легко выполнить, собирая лишние персональные данные, и продаж от этого не прибавится. Оценка говорит только о том, что Meta поняла, чьё это событие. Оплачен ли заказ, не задвоился ли он и принесла ли его реклама, EMQ не покажет: это выясняют сверкой с CRM и тестами на прирост.

Проверка до запуска

Сначала отправьте события с test_event_code. Код выдаёт вкладка «Тестирование событий» в Events Manager, серверные события появляются там вскоре после отправки, а тестовые данные хранятся 24 часа (проверка событий сервера). Там же видно, какие события Meta приняла, а какие отбросила как дубли (о дедупликации событий). В рабочих запросах кода быть не должно.

После запуска вкладка «Дедупликация событий» покажет, какие ключи вы передаёте (контроль работы Conversions API). Общий счётчик на странице набора данных показывает браузерные и серверные события без дедупликации, так что двойные цифры там - норма. Покупки после склейки считает Ads Manager (различия в подсчёте событий).

Затем прогоните восемь сценариев:

СценарийЧто должно уйти в Meta
Обычная оплата O42Пиксель и сервер, оба с O42, в итоге одна покупка
Сервер не дождался ответа и повторилТот же O42 с тем же event_time
Тот же клиент купил ещё разНовая пара с O57
Оплатил и закрыл вкладкуТолько серверное событие O42
Банк отклонил платёжНичего
Предоплата за кофемашинуТо, что решили по правилу частичной оплаты
Отказ от рекламных cookiesНичего, в том числе из очереди повторов
Очередь простояла восемь днейСтарые события не уходят, свежие уходят отдельной пачкой

Для разбора в логах храните ID событий, время, версию схемы и ответы API. Полному user_data в обычных логах делать нечего.

Упражнение: один клиент купил дважды

Разработчик поставил event_id равным ID клиента из CRM для всех его покупок, пиксель передаёт тот же ID. Имя и ключ у браузера и сервера совпадают. Настроена ли дедупликация?

Нет. Первая покупка склеится правильно, но у всех следующих покупок этого человека тот же ключ, и Meta не отличит их друг от друга. Заказ, оплаченный в пределах 48 часов после первого, она примет за копию и не посчитает. Ключом должен быть номер заказа: общий для браузерной и серверной отправки одной покупки, тот же при повторе и разный у разных покупок. После исправления прогоняйте две покупки одного клиента подряд, а не один тестовый заказ.

Теперь на своём проекте, тестовом магазине или у клиента:

Выполнено 0 из 5

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

Видео: знакомство с CAPI

Facebook Conversions API

Обзор от PIRATE CPA для первого знакомства. Названия полей и лимиты в уроке сверены с документацией Meta на октябрь 2026 года.

Нужна помощь с аналитикой вашего проекта?