Логин
Пароль
Зарегистрироваться
После регистрации на сайте вам будет доступно отслеживание состояния заказов, личный кабинет и другие новые возможности

Отправка sms при смене статуса заказа

⚡ Bitrix5 мин на чтение

Введение

В этой статье разберём, как настроить автоматическую отправку SMS-уведомлений покупателю при изменении статуса его заказа в 1С-Битрикс. SMS — один из самых эффективных каналов связи с клиентом: сообщение доставляется мгновенно, не теряется в спаме и почти всегда прочитывается. Уведомления о смене статуса (например, «Заказ принят», «Заказ отправлен», «Заказ доставлен») повышают лояльность и снижают нагрузку на службу поддержки.

В Bitrix за отправку SMS отвечает класс Bitrix\Main\Sms\Event. Он использует заранее созданные в административной панели SMS-шаблоны — точно так же, как работают почтовые события. Нам остаётся лишь подписаться на нужное событие модуля «Интернет-магазин» (sale) и в обработчике собрать данные заказа, подставить их в поля SMS-события и вызвать send().

Весь код размещается в файле init.php (в пространстве имён Bitrix\Main\Loader он подключается автоматически) либо в кастомном модуле. Никаких дополнительных настроек в админке, кроме создания самих SMS-шаблонов, не требуется.

Подготовка: создание SMS-шаблонов

Прежде чем писать код, зайдите в административную панель Bitrix: «Настройки» → «Настройки продукта» → «События» → «SMS-шаблоны» и создайте шаблон для каждого статуса, по которому будет отправляться уведомление. Имя SMS-события должно совпадать с тем, которое мы укажем в коде — например, SMS_STATUS_CHANGED_XX (для статуса «Доставлен») или SMS_STATUS_CHANGED_N (для статуса «Новый»).

Совет: В шаблоне можно использовать поля #ORDER_ID#, #ORDER_PRICE#, #ORDER_DATE# — они будут заменены на значения из массива $fields, который мы передадим в конструктор Event.

В коде событие формируется динамически, в зависимости от текущего статуса заказа:

$eventName = 'SMS_STATUS_CHANGED_' . $order->getField('STATUS_ID'); // Например: SMS_STATUS_CHANGED_XX, SMS_STATUS_CHANGED_N, SMS_STATUS_CHANGED_F

Таким образом, для каждого статуса создаётся отдельный шаблон со своим текстом — это даёт максимальную гибкость. Статусы XX (доставлен) и N (новый) используются здесь как пример; в вашем проекте коды статусов могут отличаться.

Подключение обработчика события

Bitrix построен на событийно-ориентированной архитектуре. Чтобы «поймать» момент сохранения заказа, мы регистрируем обработчик через глобальный менеджер событий:

<?php use Bitrix\Main; use Bitrix\Main\Sms\Event; use Bitrix\Sale\Order; Main\EventManager::getInstance()->addEventHandler( 'sale', // модуль 'OnSaleOrderSaved', // событие 'mySmsSendOrder' // имя функции-обработчика );

Разберём по строкам:

  1. Импорты. Подключаем классы Bitrix\Main (для работы с событиями), Bitrix\Main\Sms\Event (отправка SMS) и Bitrix\Sale\Order (работа с заказом).
  2. Регистрация.EventManager::addEventHandler принимает три аргумента: имя модуля ('sale'), имя события ('OnSaleOrderSaved') и имя колбэка ('mySmsSendOrder'). Теперь при каждом сохранении заказа Bitrix вызовет нашу функцию.

OnSaleOrderSaved срабатывает и при создании, и при обновлении заказа (в отличие от OnSaleOrderPayed, который ловит только оплату). Это универсальное событие, подходящее для отслеживания любых изменений — статуса, суммы, состава товаров и т.д.

Базовая отправка: функция mySmsSendOrder

В функции-обработчике мы получаем объект события $event, извлекаем из него заказ и старые значения полей, фильтруем нужные статусы, собираем данные и отправляем SMS. Полный код:

function mySmsSendOrder(Main\Event $event) { $order = $event->getParameter("ENTITY"); $oldValues = $event->getParameter("VALUES"); $isNew = $event->getParameter("IS_NEW"); // Отправляем только для статусов XX (доставлен) и N (новый) if (!in_array($order->getField('STATUS_ID'), ['XX', 'N'])) return; // Извлекаем телефон из свойства заказа с кодом PHONE $phone = ''; foreach ($order->getPropertyCollection() as $property) { if ($property->getField('CODE') == 'PHONE') $phone = $property->getField('VALUE'); } $fields = [ 'PHONE' => $phone, 'ORDER_ID' => $order->getField('ACCOUNT_NUMBER'), 'ORDER_REAL_ID' => $order->getField('ID'), 'ORDER_DATE' => $order->getField('DATE_STATUS')->format('Y.m.d'), 'ORDER_STATUS' => $order->getField('STATUS_ID'), 'ORDER_PRICE' => \SaleFormatCurrency($order->getPrice(), $order->getCurrency()), 'ORDER_DESCRIPTION' => $order->getField('USER_DESCRIPTION'), ]; $sms = new Event('SMS_STATUS_CHANGED_' . $order->getField('STATUS_ID'), $fields); $sms->setSite('s1'); $sms->setLanguage('ru'); $sms->send(); }

Как это работает — по шагам

  1. Извлечение данных из события:$event->getParameter("ENTITY") возвращает объект Bitrix\Sale\Order — сам заказ. getParameter("VALUES") — массив старых значений полей до сохранения (нужен, чтобы понять, что именно изменилось). getParameter("IS_NEW") — флаг, указывающий, создан заказ впервые или обновлён существующий.
  2. Фильтрация по статусу: Мы проверяем, что текущий статус заказа входит в список интересующих нас статусов (XX и N). Если нет — выходим из функции, не отправляя SMS. Это гарантирует, что уведомления уходят только в нужные моменты.
  3. Получение номера телефона: В Bitrix телефон покупателя хранится в свойствах заказа. Мы проходим по всей коллекции свойств (getPropertyCollection()), ищем свойство с кодом PHONE и забираем его значение. Важно, чтобы в настройках интернет-магазина свойство телефона имело именно такой символьный код — иначе SMS не уйдёт.
  4. Сборка полей: Формируем ассоциативный массив $fields, в который помещаем:
    • PHONE — номер получателя;
    • ORDER_ID — символьный код заказа (номер счёта);
    • ORDER_REAL_ID — числовой ID заказа в БД;
    • ORDER_DATE — дата установки статуса (формат Y.m.d);
    • ORDER_STATUS — код статуса;
    • ORDER_PRICE — сумма заказа, отформатированная функцией SaleFormatCurrency;
    • ORDER_DESCRIPTION — комментарий покупателя.
    Эти поля будут подставлены в SMS-шаблон вместо плейсхолдеров вида #ORDER_ID#.
  5. Отправка SMS: Создаём объект Event, передавая ему имя SMS-события (динамически формируемое из префикса SMS_STATUS_CHANGED_ и кода статуса) и массив полей. Указываем сайт (s1) и язык (ru), затем вызываем send(). Bitrix сам найдёт подходящий SMS-шаблон, подставит значения и отправит сообщение через настроенного SMS-провайдера.
Важно: Для каждого статуса, который вы обрабатываете, в админке должен быть создан SMS-шаблон с соответствующим кодом события. Если шаблона нет, send() тихо проигнорирует вызов, и сообщение не уйдёт. Всегда проверяйте, что шаблон активен и привязан к нужному сайту.

Асинхронная отправка с задержкой

Иногда требуется отправить SMS не сразу, а спустя какое-то время. Типичный кейс: статус заказа меняется на «Отгружен» (XX), а через 3 дня, если статус не изменился (например, покупатель не оформил возврат), нужно напомнить, что товар доставлен и попросить оценить покупку.

Механизм такой: мы регистрируем отложенную задачу через register_shutdown_function, которая выполнится после завершения текущего скрипта, и внутри неё ставим паузу (sleep) на нужное количество секунд. Перед отправкой заново загружаем заказ из БД и проверяем, остался ли статус прежним; если статус изменился — SMS не шлём.

Почему register_shutdown_function? Эта функция регистрирует колбэк, который выполняется после того, как скрипт отдал ответ клиенту (в PHP-FPM — после завершения fastcgi_finish_request()). Пользователь не ждёт завершения долгой операции. Однако sleep всё равно блокирует воркер, поэтому этот подход — демонстрационный. В реальной нагрузке используйте агенты Bitrix, очередь задач или внешний планировщик.
<?php Main\EventManager::getInstance()->addEventHandler( 'sale', 'OnSaleOrderSaved', 'mySmsSendOrderAsync' ); function mySmsSendOrderAsync(Main\Event $event) { $order = $event->getParameter("ENTITY"); $oldValues = $event->getParameter("VALUES"); // Срабатываем только при свежей смене статуса на "XX" if ($order->getField('STATUS_ID') !== 'XX' || $oldValues['STATUS_ID'] === 'XX') return; // Извлекаем телефон $phone = ''; foreach ($order->getPropertyCollection() as $property) { if ($property->getField('CODE') == 'PHONE') $phone = $property->getField('VALUE'); } $fields = [ 'PHONE' => $phone, 'ORDER_ID' => $order->getField('ACCOUNT_NUMBER'), 'ORDER_DATE' => $order->getField('DATE_STATUS')->format('Y-m-d H:i:s'), 'ORDER_STATUS'=> $order->getField('STATUS_ID'), 'ORDER_PRICE' => SaleFormatCurrency($order->getPrice(), $order->getCurrency()), ]; // Регистрируем отложенную задачу register_shutdown_function(function () use ($fields, $order) { sleep(259200); // 3 дня (60 x 60 x 72) // Проверяем, не изменился ли статус за прошедшее время $currentOrder = Order::load($order->getId()); if ($currentOrder && $currentOrder->getField('STATUS_ID') === 'XX') { sendSmsAsync($fields); } }); } // Вспомогательная функция для отправки SMS с обработкой ошибок function sendSmsAsync($fields) { try { $sms = new Event('SMS_SALE_STATUS_CHANGED_' . $fields['ORDER_STATUS'], $fields); $sms->setSite('s1'); $sms->setLanguage('ru'); $sms->send(); } catch (\Exception $e) { // Логируем ошибки в файл /sms.log file_put_contents( $_SERVER["DOCUMENT_ROOT"] . '/sms.log', $e->getMessage() ); } }

Разбор асинхронного варианта

  1. Условие срабатывания: только при смене на «XX». Проверка $order->getField('STATUS_ID') !== 'XX' отсеивает все другие статусы. Вторая часть $oldValues['STATUS_ID'] === 'XX' не даёт сработать повторно, если заказ уже был в статусе «XX» и просто пересохраняется (например, менеджер редактирует комментарий). Мы ловим именно переход из другого статуса в «XX».
  2. Сбор полей: Аналогично базовому варианту, но здесь мы не передаём ORDER_REAL_ID и ORDER_DESCRIPTION — только ключевые данные. Формат даты изменён на Y-m-d H:i:s для более точной фиксации момента.
  3. Отложенное выполнение:register_shutdown_function принимает анонимную функцию. Ключевое слово use «захватывает» переменные $fields и $order внутрь замыкания, чтобы они были доступны, когда колбэк в конечном счёте выполнится. Внутри — sleep(259200) (60 × 60 × 72 = 3 дня). После паузы мы перезагружаем заказ из базы свежим методом Order::load($order->getId()).
  4. Проверка перед отправкой: Если заказ всё ещё в статусе «XX» — вызываем sendSmsAsync(). Если статус изменился (например, покупатель оформил возврат, и заказ перешёл в «Возврат»), SMS не отправляется. Это предотвращает ложные уведомления.
  5. Вынос отправки в отдельную функцию с try-catch:sendSmsAsync() обёрнута в try-catch: если провайдер SMS недоступен или шаблон не найден, ошибка не «упадёт» с фаталом, а запишется в лог-файл /sms.log в корне сайта. Так мы не потеряем информацию о сбое и сможем вовремя отреагировать.
Предупреждение:sleep(259200) блокирует PHP-процесс на 3 дня. В production-среде это приведёт к исчерпанию пула воркеров. Для реальных проектов используйте агенты Bitrix (метод CAgent::AddAgent) — они выполняются в фоне при каждом хите и не блокируют пользовательские запросы. Либо выносите задачи в очередь через RabbitMQ, Gearman или Redis.

Заключение

Мы разобрали два подхода к отправке SMS при смене статуса заказа в Bitrix: синхронный (мгновенная отправка) и асинхронный с задержкой (отложенная отправка с верификацией статуса).

Первый подходит для большинства сценариев: «Заказ подтверждён», «Заказ отправлен», «Заказ доставлен». Второй — для случаев, когда нужно напомнить клиенту о чём-то через продолжительное время, но только при условии, что ситуация не изменилась.

Обратите внимание на ограничения register_shutdown_function + sleep: этот код написан в учебных целях. На боевом сайте обязательно замените его на агенты или очередь.

Полный код для копирования

Готовый блок кода, который можно скопировать целиком и вставить в init.php:

<?php // ============================================================ // 1. SMS при смене статуса (базовая отправка) // ============================================================ use Bitrix\Main; use Bitrix\Main\Sms\Event; use Bitrix\Sale\Order; Main\EventManager::getInstance()->addEventHandler( 'sale', 'OnSaleOrderSaved', 'mySmsSendOrder' ); function mySmsSendOrder(Main\Event $event) { $order = $event->getParameter("ENTITY"); $oldValues = $event->getParameter("VALUES"); $isNew = $event->getParameter("IS_NEW"); if (!in_array($order->getField('STATUS_ID'), ['XX', 'N'])) return; $phone = ''; foreach ($order->getPropertyCollection() as $property) { if ($property->getField('CODE') == 'PHONE') $phone = $property->getField('VALUE'); } $fields = [ 'PHONE' => $phone, 'ORDER_ID' => $order->getField('ACCOUNT_NUMBER'), 'ORDER_REAL_ID' => $order->getField('ID'), 'ORDER_DATE' => $order->getField('DATE_STATUS')->format('Y.m.d'), 'ORDER_STATUS' => $order->getField('STATUS_ID'), 'ORDER_PRICE' => \SaleFormatCurrency($order->getPrice(), $order->getCurrency()), 'ORDER_DESCRIPTION' => $order->getField('USER_DESCRIPTION'), ]; $sms = new Event('SMS_STATUS_CHANGED_' . $order->getField('STATUS_ID'), $fields); $sms->setSite('s1'); $sms->setLanguage('ru'); $sms->send(); } // ============================================================ // 2. Асинхронная отправка с задержкой 3 дня // (с проверкой статуса перед отправкой) // ============================================================ Main\EventManager::getInstance()->addEventHandler( 'sale', 'OnSaleOrderSaved', 'mySmsSendOrderAsync' ); function mySmsSendOrderAsync(Main\Event $event) { $order = $event->getParameter("ENTITY"); $oldValues = $event->getParameter("VALUES"); if ($order->getField('STATUS_ID') !== 'XX' || $oldValues['STATUS_ID'] === 'XX') return; $phone = ''; foreach ($order->getPropertyCollection() as $property) { if ($property->getField('CODE') == 'PHONE') $phone = $property->getField('VALUE'); } $fields = [ 'PHONE' => $phone, 'ORDER_ID' => $order->getField('ACCOUNT_NUMBER'), 'ORDER_DATE' => $order->getField('DATE_STATUS')->format('Y-m-d H:i:s'), 'ORDER_STATUS'=> $order->getField('STATUS_ID'), 'ORDER_PRICE' => SaleFormatCurrency($order->getPrice(), $order->getCurrency()), ]; register_shutdown_function(function () use ($fields, $order) { sleep(259200); $currentOrder = Order::load($order->getId()); if ($currentOrder && $currentOrder->getField('STATUS_ID') === 'XX') { sendSmsAsync($fields); } }); } function sendSmsAsync($fields) { try { $sms = new Event('SMS_SALE_STATUS_CHANGED_' . $fields['ORDER_STATUS'], $fields); $sms->setSite('s1'); $sms->setLanguage('ru'); $sms->send(); } catch (\Exception $e) { file_put_contents( $_SERVER["DOCUMENT_ROOT"] . '/sms.log', $e->getMessage() ); } }

После вставки не забудьте создать SMS-шаблоны в админке для каждого используемого статуса. Коды статусов (XX, N) замените на свои, если они отличаются.


Ключевые моменты

  • СобытиеOnSaleOrderSaved — срабатывает при создании и каждом обновлении заказа, подходит для отслеживания изменений статуса
  • Телефон Извлекается из свойства заказа с кодом PHONE (настраивается в админке); без корректного номера SMS не отправится
  • Шаблон Имя SMS-события формируется динамически: SMS_STATUS_CHANGED_{STATUS_ID} — для каждого статуса нужен отдельный шаблон
  • Отложка При асинхронной отправке обязательно перепроверяйте статус заказа перед отправкой, чтобы не отправить устаревшее уведомление
  • Ошибки Всегда оборачивайте отправку в try-catch и логируйте исключения — это упростит отладку
Назад к списку