Captcha v4
API 参考

Server

服务端二次验证接口文档

重要: 服务端二次验证是必需的安全步骤,不可省略!

接口信息

项目内容
接口地址请根据您注册 ID 时选择的地域使用对应域名:
全球:https://cap-global.geelabapi.com/validate
欧洲:https://cap-eu.geelabapi.com/validate
北美:https://cap-na.geelabapi.com/validate
协议支持HTTP/HTTPS
请求方法GET/POST(推荐 POST)
请求格式application/x-www-form-urlencoded
返回类型JSON

建议使用 POST 方法,并将 captcha_id 作为 URL 参数,便于日志追踪。

请求参数

必需参数

参数名类型必填说明
captcha_idstring验证 ID,32 位字符串
lot_numberstring验证流水号,从客户端获取
captcha_outputstring验证输出信息,从客户端获取
pass_tokenstring验证通过标识,从客户端获取
gen_timestring验证通过时间戳,必须是纯数字字符串
sign_tokenstring验证签名,使用 HMAC-SHA256 算法生成

gen_time 必须是纯数字字符串,不能包含其他字符,否则会返回 -50005 错误。

请求示例

cURL 请求示例
curl -X POST 'https://cap-global.geelabapi.com/validate?captcha_id=YOUR_CAPTCHA_ID' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'lot_number=4dc3cfc2cdff448cad8d13107198d473' \
  -d 'captcha_output=...' \
  -d 'pass_token=...' \
  -d 'gen_time=1234567890' \
  -d 'sign_token=...'

sign_token 生成

sign_token 使用 HMAC-SHA256 算法生成,用于验证请求的真实性。

准备参数

  • message: 用户完成验证的流水号 lot_number
  • key: 验证私钥 captcha_key

生成签名

使用 HMAC-SHA256 算法进行签名。

代码示例

Python 签名生成
import hmac
import hashlib

def generate_sign_token(lot_number, captcha_key):
    """生成验证签名"""
    sign_token = hmac.new(
        captcha_key.encode('utf-8'),
        lot_number.encode('utf-8'),
        digestmod=hashlib.sha256
    ).hexdigest()
    return sign_token

# 使用示例
sign_token = generate_sign_token(lot_number, CAPTCHA_KEY)
Node.js 签名生成
const crypto = require('crypto');

function generateSignToken(lotNumber, captchaKey) {
    return crypto
        .createHmac('sha256', captchaKey)
        .update(lotNumber)
        .digest('hex');
}

// 使用示例
const signToken = generateSignToken(lotNumber, CAPTCHA_KEY);
Java 签名生成
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public String generateSignToken(String lotNumber, String captchaKey)
    throws Exception {
    Mac mac = Mac.getInstance("HmacSHA256");
    SecretKeySpec secretKey = new SecretKeySpec(
        captchaKey.getBytes("UTF-8"),
        "HmacSHA256"
    );
    mac.init(secretKey);

    byte[] hash = mac.doFinal(lotNumber.getBytes("UTF-8"));
    return bytesToHex(hash);
}

private String bytesToHex(byte[] bytes) {
    StringBuilder result = new StringBuilder();
    for (byte b : bytes) {
        result.append(String.format("%02x", b));
    }
    return result.toString();
}
PHP 签名生成
<?php
function generateSignToken($lotNumber, $captchaKey) {
    return hash_hmac('sha256', $lotNumber, $captchaKey);
}

// 使用示例
$signToken = generateSignToken($lotNumber, $captchaKey);
?>
Go 签名生成
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
)

func GenerateSignToken(lotNumber, captchaKey string) string {
    h := hmac.New(sha256.New, []byte(captchaKey))
    h.Write([]byte(lotNumber))
    return hex.EncodeToString(h.Sum(nil))
}

// 使用示例
signToken := GenerateSignToken(lotNumber, captchaKey)

响应格式

校验成功

校验成功响应
{
    "status": "success",
    "result": "success",
    "reason": "",
    "captcha_args": {
        "used_type": "slide",
        "user_ip": "127.0.0.1",
        "lot_number": "4dc3cfc2cdff448cad8d13107198d473",
        "scene": "反爬虫",
        "referer": "http://127.0.0.1:8077/"
    }
}

校验失败

校验失败响应
{
    "status": "success",
    "result": "fail",
    "reason": "pass_token expire",
    "captcha_args": {
        "used_type": "slide",
        "user_ip": "127.0.0.1",
        "lot_number": "4dc3cfc2cdff448cad8d13107198d473"
    }
}

常见失败原因:

  • pass_token expire - 验证已过期
  • lot_number not match - 流水号不匹配
  • illegal sign_token - 签名错误

请求异常

请求异常响应
{
    "status": "error",
    "code": "-50005",
    "msg": "illegal gen_time",
    "desc": {
        "type": "defined error"
    }
}

当收到异常响应时,建议放行请求,避免阻塞业务流程。

响应字段说明

字段名类型说明
statusstring请求状态:success(成功)、error(异常)
resultstring校验结果:success(通过)、fail(失败)
reasonstring校验结果说明或失败原因
codestring错误码(仅异常时返回)
msgstring错误信息(仅异常时返回)
captcha_argsobject验证详细信息

captcha_args 字段

字段名类型说明
used_typestring使用的验证类型(slide、icon、九宫格等)
user_ipstring用户 IP 地址
lot_numberstring验证流水号
scenestring验证场景
refererstring来源页面

完整请求示例

Python 完整示例
import hmac
import hashlib
import requests

def validate_captcha(request_data):
    """验证码二次校验"""
    # 配置参数
    CAPTCHA_ID = 'YOUR_CAPTCHA_ID'
    CAPTCHA_KEY = 'YOUR_CAPTCHA_KEY'
    API_URL = 'https://cap-global.geelabapi.com/validate'

    # 获取客户端参数
    lot_number = request_data.get('lot_number')
    captcha_output = request_data.get('captcha_output')
    pass_token = request_data.get('pass_token')
    gen_time = request_data.get('gen_time')

    # 生成签名
    sign_token = hmac.new(
        CAPTCHA_KEY.encode(),
        lot_number.encode(),
        hashlib.sha256
    ).hexdigest()

    # 构造请求参数
    params = {'captcha_id': CAPTCHA_ID}
    data = {
        'lot_number': lot_number,
        'captcha_output': captcha_output,
        'pass_token': pass_token,
        'gen_time': gen_time,
        'sign_token': sign_token
    }

    # 发起请求
    try:
        response = requests.post(API_URL, params=params, data=data, timeout=5)

        if response.status_code != 200:
            # 接口异常,建议放行
            return {'result': 'success', 'reason': 'api error, fallback'}

        result = response.json()
        return result

    except Exception as e:
        # 请求异常,建议放行
        print(f'验证异常: {e}')
        return {'result': 'success', 'reason': 'request fail, fallback'}
Node.js 完整示例
const crypto = require('crypto');
const axios = require('axios');

async function validateCaptcha(requestData) {
    // 配置参数
    const CAPTCHA_ID = 'YOUR_CAPTCHA_ID';
    const CAPTCHA_KEY = 'YOUR_CAPTCHA_KEY';
    const API_URL = 'https://cap-global.geelabapi.com/validate';

    // 获取客户端参数
    const { lot_number, captcha_output, pass_token, gen_time } = requestData;

    // 生成签名
    const sign_token = crypto
        .createHmac('sha256', CAPTCHA_KEY)
        .update(lot_number)
        .digest('hex');

    // 构造请求参数
    const params = new URLSearchParams({
        lot_number,
        captcha_output,
        pass_token,
        gen_time,
        sign_token
    });

    // 发起请求
    try {
        const response = await axios.post(
            `${API_URL}?captcha_id=${CAPTCHA_ID}`,
            params,
            {
                timeout: 5000,
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            }
        );

        if (response.status !== 200) {
            // 接口异常,建议放行
            return { result: 'success', reason: 'api error, fallback' };
        }

        return response.data;

    } catch (error) {
        // 请求异常,建议放行
        console.error('验证异常:', error);
        return { result: 'success', reason: 'request fail, fallback' };
    }
}
Java 完整示例
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.http.*;
import java.net.URI;
import com.fasterxml.jackson.databind.ObjectMapper;

public class CaptchaValidator {
    private static final String CAPTCHA_ID = "YOUR_CAPTCHA_ID";
    private static final String CAPTCHA_KEY = "YOUR_CAPTCHA_KEY";
    private static final String API_URL = "https://cap-global.geelabapi.com/validate";

    public ValidationResult validate(CaptchaRequest request) {
        try {
            // 生成签名
            String signToken = generateSignToken(
                request.getLotNumber(),
                CAPTCHA_KEY
            );

            // 构造请求参数
            String params = String.format(
                "lot_number=%s&captcha_output=%s&pass_token=%s&gen_time=%s&sign_token=%s",
                request.getLotNumber(),
                request.getCaptchaOutput(),
                request.getPassToken(),
                request.getGenTime(),
                signToken
            );

            String url = API_URL + "?captcha_id=" + CAPTCHA_ID;

            // 发起请求
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest httpRequest = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString(params))
                .timeout(Duration.ofSeconds(5))
                .build();

            HttpResponse<String> response = client.send(
                httpRequest,
                HttpResponse.BodyHandlers.ofString()
            );

            // 处理响应
            if (response.statusCode() != 200) {
                // 接口异常,建议放行
                return ValidationResult.fallback();
            }

            ObjectMapper mapper = new ObjectMapper();
            return mapper.readValue(
                response.body(),
                ValidationResult.class
            );

        } catch (Exception e) {
            // 请求异常,建议放行
            e.printStackTrace();
            return ValidationResult.fallback();
        }
    }

    private String generateSignToken(String lotNumber, String key)
        throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(
            key.getBytes("UTF-8"),
            "HmacSHA256"
        );
        mac.init(secretKey);

        byte[] hash = mac.doFinal(lotNumber.getBytes("UTF-8"));
        return bytesToHex(hash);
    }

    private String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }
}
PHP 完整示例
<?php
function validateCaptcha($requestData) {
    // 配置参数
    $CAPTCHA_ID = 'YOUR_CAPTCHA_ID';
    $CAPTCHA_KEY = 'YOUR_CAPTCHA_KEY';
    $API_URL = 'https://cap-global.geelabapi.com/validate';

    // 获取客户端参数
    $lot_number = $requestData['lot_number'];
    $captcha_output = $requestData['captcha_output'];
    $pass_token = $requestData['pass_token'];
    $gen_time = $requestData['gen_time'];

    // 生成签名
    $sign_token = hash_hmac('sha256', $lot_number, $CAPTCHA_KEY);

    // 构造请求参数
    $params = http_build_query([
        'lot_number' => $lot_number,
        'captcha_output' => $captcha_output,
        'pass_token' => $pass_token,
        'gen_time' => $gen_time,
        'sign_token' => $sign_token
    ]);

    $url = $API_URL . '?captcha_id=' . $CAPTCHA_ID;

    // 发起请求
    $context = stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => 'Content-Type: application/x-www-form-urlencoded',
            'content' => $params,
            'timeout' => 5
        ]
    ]);

    try {
        $response = @file_get_contents($url, false, $context);

        if ($response === false) {
            // 请求异常,建议放行
            return ['result' => 'success', 'reason' => 'request fail, fallback'];
        }

        $result = json_decode($response, true);
        return $result;

    } catch (Exception $e) {
        // 异常处理,建议放行
        error_log('验证异常: ' . $e->getMessage());
        return ['result' => 'success', 'reason' => 'exception, fallback'];
    }
}
?>
Go 完整示例
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "net/http"
    "net/url"
    "strings"
    "time"
)

const (
    CAPTCHA_ID  = "YOUR_CAPTCHA_ID"
    CAPTCHA_KEY = "YOUR_CAPTCHA_KEY"
    API_URL     = "https://cap-global.geelabapi.com/validate"
)

type ValidationResult struct {
    Status string                 `json:"status"`
    Result string                 `json:"result"`
    Reason string                 `json:"reason"`
    Args   map[string]interface{} `json:"captcha_args"`
}

func ValidateCaptcha(requestData map[string]string) (*ValidationResult, error) {
    // 获取客户端参数
    lotNumber := requestData["lot_number"]
    captchaOutput := requestData["captcha_output"]
    passToken := requestData["pass_token"]
    genTime := requestData["gen_time"]

    // 生成签名
    h := hmac.New(sha256.New, []byte(CAPTCHA_KEY))
    h.Write([]byte(lotNumber))
    signToken := hex.EncodeToString(h.Sum(nil))

    // 构造请求参数
    data := url.Values{}
    data.Set("lot_number", lotNumber)
    data.Set("captcha_output", captchaOutput)
    data.Set("pass_token", passToken)
    data.Set("gen_time", genTime)
    data.Set("sign_token", signToken)

    // 发起请求
    client := &http.Client{Timeout: 5 * time.Second}
    apiURL := API_URL + "?captcha_id=" + CAPTCHA_ID

    resp, err := client.Post(
        apiURL,
        "application/x-www-form-urlencoded",
        strings.NewReader(data.Encode()),
    )

    if err != nil || resp.StatusCode != 200 {
        // 请求异常,建议放行
        return &ValidationResult{
            Result: "success",
            Reason: "request fail, fallback",
        }, nil
    }

    defer resp.Body.Close()

    var result ValidationResult
    json.NewDecoder(resp.Body).Decode(&result)
    return &result, nil
}

错误码

常见错误码

错误码说明解决方法
-50005illegal gen_time检查 gen_time 是否为纯数字,检查请求格式
-50101not captcha_id缺少验证 ID
-50102illegal captcha_id验证 ID 格式错误
-50103not captcha验证 ID 不存在
-50301not proof流水号信息不存在,可能已过期
-50303illegal lot_number流水号格式错误
-50305lot_number Expired流水号已失效

完整错误码列表

错误码错误描述错误详情
-50000runtime error非正常流程导致的未定义错误
-50001illegal risk_type风控模式下传入非法的 risk_type 参数
-50002param decrypt error非法加密参数引起的解密流程异常
-50003illegal verify重复进行验证
-50004jsonp xssXSS 异常
-50005illegal gen_timegen_time 参数异常,检查二次校验参数及请求格式
-50101not captcha_id缺少验证 ID
-50102illegal captcha_id非法的验证 ID
-50103not captcha不存在的验证 ID
-50104captcha_id deleted已删除的验证 ID
-50105captcha_id paused已暂停使用的验证 ID
-50301not proof流水号信息不存在,缓存过期失效
-50302not lot_number缺少流水号
-50303illegal lot_number非法的流水号
-50304lot_number not match流水号不匹配
-50305lot_number Expired流水号已失效
-50306process_token Errorprocess_token 错误
-50307payload Errorpayload 参数错误
-50308payload Usedpayload 参数失效

常见问题

如何处理接口异常?

当请求异常或响应状态非 200 时,建议放行请求,避免阻塞业务流程。

try:
    response = requests.post(url, data=query, timeout=5)
    if response.status_code != 200:
        # 接口异常,放行请求
        return {'result': 'success', 'reason': 'api error, fallback'}
except Exception as e:
    # 请求异常,放行请求
    return {'result': 'success', 'reason': 'request fail, fallback'}

为什么返回 pass_token error?

常见原因: 前后端配置的 ID 不一致。

排查步骤:

  1. 检查前端使用的 captcha_id 是否正确
  2. 检查后端验证时使用的 captcha_id 是否与前端一致
  3. 确认 captcha_key 配置正确

如仍有疑问,可提供 lot_number 联系技术支持查询日志确认。

什么情况下报 -50005 错误?

原因: 参数传输格式有误。

检查清单:

  • gen_time 是否为纯数字字符串
  • Content-Type 是否为 application/x-www-form-urlencoded
  • 参数是否使用 form data 格式

如何设置合理的超时时间?

建议设置 5 秒超时时间,这是在用户体验和服务稳定性之间的最佳平衡点。

下一步