移动应用集成
Caputchin 通过一个托管的嵌入页在原生应用里运行。你把一个 WebView 指向那个页面,访客在它里面通过挑战,页面再把得到的令牌(以及任何错误)桥接回你的原生代码。验证本身和网页上的完全一样;只是宿主外壳不同。
1. 打开嵌入页
把一个 WebView 指向 https://caputchin.com/embed,至少带一个站点密钥:
https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory省略游戏参数即为朴素的复选框。一个 games=a,b 列表会逐会话随机挑一个。
查询参数
| 参数 | 必需 | 作用 |
|---|---|---|
sitekey | 是 | 你的公钥(cpt_pub_...)。缺失则渲染一个不带组件的错误外壳。 |
game | 否 | 单个应用市场游戏 id(owner/repo,或 owner/repo/leaf)。 |
games | 否 | 一个逗号分隔的列表;逐会话随机挑一个。 |
game-src | 否 | 你自己托管的游戏包的 URL,用于自托管游戏。 |
layout | 否 | inline、modal、fullscreen 或 auto。在嵌入上默认为 inline。 |
locale / skin / config | 否 | 转发给组件以挑选语言、皮肤或配置。 |
no-verify | 否 | 跑游戏但不带验证关卡(仅游戏,无令牌)。 |
postMessageTarget | 否 | 仅供浏览器 iframe 嵌入者(见下文)。在原生 WebView 里被忽略。 |
把 game / games / game-src 三者全都省略即为朴素的复选框。没有 mode 参数;组件自己驱动触发。
2. 把令牌桥接到你的代码
当访客通过时,嵌入页会把令牌通过与你宿主相匹配的通道推送出去。注册相匹配的处理器。
iOS(WKWebView,Swift)
令牌作为一个原始字符串到达一个名为 caputchin 的消息处理器:
class TokenHandler: NSObject, WKScriptMessageHandler {
func userContentController(_ controller: WKUserContentController,
didReceive message: WKScriptMessage) {
guard let token = message.body as? String else { return }
// send `token` to your backend
}
}
let config = WKWebViewConfiguration()
config.userContentController.add(TokenHandler(), name: "caputchin")
let webView = WKWebView(frame: .zero, configuration: config)
webView.load(URLRequest(url: URL(string: "https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory")!))Android(WebView,Kotlin)
令牌作为一个原始字符串到达一个名为 caputchin 的 @JavascriptInterface:
class TokenBridge {
@JavascriptInterface
fun onToken(token: String) {
// send `token` to your backend
}
}
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(TokenBridge(), "caputchin")
webView.loadUrl("https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory")React Native(react-native-webview)
嵌入会在单一的 React Native 通道上发出一个 JSON 字符串。解析它,并据 type 分支:
<WebView
source={{ uri: "https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory" }}
onMessage={(e) => {
const msg = JSON.parse(e.nativeEvent.data);
if (msg.type === "caputchin-token") {
// send msg.token to your backend
} else if (msg.type === "caputchin-error") {
// msg.code, msg.message, msg.originalCode (optional)
}
}}
/>浏览器 iframe(postMessage)
如果你把页面嵌入一个浏览器 <iframe> 而不是原生 WebView,那么父级转发 默认是关的,只有当你通过 ?postMessageTarget=<origin> 用一个精确的目标来源主动选用时才会触发(例如 postMessageTarget=https://app.example.com)。通配符(*)、一个路径、或任何无法解析成一个光秃秃的协议、主机和端口的东西都会被拒绝,于是这个一次性、绑定站点的令牌绝不会被广播给一个未经核验的父级。在父级上监听,并在信任消息之前检查 event.origin:
window.addEventListener("message", (event) => {
if (event.origin !== "https://embed-host.example.com") return;
const msg = event.data;
if (msg.type === "caputchin-token") {
// msg.token
} else if (msg.type === "caputchin-error") {
// msg.code, msg.message
}
});3. 在你的后端核验
这和网页上一样:带你的密钥把令牌 POST 到 /siteverify。见 服务端集成。
接收错误(可选)
每当组件遇到错误时,嵌入页会通过一组并行通道把它镜像出来:iOS 的 caputchinError 消息处理器、Android 的 caputchinError 接口(onError(json))、React Native 通道上的 { type: "caputchin-error", ... }、以及同一个设了关卡的 iframe 转发。载荷携带 { code, message, originalCode? }。iOS 和 Android 把它作为一个 JSON 字符串接收;React Native 和 iframe 转发接收带类型的信封。
这完全是可选的。一个不注册任何错误处理器的应用就只是收不到错误而已,而无论如何,嵌入页本来就在 WebView 里显示一个重试 UI。