REST API 본인인증은 PG 인증창 대신 가맹점이 직접 만든 화면으로 본인인증을 진행하는 방식이에요. 서버가 SMS OTP 발송(요청) → 재전송 → OTP 검증(승인)을 단계별로 호출해요.
자체 디자인의 입력 폼이 필요하거나 인증 흐름을 서버에서 통제해야 할 때 사용해요. 일반적인 경우에는 SDK 본인인증이 더 간단해요.
흐름
| 순서 | 호출 | 내용 |
|---|---|---|
| 1 | POST /v2/request/authentication |
사용자 정보로 인증을 요청하면 SMS OTP 발송 또는 PASS 앱 푸시 전송 |
| 2 | POST /v2/authenticate/realarm |
(선택) 사용자가 OTP를 못 받았을 때 SMS 재전송 |
| 3 | POST /v2/authenticate/confirm |
사용자가 입력한 OTP로 승인 → authenticate_data 수신 |
모든 호출은 서버에서 Basic Auth로 인증해요.
1본인인증 요청
https://api.bootpay.co.kr/v2/request/authenticationBasic Auth| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
pg |
String | 필수 | 본인인증 PG사. REST 방식은 다날만 지원해요. 다른 PG를 보내면 AUTH_CONFIRM_METHOD_NOT_FOUND(2406, HTTP 404)가 반환돼요 |
method |
String | 필수 | 인증 수단. 본인인증 |
order_name |
String | 필수 | 인증 주제명 |
authentication_id |
String | 필수 | 가맹점이 부여하는 본인인증 고유 ID |
username |
String | 필수 | 사용자 이름 |
identity_no |
String | 필수 | 생년월일 6자리(YYMMDD) + 주민번호 뒤 1자리 (예: 9112121 = 91.12.12 / 남성) |
phone |
String | 필수 | 휴대폰번호 |
carrier |
String | 필수 | 통신사. 아래 6개 값만 허용돼요SKT / KT / LGT / SKT_MVNO / KT_MVNO / LGT_MVNO (_MVNO는 알뜰폰) |
authenticate_type |
String | 선택 | sms(기본) 또는 pass(PASS 앱). 두 값 외에는 요청이 거부돼요 |
client_ip |
String | 선택 | 인증을 요청한 사용자의 IP. 생략하면 API를 호출한 서버의 IP가 대신 쓰여요. 최종 사용자의 IP를 넘기는 것을 권장해요 |
site_url |
String | 선택 | 서비스 웹 URL 또는 앱 이름. 부트페이가 강제하지는 않지만 다날 인증창 표기값(CPTITLE)으로 쓰이므로 채우는 것을 권장해요 |
metadata |
Object | 선택 | 가맹점 커스텀 데이터 |
user.username |
String | 선택 | 최상위 username을 생략했을 때 대신 사용돼요 |
user.phone |
String | 선택 | 최상위 phone을 생략했을 때 대신 사용돼요 |
extra.timeout은 결제창 만료 시각을 저장할 때만 쓰이고, 인증 승인·재전송·조회 어디에서도 검사하지 않아요. 조회 제한은 승인 완료 후 30분 고정이에요.
curl -X POST "https://api.bootpay.co.kr/v2/request/authentication" \
-H "Authorization: Basic $(printf '%s' 'CLIENT_KEY:SECRET_KEY' | base64)" \
-H "Content-Type: application/json" \
-d '{
"pg": "다날",
"method": "본인인증",
"order_name": "회원가입 본인확인",
"authentication_id": "auth_1700000000",
"username": "홍길동",
"identity_no": "9112121",
"phone": "01012345678",
"carrier": "SKT",
"authenticate_type": "sms",
"client_ip": "123.123.123.123"
}'bashimport { Bootpay } from '@bootpay/backend-js'
Bootpay.setConfiguration({
client_key: process.env.BOOTPAY_CLIENT_KEY,
secret_key: process.env.BOOTPAY_SECRET_KEY,
})
const response = await Bootpay.requestAuthentication({
pg: '다날',
method: '본인인증',
order_name: '회원가입 본인확인',
authentication_id: 'auth_' + Date.now(),
username: '홍길동',
identity_no: '9112121', // 생년월일 6자리 + 주민번호 뒤 1자리
phone: '01012345678',
carrier: 'SKT',
authenticate_type: 'sms', // sms | pass
client_ip: req.ip,
})
// response.receipt_id 를 세션/DB에 저장 → 승인 단계에서 사용
console.log(response.receipt_id, response.status) // status 51: 승인 대기javascript응답 예시
{
"receipt_id": "636375501fc19203724b3ad8",
"authentication_id": "1667462479",
"metadata": {},
"pg": "다날",
"method": "인증",
"method_symbol": "auth",
"method_origin": "인증",
"method_origin_symbol": "auth",
"requested_at": "2026-04-03T17:01:20+09:00",
"status": 51,
"authenticate_data": {
"tid": "202211031701204002337011",
"number_of_realarms": 0
}
}jsonstatus: 51은 SMS/PASS 발송이 끝나고 OTP 승인을 기다리는 상태예요. receipt_id를 보관했다가 승인 단계에서 사용해요.
51은 상태 한글 라벨이 정의돼 있지 않아 status_locale 키 자체가 응답에서 빠져요.
2SMS 재전송 (선택)
사용자가 OTP 문자를 받지 못한 경우 같은 receipt_id로 재전송해요. number_of_realarms로 재전송 횟수를 확인할 수 있어요.
https://api.bootpay.co.kr/v2/authenticate/realarmBasic Auth| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
receipt_id |
String | 필수 | 본인인증 요청 시 받은 receipt_id |
curl -X POST "https://api.bootpay.co.kr/v2/authenticate/realarm" \
-H "Authorization: Basic $(printf '%s' 'CLIENT_KEY:SECRET_KEY' | base64)" \
-H "Content-Type: application/json" \
-d '{ "receipt_id": "RECEIPT_ID" }'bashconst response = await Bootpay.realarmAuthentication(receiptId)
console.log(response.authenticate_data.number_of_realarms) // 재전송 횟수javascriptPG사에 따라 SMS 재전송 횟수에 제한이 있어요. number_of_realarms를 확인해 과도한 재전송을 막고, 일정 횟수 초과 시 처음부터 다시 요청하도록 안내해요.
카운터는 PG 응답을 확인하기 전에 증가해요. 그래서 PG 발송이 실패해 AUTH_REALARM_FAILED가 나도 number_of_realarms는 +1 된 상태예요. 재시도 로직을 짤 때 이 값을 "성공한 재전송 횟수"로 해석하면 안 돼요.
3본인인증 승인
사용자가 입력한 OTP로 인증을 확정해요. 성공하면 authenticate_data(이름·CI/DI 등)를 받아요.
https://api.bootpay.co.kr/v2/authenticate/confirmBasic Auth| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
receipt_id |
String | 필수 | 본인인증 요청 시 받은 receipt_id |
otp |
String | 선택 | SMS 방식은 사용자가 입력한 6자리 OTP. PASS 앱 방식은 빈 값(null) |
curl -X POST "https://api.bootpay.co.kr/v2/authenticate/confirm" \
-H "Authorization: Basic $(printf '%s' 'CLIENT_KEY:SECRET_KEY' | base64)" \
-H "Content-Type: application/json" \
-d '{ "receipt_id": "RECEIPT_ID", "otp": "123456" }'bash// SMS 방식: 사용자가 입력한 OTP 6자리 전달 / PASS 방식: null
const result = await Bootpay.confirmAuthentication(receiptId, otp)
if (result.status === 12) {
const { name, birth, gender, foreigner, carrier, unique, di } = result.authenticate_data
// CI(unique)·DI(di)는 암호화 저장
await saveVerifiedUser({ name, birth, gender, ci: encrypt(unique), di: encrypt(di) })
}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:07:58+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"
}
}jsonunique(CI)와 di(DI)는 개인 식별 민감정보예요. 평문 로그·프론트엔드 노출을 금지하고 암호화 저장해요.
에러 코드
인증·권한 관련 에러는 에러 코드표를 참고해요.
| 코드 | 의미 | 대처 방법 |
|---|---|---|
AUTH_NEED_PG_METHOD (2400) |
pg 또는 method가 비어 있습니다 |
두 값을 모두 보내요 |
AUTH_REQUEST_FAILED (2401) |
본인인증 요청 생성에 실패했습니다 | order_name·authentication_id 등 필수값을 확인해요 |
AUTH_CONFIRM_READY_FAILED (2402) |
PG로의 SMS/PASS 발송에 실패했습니다 | identity_no·phone·carrier 형식과 PG 활성화 여부를 확인해요. PG 응답 실패면 pg_error_code에 다날 코드가 들어오지만, carrier·identity_no·authenticate_type 로컬 검증에서 걸린 경우에는 pg_error_code가 없고 사유가 payload.message에만 담겨요 |
AUTH_NOT_READY (2404) |
승인 대기(status: 51) 상태가 아닙니다. 이미 승인 완료(status: 12)된 건을 다시 승인·재전송해도 같은 코드가 나와요 |
요청 단계부터 다시 진행해요 |
AUTH_CONFIRM_FAILED (2405) |
OTP 승인에 실패했습니다 (OTP 불일치·만료 포함). HTTP 500으로 응답돼요 | message/pg_error_code를 확인하고 재전송하거나 다시 요청해요 |
AUTH_CONFIRM_METHOD_NOT_FOUND (2406) |
해당 PG에 REST 본인인증 처리 로직이 없습니다 (HTTP 404) | REST 방식은 다날만 지원해요 |
AUTH_REALARM_NOT_ABLE (2412) |
SMS 방식이 아니어서 재전송할 수 없습니다 | PASS 방식은 재전송이 없어요 |
AUTH_REALARM_FAILED (2411) |
SMS 재전송에 실패했습니다. HTTP 500으로 응답돼요 | 처음부터 다시 요청해요 |
RC_NOT_FOUND (2000) |
receipt_id에 해당하는 인증 건이 없습니다 |
receipt_id를 확인해요 |
RC_NAME_BLANK (2003) |
order_name이 비어 있습니다 |
인증 주제명을 채워요 |
RC_O_ID_BLANK (2005) |
authentication_id가 비어 있습니다 (메시지도 authentication_id를 입력하라고 안내해요) |
가맹점 고유 인증 ID를 채워요 |
RC_PG_NOT_FOUND (2001) |
요청한 PG를 찾을 수 없습니다 | 관리자에서 본인인증 PG가 등록·활성화돼 있는지 확인해요 |
RC_PM_NOT_FOUND (2002) |
요청한 결제수단(method)을 찾을 수 없습니다 |
method: '본인인증'이 해당 PG에 열려 있는지 확인해요 |
RC_RESOURCE_NOT_CONFIG (2017) |
PG 연동 설정이 사용 가능한 상태가 아닙니다 | 관리자에서 본인인증 PG 설정을 확인해요 |
API_ONLY_SELLER (600) |
판매점 계정만 호출할 수 있습니다 | 연동키가 판매점 프로젝트의 것인지 확인해요 |
AUTH_REALARM_FAILED(2411)은 한글 메시지가 정의돼 있지 않아 message가 Translation missing... 형태로 내려올 수 있어요. 사용자에게 그대로 노출하지 말고, 분기와 로깅은 항상 error_code 기준으로 처리해요.
AUTH_CONFIRM_FAILED(2405)·AUTH_REALARM_FAILED(2411)은 HTTP 500으로 응답돼요. 4xx만 에러로 처리하는 클라이언트라면 5xx 응답 본문의 error_code도 함께 확인해야 해요.
