Вебхуки
Результат без опроса: подписанный POST на ваш callbackUrl, когда задача завершена, с тремя попытками доставки.
Передайте callbackUrl в createTask (или pingback в in.php). Когда задача завершится — решена или нет, — мы отправим на этот адрес POST с JSON. getTaskResult при этом тоже работает.
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "RecaptchaV2TaskProxyless",
"websiteURL": "https://www.google.com/recaptcha/api2/demo",
"websiteKey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
},
"callbackUrl": "https://example.com/captcha-webhook"
}Что мы отправляем#
POST /captcha-webhook HTTP/1.1
Host: example.com
Content-Type: application/json
User-Agent: captcha-api-webhooks/1
X-Task-Id: 7781
X-Signature: t=1793370008,v1=6c1f0e2b9d4a…
{"taskId":7781,"errorId":0,"status":"ready","solution":{"gRecaptchaResponse":"03AFcWeA6Z3vq0VhGm1oS8r0bAxKk4…","userAgent":""},"cost":"0.000600","createTime":1793370000,"endTime":1793370007,"solveCount":1}Тело — ответ getTaskResult плюс taskId (без ip). Неудачная задача:
{"taskId":7782,"errorId":12,"errorCode":"ERROR_CAPTCHA_UNSOLVABLE","errorDescription":"the task could not be solved"}Доставка и повторы#
- Ответьте любым статусом 2xx в течение 5 секунд; тело ответа не читается.
- До 3 попыток: сразу, через 2 секунды и ещё через 6 секунд. Токен reCAPTCHA живёт около 2 минут, поэтому поздние повторы бессмысленны — если нужна гарантия, оставьте опрос как запасной путь.
- Редиректы не выполняются. Адрес должен быть http(s) и указывать на публичный IP.
- Доставка может повториться (например, если ваш ответ потерялся) — убирайте дубли по
taskId. - Вебхуки test-ключей доставляются и подписываются так же.
Подпись#
Каждый запрос подписан секретом вебхуков вашего аккаунта (whsec_…). Он находится в Кабинете → Уведомления; там же его можно перевыпустить — новый секрет действует сразу.
X-Signature: t=<unix-время>,v1=<hex HMAC-SHA256(secret, "<t>.<сырое тело>")>- Разбейте заголовок по
,и возьмитеtиv1. - Посчитайте HMAC-SHA256 с секретом в качестве ключа от строки:
t, точка и сырое тело — байты в том виде, в каком они пришли, до разбора JSON. - Сравните hex-результат с
v1за постоянное время. - Отклоняйте запросы, у которых
tотличается от ваших часов больше чем на 5 минут, — это защита от повторной отправки.
Примеры проверки#
import hashlib
import hmac
import json
import os
import time
from flask import Flask, abort, request
SECRET = os.environ["WEBHOOK_SECRET"] # whsec_… from the dashboard
TOLERANCE = 300 # seconds
def verify(header: str, body: bytes) -> bool:
try:
parts = dict(item.split("=", 1) for item in header.split(","))
t, signature = int(parts["t"]), parts["v1"]
except (KeyError, ValueError):
return False
if abs(time.time() - t) > TOLERANCE:
return False
expected = hmac.new(SECRET.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
app = Flask(__name__)
@app.post("/captcha-webhook")
def captcha_webhook():
body = request.get_data() # raw bytes, exactly as sent
if not verify(request.headers.get("X-Signature", ""), body):
abort(401)
event = json.loads(body)
if event["errorId"] == 0:
print(event["taskId"], event["solution"]["gRecaptchaResponse"])
else:
print(event["taskId"], "failed:", event["errorCode"])
return "", 204Проверка#
Создайте задачу с test-ключом и callbackUrl на ваш обработчик: вебхук придёт через пару секунд и будет подписан так же, как боевой. Локальному серверу нужен публичный адрес — используйте туннель; частные и локальные адреса отклоняются.