Интернет-магазин бытовой техники в Алматы. Большинство заказов здесь оплачивают наличными при доставке, и в серверный поток оплат они не попадают, а заказ T-310 на 48 000 ₸ оплатили картой на сайте (цифры в уроке условные). Покупатель заплатил на странице платёжного сервиса, увидел «Оплата прошла» и закрыл вкладку, не дожидаясь возврата на сайт. Пиксель Meta стоит на странице «Спасибо», поэтому из браузера Purchase не ушёл. А платёжный сервис уже прислал серверу магазина уведомление: деньги списаны, заказ оплачен.
Оплата доходит без открытой страницы
Заказ T-310 на 48 000 ₸, вкладка закрыта
Уведомление об оплате
Сервер проверяет уведомление провайдера
Сверяет операцию, сумму, валюту и заказ
Одна транзакция базы
Заказ получает статус «оплачен»
В outbox записывается событие с ID
Отдельный обработчик
Отправляет строку и записывает результат
При повторе ID события тот же
Статус оплаты и строка outbox записываются вместе: либо оба, либо ни один.
S2S и два значения server-side
Если событие отправляет сама система, которая знает об оплате, это S2S, server-to-server: запрос идёт с сервера магазина на сервер Meta, Google Ads или GA4, а браузер в нём не участвует. Закрытая вкладка такому событию не мешает.
Server-side называют и схему, где браузер шлёт события в ваш серверный контейнер; если в контейнер пишет только браузер, закрытую вкладку он не спасёт. Обе схемы и то, что сохранить при заявке, показаны на примере оплаты после заявки.
Сначала решите, что такое покупка
У заказа много шагов: создание, блокировка суммы на карте, списание, частичная оплата, отмена, возврат. Purchase должен означать один из них, и это решение записывают словами до первой строки кода. Для магазина правило такое: покупка - подтверждённая полная оплата заказа. Возвраты идут отдельным процессом. Если в магазине есть рассрочка или подписка, для неё пишут своё правило, иначе полная стоимость заказа уйдёт в рекламу при каждом платеже.
Источник факта для онлайн-оплаты - уведомление платёжного сервиса, а не открытая страница «Спасибо». Уведомлению тоже не верят на слово. Сервер проверяет его так, как требует платёжный сервис (подписью или запросом статуса операции), и сверяет номер операции, сумму, валюту и состояние заказа. Иначе любой, кто узнал адрес обработчика, «оплатит» заказ запросом со словом paid.
Цепочка одного заказа
По заказу T-310 сервер записывает каждый шаг, чтобы через неделю можно было ответить, где событие и что с ним случилось:
| Этап | Что записываем | Пример для T-310 |
|---|---|---|
| Подтверждение | какая операция сменила статус заказа и когда | уведомление об оплате 48 000 ₸ в 14:05 |
| Решение об отправке | подходит ли факт под правило покупки и можно ли его передавать | полная оплата, покупатель разрешил рекламные cookies |
| Подготовка | ID события, время оплаты, сумма, валюта, версия схемы | T-310, 14:05, 48000, KZT, v1 |
| Доставка | число попыток, последняя ошибка, время следующей попытки | две попытки, первая закончилась тайм-аутом |
| Ответ | принял ли получатель событие | Meta ответила без ошибки |
| Сверка | сходятся ли за период заказы, отправки и то, что показывает получатель | T-310 попадает в месячную сверку четырёх чисел |
Это схема для проекта, а не формат API. Поля, правила повторов и способы сверки у Meta, Google Ads и GA4 разные, и под каждого получателя пишут свой адаптер.
Одну живую покупку у Meta не найти. Отдельные события видно только в «Тестировании событий», общие счётчики Events Manager дубли не убирают, а сработала ли склейка, показывают вкладка «Дедупликация событий» и Ads Manager. Судьбу отдельного заказа ищут в своём журнале, а у получателя смотрят итоги за период.
Outbox: оплата и событие записываются вместе
Самое опасное место - промежуток между «заказ оплачен» и «событие отправлено». Если обработчик уведомления сохраняет оплату и тут же шлёт запрос в Meta, падение сервера между этими двумя действиями оставит оплаченный заказ без события, и никто этого не заметит.
Надёжнее записывать их вместе, по шаблону transactional outbox. В той же транзакции базы данных, где заказ получает статус «оплачен», в отдельную таблицу исходящих событий (outbox) пишется строка: что отправить, кому и с каким ID. Записывается либо и то и другое, либо ничего. Отправляет отдельный обработчик: берёт неотправленные строки, шлёт их и отмечает результат. Упал сервер, строка осталась и уйдёт после перезапуска.
У шаблона есть обратная сторона: после сбоя обработчик может отправить одну строку дважды. ID события поэтому хранится в самой строке outbox и не меняется между попытками.
Таблица outbox заодно показывает, что застряло. Строки старше часа без отметки об отправке - повод для оповещения. А строки старше недели в Meta уже не уйдут: она не принимает события с event_time старше семи дней. Такие строки помечают просроченными, а не повторяют бесконечно.
Повтор не должен создавать вторую покупку
Получатель принял запрос, а ответ потерялся в сети. Обработчик видит тайм-аут и не знает, дошло ли событие. Если при каждой попытке создавать новый ID, повтор превратится во вторую покупку T-310.
С тем же ID повтор безопасен: Meta убирает повторные серверные события с одинаковыми event_name и event_id. Номер заказа годится в ID, только пока правило допускает одну покупку на заказ. Если заказ оплачивают частями, у каждой операции свой ключ. Когда та же покупка приходит ещё и из браузера (покупатель всё-таки дождался страницы «Спасибо»), Meta склеивает пару с одинаковыми event_id и event_name, если они пришли в течение 48 часов. Эту пару подробно настраивают в уроке про Meta Conversions API.
Ошибки бывают двух видов. Временные, вроде тайм-аута или перегруженного сервера получателя, повторяют с растущей паузой и с пределом, например пять попыток за несколько часов. Постоянные (не та валюта, нет обязательного поля, просроченное время) повторять бесполезно, их сначала исправляют. Такие события уходят в очередь разбора, и у очереди есть хозяин. Бесконечный повтор неправильного запроса нагружает сервер и не добавляет ни одной покупки.
Сверка: четыре числа за месяц
Раз в месяц сравнивают четыре числа. Пример за закрытый месяц:
- в базе магазина 100 заказов, оплаченных картой на сайте (наличные при доставке в серверный поток оплат не попадают);
- 80 из них подходят под правило передачи в Meta, остальные 20 покупателей отказались от рекламных cookies;
- все 80 отправлены, 78 Meta приняла;
- в Ads Manager рекламе приписаны 50 покупок.
Разницу 100 - 50 хочется назвать «потеряли 50 покупок». На самом деле она складывается из трёх частей, и разбираются с ними разные люди:
| Разница | Что это | Что делать |
|---|---|---|
| 100 - 80 = 20 | заказы, которые по правилу не отправляются | ничего не чинить, только проверить, что правило выполняется |
| 80 - 78 = 2 | две доставки, которые Meta не приняла | эти две должна была поймать очередь outbox в течение недели; месячная сверка только подтверждает, что так и было |
| 78 - 50 = 28 | покупки, которые Meta приняла, но не приписала рекламе | разбираться с атрибуцией, а не с доставкой |
20 + 2 + 28 = 50: вся разница разложена.
Последние 28 покупок никуда не пропали. Meta приписывает покупку объявлению, только если узнала покупателя и если покупка случилась в окне атрибуции. По умолчанию Meta засчитывает объявлению события в течение семи дней после клика по ссылке и одного дня после просмотра, а окно по кликам можно сократить до одного дня. Среди 28 могут быть покупатели из поиска, с 2GIS или по совету знакомых, люди, которых Meta не смогла сопоставить, и те, кто кликнул больше недели назад. Сколько из них каких, по четырём числам не сказать.
Перед сравнением выровняйте период и часовой пояс. Учтите, что Ads Manager показывает покупку в дне показа объявления, а база магазина - в дне оплаты, и договоритесь, как считать возвраты.
Если после запуска S2S в кабинете стало больше покупок, изменилось измерение. Заказы лежат в базе магазина, и от новой интеграции их не прибавилось. Продажи и окупаемость рекламы считают по базе, а кабинет показывает, какую часть этих продаж Meta теперь видит.
Свой сервер - точка контроля, а не готовая настройка
Перенос отправки на свой сервер даёт место, где решают, какие данные и куда уходят. Сами решения сервер за вас не примет: какие поля отправлять, каким получателям и с какими разрешениями, настраивает команда. Данные от переезда не становятся анонимными, телефон в запросе остаётся телефоном на любом сервере. И доставку по-прежнему проверяют: серверные отправки тоже теряются, задваиваются и уходят с ошибками.
Согласие покупателя едет по тому же маршруту. Если человек отказался от рекламных cookies, его оплата не уходит в Meta ни сразу, ни при повторе, ни при выгрузке из CRM через месяц. Для события из базы магазина это значит, что выбор покупателя записывают в заказ при оформлении и смотрят на него перед каждой отправкой.
Поля отбирают по задаче. Событию покупки нужны ID, сумма, валюта, время и данные для сопоставления, а карточка клиента с комментариями менеджера ему ни к чему. Хеш телефона Meta использует, чтобы узнать человека, так что вопрос, можно ли его передавать, хеширование не снимает.
Чек-лист первой цепочки
Выполнено 0 из 8
Упражнение: четыре числа вашего проекта
Возьмите прошлый месяц своего проекта или клиента и посчитайте четыре числа: оплаченные заказы в базе, сколько из них подходило под правило отправки, сколько принял получатель и сколько покупок кабинет приписал рекламе. Разложите разницу на три части, как в таблице выше. Если какое-то число взять неоткуда, это первая находка: этот шаг никто не записывает.
Вторая часть - письмо клиенту. Он смотрит на пример выше и просит дослать эти 28 покупок с новыми ID, чтобы в кабинете стало 78. Напишите ему ответ в три-четыре предложения, а потом сравните с разбором.
Просьбу стоит отклонить и объяснить почему. Эти 28 покупок Meta уже получила и приняла, она просто не нашла для них клика или показа в окне атрибуции. Новый ID не создаст рекламное касание, которого не было. Зато Meta получит 28 копий уже принятых покупок, и если какую-то из них она свяжет с кликом, ROAS вырастет на деньги, которых магазин не получал. Если клиент хочет, чтобы Meta узнавала больше покупателей, работать нужно с данными для сопоставления: сохранять cookies _fbp и _fbc (параметры fbp и fbc) при оформлении заказа и передавать хеш телефона там, где покупатель это разрешил.
Видео: введение в серверный GTM
Видео Якова Осипенкова на русском: знакомство с серверным контейнером GTM, то есть со схемой, где события приходят из браузера.