Перейти к содержимому

СМС из кода - событие и шаблон, прямая отправка, свой провайдер

Отправляем СМС с сайта: код подтверждения, статус заказа, уведомление менеджеру. Разбираем два уровня API, очередь, своего провайдера и разбор ошибок отправки.

Механика

За отправку СМС в платформе отвечает отдельный модуль службы сообщений. Он соединяет сайт с внешними провайдерами и хранит все настройки самой отправки.

Работать с отправкой СМС можно на двух разных уровнях. Высокий - событие и шаблон, как у почтовых писем, низкий - прямой вызов выбранного провайдера с готовым текстом.

Событие ищет подходящий шаблон по своему имени, сайту и языку сообщения. Найденный шаблон подставляет переданные значения вместо меток и отдаёт готовый текст модулю.

Текст самого сообщения живёт в шаблоне, а вовсе не в коде решения. Правит его администратор сайта, и изменение формулировки не требует выкладки новой версии.

Отправка сообщения по умолчанию идёт через общую очередь. Запрос посетителя не ждёт ответа провайдера, а сообщение уходит следом за завершением запроса.

Немедленная отправка нужна только по-настоящему срочным сообщениям. Код подтверждения при входе

  • как раз такой случай, потому что человек ждёт его на экране.

Прямой вызов провайдера обходится вообще без шаблона сообщения. Он берёт получателя, отправителя и текст, а всё остальное - настройки самого провайдера.

Свой провайдер подключают обработчиком события, а вовсе не правкой файлов модуля. Класс наследуют от базового класса модуля и регистрируют на событии списка провайдеров.

Результат отправки возвращается объектом результата, а не исключением. Без проверки ошибки провайдера проходят молча, и отсутствие СМС первым обнаруживает клиент.

Номер получателя приводит к нужному виду сам модуль. Своя нормализация мешает: она ломает международный формат, на который рассчитывает провайдер.

Уровень выбирают по одному признаку: кто отвечает за формулировку. Служебные сообщения покупателю пишет и правит администратор, поэтому им нужен шаблон и событие с подстановками.

Технические сообщения себе и коллегам собирает код, и шаблон для них только лишний слой. Такие сообщения отправляют прямым вызовом провайдера, но результат проверяют так же внимательно, как и в первом случае.

Шаги

  1. Подключить модуль службы сообщений и выбрать подходящего провайдера.
  2. Завести шаблон СМС на имя нужного события со всеми нужными метками.
  3. Отправлять типовые сообщения событием с шаблоном, а не прямым вызовом провайдера.
  4. Срочные сообщения отправлять немедленно, а все остальные - через общую очередь.
  5. Проверять результат каждой отправки и писать ошибки в журнал.
  6. Свой провайдер регистрировать обработчиком события со списком провайдеров.

Код

Отправляем событие с шаблоном:

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. Без активного шаблона на имя события сообщение не собирается, и отправлять становится нечего.

Своя нормализация номера мешает модулю. Номер приводится к международному виду самим модулем, и предварительная обрезка символов ломает этот разбор.

Массовые рекламные рассылки СМС - совсем не эта задача. Штатный механизм рассчитан на служебные сообщения, а маркетинговые рассылки живут в своём модуле со списками и отписками.

Типичные проблемы

СМС не приходит, ошибок в коде нет.

Шаблон на имя этого события не найден или неактивен. Событие в этом случае не собирает сообщение и молча завершается общим неуспехом.

Код подтверждения приходит с задержкой в минуты.

Сообщение отправлено через очередь, а разбор этой очереди идёт на обычных запросах. Срочные сообщения отправляют немедленно.

В журнале пусто, а клиент жалуется на отсутствие СМС.

Результат отправки не проверяется. Ошибки провайдера возвращаются объектом результата и без явной проверки нигде не остаются.

Провайдер не появляется в списке настроек.

Класс провайдера не зарегистрирован на событии списка провайдеров. Правка файлов модуля вместо регистрации к тому же исчезнет при очередном обновлении.

Провайдер отвечает отказом на корректный номер.

Номер обрезан или переформатирован своим кодом ещё до отправки. Приведение номера к международному виду выполняет сам модуль.

Частые вопросы

Чем событие лучше прямого вызова?

Текст живёт в шаблоне, его правит администратор, а код остаётся неизменным. Прямой вызов удобнее там, где текст собирается алгоритмом и шаблон только мешает.

Когда отправлять немедленно?

Когда человек ждёт сообщение прямо сейчас: код подтверждения, одноразовый пароль. Всё остальное отправляют через очередь и не задерживают запрос.

Где смотреть, ушло ли сообщение?

В списке сообщений службы и в своём журнале. Событие успешной отправки позволяет записать факт отправки со своей стороны.

Можно ли слать СМС из агента?

Да, ограничений нет: агент такой же серверный код. Важно только проверять результат и не отправлять одно и то же дважды при повторном запуске.

Как проверить отправку без реальных денег?

Провайдером-заглушкой из поставки модуля: он ведёт себя как настоящий, но никуда не отправляет. Так проверяют шаблоны, метки и обработку ошибок.

Смежное

Первоисточник