访客端 WebView 自行接入
App 用自己的 WebView / WKWebView 打开访客页,不接入 Visitor SDK 时按本文对接。
优先使用 访客 SDK 接入文档。本文只覆盖「自己加载页面」。
打开页面
url 填 控制台配置的 URL 地址,必须 HTTPS。业务参数拼在 query 上,自行做 UTF-8 编码。不要把密码、Cookie、AppSecret 或长期 token 放进 URL。
App 内打开建议带 is_app=1,访客页才会显示关闭按钮。
控制台配置的 URL 地址?is_app=1&lang=zh-cn&theme=system
Android:
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.loadUrl(buildVisitorUrl())
iOS:
let config = WKWebViewConfiguration()
config.preferences.javaScriptEnabled = true
webView.load(URLRequest(url: visitorURL))
只加载 URL 即可聊天。关闭按钮、原生下载、原生铃声、Android 录音授权需要注入 AppBridge,见下文。
query 参数
| 参数 | 必填? | 含义 |
|---|---|---|
is_app | App 内建议 1 | 1 时显示关闭按钮,并走 App 返回逻辑 |
lang | 可选,默认页面语言 | 界面语言:en / zh-cn / zh-tw / ja / ko / de / fr / pt / ru / es / vi / th / id / ms / tl |
theme | 可选,默认 light | light / dark / system |
direct | 直达会话时必填 1 | 直达开关,没有它时 chatid 无效 |
chatid | 直达会话时必填 | 目标会话 ID(也认 chat_id) |
sbs | 绑用户时必填 | 业务系统里的用户唯一 ID |
sbs_mm | 有 sbs 就要 | sbs 的签名 |
ranstr | 有签名就要 | 参与签名的随机串 |
name | 可选 | 访客姓名 |
nickname | 可选 | 访客昵称 |
email | 可选 | 访客邮箱 |
phone | 可选 | 访客手机号 |
customer_remark | 可选 | 客户备注 |
ext_fk_id | 可选 | 已有访客 ID,用来续历史会话 |
referer | 可选 | 来源页 URL(也认 referer_url) |
source_title | 可选 | 来源页标题 |
new_message_sound_mode | 可选 | native:新消息由 App 响铃;不传则页面自己响 |
source | 可选 | webview 隐藏默认返回按钮 |
不要传 visitor_id。source=webview 效果等同 is_app=1,传一个即可。
WebView 要求
- 启用 JavaScript、DOM Storage
- Cookie / LocalStorage 默认持久化,不要无故清掉(清掉会丢访客身份)
- 仅加载 HTTPS;系统 ATS / Cleartext 保持默认
- 同域麦克风、摄像头:在系统里声明权限(Android
RECORD_AUDIO/CAMERA;iOSNSMicrophoneUsageDescription/NSCameraUsageDescription) - 附件选择走 WebView 文件选择器
- 同域 HTTPS 留在 WebView;跨域 HTTPS 用系统浏览器
AppBridge
页面通过 window.AppBridge.call(method, params, callback) 调 App。页面每次加载前注入下面脚本(建议 atDocumentStart,不要等 onPageFinished)。
(function () {
window.__TWT_VISITOR_BRIDGE_CONFIG__ = { newMessageSoundMode: 'web' };
window.AppBridge = {
_callbacks: {},
call: function (method, params, callback) {
var id = callback ? 'cb_' + Date.now() + '_' + Math.random().toString(16).slice(2) : null;
if (callback) this._callbacks[id] = callback;
var msg = JSON.stringify({ method: method, params: params || {}, callback: id });
if (window.AppBridgeChannel_native) {
window.AppBridgeChannel_native.postMessage(msg);
} else if (window.webkit && window.webkit.messageHandlers.AppBridgeChannel) {
window.webkit.messageHandlers.AppBridgeChannel.postMessage(msg);
}
return Promise.resolve({ status: 'sent', callbackId: id });
},
invokeCallback: function (id, result) {
var cb = this._callbacks[id];
if (cb) { cb(result); delete this._callbacks[id]; }
}
};
})();
Android 注册名为 AppBridgeChannel_native 的 JavascriptInterface(方法名 postMessage)。
iOS 注册名为 AppBridgeChannel 的 WKScriptMessageHandler。
消息 JSON:{ "method": "...", "params": {}, "callback": "cb_..." }。
页面会调用的方法
| method | 何时 | App 做什么 |
|---|---|---|
close | 用户点页面关闭按钮 | 关闭 WebView / 退出当前页 |
download | 保存图片、视频、文件 | 下载 params.url;完成后见下方回传 |
new_message | 收到新消息且铃声为 native | 播放提示音。params 可能含 messageId / chatId |
android_recording_permission | Android 录音 | 申请麦克风。回传 { code: 1, data: { permission: true/false } } |
visitor_back_result | 页面应答了返回 | 见「系统返回」 |
不注入 Bridge 时:关闭按钮无效果;下载走浏览器;铃声由页面自己播。
下载回传
download 的 params:
| 字段 | 含义 |
|---|---|
url | 文件地址 |
type | image / video / 其它 |
fileName | 建议文件名 |
mimeType | MIME |
requestId | 本次下载 ID |
有 callback 时,下载结束调用:
window.AppBridge.invokeCallback(callbackId, {
code: 1,
data: { requestId: '...', status: 'completed' }
})
status:completed / failed / cancelled。失败时 code 用 0。
若要把铃声交给 App,注入时把 newMessageSoundMode 设为 'native',或 URL 带 new_message_sound_mode=native。
系统返回(可选)
用户按系统返回时,先问页面(关闭预览、从会话退回列表):
window.dispatchEvent(new CustomEvent('twt:visitor-event', {
detail: { type: 'back', payload: {} }
}))
约 300ms 内页面会调 visitor_back_result,params.consumed === true 表示已处理。未处理再关闭 WebView。
运行中切主题(仅 light / dark):
window.dispatchEvent(new CustomEvent('twt:visitor-event', {
detail: { type: 'themeChanged', payload: { theme: 'dark' } }
}))