Настройка web-push уведомлений на сайте 1С-Битрикс
Web-push уведомления работают через браузерный Push API и Service Worker — технологии, независимые от PHP и Битрикса. Задача Битрикса: хранить подписки пользователей и инициировать отправку уведомлений через события (новый заказ, акция, напоминание о корзине). Сама отправка уходит через Web Push Protocol на серверы браузерных вендоров (FCM для Chrome, Mozilla Push Service для Firefox).
Инфраструктура: VAPID-ключи и Service Worker
Для Web Push нужны VAPID-ключи (Voluntary Application Server Identification) — пара публичный/приватный ключ для аутентификации сервера перед браузерным push-сервисом. Генерируются один раз:
composer require minishlink/web-push
use Minishlink\WebPush\VAPID;
$keys = VAPID::createVapidKeys();
// ['publicKey' => '...', 'privateKey' => '...']
Публичный ключ передаётся браузеру при регистрации подписки, приватный хранится на сервере в b_option:
COption::SetOptionString('local', 'vapid_public_key', $keys['publicKey']);
COption::SetOptionString('local', 'vapid_private_key', $keys['privateKey']);
Service Worker — это JS-файл, регистрируемый браузером для фоновой работы. Он обрабатывает входящие push-сообщения, когда пользователь не находится на вашем сайте. Файл service-worker.js должен располагаться в корне сайта (/service-worker.js), а не в поддиректории — это ограничение браузерного scope:
self.addEventListener('push', function(event) {
const data = event.data.json();
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: data.icon || '/local/images/push-icon.png',
data: { url: data.url }
})
);
});
self.addEventListener('notificationclick', function(event) {
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data.url));
});
Подписка пользователя
На стороне браузера запрашиваете разрешение и создаёте подписку:
async function subscribeToPush() {
const registration = await navigator.serviceWorker.register('/service-worker.js');
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('YOUR_VAPID_PUBLIC_KEY')
});
// Отправляем подписку на сервер
await fetch('/local/ajax/push_subscribe.php', {
method: 'POST',
body: JSON.stringify(subscription),
headers: { 'Content-Type': 'application/json' }
});
}
На сервере в /local/ajax/push_subscribe.php сохраняете подписку. Создаёте таблицу подписок:
CREATE TABLE b_local_push_subscription (
ID INT AUTO_INCREMENT PRIMARY KEY,
USER_ID INT, -- NULL для неавторизованных
ENDPOINT VARCHAR(500) NOT NULL,
P256DH TEXT NOT NULL,
AUTH VARCHAR(100) NOT NULL,
CREATED_AT DATETIME,
LAST_ACTIVE DATETIME,
UNIQUE KEY idx_endpoint (ENDPOINT(200))
);
Отправка уведомлений из Битрикса
Сервисный класс отправки через библиотеку minishlink/web-push:
use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;
function sendPushNotification(array $subscription, string $title, string $body, string $url): void {
$auth = [
'VAPID' => [
'subject' => 'https://example.com',
'publicKey' => COption::GetOptionString('local', 'vapid_public_key'),
'privateKey' => COption::GetOptionString('local', 'vapid_private_key'),
],
];
$webPush = new WebPush($auth);
$webPush->queueNotification(
Subscription::create([
'endpoint' => $subscription['ENDPOINT'],
'keys' => ['p256dh' => $subscription['P256DH'], 'auth' => $subscription['AUTH']],
]),
json_encode(['title' => $title, 'body' => $body, 'url' => $url])
);
foreach ($webPush->flush() as $report) {
if ($report->isSubscriptionExpired()) {
// Удаляем протухшую подписку из б_local_push_subscription
deleteExpiredSubscription($report->getEndpoint());
}
}
}
Интеграция с событиями Битрикса
Напоминание о брошенной корзине. Агент раз в час находит корзины с товарами старше 2 часов у пользователей с подпиской и отправляет push. Таблица b_sale_basket, фильтр по DATE_UPDATE < NOW() - INTERVAL 2 HOUR и ORDER_ID IS NULL.
Уведомление о смене статуса заказа. Обработчик OnSaleOrderStatusUpdate:
AddEventHandler("sale", "OnSaleOrderStatusUpdate", function($orderId, $arFields) {
if ($arFields['STATUS_ID'] === 'D') { // Delivered
$userId = CSaleOrder::GetByID($orderId)['USER_ID'];
sendPushToUser($userId, 'Заказ доставлен', 'Ваш заказ #' . $orderId . ' ожидает вас');
}
});
Массовые рассылки. Выбираете всех подписчиков из b_local_push_subscription, отправляете батчами по 100 через webPush->queueNotification() + flush(). Большие рассылки — через агент с постраничной обработкой, чтобы не исчерпать лимит времени выполнения скрипта.
Протухшие подписки (endpoint вернул 404 или 410) удаляйте сразу — они накапливаются быстро и замедляют рассылки.







