Checkify
개발자 문서

백엔드에서 확인

브라우저 포함이 완료된 후 서버는 가입, 결제 또는 보호된 작업을 허용하기 전에 사이트 API 키로 결과를 확인해야 합니다.

서버 인증 완료까지 5분 소요

  1. 페이지에 Checkify 브라우저 임베드를 추가하세요.
  2. 이 임베드는 Checkify 세션을 시작하고 검증 참조를 폼에 기록합니다.
  3. 백엔드에서 숨겨진 필드 값을 읽습니다.
  4. 백엔드에서 해당 값을 POST /v1/qr/results/verify 요청의 request_id로 전송합니다.
  5. 필요한 청구가 승인되면 보호 조치를 허용하십시오.
  6. 프런트엔드 상태만 절대 신뢰하지 마세요.

숨겨진 폼 필드의 이름은 checkify_token일 수 있지만, 실제 값은 Checkify request_id입니다.

전체 흐름

1. 사이트 API 키

를 생성하세요. 비즈니스 대시보드에서 개발자 를 열고 통합하려는 사이트에 대한 사이트 API 키를 생성하세요. 서버 환경 변수에만 저장하고 브라우저에는 절대 전달하지 마세요.

프런트엔드 SDK을 사용하시겠습니까?

GET /v1/qr/pass/{PASS_ID}/start를 수동으로 호출할 필요가 없습니다. Checkify JavaScript 임베드가 세션을 시작하고 request_id를 수신하여 폼에 자동으로 기록합니다.

백엔드는 다음 기능만 수행하면 됩니다.

2. 서버에서 request_id를 수신합니다.

프런트엔드 SDK은 사용자가 인증을 완료한 후 Checkify request_id를 숨겨진 폼 필드에 기록합니다. 기본 필드 이름은 checkify_token입니다. 이는 필드 이름일 뿐, 별도의 토큰 유형이 아닙니다. 해당 필드 값을 request_id로 인증 엔드포인트에 전송하세요.

모바일 앱 핸드오프 시 페이지 URL에 checkify_request_id가 반환될 수 있습니다. SDK은 페이지 로드 시 해당 ID를 읽어오지만, 서버는 여전히 동일한 request_id 값을 검증합니다.

프런트엔드에서 POST 폼을 보내주세요

{
  "email": "user@example.com",
  "checkify_token": "56a57761-ff5b-42f0-9c97-6c13e223e017"
}

백엔드에서 보낸 요청을 확인하세요

{
  "request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
  "required_claims": ["human_verified"],
  "required_fields": [],
  "consume": true
}

프런트엔드 통합은 수동으로만 가능합니다.

3. cURL을 이용한 수동 테스트

백엔드 개발자는 일반적으로 `/start`를 호출하지 않습니다. 이 섹션은 사용자 지정 프런트엔드를 구축하거나 임베드 없이 테스트할 때만 사용하십시오. zsh/bash에서 URL을 따옴표로 묶어야 `?`가 글로브 패턴으로 처리되지 않습니다.

/start를 실행하려면 Origin 헤더가 필요합니다.

Pass Start는 등록된 웹사이트 도메인에서만 작동합니다. 브라우저는 Origin을 자동으로 전송하지만 cURL은 그렇지 않습니다. 사이트 → 허용된 도메인에 등록된 호스트 이름을 사용하여 Origin(또는 X-Checkify-Site-Url)을 전송하십시오. 호스트 이름은 정확히 일치해야 합니다. checkify.me와 www.checkify.me는 서로 다릅니다.

가장 흔한 테스트 문제

/start 요청이 HTTP 403 오류를 반환하더라도 Pass ID는 여전히 유효할 수 있습니다. 일반적인 원인은 요청 도메인이 허용된 도메인 목록에 없거나, www.example.com이 등록되어 있지만 example.com이 사용되었거나(또는 그 반대의 경우)입니다.

통합 테스트를 위해 전용 테스트 사이트를 사용하고 Checkify 대시보드에서 Pass를 실행하세요. API 사이트 키는 csk_ 접두사를 사용하며, 별도의 테스트 키 형식은 없습니다. 성공 및 거부 흐름 모두를 확인하기 전까지는 프로덕션 체크아웃을 대상으로 테스트하지 마세요.

# Step A — start a session (replace PASS_ID and YOUR_REGISTERED_DOMAIN)
curl -sS \
  -H "Accept: application/json" \
  -H "Origin: https://YOUR_REGISTERED_DOMAIN" \
  "https://checkify.me/v1/qr/pass/chk_live_YOUR_PASS_ID/start?request_type=human"

# Response includes request_id and qr_url — open qr_url and complete verification

# Step B — verify on your server (after the user completes verification)
curl -sS -X POST "https://checkify.me/v1/qr/results/verify" \
  -H "Authorization: Bearer $CHECKIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "request_id": "PASTE_request_id_FROM_STEP_A",
    "required_claims": ["human_verified"],
    "consume": true
  }'

사이트 키 API은 검증 호출 시에만 사용하십시오. 검증 호출 전에 앱에서 검증을 완료하십시오. 그렇지 않으면 상태가 '대기 중'으로 표시됩니다.

사용 사례별 필수 클레임

required_claims를 보호 작업에 필요한 증명에 맞게 설정하세요. 클레임 이름은 해당 검증 세션에 대해 Checkify에서 승인한 이름과 일치해야 합니다. 연령 확인에는 age_over_{N} (예: age_over_18)이 사용됩니다. 임베드에서 일치하는 요청 유형을 요청하는 경우 10에서 110까지의 동적 임계값이 지원됩니다.

사용 사례 제안된 필수 클레임 메모
봇 / CAPTCHA 대체 ["human_verified"] 실제 사용자가 Checkify 흐름을 완료했음을 확인합니다.
13세 이상 관람가 콘텐츠 또는 청소년 대상 제품 ["age_over_13"] 이용권에서 age_over_13을 요구하는 경우에 사용하세요.
16세 이상 이용가 콘텐츠 또는 지역별 연령 제한 규정 ["age_over_16"] 이용권에서 age_over_16을 요구하는 경우에 사용하세요.
전자담배, 주류 또는 18세 이상 결제 ["age_over_18"] 제품 및 시장에 맞춰 연령 제한을 설정하세요.
21세 이상 구매 제한 상품 (해당되는 경우) ["age_over_21"] 패스에서 age_over_21을 요구하는 경우에 사용하세요.
챌린지 25 또는 그보다 더 엄격한 소매 정책 ["age_over_25"] 패스에서 age_over_25를 요구하는 경우에 사용하세요.
사용자 지정 연령 임계값 ["age_over_N"] 임베드 요청 유형에 맞춰 N이 10~110인 age_over_N을 사용하세요.

임베드 요청 유형 → 서버 클레임

브라우저 요청 유형은 서버 측에서 검증하는 클레임과 일치해야 합니다.

임베드 요청 유형필수 클레임
human["human_verified"]
age_over_13["age_over_13"]
age_over_16["age_over_16"]
age_over_18["age_over_18"]
age_over_21["age_over_21"]
age_over_25["age_over_25"]
age_over_N["age_over_N"] (N = 10–110)

선택 사항 필수 입력 항목

required_claims 외에, 통합이 수집한 경우 승인된 특정 신원 필드(예: 국가 또는 연령대)를 요구할 수 있습니다. required_fields에 필드 이름을 전달하세요. 승인 결과에 없으면 verify는 error.details의 missing_fields와 함께 missing_required_fields를 반환합니다.

4. 서버에서 verify를 호출하세요.

접근 권한을 부여하기 전에 사이트 API 키를 사용하여 POST /v1/qr/results/verify를 호출하십시오. Checkify에서 결과가 확인될 때까지 브라우저 참조(request_id 또는 기존 폴링 토큰)를 신뢰할 수 없는 것으로 간주하십시오.

POST https://checkify.me/v1/qr/results/verify
Authorization: Bearer YOUR_SITE_API_KEY
Content-Type: application/json

{
  "request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
  "required_claims": ["human_verified"],
  "consume": true
}

또한 보낼 수도 있습니다. token 대신에 request_id. API 키는 Checkify 사이트에 한정됩니다. 따라서 요청 본문에 site_id를 전송할 필요가 없습니다.

구현 예시

# checkify_token from your form POST is sent as request_id
curl -sS -X POST "https://checkify.me/v1/qr/results/verify" \
  -H "Authorization: Bearer $CHECKIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
    "required_claims": ["human_verified"],
    "consume": true
  }'

consume은 언제 사용해야 할까요?

최종 보호 작업에는 consume: true를 사용하십시오. — 결제, 회원가입, 비밀번호 재설정, 연령 제한 구매, 보호된 콘텐츠 접근

consume: false는 다음의 경우에만 사용하십시오. — 테스트, 디버깅 또는 최종 검증이 아닌 단계에서 결과를 나중에 다시 확인해야 할 경우

규제 대상 또는 고위험 작업의 경우, 동일한 검증 결과가 여러 보호 대상 결정에 재사용되지 않도록 consume: true를 사용하는 것이 좋습니다.

규제 조치에 대한 실패 마감

백엔드에서 Checkify에 연결할 수 없는 경우, 서버 측에서 확인된 결과 없이는 연령 제한 결제, 도박 접속, 성인 콘텐츠 접속, 전자담배 또는 주류 구매, 기타 보호된 작업을 허용하지 마십시오.

HTTP 타임아웃 시간을 짧게 설정하십시오(예: 10초). 일시적인 5xx 오류 또는 네트워크 오류가 발생하면 한두 번 재시도한 후 접근을 차단하십시오. 서버 측에서 오류를 기록하고 사용자에게 안전한 메시지를 표시하십시오. 내부 오류 세부 정보를 고객에게 노출하지 마십시오.

try {
  const verdict = await verifyCheckifyResult(requestId);

  if (!verdict.allow) {
    return res.status(403).json({ error: "Verification required" });
  }

  // Continue protected action
} catch (err) {
  console.error("Checkify verification unavailable", err);

  return res.status(403).json({
    error: "Verification is temporarily unavailable. Please try again.",
  });
}

서버 SDKs 및 툴링

The Checkify monorepo includes @checkify/server (Node.js) and checkify-server (Python) packages for server-side verify calls. There is no published OpenAPI spec or Postman collection yet — use the examples on this page and POST /v1/qr/results/verify directly.

@checkify/server v1.0.0이 npm에 게시되었습니다. Python checkify-server 패키지는 Checkify SDK 모노레포에 포함되어 있습니다.

결과 만료 기간

완료된 인증은 QR_RESULT_MAX_AGE_SECONDS(기본값 900초/15분) 후에 만료됩니다. 만료 후에는 verify 함수가 result_expired를 반환하고 사용자에게 다시 인증하도록 요청합니다.

상태 폴링 엔드포인트(사용자 지정 프런트엔드)

JavaScript 및 SDK은 표준 임베드의 세션 상태를 관리합니다. 이러한 기능은 GET /v1/qr/pass/{pass_id}/start를 수동으로 호출하는 사용자 지정 프런트엔드를 구축할 때만 사용하십시오.

응답 처리

성공 시, Checkify는 HTTP 200 응답 코드와 success: true, status: completed를 반환합니다. 필수 클레임(예: human_verified: true)이 존재하는 경우에만 접근을 허용합니다.

{
  "success": true,
  "status": "completed",
  "message": "Verification result confirmed",
  "request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
  "site_id": "YOUR_SITE_ID",
  "business_id": "YOUR_BUSINESS_ID",
  "approved_claims": {
    "human_verified": true
  },
  "approved_fields": [],
  "signed_result": {
    "payload": { "...": "..." },
    "signature": "...",
    "signature_algorithm": "EdDSA",
    "key_id": "checkify:default"
  }
}

서명된 결과

signed_result 객체를 통해 백엔드는 검증 시점에 Checkify가 필요한 클레임을 승인했다는 변조 방지 감사 기록을 보관할 수 있습니다. 대부분의 통합은 approved_claims만 필요합니다. 규제 대상이거나 고위험 비즈니스는 signed_result도 저장할 수 있습니다. 중첩된 proof_metadata에는 암호/운영 증거(assurance 메타데이터, 증거 유형, 동의 해시 등)가 포함될 수 있으며 신분증이나 원시 생년월일은 포함되지 않습니다. 중첩 assurance 필드를 인증이나 신뢰 보장으로 취급하지 마세요. certification_status와 자체 정책을 확인하세요. 필요한 것보다 더 많은 개인정보를 저장하지 마세요.

고객이 앱에서 아직 작업을 완료하지 않은 경우, Checkify은 HTTP 200 응답 코드와 성공 여부(success: false), 상태(status: pending)를 반환합니다. 보호된 작업은 거부하고 사용자에게 인증을 완료하도록 요청하십시오.

{
  "success": false,
  "status": "pending",
  "message": "Verification is not completed yet",
  "request_id": "56a57761-ff5b-42f0-9c97-6c13e223e017",
  "approved_claims": {},
  "signed_result": null
}
필드의미
successtrue 확인 완료 시 및 요구 사항 일치
statuscompleted 또는 pending
approved_claims클레임 Checkify 승인됨, 예: human_verified: true
signed_result감사 추적을 위한 선택적 서명 페이로드

JSON 오류 페이로드

검증을 진행할 수 없는 경우, Checkify은 구조화된 JSON 본문과 함께 HTTP 4xx/5xx 오류를 반환합니다. 서버 측에서 error.code를 확인하고 error.details를 로그에 기록합니다. 최종 사용자에게는 일반적인 메시지를 반환합니다.

{
  "success": false,
  "error": {
    "code": "verification_failed",
    "message": "The verification did not include all required claims.",
    "details": {
      "missing_claims": ["age_over_18"]
    }
  }
}
암호 HTTP 의미 권장 조치
missing_authorization401Authorization 헤더가 전송되지 않았습니다.서버 측 코드에서만 전송 인증: Bearer YOUR_SITE_API_KEY를 전송하십시오.
invalid_token401베어러 토큰이 없거나, 형식이 잘못되었거나, 유효한 사이트 API 키가 아닙니다.비즈니스 대시보드에서 키를 확인하고 환경 변수에 저장하세요.
expired_token401사이트 API 키가 취소되었습니다.새 사이트 키 API을 생성하고 서버에서 해당 키를 주기적으로 변경하십시오.
missing_required_field400 / 422JSON 본문에 request_id 또는 token이 누락되었습니다.숨겨진 폼 필드의 request_id를 전달하거나, 연동에서 여전히 폴링 토큰을 사용하는 경우 해당 토큰을 전달하세요.
invalid_request_id400참조를 구문 분석할 수 없거나 정규화 후 비어 있습니다.프런트엔드에서 Checkify request_id를 변경하지 않고 제출하는지 확인하십시오.
result_not_found404해당 참조에 대한 확인 요청이 없습니다.해당 작업을 거부합니다. 사용자가 숨겨진 필드를 조작했거나 이전 세션을 제출했을 수 있습니다.
result_expired410검증이 너무 오래전에 완료되어 이 조치를 신뢰할 수 없습니다.사용자에게 다시 스캔하도록 요청하고 새 request_id로 verify를 호출하십시오.
verification_failed403 / 409사이트/비즈니스 불일치, 이미 소비된 결과(HTTP 409) 또는 일반 검증 실패.액세스를 거부하세요. error.details를 확인하고, 클레임/필드 누락은 missing_required_claims와 missing_required_fields를 참고하세요.
missing_required_claims403developers_server.err_missing_required_claims_meaningdevelopers_server.err_missing_required_claims_action
missing_required_fields403developers_server.err_missing_required_fields_meaningdevelopers_server.err_missing_required_fields_action
unknown_required_attributes400developers_server.err_unknown_required_attributes_meaningdevelopers_server.err_unknown_required_attributes_action
business_not_operational403비즈니스 계정이 잠겨 있거나, 보관 처리되었거나, 사용 중이 아닙니다.사업주 또는 Checkify 지원팀에 문의하십시오. 계정이 활성화될 때까지 보호된 작업을 허용하지 마십시오.
validation_errors422JSON 본문이 스키마 유효성 검사에 실패했습니다(HTTP 422).오류 세부 정보(error.details.validation_errors)에서 필드 수준 메시지를 확인하십시오. 재시도하기 전에 request_id, required_claims 또는 required_fields 유형을 수정하십시오.
rate_limited429짧은 시간 내에 너무 많은 인증 요청이 있었습니다.지수 백오프를 사용하여 재시도합니다. 모든 페이지 보기에서가 아니라 보호된 작업에서만 검증합니다.
server_error500+일시적인 서버 문제로 인해 Checkify 인증을 완료할 수 없습니다.한두 번 재시도하고, 그래도 안 되면 종료하고 해당 사건을 기록하세요.

웹훅 및 비동기 검증

현재 서버 검증은 동기식으로 이루어집니다. 사용자가 보호된 작업을 제출할 때 백엔드에서 request_id를 검증합니다. 결제, 회원가입 및 접근 제어 흐름에는 이 방식이 권장됩니다. 일반적인 통합에는 범용 검증 웹훅이 필요하지 않습니다. GoHighLevel 및 기타 파트너 통합의 경우 비즈니스 대시보드에서 별도의 아웃바운드 웹훅 구성을 사용할 수 있습니다.

흔히 저지르는 실수

보안 알림

숨겨진 필드를 증거가 아닌 신뢰할 수 없는 참조로 취급하십시오. 액세스 권한을 부여하기 전에 항상 서버에서 사이트 API 키를 확인하세요. 사용 consume: true

다음 단계