Análises Técnicas

Mecanismo de retorno de chamada reCAPTCHA v2: como funcionam os retornos de chamada e como acioná-los

Quando um usuário conclui um desafio reCAPTCHA v2, o Google chama uma função de retorno de chamada JavaScript definida pelo site. Esse retorno de chamada normalmente ativa um botão de envio, valida o formulário ou envia uma solicitação AJAX. Após injetar um token resolvido de CaptchaAI, você precisa acionar o mesmo callback — caso contrário, o formulário não reconhecerá que o CAPTCHA foi resolvido.


Como funcionam os retornos de chamada

O site define um retorno de chamada no 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>

Quando o usuário resolve o CAPTCHA, o JavaScript do Google chama onCaptchaSuccess(token). A função recebe a string do token como único argumento.


Encontrando a função de retorno de chamada

Método 1: verifique o atributo de retorno de chamada de dados

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

Método 2: verificar os componentes internos do grecaptcha

Alguns sites usam grecaptcha.render() com uma opção de retorno de chamada em vez do 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 o registro de retorno de chamada

Execute isso no DevTools antes de a página carregar (Fontes → Snippets):

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);
};

Acionando o retorno de chamada após envio controlado ao endpoint QA

Python (Selênio)

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 with 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 (titereiro)

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);

Sites sem retorno de chamada de dados

Alguns sites não usam retorno de chamada. Em vez disso, eles verificam grecaptcha.getResponse() quando o formulário é enviado. Para esses sites, substitua a função:

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()

Retornos de chamada expirados

Alguns sites definem data-expired-callback para desabilitar o botão de envio quando o token expirar. Se o seu token estiver próximo da expiração, esse retorno de chamada poderá ser acionado e bloquear novamente o formulário. Resolva próximo ao momento do envio para evitar isso.

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

Solução de problemas

Problema Causa Correção
Formulário ainda desativado após injeção Retorno de chamada não acionado Encontre e chame a função de retorno de chamada
ReferenceError: function not defined Retorno de chamada definido em um encerramento Usar substituto ___grecaptcha_cfg
Token injetado mas AJAX não enviado O retorno de chamada aciona AJAX, não o envio do formulário Verifique o que a função de retorno de chamada faz
Token aceito, mas a página mostra erro O token expirou antes do retorno de chamada Resolva mais perto do momento do envio

Perguntas frequentes

E se não houver nenhum retorno de chamada?

Alguns sites verificam o token apenas no lado do servidor quando o formulário é enviado. Nesse caso, basta definir o valor da área de texto g-recaptcha-response e enviar o formulário.

O nome da função de retorno de chamada pode mudar entre carregamentos de página?

Sim, em alguns sites. Sempre extraia o nome do retorno de chamada dinamicamente do DOM, em vez de codificá-lo.

reCAPTCHA v3 usa o mesmo mecanismo de retorno de chamada?

Não. reCAPTCHA v3 usa grecaptcha.execute() que retorna uma promessa. Não há widget visível ou atributo data-callback.


Resolva reCAPTCHA v2 com tratamento de retorno de chamada adequado usando CaptchaAI

Obtenha sua chave API emcaptchaai.com.


Guias relacionados

Os comentários estão desativados para este artigo.