Свой провайдер SMS - класс, регистрация, отправка и статусы
Подключаем своего оператора SMS: класс провайдера, регистрация событием, отправка через менеджер службы и проверка статуса доставки.
Механика
Служба сообщений отделяет отправку сообщения от кода прикладного приложения. Приложение говорит «отправь этот текст на этот номер», а как именно сообщение уйдёт, решает выбранный провайдер.
Такое разделение позволяет сменить оператора без правки прикладного кода вообще. Меняется идентификатор отправителя в настройках, а вызовы отправки в коде проекта остаются прежними.
Провайдер в платформе - это класс, а не запись в настройках. Список доступных отдаёт менеджер службы, и каждый провайдер известен по собственной строковой константе идентификатора.
Свой провайдер наследуют от базового класса службы сообщений и кладут в свой модуль. Он описывает своё имя, доступность, поля настроек и метод отправки, который обращается к программному интерфейсу оператора по протоколу HTTP.
Настройки провайдера - ключ доступа, имя отправителя и адрес интерфейса - хранят рядом с модулем. Провайдер читает их при проверке доступности и отдаёт признак готовности: без ключа он в списке отправителей просто не появится.
Регистрируют провайдер обработчиком события сбора отправителей службы сообщений. После регистрации он появляется в общем списке рядом со встроенными и становится доступен по своему идентификатору.
Отправка по умолчанию идёт через очередь, а не мгновенно. Сообщение уходит после завершения запроса, поэтому проверка «нажал и жду сразу» вводит в заблуждение и рождает лишние вопросы.
Номера приводятся к международному виду автоматически, без участия прикладного кода. Ручная чистка номера своим кодом чаще ломает это преобразование, чем помогает, и оператор получает неверный формат.
Результат отправки возвращается объектом результата, где лежат ошибки постановки. Его проверяют всегда: неактивный шаблон, чужой сайт и недоступный провайдер выглядят одинаково - сообщение просто не уходит.
Статус доставки запрашивают у службы отдельным вызовом по идентификатору сообщения. Оператор отдаёт свой внешний статус, а служба переводит его в понятный текст для журнала.
Шаги
- Заключить договор с оператором и получить доступ к его программному интерфейсу.
- Написать класс провайдера наследником базового класса службы сообщений платформы.
- Зарегистрировать класс обработчиком события сбора отправителей прямо в установщике модуля.
- Проверить, что провайдер появился в списке и доступен по своему идентификатору.
- Отправить пробное сообщение прямой отправкой, минуя очередь, и прочитать результат.
- Записать в журнал внешний статус доставки после успешной отправки сообщения.
Код
Подключаем модуль службы сообщений:
if (!\Bitrix\Main\Loader::includeModule('messageservice')) { throw new \RuntimeException('модуль службы сообщений не установлен');}// без подключения модуля классы менеджера сообщений недоступны вовсеОтсутствие модуля - первая причина ошибки «класс не найден» при работе с SMS. Проверку ставят один раз на входе в свой код, а не перед каждым вызовом отправки сообщения.
Описываем свой провайдер:
namespace Vendor\Module\Sms;
class OperatorSender extends \Bitrix\MessageService\Sender\Base{ public const ID = 'vendor_operator';
public function getId(): string { return static::ID; } public function getName(): string { return 'Оператор «Вендор»'; } public function isRegistered(): bool { return $this->getOption('api_key') !== null; } // метод отправки обращается к интерфейсу оператора и возвращает объект результата}Идентификатор провайдера попадает в поля сообщений и в настройки, поэтому его не меняют после запуска. Имя видно администратору в списке отправителей, а признак регистрации отвечает за доступность провайдера.
Отправляем сообщение из самого провайдера:
public function sendMessage(array $messageFields): \Bitrix\Main\Result{ $http = new \Bitrix\Main\Web\HttpClient(['socketTimeout' => 5]); $answer = $http->post($this->getOption('api_url'), [ 'key' => $this->getOption('api_key'), 'to' => $messageFields['MESSAGE_TO'], // номер уже в международном виде 'text' => $messageFields['MESSAGE_BODY'], ]); return $this->parseAnswer($answer); // ответ оператора превращаем в результат}Ограничение времени ожидания здесь обязательно: медленный оператор не должен задерживать обработку очереди. Ответ оператора разбирают в объект результата, чтобы служба видела ошибку, а не считала отправку успешной.
Регистрируем провайдер событием:
\Bitrix\Main\EventManager::getInstance()->registerEventHandler( 'messageservice', 'onGetSmsSenders', // событие сбора отправителей 'vendor.module', '\Vendor\Module\Sms\OperatorSender', 'onGetSmsSenders');// регистрацию выполняют при установке модуля, а не на каждом запросеПостоянную регистрацию делают в установщике модуля, чтобы она пережила перезагрузку. После этого провайдер виден в списке отправителей наравне со встроенными.
Отправляем сообщение через менеджер:
$result = \Bitrix\MessageService\Sender\SmsManager::sendMessage([ 'SENDER_ID' => \Vendor\Module\Sms\OperatorSender::ID, 'MESSAGE_TO' => '+79161234567', // номер в международном виде 'MESSAGE_BODY' => 'Заказ №' . $orderId . ' готов к выдаче',]);if (!$result->isSuccess()) { \Bitrix\Main\Diag\Debug::writeToFile($result->getErrorMessages(), date('H:i:s'), 'sms.log');}Отправка кладёт сообщение в очередь и отдаёт результат постановки. Ошибки в результате означают отказ до отправки: неверный провайдер, пустой номер или недоступные настройки оператора.
Отправляем немедленно при проверке:
$result = \Bitrix\MessageService\Sender\SmsManager::sendMessageDirectly([ 'SENDER_ID' => \Vendor\Module\Sms\OperatorSender::ID, 'MESSAGE_TO' => $phone, 'MESSAGE_BODY' => $text,]);// прямая отправка нужна для проверки: обычная уходит после завершения запросаПрямая отправка полезна на стенде и в отладке, но не в обычном сценарии. На витрине она задерживает ответ страницы на время разговора с сервером оператора.
Читаем статус доставки:
$status = \Bitrix\MessageService\Sender\SmsManager::getMessageStatus($messageId);printf("внешний=%s текст=%s\n", $status->getExternalStatus(), $status->getStatusText());// внешний статус приходит от оператора, текст - перевод для журналаСтатус запрашивают не сразу, а через некоторое время после отправки. Оператор обновляет его по мере доставки, и мгновенный запрос почти всегда возвращает промежуточное значение.
Пишем в журнал после успешной отправки:
\Bitrix\Main\EventManager::getInstance()->addEventHandler( 'messageservice', 'OnMessageSuccessfullySent', static function (\Bitrix\Main\Event $event) { \Bitrix\Main\Diag\Debug::writeToFile($event->getParameter('ID'), date('H:i:s'), 'sms.log'); });Событие успешной отправки - удобное место для журнала и для отметки в своей таблице. Оно срабатывает уже после разговора с оператором, поэтому тяжёлую работу из него выносят в фоновое задание.
Ограничения
Провайдер отвечает за доставку, но не за содержание сообщения. Требования к подписи отправителя, к рекламным текстам и к согласию получателя выполняет ваш проект, а не платформа.
Стоимость сообщений считает оператор по своим правилам и своему тарифу. Ошибка в цикле рассылки обходится деньгами сразу, поэтому массовую отправку сначала проверяют на коротком списке номеров.
Внешние статусы у операторов различаются по названиям и приходят с заметной задержкой. Одинаковой картины доставки по всем провайдерам не бывает, и отчёты строят по своим отметкам, а не только по статусам оператора.
Очередь отправки живёт внутри платформы и зависит от фоновых заданий. Выключенные задания останавливают отправку целиком, и сообщения копятся до восстановления расписания.
Типичные проблемы
Вызов менеджера падает с ошибкой «класс не найден».
Модуль службы сообщений не подключён перед первым обращением к его менеджеру. Подключение проверяют один раз на входе в свой код.
Код отработал, а сообщение не пришло.
Сообщение ушло в очередь и отправляется после завершения запроса. Для проверки берут прямую отправку, которая уходит оператору без ожидания конца запроса.
Сообщения по шаблону не уходят молча.
Шаблон неактивен, привязан к другому сайту или его имя не совпадает с событием. Результат отправки проверяют всегда и печатают его ошибки в журнал проекта.
Оператор жалуется на неверный формат номера.
Номер почищен своим кодом и потерял международный вид перед отправкой оператору. Приведение номера к международному формату платформа выполняет сама, без помощи кода.
Свой провайдер не появился в списке отправителей.
Обработчик события сбора отправителей не зарегистрирован или зарегистрирован только на текущий запрос. Регистрацию выполняют в установщике модуля, чтобы она пережила перезагрузку сайта.
Частые вопросы
Можно ли работать без своего провайдера?
Да, в поставке есть готовые провайдеры популярных операторов. Свой пишут, когда нужного оператора среди них нет.
Как выбрать провайдера по умолчанию?
Менеджер отдаёт провайдера по умолчанию для региона и первый доступный из активных. В коде обычно указывают идентификатор явно.
Где хранить ключ доступа к API оператора?
В настройках модуля или в файле настроек проекта, но не в репозитории. Провайдер читает его при проверке своей доступности.
Как отправить сообщение по шаблону события?
Через высокоуровневый интерфейс событий: код передаёт имя события и данные, а текст берётся из активного шаблона. Так текст правит администратор.
Что делать с недоставленными сообщениями?
Смотреть внешний статус по идентификатору сообщения и вести свой журнал отправок. По журналу видно, повторять отправку или менять номер.
Смежное
- SMS с сайта - оглавление подтемы
- СМС из кода: событие и шаблон, прямая отправка, свой провайдер - отправка сообщений двумя уровнями интерфейса
- Уведомления покупателю: письма и SMS по статусам заказа - типовой сценарий отправки
- Очередь сообщений: фоновая обработка задач в ядре - куда выносят медленную отправку
- Вызов внешнего сервиса из кода: таймауты, повторы, журнал - разговор с API оператора
- Свой модуль: структура, установка, автозагрузка - где живёт класс провайдера
- Обработчик события: регистрация, аргументы, отмена действия - как регистрируют такие обработчики
- SMS не доходят до получателя: разбор причин - если сообщение не дошло
- SMS и очереди сообщений - устройство канала сообщений
- Подтверждение телефона кодом из SMS - прикладной сценарий поверх своего провайдера