API Tutorials

为 CaptchaAI 构建 PHP Composer 包

PHP 为大部分 Web 自动化后端提供支持。 Composer 包将 CaptchaAI API 包装到可重用库中 - $client->solveRecaptchaV2($sitekey, $url),而不是每个项目中的原始 cURL 调用和手动 JSON 解析。

封装结构

captchaai-php/
├── src/
│   ├── CaptchaAI.php        # Main client class
│   ├── Exception/
│   │   ├── CaptchaAIException.php
│   │   ├── SubmitException.php
│   │   ├── SolveException.php
│   │   └── TimeoutException.php
│   └── Enum/
│       └── Method.php
├── composer.json
└── README.md

作曲家配置

{
    "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/"
        }
    }
}

异常类

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

主要客户

<?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',
        float $minScore = 0.3
    ): string {
        return $this->solve([
            'method' => 'userrecaptcha',
            'version' => 'v3',
            'googlekey' => $sitekey,
            'pageurl' => $pageurl,
            'action' => $action,
            'min_score' => $minScore,
        ]);
    }

    /**

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

使用示例

<?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://staging.example.com/qa-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";

故障排除

问题 原因 处理方式
SubmitException: ERROR_WRONG_USER_KEY API 密钥无效 从仪表板检查密钥
TimeoutException 频繁 超时时间太短 $timeout增加到180+
Class not found 自动装载机未配置 运行composer dump-autoload
咕咕连接错误 网络问题或防火墙 检查服务器是否可以到达 ocr.captchaai.com
json_decode 返回 null 响应正文无效 检查API URL;记录原始响应以进行调试

常问问题

为什么使用 Guzzle 而不是原生 cURL?

Guzzle 提供 PSR-7 消息接口、自动 JSON 处理、连接池和中间件支持。对于已经使用 Guzzle 的项目,SDK 无需添加依赖项即可集成。您可以替换任何 PSR-18 兼容客户端。

我如何在 Laravel 中使用它?

将客户端注册为服务提供者中的单例。将 CaptchaAI::classconfig/services.php 中的 API 密钥绑定。通过控制器或作业中的构造函数注入将其注入。

我应该发布到 Packagist 吗?

对于内部使用,请通过 composer.json 中的 repositories 指向您的 Git 存储库进行引用。对于公开分发,请提交给 Packagist 并提供适当的版本控制和自述文件。

相关文章

下一步

构建您的 PHP CAPTCHA 包 –获取您的 CaptchaAI API 密钥并创建一个 Composer 库。

相关指南:

该文章已禁用评论。

相关文章

DevOps & Scaling 用于 CaptchaAI Worker 部署的 Ansible Playbook
使用 Captcha AI Worker 部署 Ansible Playbook 的 Dev Ops 指南,包括生产中 Captcha AI 工作流程的架构决策、操作注意事项和自动化模式。

使用 Captcha AI Worker 部署 Ansible Playbook 的 Dev Ops 指南,包括生产中 Captcha AI 工作流程的架构决策、操作注...

Apr 19, 2026
DevOps & Scaling AWS Lambda + CaptchaAI:无服务器验证码解决
AWS Lambda + Captcha AI 的开发运营指南:无服务器验证码解决方案,包含生产中 Captcha AI 工作流程的架构决策、操作注意事项和自动化模式。

AWS Lambda + Captcha AI 的开发运营指南:无服务器验证码解决方案,包含生产中 Captcha AI 工作流程的架构决策、操作...

Apr 21, 2026
Comparisons 最佳验证码解决服务比较(2025 年)
最佳验证码解决服务比较(2025 年)的实际比较,重点关注 Captcha AI 的成本、准确性、速度和集成工作方面的差异。

最佳验证码解决服务比较(2025 年)的实际比较,重点关注 Captcha AI 的成本、准确性、速度和集成工作方面的差异。

Apr 24, 2026