Как push-уведомления вернули 18% брошенных корзин
Один из наших клиентов — интернет-магазин электроники с аудиторией 300 000 уникальных посетителей ежемесячно. Теряли до 40% брошенных корзин. После внедрения push-уведомлений через OneSignal конверсия в восстановлении корзины выросла на 18%. Ежедневно отправляем 5 000+ персонализированных уведомлений. Средний CTR — 12% (против 3% у email). Окупаемость интеграции — 2 недели, а средний LTV клиентов вырос на 25% за счёт повторных покупок.
OneSignal использует Web Push Protocol для доставки push-уведомлений в браузер. Даже при закрытом браузере Service Worker обрабатывает сообщение и отображает его. Это принципиально экономит ресурсы: не нужен почтовый сервер или SMS-шлюз.
Как работает интеграция: архитектура и компоненты
Браузер пользователя → OneSignal SDK (подписка, получение player_id) → POST /local/api/onesignal/register (сохраняем player_id в b_user) → событие в Битрикс (заказ, доставка, скидка) → PHP → OneSignal REST API → push пользователю.
Ключевой элемент — player_id (или subscription_id в новом SDK). Один пользователь может иметь несколько (разные браузеры и устройства).
Хранение подписок в Битрикс: таблица и ORM
Для хранения подписок используем отдельную таблицу local_push_subscriptions:
CREATE TABLE local_push_subscriptions (
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
USER_ID INT,
SUBSCRIPTION_ID VARCHAR(200) NOT NULL UNIQUE,
PLATFORM ENUM('web','android','ios') DEFAULT 'web',
ACTIVE CHAR(1) DEFAULT 'Y',
CREATED_AT DATETIME,
LAST_USED_AT DATETIME,
INDEX idx_user (USER_ID)
);
USER_ID может быть NULL для неавторизованных посетителей — они идентифицируются по SUBSCRIPTION_ID. При авторизации «подтягиваем» анонимные подписки к аккаунту (событие OnAfterUserAuthorize).
// При авторизации (событие OnAfterUserAuthorize)
AddEventHandler('main', 'OnAfterUserAuthorize', function(array $fields) {
if (empty($fields['USER_ID'])) return;
// Ищем подписку по сессии, привязываем к пользователю
LocalPushSubscriptionTable::update(
['=USER_ID' => false], // NULL
['USER_ID' => $fields['USER_ID']]
);
});
Почему OneSignal выгоднее самописного решения
Самописные push-решения требуют поддержки Web Push Protocol (VAPID), обработки подписок под разные браузеры и масштабирования. OneSignal избавляет от этого: SDK сам обновляется, сегментация и аналитика встроены, TTL сообщений настраивается. Для типового интернет-магазина OneSignal дешевле и быстрее в разы. Экономия на инфраструктуре достигает 70%.
| Критерий | OneSignal | Самописное решение |
|---|---|---|
| Время настройки | 1–2 дня | 1–2 недели |
| Поддержка кросс-браузерности | Встроена | Требуется ручная настройка |
| Аналитика и A/B тесты | Готовый дашборд | Разработка с нуля |
| Цена за 10 000 подписчиков | Бесплатно до 10 000 | ~$50/мес на инфраструктуру |
Как сегментировать пользователей для целевых уведомлений?
OneSignal позволяет создавать сегменты по поведению: брошенная корзина, просмотр категорий, тип устройства. Мы настраиваем автоматические сегменты через REST API по данным из Битрикс. Например, сегмент «Покупатели с суммой чека > 5000 руб. и не было покупок 30 дней» — отправляем им персональную скидку. Эффективность таких сегментов в 2–3 раза выше массовых рассылок.
Пошаговая настройка SDK на фронтенде
Устанавливаем скрипт в <head> шаблона или через менеджер тегов:
<!-- OneSignal Web Push SDK -->
<script src="https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js" defer></script>
<script>
window.OneSignalDeferred = window.OneSignalDeferred || [];
OneSignalDeferred.push(async function(OneSignal) {
await OneSignal.init({
appId: "YOUR_ONESIGNAL_APP_ID",
safari_web_id: "web.onesignal.auto.YOUR_SAFARI_ID",
notifyButton: { enable: false },
allowLocalhostAsSecureOrigin: false,
});
const subscription = await OneSignal.User.PushSubscription;
if (subscription.optedIn) {
await registerSubscription(subscription.id);
}
OneSignal.User.PushSubscription.addEventListener('change', async (event) => {
if (event.current.optedIn) {
await registerSubscription(event.current.id);
}
});
});
async function registerSubscription(subscriptionId) {
await fetch('/local/api/onesignal/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Bitrix-Csrf-Token': BX.bitrix_sessid()
},
body: JSON.stringify({ subscription_id: subscriptionId })
});
}
</script>
Важно: Service Worker должен быть зарегистрирован через SDK. OneSignal автоматически генерирует файл OneSignalSDKWorker.js. Если используете свой Service Worker, нужно его копнуть.
Отправка уведомлений через REST API
Серверная часть на PHP отправляет уведомления по событиям Битрикс:
class OneSignalService
{
private string $appId;
private string $restApiKey;
private string $baseUrl = 'https://onesignal.com/api/v1';
public function sendToUser(int $userId, string $title, string $body, array $data = []): array
{
$subscriptions = LocalPushSubscriptionTable::getList([
'filter' => ['USER_ID' => $userId, 'ACTIVE' => 'Y'],
'select' => ['SUBSCRIPTION_ID'],
])->fetchAll();
if (empty($subscriptions)) {
return ['skipped' => 'no_subscriptions'];
}
$subscriptionIds = array_column($subscriptions, 'SUBSCRIPTION_ID');
return $this->send([
'app_id' => $this->appId,
'include_subscription_ids' => $subscriptionIds,
'headings' => ['en' => $title, 'ru' => $title],
'contents' => ['en' => $body, 'ru' => $body],
'data' => $data,
'web_url' => $data['url'] ?? '/',
'ttl' => 86400,
]);
}
public function sendToSegment(string $segment, string $title, string $body, array $extra = []): array
{
return $this->send(array_merge([
'app_id' => $this->appId,
'included_segments' => [$segment],
'headings' => ['en' => $title, 'ru' => $title],
'contents' => ['en' => $body, 'ru' => $body],
], $extra));
}
private function send(array $payload): array
{
$ch = curl_init("{$this->baseUrl}/notifications");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"Authorization: Key {$this->restApiKey}",
],
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true) ?? [];
}
}
Пример ответа при ошибке InvalidSubscriptionId
{
"id": "",
"recipients": 0,
"errors": ["All included subscription ids are invalid or have reached their rate limit"]
}
При получении такого ответа мы обнуляем ACTIVE для соответствующих подписок.
Точки интеграции с событиями Битрикс
Смена статуса заказа:
AddEventHandler('sale', 'OnSaleStatusOrder', function(string $statusId, \Bitrix\Sale\Order $order) {
$messages = [
'P' => ['Заказ принят', 'Ваш заказ #' . $order->getField('ACCOUNT_NUMBER') . ' принят в обработку'],
'D' => ['Заказ отправлен', 'Ваш заказ передан в доставку'],
'F' => ['Заказ выполнен', 'Спасибо за покупку! Оставьте отзыв о товаре'],
];
if (!isset($messages[$statusId])) return;
[$title, $body] = $messages[$statusId];
$userId = (int)$order->getUserId();
(new OneSignalService())->sendToUser($userId, $title, $body, [
'url' => '/personal/order/detail/' . $order->getField('ACCOUNT_NUMBER') . '/',
'order_id' => $order->getId(),
]);
});
Брошенная корзина — через агент каждые 60 минут. Персональные акции — массовая отправка через сегменты OneSignal или по фильтрам пользователей.
Управление подписками из личного кабинета
На странице «Уведомления» пользователь выбирает типы push: статус заказа, акции, напоминания. Настройки храним в пользовательских полях (UF_PUSH_ORDER_STATUS, UF_PUSH_PROMO). Перед отправкой проверяем флаг.
Как обрабатываются ошибки подписок?
При получении от OneSignal InvalidSubscriptionId — помечаем запись как ACTIVE = N и не шлём повторно. Очистка происходит автоматически при каждой отправке.
Что входит в работу и сроки
- Развёртывание SDK на фронтенде (custom кнопка подписки).
- Создание таблицы подписок с ORM Битрикс.
- Привязка подписок к пользователям (включая авторизацию).
- Настройка отправки по событиям: заказы, брошенные корзины, акции.
- Интерфейс управления подписками в ЛК.
- Документация и обучение ваших разработчиков.
- Поддержка 14 дней после сдачи.
| Задача | Срок |
|---|---|
| Подключение SDK, сохранение subscription_id | 2–3 дня |
| Таблица подписок, привязка к пользователям | 1–2 дня |
| Отправка по событиям заказов | 2–3 дня |
| Брошенная корзина, управление подпиской из ЛК | 3–5 дней |
| Полный комплекс | 2–3 недели |
Оценим ваш проект за один день. Свяжитесь — подберём оптимальное решение под ключ. Гарантируем стабильность и прозрачность. Получите консультацию прямо сейчас — это бесплатно. Закажите интеграцию и начните получать дополнительную прибыль уже через неделю.







