Skip to content

快速接入 ​

Corptcha 是免安装、无侵入的人机验证服务:在你的网页中放置一个容器、引入 SDK 并初始化,用户完成验证后回调会返回一次性验证令牌(token)。将 token 随表单提交给后端,由后端向验证服务确认后即可放行。

第 1 步:创建站点,获取 Site ID ​

在控制台的「站点管理」中创建站点,即可获得 Site ID(形如 cpt_xxxxxxxxxxxx)。

Site ID 是公开的,可以安全地出现在前端代码里;请勿泄露密钥 Secret。

第 2 步:引入 SDK ​

在页面中引入 SDK 文件(仅一个脚本,无任何其它依赖),SDK 托管于国内静态资源 CDN。

html
<!-- 放在 </body> 之前,或使用 defer 属性 -->
<script src="https://res.25y.cn/corptcha/corptcha.iife.js"></script>

SDK 文件名固定不变,接入方无需因升级而改动代码。每次发版上传新文件后,请在 CDN 控制台刷新该 URL 的缓存(或将其缓存时间设短,如 no-cache / max-age=60)。自建部署时可改用验证服务自带的 /widget/corptcha.iife.js,或通过 npm install @corptcha/widget-sdk 本地打包引入。

第 3 步:渲染验证码 ​

准备一个容器节点并调用 Corptcha.render() 初始化。

html
<div id="corptcha-widget"></div>
<input type="hidden" id="captcha-token" name="captchaToken" />
js
const widget = Corptcha.render(document.querySelector('#corptcha-widget'), {
  apiBaseUrl: 'https://cpt-api.25y.cn',     // 验证服务 API 地址
  siteKey: '你的 Site ID',                  // 形如 cpt_xxxxxxxxxxxx
  purpose: 'login',                         // 场景标识:login / register / comment 等
  language: 'zh-CN',
  theme: { mode: 'auto' },                  // light / dark / auto,可自定义 accentColor
  onSuccess: (token) => {
    // 验证通过:把 token 写入表单,随请求提交给后端
    document.querySelector('#captcha-token').value = token;
  },
  onError: (error) => {
    console.error(error.errorCode, error.message);
  },
  onExpired: () => {
    // token 过期,需要重新验证
    widget.execute();
  },
});

第 4 步:后端校验 token ​

前端获取的验证令牌是一次性的,请连同业务请求一起提交给后端。后端调用验证服务核验令牌通过后再执行业务逻辑,不要信任前端传递的任何布尔值。

校验接口(所有语言通用):

  • 接口:POST {apiBaseUrl}/v1/verify
  • 鉴权:请求头 Authorization: Bearer {Secret}(控制台「站点管理」中的站点密钥)
  • 请求体:{ "token": "...", "purpose": "login", "siteKey": "cpt_xxx" }
  • 成功响应:HTTP 200,{ "success": true, "siteId": "...", "purpose": "login", "issuedAt": "...", "riskLevel": "low|medium|high|critical", "requestId": "..." }
  • 失败响应:HTTP 4xx,{ "errorCode": "TOKEN_NOT_FOUND", "message": "...", "requestId": "..." }

常见错误码:TOKEN_NOT_FOUND(无效/已使用)、TOKEN_EXPIRED(已过期)、TOKEN_PURPOSE_MISMATCH(用途不匹配)、UNAUTHORIZED(Secret 错误)。

以下各语言示例任选其一,逻辑完全一致:调用核验接口 → 非 200 或 success !== true 即拒绝请求。

Node.js / TypeScript ​

js
// 建议放在服务端,Node 18+ 原生支持 fetch
const VERIFY_URL = 'https://cpt-api.25y.cn/v1/verify';

async function verifyCaptcha(token, purpose, siteKey, secret) {
  const response = await fetch(VERIFY_URL, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      authorization: `Bearer ${secret}`,
    },
    body: JSON.stringify({ token, purpose, siteKey }),
  });

  const result = await response.json();
  if (!response.ok || result.success !== true) {
    throw new Error(`人机验证未通过:${result.errorCode ?? 'UNKNOWN'}`);
  }
  return result; // result.siteId / result.purpose / result.riskLevel
}

Python ​

python
import requests

VERIFY_URL = "https://cpt-api.25y.cn/v1/verify"

def verify_captcha(token: str, purpose: str, site_key: str, secret: str) -> dict:
    response = requests.post(
        VERIFY_URL,
        headers={"content-type": "application/json",
                 "authorization": f"Bearer {secret}"},
        json={"token": token, "purpose": purpose, "siteKey": site_key},
        timeout=5,
    )
    result = response.json()
    if response.status_code != 200 or result.get("success") is not True:
        raise ValueError(f"人机验证未通过:{result.get('errorCode', 'UNKNOWN')}")
    return result  # result["siteId"] / result["purpose"] / result["riskLevel"]

Java ​

java
// 依赖:okhttp 4.x + gson(或任何 JSON 库)
import okhttp3.*;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class CaptchaVerifier {
    private static final String VERIFY_URL = "https://cpt-api.25y.cn/v1/verify";

    public static JsonObject verifyCaptcha(String token, String purpose,
                                           String siteKey, String secret) throws Exception {
        OkHttpClient client = new OkHttpClient();
        JsonObject body = new JsonObject();
        body.addProperty("token", token);
        body.addProperty("purpose", purpose);
        body.addProperty("siteKey", siteKey);

        Request request = new Request.Builder()
                .url(VERIFY_URL)
                .post(RequestBody.create(body.toString(), MediaType.get("application/json")))
                .header("authorization", "Bearer " + secret)
                .build();

        try (Response response = client.newCall(request).execute()) {
            JsonObject result = JsonParser.parseString(
                    response.body().string()).getAsJsonObject();
            if (!response.isSuccessful() || !result.get("success").getAsBoolean()) {
                String code = result.has("errorCode")
                        ? result.get("errorCode").getAsString() : "UNKNOWN";
                throw new IllegalStateException("人机验证未通过:" + code);
            }
            return result; // siteId / purpose / riskLevel
        }
    }
}

Go ​

go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

const verifyURL = "https://cpt-api.25y.cn/v1/verify"

type verifyResult struct {
	Success   bool   `json:"success"`
	SiteID    string `json:"siteId"`
	Purpose   string `json:"purpose"`
	RiskLevel string `json:"riskLevel"`
	ErrorCode string `json:"errorCode"`
}

func verifyCaptcha(token, purpose, siteKey, secret string) (*verifyResult, error) {
	payload, _ := json.Marshal(map[string]string{
		"token": token, "purpose": purpose, "siteKey": siteKey,
	})
	req, err := http.NewRequest(http.MethodPost, verifyURL, bytes.NewReader(payload))
	if err != nil {
		return nil, err
	}
	req.Header.Set("content-type", "application/json")
	req.Header.Set("authorization", "Bearer "+secret)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var result verifyResult
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK || !result.Success {
		return nil, fmt.Errorf("人机验证未通过:%s", result.ErrorCode)
	}
	return &result, nil
}

PHP ​

php
<?php
// 依赖:cURL 扩展(通常默认开启)

function verifyCaptcha(string $token, string $purpose, string $siteKey, string $secret): array {
    $ch = curl_init('https://cpt-api.25y.cn/v1/verify');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode([
            'token' => $token,
            'purpose' => $purpose,
            'siteKey' => $siteKey,
        ]),
        CURLOPT_HTTPHEADER => [
            'content-type: application/json',
            'authorization: Bearer ' . $secret,
        ],
        CURLOPT_TIMEOUT => 5,
    ]);

    $response = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $result = json_decode($response, true);
    if ($status !== 200 || ($result['success'] ?? false) !== true) {
        throw new RuntimeException('人机验证未通过:' . ($result['errorCode'] ?? 'UNKNOWN'));
    }
    return $result; // siteId / purpose / riskLevel
}

token 只能被成功核验一次:重复使用同一个 token 会返回 TOKEN_NOT_FOUND。建议将验证与业务操作放在同一个请求流程中,失败时直接中断业务。

Corptcha 人机验证