Usa Guzzle con la API de tareas de CAPTCHA

Por solvethree4 min de lectura

Usa Guzzle 7 para crear una tarea y luego consulta su ID hasta que la tarea esté en ready o failed. Conserva el requestId si algo sale mal: que se agote el tiempo de conexión no demuestra que la creación de la tarea haya fallado.

Esta es una integración REST, no un SDK oficial de PHP.

En esta página

Prepara la solicitud de reCAPTCHA

  • Instala Guzzle 7 con Composer. El comando de instalación aparece al principio del ejemplo.
  • Define SOLVETHREE_API_KEY con la clave de API de tu espacio de trabajo y SOLVETHREE_PROXY con la URL del proxy. Mantén la clave en el servidor.
  • Crea un perfil activo para tu sitio de destino en la consola antes de solicitar una resolución. Consulta la configuración del perfil del sitio.
  • Sustituye accounts.example.com y login por el nombre de host y la acción de destino. El campo site acepta un nombre de host, no una URL completa.

El ejemplo solicita reCAPTCHA Enterprise v3, el valor predeterminado de recaptchaType. No añadas los campos type ni url. Proporciona un único proxy; si usas proxyCandidates en su lugar, omite proxy por completo.

Crea una sola vez y luego consulta dentro de un límite de tiempo fijo

Guzzle · un POST y consultas periódicas con límites
<?php
// composer require guzzlehttp/guzzle:^7
require __DIR__.'/vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client([
    'base_uri' => 'https://api.solvethree.com/v1/',
    'headers' => ['Authorization' => 'Bearer '.getenv('SOLVETHREE_API_KEY'), 'Accept' => 'application/json'],
    'allow_redirects' => false,
    'http_errors' => false,
]);
$input = ['site' => 'accounts.example.com', 'action' => 'login',
    'proxy' => getenv('SOLVETHREE_PROXY'), 'requestId' => bin2hex(random_bytes(16))];
$deadline = microtime(true) + 180;
$call = function (string $method, string $path, array $options = []) use ($client, $deadline): array {
    $remaining = $deadline - microtime(true);
    if ($remaining <= 0) throw new RuntimeException('Local deadline reached');
    $response = $client->request($method, $path, $options + ['timeout' => min(25, $remaining), 'connect_timeout' => min(10, $remaining)]);
    if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
        throw new RuntimeException('API HTTP '.$response->getStatusCode());
    }
    return json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR);
};
$id = null;
try {
    $result = $call('POST', 'tasks?waitMs=0', ['json' => $input]);
    $id = $result['id'];
    while (in_array($result['status'], ['pending', 'solving'], true)) {
        $result = $call('GET', 'tasks/'.rawurlencode($id).'?waitMs=15000');
        if (in_array($result['status'], ['pending', 'solving'], true)) usleep(1000000);
    }
    if ($result['status'] !== 'ready') {
        throw new RuntimeException('Task failed: '.($result['errorCode'] ?? 'unknown'));
    }
    echo 'Ready task: '.$id.PHP_EOL;
    // Submit $result['solution']['token'] with userAgent/secChUa and the same proxy.
} catch (Throwable $error) {
    fwrite(STDERR, 'Recovery requestId='.$input['requestId'].' taskId='.($id ?? 'unknown').PHP_EOL);
    throw $error;
}

El cliente se autentica con una clave Bearer. Su base_uri termina en /v1/ y las rutas de las solicitudes son relativas, por lo que conservan ese prefijo de la API. La opción json de Guzzle envía las entradas de la tarea como JSON.

La función auxiliar realiza un único POST de creación. Después consulta el ID de tarea devuelto mientras status sea pending o solving. La creación y las consultas comparten un límite de 180 segundos; cada llamada HTTP se limita al tiempo restante, con tiempos de espera máximos de solicitud y conexión de 25 y 10 segundos, respectivamente. Una nueva consulta no reinicia el límite global.

Las redirecciones están desactivadas. Establecer http_errors en false permite que la función auxiliar compruebe explícitamente las respuestas que no sean 2xx antes de decodificar el contenido de una respuesta satisfactoria; no suprime los errores de conexión ni los tiempos de espera agotados.

  • Una respuesta HTTP que no sea 2xx lanza un error HTTP de la API. Diagnostica el estado y el cuerpo de la respuesta en lugar de tratarla como una resolución fallida.
  • Una tarea en el estado terminal failed detiene las consultas. El ejemplo lanza su errorCode; revisa también el campo error de la tarea para ver el mensaje de diagnóstico. Las resoluciones fallidas se reembolsan.
  • Solo ready contiene una solución utilizable. La línea impresa “Ready task” indica que la tarea está lista, no que el sitio web de destino la haya aceptado.

Recupera una solicitud cuyo tiempo de espera se ha agotado sin pagar por otra tarea

Conserva el requestId generado y las entradas exactas de la tarea antes de enviar POST. El bloque catch imprime requestId y, si la creación devolvió uno, taskId. Esa salida ayuda a recuperar la tarea, pero no sustituye guardar la solicitud antes de enviarla.

  1. 1.

    Si tienes un ID de tarea, recupera esa tarea

    Usa GET /v1/tasks/:id para comprobar su estado. Alcanzar el plazo local de la función auxiliar no demuestra que la tarea remota haya fallado.

  2. 2.

    Si la creación agotó el tiempo de espera sin devolver un ID, recupérala con requestId

    Envía la misma solicitud de tarea con el requestId guardado. Si las entradas coinciden, se devuelve la tarea existente; si son diferentes y usan ese ID, se genera un conflicto.

  3. 3.

    No vuelvas a ejecutar el script como atajo para la recuperación

    Cada ejecución genera un requestId nuevo. No añadas un reintento automático de POST que genere otro ID ni reintentes a ciegas entradas no válidas sin modificarlas.

Envía el token a través del mismo proxy

El ejemplo se detiene antes de contactar con el destino. Envía solution.token, no el ID de tarea, y usa los valores devueltos en solution.userAgent y solution.secChUa como cabeceras User-Agent y Sec-CH-UA de la solicitud al destino.

El proxy incluido en el JSON de la tarea indica a solvethree qué proxy usar para la resolución. No configura la conexión de Guzzle a la API ni tu solicitud posterior al destino. Configura por separado la solicitud al destino para que use ese mismo proxy.

Usa un cliente independiente para la solicitud al destino, de modo que la cabecera Authorization predeterminada del cliente de la API no se envíe al sitio web. Mantén juntos el token, la identidad de navegador devuelta, el proxy y el destino previsto. Un token listo puede ser rechazado si la solicitud posterior no coincide.

Ejecuta tu primera tarea.

Empieza con $5 de crédito en tu espacio de trabajo.

Crear un espacio de trabajo
Usa Guzzle con la API de tareas de CAPTCHA · solvethree