PHP alimente une grande partie des backends d’automatisation Web. Un package Composer intègre l'API CaptchaAI dans une bibliothèque réutilisable - $client->solveRecaptchaV2($sitekey, $url) au lieu d'appels cURL bruts et d'analyse JSON manuelle dans chaque projet.
Structure du paquet
captchaai-php/
├── src/
│ ├── CaptchaAI.php # Main client class
│ ├── Exception/
│ │ ├── CaptchaAIException.php
│ │ ├── SubmitException.php
│ │ ├── SolveException.php
│ │ └── TimeoutException.php
│ └── Enum/
│ └── Method.php
├── composer.json
└── README.md
Configuration du compositeur
{
"name": "your-vendor/captchaai",
"description": "PHP client library for CaptchaAI API",
"type": "library",
"license": "MIT",
"require": {
"php": ">=8.1",
"guzzlehttp/guzzle": "^7.0"
},
"autoload": {
"psr-4": {
"CaptchaAI\\": "src/"
}
}
}
Classes d'exceptions
<?php
// src/Exception/CaptchaAIException.php
namespace CaptchaAI\Exception;
class CaptchaAIException extends \RuntimeException
{
private ?string $errorCode;
private const FATAL_CODES = [
'ERROR_WRONG_USER_KEY',
'ERROR_KEY_DOES_NOT_EXIST',
'ERROR_ZERO_BALANCE',
'ERROR_IP_NOT_ALLOWED',
];
public function __construct(string $message, ?string $errorCode = null)
{
parent::__construct($message);
$this->errorCode = $errorCode;
}
public function getErrorCode(): ?string
{
return $this->errorCode;
}
public function isFatal(): bool
{
return in_array($this->errorCode, self::FATAL_CODES, true);
}
}
<?php
// src/Exception/SubmitException.php
namespace CaptchaAI\Exception;
class SubmitException extends CaptchaAIException
{
public function __construct(string $code)
{
parent::__construct("Task submission failed: {$code}", $code);
}
}
<?php
// src/Exception/SolveException.php
namespace CaptchaAI\Exception;
class SolveException extends CaptchaAIException
{
public function __construct(string $code)
{
parent::__construct("Task solving failed: {$code}", $code);
}
}
<?php
// src/Exception/TimeoutException.php
namespace CaptchaAI\Exception;
class TimeoutException extends CaptchaAIException
{
private string $taskId;
public function __construct(string $taskId, int $timeoutSeconds)
{
parent::__construct("Task {$taskId} timed out after {$timeoutSeconds}s");
$this->taskId = $taskId;
}
public function getTaskId(): string
{
return $this->taskId;
}
}
Client principal
<?php
// src/CaptchaAI.php
namespace CaptchaAI;
use GuzzleHttp\Client as HttpClient;
use CaptchaAI\Exception\SubmitException;
use CaptchaAI\Exception\SolveException;
use CaptchaAI\Exception\TimeoutException;
class CaptchaAI
{
private const SUBMIT_URL = 'https://ocr.captchaai.com/in.php';
private const RESULT_URL = 'https://ocr.captchaai.com/res.php';
private string $apiKey;
private HttpClient $http;
private int $pollInterval;
private int $timeout;
public function __construct(
string $apiKey,
int $pollInterval = 5,
int $timeout = 180,
?HttpClient $httpClient = null
) {
$this->apiKey = $apiKey;
$this->pollInterval = $pollInterval;
$this->timeout = $timeout;
$this->http = $httpClient ?? new HttpClient(['timeout' => 30]);
}
// --- Core methods ---
private function submit(array $params): string
{
$params['key'] = $this->apiKey;
$params['json'] = 1;
$response = $this->http->post(self::SUBMIT_URL, [
'form_params' => $params,
]);
$result = json_decode($response->getBody()->getContents(), true);
if (($result['status'] ?? 0) !== 1) {
throw new SubmitException($result['request'] ?? 'unknown');
}
return $result['request']; // task ID
}
private function poll(string $taskId): string
{
$startTime = time();
while (time() - $startTime < $this->timeout) {
sleep($this->pollInterval);
$response = $this->http->get(self::RESULT_URL, [
'query' => [
'key' => $this->apiKey,
'action' => 'get',
'id' => $taskId,
'json' => 1,
],
]);
$result = json_decode($response->getBody()->getContents(), true);
if (($result['request'] ?? '') === 'CAPCHA_NOT_READY') {
continue;
}
if (($result['status'] ?? 0) === 1) {
return $result['request'];
}
throw new SolveException($result['request'] ?? 'unknown');
}
throw new TimeoutException($taskId, $this->timeout);
}
private function solve(array $params): string
{
$taskId = $this->submit($params);
return $this->poll($taskId);
}
// --- Solver methods ---
/**
* Solve reCAPTCHA v2
*/
public function solveRecaptchaV2(
string $sitekey,
string $pageurl,
bool $invisible = false,
?string $cookies = null
): string {
$params = [
'method' => 'userrecaptcha',
'googlekey' => $sitekey,
'pageurl' => $pageurl,
];
if ($invisible) $params['invisible'] = 1;
if ($cookies) $params['cookies'] = $cookies;
return $this->solve($params);
}
/**
* Solve reCAPTCHA v3
*/
public function solveRecaptchaV3(
string $sitekey,
string $pageurl,
string $action = 'verify',
): string {
return $this->solve([
'method' => 'userrecaptcha',
'version' => 'v3',
'googlekey' => $sitekey,
'pageurl' => $pageurl,
'action' => $action,
]);
}
/**
* Solve Cloudflare Turnstile
*/
public function solveTurnstile(
string $sitekey,
string $pageurl,
?string $action = null,
?string $cdata = null
): string {
$params = [
'method' => 'turnstile',
'sitekey' => $sitekey,
'pageurl' => $pageurl,
];
if ($action) $params['action'] = $action;
if ($cdata) $params['data'] = $cdata;
return $this->solve($params);
}
/**
* Solve hCaptcha
*/
public function solveHCaptcha(string $sitekey, string $pageurl): string
{
return $this->solve([
'method' => 'hcaptcha',
'sitekey' => $sitekey,
'pageurl' => $pageurl,
]);
}
/**
* Solve image/text CAPTCHA from base64
*/
public function solveImage(
string $base64Image,
bool $caseSensitive = false,
?int $minLength = null,
?int $maxLength = null
): string {
$params = [
'method' => 'base64',
'body' => $base64Image,
];
if ($caseSensitive) $params['regsense'] = 1;
if ($minLength !== null) $params['min_len'] = $minLength;
if ($maxLength !== null) $params['max_len'] = $maxLength;
return $this->solve($params);
}
/**
* Solve GeeTest v3
*/
public function solveGeeTestV3(
string $gt,
string $challenge,
string $pageurl
): string {
return $this->solve([
'method' => 'geetest',
'gt' => $gt,
'challenge' => $challenge,
'pageurl' => $pageurl,
]);
}
// --- Utility methods ---
/**
* Get current account balance
*/
public function getBalance(): float
{
$response = $this->http->get(self::RESULT_URL, [
'query' => [
'key' => $this->apiKey,
'action' => 'getbalance',
'json' => 1,
],
]);
$result = json_decode($response->getBody()->getContents(), true);
return (float) ($result['request'] ?? 0);
}
/**
* Report a bad solution
*/
public function reportBad(string $taskId): bool
{
$response = $this->http->get(self::RESULT_URL, [
'query' => [
'key' => $this->apiKey,
'action' => 'reportbad',
'id' => $taskId,
'json' => 1,
],
]);
$result = json_decode($response->getBody()->getContents(), true);
return ($result['status'] ?? 0) === 1;
}
}
Exemples d'utilisation
<?php
require_once 'vendor/autoload.php';
use CaptchaAI\CaptchaAI;
use CaptchaAI\Exception\SubmitException;
use CaptchaAI\Exception\TimeoutException;
$client = new CaptchaAI(
apiKey: 'YOUR_API_KEY',
pollInterval: 5,
timeout: 120
);
// Check balance
$balance = $client->getBalance();
echo "Balance: \${$balance}\n";
// Solve reCAPTCHA v2
try {
$token = $client->solveRecaptchaV2(
sitekey: '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
pageurl: 'https://example.com/login'
);
echo "Token: " . substr($token, 0, 40) . "...\n";
} catch (TimeoutException $e) {
echo "Timed out: {$e->getMessage()}\n";
} catch (SubmitException $e) {
if ($e->isFatal()) {
echo "Fatal: {$e->getErrorCode()}\n";
exit(1);
}
echo "Retryable: {$e->getErrorCode()}\n";
}
// Solve Turnstile
$turnstileToken = $client->solveTurnstile(
sitekey: '0x4AAAAAAADnPIDROrmt1Wwj',
pageurl: 'https://example.com/checkout'
);
// Solve image CAPTCHA
$imageBase64 = base64_encode(file_get_contents('captcha.png'));
$text = $client->solveImage($imageBase64, caseSensitive: true);
echo "Text: {$text}\n";
Dépannage
| Problème | Parce que | Corriger |
|---|---|---|
SubmitException: ERROR_WRONG_USER_KEY |
Clé API invalide | Vérifier la clé depuis le tableau de bord |
TimeoutException fréquemment |
Délai d'attente trop court | Augmentez $timeout à 180+ |
Class not found |
Chargeur automatique non configuré | Exécutez composer dump-autoload |
| Erreur de connexion Guzzle | Problème de réseau ou pare-feu | Le serveur de vérification peut atteindre ocr.captchaai.com |
json_decode renvoie null |
Corps de réponse invalide | Vérifiez l'URL de l'API ; enregistrer la réponse brute pour le débogage |
FAQ
Pourquoi Guzzle au lieu de cURL natif ?
Guzzle fournit des interfaces de messages PSR-7, une gestion automatique de JSON, un regroupement de connexions et une prise en charge des middlewares. Pour les projets utilisant déjà Guzzle, le SDK s'intègre sans ajouter de dépendances. Vous pouvez remplacer n'importe quel client compatible PSR-18.
Comment puis-je l’utiliser dans Laravel ?
Enregistrez le client en tant que singleton chez un fournisseur de services. Liez CaptchaAI::class avec la clé API de config/services.php. Injectez-le via une injection de constructeur dans des contrôleurs ou des tâches.
Dois-je publier sur Packagist ?
Pour un usage interne, référencez via repositories dans composer.json pointant vers votre dépôt Git. Pour une distribution publique, soumettez-le à Packagist avec la version appropriée et un README.
Articles connexes
- Création de pipelines Captcha client Captchaai
- Construire une automatisation responsable Captchaai
- Création d'une surveillance du tableau de bord d'utilisation de Captchaai
Prochaines étapes
Créez votre package PHP CAPTCHA -récupérez votre clé API CaptchaAIet créez une bibliothèque Composer.
Guides associés :