Повторный вебхук Telegram: как не создать второй лид в CRM
Как различать повтор события и новое обращение, сохранять входящие обновления и разбирать таймаут создания лида без слепого повтора.
Повторный вебхук Telegram: как не создать второй лид в CRM
Если одно событие Telegram доставлено дважды, CRM не должна создавать второй лид. Устойчивый идентификатор события и запись о его обработке помогают этого избежать. Но одной проверки «мы уже видели этот ID» недостаточно: если CRM создала лид, а ответ потерялся, обработчик не знает, безопасно ли повторять запрос.
Это руководство по проектированию интеграции Telegram Bot API → собственного обработчика → CRM. Оно не описывает проверенную встроенную функцию 24go.site. Предполагается, что бот уже законно получает нужные обращения, а у вас есть база данных и тестовая CRM. Эта схема не даёт права собирать чужие контактные данные или отправлять сообщения без соответствующих оснований.
Разделите идентификаторы событий, контактов и сделок
Для этих объектов нужны разные ключи и правила обработки повторов.
| Объект | Что защищает его ключ | Пример ключа в вашей интеграции |
|---|---|---|
| Входящее событие | Повторную обработку одной доставки | источник + ваш tenant + бот + update_id |
| Контакт | Связь нескольких обращений с одним человеком | tenant + источник + идентификатор пользователя, если он доступен |
| Сделка | Разделение нового обращения и продолжения существующего диалога | внутренний ID обращения согласно согласованному бизнес-правилу |
update_id служит уникальным идентификатором обновления. Telegram прямо указывает, что он позволяет игнорировать повторные обновления. Дополнительные компоненты составного ключа выше предлагают изолировать записи в вашей системе; это не формат ключа Telegram.
Два разных сообщения одного человека не являются повторной доставкой одного события. Они могут относиться к одной сделке или начинать разные сделки. Это следует согласовать с владельцем процесса. Не превращайте правило «одна доставка, одна обработка» в правило «один человек, одна сделка навсегда».
Приёмник: сначала сохранить, затем подтвердить
Идемпотентность означает, что повтор операции не создаёт дополнительного эффекта. Входящие обновления нужно сохранять в постоянном хранилище, а не только в памяти процесса.
- Проверьте подлинность запроса и ожидаемую структуру. В
setWebhookпредусмотрен параметрsecret_token. Если он настроен, Telegram отправляет его в заголовкеX-Telegram-Bot-Api-Secret-Token. Проверьте его до постановки задания в очередь. Не записывайте значение токена в журнал. - Сформируйте ключ события. Не используйте время получения запроса: при повторной доставке оно изменится.
- В одной транзакции сохраните событие и установите ему состояние
pending. Наложите уникальное ограничение на составной ключ. Если задания хранятся в отдельной таблице, записывайте событие и задание в той же транзакции, чтобы между записями не потерять работу. - После успешного commit верните успешный HTTP-ответ. Если ключ уже существует, подтвердите доставку, но не создавайте новое задание. Существующая запись
pendingдолжна оставаться доступной воркеру. - Если сохранить данные в постоянном хранилище не удалось, не отвечайте успехом. Обработчик не должен сообщать о приёме события, которого он не сохранил.
Воркер: таймаут не означает, что создание не удалось
До попытки создания сохраните постоянный operation_id, идентификатор этой бизнес-операции. Он должен оставаться неизменным при повторных запусках воркера. Не допускайте, чтобы два воркера одновременно выполняли одну операцию: используйте атомарный захват задания, срок владения и процедуру восстановления зависших заданий.
Выберите следующий шаг, исходя из возможностей конкретной CRM, подтверждённых её документацией:
- Поддерживается идемпотентное создание. Повторно используйте тот же ключ операции в течение установленного срока его действия. Не предполагайте, что любая CRM принимает заголовок
Idempotency-Key. - Поддерживаются уникальный внешний ID и атомарный upsert. Сопоставьте операцию с этим ID. Здесь upsert означает создание или обновление по ключу. Обычный поиск с последующим созданием не обеспечивает аналогичную защиту от гонки.
- Нет ни одного из этих механизмов. После неоднозначного таймаута переведите задание в
needs_reconciliation, то есть на сверку. Проверьте CRM по внешней ссылке на операцию, журналам запросов или вручную. Если результат установить не удалось, не отправляйте запрос на создание повторно вслепую.
crm_id и отметьте задание как done. Если процесс завершится после создания лида, но до сохранения результата, при восстановлении нужно использовать тот же механизм идемпотентности или выполнить сверку. Одна лишь локальная таблица событий не гарантирует выполнение операции ровно один раз в удалённой CRM.
Определите, что делать с повторными и новыми сообщениями
| Ситуация | Действие |
|---|---|
Тот же ключ события, запись pending | Подтвердить доставку и продолжить исходное задание |
Тот же ключ события, запись done | Не повторять действие в CRM |
Другой update_id, тот же контакт | Применить правило продолжения или открытия сделки |
| Таймаут после отправки запроса на создание | Повторить запрос с поддерживаемым постоянным ключом или выполнить сверку; не генерировать новый случайный ключ |
| Запоздавшее событие об изменении стадии | Проверить версию или допустимость перехода, а не перезаписывать стадию безусловно |
Проверьте сценарии отказа до включения интеграции
На обезличенных тестовых обращениях проверьте не только штатный сценарий:
- Доставьте один и тот же ключ дважды, затем одновременно в двух запросах. В хранилище должно остаться одно задание, а эффект в CRM не должен дублироваться.
- Остановите воркер после сохранения
pending. После перезапуска событие должно оставаться доступным для обработки. - Смоделируйте создание записи в CRM с потерей ответа. При восстановлении нужно использовать исходный ключ или перевести задание в
needs_reconciliation, а не повторять создание вслепую. - Отправьте другое сообщение от того же тестового контакта. Убедитесь, что дедупликация событий не отбрасывает его.
- Проверьте отказ в авторизации и сбой базы данных. Поддельный запрос не должен попасть в очередь, а несохранённое событие не должно получить успешное подтверждение.
Для эксплуатации отслеживайте возраст заданий pending, ошибки и количество заданий needs_reconciliation. Храните связь event_key → operation_id → crm_id. Не записывайте полные переписки и токены в технические журналы без необходимости. Срок хранения ключей согласуйте с фактическим периодом повторной доставки и требованиями к данным: после удаления ключа старое событие может снова выглядеть новым.
Готовность определяется не тем, что тестовый лид появился один раз. Нужно убедиться, что повторная доставка не создаёт дополнительный эффект, незавершённая работа восстанавливается, а неоднозначный ответ не приводит к дублирующей сделке.