כיצד התראות Push החזירו 18% מעגלות נטושות
אחד הלקוחות שלנו—חנות אלקטרוניקה מקוונת עם 300,000 מבקרים ייחודיים בחודש—איבד עד 40% מעגלות קניות נטושות. לאחר הטמעת התראות push באמצעות OneSignal, שיעור ההמרה לשחזור עגלות עלה ב-18%. אנו שולחים 5,000+ התראות מותאמות אישית מדי יום. שיעור הקליקים הממוצע (CTR) הוא 12% (לעומת 3% בדוא"ל). ההטמעה החזירה את עצמה תוך שבועיים, וערך הלקוח הממוצע (LTV) גדל ב-25% בזכות רכישות חוזרות. לצוות שלנו יש ניסיון של 5+ שנים בפיתוח Bitrix וביצענו למעלה מ-50 הטמעות מוצלחות של התראות push.
OneSignal משתמש ב-פרוטוקול Web Push כדי לספק התראות push לדפדפן. גם כשהדפדפן סגור, ה-Service Worker מעבד את ההודעה ומציג אותה. זה חוסך משאבים מהותית: אין צורך בשרת דוא"ל או בשער SMS.
ארכיטקטורה ואחסון נתונים
דפדפן → OneSignal SDK (הרשמה, קבלת player_id) → POST /local/api/onesignal/register (שמירת player_id ב-b_user) → אירוע Bitrix (הזמנה, משלוח, הנחה) → PHP → OneSignal REST API → שליחת push למשתמש.
המרכיב המרכזי הוא player_id (או subscription_id ב-SDK החדש). למשתמש אחד יכולים להיות כמה כאלה (דפדפנים ומכשירים שונים).
לאחסון הרשמות אנו משתמשים בטבלה נפרדת 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) ); 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) ); יכול להיות NULL למבקרים לא מחוברים—הם מזוהים באמצעות USER_ID. בעת התחברות, אנו "מחברים" הרשמות אנונימיות לחשבון (אירוע SUBSCRIPTION_ID).
// При авторизации (событие 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 (VAPID), טיפול בהרשמות לדפדפנים שונים, וסקיילינג. OneSignal מבטלת את זה: ה-SDK מתעדכן אוטומטית, סגמנטציה ואנליטיקה מובנות, וניתן להגדיר TTL להודעות. עבור חנות איקומרס טיפוסית, OneSignal זולה ומהירה פי כמה. החיסכון בתשתיות יכול להגיע ל-70%.
| קריטריון | OneSignal | פתרון עצמי |
|---|---|---|
| זמן הגדרה | 1–2 ימים | 1–2 שבועות |
| תמיכה בדפדפנים שונים | מובנית | דורש הגדרה ידנית |
| אנליטיקה ובדיקות A/B | לוח בקרה מוכן | פיתוח מאפס |
| מחיר ל-10,000 מנויים | חינם עד 10,000 | ~$50 לחודש לתשתית |
הגדרת SDK בצד הלקוח שלב אחר שלב
התקינו את הסקריפט ב-OnAfterUserAuthorize של התבנית או דרך מנהל תגים:
<!-- 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 שולח התראות על בסיס אירועי Bitrix:
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"] } בקבלת תגובה כזו, אנו מגדירים // При авторизации (событие OnAfterUserAuthorize) AddEventHandler('main', 'OnAfterUserAuthorize', function(array $fields) { if (empty($fields['USER_ID'])) return; // Ищем подписку по сессии, привязываем к пользователю LocalPushSubscriptionTable::update( ['=USER_ID' => false], // NULL ['USER_ID' => $fields['USER_ID']] ); }); ל-<head> עבור ההרשמות המתאימות.
נקודות אינטגרציה עם אירועי Bitrix וסגמנטציה
שינוי סטטוס הזמנה:
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 או פילטרי משתמשים.
OneSignal יכולה לסגמנט משתמשים לפי התנהגות: עגלה נטושה, קטגוריות שנצפו, סוג מכשיר. אנו מגדירים סגמנטים אוטומטיים דרך REST API באמצעות נתונים מ-Bitrix. לדוגמה, הסגמנט "קונים עם סכום הזמנה גבוה וללא רכישה ב-30 יום"—אנו שולחים להם הנחה אישית. היעילות של סגמנטים כאלה גבוהה פי 2–3 משליחות המוניות.
ניהול הרשמות מחשבון אישי
בעמוד "התראות", המשתמש בוחר סוגי push: סטטוס הזמנה, מבצעים, תזכורות. ההגדרות נשמרות בשדות משתמש (<!-- 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> , 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) ?? []; } } ). אנו בודקים את הדגל לפני שליחה.
מה כלול בעבודה ולוחות זמנים
- פריסת SDK בצד הלקוח (כפתור הרשמה מותאם אישית).
- יצירת טבלת הרשמות עם Bitrix ORM.
- קישור הרשמות למשתמשים (כולל התחברות).
- הגדרת שליחה לפי אירועים: הזמנות, עגלות נטושות, מבצעים.
- ממשק ניהול הרשמות בחשבון האישי.
- תיעוד והדרכה למפתחים שלכם.
- 14 ימי תמיכה לאחר מסירה.
עלות ההטמעה מתחילה מ-$1,500 ובדרך כלל מחזירה את עצמה תוך 2–3 חודשים.
| משימה | משך זמן |
|---|---|
| אינטגרציית SDK, שמירת subscription_id | 2–3 ימים |
| טבלת הרשמות, קישור למשתמשים | 1–2 ימים |
| שליחה לפי אירועי הזמנה | 2–3 ימים |
| עגלה נטושה, ניהול הרשמות מהחשבון האישי | 3–5 ימים |
| חבילה מלאה | 2–3 שבועות |
נעריך את הפרויקט שלכם תוך יום אחד. צרו קשר—נבחר את הפתרון האופטימלי כולל הכל. אנו מבטיחים יציבות ושקיפות. קבלו ייעוץ עכשיו—זה בחינם. הזמינו את ההטמעה והתחילו להרוויח רווח נוסף תוך שבוע בלבד.







