Tích hợp tiện ích
Tích hợp tiện ích ChatHub vào trang web của bạn để cung cấp hỗ trợ trò chuyện theo thời gian thực cho khách hàng của bạn.
Tổng quan
Tiện ích ChatHub là một thành phần JavaScript:
- Nhúng vào bất kỳ trang web nào
- Cung cấp giao diện trò chuyện thời gian thực
- Kết nối khách hàng với nhà điều hành
- Yêu cầu mã thông báo xác thực của nhà điều hành
- Tải dưới dạng mô-đun ES
Bắt đầu nhanh
1. Nhận Token của nhà điều hành
Trước tiên, hãy lấy mã thông báo của nhà điều hành theo luồng xác thực:
// 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. Nhúng tiện ích
Thêm tập lệnh widget vào HTML của bạn:
<!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>
Tham số tập lệnh
| Thuộc tính | Giá trị | Bắt buộc | Mô tả |
|---|---|---|---|
loại |
mô-đun |
Có | Loại mô-đun ES |
id |
điều hành-chat-panel-script |
Có | Mã định danh tập lệnh duy nhất |
src |
URL tiện ích | Có | Vị trí tập lệnh widget |
mã thông báo |
Mã thông báo JWT | Có | Mã thông báo xác thực nhà điều hành |
Phương pháp tích hợp
HTML tĩnh
Đối với các trang web tĩnh, nhúng trực tiếp vào HTML:
<!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>
Chèn động (JavaScript)
Đối với các ứng dụng một trang, hãy thêm động:
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);
Phản ứng
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>
Góc cạnh
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);
}
}
Quản lý mã thông báo
Tạo mã thông báo phía máy chủ
Không bao giờ tiết lộ thông tin xác thực của công ty trong mã phía máy khách. Tạo mã thông báo trên máy chủ của bạn:
// 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();
}
Làm mới mã thông báo
Triển khai làm mới mã thông báo tự động:
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();
Nhiều tổ chức
Tải các vật dụng khác nhau cho các tổ chức khác nhau:
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');
Các phương pháp hay nhất
Bảo mật
- ✅ Tạo token phía máy chủ
- ✅ Không bao giờ tiết lộ thông tin xác thực của công ty trong mã khách hàng
- ✅ Sử dụng HTTPS cho tất cả các yêu cầu API
- ✅ Triển khai hết hạn token
- ✅ Xác thực token trước khi sử dụng
- ❌ Không lưu trữ token trong localStorage mà không mã hóa
- ❌ Không cam kết kiểm soát phiên bản bằng mã thông báo
Hiệu suất
- ✅ Tải widget không đồng bộ
- ✅ Sử dụng module ES (trình duyệt hiện đại)
- ✅ Triển khai bộ nhớ đệm mã thông báo
- ✅ Xử lý lỗi một cách khéo léo
- ❌ Không chặn tải trang
Trải nghiệm người dùng
- ✅ Hiển thị trạng thái tải trong khi khởi chạy widget
- ✅ Xử lý lỗi mạng
- ✅ Cung cấp phương thức liên hệ dự phòng
- ✅ Kiểm tra trên các trình duyệt và thiết bị khác nhau
Xử lý lỗi
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);
}
Khắc phục sự cố
Tiện ích không tải
- Kiểm tra mã thông báo của nhà điều hành có hợp lệ không
- Xác minh mã thông báo chưa hết hạn
- Đảm bảo URL tập lệnh là chính xác
- Kiểm tra lỗi bảng điều khiển trình duyệt
- Xác minh kết nối mạng
Mã thông báo đã hết hạn
// Detect expired token and refresh
window.addEventListener('error', async (event) => {
if (event.message.includes('token expired')) {
console.log('Token expired, refreshing...');
await refreshWidgetToken();
}
});
Nhiều phiên bản widget
Đảm bảo chỉ tải một tiện ích tại một thời điểm:
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);
}
Vấn đề về nhiều nguồn gốc
Đảm bảo tên miền của bạn được đưa vào danh sách trắng. Liên hệ với bộ phận hỗ trợ nếu bạn gặp lỗi CORS.
Thử nghiệm
Phát triển địa phương
// Use test token for development
const isDevelopment = process.env.NODE_ENV === 'development';
const token = isDevelopment
? 'test-token-for-development'
: await getProductionToken();
loadWidget(token);
Kiểm thử tích hợp
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');
}
});
});