Ana içeriğe geç

Widget Entegrasyonu

Müşterilerinize gerçek zamanlı sohbet desteği sağlamak için ChatHub widget'ını web sitenize entegre edin.

Genel Bakış

ChatHub widget'ı aşağıdaki özelliklere sahip bir JavaScript bileşenidir:

  • Herhangi bir web sitesine gömülür
  • Gerçek zamanlı sohbet arayüzü sağlar
  • Müşterileri operatörlerle buluşturur
  • Operatör kimlik doğrulama jetonunu gerektirir
  • ES modülü olarak yüklenir

Hızlı Başlangıç

1. Operatör Jetonu Alın

İlk olarak, kimlik doğrulama akışını izleyerek bir operatör belirteci edinin:

// 1. Get company token
const companyToken = await getCompanyToken(login, password);

// 2. Get organization
const organizations = await getOrganizations(companyToken);
const orgId = organizations[0].id;

// 3. Get operator
const operators = await getOperators(companyToken, orgId);
const operatorId = operators[0].id;

// 4. Generate operator token
const operatorToken = await getOperatorToken(
  companyToken,
  operatorId,
  expiresAt
);

// 5. Validate token
const isValid = await validateToken(companyToken, operatorToken);

2. Yerleştirme Widget'ı

Widget komut dosyasını HTML'nize ekleyin:

<!DOCTYPE html>
<html>
<head>
    <title>Your Website</title>
</head>
<body>
    <!-- Your website content -->

    <!-- ChatHub Widget -->
    <script type="module" id="operator-chat-panel-script"
      src="https://widget.smsbat.com/operator-chat-panel/widget-script.js"
      token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."></script>
</body>
</html>

Komut Dosyası Parametreleri

Özellik Değer Gerekli Açıklama
'tür' 'modül' Evet ES modül türü
'kimlik' 'operatör-sohbet-paneli-komut dosyası' Evet Benzersiz komut dosyası tanımlayıcı
'kaynak' Widget URL'si Evet Widget komut dosyası konumu
'jeton' JWT jetonu Evet Operatör kimlik doğrulama jetonu

Entegrasyon Yöntemleri

Statik HTML

Statik web siteleri için doğrudan HTML'ye yerleştirin:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My Website</title>
</head>
<body>
    <h1>Welcome to My Website</h1>

    <!-- ChatHub Widget -->
    <script type="module" id="operator-chat-panel-script"
      src="https://widget.smsbat.com/operator-chat-panel/widget-script.js"
      token="YOUR_OPERATOR_TOKEN"></script>
</body>
</html>

Dinamik Enjeksiyon (JavaScript)

For single-page applications, inject dynamically:

function loadChatHubWidget(operatorToken) {
  // Check if widget already loaded
  const existing = document.getElementById('operator-chat-panel-script');
  if (existing) {
    existing.remove();
  }

  // Create script element
  const script = document.createElement('script');
  script.type = 'module';
  script.id = 'operator-chat-panel-script';
  script.src = 'https://widget.smsbat.com/operator-chat-panel/widget-script.js';
  script.setAttribute('token', operatorToken);

  // Append to body
  document.body.appendChild(script);
}

// Usage
const token = await getOperatorToken();
loadChatHubWidget(token);

Tepki ver

import { useEffect } from 'react';

function ChatHubWidget({ operatorToken }) {
  useEffect(() => {
    if (!operatorToken) return;

    // Load widget
    const script = document.createElement('script');
    script.type = 'module';
    script.id = 'operator-chat-panel-script';
    script.src = 'https://widget.smsbat.com/operator-chat-panel/widget-script.js';
    script.setAttribute('token', operatorToken);

    document.body.appendChild(script);

    // Cleanup on unmount
    return () => {
      const existing = document.getElementById('operator-chat-panel-script');
      if (existing) {
        existing.remove();
      }
    };
  }, [operatorToken]);

  return null;
}

// Usage
function App() {
  const [token, setToken] = useState('');

  useEffect(() => {
    async function init() {
      const operatorToken = await fetchOperatorToken();
      setToken(operatorToken);
    }
    init();
  }, []);

  return (
    <div>
      <h1>My App</h1>
      <ChatHubWidget operatorToken={token} />
    </div>
  );
}

Vue.js

<template>
  <div id="app">
    <h1>My App</h1>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      operatorToken: ''
    };
  },
  async mounted() {
    // Get operator token
    this.operatorToken = await this.fetchOperatorToken();

    // Load widget
    this.loadWidget();
  },
  methods: {
    async fetchOperatorToken() {
      // Your token fetching logic
      const response = await fetch('/api/chathub/token');
      return response.text();
    },
    loadWidget() {
      if (!this.operatorToken) return;

      const script = document.createElement('script');
      script.type = 'module';
      script.id = 'operator-chat-panel-script';
      script.src = 'https://widget.smsbat.com/operator-chat-panel/widget-script.js';
      script.setAttribute('token', this.operatorToken);

      document.body.appendChild(script);
    }
  },
  beforeUnmount() {
    const script = document.getElementById('operator-chat-panel-script');
    if (script) {
      script.remove();
    }
  }
};
</script>

Açısal

import { Component, OnInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-root',
  template: '<h1>My App</h1>'
})
export class AppComponent implements OnInit, OnDestroy {
  private operatorToken: string = '';

  async ngOnInit() {
    // Get operator token
    this.operatorToken = await this.fetchOperatorToken();

    // Load widget
    this.loadWidget();
  }

  ngOnDestroy() {
    const script = document.getElementById('operator-chat-panel-script');
    if (script) {
      script.remove();
    }
  }

  private async fetchOperatorToken(): Promise<string> {
    const response = await fetch('/api/chathub/token');
    return response.text();
  }

  private loadWidget() {
    if (!this.operatorToken) return;

    const script = document.createElement('script');
    script.type = 'module';
    script.id = 'operator-chat-panel-script';
    script.src = 'https://widget.smsbat.com/operator-chat-panel/widget-script.js';
    script.setAttribute('token', this.operatorToken);

    document.body.appendChild(script);
  }
}

Token Yönetimi

Sunucu Tarafı Token Üretimi

Şirket kimlik bilgilerini hiçbir zaman istemci tarafı kodunda açığa çıkarmayın. Sunucunuzda belirteçler oluşturun:

// Node.js Express example
const express = require('express');
const app = express();

app.get('/api/chathub/token', async (req, res) => {
  try {
    // Authenticate your user first
    const userId = req.session.userId;
    if (!userId) {
      return res.status(401).json({ error: 'Unauthorized' });
    }

    // Get company token (stored securely on server)
    const companyToken = process.env.CHATHUB_COMPANY_TOKEN;

    // Get operator ID for this user
    const operatorId = await getOperatorIdForUser(userId);

    // Generate operator token
    const operatorToken = await generateOperatorToken(
      companyToken,
      operatorId
    );

    res.json({ token: operatorToken });
  } catch (error) {
    res.status(500).json({ error: 'Failed to generate token' });
  }
});

async function generateOperatorToken(companyToken, operatorId) {
  const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours

  const response = await fetch(
    'https://chatapi.smsbat.com/api/operator/get-token',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${companyToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        id: operatorId,
        expiresAt: expiresAt.toISOString()
      })
    }
  );

  return response.text();
}

Jeton Yenileme

Otomatik belirteç yenilemeyi uygulayın:

class WidgetTokenManager {
  constructor() {
    this.token = null;
    this.expiresAt = null;
    this.refreshInterval = null;
  }

  async initialize() {
    await this.refreshToken();

    // Refresh token 1 hour before expiration
    this.refreshInterval = setInterval(
      () => this.checkAndRefresh(),
      60 * 60 * 1000 // Check every hour
    );
  }

  async refreshToken() {
    const response = await fetch('/api/chathub/token');
    const data = await response.json();

    this.token = data.token;
    this.expiresAt = new Date(data.expiresAt);

    this.reloadWidget();
  }

  async checkAndRefresh() {
    const oneHour = 60 * 60 * 1000;
    const timeUntilExpiry = this.expiresAt - Date.now();

    if (timeUntilExpiry < oneHour) {
      await this.refreshToken();
    }
  }

  reloadWidget() {
    // Remove old widget
    const existing = document.getElementById('operator-chat-panel-script');
    if (existing) {
      existing.remove();
    }

    // Load new widget with fresh token
    const script = document.createElement('script');
    script.type = 'module';
    script.id = 'operator-chat-panel-script';
    script.src = 'https://widget.smsbat.com/operator-chat-panel/widget-script.js';
    script.setAttribute('token', this.token);

    document.body.appendChild(script);
  }

  destroy() {
    if (this.refreshInterval) {
      clearInterval(this.refreshInterval);
    }
  }
}

// Usage
const widgetManager = new WidgetTokenManager();
await widgetManager.initialize();

Çoklu Organizasyonlar

Farklı kuruluşlar için farklı widget'lar yükleyin:

function loadWidgetForOrganization(organizationId) {
  return new Promise((resolve, reject) => {
    // Get operator for this organization
    fetch(`/api/chathub/token?org=${organizationId}`)
      .then(response => response.json())
      .then(data => {
        const script = document.createElement('script');
        script.type = 'module';
        script.id = `operator-chat-panel-script-${organizationId}`;
        script.src = 'https://widget.smsbat.com/operator-chat-panel/widget-script.js';
        script.setAttribute('token', data.token);

        script.onload = () => resolve();
        script.onerror = () => reject(new Error('Failed to load widget'));

        document.body.appendChild(script);
      })
      .catch(reject);
  });
}

// Usage
await loadWidgetForOrganization('sales');
await loadWidgetForOrganization('support');

En İyi Uygulamalar

Güvenlik

  • ✅ Sunucu tarafında jetonlar oluşturun
  • ✅ Hiçbir zaman müşteri kodunda şirket kimlik bilgilerini açığa çıkarmayın
  • ✅ Tüm API istekleri için HTTPS kullanın
  • ✅ Belirteç süresinin sona ermesini uygulayın
  • ✅ Kullanmadan önce belirteçleri doğrulayın
  • ❌ Jetonları localStorage'da şifreleme olmadan saklamayın
  • ❌ Belirteçleri sürüm kontrolüne teslim etmeyin

Performans

  • ✅ Widget'ı eşzamansız olarak yükleyin
  • ✅ ES modüllerini kullanın (modern tarayıcılar)
  • ✅ Belirteç önbelleğe almayı uygulayın
  • ✅ Hataları incelikle ele alın
  • ❌ Sayfa yüklenmesini engellemeyin

Kullanıcı Deneyimi

  • ✅ Widget başlatılırken yükleme durumunu göster
  • ✅ Ağ hatalarını yönetin
  • ✅ Geri dönüş iletişim yöntemi sağlayın
  • ✅ Farklı tarayıcılarda ve cihazlarda test edin

Hata İşleme

async function loadWidgetSafely(operatorToken) {
  try {
    // Validate token first
    const isValid = await validateToken(operatorToken);

    if (!isValid) {
      console.error('Invalid operator token');
      showFallbackContact();
      return;
    }

    // Load widget
    await loadWidget(operatorToken);

  } catch (error) {
    console.error('Failed to load chat widget:', error);
    showFallbackContact();
  }
}

function showFallbackContact() {
  // Show alternative contact method
  const fallback = document.createElement('div');
  fallback.innerHTML = `
    <div class="chat-fallback">
      <p>Chat is temporarily unavailable.</p>
      <p>Contact us: <a href="mailto:support@example.com">support@example.com</a></p>
    </div>
  `;
  document.body.appendChild(fallback);
}

Sorun Giderme

Widget Yüklenmiyor

  1. Operatör jetonunun geçerli olup olmadığını kontrol edin
  2. Belirtecin süresinin dolmadığını doğrulayın
  3. Komut dosyası URL'sinin doğru olduğundan emin olun
  4. Tarayıcı konsolunda hatalar olup olmadığını kontrol edin
  5. Ağ bağlantısını doğrulayın

Belirtecin Süresi Doldu

// Detect expired token and refresh
window.addEventListener('error', async (event) => {
  if (event.message.includes('token expired')) {
    console.log('Token expired, refreshing...');
    await refreshWidgetToken();
  }
});

Multiple Widget Instances

Aynı anda yalnızca bir widget'ın yüklendiğinden emin olun:

function loadWidgetOnce(token) {
  // Remove any existing widgets
  const existingScripts = document.querySelectorAll(
    'script[id^="operator-chat-panel-script"]'
  );

  existingScripts.forEach(script => script.remove());

  // Load new widget
  loadWidget(token);
}

Çapraz Kaynak Sorunları

Alan adınızın beyaz listeye alındığından emin olun. CORS hatalarıyla karşılaşırsanız desteğe başvurun.

Test etme

Yerel Kalkınma

// Use test token for development
const isDevelopment = process.env.NODE_ENV === 'development';
const token = isDevelopment
  ? 'test-token-for-development'
  : await getProductionToken();

loadWidget(token);

Entegrasyon Testi

describe('ChatHub Widget', () => {
  it('should load widget with valid token', async () => {
    const token = await getTestToken();
    loadWidget(token);

    await waitFor(() => {
      const widget = document.getElementById('operator-chat-panel-script');
      expect(widget).toBeTruthy();
    });
  });

  it('should handle invalid token', async () => {
    const invalidToken = 'invalid-token';

    try {
      await loadWidget(invalidToken);
    } catch (error) {
      expect(error.message).toContain('Invalid token');
    }
  });
});

Sonraki Adımlar