Mobil: webview ve Flutter
Mobil uygulamada oturumu taşıyan güvenli webview: köprü sözleşmesi, oturum devri, yerel çekirdek ve uygulama anahtarı, derin bağlantı, güvenlik kuralları, clickfix_webview Flutter paketi.
Mobil uygulamalar için yol, ayrı bir yerel SDK değil, oturumu taşıyan güvenli bir webview'dir: uygulama https://getclickfix.com/widget-embed.html?widget=WIDGET_ID sayfasını bir webview'de açar; widget tam ekran açılır ve uygulamayla köprü üzerinden konuşur. Böylece Flutter, React Native, iOS, Android ve diğer her platform aynı yolla, aynı sözleşmeyle bağlanır. Köprü sözleşmesi (bridge_version 1: komutlar, mesajlar, hata kodları, güvenlik kuralları) bu sayfanın Köprü sözleşmesi bölümündedir; Flutter için hazır paket clickfix_webview, diğer platformlar için tam uygulamalar clickfix-examples deposundadır (react-native-webview, flutter-webview).
Oturum devri
- Uygulama, kendi sunucunuzdan
{ user_id, user_hash, company_id }alır (kimlik doğrulama);user_hashkullanıcı ve firma kimliğini birlikte imzalar, anahtar uygulamaya hiç inmez. - Webview
widget-embed.html?widget=WIDGET_ID&locale=tr&theme=autoadresini açar. Widget hazır olunca köprüden{"type":"clickfix:ready", ...}gelir. - Uygulama
{"type":"clickfix:identify","id":"1","user":{"user_id":"1001","user_hash":"…","name":"…","company":{"id":"F-77"}}}gönderir;company.idimzadaki firma kimliğidir (köprü sözleşmesi 1.2,bridge_version1). Cevap{"type":"clickfix:result","id":"1","ok":true}olur. Reddedilirseok:falseveerror.code(WIDGET_IDENTITY_INVALIDgibi) döner; kullanıcı anonim kalır. - Çıkışta
{"type":"clickfix:reset"}gönderin; ziyaretçi unutulur, sonraki açılış yeni ziyaretçidir.
Webview'in çerez ve depolaması oturumu sayfa yenilemeleri arasında taşır; reset göndermeden hesap değiştirmeyin.
Yerel çekirdekle oturum devri (CF-201). Uygulama widget uçlarını webview olmadan da çağırabilir: Web widget → Gelişmiş → Mobil uygulamalar bölümünde platform ve paket adıyla bir uygulama anahtarı oluşturun; @getclickfix/visitor-core paketi mode: "app" ile bu anahtarı X-ClickFix-App-Key ve X-ClickFix-App-Package başlıklarında gönderir, sayfa kaynağı gerekmez. Yerel oturumu webview'e {"type":"clickfix:session","token":client.getToken()} komutuyla devredin; widget de ready, identify ve reset sonrasında clickfix:session mesajıyla kendi belirtecini verir (client.adoptToken). Anahtar yalnızca oluşturulduğunda gösterilir ve panelden iptal edilir; user_hash doğrulaması tarayıcıdakiyle aynıdır.
Köprü: uygulamadan widget'a ve widget'tan uygulamaya
Uygulama → widget (komutlar): clickfix:identify, clickfix:track (uygulama olayını ekran bağlamına ekler), clickfix:open (view ya da conversation), clickfix:close, clickfix:update (dil, tema, profil, öznitelik), clickfix:consent, clickfix:reset, clickfix:get-state. Her komut isteğe bağlı bir id taşır; id varsa widget clickfix:result ile cevap verir. Komut window.ClickFixBridge.receive(json) çağrısıyla ya da sayfaya postMessage ile ulaşır.
Widget → uygulama: clickfix:ready, clickfix:event (her SDK olayı: message, unread, close, conversation:ended…), clickfix:link (dışarı açılacak bağlantı; sistem tarayıcısında açın) ve clickfix:result. Taşıma, platforma göre widget'ın kendisi tarafından seçilir: ReactNativeWebView.postMessage, iOS webkit.messageHandlers.ClickFix, Android ClickFixAndroid arayüzü, Flutter ClickFixFlutter kanalı.
Aşağıdaki uygulama tarafı örnekleri köprüyü kurar, hazır olunca oturumu devreder, uygulama olayı gönderir ve widget'ın close olayında destek ekranını kapatır. Bunlar kılavuzun otomatik testine girmez; kendi projenizde derleyip doğrulayın.
// Paket: clickfix_webview (sdks/flutter/clickfix_webview; ilk sürüme kadar git bağımlılığı). Köprüyü, kaynak
// kilidini ve ClickFixFlutter kanalını paket kurar; siz oturumu devredersiniz.
import 'package:clickfix_webview/clickfix_webview.dart';
import 'package:url_launcher/url_launcher.dart';
final controller = ClickFixController(
widgetId: 'WIDGET_ID',
locale: 'tr',
conversation: conversationUuid, // isteğe bağlı derin bağlantı
);
ClickFixWebView(
controller: controller,
onReady: (_) async {
final s = await api.supportSession(); // { user_id, user_hash } sunucunuzdan
try {
await controller.identify(ClickFixIdentity(
userId: s.userId, userHash: s.userHash, company: ClickFixCompany(id: s.companyId)));
await controller.track('app_screen', properties: {'screen': 'orders'});
} on ClickFixBridgeError catch (e) {
debugPrint('kimlik reddedildi: ${e.code}'); // WIDGET_IDENTITY_INVALID gibi; ziyaretçi anonim kalır
}
},
onUnread: (count) => badge.value = count,
onLink: (link) => launchUrl(link.url, mode: LaunchMode.externalApplication), // yalnız https gelir
onClose: () => Navigator.of(context).pop(),
);
// Çıkışta: controller.reset(); ekran kapanınca: controller.dispose().Derin bağlantı
- Belirli bir konuşmayı açmak: sayfayı
…/widget-embed.html?widget=WIDGET_ID&conversation=KONUSMA_UUIDile açın ya da açık webview'e{"type":"clickfix:open","conversation":"KONUSMA_UUID"}gönderin. Sekme içinview(home,messages,help,new). - Bildirimden konuşmaya: uygulamanızın kendi anlık bildirimi için
message.sentwebhook olayına abone olun; olayconversation_uuidvecontact_external_idtaşır. Bildirimin derin bağlantısı (uygulamam://destek?conversation=…) açılınca webview'i yukarıdaki adresle açın. Okunmamış rozeti için widget'ınunreadolayı (clickfix:event) yeterlidir. - Widget'tan dışarı: yardım makaleleri ve mesajlardaki bağlantılar
clickfix:linkolarak gelir; sistem tarayıcısında açın. Webview içindegetclickfix.comdışına gezinmeye izin vermeyin.
Güvenlik notları
- Kimlik anahtarı uygulamaya girmez.
user_hashsunucunuzdan, kimliği doğrulanmış bir uçtan gelir; uygulamada hesaplanmaz, sabitlenmez. - Kaynak kısıtı: webview yalnız
https://getclickfix.com(ya da size verilen uygulama adresi) yükler; başka adrese gezinme engellenir,clickfix:linkile gelen adres uygulamada yeniden doğrulanıp (yalnızhttps:) sistem tarayıcısında açılır, intent ya da derin bağlantı işleyicisine verilmez. Üçüncü taraf çerez ve paylaşılan çerez depolarını kapatın; dosya ve içerik erişimini (allowFileAccess,allowContentAccess) kapatın. - JS köprüsü:
window.ClickFixBridge.receiveyalnız bu sayfada vardır; sayfa ClickFix'e aittir. Köprü,postMessageile gelen iletileri çerçeve içindeyken yalnız yapılandırılmış üst kaynaktan (boot({ bridge: { origin } })), çerçeve dışındayken yalnız sayfanın kendisinden kabul eder;"*"kabul edilmez. Reddedilen komutlarda ileti içeriği (örneğinuser_hash) ne konsola ne sonuca yazılır. - Çerez ve depolama: oturum webview'in depolamasında kalır; hesap değişiminde ve çıkışta
clickfix:resetgönderin. Cihaz paylaşımlıysa webview depolamasını da temizleyin. - Onay: uygulamanız onay topluyorsa adrese
consent=1ekleyin ya daclickfix:consentgönderin; onaysız hiçbir şey saklanmaz.
Köprü sözleşmesi
Her komut bir JSON nesnesidir; type zorunlu, id isteğe bağlıdır (≤128 karakter). id verilirse widget clickfix:result ile cevap verir. Mesaj metni 64 KB'ı aşamaz. Sözleşmede yalnız geriye uyumlu değişiklik yapılır; uygulama tanımadığı alan ve olayları yok saymalıdır.
Uygulama → widget (komutlar)
type | Alanlar | Widget ne yapar |
|---|---|---|
clickfix:identify | user: { user_id, user_hash, name?, email?, phone?, company?: { id?, name?, domain? }, attributes? } | identify; user_hash sunucunuzda hesaplanır, company.id imzaya girerse firma doğrulanmış bağlanır |
clickfix:track | name (≤100), properties? | Uygulama olayını ekran bağlamına ekler |
clickfix:open | view? (home/messages/help/new), conversation? (UUID) | Konuşmayı ya da sekmeyi açar |
clickfix:close | – | Paneli kapatır |
clickfix:update | update (locale, theme, name, email, phone, öznitelikler) | Profil ve görünümü günceller |
clickfix:consent | granted (boolean) | Onayı iletir; false saklanan her şeyi siler |
clickfix:reset | – | Ziyaretçiyi unutur (çıkış, hesap değişimi) |
clickfix:get-state | – | result.data içinde ClickFixState döner (open, view, unread, verified, visitor_id…) |
clickfix:session | token (≤2048) | Yerel çekirdeğin oturumunu devralır |
Widget → uygulama (mesajlar)
type | Alanlar | Ne zaman |
|---|---|---|
clickfix:ready | bridge_version: 1, protocol_version, sdk_version, widget_id, visitor_id | Widget çizildiğinde; identify bundan sonra gönderilir (önce gönderilirse kuyruklanır) |
clickfix:event | event, payload | Her SDK olayında (ready, open, close, message, unread, identify, csat, error…) |
clickfix:link | url | Dışa açılacak bağlantı; uygulama https: olduğunu doğrulayıp sistem tarayıcısında açar |
clickfix:result | id, ok, data?, error?: { code, message } | id taşıyan her komuttan sonra |
clickfix:session | token, visitor_id | ready, identify ve reset sonrasında; yerel çekirdeğe adoptToken ile verilir |
Hata kodları (result.error.code): INVALID_JSON, INVALID_MESSAGE, UNKNOWN_COMMAND, TOO_LARGE, komut çalışırken oluşan SDK hataları (WIDGET_IDENTITY_DISABLED, WIDGET_IDENTITY_INVALID, RATE_LIMITED, NETWORK_ERROR…) ve bilinmeyen hatalar için COMMAND_FAILED. error.message yalnız kodu tekrarlar; mesaj içeriği hiçbir zaman sonuca, konsola ya da error olayına yazılmaz.
Taşıma yolu platforma göre widget tarafından seçilir: React Native window.ReactNativeWebView.postMessage, iOS window.webkit.messageHandlers.ClickFix, Android window.ClickFixAndroid (@JavascriptInterface), Flutter window.ClickFixFlutter (JavaScriptChannel), iframe içinde window.parent.postMessage (yalnız yapılandırılmış origin). Uygulama → widget yönünde komut window.ClickFixBridge.receive(json) ile (tercih edilen) ya da sayfaya postMessage ile verilir.
Flutter paketi: clickfix_webview
clickfix_webview (Flutter 3.38+, Dart 3.10+; webview_flutter üzerinde, Android minSdk 24, iOS 12+) köprü sözleşmesini ClickFixController + ClickFixWebView olarak uygular: kaynak kilidi, ready öncesi komut kuyruğu, derin bağlantı, clickfix:link doğrulaması. Yukarıdaki Flutter sekmesi bu paketi kullanır; çalışan uygulama clickfix-examples/flutter-webview klasöründedir.
flutter pub add clickfix_webview| Yöntem | Köprü komutu |
|---|---|
identify(ClickFixIdentity) | clickfix:identify |
track(name, properties:) | clickfix:track |
open(view:, conversation:) | clickfix:open |
close() | clickfix:close |
update({...}) | clickfix:update |
setConsent(bool) | clickfix:consent |
reset() | clickfix:reset |
getState() | clickfix:get-state → ClickFixState |
send(ClickFixCommand) | herhangi bir komut, ham |
Widget → uygulama mesajları onUnread, onLink, onClose gibi geri çağrılar ve controller.messages, events, links, results akışları olarak gelir; hatalar ClickFixBridgeError (ClickFixErrorCode) olarak fırlatılır. Yerel Dart istemcisi ve diğer yerel SDK'lar ayrı işlerdir; çıktıklarında aynı köprü sözleşmesini kullanacaklardır.
Son güncelleme
Webhook'lar
Olaylara imzalı POST ile abone olun: abonelik açma, imza doğrulama, tekilleştirme ve tekrar denemeler; widget, Node.js, Go, PHP ve curl örnekleri.
SDK'lar
Node.js, visitor-core, React, Go, PHP, Laravel ve Flutter paketleri: kurulum, en küçük örnek, örnek uygulama; istemci oluşturma, sayfalama, webhook imzası ve SDK'sız istekler.