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/webimport 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çenek | Varsayılan | Açıklama |
|---|---|---|
appId | — | Zorunlu |
apiUrl | SDK'nın yüklendiği origin | API 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ı |
allowLocalhostAsSecureOrigin | false | HTTP localhost'ta çalışmaya izin ver (geliştirme) |
requiresPrivacyConsent | false | setConsentGiven(true) çağrılana kadar ağ çağrısı yok |
autoResubscribe | true | İzin verilmişse açılışta aboneliği otomatik oluştur/yenile |
autoPrompt | false | Açılışta tarayıcı native istemini aç (geri çekilme takvimine uyar) |
slidedown | kapalı | { enabled, collect?, delaySeconds, pageViews, retryDays, title, message, acceptText, cancelText, emailLabel?, smsLabel? }; collect: 'push' | 'email' | 'sms' | 'email_sms' |
welcomeNotification | açık | { disable?, title?, message?, url? } — ilk abonelikten sonra yerel karşılama bildirimi |
notificationClickHandlerMatch | exact | exact: aynı URL'deki sekme, origin: aynı origin'deki sekme |
notificationClickHandlerAction | navigate | Eş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) |
bell | kapalı | { enabled, position: 'bottom-right' | 'bottom-left', subscribedText, unsubscribedText } |
logLevel | warn | silent | error | warn | info | debug |
language | navigator.language | Slidedown/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.retryDayssabit 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) verequire_interactiondesteklenir; düğme tıklamasında düğmeninurl'si açılır veopenedkaydınabutton_ideklenir.
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_durationtürleri. Sıklık (remaining_impressions,min_interval_seconds,stop_after_interaction, app geneliin_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üzenlermodal,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ındavariant_idile).
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
| Belirti | Neden / çözüm |
|---|---|
permission: unsupported | HTTP origin (localhost dışı), eski Safari, gizli mod, iOS'ta ana ekrana eklenmemiş site. HTTPS kullanın. |
| SW kaydı 404 | PushizoSDKWorker.js site kökünde değil veya serviceWorkerPath yanlış. |
| Abonelik oluşmuyor | Dashboard → Kanallar → Web Push etkin değil (vapid_public_key GET /sdk/v1/apps/{appId}/config ile gelmeli). |
| Bildirim gelmiyor | Mesaj İzleme'de sağlayıcı yanıtı: 410 abonelik iptal; 401/403 VAPID anahtarı değişmiş → tarayıcı aboneliğini yenileyin. |
login 403 | Identity verification zorunlu; sunucudan identity_hash alın. |
| In-app görünmüyor | Abonelik yok, tetik sağlanmıyor, sıklık hakkı dolmuş ya da paused; logLevel: 'debug' ile günlüğe bakın. |
SDK'lara genel bakış
İstemci (Web, iOS, Android, Flutter ve köprüler) ve sunucu (Node, PHP, Python, Go, Java, .NET, Ruby) SDK'ları; ortak kavramlar ve uçtan uca doğrulama.
iOS SDK
Swift 5.9, iOS 15+; SPM ve CocoaPods. APNs push, Notification Service Extension ile zengin medya ve teslimat onayı, in-app, bildirim kutusu, tercihler.