Ga naar inhoud

Widget-integratie

Integreer de ChatHub-widget in uw website om uw klanten realtime chatondersteuning te bieden.

Overzicht

De ChatHub-widget is een JavaScript-component die:

  • Kan in elke website worden ingesloten
  • Biedt realtime chatinterface
  • Verbindt klanten met operators
  • Vereist operatorauthenticatietoken
  • Laadt als ES-module

Snelle start

1. Operatortoken ophalen

Verkrijg eerst een operatortoken volgens de authenticatiestroom:

// 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. Widget insluiten

Voeg het widgetscript toe aan uw HTML:

<!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>

Scriptparameters

Kenmerk Waarde Vereist Beschrijving
type module Ja ES-moduletype
id operator-chat-paneel-script Ja Unieke script-ID
src Widget-URL Ja Widgetscriptlocatie
teken JWT-token Ja Operatorverificatietoken

Integratiemethoden

Statische HTML

Voor statische websites kunt u deze rechtstreeks in HTML insluiten:

<!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>

Dynamische injectie (JavaScript)

Voor toepassingen met één pagina kunt u dynamisch injecteren:

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);

Reageer

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>

Hoekig

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);
  }
}

Tokenbeheer

Tokengeneratie aan de serverzijde

Geef nooit bedrijfsreferenties weer in code aan de clientzijde. Genereer tokens op uw server:

// 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();
}

Token vernieuwen

Implementeer automatische tokenvernieuwing:

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();

Meerdere organisaties

Laad verschillende widgets voor verschillende organisaties:

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');

Beste praktijken

Beveiliging

  • ✅ Genereer tokens aan de serverzijde
  • ✅ Geef nooit bedrijfsgegevens weer in klantcode
  • ✅ Gebruik HTTPS voor alle API-verzoeken
  • ✅ Implementeer het verlopen van tokens
  • ✅ Valideer tokens voor gebruik
  • ❌ Bewaar tokens niet zonder codering in localStorage
  • ❌ Leg tokens niet vast in versiebeheer

Prestaties

  • ✅ Laad widget asynchroon
  • ✅ Gebruik ES-modules (moderne browsers)
  • ✅ Implementeer tokencaching
  • ✅ Ga netjes om met fouten
  • ❌ Blokkeer het laden van pagina's niet

Gebruikerservaring

  • ✅ Toon laadstatus terwijl widget wordt geïnitialiseerd
  • ✅ Netwerkfouten afhandelen
  • ✅ Zorg voor een terugvalcontactmethode
  • ✅ Test op verschillende browsers en apparaten

Foutafhandeling

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);
}

Problemen oplossen

Widget wordt niet geladen

  1. Controleer of de operatortoken geldig is
  2. Controleer of het token niet is verlopen
  3. Zorg ervoor dat de script-URL correct is
  4. Controleer de browserconsole op fouten
  5. Controleer de netwerkconnectiviteit

Token verlopen

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

Meerdere widgetinstanties

Zorg ervoor dat er slechts één widget tegelijk wordt geladen:

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);
}

Cross-Originele problemen

Zorg ervoor dat uw domein op de witte lijst staat. Neem contact op met de ondersteuning als u CORS-fouten tegenkomt.

Testen

Lokale ontwikkeling

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

loadWidget(token);

Integratietesten

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');
    }
  });
});

Volgende stappen