App 客服页
在你的 iOS、Android、React Native 或 Flutter App 里用 WebView 打开同一个 AI 客服窗口。
App 里不用装 SDK:用 WebView 打开一个网页,就是和网站同一个聊天窗口,主题、知识库、收件箱都一样。控制台「接入」页的「5. 装到你的 App(H5 客服页)」里有你的完整链接。
链接格式
https://askpurser.com/c/<公开密钥>?vid=<访客 ID>&lang=zh-CN#token=<身份令牌>| 部分 | 说明 |
|---|---|
<公开密钥> | 工作区的公开密钥,以 pk_ 开头。密钥不存在时页面返回 404 |
vid | 访客 ID。App 自己生成一个 UUID 存在本地,每次打开都用同一个。不传的话页面会在 WebView 里自己生成一个,但那样你就没法为它签身份令牌 |
lang | 窗口界面语言,例如 zh-CN、ja。不传就按 WebView 的系统语言 |
#token= | 已登录顾客的身份令牌。没登录就不带,是匿名对话 |
close=1 | 可选。App 没装下面任何一种原生桥时,加上它会显示关闭按钮,点了返回上一页 |
App 客服页不受「允许的网站」限制,正式工作区不填也能用。
已登录的顾客
- 顾客在 App 里打开客服时,App 向你的服务器要一个身份令牌,并把
vid一起传过去。 - 你的服务器用工作区的身份签名密钥签令牌,
visitorId填这个vid。签法和网站完全一样,见 身份令牌。 - App 把令牌放在链接的
#token=后面打开页面。
# 后面的部分不会发给任何服务器,页面读到后会立刻从地址栏里擦掉。令牌只能用一次,最长 10 分钟有效(SDK 默认签 5 分钟),所以每次打开页面都要新签一个。令牌无效时对话按匿名处理,不会报错;没有令牌就是一段新的匿名对话,不会接着上一位顾客的对话。
也可以不放在链接里,而是在页面脚本运行前(例如 iOS 的 WKUserScript 在 document start 注入)设置 window.__PURSER_IDENTITY__ = "<令牌>"。
在浏览器里测试
只改 # 后面部分的跳转不会重新加载页面。测试「换一个令牌」时,先跳到别的页面再打开新链接。
关闭页面
窗口右上角的关闭按钮通过你在 App 里装的原生桥通知 App。页面按下面的顺序找桥,找到第一个就用它:
| 平台 | 桥 | 页面发出的内容 |
|---|---|---|
| iOS(WKWebView) | webkit.messageHandlers.purser | { type: "close" } |
| Android(WebView) | PurserAndroid.close() | 直接调用 close() |
| React Native(react-native-webview) | ReactNativeWebView.postMessage | 字符串 {"type":"purser:close"} |
| Flutter(webview_flutter) | JavaScript 通道 Purser | 字符串 close |
| Flutter(flutter_inappwebview) | callHandler("purser", "close") | 参数 close |
页面加载时检查有没有桥:桥要在加载页面之前装好。一个都没有时不显示关闭按钮(除非链接带了 close=1)。
iOS(WKWebView)
import UIKit
import WebKit
final class SupportViewController: UIViewController, WKScriptMessageHandler {
var chatURL: URL! // https://askpurser.com/c/pk_…?vid=…&lang=…#token=…
override func viewDidLoad() {
super.viewDidLoad()
let config = WKWebViewConfiguration()
// 在加载页面之前注册,页面才会显示关闭按钮
config.userContentController.add(self, name: "purser")
let webView = WKWebView(frame: view.bounds, configuration: config)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(webView)
webView.load(URLRequest(url: chatURL))
}
func userContentController(_ controller: WKUserContentController,
didReceive message: WKScriptMessage) {
if let body = message.body as? [String: Any], body["type"] as? String == "close" {
dismiss(animated: true)
}
}
}iOS 上顾客可以直接在窗口里选照片发送。
Android(WebView)
class SupportActivity : AppCompatActivity() {
private var fileCallback: ValueCallback<Array<Uri>>? = null
private val pickImage =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
fileCallback?.onReceiveValue(uri?.let { arrayOf(it) } ?: emptyArray())
fileCallback = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val webView = WebView(this)
setContentView(webView)
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
// 在 loadUrl 之前注入
webView.addJavascriptInterface(object {
@JavascriptInterface
fun close() = runOnUiThread { finish() }
}, "PurserAndroid")
// 让顾客能发照片:WebView 默认不弹文件选择器
webView.webChromeClient = object : WebChromeClient() {
override fun onShowFileChooser(
view: WebView,
callback: ValueCallback<Array<Uri>>,
params: FileChooserParams,
): Boolean {
fileCallback = callback
pickImage.launch("image/*")
return true
}
}
webView.loadUrl(intent.getStringExtra("chatUrl")!!)
}
}Android WebView 不实现 onShowFileChooser 的话,窗口里的「添加照片」没有反应。
React Native(react-native-webview)
import { WebView } from "react-native-webview";
export function SupportScreen({ navigation, chatUrl }) {
return (
<WebView
source={{ uri: chatUrl }}
// 设置了 onMessage,页面里才有 ReactNativeWebView 桥
onMessage={(e) => {
try {
if (JSON.parse(e.nativeEvent.data).type === "purser:close") navigation.goBack();
} catch {}
}}
/>
);
}Flutter(webview_flutter)
final controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel('Purser', onMessageReceived: (m) {
if (m.message == 'close') Navigator.pop(context);
})
..loadRequest(Uri.parse(chatUrl));用 flutter_inappwebview 的话,注册一个名为 purser 的 JavaScript handler,收到参数 close 时关闭页面。
和网站一样的部分
- 窗口顶部有 AI 身份告知的开场白,AI 的回复标「AI 助手」,客服的回复标「人工客服」。
- 界面 12 种语言,AI 用顾客最后一条消息的语言回答。
- 测试工作区里标题旁显示「测试模式」。
- 页面会避开刘海和底部横条(safe area),不需要你自己留边距。
详见 网站聊天窗口。