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_id | string | ✅ | 验证 ID,32 位字符串 |
lot_number | string | ✅ | 验证流水号,从客户端获取 |
captcha_output | string | ✅ | 验证输出信息,从客户端获取 |
pass_token | string | ✅ | 验证通过标识,从客户端获取 |
gen_time | string | ✅ | 验证通过时间戳,必须是纯数字字符串 |
sign_token | string | ✅ | 验证签名,使用 HMAC-SHA256 算法生成 |
gen_time 必须是纯数字字符串,不能包含其他字符,否则会返回 -50005 错误。
请求示例
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 算法生成,用于验证请求的真实性。
代码示例
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)const crypto = require('crypto');
function generateSignToken(lotNumber, captchaKey) {
return crypto
.createHmac('sha256', captchaKey)
.update(lotNumber)
.digest('hex');
}
// 使用示例
const signToken = generateSignToken(lotNumber, CAPTCHA_KEY);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
function generateSignToken($lotNumber, $captchaKey) {
return hash_hmac('sha256', $lotNumber, $captchaKey);
}
// 使用示例
$signToken = generateSignToken($lotNumber, $captchaKey);
?>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"
}
}当收到异常响应时,建议放行请求,避免阻塞业务流程。
响应字段说明
| 字段名 | 类型 | 说明 |
|---|---|---|
status | string | 请求状态:success(成功)、error(异常) |
result | string | 校验结果:success(通过)、fail(失败) |
reason | string | 校验结果说明或失败原因 |
code | string | 错误码(仅异常时返回) |
msg | string | 错误信息(仅异常时返回) |
captcha_args | object | 验证详细信息 |
captcha_args 字段
| 字段名 | 类型 | 说明 |
|---|---|---|
used_type | string | 使用的验证类型(slide、icon、九宫格等) |
user_ip | string | 用户 IP 地址 |
lot_number | string | 验证流水号 |
scene | string | 验证场景 |
referer | string | 来源页面 |
完整请求示例
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'}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' };
}
}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
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'];
}
}
?>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
}错误码
常见错误码
| 错误码 | 说明 | 解决方法 |
|---|---|---|
-50005 | illegal gen_time | 检查 gen_time 是否为纯数字,检查请求格式 |
-50101 | not captcha_id | 缺少验证 ID |
-50102 | illegal captcha_id | 验证 ID 格式错误 |
-50103 | not captcha | 验证 ID 不存在 |
-50301 | not proof | 流水号信息不存在,可能已过期 |
-50303 | illegal lot_number | 流水号格式错误 |
-50305 | lot_number Expired | 流水号已失效 |
完整错误码列表
| 错误码 | 错误描述 | 错误详情 |
|---|---|---|
-50000 | runtime error | 非正常流程导致的未定义错误 |
-50001 | illegal risk_type | 风控模式下传入非法的 risk_type 参数 |
-50002 | param decrypt error | 非法加密参数引起的解密流程异常 |
-50003 | illegal verify | 重复进行验证 |
-50004 | jsonp xss | XSS 异常 |
-50005 | illegal gen_time | gen_time 参数异常,检查二次校验参数及请求格式 |
-50101 | not captcha_id | 缺少验证 ID |
-50102 | illegal captcha_id | 非法的验证 ID |
-50103 | not captcha | 不存在的验证 ID |
-50104 | captcha_id deleted | 已删除的验证 ID |
-50105 | captcha_id paused | 已暂停使用的验证 ID |
-50301 | not proof | 流水号信息不存在,缓存过期失效 |
-50302 | not lot_number | 缺少流水号 |
-50303 | illegal lot_number | 非法的流水号 |
-50304 | lot_number not match | 流水号不匹配 |
-50305 | lot_number Expired | 流水号已失效 |
-50306 | process_token Error | process_token 错误 |
-50307 | payload Error | payload 参数错误 |
-50308 | payload Used | payload 参数失效 |
常见问题
如何处理接口异常?
当请求异常或响应状态非 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 不一致。
排查步骤:
- 检查前端使用的
captcha_id是否正确 - 检查后端验证时使用的
captcha_id是否与前端一致 - 确认
captcha_key配置正确
如仍有疑问,可提供 lot_number 联系技术支持查询日志确认。
什么情况下报 -50005 错误?
原因: 参数传输格式有误。
检查清单:
-
gen_time是否为纯数字字符串 - Content-Type 是否为
application/x-www-form-urlencoded - 参数是否使用 form data 格式
如何设置合理的超时时间?
建议设置 5 秒超时时间,这是在用户体验和服务稳定性之间的最佳平衡点。