6.2 KiB
6.2 KiB
嵌入客服挂件
挂件会在任意网页上添加一个聊天入口:只需加载一个脚本,页面上显示悬浮按钮,点击后在 iframe 中打开客服聊天。访客可以同时保留多个请求并随时切换。
快速开始
将 https://support.example.com 替换为你的部署地址,将 YOUR_CHANNEL_ID 替换为控制台中的渠道 ID。
<script>
window.AgentDeskConfig = {
channelId: "YOUR_CHANNEL_ID",
}
</script>
<script src="https://support.example.com/sdk/agent-desk-sdk.min.js" async></script>
两段标签放在 </body> 之前,且 AgentDeskConfig 必须在 SDK 脚本之前执行。挂件会自动挂载,按钮出现在页面角落。
标题、副标题、主题色、位置和宽度来自控制台的渠道设置,通常无需在代码中配置。
配置项
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
channelId |
string |
– | 必填,挂件所属渠道。 |
baseUrl |
string |
脚本所在域名 | Agent Desk 服务地址。 |
apiBaseUrl |
string |
baseUrl |
API 与页面不同源时使用。 |
widgetBaseUrl |
string |
脚本所在域名 | 聊天页面的加载地址。 |
language |
"zh-CN" | "en-US" |
服务端设置 | 界面语言,任何 en* 取值都会选择英文。 |
position |
"left" | "right" |
渠道设置 | 按钮和窗口所在一侧。 |
themeColor |
string |
渠道设置 | 主题色,例如 #0f6cbd。 |
width |
string |
380px |
桌面端窗口宽度。 |
title、subtitle |
string |
渠道设置 | 窗口标题文字。 |
externalId |
string |
浏览器本地 ID | 你自己系统中的稳定用户 ID。 |
signExternalId |
(id) => {signature, issuedAt} |
– | 证明 externalId 真实有效,见已识别访客。 |
externalName |
string |
– | 显示名称,仅在首次创建访客时使用。 |
getUserToken |
() => string | Promise<string> |
– | 返回由你的系统签发的 JWT。 |
屏幕宽度不超过 480px 时,聊天窗口会全屏打开。
JavaScript API
AgentDeskWidget.open() // Promise<void>,打开聊天
AgentDeskWidget.close() // 最小化聊天
AgentDeskWidget.mount(cfg) // 使用显式配置挂载
AgentDeskWidget.destroy() // 移除按钮和聊天窗口
AgentDeskWidget.getChatUrl() // Promise<string>,用于独立页面或新标签页
从自己的按钮打开聊天:
<button onclick="AgentDeskWidget.open()">联系客服</button>
React 与 Next.js
只在客户端加载一次脚本。Next.js(App Router)示例:
"use client"
import Script from "next/script"
export function SupportWidget({ channelId }: { channelId: string }) {
return (
<>
<Script id="agent-desk-config" strategy="afterInteractive">
{`window.AgentDeskConfig = ${JSON.stringify({ channelId })}`}
</Script>
<Script
src="https://support.example.com/sdk/agent-desk-sdk.min.js"
strategy="afterInteractive"
/>
</>
)
}
包含函数的配置(signExternalId、getUserToken)无法序列化,请手动挂载:
useEffect(() => {
const script = document.createElement("script")
script.src = "https://support.example.com/sdk/agent-desk-sdk.min.js"
script.onload = () =>
window.AgentDeskWidget?.mount({
channelId,
externalId: user.id,
signExternalId: (id) => fetch(`/api/support/sign?id=${id}`).then((r) => r.json()),
})
document.body.appendChild(script)
return () => window.AgentDeskWidget?.destroy()
}, [channelId, user.id])
已识别访客
默认情况下访客是由浏览器本地 ID 标识的匿名游客。若要把聊天关联到你自己的用户,需要同时传入 externalId 和签名,防止他人冒充:
signature = hex(HMAC-SHA256(identitySecret, channelId + "\n" + externalId + "\n" + issuedAt))
issuedAt 为当前 Unix 时间(秒)。服务端只接受未超过 identity.maxAgeMinutes 的签名,因此每次打开聊天时挂件都会重新获取签名。
Important
签名必须在你的服务端计算,身份密钥绝不能下发到浏览器。
Node.js 接口:
import crypto from "node:crypto"
app.get("/api/support/sign", requireLogin, (req, res) => {
const issuedAt = Math.floor(Date.now() / 1000)
const payload = `${CHANNEL_ID}\n${req.user.id}\n${issuedAt}`
const signature = crypto
.createHmac("sha256", process.env.AGENT_DESK_IDENTITY_SECRET)
.update(payload)
.digest("hex")
res.json({ signature, issuedAt })
})
Go:
func sign(secret, channelID, externalID string) (string, int64) {
issuedAt := time.Now().Unix()
mac := hmac.New(sha256.New, []byte(secret))
fmt.Fprintf(mac, "%s\n%s\n%d", channelID, externalID, issuedAt)
return hex.EncodeToString(mac.Sum(nil)), issuedAt
}
注意:接口签名的 externalId 应取自已登录用户,而不是查询参数。签名缺失或无效不会导致挂件失效,访客只会保持游客身份。
内容安全策略(CSP)与跨域
如果站点设置了 CSP,需要放行 Agent Desk 的域名:
script-src https://support.example.com;
frame-src https://support.example.com;
connect-src https://support.example.com;
挂件与 API 不同源时,请把你的站点加入部署的允许来源,否则配置请求会失败。
常见问题
| 现象 | 原因与解决 |
|---|---|
| 没有出现按钮 | 缺少 channelId,或 AgentDeskConfig 定义在 SDK 脚本之后。查看控制台是否有 channelId is required。 |
| 按钮出现但聊天空白 | iframe 被浏览器拦截。检查 CSP 的 frame-src,以及代理是否为 Agent Desk 域名设置了 X-Frame-Options。 |
| 访客始终显示为游客 | 签名缺失、已过期,或使用了错误的密钥或拼接顺序。请对照 identity.maxAgeMinutes 检查服务器时钟偏差。 |
| 语言不对 | 显式设置 language,否则使用服务端默认语言。 |
| 单页应用中挂件重复 | 组件卸载时调用 AgentDeskWidget.destroy()。 |
| 聊天显示“出了点问题” | 点击重新加载聊天。若仍然出现,请打开控制台并反馈 Support chat crashed 错误。 |