PHP Server SDK
공식 PHP 서버 측 SDK입니다. Packagist에 captchala/captchala-php 로 배포되어 있습니다.
SDK가 대신 처리해 주는 세 가지 작업:
- Validate — 브라우저 SDK에서 받은
pt_pass token 을 검증합니다. - Issue — 다가올 챌린지를 특정 액션 / IP / UID 에 바인딩하는 일회용
sct_server token 을 발급합니다. - Moderate — 대시보드가 사용하는 동일한 OpenAI 호환 파이프라인을 통한 멀티모달(텍스트 + 이미지) 콘텐츠 모더레이션을 수행합니다.
설치
composer require captchala/captchala-phpPHP ≥ 8.0 과 ext-json 이 필요합니다. cURL이 사용 가능하면 cURL을, 그렇지 않으면 file_get_contents 로 폴백합니다.
Validate (pt_ 토큰)
최종 사용자의 IP 를 세 번째 인자로 전달하십시오(CF-Connecting-IP / X-Forwarded-For, 최종적으로 REMOTE_ADDR 로 폴백). 선택 사항이지만 권장됩니다 — 추가 위험 검사에 사용됩니다.
use Captchala\Client;
$client = new Client('your_app_key', 'your_app_secret');
$result = $client->validate($_POST['captcha_token'], false, captchala_client_ip());
if (!$result->isValid()) {
http_response_code(400);
exit($result->getError()); // e.g. "token_expired"
}
// Verification passed; proceed with the request.
// $result->getCaptchaArgs() has platform / user_ip / referer / pkg / solved_at / risk_score
// Real end-user IP behind a CDN / proxy
function captchala_client_ip(): string {
foreach (['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'] as $h) {
if (!empty($_SERVER[$h])) {
$ip = trim(explode(',', $_SERVER[$h])[0]);
if (filter_var($ip, FILTER_VALIDATE_IP)) return $ip;
}
}
return '';
}가진 IP 가 없나요? $client->validate($_POST['captcha_token']) 를 사용하십시오 — 여전히 검증되며, IP 기반의 추가 위험 신호만 빠집니다.
원본 server_token 을 bind_uid 와 함께 발급했다면, 반환된 uid 를 실제로 기대하는 사용자와 비교하십시오:
if ($result->getUid() !== $expectedUserId) {
http_response_code(400);
exit('user mismatch');
}Server token 발급
가치가 높은 플로우(로그인, 회원가입, 결제)에서 권장되는 패턴은 다음과 같습니다: 백엔드가 일회용 sct_ 토큰을 발급하여 브라우저에 전달하고, 브라우저는 이를 serverToken prop 으로 전달합니다. 각 토큰은 단일 사용이며 액션에 한정되고, 발급 시점에 IP / UID 에 선택적으로 바인딩됩니다.
$issue = $client->issueServerToken(
action: 'login',
bindingIp: $request->ip(),
ttl: 300, // 초; 기본값 300
maxUses: 5, // SDK 재시도 예산
bindUid: $user->id, // 검증 시 ValidateResult::getUid() 와 매칭
);
if (!$issue->isOk()) {
return ['error' => $issue->getError()]; // rate_limit_exceeded, invalid_action, ...
}
return ['server_token' => $issue->getToken()]; // 브라우저로 전달콘텐츠 모더레이션
멀티모달 — OpenAI 호환 형식의 텍스트 및 이미지 URL 조합을 입력으로 받습니다:
$result = $client->moderationCheck([
['type' => 'text', 'text' => $userComment],
['type' => 'image_url', 'image_url' => ['url' => $uploadedImageUrl]],
], userId: $user->id);
if (!$result->isOk()) {
// 요청 오류: invalid_credentials, no_content, transport failure, ...
return ['error' => $result->getError()];
}
if ($result->isFlagged()) {
// 업스트림 모델이 플래그를 지정한 경우; 사유는 카테고리에서 확인
if ($result->hasCategory('violence', 'csam')) {
// 강제 차단
}
}순수 텍스트용 단축 메서드:
$result = $client->moderationText('user comment here', userId: $user->id);카테고리는 모델이 정의합니다(예: violence, hate, sexual, self-harm). 고정된 집합을 하드코딩하는 대신 getCategories() 를 방어적으로 순회하십시오.
결과 클래스
| Class | Methods |
|---|---|
ValidateResult | isValid(), getError(), getUid(), getChallengeId(), getAction(), isOffline(), isClientOnly(), getWarning(), toArray() |
IssueResult | isOk(), getToken(), getExpiresIn(), getIssuedAt(), getError(), getMessage(), toArray() |
ModerationResult | isOk(), isFlagged(), hasCategory(...$names), getCategories(), getContentType(), getRaw(), getError(), getMessage(), toArray() |