Explicaciones Técnicas

Mecanismo de devolución de llamada reCAPTCHA v2: cómo funcionan las devoluciones de llamada y cómo activarlas

Cuando un usuario completa un desafío reCAPTCHA v2, Google llama a una función de devolución de llamada de JavaScript definida por el sitio. Esta devolución de llamada normalmente habilita un botón de envío, valida el formulario o envía una solicitud AJAX. Después de inyectar un token resuelto de CaptchaAI, debe activar la misma devolución de llamada; de lo contrario, el formulario no reconocerá que el CAPTCHA se resolvió.


Cómo funcionan las devoluciones de llamada

El sitio define una devolución de llamada en el widget reCAPTCHA:

<div class="g-recaptcha"
     data-sitekey="6Le-SITEKEY"
     data-callback="onCaptchaSuccess"
     data-expired-callback="onCaptchaExpired">
</div>

<script>
function onCaptchaSuccess(token) {
  document.getElementById('submit-btn').disabled = false;
  document.getElementById('captcha-token').value = token;
}
</script>

Cuando el usuario resuelve el CAPTCHA, el JavaScript de Google llama a onCaptchaSuccess(token). La función recibe la cadena del token como único argumento.


Encontrar la función de devolución de llamada

Método 1: verificar el atributo de devolución de llamada de datos

// In browser console
const widget = document.querySelector('.g-recaptcha');
const callbackName = widget?.getAttribute('data-callback');
console.log('Callback:', callbackName);

Método 2: comprobar los componentes internos de grecaptcha

Algunos sitios usan grecaptcha.render() con una opción de devolución de llamada en lugar del atributo data-callback:

// Search page source for grecaptcha.render
document.querySelectorAll('script:not([src])').forEach(s => {
  if (s.textContent.includes('grecaptcha.render')) {
    console.log(s.textContent.match(/callback\s*:\s*(\w+)/)?.[1]);
  }
});

Método 3: interceptar el registro de devolución de llamada

Ejecute esto en DevTools antes de que se cargue la página (fuentes fragmentos de →):

const origRender = grecaptcha.render;
grecaptcha.render = function(container, params) {
  console.log('Render callback:', params.callback);
  console.log('Expired callback:', params['expired-callback']);
  return origRender.apply(this, arguments);
};

Activar la devolución de llamada después de la inyección del token

Pitón (selenio)

import requests
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

API_KEY = "YOUR_API_KEY"
driver = webdriver.Chrome()
driver.get("https://staging.example.com/qa-login")

# Extract sitekey and callback
sitekey = driver.find_element(
    By.CSS_SELECTOR, ".g-recaptcha"
).get_attribute("data-sitekey")

callback = driver.find_element(
    By.CSS_SELECTOR, ".g-recaptcha"
).get_attribute("data-callback")

# Solve con CaptchaAI
resp = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": driver.current_url,
    "json": "1",
}).json()
task_id = resp["request"]

token = None
for _ in range(24):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": "1"
    }).json()
    if result["status"] == 1:
        token = result["request"]
        break

# Inject token into textarea
driver.execute_script("""
    document.querySelector('textarea[name="g-recaptcha-response"]').value = arguments[0];
""", token)

# Trigger the callback
if callback:
    driver.execute_script(f"window['{callback}'](arguments[0]);", token)
    print(f"Triggered callback: {callback}")
else:
    # Fallback: try ___grecaptcha_cfg
    driver.execute_script("""
        try {
            var widgetId = Object.keys(___grecaptcha_cfg.clients)[0];
            var callback = ___grecaptcha_cfg.clients[widgetId].aa.l.callback;
            if (typeof callback === 'function') callback(arguments[0]);
        } catch(e) {}
    """, token)
    print("Triggered callback via ___grecaptcha_cfg")

JavaScript (titiritero)

const puppeteer = require('puppeteer');

// After solving and getting the token...
await page.evaluate((token, callbackName) => {
  // Set textarea value
  const textarea = document.querySelector(
    'textarea[name="g-recaptcha-response"]'
  );
  textarea.value = token;
  textarea.style.display = 'block'; // sometimes hidden

  // Trigger callback
  if (callbackName && typeof window[callbackName] === 'function') {
    window[callbackName](token);
    console.log(`Called ${callbackName}()`);
  } else {
    // Fallback: search grecaptcha config
    try {
      const clients = ___grecaptcha_cfg.clients;
      const widgetId = Object.keys(clients)[0];
      const cb = clients[widgetId]?.aa?.l?.callback;
      if (typeof cb === 'function') cb(token);
    } catch (e) {}
  }
}, token, callbackName);

Sitios sin devolución de llamada de datos

Algunos sitios no utilizan una devolución de llamada. En cambio, marcan grecaptcha.getResponse() cuando se envía el formulario. Para estos sitios, anule la función:

driver.execute_script("""
    const token = arguments[0];
    document.querySelector('textarea[name="g-recaptcha-response"]').value = token;
    // Override getResponse to return the token
    if (typeof grecaptcha !== 'undefined') {
        grecaptcha.getResponse = function() { return token; };
    }
""", token)

# Then submit the form normally
driver.find_element(By.CSS_SELECTOR, "form").submit()

Devoluciones de llamada caducadas

Algunos sitios definen data-expired-callback para deshabilitar el botón de envío cuando caduca el token. Si su token está a punto de caducar, esta devolución de llamada podría activarse y volver a bloquear el formulario. Resuelva cerca del tiempo de envío para evitar esto.

// Check for expired callback
const expiredCallback = document.querySelector('.g-recaptcha')
  ?.getAttribute('data-expired-callback');
console.log('Expired callback:', expiredCallback);

Solución de problemas

problema causa Solución
El formulario aún está deshabilitado después de la inyección. Devolución de llamada no activada Busque y llame a la función de devolución de llamada
ReferenceError: function not defined Devolución de llamada definida en un cierre Utilice el respaldo ___grecaptcha_cfg
Token inyectado pero AJAX no enviado La devolución de llamada activa AJAX, no el envío del formulario Comprueba qué hace la función de devolución de llamada
Token aceptado pero la página muestra un error El token expiró antes de la devolución de llamada Resolver más cerca del tiempo de envío

Preguntas frecuentes

¿Qué pasa si no hay ninguna devolución de llamada?

Algunos sitios solo verifican el token en el lado del servidor cuando se envía el formulario. En ese caso, es suficiente establecer el valor del área de texto g-recaptcha-response y enviar el formulario.

¿Puede el nombre de la función de devolución de llamada cambiar entre cargas de página?

Sí, en algunos sitios. Extraiga siempre el nombre de la devolución de llamada dinámicamente del DOM en lugar de codificarlo.

¿reCAPTCHA v3 utiliza el mismo mecanismo de devolución de llamada?

No. reCAPTCHA v3 usa grecaptcha.execute() que devuelve una Promesa. No hay ningún widget visible ni atributo data-callback.


Resuelva reCAPTCHA v2 con un manejo adecuado de devolución de llamada usando CaptchaAI

Obtenga su clave API encaptchaai.com.


Guías relacionadas

  • reCAPTCHA v2 Invisible: Detección de disparador
  • Extracción de parámetros reCAPTCHA de la fuente de la página
  • Manejo de múltiples CAPTCHA en una sola página
Los comentarios están deshabilitados para este artículo.