Заявки из чата приходят в сквозную аналитику без источника
Чат на сайте отдаёт заявки в сквозную аналитику через вебхук, и они приходят без источника: не видно ни рекламной кампании, ни страницы, с которой человек написал. Причина обычно не в аналитике и не в настройках чата.
Почему заявки из чата приходят без источника
Потому что вебхук чата приходит с сервера сервиса на ваш сервер, а метка визита живёт в cookie браузера. В запросе от чата этой cookie нет и быть не может: браузер посетителя в обмене не участвует.
Обработчик при этом выглядит рабочим — он читает метку из полей формы. Но если поле в форму никто не положил, читается пустая строка, и так на каждом событии.
// Выглядит правильно, а не работает никогда:
// вебхук приходит сервер-серверу, cookie roistat_visit в нём нет,
// а поля roistat_visit в форме чата никто не заводил.
$roistatVisit = isset($form['roistat_visit']) ? $form['roistat_visit'] : '';Факты в одном из разборов сошлись с точностью до строчки. В кабинете аналитики 23 заявки из 23 по каналу чата — без номера визита, источник помечен как неопределённый. В логе обработчика за семь месяцев 182 события, из них 26 контактных форм, и во всех payload метка визита пустая.
Проверка занимает минуту и не требует доступа в кабинет: откройте лог обработчика и посмотрите одно событие целиком. Если поля с меткой нет в самом payload — чинить надо на сайте, а не в аналитике.
Как передать метку визита, если cookie в вебхук не попадает
Через произвольное поле клиента. Виджет чата умеет принимать свои поля с сайта: код на странице читает cookie и отдаёт значение виджету. Клиенту это поле не показывается, оператор видит его в карточке, а в вебхук оно приезжает вместе с данными формы.
window.replainSettings = {
id: 'идентификатор чата',
// Метку кладём в произвольное поле клиента: клиент его не видит,
// в вебхуке оно придёт вместе с данными формы.
fields: {
roistat_visit: (document.cookie.match(/(?:^|;\s*)roistat_visit=([^;]+)/) || [])[1] || ''
},
onWidgetLoaded: function () {
// Счётчик аналитики ставит cookie асинхронно и может не успеть
// к моменту загрузки виджета — досылаем поле, когда она появится.
var tries = 0, t = setInterval(function () {
var m = document.cookie.match(/(?:^|;\s*)roistat_visit=([^;]+)/);
if (m) { window.ReplainAPI('setFields', { roistat_visit: m[1] }); clearInterval(t); }
if (++tries > 30) { clearInterval(t); }
}, 1000);
}
};- Cookie аналитики ставится асинхронно, поэтому одной строки в настройках мало — нужна досылка после загрузки виджета
- Имя, почту и телефон через этот же механизм передавать нельзя: если передать значения всех полей формы, форма клиенту показана не будет
- Поле сохраняется в карточке клиента на стороне чата, поэтому приходит и в последующих событиях того же диалога, а не только в момент отправки формы
Проверять это надо на живом трафике, а не на тестовом запросе. В логе после правки видно и метку в payload, и ответ аналитики: пришёл визит 467383, заявка создана.
Все ли события чата приносят контакты
Нет. Контакты клиента несут четыре разных события, а обработчик в том разборе реагировал на одно — остальные три логировались и пропускались, то есть эти обращения не попадали в аналитику вообще.
| Событие | Когда возникает | Обрабатывалось |
|---|---|---|
| Контактная форма | Клиент заполнил форму в виджете | да |
| Офлайн-форма | Операторы не в сети, клиент оставил контакты | нет |
| Авто-запрос контактов | Чат сам просит контакты по ходу диалога | нет |
| Запрос обратного звонка | Клиент просит перезвонить | нет |
Это вторая причина «неполной передачи данных», и снаружи она неотличима от первой: заявки просто есть не все. Разница видна только в логе — событие пришло, а в аналитику ушёл ответ «пропущено».
Что вебхук требует от обработчика
Ответ 200 и быстро. У этого сервиса таймаут обработчика 10 секунд и до 5 повторов примерно раз в минуту; если ответ не 200, доставка всех последующих событий этого клиента приостанавливается, а по исчерпании попыток события удаляются.
- Отвечать 200 всегда, даже когда событие вам неинтересно — иначе сервис перестанет присылать события по этому клиенту
- Не поднимать CMS ради вебхука: из ядра берутся только реквизиты подключения к базе, чтобы уложиться в таймаут
- Быть идемпотентным: до пяти повторов означает, что одно обращение может приехать пять раз
- Держать секретный токен в адресе обработчика и отдавать 403 без него
// Идемпотентность на уровне схемы, а не логики:
// UNIQUE по идентификатору события + INSERT IGNORE.
// Повтор доставки не создаст ни второй строки, ни второй заявки.
$stmt->execute();
$stored = $stmt->affected_rows > 0; // 0 — такое событие уже приходило
if (!$stored) {
echo json_encode(array('status' => 'ok', 'message' => 'Duplicate event'));
exit;
}Отдельная и почти невидимая беда: после переезда сайта на новую площадку адрес вебхука начал отдавать 404. По правилам сервиса это сначала приостанавливает доставку, а потом удаляет события — обращения через чат не доходили никуда и следа не оставляли. При смене домена, хостинга или движка адреса вебхуков проверяются первыми.
Как различить заявки нескольких сайтов в одной аналитике
Класть домен в заголовок заявки и в её поля. Если у компании несколько сайтов, они обычно пишут в один проект аналитики одним ключом счётчика и одним ключом вебхука, а заголовок заявки у всех одинаковый — «Новая заявка с чата». В списке заявок такие обращения неразличимы.
// Домен — в заголовок заявки и в поля.
// Это разделение по сайтам на уровне заявок; визиты им не разделишь.
$title = $leadEvents[$eventType] . ($domain !== '' ? ' — ' . $domain : '');
$payload = array(
'title' => $title,
'roistat_visit' => $roistatVisit,
'fields' => array(
'site' => $domain,
'event_type' => $eventType,
'page' => $page,
),
);Полного разделения это не даёт: пока в проекте аналитики выключен мультидомен, визиты всех сайтов лежат в одной куче, и разбивка возможна только по заявкам. Это отдельное решение и отдельные деньги, но заголовок с доменом делается за час и закрывает вопрос «откуда эта заявка» уже сегодня.
Куда девать переписку, если в аналитику её не отправить
Хранить у себя и связывать с заявкой по номеру визита. Дописать сообщения в уже созданную заявку нельзя: вебхук умеет только создавать, а API заявок доступно на чтение. Отправлять каждое сообщение отдельно — значит превратить один диалог в десяток заявок.
- Две таблицы в базе сайта: карточка диалога на клиента и лента всех событий, включая реплики оператора
- Ключ склейки с аналитикой — номер визита: он виден в карточке заявки, по нему достаётся переписка
- Метка визита в карточке перезаписывается только непустым значением, иначе позднее событие затрёт раннее
- Если в событии метки нет, она подтягивается из карточки клиента по его идентификатору
Читать это должны менеджеры, а не разработчик через выгрузку. Поэтому переписка выведена в админку сайта отдельным разделом: список диалогов с фильтром по имени, телефону, почте, номеру визита и периоду — и карточка с лентой сообщений в хронологии.
- Переписка с телефонами и почтой — персональные данные: доступ по сессии администратора либо по токену, каталог логов наружу отдаёт 403
- Срок хранения задаётся явно и согласуется с владельцем данных; чистка идёт попутно при записи не чаще раза в сутки, если планировщика на хостинге нет
- Соединение с базой на время своих запросов переключается в utf8mb4 — иначе эмодзи из чата приезжают вопросительными знаками
- В выгрузке CSV значения, начинающиеся с =, +, - или @, предваряются апострофом, иначе таблица исполнит их как формулы
Что проверять после правки
Не «код на месте», а сквозной путь: обращение в чате должно закончиться заявкой с номером визита. Промежуточные признаки ничего не доказывают.
- На боевой странице отдаётся новая версия скрипта, а не закэшированная — при кэше статики в неделю правку приходится доставлять сменой версии у подключений, в том разборе на 679 страницах
- В логе обработчика появилось событие с непустой меткой визита
- В кабинете аналитики по этому обращению создан лид, и номер визита у него заполнен
- Повторная отправка того же события заявку не дублирует
- Обработчик без токена отдаёт 403, с токеном — 200
- Перед заливкой у каждого правленого файла есть резервная копия на сервере, а откат описан одной строкой
Может ли чужой виджет остановить сбор заявок
Может, и без вашего участия. В день работ у сервиса чата истёк TLS-сертификат домена, с которого грузится виджет. Браузеры перестали загружать скрипт с ошибкой сертификата, файл при этом сервером отдавался — виджет исчез со всех сайтов, где он стоял, примерно на три часа, пока сервис не выпустил новый сертификат.
Обхода на своей стороне нет: домен и сертификат чужие. Что реально можно сделать — знать об этом раньше клиента.
- Следить за сроком сертификата у внешних доменов, от которых зависит сбор заявок, а не только у своего
- Ставить оповещение на тишину: лог обработчика не пополнялся N дней — повод проверить, а не радоваться
- Держать адреса вебхуков в списке того, что проверяется после любого переезда
Тишина, впрочем, не всегда поломка. В том же разборе лог молчал три месяца — и оказалось, что обращений через чат за это время действительно не было, а вебхук всё время был исправен. Отличить одно от другого можно только живой проверкой.
Если заявки не сходятся с аналитикой
Разбор начинается с лога обработчика и одной живой заявки, проведённой от клика до карточки в аналитике. Отчёт с причинами и приоритетами остаётся у вас, даже если чинить будете сами.