Этот текст написан в Сообществе, в нем сохранены авторский стиль и орфография
Когда платформа Business MAX стала доступна разработчикам, мы решили проверить, насколько удобно использовать ее API в реальном проекте. Задача выглядела вполне привычно: создать чат-бота в Макс на Laravel, который сможет принимать сообщения, отвечать пользователям, работать с кнопками и отправлять медиафайлы.
На первый взгляд все казалось стандартным. Есть документация, описание методов и примеры запросов. Но уже во время разработки выяснилось, что некоторые вещи работают не совсем так, как в других популярных мессенджерах. Например, загрузка файлов разделена на несколько этапов, события приходят в разных форматах, а отдельные поля запроса требуют дополнительной обработки.
В статье покажу, как устроено взаимодействие между приложением и MAX, какие решения мы использовали и на какие детали стоит обратить внимание, если планируете интеграцию собственного сервиса с платформой.
О Сообщнике Про
SEO в диджитал-агентстве. Специализируюсь на комплексном продвижении: от тех. аудитов и семантики до стратегий роста. Провожу кластеризацию, готовлю ТЗ и линкбилдинг.
Это новый раздел Журнала, где можно пройти верификацию и вести свой профессиональный блог.
С чего начинается разработка чат-бота в Макс
Первым делом необходимо зарегистрировать бота в Business MAX и указать адрес вебхука. Именно на этот URL платформа будет отправлять информацию обо всех действиях пользователей.


После регистрации никакие дополнительные запросы для получения новых сообщений уже не требуются. MAX самостоятельно уведомляет приложение обо всех изменениях. Такой подход называют событийной моделью. Сервер получает данные только тогда, когда действительно произошло какое-либо действие.
Это позволяет отказаться от постоянных обращений к API с вопросом, не появилось ли новое сообщение. Вместо регулярного опроса приложение просто ожидает входящий HTTP-запрос.
Практика показала, что этот механизм заметно снижает нагрузку на сервер и делает реакцию бота практически мгновенной.
Как происходит обмен данными
После настройки вебхука вся работа строится вокруг одной последовательности действий.
Сначала пользователь отправляет сообщение, запускает бота или нажимает кнопку в интерфейсе. MAX фиксирует это действие и формирует соответствующее событие.
Затем платформа отправляет HTTP POST-запрос на адрес вебхука. В теле запроса находится JSON-документ со всей необходимой информацией: тип события, данные пользователя, идентификатор чата и содержимое сообщения.
Laravel принимает запрос и начинает его обработку. На этом этапе приложение проверяет, действительно ли запрос поступил от MAX, затем определяет тип события и выбирает нужный сценарий обработки.
Дальнейшая логика полностью зависит от конкретного проекта. В одном случае достаточно отправить пользователю текстовый ответ. В другом необходимо сохранить информацию в базе данных, обратиться к CRM, получить сведения из стороннего сервиса или запустить длительную фоновую задачу.
После выполнения всех необходимых действий приложение обращается к API MAX уже как клиент и отправляет ответное сообщение в нужный чат.
Если представить весь процесс максимально упрощенно, получится следующая цепочка:
Пользователь → MAX → вебхук Laravel → обработка данных → API MAX → пользователь.

Несмотря на простоту схемы, именно здесь сосредоточена большая часть логики любого чат-бота.
Почему важно быстро отвечать вебхуку
Во время тестирования мы обратили внимание еще на одну особенность платформы.
Для MAX недостаточно просто обработать полученные данные. После завершения обработки сервер должен вернуть успешный HTTP-ответ. Это подтверждает, что событие принято и обработано корректно.
Если подтверждение не будет отправлено или сервер завершит работу с ошибкой, платформа воспримет это как неудачную попытку доставки и повторит запрос. В результате один и тот же сценарий может выполниться несколько раз.
Поэтому мы разделили обработку на две части. Сам вебхук выполняет только минимально необходимую работу: проверяет запрос, определяет тип события и запускает нужный обработчик. Все ресурсоемкие операции, например обмен данными с внешними сервисами или обработку крупных файлов, лучше выполнять асинхронно через очередь Laravel.
Такой подход позволяет быстро вернуть ответ платформе и одновременно избежать повторной обработки одинаковых событий.
Когда общая схема работы становится понятной, можно переходить к следующему этапу и создавать чат-бот в мессенджере Макс с обработкой входящих запросов. Структура этих данных определяет, каким образом приложение будет реагировать на сообщения, команды, нажатия кнопок и другие события.

Что приходит на вебхук и как мы разбираем JSON
Когда вебхук начинает принимать первые события, быстро становится понятно, что независимо от действия пользователя сервер получает практически одинаковую структуру данных. Меняется лишь содержимое отдельных полей. Благодаря этому нет необходимости создавать отдельный маршрут для каждого типа события. Достаточно одного обработчика, который определит, что произошло, и передаст запрос дальше.
Первое поле, которое мы анализируем, — тип события. Обычно платформа передает его в update_type, хотя в некоторых случаях используется поле type. Именно оно определяет дальнейший сценарий работы приложения.
Например, если пользователь отправил обычное сообщение, обработчик запускает логику обработки текста. Если бот был только что открыт, выполняется стартовый сценарий с приветственным сообщением. Для нажатий на кнопки предусмотрен отдельный тип события, который позволяет сразу понять, какое действие выбрал пользователь.

Такой подход оказался удобным еще и потому, что вся маршрутизация сосредоточена в одном месте. Вместо множества контроллеров достаточно определить тип события и передать управление соответствующему сервису.
Какие поля используются чаще всего
Хотя входящий JSON содержит большое количество информации, на практике активно используется лишь часть полей.
В первую очередь приложение извлекает идентификатор чата. Именно он нужен для всех последующих ответов пользователю. Если не сохранить chat_id, бот не сможет отправить сообщение обратно в тот же диалог.
Следом мы получаем сведения об отправителе. Обычно достаточно идентификатора пользователя и его имени. Эти данные позволяют связать переписку с внутренней системой приложения, авторизовать пользователя или сохранить историю общения.
Основной текст сообщения чаще всего находится в отдельном поле объекта body. Если пользователь просто написал сообщение, никаких дополнительных действий выполнять не требуется. Однако при работе с вложениями ситуация оказывается немного сложнее.
Что оказалось неочевидным
При разработке чат-бота в Макс мы отметили еще на одну особенность.
Первая касается временной метки события. Значение timestamp может приходить в миллисекундах, хотя большинство функций PHP и Laravel работают с Unix-временем в секундах. Если использовать значение без проверки, время сообщений будет определяться неверно.
Поэтому перед обработкой мы всегда проверяем размер числа. Если видно, что используется формат с миллисекундами, просто делим значение на тысячу. После этого даты корректно работают при сортировке сообщений, формировании истории переписки и записи информации в базу данных.
Вторая особенность связана со структурой самого JSON. Не все поля гарантированно присутствуют в каждом событии. Одни появляются только при отправке файлов, другие — исключительно при работе с кнопками.
По этой причине мы отказались от прямого обращения к вложенным элементам массива. Вместо этого используем безопасное получение данных через оператор?? или вспомогательные методы Laravel. Такой подход позволяет спокойно обрабатывать события даже после изменений в API и не получать ошибки из-за отсутствующего ключа.
Работа с вложениями
Если пользователь отправляет изображение, документ или голосовое сообщение, соответствующая информация появляется в массиве вложений. Каждый элемент содержит собственный тип и набор технических параметров.
Мы довольно быстро поняли, что разбирать эти данные прямо внутри контроллера неудобно. Количество условий начинает расти, а код становится сложнее поддерживать.
Поэтому обработку медиа вынесли в отдельный сервис. Его задача — пройти по всем полученным вложениям, определить их тип и привести информацию к единому виду независимо от того, что именно прислал пользователь.
В результате остальная часть приложения работает уже не с исходным JSON, а с подготовленным массивом данных. В нем находятся только действительно нужные параметры: идентификатор файла, MIME-тип, размер, ссылка, токен и другие характеристики, которые могут понадобиться в дальнейшем.

Такое разделение заметно упростило бизнес-логику. Контроллер отвечает только за прием события, сервис занимается обработкой структуры ответа API, а остальные части приложения получают уже готовые данные в удобном формате.
После того как информация успешно разобрана, остается решить главную задачу — сформировать ответ пользователю. На этом этапе приложение начинает работать уже не как сервер, принимающий события, а как клиент API MAX, отправляющий новые сообщения в чат.
Как бот отправляет сообщения пользователю
После того как приложение разобрало входящее событие и выполнило необходимую бизнес-логику, остается сформировать ответ. Именно на этом этапе Laravel уже не принимает запросы, а сам обращается к API MAX.
Технически отправка сообщения выглядит довольно просто. Приложение выполняет HTTP POST-запрос к соответствующему методу API, передает идентификатор чата и текст, который должен увидеть пользователь.
Однако уже после первых экспериментов стало понятно, что писать такие запросы вручную в разных частях проекта неудобно. Если каждый контроллер самостоятельно будет формировать HTTP-запросы, задавать заголовки и обрабатывать ответы API, код быстро станет избыточным.
Поэтому мы вынесли всю работу с MAX в отдельный сервис. Он стал единой точкой взаимодействия с API. Остальным компонентам приложения достаточно вызвать нужный метод и передать необходимые параметры, а детали обмена данными скрыты внутри сервиса.
Такое решение оказалось полезным не только с точки зрения читаемости кода. Если API меняется или появляется новый способ отправки сообщений, достаточно обновить один сервис, а не искать одинаковые фрагменты по всему проекту.
После успешного выполнения запроса MAX возвращает информацию о созданном сообщении. При необходимости эти данные можно использовать для ведения журналов, сохранения истории общения или последующего анализа работы бота.

Почему кнопки оказались удобнее обычных команд
Почти любой чат-бот со временем перестает ограничиваться текстовыми сообщениями. Пользователям гораздо проще выбрать готовый вариант действия, чем каждый раз вводить команду вручную.
Поэтому в одном из первых сценариев мы добавили интерактивные кнопки.
При формировании ответа приложение передает не только текст сообщения, но и описание клавиатуры. Для каждой кнопки задается отображаемое название и служебное значение, которое будет отправлено обратно после нажатия.
Для пользователя этот процесс выглядит максимально просто. Он видит несколько вариантов действий и выбирает нужный одним нажатием.
С точки зрения сервера происходит уже знакомая последовательность событий. MAX снова обращается к вебхуку, но на этот раз сообщает не о новом сообщении, а о выборе одной из кнопок.

Во входящем событии содержится значение callback_data. Именно по нему приложение понимает, какой вариант выбрал пользователь.
Это позволяет практически полностью отказаться от анализа свободного текста в тех местах, где последовательность действий заранее известна. Вместо проверки множества вариантов написания команды достаточно сравнить одно служебное значение и выполнить соответствующий сценарий.
На практике подобная схема заметно уменьшает количество ошибок. Пользователь не может случайно допустить опечатку или отправить команду в неправильном формате, поскольку выбирает уже подготовленный вариант.
Как мы организовали обработку пользовательских сценариев
По мере роста функциональности стало очевидно, что обычной проверки типа события уже недостаточно. После получения сообщения приложение должно понимать, на каком этапе диалога находится пользователь и какое действие ожидается следующим.
Поэтому после определения типа события управление передается не сразу в конкретный метод, а в отдельный слой бизнес-логики.
Именно здесь принимаются основные решения:
- нужно ли отправить простой текстовый ответ;
- следует ли обратиться к CRM или другой внешней системе;
- необходимо ли сохранить информацию в базе данных;
- требуется ли изменить состояние диалога;
- нужно ли поставить задачу в очередь для фоновой обработки.
Такое разделение позволило сделать архитектуру значительно понятнее. Контроллер отвечает только за прием HTTP-запроса, сервис взаимодействует с API MAX, а вся логика работы бота сосредоточена в отдельных обработчиках.
В результате каждый компонент решает только одну задачу, а поддерживать и расширять проект становится гораздо проще.
Почему отправка файлов устроена иначе
После реализации текстовых сообщений мы перешли к работе с изображениями и документами. Именно здесь обнаружилось одно из главных отличий API MAX от многих привычных мессенджеров.
Интуитивно ожидаешь, что файл можно отправить тем же запросом, что и обычное сообщение. Но платформа использует другую схему.
Перед загрузкой необходимо получить специальное разрешение от сервера. Только после этого можно передать сам файл, а затем использовать его при отправке сообщения.
Получается, что отправка любого изображения, документа или другого вложения состоит не из одного HTTP-запроса, а сразу из нескольких последовательных этапов.
Сначала такая архитектура кажется более сложной, однако позже становится понятно, зачем она нужна. Платформа разделяет загрузку файлов и обмен сообщениями. Благодаря этому можно независимо контролировать передачу больших объектов, проверять корректность загрузки и не перегружать основной метод отправки сообщений.

Именно эта последовательность стала для нас одной из самых необычных особенностей API MAX. Поэтому процесс загрузки медиа пришлось реализовывать как отдельный сценарий со своей логикой обработки.
Как устроена отправка изображений и документов
Когда мы начали добавлять поддержку медиафайлов, выяснилось, что здесь API MAX работает иначе, чем при отправке обычных сообщений.
Если текст можно передать одним HTTP-запросом, то с файлами такой подход не сработает. Платформа требует сначала подготовить загрузку, затем передать сам файл и только после этого прикрепить его к сообщению. Пока не пройдены все этапы, пользователь ничего не увидит в чате.
Поначалу такая схема кажется избыточной, но позже становится понятно, что она позволяет платформе отдельно контролировать процесс загрузки крупных файлов и не смешивать его с отправкой сообщений.
Шаг первый. Получение адреса для загрузки
Перед тем как отправить изображение или документ, приложение обращается к специальному методу API.
В запросе указывается тип будущего вложения. Например, если необходимо отправить фотографию, серверу передается соответствующее значение, обозначающее этот формат.
В ответ приложение получает не токен и не идентификатор файла, а временный URL.
Этот адрес создается специально для одной операции загрузки и действует ограниченное время. Использовать его повторно нельзя, поэтому каждый новый файл требует получения новой ссылки.
Подобный механизм может показаться непривычным, однако именно он позволяет платформе разделить подготовительный этап и непосредственную передачу содержимого файла.
Шаг второй. Передача файла
Получив временный URL, приложение выполняет еще один HTTP-запрос.
На этот раз отправляется уже не JSON, а содержимое самого файла. Для этого используется формат multipart/form-data, который давно стал стандартом при передаче бинарных данных через HTTP.

Во время реализации важно учитывать одну особенность. В запрос необходимо передать не только сам файл, но и параметры, которые ожидает API. Если структура окажется неверной, загрузка завершится ошибкой, даже если файл полностью корректный.
После успешной передачи MAX сохраняет полученные данные и начинает их обработку.
Шаг третий. Получение токена
Когда файл загружен, API возвращает новый JSON-ответ.
Самым важным элементом этого ответа становится токен.
Фактически он превращается в идентификатор загруженного файла внутри платформы. Именно этот токен понадобится на следующем этапе.
Интересно, что после его получения повторно передавать изображение уже не требуется. Платформа запомнила файл, а приложению остается лишь сослаться на него при отправке сообщения.
Это удобно и с точки зрения производительности. Даже если дальнейшая обработка сообщения занимает некоторое время, файл уже находится на стороне MAX и готов к использованию.
Последний этап
Когда токен получен, можно отправлять сообщение пользователю.
Для этого приложение снова обращается к API, указывает идентификатор нужного чата и формирует объект вложения.
Вместо самого изображения передается только тип файла и ранее полученный токен. MAX находит соответствующий объект в своем хранилище и автоматически прикрепляет его к сообщению.
Для пользователя весь этот процесс остается незаметным. Он просто получает фотографию, документ или другое вложение в диалоге. Но с точки зрения приложения между выбором файла и его отображением в чате выполняется сразу несколько последовательных запросов.

Именно поэтому мы вынесли загрузку медиа в отдельный сервис. Он полностью управляет этой цепочкой: получает временный URL, передает файл, извлекает токен и только после этого сообщает основному приложению, что вложение готово к отправке.
Такой подход сделал код значительно проще. Остальная бизнес-логика вообще не знает, сколько запросов потребовалось для загрузки изображения. Для нее существует всего один метод, который принимает файл и возвращает готовый токен.
Что стоит предусмотреть заранее
Во время тестирования мы заметили еще несколько моментов, которые лучше учитывать до запуска проекта.
Во-первых, не стоит предполагать, что любой файл будет успешно загружен с первой попытки. Возможны сетевые ошибки, истечение срока действия временного URL или отклонение файла самим API. Поэтому после каждого запроса необходимо проверять код ответа и фиксировать возможные ошибки в журнале приложения.
Во-вторых, полезно заранее проверять размер и тип файлов. Если пользователь пытается отправить вложение, которое не соответствует ограничениям платформы, лучше сообщить об этом сразу, чем выполнять всю последовательность запросов и получить отказ только на завершающем этапе.
Мы также отказались от хранения промежуточных данных внутри контроллеров. Вся информация о загрузке существует только внутри сервиса, который отвечает за работу с медиа. Благодаря этому основной код приложения остается компактным и не зависит от технических особенностей API.
После реализации загрузки файлов оставалось решить последнюю важную задачу — сделать работу бота устойчивой к ошибкам, защитить вебхук от посторонних запросов и исключить повторную обработку одних и тех же событий.
Что важно предусмотреть до запуска бота
Когда основные сценарии начали работать, мы перешли к проверке того, как приложение ведет себя в нестандартных ситуациях. Именно на этом этапе обычно обнаруживаются проблемы, которые сложно заметить во время разработки, но которые могут повлиять на стабильность сервиса после запуска.
Защита вебхука
Вебхук представляет собой обычный URL, доступный через интернет. Если не предусмотреть дополнительную проверку, любой желающий сможет отправить на этот адрес собственный HTTP-запрос.
Даже если поддельное событие не приведет к серьезным последствиям, оно может засорить журналы, создать лишние записи в базе данных или запустить сценарий, который вообще не должен был выполняться.
Чтобы избежать подобных ситуаций, каждый входящий запрос необходимо проверять до начала основной обработки.
Для этого можно использовать секретный ключ, который известен только приложению и платформе, либо механизм цифровой подписи. Во втором случае сервер самостоятельно вычисляет подпись на основании содержимого запроса и сравнивает ее с той, что пришла в заголовках. Если значения не совпадают, обработка сразу прекращается.
Такой механизм занимает минимум времени, но существенно снижает вероятность получения поддельных событий.
Почему важно возвращать успешный ответ
Еще одна особенность платформы связана с подтверждением получения события.
После обработки запроса сервер должен вернуть код состояния HTTP 200 OK. Для MAX это сигнал о том, что событие успешно принято и повторно отправлять его не нужно.
Если приложение завершится с ошибкой или не успеет сформировать ответ, платформа воспримет ситуацию как неудачную доставку и попробует выполнить отправку повторно.
На практике это может привести к неожиданным последствиям. Например, одна и та же заявка будет сохранена несколько раз, пользователь получит повторяющиеся сообщения, а внешняя система обработает идентичный запрос повторно.
Поэтому мы старались сделать обработку вебхука максимально быстрой. Все действия, которые не влияют на формирование ответа пользователю прямо сейчас, лучше выполнять отдельно.
Когда стоит использовать очереди
Во многих проектах после получения сообщения начинается длинная цепочка операций.
Необходимо обратиться к CRM, получить информацию из стороннего сервиса, обработать изображение, выполнить запрос к базе данных или сформировать отчет. Если выполнять все это прямо внутри вебхука, время обработки заметно увеличится.
В такой ситуации гораздо надежнее передать длительную задачу в очередь Laravel.
Сам вебхук лишь принимает событие, выполняет базовые проверки, сохраняет необходимую информацию и сразу возвращает успешный ответ платформе. Остальная работа продолжается уже в фоновом режиме.
Такой подход позволяет одновременно решить две задачи. Пользователь получает быстрый отклик, а приложение не рискует столкнуться с повторной доставкой одного и того же события.
Обработка ошибок
Даже хорошо протестированное приложение не застраховано от непредвиденных ситуаций.
Недоступность внешнего сервиса, временные проблемы с сетью или неожиданные изменения в структуре ответа API могут привести к исключению во время выполнения программы.
Поэтому критически важные участки лучше выполнять внутри конструкций обработки исключений.
Если возникает ошибка, приложение должно не завершиться аварийно, а сохранить подробную информацию в журнале и корректно завершить обработку текущего события.
Подобная стратегия значительно упрощает поиск причин неисправностей. Вместо сообщений от пользователей о том, что "бот перестал отвечать", разработчик сразу видит, на каком этапе возникла проблема и какие данные привели к ошибке.
Что еще стоит учитывать
Во время интеграции полезно заранее ознакомиться с техническими ограничениями API.
Они могут касаться максимального размера сообщений, допустимого объема вложений, обязательных HTTP-заголовков или формата отдельных полей запроса.
Лучше проверять такие условия еще до обращения к API. Например, если пользователь пытается отправить слишком большой файл, приложение может сразу вывести понятное сообщение с объяснением причины отказа, не выполняя лишних запросов к серверу.
Подобные проверки делают работу бота более предсказуемой и уменьшают количество ситуаций, когда пользователь сталкивается с непонятной ошибкой.
Разработка чат-бота для MAX оказалась несложной с точки зрения общей архитектуры. Большинство механизмов знакомы тем, кто уже работал с современными API: события передаются через вебхуки, ответы отправляются обычными HTTP-запросами, а основная логика остается на стороне приложения. При этом у платформы есть несколько особенностей, которые лучше учитывать заранее.













