SDK 본인인증은 프론트엔드에서 requestAuthentication()을 호출하면 PG가 제공하는 인증창(통신사 선택·OTP 입력)을 띄우고, 인증이 끝나면 receipt_id를 돌려주는 방식이에요. 가장 일반적인 본인인증 연동이에요.
이후 서버에서 그 receipt_id로 결과를 조회해 이름·CI/DI 같은 값을 확정해요.
핵심 요약
- 프론트엔드
requestAuthentication()→ 인증창 표시 →done이벤트로receipt_id수신 receipt_id를 서버로 보내GET /v2/certificate/{receipt_id}로 결과를 다시 조회- 조회 응답의
authenticate_data(이름·CI/DI 등)를 검증한 뒤 회원·주문 흐름에 반영 - 프론트엔드 결과만 믿고 실명·성인 여부를 확정하지 않아요
1인증창 호출 (프론트엔드)
import { Bootpay } from '@bootpay/client-js'
async function requestAuthentication() {
try {
const response = await Bootpay.requestAuthentication({
client_key: '[ Client Key ]',
pg: '다날',
order_name: '본인인증', // 인증창에 표시될 이름
authentication_id: 'auth_' + Date.now(), // 가맹점이 부여하는 고유 본인인증 ID
user: {
username: '홍길동', // 선입력 시 사용자가 다시 입력하지 않아도 됨 (선택)
phone: '01012345678'
}
})
// 인증 완료 → receipt_id를 서버로 보내 결과를 확정
if (response.event === 'done') {
await confirmIdentity(response.data.receipt_id)
}
} catch (e) {
console.log(e.message) // 사용자가 인증창을 닫거나 인증에 실패한 경우
}
}
async function confirmIdentity(receiptId) {
await fetch('/api/identity/confirm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ receipt_id: receiptId })
})
}javascript앱 SDK도 동일한 payload(pg, order_name, authentication_id, user)로 본인인증을 요청해요. 네이티브 SDK의 초기화·client_key 설정 방법은 결제창 통합 가이드의 플랫폼별 설정과 같아요. 인증 완료 콜백에서 받은 receipt_id를 서버로 전달하면 이후 처리는 동일해요.
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
client_key |
String | 필수 | Client Key. 관리자 > 개발자 설정 > API 연동키에서 확인 |
pg |
String | 필수 | 본인인증 PG사 (예: 다날) |
order_name |
String | 필수 | 인증창에 표시할 이름 |
authentication_id |
String | 필수 | 가맹점이 부여하는 본인인증 고유 ID (중복 요청 추적용) |
method |
String | 선택 | 인증 수단. 생략하면 본인인증으로 처리돼요 |
user.username |
String | 선택 | 이름 선입력값 |
user.phone |
String | 선택 | 휴대폰번호 선입력값 |
metadata |
Object | 선택 | 가맹점 커스텀 데이터. 결과 조회 응답에 그대로 되돌아와요 |
extra |
Object | 선택 | 추가 옵션. extra.carrier로 다날 인증창의 통신사를 고정할 수 있어요 |
인증 완료 이벤트 데이터
done 이벤트로 받는 데이터는 결과 조회를 위한 최소 정보예요. 실명·CI/DI 같은 값은 들어있지 않으므로 서버 조회가 필요해요.
{
"event": "done",
"data": {
"receipt_id": "624ce9d31fc19202e4746f3b",
"authentication_id": "1649207763202",
"gateway_url": "https://gw.bootpay.co.kr",
"metadata": {},
"pg": "다날",
"method": "인증",
"method_symbol": "auth",
"method_origin": "인증",
"method_origin_symbol": "auth",
"authenticated_at": "2026-04-06T10:16:20+09:00",
"requested_at": "2026-04-06T10:16:03+09:00",
"status_locale": "본인인증완료",
"status": 12
}
}json2결과 조회 (서버)
프론트엔드에서 받은 receipt_id로 서버에서 인증 결과를 조회해요. 이 응답의 authenticate_data가 신뢰할 수 있는 최종 결과예요.
https://api.bootpay.co.kr/v2/certificate/{receipt_id}Basic Auth| 파라미터 | 위치 | 필수 | 설명 |
|---|---|---|---|
receipt_id |
Path | 필수 | 프론트엔드 done 이벤트에서 받은 본인인증 영수증 ID |
본인인증 결과는 인증 완료 후 30분 이내에 조회해야 안전해요. 프론트엔드에서 receipt_id를 받으면 곧바로 서버로 보내 조회·저장하는 것을 권장해요.
curl -X GET "https://api.bootpay.co.kr/v2/certificate/RECEIPT_ID" \
-H "Authorization: Basic $(printf '%s' 'CLIENT_KEY:SECRET_KEY' | base64)"bashimport { Bootpay } from '@bootpay/backend-js'
Bootpay.setConfiguration({
client_key: process.env.BOOTPAY_CLIENT_KEY,
secret_key: process.env.BOOTPAY_SECRET_KEY,
})
export async function confirmIdentity(receiptId) {
const result = await Bootpay.certificate(receiptId)
if (result.status !== 12) {
throw new Error('본인인증이 완료되지 않았습니다')
}
const { name, birth, gender, foreigner, carrier, unique, di, phone } = result.authenticate_data
// CI(unique)·DI(di)는 개인정보이므로 암호화하여 저장해요
await db.users.update({
verified_name: name,
verified_birth: birth,
verified_ci: encrypt(unique),
verified_di: encrypt(di),
verified_at: new Date(),
}, { where: { phone } })
return result.authenticate_data
}javascript응답 예시
{
"receipt_id": "624d2e531fc19202e4746f40",
"authentication_id": "1649225299700",
"gateway_url": "https://gw.bootpay.co.kr",
"metadata": {},
"pg": "다날",
"method": "인증",
"method_symbol": "auth",
"method_origin": "인증",
"method_origin_symbol": "auth",
"authenticated_at": "2026-04-06T15:08:42+09:00",
"requested_at": "2026-04-06T15:08:19+09:00",
"status_locale": "본인인증완료",
"status": 12,
"authenticate_data": {
"phone": "01012345678",
"name": "홍길동",
"birth": "19911212",
"gender": 0,
"foreigner": "0",
"carrier": "SKT",
"unique": "[ CI 값 ]",
"di": "[ DI 값 ]",
"tid": "202204061508210451927011"
}
}jsonauthenticate_data 필드의 의미는 본인인증 개요를 참고해요.
unique(CI)와 di(DI)는 개인을 식별하는 민감정보예요. 평문으로 로그에 남기거나 프론트엔드로 내려보내지 말고, 저장 시 반드시 암호화해요.
에러 코드
인증·권한 관련 에러는 에러 코드표를 참고해요.
| 코드 | 의미 | 대처 방법 |
|---|---|---|
AUTH_NOT_FOUND (2407) |
receipt_id에 해당하는 본인인증 건이 없습니다 |
receipt_id가 올바른지 확인해요 |
AUTH_NOT_CONFIRMED (2408) |
본인인증이 완료(status: 12)되지 않았습니다 |
인증창이 끝난 뒤 조회해요 |
AUTH_EXPIRED (2409) |
인증 완료 후 30분이 지나 조회할 수 없습니다 | 인증 직후에 곧바로 조회·저장해요 |
RC_NOT_AUTH (2064) |
본인인증 건이 아닙니다 (결제·정기결제 영수증) | 본인인증으로 발급된 receipt_id인지 확인해요 |
API_ONLY_SELLER (600) |
판매점 계정만 호출할 수 있습니다 | 연동키가 판매점 프로젝트의 것인지 확인해요 |
조회 API는 status: 12 여부(AUTH_NOT_CONFIRMED)를 RC_NOT_AUTH보다 먼저 검사해요. 그래서 결제 건의 receipt_id를 잘못 넣으면 "본인인증 건이 아니다"(2064)가 아니라 AUTH_NOT_CONFIRMED(2408) 가 반환돼요. 2408을 받았다고 무조건 "인증이 아직 안 끝났다"고 해석하지 말고, 보낸 receipt_id의 출처도 함께 확인해요.
에러 응답에는 error_code·message·pg_error_code와 함께 payload가 들어와요. error_code는 숫자가 아니라 상수명 문자열(예: "AUTH_NOT_CONFIRMED")로 내려오므로, 분기 조건도 문자열로 비교해요. 위 표의 괄호 안 숫자는 내부 코드값이라 응답 본문에는 나타나지 않아요.
다음 단계
- 인증 화면을 직접 구성하거나 OTP를 단계별로 제어하려면 REST API 본인인증을 확인해요.
- 결과값(CI/DI) 저장 설계는 데이터 모델 설계를 참고해요.
