Перейти к содержанию

Мобильное push-уведомление сообщает пользователю об изменении на телефоне. Например, о готовности отчета, который пользователь запустил на сайте. PHP передает уведомление через Bitrix\Pull\Push::add, а модуль pull выбирает способ отправки с учетом устройства, настроек и активности получателя.

Как уведомление попадает на телефон

Отправка проходит несколько этапов:

  1. PHP выбирает пользователя и передает текст модулю pull.

  2. Модуль находит зарегистрированные устройства и определяет режим отправки.

  3. Уведомление уходит в push-сервис сразу или остается в очереди отложенных уведомлений.

  4. Push-сервис передает уведомление устройству. Отображение зависит от приложения и настроек операционной системы.

Различайте три механизма, которые могут участвовать в одном сценарии.

|

|| Механизм | Для чего нужен | Что использовать || || Pull-команда | Обновить интерфейс подключенного браузера или мобильного приложения | CPullStack::AddByUsers и обработчик команды || || Мобильное push-уведомление | Сообщить об изменении на телефоне | Bitrix\Pull\Push::add || || Уведомление мессенджера | Добавить уведомление в интерфейс мессенджера и учитывать настройки его типа | API уведомлений модуля im, например CIMNotify::Add || |#

Прямая отправка push не создает уведомление в мессенджере. Получение Pull-команды внутри мобильного приложения тоже не подтверждает, что операционная система показала push. Подключение обработчиков разобрано в статье Обработка команд в JavaScript.

Подготовить окружение

Используйте тестовую коробочную установку Битрикс24 с мобильным приложением Битрикс24 и тестового пользователя. Пример отправляет уведомление только текущему пользователю. Для другой установки сначала потребуется отдельная интеграция мобильного приложения с push-сервисом.

Перед запуском проверьте условия:

  • Модуль pull установлен, мобильные push-уведомления включены, push-сервис настроен.

  • Устройство зарегистрировано для нужного пользователя и приложения. Регистрация содержит токен устройства, по которому push-сервис выбирает адресата.

  • Пользователь разрешил push в своих настройках, а операционная система разрешает приложению показывать уведомления.

  • На установке выполняются агенты — фоновые задания Bitrix, которые запускаются по расписанию. Агент CPushManager::SendAgent обрабатывает отложенную очередь.

Подготовьте мобильную отправку:

  1. Под учетной записью администратора откройте Настройки > Настройки продукта > Настройки модулей > Push and Pull. Включите Отправлять PUSH уведомления на мобильные телефоны и сохраните настройки. Эта опция разрешает модулю отправлять мобильные push-уведомления.

  2. В мобильном приложении войдите на тестовый портал. Разрешите уведомления в операционной системе. При входе приложение регистрирует устройство для push-уведомлений. Самостоятельно создавать токен в PHP не нужно.

  3. В браузере войдите на тот же портал под тем же пользователем. После создания тестовой страницы проверьте получателя по разделу Найти причину отсутствия уведомления. При RECORD_NOT_FOUND сначала восстановите регистрацию или разрешение push у пользователя.

  4. У администратора уточните способ запуска агентов — при обращениях к сайту или по расписанию сервера. Для теста очереди потребуется возможность временно приостановить этот запуск на изолированном стенде и затем восстановить его.

В штатной конфигурации адрес внешнего сервиса мобильной отправки задает модуль. Если соединение блокируется сетью, передайте администратору значение опции pull.push_service_url и время контрольной отправки. Замена адресов Pull-сервера не настраивает мобильный push-сервис.

Работающий сервер Pull-команд сам по себе не подтверждает готовность мобильного push-сервиса. Общие проверки окружения приведены в статье Настройка модуля и сервера очередей.

Пример рассчитан на приложение с идентификатором Bitrix24. Для собственного приложения нужны его регистрация в push-сервисе, получение и обновление токенов устройств, а также обработка уведомления и нажатия на него. Это отдельная клиентская интеграция. После ее подготовки используйте согласованный APP_ID в отправке, диагностике и отмене. Одной замены строки Bitrix24 недостаточно.

Создайте файл /local/pull-demo/push.php на тестовой установке. Путь указан относительно корня сайта. Исключите страницу из композитного и дополнительного HTML-кеша, как в статье Обновление страницы по команде сервера.

Отчет 42 условный. Код не формирует файл и не сохраняет результат. Для проверки достаточно подключения pull. Обработчик OnGetDependentModule и библиотека pull.client понадобятся отдельно, если вместе с push вы хотите получать Pull-команду на странице.

Отправить уведомление

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

Создать страницу отправки

Добавьте в /local/pull-demo/push.php следующий код. Страница проверяет авторизацию и CSRF-токен — значение, которое связывает отправку формы с сессией пользователя. Получателя она берет из сессии.

Сначала используйте только кнопку Отправить push. Две кнопки отмены понадобятся после перехода к проверке отложенной очереди. Они не останавливают отправку, которая уже началась.

<?php

use Bitrix\Main\Loader;
use Bitrix\Pull\Push;

require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php';

$APPLICATION->SetTitle('Отправка мобильного push-уведомления');

// Проверяем вход пользователя, подключение модуля и разрешение мобильного push
if (!$USER->IsAuthorized())
{
    echo '<p>Войдите под тестовым пользователем.</p>';
}
elseif (!Loader::includeModule('pull'))
{
    echo '<p>Не удалось подключить модуль pull.</p>';
}
elseif (!CPullOptions::GetPushStatus())
{
    echo '<p>Мобильные push-уведомления отключены на установке.</p>';
}
else
{
    // Получателя берем из сессии, а не из параметров формы
    $userId = (int)$USER->GetID();
    $appId = 'Bitrix24';
    // Общая метка объединяет уведомления отчета, дополнительная отмечает готовность
    $tag = 'example.reports.report.42';
    $subTag = 'example.reports.report.42.ready';
    $message = '';

    // Отправляем и отменяем уведомления только по POST
    if ($_SERVER['REQUEST_METHOD'] === 'POST')
    {
        // Проверяем, что форма отправлена в текущей сессии пользователя
        if (!check_bitrix_sessid())
        {
            $message = 'Сессия истекла. Обновите страницу и повторите действие.';
        }
        else
        {
            $action = $_POST['action'] ?? '';
            $result = false;

            if ($action === 'send')
            {
                // Передаем текст и метки, снимаем отсрочку отправки
                $result = Push::add($userId, [
                    'module_id' => 'example.reports',
                    'push' => [
                        'message' => 'Отчет 42 готов.',
                        'expiry' => 3600,
                        'tag' => $tag,
                        'sub_tag' => $subTag,
                        'app_id' => $appId,
                        'send_immediately' => 'Y',
                    ],
                ]);
            }
            elseif ($action === 'cancel_tag')
            {
                // Удаляем из очереди группу уведомлений текущего пользователя
                $result = CPushManager::DeleteFromQueueByTag(
                    $userId,
                    $tag,
                    $appId
                );
            }
            elseif ($action === 'cancel_sub_tag')
            {
                // Удаляем из очереди уведомления с дополнительной меткой готовности
                $result = CPushManager::DeleteFromQueueBySubTag(
                    $userId,
                    $subTag,
                    $appId
                );
            }

            // Результат API не подтверждает доставку или число удаленных записей
            if (!$result)
            {
                $message = 'Вызов API не выполнен. Проверьте действие и настройки.';
            }
            elseif ($action === 'send')
            {
                $message = 'API принял параметры. Это не подтверждение доставки.';
            }
            else
            {
                $message = 'Вызов отмены выполнен. Проверьте удаление записи из очереди.';
            }
        }
    }
    ?>
    <p>Получатель: <?= $userId ?></p>
    <p><?= htmlspecialcharsbx($message) ?></p>
    <form method="post">
        <?= bitrix_sessid_post() ?>
        <button type="submit" name="action" value="send">Отправить push</button>
        <button type="submit" name="action" value="cancel_tag">Отменить по TAG</button>
        <button type="submit" name="action" value="cancel_sub_tag">Отменить по SUB_TAG</button>
    </form>
    <?php
}

require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/footer.php';

Откройте страницу, сверните мобильное приложение и нажмите Отправить push. Дождитесь завершения запроса. Ожидаемый результат на телефоне — уведомление «Отчет 42 готов.». Страница не создает обработчик нажатия на уведомление и не настраивает переход к отчету.

Метод Push::add возвращает true, когда модуль принял параметры для последующей обработки, и false, если добавление отклонено. Например, при отключенном push, некорректных получателях или неверной кодировке данных. Значение true не подтверждает регистрацию устройства, прием push-сервисом или отображение уведомления.

Не обновляйте страницу с повторной отправкой POST для проверки результата. Каждое новое нажатие может отправить еще одно уведомление. Теги не защищают от повторной отправки.

Выбрать данные уведомления

В примере module_id содержит условный идентификатор отправителя example.reports. Для базовой отправки создавать отдельный модуль отчетов не нужно. Пользователя передают отдельно, в $userId. Для нескольких получателей можно передать массив их положительных идентификаторов.

Передайте module_id и непустой массив push. В таблице перечислены поля push, которые нужны для текста, адресации приложению, пользовательских настроек и управления отправкой. Обязательность для учебного сценария отличается от проверки API. Например, служебный push может не содержать текста.

|

|| Поле | Тип и допустимые значения | Назначение | Обязательность и поведение при пропуске || || message | string, UTF-8 | Текст уведомления | Для показа текста передайте непустую строку. При пропуске используется пустая строка, текст уведомления не формируется || || expiry | int, не меньше 0 | Срок жизни для push-сервиса в секундах. В примере — 3600 | Передавайте явно. Особенность пропуска поля описана ниже || || tag | string, до 255 символов после удаления пробелов по краям | Метка группы уведомлений для отмены. Например, все уведомления отчета 42 | Необязательное поле. При пропуске используется пустая метка, отменить по ней уведомление нельзя || || sub_tag | string, до 255 символов после удаления пробелов по краям | Метка для более точной отмены. Например, уведомления о готовности отчета 42 | Необязательное поле. При пропуске используется пустая метка, отменить по ней уведомление нельзя || || app_id | string | Идентификатор зарегистрированного приложения | Необязательное поле. При пропуске или пустой строке используется Bitrix24 || || send_immediately | string, 'Y' или 'N' | Значение 'Y' переводит отложенный режим в немедленный | Необязательное поле. При пропуске используется 'N' || || params | array или string с корректным JSON для отложенной отправки | Данные, формат которых согласован с мобильным приложением. Массив модуль кодирует в JSON | Необязательное поле. При пропуске используется пустой массив [] || || type | string | Код типа из схемы уведомлений отправляющего модуля | Передайте, если нужна проверка настройки этого типа. При пропуске или пустой строке модуль не выполняет проверку настройки по типу || |#

В CPushManager::AddQueue значение EXPIRY по умолчанию равно 43200 секундам. Оно применяется при отсутствии поля, null или отрицательном значении. При вызове Push::add и AddByUsers с мобильным push задавайте expiry явно: отсутствие ключа может вызвать предупреждение PHP до передачи данных в AddQueue.

Модуль принимает expiry = 0, но не используйте его как команду удаления или отмены. Срок жизни обрабатывает push-сервис. Поле не задает задержку обработки и срок удаления записи из локальной очереди.

Передавайте tag и sub_tag длиной не более 255 символов. Модуль обрезает пробелы по краям. Пустую или слишком длинную метку он не сохраняет как метку очереди.

Для данных, которые должно обработать мобильное приложение, используйте push.params. Их формат и действие при открытии задает приложение. Не переносите в это поле произвольный URL в расчете на автоматический переход. Параметры Pull-команды params и данные мобильного push push.params имеют разное назначение.

Предпочтительно передавать push.params массивом. Если передаете строку, используйте корректный JSON. При немедленной отправке модуль передает строку как есть, а при обработке отложенной очереди агент декодирует ее как JSON. При ошибке декодирования агент заменяет параметры пустой строкой.

В рабочем приложении сохраните результат операции до отправки и проверьте права получателя на отчет. В push передавайте минимум данных, потому что текст может появиться на заблокированном экране. При открытии отчета повторно проверьте доступ на сервере.

Учесть настройки типа уведомления

Для разовой проверки достаточно прямой отправки. Если пользователь должен отдельно включать и отключать уведомления о готовности отчетов, добавьте тип в схему уведомлений модуля im. Схема — список типов с названиями и настройками каналов доставки. Код типа связывает отправку с пользовательской настройкой.

На тестовой установке с установленным im добавьте обработчик в /local/php_interface/init.php, сохранив существующий код. Если файла нет, создайте его с открывающим тегом <?php.

// Добавляем тип уведомления в схему модуля im
AddEventHandler('im', 'OnGetNotifySchema', static function () {
    return [
        'example.reports' => [
            'NAME' => 'Учебные отчеты',
            'NOTIFY' => [
                'report_ready' => [
                    'NAME' => 'Отчет готов',
                    // Включаем мобильный канал по умолчанию, остальные выключаем
                    'PUSH' => 'Y',
                    'SITE' => 'N',
                    'MAIL' => 'N',
                    'XMPP' => 'N',
                ],
            ],
        ],
    ];
});

Обработчик объявляет тип report_ready для отправителя example.reports. Значение PUSH = 'Y' разрешает мобильный канал по умолчанию. Параметры SITE, MAIL и XMPP задают выключенные по умолчанию каналы сайта, почты и XMPP. Регистрация типа сама не отправляет уведомление.

В массив push вызова Push::add добавьте 'type' => 'report_ready'. Сохраните 'module_id' => 'example.reports' на внешнем уровне массива. На новом запросе проверьте, что CIMNotifySchema::GetNotifySchema() после подключения im содержит путь ['example.reports']['NOTIFY']['report_ready'].

Для проверки выключите этот тип в пользовательских настройках уведомлений и повторите отправку. Вызов Push::getConfigTypeStatus('example.reports', 'report_ready', $userId) должен вернуть false, а текст push не должен прийти. Верните настройку и выполните контрольную отправку. Проверку выполняйте через Push::add. Прямой вызов AddQueue не проверяет push.type.

Если уведомление должно также появиться в мессенджере, используйте метод CIMNotify::Add модуля im. Он создает уведомление для выбранного пользователя. Мобильный push можно включить в это уведомление. Не добавляйте вторую прямую отправку того же push, если его уже формирует уведомление мессенджера.

Отправить push вместе с Pull-командой

Если отчет должен обновиться и на открытой странице, замените вызов Push::add в ветке send вызовом CPullStack::AddByUsers. Сохраните проверки формы и переменные $userId, $tag, $subTag, $appId:

// Отправляем Pull-команду и мобильный push одному пользователю
$result = CPullStack::AddByUsers([$userId], [
    'module_id' => 'example.reports',
    'command' => 'reportReady',
    // Эти данные получит обработчик reportReady на открытой странице
    'params' => [
        'reportId' => 42,
        'text' => 'Отчет 42 готов.',
    ],
    // Текст и метки мобильного уведомления передаем отдельно от команды
    'push' => [
        'message' => 'Отчет 42 готов.',
        'expiry' => 3600,
        'tag' => $tag,
        'sub_tag' => $subTag,
        'app_id' => $appId,
        'send_immediately' => 'Y',
    ],
]);

Если в предыдущем разделе вы настроили тип уведомления, сохраните 'type' => 'report_ready' внутри массива push нового вызова. Поле module_id должно остаться равным example.reports. Без push.type отправка не проверит пользовательскую настройку типа report_ready.

Подготовьте страницу получателя и зависимость модуля по статье Обновление страницы по команде сервера. Имена example.reports, reportReady, reportId и text совпадают с ее обработчиком.

Проверьте два результата отдельно — изменение страницы и уведомление на телефоне. Метод AddByUsers может вернуть true для Pull-команды, даже если добавление мобильного push не удалось. Общего подтверждения доставки по обоим каналам этот результат не дает.

Как работает отложенная отправка

У отправки есть два разных этапа ожидания. Сначала Push::add сохраняет данные в буфере текущего PHP-запроса. В обычной странице модуль планирует их обработку фоновой задачей приложения. После обработки CPushManager может сохранить уведомление в отложенную очередь, которую разбирает агент CPushManager::SendAgent.

Не завершайте выполнение примера сразу после Push::add. Сохраните подключение footer.php. Фоновая задача текущего запроса выполняется при завершении приложения. Она отличается от агента, который обрабатывает сохраненную очередь в последующих запусках. Контексты разобраны в статье Отправка команд из PHP.

Режим зависит от активности пользователя, мобильного и настольного клиентов, а также интеллектуального фильтра push-уведомлений. Фильтр помогает учитывать, где пользователь уже работает с сообщениями. Для проверки обычного поведения замените 'send_immediately' => 'Y' на 'send_immediately' => 'N'.

|

|| Режим CPushManager | Что происходит || || SEND_IMMEDIATELY | Модуль готовит немедленную отправку текста || || SEND_IMMEDIATELY_SILENT | Модуль готовит отправку без обычного звука, если звук не задан явно || || SEND_DEFERRED | Текст остается в отложенной очереди. Отдельно могут уйти служебные данные, например счетчик || || SEND_SKIP | Текст уведомления исключается из отправки. Служебные данные могут отправляться || || RECORD_NOT_FOUND | Нет подходящей записи для отправки, например при отсутствии устройства или отключенном push у пользователя || |#

Значение send_immediately = 'Y' снимает отсрочку режима SEND_DEFERRED. Параметр не создает устройство и не отменяет любой запрет отправки. Обработчик события OnBeforeSendPush также может изменить выбранный режим.

Агент обрабатывает очередь порциями. Момент запуска зависит от настройки агентов и нагрузки, поэтому не рассчитывайте на получение через фиксированное число секунд. Параметр expiry относится к push-сервису, а не к расписанию агента. Не используйте это поле как срок удаления записи из локальной очереди.

Проверить очередь отдельно от онлайн-статуса

Для воспроизводимой проверки отложенной отправки замените только вызов отправки в ветке send на CPushManager::AddQueue с SEND_DEFERRED = 'Y'. Метод вызывается у объекта, а названия его полей записываются в верхнем регистре.

// Обращаемся к менеджеру напрямую, чтобы проверить отложенную очередь
$manager = new CPushManager();
$result = $manager->AddQueue([
    'USER_ID' => $userId,
    'SKIP_USERS' => [],
    'MESSAGE' => 'Отчет 42 готов.',
    'PARAMS' => [],
    'ADVANCED_PARAMS' => [],
    'BADGE' => -1,
    'SOUND' => '',
    'EXPIRY' => 3600,
    'TAG' => $tag,
    'SUB_TAG' => $subTag,
    'APP_ID' => $appId,
    // Переводим немедленные режимы в отложенный, чтобы проверить отмену
    'SEND_IMMEDIATELY' => 'N',
    'SEND_DEFERRED' => 'Y',
]);

Параметр SKIP_USERS задает пользователей, которых нужно исключить из отправки текста. В примере список пуст. Параметры PARAMS и ADVANCED_PARAMS содержат данные для мобильного приложения. Значение BADGE = -1 оставляет расчет счетчика модулю. Пустое значение SOUND не задает собственный звук.

Значение SEND_DEFERRED = 'Y' переводит немедленные режимы в отложенный. Параметр не исправляет отсутствие устройства и не переводит SEND_SKIP в отправку. Вызов напрямую проходит через CPushManager и не выполняет проверку push.type, которую делает Push::add.

Не передавайте send_deferred внутри массива push в расчете на тот же результат: Bitrix\Pull\Event не передает это поле в AddQueue.

Метод AddQueue возвращает false при отключенном push на установке или отсутствии допустимых идентификаторов пользователей. Значение true не означает, что для каждого адресата появилась запись очереди. Сначала проверьте устройство и режим отправки.

Удалить уведомление из очереди

Отменяйте уведомление, если пользователь уже прочитал результат или операция больше не актуальна. Для группы уведомлений используйте DeleteFromQueueByTag, для отдельной метки — DeleteFromQueueBySubTag.

На тестовой странице кнопка Отменить по TAG вызывает удаление записей текущего пользователя с меткой example.reports.report.42. Кнопка Отменить по SUB_TAG использует метку example.reports.report.42.ready. Эти вызовы уже включены в код страницы.

Метка SUB_TAG не является вторым условием к TAG. Метод DeleteFromQueueBySubTag ищет записи только по пользователю и значению SUB_TAG. Поэтому задавайте метки с префиксом приложения и идентификатором объекта, чтобы отмена не затронула другую операцию.

Push-метки не связаны с тегами CPullWatch. Отмена push не снимает наблюдение за объектом и не отменяет уже отправленную Pull-команду. Работа с наблюдателями описана в статье Подписки по тегам.

Учитывайте границы отмены:

  • Методы удаляют записи отложенной очереди. Они не удаляют push из буфера текущего PHP-запроса. Последовательность Push::add и немедленного удаления по тегу в том же запросе не гарантирует отмену.

  • Успешное удаление возвращает true, даже если совпадающих записей уже нет. Результат не содержит число удаленных уведомлений.

  • Методы дополнительно формируют служебный push с notificationsToCancel. Его обработка зависит от мобильного приложения. Не считайте его гарантированным удалением уведомления с экрана телефона.

  • Параметр $appId адресует служебную отмену приложению. Удаление из локальной очереди ограничено пользователем и меткой, без отдельного фильтра по приложению. Для разных приложений используйте разные префиксы меток.

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

Проверить результат

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

Проверить отправку и отмену

Выполните проверки на одной установке и с одним тестовым пользователем:

  1. Откройте мобильное приложение и убедитесь, что выбрана нужная учетная запись. Сверните приложение. Отправьте push через исходный вариант страницы и проверьте текст на телефоне.

  2. Если добавили Pull-команду, откройте страницу получателя из быстрого старта. Проверьте изменение текста и счетчика независимо от мобильного уведомления.

  3. Замените отправку на вариант с AddQueue и SEND_DEFERRED. На изолированной тестовой установке приостановите запуск агентов вместе с администратором. Откройте мобильное приложение для обновления его активности и проверьте режим через GetDeviceInfo. При SEND_SKIP или RECORD_NOT_FOUND устраните причину по таблице диагностики. Не переходите к отмене, пока не подтвердите добавление записи по инструкции ниже.

  4. Отправьте одно уведомление. Подтвердите создание записи, затем отдельным запросом нажмите Отменить по SUB_TAG и подтвердите удаление одной записи.

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

  6. Повторите проверку с кнопкой Отменить по TAG. Чтобы проверить групповое удаление, при приостановленных агентах добавьте два уведомления с одинаковым TAG и разными SUB_TAG. Для второго отправления временно замените $subTag на example.reports.report.42.updated. Отмена по TAG должна удалить обе записи.

  7. Верните исходный вызов Push::add и войдите на тестовую страницу под другим тестовым пользователем, который не входил в мобильное приложение. Проверьте отправку без зарегистрированного устройства. Успешное добавление через Push::add не должно трактоваться как доставка на телефон.

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

Для подтверждения работы очереди подключите PHP-отладчик к тестовым запросам. Если отладчик не настроен, выполните этот этап вместе с разработчиком или администратором стенда. Исходники ядра менять не нужно.

|

|| Точка остановки в /bitrix/modules/pull/classes/general/pull_push.php | Что проверить | Условие успешной проверки || || AddQueue, цикл по $groupMode[self::SEND_DEFERRED], перед $DB->Add(...) | $userId, $arAdd['TAG'], $arAdd['SUB_TAG'], $arAdd['APP_ID'] | Выбраны тестовый пользователь, нужные метки и приложение || || Возврат из $DB->Add(...) при пошаговом выполнении | Возвращаемый идентификатор добавленной записи | Получен положительный идентификатор. Запишите его в отчет о тесте || || DeleteFromQueueBySubTag или DeleteFromQueueByTag, выполнение $DB->Query($strSql) | $userId, $tag и число затронутых записей результата запроса | Удалена одна запись для одиночного теста или две для группового || |#

Чтобы получить число удаленных записей, шагните внутрь $DB->Query и остановитесь на return $res в /bitrix/modules/main/classes/general/database.php. В окне вычисления выражений отладчика выполните $res->AffectedRowsCount(). Сделайте это до следующего SQL-запроса, который может изменить счетчик. Сам запрос повторно не выполняйте.

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

В отчете о проверке запишите дату, версии продукта, модулей main и pull, PHP, мобильного приложения и ОС. Укажите состояние приложения, настройки уведомлений и режим запуска агентов. Повторите проверку в открытом и свернутом приложении — отображение может отличаться.

Найти причину отсутствия уведомления

Для проверки зарегистрированного устройства используйте GetDeviceInfo. Добавьте следующий фрагмент внутрь ветки страницы после определения $userId и $appId. Он показывает только режим и число доступных записей устройств:

// Получаем режим отправки для текущего пользователя и выбранного приложения
$deviceInfo = CPushManager::GetDeviceInfo($userId, [], $appId);
$recipientInfo = is_array($deviceInfo) ? ($deviceInfo[$userId] ?? []) : [];
$mode = $recipientInfo['mode'] ?? CPushManager::RECORD_NOT_FOUND;
$deviceCount = count($recipientInfo['device'] ?? []);

// Выводим только режим и количество устройств, без токенов
echo '<p>Режим: ' . htmlspecialcharsbx($mode) . '</p>';
echo '<p>Доступных устройств: ' . $deviceCount . '</p>';

Режим отражает состояние на момент проверки. Метод AddQueue может изменить его параметрами отправки или обработчиком OnBeforeSendPush. Пустой массив device не всегда означает отсутствие регистрации — модуль может очистить его при выборе отложенного режима. Проверяйте режим вместе с массивом. Не выводите полный результат GetDeviceInfo на страницу, потому что он содержит токены устройств.

|

|| Симптом | Что проверить | Ожидаемый результат || || Страница сообщает об отключенном push | CPullOptions::GetPushStatus() и настройки мобильного push | Push включен на установке || || Режим RECORD_NOT_FOUND | Регистрацию устройства, APP_ID, активность пользователя и его настройку push | Для нужного приложения найден получатель, которому разрешена отправка || || Режим SEND_DEFERRED, текста пока нет | Выполнение CPushManager::SendAgent и очередь | Агент обрабатывает записи || || Режим SEND_SKIP | Онлайн-статус, настольный клиент, интеллектуальный фильтр и обработчики событий | Причина исключения текста установлена || || API вернул true, уведомления нет | Настройку и доступность push-сервиса, регистрацию устройства, разрешения ОС | Контрольное уведомление приходит на выбранное устройство || || Pull-команда пришла, push отсутствует | Мобильную отправку отдельно от соединения Pull-клиента | Подтверждены оба результата || || Отмена вернула true, текст появился | Метки, момент обработки очереди и наличие push в буфере запроса | Отмена выполнена до того, как агент забрал запись || |#

Если устройство найдено и режим допускает отправку, но уведомление не пришло, выполните проверки вместе с администратором:

  1. Убедитесь, что запрос тестовой страницы завершился без PHP-ошибок. Для отложенной отправки проверьте запуск CPushManager::SendAgent и ошибки этого запуска в журнале PHP.

  2. Проверьте разрешения уведомлений для приложения в ОС, затем выполните новую контрольную отправку при свернутом приложении.

  3. Проверьте доступ с PHP-сервера к адресу из опции pull.push_service_url, разрешение имени, TLS-соединение и сетевые ограничения. Успешное соединение еще не подтверждает прием конкретного уведомления сервисом.

  4. Если результат не изменился, передайте в поддержку время отправки, версии модуля и приложения, ОС, идентификатор тестового пользователя, APP_ID, режим и ошибки сервера. Не включайте токены устройств и лицензионный ключ в общедоступный отчет.

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