Pushizo Docs
SDK rehberleri

Web SDK

Tarayıcı push (Chrome, Edge, Firefox, Safari 16.4+), e-posta/SMS aboneliği, in-app mesajlar ve bildirim kutusu için @pushizo/web.

Web SDK'sı tarayıcıda çalışır: izin istemi, Web Push aboneliği (VAPID), kullanıcı/tag/alias yönetimi, e-posta/SMS toplama, olaylar, in-app mesajlar ve bildirim kutusu. Push için HTTPS zorunludur (localhost muaf). Paket @pushizo/web; CDN sürümü API sunucusundan /sdk-web/PushizoSDK.js olarak sunulur. Çatı rehberleri: React, Vue, Angular; iOS Safari PWA: iOS web push.

Kurulum

Script etiketi

<script src="https://api.example.com/sdk-web/PushizoSDK.js" async></script>
<script>
  window.PushizoDeferred = window.PushizoDeferred || [];
  PushizoDeferred.push(async (Pushizo) => {
    await Pushizo.init({ appId: 'app_…', apiUrl: 'https://api.example.com' });
  });
</script>

npm (ESM)

npm install @pushizo/web
import Pushizo from '@pushizo/web';
await Pushizo.init({ appId: 'app_…', apiUrl: 'https://api.example.com' });

SDK yalnızca tarayıcıda çalışır; SSR çatılarında istemci bileşeni/useEffect içinde başlatın.

Service worker

Sitenizin kökünde PushizoSDKWorker.js:

const apiUrl = new URL(self.location.href).searchParams.get('apiUrl') || 'https://api.example.com';
importScripts(`${apiUrl}/sdk-web/PushizoSDKWorker.js`);

Mevcut bir service worker'ınız varsa aynı importScripts satırını ona ekleyip serviceWorkerPath ile yolunu verin. Dosya kök dışındaysa sunucu Service-Worker-Allowed: / başlığı göndermelidir.

init seçenekleri

SeçenekVarsayılanAçıklama
appId—Zorunlu
apiUrlSDK'nın yüklendiği originAPI kökü; verilmezse (veya global kök verilirse) config api_url ile bölgesel köke geçilir, özel adres korunur; 421 HS_WRONG_CELL her durumda izlenir (bkz. SDK genel bakış → Bölgesel API adresi)
serviceWorkerPath / serviceWorkerScope/PushizoSDKWorker.js / /SW yolu ve kapsamı
allowLocalhostAsSecureOriginfalseHTTP localhost'ta çalışmaya izin ver (geliştirme)
requiresPrivacyConsentfalsesetConsentGiven(true) çağrılana kadar ağ çağrısı yok
autoResubscribetrueİzin verilmişse açılışta aboneliği otomatik oluştur/yenile
autoPromptfalseAçılışta tarayıcı native istemini aç (geri çekilme takvimine uyar)
slidedownkapalı{ enabled, collect?, delaySeconds, pageViews, retryDays, title, message, acceptText, cancelText, emailLabel?, smsLabel? }; collect: 'push' | 'email' | 'sms' | 'email_sms'
welcomeNotificationaçık{ disable?, title?, message?, url? } — ilk abonelikten sonra yerel karşılama bildirimi
notificationClickHandlerMatchexactexact: aynı URL'deki sekme, origin: aynı origin'deki sekme
notificationClickHandlerActionnavigateEşleşen sekmede navigate URL'ye gider, focus yalnızca öne getirir
promptOptions—{ customlink?, backoffDays? } — özel bağlantı istemi ve geri çekilme takvimi (varsayılan [3, 7, 30] gün)
bellkapalı{ enabled, position: 'bottom-right' | 'bottom-left', subscribedText, unsubscribedText }
logLevelwarnsilent | error | warn | info | debug
languagenavigator.languageSlidedown/bell metin dili ve device.locale

API

await Pushizo.setConsentGiven(true);
await Pushizo.login('user_42', identityHash?);        // anonim → kimlikli
await Pushizo.logout();

Pushizo.User.addTag('plan', 'pro');   Pushizo.User.addTags({ a: '1' });
Pushizo.User.removeTag('plan');       Pushizo.User.removeTags(['a', 'b']);
Pushizo.User.getTags();               Pushizo.User.setLanguage('tr');
Pushizo.User.setProperty('city', 'İzmir');
Pushizo.User.addAlias('crm_id', '…'); Pushizo.User.addAliases({ crm_id: '…' }); Pushizo.User.removeAlias('crm_id');
Pushizo.User.id; Pushizo.User.externalId;

await Pushizo.User.pushSubscription.optIn();   // gerekirse izin ister
await Pushizo.User.pushSubscription.optOut();
Pushizo.User.pushSubscription.id / optedIn / state;      // state: { id, token, optedIn, permission }
const off = Pushizo.User.pushSubscription.addObserver((state) => …); // off() dinleyiciyi kaldırır
Pushizo.User.pushSubscription.removeObserver(observer); // off() ile eşdeğer

Pushizo.Notifications.permission;               // 'default' | 'granted' | 'denied' | 'unsupported'
await Pushizo.Notifications.requestPermission(); // izin + abonelik → boolean
Pushizo.Notifications.addClickListener(({ notification, result }) => …); // result.url, result.actionId
Pushizo.Notifications.addForegroundLifecycleListener((e) => e.preventDefault()); // sayfa açıkken gösterme
Pushizo.Notifications.addForegroundLifecycleListener((e) => { e.preventDefault(); later(() => e.display()); }); // ertele, sonra göster
Pushizo.Notifications.addPermissionObserver(({ permission, state }) => …); // permission: boolean, state: 'granted' | …
Pushizo.Notifications.removePermissionObserver(observer);
await Pushizo.Notifications.clearAll();         // gösterilen bildirimleri kapatır

Pushizo.Session.sendEvent('purchase', { sku: 'A1' }, 49.9);
Pushizo.Debug.setLogLevel('debug');
Pushizo.pendingOperations;                      // kuyruktaki işlem sayısı

Tag/özellik/alias/olay çağrıları senkron döner ve çevrimdışı kuyruğa (localStorage, hs:<appId>) yazılır; ardışık tag işlemleri birleştirilir. Kullanıcı henüz yokken bu işlemler sırasıyla bekler ve addEmail/addSms gibi kullanıcıyı oluşturan işlemleri engellemez. requiresPrivacyConsent açıkken onay verilene kadar hiçbir ağ isteği yapılmaz; kuyruk işlemleri bekler ve setConsentGiven(true) ile gönderilir. Olaylar abonelik veya login gelene kadar kuyrukta kalır; token_hash (abonelik token özeti) ve varsa identity_hash ile gönderilir. Oturum: sayfa görünür olunca session_start, pagehide'da session_end (keepalive fetch).

İstemler: slidedown, bell, özel bağlantı

await Pushizo.init({
  appId: 'app_…',
  slidedown: { enabled: true, delaySeconds: 5, pageViews: 2 },
  bell: { enabled: true, position: 'bottom-right' },
  promptOptions: {
    backoffDays: [3, 7, 30],                 // [] → geri çekilme kapalı
    customlink: {
      enabled: true, style: 'button', size: 'medium',
      color: { button: '#e54b4d', text: '#ffffff' },
      text: { subscribe: 'Bildirimlere abone ol', unsubscribe: 'Abonelikten çık', explanation: 'Kampanyalardan ilk siz haberdar olun.' },
      unsubscribeEnabled: true,
    },
  },
});
await Pushizo.Slidedown.promptPush();               // takvime ve tetiklere uyar
await Pushizo.Slidedown.promptPush({ force: true }); // takvimi atlar (izin sorulmamışsa)
Pushizo.CustomLink.render();                         // sonradan eklenen `.pushizo-customlink-container` için
  • Dashboard'da yapılandırılan istemler (GET /sdk/v1/apps/{appId}/config → web.prompt) init seçenekleriyle birleşir; init değerleri önceliklidir.
  • Slidedown "Şimdi değil" ile kapatıldığında ret sayısı artar ve istem 1. retten sonra 3, 2. retten sonra 7, sonrakilerde 30 gün gösterilmez (slidedown.retryDays sabit bekleme olarak önceliklidir). Notifications.requestPermission() doğrudan çağrısı takvimden etkilenmez.
  • Karşılama bildirimi abonelik sunucuda ilk kez oluşturulduğunda yerel gösterilir; teslimat kaydı oluşturmaz.
  • Aksiyon düğmeleri (web_buttons, en fazla 2) ve require_interaction desteklenir; düğme tıklamasında düğmenin url'si açılır ve opened kaydına button_id eklenir.

E-posta ve SMS aboneliği

Pushizo.User.addEmail('Ayse.Kaya@Example.com');   // → ayse.kaya@example.com
Pushizo.User.addSms('0090 (532) 123-45-67');       // → +905321234567 (E.164)
Pushizo.User.removeEmail('ayse.kaya@example.com');
Pushizo.User.removeSms('+905321234567');

Geçersiz değerde ContactValidationError fırlatılır (error.channel: email | sms). Kayıt kuyruğa yazılır ve POST /sdk/v1/apps/{appId}/subscriptions ile gönderilir; login olunmuşsa external_id (+ identity_hash), değilse cihazın mevcut aboneliği user.subscription_id olarak eklenir ve adres aynı kullanıcıya bağlanır. Cihazda adresin yalnızca SHA-256 özeti ve abonelik kimliği saklanır. Push aboneliği olmayan ziyaretçide kullanıcı kimliği (tag, inbox, in-app için) e-posta/SMS aboneliğinden gelir.

E-posta/SMS toplama istemi: slidedown.collect: 'email' | 'sms' | 'email_sms' ya da Pushizo.Slidedown.promptEmail(), promptSms({ force: true }), promptEmailAndSms(). Kabulde adresler User.addEmail/addSms ile eklenir; istem bir daha gösterilmez.

Kullanıcı tercihleri (kategoriler)

const { preferences, categories } = await Pushizo.User.getPreferences();
// preferences[i] → { category, channel: null | 'web_push' | 'email' | 'sms' | …, optedIn, explicit, source, updatedAt }
// categories[i]  → { key, name: { tr, en }, description, channels: string[] | null, defaultOptedIn }
Pushizo.User.setPreferences([
  { category: 'promo', optedIn: false },                   // tüm kanallar
  { category: 'news', optedIn: true, channel: 'email' },   // yalnızca e-posta
]);
Pushizo.User.getCategories(); // config'ten gelen kategoriler (ağ çağrısı yok)

Abonelik (push, yoksa e-posta/SMS) ve sahiplik kanıtı gerekir; abonelik yoksa getPreferences() açıklayıcı hata fırlatır. explicit: false kayıtlar kategori varsayılanından türetilmiştir.

In-app mesajlar

Pushizo.InAppMessages.addTrigger('level', 5);           // trigger koşulları (eq|neq|exists|gt|lt)
Pushizo.InAppMessages.addTriggers({ plan: 'pro', vip: true });
Pushizo.InAppMessages.removeTrigger('vip');  Pushizo.InAppMessages.clearTriggers();  Pushizo.InAppMessages.getTriggers();
Pushizo.InAppMessages.setScreen('pricing');             // screen koşulları (SPA rota değişiminde çağırın)
Pushizo.Session.sendEvent('cart_viewed');               // event koşulları

const off = Pushizo.InAppMessages.addClickListener(({ message, result }) => { /* message.id, result.actionId, result.url */ });
Pushizo.InAppMessages.addLifecycleListener({ willDisplay: ({ message }) => …, didDisplay: …, willDismiss: …, didDismiss: … });
Pushizo.InAppMessages.paused = true;  // yeni mesaj gösterme (ör. ödeme adımında)
  • Liste oturum başında ve en fazla 5 dakikada bir GET /sdk/v1/apps/{appId}/in-app?subscription_id= ile yenilenir; abonelik (push ya da e-posta/SMS) yoksa in-app çalışmaz.
  • Tetik grupları arası VEYA, grup içi VE; session_start, event, screen, trigger, session_duration türleri. Sıklık (remaining_impressions, min_interval_seconds, stop_after_interaction, app geneli in_app.min_interval_seconds) yerelde de uygulanır; aynı anda tek mesaj, önceliği yüksek olan.
  • Çizim: #hs-in-app öğesinde açık Shadow DOM; düzenler modal, banner_top, banner_bottom, fullscreen; erişilebilir (role="dialog", odak yönetimi, Esc). Aksiyonlar: dismiss, url (yeni sekme), deeplink, tags, push_prompt, outcome.
  • Olaylar impression, click (+button_id), dismiss → POST /sdk/v1/apps/{appId}/in-app/{messageId}/events (A/B varyantında variant_id ile).

Bildirim kutusu

const page = await Pushizo.Inbox.list({ limit: 20 });     // yeniden eskiye
// page.items[i] → { id, messageId, title, body, url, imageUrl, data, read, readAt, archivedAt, expiresAt, createdAt }
// page.unreadCount, page.nextCursor, page.hasMore
const next = await Pushizo.Inbox.list({ cursor: page.nextCursor! });
await Pushizo.Inbox.markRead(item.id);       // güncel öğeyi döndürür
await Pushizo.Inbox.markAllRead();           // { updated, unreadCount }
await Pushizo.Inbox.archive(item.id);        // kutudan kaldırır
await Pushizo.Inbox.unreadCount();
const stop = Pushizo.Inbox.addObserver(({ type, itemId, unreadCount }) => updateBadge(unreadCount));

Kullanıcı ve ona ait bir abonelik gerekir; login(externalId, identityHash) ile verilen hash isteklere eklenir.

Boyut ve test

IIFE gzip ≈ 23 KB (bütçe 25 KB), service worker gzip ≈ 4 KB (bütçe 5 KB). Service worker gönderilemeyen received/opened geri bildirimlerini IndexedDB'de saklayıp sonraki push/tıklamada yeniden dener; tıklamada yalnızca http(s) veya aynı origin'e göreli adres açılır. Birim testler sdk/web/src/__tests__ (Vitest + happy-dom), E2E tests/e2e/web-sdk.spec.ts (Playwright). Elle doğrulama: examples/web-vanilla (node examples/web-vanilla/serve.mjs, http://localhost:5173), examples/web-nextjs (npm run dev --workspace @pushizo/example-web-nextjs, http://localhost:3200).

Sorun giderme

BelirtiNeden / çözüm
permission: unsupportedHTTP origin (localhost dışı), eski Safari, gizli mod, iOS'ta ana ekrana eklenmemiş site. HTTPS kullanın.
SW kaydı 404PushizoSDKWorker.js site kökünde değil veya serviceWorkerPath yanlış.
Abonelik oluşmuyorDashboard → Kanallar → Web Push etkin değil (vapid_public_key GET /sdk/v1/apps/{appId}/config ile gelmeli).
Bildirim gelmiyorMesaj İzleme'de sağlayıcı yanıtı: 410 abonelik iptal; 401/403 VAPID anahtarı değişmiş → tarayıcı aboneliğini yenileyin.
login 403Identity verification zorunlu; sunucudan identity_hash alın.
In-app görünmüyorAbonelik yok, tetik sağlanmıyor, sıklık hakkı dolmuş ya da paused; logLevel: 'debug' ile günlüğe bakın.

Bu sayfada