СМС из кода - событие и шаблон, прямая отправка, свой провайдер
Отправляем СМС с сайта: код подтверждения, статус заказа, уведомление менеджеру. Разбираем два уровня API, очередь, своего провайдера и разбор ошибок отправки.
Механика
За отправку СМС в платформе отвечает отдельный модуль службы сообщений. Он соединяет сайт с внешними провайдерами и хранит все настройки самой отправки.
Работать с отправкой СМС можно на двух разных уровнях. Высокий - событие и шаблон, как у почтовых писем, низкий - прямой вызов выбранного провайдера с готовым текстом.
Событие ищет подходящий шаблон по своему имени, сайту и языку сообщения. Найденный шаблон подставляет переданные значения вместо меток и отдаёт готовый текст модулю.
Текст самого сообщения живёт в шаблоне, а вовсе не в коде решения. Правит его администратор сайта, и изменение формулировки не требует выкладки новой версии.
Отправка сообщения по умолчанию идёт через общую очередь. Запрос посетителя не ждёт ответа провайдера, а сообщение уходит следом за завершением запроса.
Немедленная отправка нужна только по-настоящему срочным сообщениям. Код подтверждения при входе
- как раз такой случай, потому что человек ждёт его на экране.
Прямой вызов провайдера обходится вообще без шаблона сообщения. Он берёт получателя, отправителя и текст, а всё остальное - настройки самого провайдера.
Свой провайдер подключают обработчиком события, а вовсе не правкой файлов модуля. Класс наследуют от базового класса модуля и регистрируют на событии списка провайдеров.
Результат отправки возвращается объектом результата, а не исключением. Без проверки ошибки провайдера проходят молча, и отсутствие СМС первым обнаруживает клиент.
Номер получателя приводит к нужному виду сам модуль. Своя нормализация мешает: она ломает международный формат, на который рассчитывает провайдер.
Уровень выбирают по одному признаку: кто отвечает за формулировку. Служебные сообщения покупателю пишет и правит администратор, поэтому им нужен шаблон и событие с подстановками.
Технические сообщения себе и коллегам собирает код, и шаблон для них только лишний слой. Такие сообщения отправляют прямым вызовом провайдера, но результат проверяют так же внимательно, как и в первом случае.
Шаги
- Подключить модуль службы сообщений и выбрать подходящего провайдера.
- Завести шаблон СМС на имя нужного события со всеми нужными метками.
- Отправлять типовые сообщения событием с шаблоном, а не прямым вызовом провайдера.
- Срочные сообщения отправлять немедленно, а все остальные - через общую очередь.
- Проверять результат каждой отправки и писать ошибки в журнал.
- Свой провайдер регистрировать обработчиком события со списком провайдеров.
Код
Отправляем событие с шаблоном:
use Bitrix\Main\Sms\Event;
$result = (new Event('USER_CONFIRM_PHONE', [ 'CODE' => '123456', // подставится вместо метки в шаблоне 'USER_ID' => 42, // получателя ищут по данным события и шаблона])) ->setSite('s1') // сайт для поиска шаблона ->setLanguage('ru') ->send(false); // отправка через очередь// true в этом же месте означает немедленную отправкуСобытие само находит шаблон по имени, сайту и языку. Отсутствие подходящего шаблона означает тихое отсутствие сообщения, поэтому результат проверяют всегда.
Проверяем результат отправки:
if (!$result->isSuccess()) { foreach ($result->getErrorMessages() as $message) { \Bitrix\Main\Diag\Debug::writeToFile($message, 'sms', 'sms.log'); }}// ошибки провайдера без такой проверки проходят совершенно молча// getErrorMessages отдаёт список текстов, а не один общийОтправка возвращает объект результата, как и остальные операции ядра. Запись ошибок в журнал - минимальная защита от жалобы «код не пришёл» без единого следа в системе.
Отправляем срочное сообщение немедленно:
$result = (new Event('PASSWORD_RESTORE', $fields)) ->setSite('s1') ->send(true); // сразу, минуя очередь
$result = (new Event('IGNORED', $fields))->setTemplate(105)->send();// при явном шаблоне имя события не используется вовсеНемедленная отправка занимает время запроса, зато человек получает код сразу. Всё остальное - статусы заказа, напоминания, уведомления менеджеру - спокойно живёт в очереди.
Зовём провайдера напрямую:
\Bitrix\Main\Loader::includeModule('messageservice');
use Bitrix\MessageService\Sender\SmsManager;
SmsManager::sendMessage([ 'SENDER_ID' => $senderId, // идентификатор провайдера 'MESSAGE_TO' => '+79161234567', // номер в международном формате 'MESSAGE_FROM' => $channelId, // имя отправителя или канал 'MESSAGE_BODY' => 'Заказ собран и передан в доставку',]);// sendMessageDirectly отправляет то же самое немедленно// AUTHOR_ID указывает, от чьего имени ушло сообщениеПрямой вызов удобен там, где текст собирается кодом и шаблон только мешает. Взамен теряется возможность править формулировку без разработчика, и это стоит взвесить заранее.
Регистрируем своего провайдера:
\Bitrix\Main\EventManager::getInstance()->registerEventHandler( 'messageservice', 'onGetSmsSenders', // событие списка провайдеров 'vendor.module', \Vendor\Module\Sms\OwnSender::class, 'onGetSmsSenders');// класс наследуют от базового класса провайдера службы сообщенийПосле регистрации провайдер появляется в общем списке и становится доступен по своему идентификатору. Патчить сам модуль службы сообщений ради этого не нужно и попросту вредно.
Смотрим список доступных провайдеров:
print_r(array_keys(\Bitrix\MessageService\Sender\SmsManager::getSenders()));// пустой список означает, что ни один провайдер не настроенСписок отвечает на вопрос «почему не отправляется» быстрее настроек в интерфейсе. Пустой ответ означает, что до провайдера дело вообще ни разу не доходило.
Проверяем отправку без реальных сообщений:
// провайдер-заглушка из поставки модуля ведёт себя как настоящий$senders = \Bitrix\MessageService\Sender\SmsManager::getSenders();printf("провайдеров настроено: %d\n", count($senders));// на заглушке проверяют шаблоны, метки и обработку ошибокЗаглушка проходит весь путь отправки, но никуда не обращается. На ней проверяют подстановки в шаблоне и поведение кода при отказе, не тратя ни рубля на настоящие сообщения.
Отдельно стоит договориться, что делать при отказе провайдера. Молчаливая потеря сообщения недопустима там, где от него зависит вход в личный кабинет или подтверждение заказа.
Ограничения
Отправка СМС всегда остаётся платной и полностью внешней услугой. Сайт лишь передаёт сообщение провайдеру, а доставку и её стоимость определяет он.
Шаблон сообщения обязателен для высокого уровня этого API. Без активного шаблона на имя события сообщение не собирается, и отправлять становится нечего.
Своя нормализация номера мешает модулю. Номер приводится к международному виду самим модулем, и предварительная обрезка символов ломает этот разбор.
Массовые рекламные рассылки СМС - совсем не эта задача. Штатный механизм рассчитан на служебные сообщения, а маркетинговые рассылки живут в своём модуле со списками и отписками.
Типичные проблемы
СМС не приходит, ошибок в коде нет.
Шаблон на имя этого события не найден или неактивен. Событие в этом случае не собирает сообщение и молча завершается общим неуспехом.
Код подтверждения приходит с задержкой в минуты.
Сообщение отправлено через очередь, а разбор этой очереди идёт на обычных запросах. Срочные сообщения отправляют немедленно.
В журнале пусто, а клиент жалуется на отсутствие СМС.
Результат отправки не проверяется. Ошибки провайдера возвращаются объектом результата и без явной проверки нигде не остаются.
Провайдер не появляется в списке настроек.
Класс провайдера не зарегистрирован на событии списка провайдеров. Правка файлов модуля вместо регистрации к тому же исчезнет при очередном обновлении.
Провайдер отвечает отказом на корректный номер.
Номер обрезан или переформатирован своим кодом ещё до отправки. Приведение номера к международному виду выполняет сам модуль.
Частые вопросы
Чем событие лучше прямого вызова?
Текст живёт в шаблоне, его правит администратор, а код остаётся неизменным. Прямой вызов удобнее там, где текст собирается алгоритмом и шаблон только мешает.
Когда отправлять немедленно?
Когда человек ждёт сообщение прямо сейчас: код подтверждения, одноразовый пароль. Всё остальное отправляют через очередь и не задерживают запрос.
Где смотреть, ушло ли сообщение?
В списке сообщений службы и в своём журнале. Событие успешной отправки позволяет записать факт отправки со своей стороны.
Можно ли слать СМС из агента?
Да, ограничений нет: агент такой же серверный код. Важно только проверять результат и не отправлять одно и то же дважды при повторном запуске.
Как проверить отправку без реальных денег?
Провайдером-заглушкой из поставки модуля: он ведёт себя как настоящий, но никуда не отправляет. Так проверяют шаблоны, метки и обработку ошибок.
Смежное
- Почта и рассылки - оглавление подтемы
- Своё письмо с сайта: тип события, шаблон, поля - тот же механизм для писем
- Уведомления покупателю: письма и SMS по статусам заказа - события магазина и сообщения по ним
- Очередь сообщений: фоновая обработка задач в ядре - как устроена очередь под отправкой
- Свой провайдер SMS: класс, регистрация, отправка и статусы - класс провайдера подробнее
- SMS не доходят до получателя: разбор причин - когда сообщение не дошло
- Письма с сайта не доходят: разбор причин по убыванию частоты - соседний разбор доставки
- Рассылка с нуля: сегмент, письмо, отправка, отписка - массовые рассылки вместо служебных сообщений
- Инфраструктура и хостинг - устройство площадки целиком