Caputchin
Guías de integración

Integración en apps móviles

Caputchin corre dentro de una app nativa a través de una página de incrustación alojada. Apuntas un WebView a esa página, el visitante supera el reto dentro de ella, y la página puentea el token resultante (y cualquier error) de vuelta a tu código nativo. La verificación en sí es idéntica a la web; solo difiere el shell anfitrión.

1. Abre la página de incrustación

Apunta un WebView a https://caputchin.com/embed con al menos una clave de sitio:

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

Omite los parámetros de juego para la checkbox simple. Una lista games=a,b elige uno al azar por sesión.

Parámetros de query

ParámetroObligatorioEfecto
sitekeyTu clave pública (cpt_pub_...). Si falta, renderiza un shell de error sin widget.
gamenoUn id de juego del marketplace (owner/repo, o owner/repo/leaf).
gamesnoUna lista separada por comas; uno se elige al azar por sesión.
game-srcnoURL de tu propio bundle de juego alojado, para un juego autoalojado.
layoutnoinline, modal, fullscreen o auto. Por defecto inline en la incrustación.
locale / skin / confignoSe reenvían al widget para elegir idioma, skin o configuración.
no-verifynoCorre el juego sin gate de verificación (solo juego, sin token).
postMessageTargetnoSolo para incrustadores en iframe de navegador (ver abajo). Se ignora en un WebView nativo.

Omite los tres game / games / game-src para la checkbox simple. No hay un parámetro mode; el widget conduce el disparador por sí mismo.

2. Puentea el token a tu código

Cuando el visitante pasa, la página de incrustación empuja el token por el canal que corresponde a tu anfitrión. Registra el handler que corresponda.

iOS (WKWebView, Swift)

El token llega como una cadena en bruto en un message handler llamado 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)

El token llega como una cadena en bruto en un @JavascriptInterface llamado 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)

La incrustación postea una cadena JSON por el único canal de React Native. Parséala y ramifica según 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)

Si incrustas la página en un <iframe> de navegador en lugar de un WebView nativo, el relay al padre está desactivado por defecto y se dispara solo cuando lo activas con un origin objetivo exacto vía ?postMessageTarget=<origin> (por ejemplo postMessageTarget=https://app.example.com). Un comodín (*), una ruta, o cualquier cosa que no parsee a un esquema, host y puerto a secas se rechaza, así que el token de un solo uso, ligado al sitio, nunca se difunde a un padre no verificado. Escucha en el padre y comprueba event.origin antes de fiarte del mensaje:

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. Verifica en tu backend

Esto es igual que en la web: haz POST del token a /siteverify con tu secreto. Mira Integración del lado del servidor.

Recibir errores (opcional)

Cada vez que el widget topa con un error, la página de incrustación lo refleja por un conjunto paralelo de canales: el message handler caputchinError de iOS, la interfaz caputchinError de Android (onError(json)), el canal de React Native como { type: "caputchin-error", ... }, y el mismo relay de iframe con gate. El payload lleva { code, message, originalCode? }. iOS y Android lo reciben como una cadena JSON; React Native y el relay de iframe reciben el sobre tipado.

Esto es del todo opcional. Una app que no registra ningún handler de error simplemente no recibe errores, y la página de incrustación ya muestra una UI de reintento dentro del WebView de todos modos.

Véase también

En esta página