Caputchin
Guias de integração

Integração com apps móveis

O Caputchin roda dentro de um app nativo por meio de uma página de embed hospedada. Você aponta um WebView para essa página, o visitante resolve o desafio dentro dela, e a página leva o token resultante (e qualquer erro) de volta ao seu código nativo. A verificação em si é idêntica à da web; só o shell anfitrião difere.

1. Abra a página de embed

Aponte um WebView para https://caputchin.com/embed com ao menos uma chave de site:

https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory

Omita os parâmetros de jogo para a checkbox simples. Uma lista games=a,b escolhe um ao acaso por sessão.

Parâmetros de query

ParâmetroObrigatórioEfeito
sitekeysimSua chave pública (cpt_pub_...). Ausente, renderiza um shell de erro sem widget.
gamenãoUm único id de jogo do marketplace (owner/repo, ou owner/repo/leaf).
gamesnãoUma lista separada por vírgulas; um é escolhido ao acaso por sessão.
game-srcnãoURL do seu próprio bundle de jogo hospedado, para um jogo auto-hospedado.
layoutnãoinline, modal, fullscreen ou auto. O padrão é inline no embed.
locale / skin / confignãoEncaminhados ao widget para escolher um idioma, skin ou configuração.
no-verifynãoRoda o jogo sem portão de verificação (só o jogo, sem token).
postMessageTargetnãoSó para quem embuta em iframe de navegador (veja abaixo). Ignorado em um WebView nativo.

Omita os três game / games / game-src para a checkbox simples. Não há parâmetro mode; o widget conduz o gatilho sozinho.

2. Leve o token até seu código

Quando o visitante passa, a página de embed empurra o token pelo canal que combina com seu anfitrião. Registre o handler correspondente.

iOS (WKWebView, Swift)

O token chega como uma string crua em um message handler chamado 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)

O token chega como uma string crua em um @JavascriptInterface chamado caputchin:

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)

O embed posta uma string JSON no canal único do React Native. Faça o parse e ramifique por 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 de navegador (postMessage)

Se você embuta a página em um <iframe> de navegador em vez de um WebView nativo, o repasse ao pai vem desligado por padrão e dispara só quando você opta por ele com uma origem-alvo exata via ?postMessageTarget=<origin> (por exemplo postMessageTarget=https://app.example.com). Um curinga (*), um caminho, ou qualquer coisa que não se reduza a um simples esquema, host e porta é rejeitado, de modo que o token de uso único, vinculado ao site, nunca é difundido para um pai não verificado. Escute no pai e cheque event.origin antes de confiar na mensagem:

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. Verifique no seu backend

Isto é o mesmo que na web: faça POST do token em /siteverify com seu segredo. Veja Integração no lado do servidor.

Receber erros (opcional)

Sempre que o widget bate em um erro, a página de embed o espelha por um conjunto paralelo de canais: o message handler caputchinError do iOS, a interface caputchinError do Android (onError(json)), o canal do React Native como { type: "caputchin-error", ... } e o mesmo repasse de iframe protegido. O payload carrega { code, message, originalCode? }. iOS e Android o recebem como string JSON; React Native e o repasse de iframe recebem o envelope tipado.

Isto é totalmente opcional. Um app que não registra nenhum handler de erro simplesmente não recebe erros, e a página de embed já mostra uma UI de retry dentro do WebView de qualquer forma.

Veja também

Nesta página