Технические разборы

Механизм обратного вызова reCAPTCHA v2: как работают обратные вызовы и как их активировать

Когда пользователь выполняет задание reCAPTCHA v2, Google вызывает функцию обратного вызова JavaScript, определенную сайтом. Этот обратный вызов обычно активирует кнопку отправки, проверяет форму или отправляет запрос AJAX. После внедрения решенного токена из CaptchaAI вам необходимо запустить тот же обратный вызов — иначе форма не распознает, что CAPTCHA была решена.


Как работают обратные вызовы

Сайт определяет обратный вызов в виджете 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>

Когда пользователь решает CAPTCHA, JavaScript Google вызывает onCaptchaSuccess(token). Функция получает строку токена в качестве единственного аргумента.


Находим функцию обратного вызова

Способ 1: проверьте атрибут обратного вызова данных

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

Способ 2. Проверьте внутренние компоненты грекапчи.

Некоторые сайты используют grecaptcha.render() с возможностью обратного вызова вместо атрибута 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]);
  }
});

Способ 3: перехватить регистрацию обратного вызова

Запустите это в DevTools до загрузки страницы (фрагменты источников →):

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

Запуск обратного вызова после передача токена во внутренний QA endpoint

Питон (селен)

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://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 (Puppeteer)

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

Сайты без обратного вызова данных

Некоторые сайты не используют обратный вызов. Вместо этого они проверяют grecaptcha.getResponse() при отправке формы. Для этих сайтов переопределите функцию:

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

Обратные вызовы с истекшим сроком действия

Некоторые сайты определяют data-expired-callback, чтобы отключить кнопку отправки по истечении срока действия токена. Если срок действия вашего токена близок к истечении срока действия, этот обратный вызов может сработать и повторно заблокировать форму. Чтобы избежать этого, решайте задачу незадолго до подачи заявки.

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

Поиск неисправностей

Проблема Причина Исправить
Форма по-прежнему отключена после инъекции Обратный звонок не запущен Найдите и вызовите функцию обратного вызова
ReferenceError: function not defined Обратный вызов, определенный в замыкании Использовать запасной вариант ___grecaptcha_cfg
Токен введен, но AJAX не отправлен Обратный вызов запускает AJAX, а не отправку формы Проверьте, что делает функция обратного вызова
Токен принят, но на странице отображается ошибка Срок действия токена истек до обратного вызова Решите ближе ко времени подачи

Часто задаваемые вопросы

Что делать, если обратного вызова вообще нет?

Некоторые сайты проверяют токен только на стороне сервера при отправке формы. В этом случае достаточно установить значение текстовой области g-recaptcha-response и отправить форму.

Может ли имя функции обратного вызова меняться между загрузками страниц?

Да, на некоторых сайтах. Всегда извлекайте имя обратного вызова динамически из DOM, а не жестко его кодируйте.

Использует ли reCAPTCHA v3 тот же механизм обратного вызова?

Нет. reCAPTCHA v3 использует grecaptcha.execute(), который возвращает обещание. Нет видимого виджета или атрибута data-callback.


Решите reCAPTCHA v2 с правильной обработкой обратного вызова, используя CaptchaAI.

Получите ключ API по адресуcaptchaai.com.


Связанные руководства

  • reCAPTCHA v2 Невидимый: обнаружение триггера
  • Извлечение параметров reCAPTCHA из исходного кода страницы
  • Обработка нескольких CAPTCHA на одной странице
Комментарии для этой статьи отключены.