Дашборд
Статистика отправок
Устройства по платформам
Последние уведомления
| Заголовок | Платформа | Отправлено | Доставлено | Статус | Дата |
|---|---|---|---|---|---|
|
Пока нет уведомлений |
|||||
Уведомления
История отправок
| Приложение | Заголовок | Текст | Платформа | Отправлено | Доставлено | Клики | Статус | Дата |
|---|
Приложения
| Название | API Key | Устройств | Web Push | iOS | Android | Действия |
|---|
Устройства
| ID | Проект | Платформа | Пользователь | Теги | Последняя активность | Действия |
|---|
Подписка
Ваш текущий план
Использование лимитов
Сравнение тарифов
| Возможность | Free | Starter | Pro |
|---|---|---|---|
| Web Push проекты | 1 | 5 | |
| iOS проекты | 1 | 5 | |
| Android проекты | 1 | 5 | |
| Уведомлений | |||
| Устройств | |||
| REST API | |||
| Расширенная аналитика | |||
| Приоритетная поддержка |
Управление пользователями
Список пользователей
| Пользователь | Роль | План | Проекты | Последний вход | Регистрация | Действия | |
|---|---|---|---|---|---|---|---|
Загрузка... |
|||||||
Документация
Подробные инструкции по интеграции для каждой платформы
Web Push — Браузерные уведомления
Web Push работает через стандартный Push API браузера с VAPID-ключами. Поддерживается в Chrome, Firefox, Edge, Safari 16+.
Важно знать
- Требуется HTTPS (кроме localhost)
- Пользователь должен дать разрешение на уведомления
- Service Worker обрабатывает уведомления в фоне
Шаг 1: Создайте проект
Перейдите в раздел "Проекты" → "Web Push" → "Создать проект". Сохраните API-ключи.
Шаг 2: Настройте Service Worker
Service Worker обязан находиться на вашем домене — это требование безопасности браузеров.
Выберите нужный вариант ниже в зависимости от вашей ситуации.
Если на вашем сайте нет Service Worker — создайте файл sw.js в корне сайта:
// Service Worker для push-уведомлений Push360
// Версия: 1.0
self.addEventListener('push', function(event) {
var data = event.data ? event.data.json() : {};
var options = {
body: data.body || '',
icon: data.icon || '/favicon.ico',
badge: data.badge,
image: data.image,
data: data.data || { url: '/' }
};
event.waitUntil(
self.registration.showNotification(data.title || 'Уведомление', options)
.then(function() {
// Отправляем статистику доставки
if (data.data && data.data.notificationId && data.data.apiUrl) {
return fetch(data.data.apiUrl + '/api/v1/notifications/' + data.data.notificationId + '/delivered', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ deviceId: data.data.deviceId || 'unknown' })
});
}
})
);
});
self.addEventListener('notificationclick', function(event) {
event.notification.close();
var data = event.notification.data || {};
// Отправляем статистику клика
if (data.notificationId && data.apiUrl) {
fetch(data.apiUrl + '/api/v1/notifications/' + data.notificationId + '/click', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ deviceId: data.deviceId || 'unknown' })
});
}
// Открываем ссылку
event.waitUntil(clients.openWindow(data.url || '/'));
});
Если на сайте уже есть свой sw.js (проверьте: откройте https://ваш-сайт.ru/sw.js),
то нужно добавить код ниже в существующий файл, а не заменять его!
Как проверить есть ли SW: В консоли браузера (F12) выполните:
navigator.serviceWorker.ready.then(r => console.log('SW:', r.active?.scriptURL))
Добавьте этот код в КОНЕЦ вашего существующего sw.js:
// ==========================================
// Push360 — обработка push-уведомлений
// Добавьте этот код в конец вашего sw.js
// ==========================================
self.addEventListener('push', function(event) {
var data = event.data ? event.data.json() : {};
// Проверяем что это уведомление от Push360
if (!data.data || !data.data.apiUrl) return;
var options = {
body: data.body || '',
icon: data.icon || '/favicon.ico',
badge: data.badge,
image: data.image,
tag: data.tag || 'push360',
data: data.data
};
event.waitUntil(
self.registration.showNotification(data.title || 'Уведомление', options)
.then(function() {
// Статистика доставки
if (data.data.notificationId && data.data.apiUrl) {
return fetch(data.data.apiUrl + '/api/v1/notifications/' + data.data.notificationId + '/delivered', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ deviceId: data.data.deviceId || 'unknown' })
}).catch(function() {}); // Игнорируем ошибки сети
}
})
);
});
self.addEventListener('notificationclick', function(event) {
var data = event.notification.data || {};
// Проверяем что это клик по уведомлению Push360
if (!data.apiUrl) return;
event.notification.close();
// Статистика клика
if (data.notificationId && data.apiUrl) {
fetch(data.apiUrl + '/api/v1/notifications/' + data.notificationId + '/click', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ deviceId: data.deviceId || 'unknown' })
}).catch(function() {});
}
// Открываем ссылку
if (data.url) {
event.waitUntil(clients.openWindow(data.url));
}
});
// ==========================================
// Конец кода Push360
// ==========================================
- Сохраните файл sw.js на сервере
- В браузере: F12 → Application → Service Workers → "Update"
- Или выполните в консоли:
navigator.serviceWorker.ready.then(r => r.update()) - Перезагрузите страницу (Ctrl+Shift+R)
Шаг 3: Подключите SDK на страницу
SDK должен загрузиться до вызова PushSDK.init(). Используйте один из способов ниже.
Способ 1: Подключение перед </body> (рекомендуется)
<!-- Подключите SDK перед закрывающим </body> -->
<script src="https://push360.ru/sdk/push-sdk.js"></script>
<script>
// Инициализация с автоподпиской (всё в одном!)
PushSDK.init({
apiKey: 'pk_ваш_публичный_ключ',
vapidPublicKey: 'ваш_VAPID_публичный_ключ',
serviceWorkerPath: '/sw.js',
apiUrl: 'https://push360.ru',
userId: window.currentUserId, // ОБЯЗАТЕЛЬНО — ID пользователя вашего сайта
tags: ['news'], // опционально — теги для сегментации
debug: true // включить логи в консоль (убрать в продакшене)
});
</script>
Способ 2: Универсальный (если SDK в <head>)
<script>
function initPush() {
if (typeof PushSDK === 'undefined') {
setTimeout(initPush, 100);
return;
}
PushSDK.init({
apiKey: 'pk_ваш_публичный_ключ',
vapidPublicKey: 'ваш_VAPID_публичный_ключ',
serviceWorkerPath: '/sw.js',
apiUrl: 'https://push360.ru',
userId: window.currentUserId, // ОБЯЗАТЕЛЬНО
tags: ['news'],
debug: true
});
}
window.addEventListener('load', initPush);
</script>
<script src="https://push360.ru/sdk/push-sdk.js" async></script>
Пример для Bitrix/PHP:
<?php // В footer.php перед </body> ?>
<?php if ($USER->IsAuthorized()): // SDK работает только для авторизованных ?>
<script src="https://push360.ru/sdk/push-sdk.js"></script>
<script>
PushSDK.init({
apiKey: 'pk_ваш_публичный_ключ',
vapidPublicKey: 'ваш_VAPID_публичный_ключ',
serviceWorkerPath: '/sw.js',
apiUrl: 'https://push360.ru',
userId: '<?= $USER->GetID() ?>', // ОБЯЗАТЕЛЬНО — ID пользователя Bitrix
tags: ['news'],
debug: true
});
</script>
<?php endif; ?>
Файл sw.js должен лежать в корне вашего сайта (например, https://your-site.ru/sw.js).
Скачайте актуальную версию: push360.ru/sdk/push-sw.js → сохраните как /sw.js
Пояснение параметров
- apiKey — публичный ключ приложения (начинается с pk_)
- vapidPublicKey — VAPID ключ для Web Push (из настроек приложения)
- serviceWorkerPath — путь к Service Worker на вашем сайте
- apiUrl — адрес сервера Push360 (обязателен!)
- userId — ОБЯЗАТЕЛЬНО! ID пользователя в вашей системе. Без него SDK не будет работать.
- tags — массив тегов для сегментации (например: ['news', 'promo', 'vip'])
- debug — включить логи в консоль браузера (отключите в продакшене)
- autoSubscribe — автоматически подписывать (по умолчанию true)
Готово!
После подключения SDK автоматически запросит разрешение и подпишет пользователя.
Отправляйте уведомления через админ-панель или REST API.
iOS — Apple Push Notification Service (APNS)
Прямая интеграция с APNS без посредников. Требуется Apple Developer аккаунт ($99/год).
Требования
- Apple Developer аккаунт
- APNS ключ (.p8 файл) или сертификат (.p12)
- Bundle ID вашего приложения
- Team ID из Apple Developer Portal
Шаг 1: Получите APNS ключ
- Откройте Apple Developer → Keys
- Нажмите "+" для создания нового ключа
- Введите имя и включите "Apple Push Notifications service (APNs)"
- Скачайте .p8 файл (хранится только один раз!)
- Запомните Key ID (10 символов)
Шаг 2: Создайте проект
Перейдите в "Проекты" → "iOS" → "Создать проект" и загрузите:
- APNS Key (.p8) — скачанный ключ
- Key ID — идентификатор ключа
- Team ID — найдите в Apple Developer → Membership
- Bundle ID — например, com.yourcompany.app
Шаг 3: Интеграция в iOS приложение (Swift)
import UIKit
import UserNotifications
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Запрос разрешения на уведомления
UNUserNotificationCenter.current().requestAuthorization(
options: [.alert, .sound, .badge]
) { granted, error in
if granted {
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
}
return true
}
// Получение Device Token
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
print("Device Token: \(token)")
// Отправляем токен на сервер
registerDeviceToken(token)
}
func registerDeviceToken(_ token: String) {
let url = URL(string: "https://push360.ru/api/v1/ios/register")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("pk_ваш_api_key", forHTTPHeaderField: "X-API-Key")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"deviceToken": token,
"userId": "user_123",
"tags": ["ios", "premium"]
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
URLSession.shared.dataTask(with: request).resume()
}
}
Тестирование
Push-уведомления не работают в симуляторе iOS. Используйте реальное устройство.
Android — WebSocket Push (без Google!)
Собственная реализация push-уведомлений через WebSocket. Работает без Google Play Services и Firebase.
Преимущества
- Работает на всех Android устройствах (включая китайские без Google)
- Полная независимость от Google/Firebase
- Мгновенная доставка через persistent WebSocket
- Автоматическое переподключение при потере связи
Шаг 1: Добавьте зависимости
// build.gradle (app)
dependencies {
// OkHttp для WebSocket
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
// Для работы в фоне
implementation 'androidx.work:work-runtime-ktx:2.9.0'
}
Шаг 2: Создайте PushService
class PushService : Service() {
private var webSocket: WebSocket? = null
private val client = OkHttpClient.Builder()
.pingInterval(30, TimeUnit.SECONDS)
.build()
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForeground(1, createNotification())
connectWebSocket()
return START_STICKY
}
private fun connectWebSocket() {
val deviceId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
val request = Request.Builder()
.url("wss://push360.ru/ws/android?apiKey=pk_ваш_ключ&deviceId=$deviceId")
.build()
webSocket = client.newWebSocket(request, object : WebSocketListener() {
override fun onMessage(webSocket: WebSocket, text: String) {
val json = JSONObject(text)
showNotification(
json.getString("title"),
json.getString("body"),
json.optString("url", null)
)
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
// Переподключение через 5 секунд
Handler(Looper.getMainLooper()).postDelayed({ connectWebSocket() }, 5000)
}
})
}
private fun showNotification(title: String, body: String, url: String?) {
val intent = if (url != null) {
Intent(Intent.ACTION_VIEW, Uri.parse(url))
} else {
packageManager.getLaunchIntentForPackage(packageName)
}
val pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_IMMUTABLE
)
val notification = NotificationCompat.Builder(this, "push_channel")
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(body)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build()
NotificationManagerCompat.from(this).notify(System.currentTimeMillis().toInt(), notification)
}
}
Шаг 3: Добавьте в AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application ...>
<service
android:name=".PushService"
android:foregroundServiceType="dataSync"
android:exported="false" />
</application>
Оптимизация батареи
Попросите пользователей отключить оптимизацию батареи для вашего приложения в настройках Android.
Flutter/Dart SDK (без Firebase!)
Кроссплатформенный SDK для Flutter. Работает на iOS (через APNs) и Android (через WebSocket) без Firebase.
Преимущества
- Один SDK для Android и iOS
- Полная независимость от Firebase/Google
- Автоматическая отправка статистики (доставка, клики)
- Поддержка userId и тегов для сегментации
Полноценный SDK с WebSocket для Android и APNs для iOS. Автоматическое подключение и показ уведомлений.
Шаг 1: Скачайте SDK
Скачать push360_full.dartШаг 2: Добавьте зависимости в pubspec.yaml
dependencies:
http: ^1.1.0
web_socket_channel: ^2.4.0
flutter_local_notifications: ^16.0.0
shared_preferences: ^2.2.0
Шаг 3: Для iOS — добавьте в Info.plist
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
Шаг 4: Инициализация в main.dart
import 'push360_full.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Инициализация Push360
final push360 = await Push360.initialize(
apiKey: 'pk_ваш_публичный_ключ',
onNotificationReceived: (data) {
print('Получено уведомление: ${data['title']}');
},
onNotificationClick: (data) {
print('Клик по уведомлению: ${data['url']}');
// Навигация по data['url']
},
onConnectionChanged: (connected) {
print('WebSocket: ${connected ? 'подключён' : 'отключён'}');
},
);
runApp(MyApp());
}
// В любом месте приложения:
// 1. Регистрация устройства (без userId при первом запуске)
final deviceId = await Push360.instance?.registerDevice();
print('Device ID: $deviceId');
// 2. После авторизации — привязываем пользователя
await Push360.instance?.linkUser('user_123');
// 3. При выходе из аккаунта
await Push360.instance?.unlinkUser();
// 4. Обновление тегов
await Push360.instance?.updateTags(['news', 'promo']);
- Android: WebSocket соединение с push360.ru/ws/android
- iOS: APNs через ваш сервер (нужен .p8 сертификат)
- Автоматическое переподключение при обрыве
- Heartbeat каждые 25 секунд
- Локальные уведомления через flutter_local_notifications
HTTP Polling, локальные уведомления, автоматическая статистика. Без Firebase!
Шаг 1: Добавьте зависимости
Settings → Project Dependencies → Add Dependency:
httpflutter_local_notifications
Шаг 2: Создайте Custom Actions
1. initPush360 (вызвать при запуске приложения)
- Return Type:
Boolean - Arguments:
apiKey(String, required)
2. registerPush360Device (после initPush360)
- Return Type:
String(nullable ✓) - Arguments:
userId(String, nullable ✓) - ⚠️ Сохраните результат в App State!
- 🔄 Автоматически запускает polling каждые 10 сек
3. linkPush360User (после авторизации)
- Return Type:
Boolean - Arguments:
userId(String, required)
4. unlinkPush360User (при выходе из аккаунта)
- Return Type:
Boolean - Arguments: нет
Шаг 3: Скопируйте код
Скачать push360_lite.dartСкопируйте весь файл в каждый Custom Action. FlutterFlow использует только нужные функции.
Шаг 4: Порядок вызова в приложении
// 1. При старте приложения (On Page Load главной страницы)
await initPush360('pk_ваш_публичный_ключ');
// 2. Сразу после — регистрация устройства
final deviceId = await registerPush360Device(null);
// Сохраните deviceId в App State!
// 3. После авторизации пользователя
await linkPush360User(currentUser.uid);
// 4. При выходе из аккаунта
await unlinkPush360User();
- Polling сервера каждые 10 секунд
- Показ локальных уведомлений
- Отправка статистики (доставка, клики)
initPush360— инициализация SDKregisterPush360Device— регистрация + старт pollinglinkPush360User— привязка пользователяunlinkPush360User— отвязка пользователяupdatePush360Tags— обновление теговstartPush360Polling— запуск pollingstopPush360Polling— остановка pollinggetPush360DeviceId— получить deviceId
Скоро на pub.dev
Пакет будет опубликован на pub.dev. После публикации установка станет простой:
# pubspec.yaml (после публикации)
dependencies:
push360_sdk: ^1.0.0
А пока используйте Полный SDK или Lite версию для FlutterFlow.
Настройка Android
<!-- android/app/src/main/AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application ...>
<!-- Для уведомлений в фоне -->
<service
android:name="com.dexterous.flutterlocalnotifications.ForegroundService"
android:exported="false"
android:stopWithTask="false"/>
</application>
Настройка iOS
<!-- ios/Runner/Info.plist -->
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
<!-- В Xcode: Runner → Signing & Capabilities → + Push Notifications -->
Полный пример (Full SDK)
import 'package:push360_sdk/push360_sdk.dart';
class _MyAppState extends State<MyApp> {
late Push360SDK push;
@override
void initState() {
super.initState();
_initPush();
}
Future<void> _initPush() async {
// Создаём SDK
push = Push360SDK(
apiUrl: 'https://push360.ru',
apiKey: 'pk_ваш_публичный_ключ',
debug: true,
);
// Инициализация
await push.init();
// Запрос разрешения
final granted = await push.requestPermission();
if (!granted) return;
// Регистрация устройства
await push.register(
userId: 'user_123', // ОБЯЗАТЕЛЬНО — ID пользователя
tags: ['news', 'promo'], // Теги (опционально)
);
// Обработка уведомлений
push.onNotificationReceived = (notification) {
print('📩 Получено: ${notification.title}');
};
// Обработка кликов
push.onNotificationClick = (notification, action) {
print('👆 Клик: ${notification.title}');
if (notification.url != null) {
Navigator.pushNamed(context, notification.url!);
}
};
}
@override
void dispose() {
push.dispose();
super.dispose();
}
}
API методы
| Метод | Описание |
|---|---|
init() | Инициализация SDK |
requestPermission() | Запрос разрешения на уведомления |
register({userId, tags}) | Регистрация устройства |
setUserId(userId) | Привязка пользователя |
setTags(tags) | Установка тегов |
addTag(tag) / removeTag(tag) | Добавить/удалить тег |
unregister() | Отписка от уведомлений |
dispose() | Освобождение ресурсов |
Свойства (read-only)
deviceId— ID устройстваuserId— ID пользователяtags— список теговisConnected— статус WebSocketisRegistered— зарегистрировано ли устройство
REST API — Отправка уведомлений
Отправляйте уведомления программно с вашего сервера через REST API.
Аутентификация
Все запросы требуют заголовки X-API-Key и X-API-Secret.
Базовый URL
https://push360.ru/api/v1
POST /notifications/send
curl -X POST https://push360.ru/api/v1/notifications/send \
-H "X-API-Key: pk_ваш_публичный_ключ" \
-H "X-API-Secret: sk_ваш_секретный_ключ" \
-H "Content-Type: application/json" \
-d '{
"title": "Привет!",
"body": "Это тестовое уведомление",
"url": "https://example.com/page",
"icon": "https://example.com/icon.png",
"platform": "all"
}'
Параметры запроса
| Параметр | Тип | Описание |
|---|---|---|
title * | string | Заголовок уведомления |
body * | string | Текст уведомления |
url | string | URL при клике |
icon | string | URL иконки (192x192) |
image | string | URL большого изображения |
platform | string | "web", "ios", "android" или пусто для всех |
tags | array | Фильтр по тегам: ["news", "promo"] |
userId | string | Отправить одному пользователю |
userIds | array | Отправить нескольким: ["user1", "user2"] |
🎯 Отправка конкретному пользователю
Используйте userId в параметрах или отдельный эндпоинт:
// Способ 1: через параметр userId
curl -X POST https://push360.ru/api/v1/notifications/send \
-H "X-API-Key: pk_ваш_ключ" \
-H "X-API-Secret: sk_ваш_секрет" \
-H "Content-Type: application/json" \
-d '{
"title": "Персональное уведомление",
"body": "Только для вас!",
"userId": "123"
}'
// Способ 2: через URL
curl -X POST https://push360.ru/api/v1/notifications/send-to-user/123 \
-H "X-API-Key: pk_ваш_ключ" \
-H "X-API-Secret: sk_ваш_секрет" \
-H "Content-Type: application/json" \
-d '{"title": "Привет!", "body": "Сообщение"}'
Node.js
const response = await fetch('https://push360.ru/api/v1/notifications/send', {
method: 'POST',
headers: {
'X-API-Key': 'pk_ваш_ключ',
'X-API-Secret': 'sk_ваш_секрет',
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'Привет!',
body: 'Тестовое уведомление',
platform: 'all'
})
});
Python
import requests
response = requests.post(
'https://push360.ru/api/v1/notifications/send',
headers={
'X-API-Key': 'pk_ваш_ключ',
'X-API-Secret': 'sk_ваш_секрет'
},
json={
'title': 'Привет!',
'body': 'Тестовое уведомление',
'platform': 'all'
}
)
PHP
$ch = curl_init('https://push360.ru/api/v1/notifications/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: pk_ваш_ключ',
'X-API-Secret: sk_ваш_секрет',
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode([
'title' => 'Привет!',
'body' => 'Тестовое уведомление'
])
]);
$response = curl_exec($ch);
PHP (Bitrix) — отправка конкретному пользователю
// Функция для отправки push-уведомления
function sendPushToUser($userId, $title, $body, $url = null) {
$ch = curl_init('https://push360.ru/api/v1/notifications/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: pk_ваш_ключ',
'X-API-Secret: sk_ваш_секрет',
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode([
'title' => $title,
'body' => $body,
'url' => $url,
'userId' => (string)$userId // ID пользователя Bitrix
])
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
return $response;
}
// Пример: уведомление при новом заказе
sendPushToUser(
$order->getUserId(),
'Заказ оформлен!',
'Ваш заказ #' . $order->getId() . ' принят в обработку',
'/personal/orders/' . $order->getId() . '/'
);
// Пример: напоминание о брошенной корзине
sendPushToUser(
$USER->GetID(),
'Вы забыли товары в корзине',
'Завершите оформление заказа со скидкой 5%',
'/personal/cart/'
);
Успешный ответ
{
"success": true,
"data": {
"notificationId": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"stats": { "total": 2, "sent": 2, "failed": 0 }
}
}