본인확인

가장 보편적인 본인확인 방식으로 고객이 입력한 개인정보와 토스에 가입된 정보를 대조하고 토스 앱 인증을 통해 신원확인을 합니다.

고객의 CI/DI가 필요한 회원가입, 로그인, 계정조회 등 휴대폰 본인 확인 대신으로 사용이 가능합니다.

토스인증 흐름

1. Access Token 발급 API

서버 인증을 위한 Access Token을 발급받는 API 입니다. 인증 방식 살펴보기

발급받은 Access Token은 모든 서비스 API 호출 시, 인증 용도로 사용됩니다. Access Token은 만료시간을 가지며, 토큰이 만료되면 유효한 토큰을 다시 발급받아 사용해야 합니다.

이미 유효한 토큰을 가지고 있는 상태에서 새로운 토큰 발급 API 를 반복 호출하지 마세요.

EndPoint

POST /token

요청

curl --request POST 'https://oauth2.cert.toss.im/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=test_a8e23336d673ca70922b485fe806eb2d' \
--data-urlencode 'client_secret=test_418087247d66da09fda1964dc4734e453c7cf66a7a9e3' \
--data-urlencode 'scope=ca'

요청 헤더

  • Content-Type 필수 · String

    application/x-www-form-urlencoded

요청 파라미터

  • grant_type 필수

    client_credentials

  • scope 필수

    ca

  • client_id 필수

    고객사에 발급된 클라이언트 아이디

  • client_secret 필수

    고객사에 발급된 클라이언트 시크릿

응답

{
"access_token": "eyJraWQiOiJjZXJ0IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ0ZXN0X2E4ZTIzMzM2ZDY3M2NhNzA5MjJiNDg1ZmU4MDZlYjJkIiwiYXVkIjoidGVzdF9hOGUyMzMzNmQ2NzNjYTcwOTIyYjQ4NWZlODA2ZWIyZCIsIm5iZiI6MTY0OTIyMjk3OCwic2NvcGUiOlsiY2EiXSwiaXNzIjoiaHR0cHM6XC9cL2NlcnQudG9zcy5pbSIsImV4cCI6MTY4MDc1ODk3OCwiaWF0IjoxNjQ5MjIyOTc4LCJqdGkiOiI4MDNjNDBjOC1iMzUxLTRmOGItYTIxNC1iNjc5MmNjMzBhYTcifQ.cjDZ0lAXbuf-KAgi3FlG1YGxvgvT3xrOYKDTstfbUz6CoNQgvd9TqI6RmsGZuona9jIP6H12Z1Xb07RIfAVoTK-J9iC5_Yp8ZDdcalsMNj51pPP8wso86rn-mKsrx1J5Rdi3GU58iKt0zGr4KzqSxUJkul9G4rY03KInwvl692HU19kYA9y8uTI4bBX--UPfQ02G0QH9HGTPHs7lZsISDtyD8sB2ikz5p7roua7U467xWy4BnRleCEWO2uUaNNGnwd7SvbjhmsRZqohs9KzDUsFjVhSiRNdHL53XJQ5zFHwDF92inRZFLu6Dw8xttPtNHwAD1kT84uXJcVMfEHtwkQ",
"scope": "ca",
"token_type": "Bearer",
"expires_in": 31536000
}
JSON

응답 파라미터

  • access_token String

    Access Token 값

  • expires_in Number

    Access Token 만료 시간(초 단위)

    인터넷 통신 방식의 경우 접근토큰 수명은 기본 3600초(1시간)로 제공합니다.

    고객사에서 선택한 통신 방식에 따라 차이가 있을 수 있으며, 토스 테스트 자격증명 정보는 연동의 편의를 위해 31536000초(1년)로 제공됩니다.

2. 본인확인 요청 API

본인확인을 시작하기 위한 API로서 토스 인증 서버에서 txId를 발급합니다.

EndPoint

POST /api/v2/sign/user/auth/id/request

Case1. 표준창을 이용해서 인증 요청하기

curl --location --request POST 'https://cert.toss.im/api/v2/sign/user/auth/id/request' \
--header 'Authorization: Bearer eyJraWQiOiJjZXJ0IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ0ZXN0X2E4ZTIzMzM2ZDY3M2NhNzA5MjJiNDg1ZmU4MDZlYjJkIiwiYXVkIjoidGVzdF9hOGUyMzMzNmQ2NzNjYTcwOTIyYjQ4NWZlODA2ZWIyZCIsIm5iZiI6MTY0OTIyMjk3OCwic2NvcGUiOlsiY2EiXSwiaXNzIjoiaHR0cHM6XC9cL2NlcnQudG9zcy5pbSIsImV4cCI6MTY4MDc1ODk3OCwiaWF0IjoxNjQ5MjIyOTc4LCJqdGkiOiI4MDNjNDBjOC1iMzUxLTRmOGItYTIxNC1iNjc5MmNjMzBhYTcifQ.cjDZ0lAXbuf-KAgi3FlG1YGxvgvT3xrOYKDTstfbUz6CoNQgvd9TqI6RmsGZuona9jIP6H12Z1Xb07RIfAVoTK-J9iC5_Yp8ZDdcalsMNj51pPP8wso86rn-mKsrx1J5Rdi3GU58iKt0zGr4KzqSxUJkul9G4rY03KInwvl692HU19kYA9y8uTI4bBX--UPfQ02G0QH9HGTPHs7lZsISDtyD8sB2ikz5p7roua7U467xWy4BnRleCEWO2uUaNNGnwd7SvbjhmsRZqohs9KzDUsFjVhSiRNdHL53XJQ5zFHwDF92inRZFLu6Dw8xttPtNHwAD1kT84uXJcVMfEHtwkQ' \
--header 'Content-Type: application/json' \
--data-raw '{
       "requestType" : "USER_NONE"
    }'

Case 2. 앱 푸쉬를 이용해서 인증 요청하기

curl --location --request POST 'https://cert.toss.im/api/v2/sign/user/auth/id/request' \
--header 'Authorization: Bearer eyJraWQiOiJjZXJ0IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ0ZXN0X2E4ZTIzMzM2ZDY3M2NhNzA5MjJiNDg1ZmU4MDZlYjJkIiwiYXVkIjoidGVzdF9hOGUyMzMzNmQ2NzNjYTcwOTIyYjQ4NWZlODA2ZWIyZCIsIm5iZiI6MTY0OTIyMjk3OCwic2NvcGUiOlsiY2EiXSwiaXNzIjoiaHR0cHM6XC9cL2NlcnQudG9zcy5pbSIsImV4cCI6MTY4MDc1ODk3OCwiaWF0IjoxNjQ5MjIyOTc4LCJqdGkiOiI4MDNjNDBjOC1iMzUxLTRmOGItYTIxNC1iNjc5MmNjMzBhYTcifQ.cjDZ0lAXbuf-KAgi3FlG1YGxvgvT3xrOYKDTstfbUz6CoNQgvd9TqI6RmsGZuona9jIP6H12Z1Xb07RIfAVoTK-J9iC5_Yp8ZDdcalsMNj51pPP8wso86rn-mKsrx1J5Rdi3GU58iKt0zGr4KzqSxUJkul9G4rY03KInwvl692HU19kYA9y8uTI4bBX--UPfQ02G0QH9HGTPHs7lZsISDtyD8sB2ikz5p7roua7U467xWy4BnRleCEWO2uUaNNGnwd7SvbjhmsRZqohs9KzDUsFjVhSiRNdHL53XJQ5zFHwDF92inRZFLu6Dw8xttPtNHwAD1kT84uXJcVMfEHtwkQ' \
--header 'Content-Type: application/json' \
# 세션 키는 매 요청 시 새로 생성해야 합니다.
--data-raw '{
       "requestType" : "USER_PERSONAL",
       "triggerType" : "PUSH",
       "userName" : "v1$c9c5be61-2309-4a54-af02-b12b43de3a51$tp5EQzbFbKSWx3DAimLAzVOIb7hofTh/Sw==",
       "userPhone" : "v1$c9c5be61-2309-4a54-af02-b12b43de3a51$bBb0nIhutSUqmy2549+c2pOQ+CGkiarK7539",
       "userBirthday" : "v1$c9c5be61-2309-4a54-af02-b12b43de3a51$bR79nI5styn0Kd4cr2KaeCvbOxmME44y",
       "sessionKey" : "v1$c9c5be61-2309-4a54-af02-b12b43de3a51$P23BeUpZWxV4S3yZwvwawd5biZsusLVjYa2N8t1aMXKovD4GivY7GnLaKWFBwY0yDoB9a8GGascbLoPE6Z/H7fUlUMcoGVD71UhQj56azchKPAtsiarTJO4sICL7aRmqOQMNWvapKRf9wgvXzEJ45xA2g4IFhmTTTKBr0qbEJhkAPf5bRZMNL9pgH9c4KL97qQSpZwfg4PnP/DB7QmG/ckEwl2OouGjNFlyUSqtmSAy0XGoibUUu6wNuf14HTRb5cWcezNeFuyuU6xNW6KpoAHYqHrIc5Ud1wCasokXIWLIz+EAQxdWWoXDUToC6HsDeVLDQJ0zdLmbFNhCaPhIEXe+lLRNMBXqTslJ0unj3alU/BJWdW81EX6J5CsDE/ng7rS/bram7K6KJ8V4OGOF7nNgHEwcMnzptOhBCOmG6+CNv4COL3Sg15mkFhdTwWa8H5gPl0SDZHm5fai/QBizZJhLQhpdiJ04w67lcPlUOJLLYTSVVcqaaaIKHnIzyWKcJ72NaJP8ZOGTs/EXT9zPwWFG57Z4E5wLNjSUG4SRlJ43X0otzYWz0plIWptkLhFW1qsHMWLp5stlqCeG2LLDqG7LJszwxwEhVEHVYo2ZUQnE7D/tiiV37b9v8EWNODNN25d6C5QypOcOF78isz0YY3Ze+RIMjY6j5Swu/eEumcdo="
    }'

Case3. 토스 서버에서 생성하는 앱스킴을 이용해서 인증 요청하기

curl --location --request POST 'https://cert.toss.im/api/v2/sign/user/auth/id/request' --header 'Authorization: Bearer eyJraWQiOiJjZXJ0IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ0ZXN0X2E4ZTIzMzM2ZDY3M2NhNzA5MjJiNDg1ZmU4MDZlYjJkIiwiYXVkIjoidGVzdF9hOGUyMzMzNmQ2NzNjYTcwOTIyYjQ4NWZlODA2ZWIyZCIsIm5iZiI6MTY0OTIyMjk3OCwic2NvcGUiOlsiY2EiXSwiaXNzIjoiaHR0cHM6XC9cL2NlcnQudG9zcy5pbSIsImV4cCI6MTY4MDc1ODk3OCwiaWF0IjoxNjQ5MjIyOTc4LCJqdGkiOiI4MDNjNDBjOC1iMzUxLTRmOGItYTIxNC1iNjc5MmNjMzBhYTcifQ.cjDZ0lAXbuf-KAgi3FlG1YGxvgvT3xrOYKDTstfbUz6CoNQgvd9TqI6RmsGZuona9jIP6H12Z1Xb07RIfAVoTK-J9iC5_Yp8ZDdcalsMNj51pPP8wso86rn-mKsrx1J5Rdi3GU58iKt0zGr4KzqSxUJkul9G4rY03KInwvl692HU19kYA9y8uTI4bBX--UPfQ02G0QH9HGTPHs7lZsISDtyD8sB2ikz5p7roua7U467xWy4BnRleCEWO2uUaNNGnwd7SvbjhmsRZqohs9KzDUsFjVhSiRNdHL53XJQ5zFHwDF92inRZFLu6Dw8xttPtNHwAD1kT84uXJcVMfEHtwkQ' --header 'Content-Type: application/json' # 세션 키는 매 요청 시 새로 생성해야 합니다.
--data-raw '{
       "requestType" : "USER_PERSONAL",
       "triggerType" : "APP_SCHEME",
       "userName" : "v1$255f8cc3-7d1d-4667-b28b-03f44e09483f$ZUyjGpmb/nL9W7//N1/VUN/F/947biU+1w==",
       "userPhone" : "v1$255f8cc3-7d1d-4667-b28b-03f44e09483f$v8QTxScwJ/NBA+Gp/TXerMWQp9BrQ45M7xaC",
       "userBirthday" : "v1$255f8cc3-7d1d-4667-b28b-03f44e09483f$vswaxSEyJf/J7Qndq2E7iOPJOmIWILby",
       "sessionKey" : "v1$255f8cc3-7d1d-4667-b28b-03f44e09483f$MdeifC7fngWC9qn/qia1Itp6jYulvhTH1JCln0oYp3xg95VpnqN5O5isZ58ZYD1WM03YooETiwQIjtLEMl2HSjxwlXZQ2yoyd/zH3mgCzlVDd/nOO0AI04Rzo/M7mvdfoN/k+DUBAeQ4NBIC9uqMt0JrkpuGvv3O1faYPBCV6OCMIcAOAbQb9fJx1bWFRLMkhd3meTD9wt2px46Kz7kW/FZXefMJr4qdnJEX9pZKz2QMJCMEnu0aW+AkJQUS5VfPSohrFTS8VeWoiNArFSESIxZhX3A+duAIJrYxTvXvOCM9ntO9xIMwTd+hp9L1UAYFOrqh6J2gmN1nI5ScBvtkLwRMFQ1D/eoSL/HD7Xb9cBAxMxPX4dn0OjLkUuO0y+KW4s+Prj9IYesDEkXUQpuQKz+4mLfX88lyplJ7o2x3uoecFlIBT92oY6BI+yxTATnwSIK249OwX2vuG6HLSOXlI3RScHjEDtzz1zPK6kjCGkFBM+5+gMyTIF4C130Uk4rlXpxYOGwPoBKBD2buGbBh7AHIaFgHlYjShdPiHMCF0ZDQnOubk139cRzoCVkKqdpPgVqIy8q6982UXzeoSIxqpSauqaL0IK2PPbMy3s9WH12NrDFDkg4NLAhnEgGNfOPjMl9lRMW+i4cCw0jhaCx1bu5GTEQzDCpc0DFT+/KPkKc="
    }'

요청 파라미터

  • requestType String
    • USER_NONE : 토스 표준창을 호출하는 방식으로 토스 웹 페이지에서 고객 정보를 입력받아 인증 요청

      (표준창 사용에는 triggerType 선언 불필요)

    • USER_PERSONAL : 고객의 이름, 생년월일, 전화번호 정보를 기반으로 인증 요청

      (암호화된 userName, userPhone, userBirthday 정보 필수)

  • triggerType String
    • PUSH : 토스 앱 푸쉬를 이용해서 고객의 토스 앱으로 직접 푸쉬를 발송하고 인증을 진행하는 방식
    • APP_SCHEME : 토스 인증 서버에서 인증 결과로 전달하는 OS별 앱스킴을 고객사 서버에서 직접 호출하고 인증을 진행하는 방식
  • userName String

    requestType이 USER_PERSONAL인 경우에만 필수로 전달하는 고객의 이름

    평문이 아닌 반드시 암호화된 값으로 전달해야 합니다. 개인정보 암복호화 확인하기

  • userPhone String

    requestType이 USER_PERSONAL인 경우에만 필수로 전달하는 고객의 휴대폰번호로 문자 포함없이 순수한 숫자형태로 전달 필요

    평문이 아닌 반드시 암호화된 값으로 전달해야 합니다. 개인정보 암복호화 확인하기

  • userBirthday String

    requestType이 USER_PERSONAL인 경우에만 필수로 전달하는 고객의 생년월일 8자리 YYYYMMDD형식

    평문이 아닌 반드시 암호화된 값으로 전달해야 합니다. 개인정보 암복호화 확인하기

  • sessionKey String

    API에서 사용자의 개인정보 전달이 필요한 경우 즉, requestType이 USER_PERSONAL인 경우는 필수 파라미터

    인증 요청과 응답을 AES 암복호화 할 때 사용되는 세션 키로 매 요청 시 마다 새로 생성해서 암호화된 개인정보와 함께 전달해야 합니다. 세션키 생성 방법 확인하기

  • successCallbackUrl String

    토스 앱 인증이 성공적으로 완료되고 돌아갈 고객사 앱스킴 주소

  • failCallbackUrl String

    토스 앱 인증이 실패 했을 때 돌아갈 고객사 앱스킴 주소

  • nonce String

    중복 요청 방지를 위한 옵셔널 파라미터

    전자서명 원문에 포함시킬 값으로 고객사에서 자체 생성하는 트랜잭션아이디의 기능처럼 활용 가능합니다.

  • expireSeconds Number

    인증을 할 수 있는 유효시간으로 최대 1800(초)으로 설정 가능

성공응답

// Case1. 표준창을 이용해서 인증 요청한 경우의 결과 데이터
// authUrl를 팝업으로 호출해야 합니다.
{
"resultType": "SUCCESS",
"success": {
"txId": "c1ce9214-9878-4751-b433-0c96641b0e13",
"appScheme": null,
"androidAppUri": null,
"iosAppUri": null,
"requestedDt": "2022-02-13T17:52:22+09:00",
"authUrl": "https://auth.cert.toss.im/start?serviceType=SIGN_USER_AUTH"
}
}
// Case 2. 앱 푸쉬를 이용해서 인증 요청한 경우의 결과 데이터
// 결과가 조회되는 시점과 동시에 인증 요청으로 전달된 사용자의 토스 앱으로 푸시가 발송됩니다.
{
"resultType": "SUCCESS",
"success": {
"txId": "de5f5d42-508b-4686-bbc0-4736bfebacad",
"appScheme": null,
"androidAppUri": null,
"iosAppUri": null,
"requestedDt": "2022-02-13T17:52:22+09:00"
}
}
// Case3. 토스 서버에서 생성하는 앱 스킴을 이용해서 인증 요청한 경우의 결과 데이터
// 생성된 앱스킴 그대로 호출해야 합니다.
{
"resultType": "SUCCESS",
"success": {
"txId": "d7b7273b-407b-46be-a9d8-97d2e895b009",
"appScheme": "supertoss://toss-cert/v2/sign/user/auth?txId=686097ba-1567-4b0f-97d3-5414ff7ce7af&_minVerAos=5.36.0&_minVerIos=5.10.0",
"androidAppUri": "intent://toss-cert/v2/sign/user/auth?txId=686097ba-1567-4b0f-97d3-5414ff7ce7af&_minVerAos=5.36.0&_minVerIos=5.10.0#Intent;scheme=supertoss;package=viva.republica.toss;end",
"iosAppUri": "https://ul.toss.im?scheme=supertoss%3A%2F%2Ftoss-cert%2Fv2%2Fsign%2Fuser%2Fauth%3FtxId%3D686097ba-1567-4b0f-97d3-5414ff7ce7af%26_minVerAos%3D5.36.0%26_minVerIos%3D5.10.0",
"requestedDt": "2022-02-13T17:52:22+09:00"
}
}
JSON

실패응답

{
"resultType": "FAIL",
"error": {
"errorType": 0,
"errorCode": "CE1000",
"reason": "토큰이 유효하지 않습니다.",
"data": {},
"title": null
},
"success": null
}
JSON

응답 파라미터

  • resultType 필수 · String
    요청 결과 (성공: SUCCESS, 실패: FAIL)
  • errorCode String
    요청이 실패한 경우 반환되는 에러코드 (인증 에러코드 확인하기)
  • reason String
    에러코드에 해당하는 에러 메시지
  • txId String
    인증 요청 트랜잭션 아이디로 거래를 고유할 수 있는 값
    특정 거래를 고유할 수 있는 값이므로 반드시 저장 관리해야 합니다.
  • requestedDt String
    최초 인증 요청된 시각

    ISO 8601 형식인 YYYY-MM-DDThh:mm:ss±hh:mm 를 사용합니다.

  • appScheme String

    토스 인증 화면을 띄울 수 있는 앱 스킴 정보 triggerType: APP_SCHEME일 경우에만 전달

  • androidAppUri String

    triggerType이 APP_SCHEME인 경우에만 전달되는 안드로이드 인증 앱 스킴 값으로 appScheme과 같은 역할을 하지만, Chrome Intent를 사용하기 때문에 고객사의 추가 기능 구현없이 토스 앱 설치 유무를 판별할 수 있는 장점이 있습니다.
    클라이언트에서 호출하는 코드는 아래 가이드를 참고해주세요.

ㄴAndroid Chrome Intent 실행하는 코드

토스앱이 설치되어 있으면 앱스킴을 실행하고 설치 되어있지 않다면 플레이 스토어로 이동해서 토스앱 설치를 유도하는 코드입니다.

Kotlin
val intent = Intent.parseUri(androidAppUri, Intent.URI_INTENT_SCHEME)
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
val marketIntent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("market://details?id=" + intent.`package`)
}
startActivity(marketIntent)
}
크롬 인텐트 문서 참고하기
  • iosAppUri String

    triggerType이 APP_SCHEME인 경우에만 전달되는 iOS 인증 앱 스킴 값으로 appScheme과 같은 역할을 하지만, Universal Link를 사용하기 때문에 안드로이드와 마찬가지로 고객사의 추가 기능 구현 없이 토스 앱 설치 유무를 판별할 수 있는 장점이 있습니다.
    클라이언트에서 호출하는 코드는 아래 가이드를 참고해주세요.

ㄴiOS Universal Link 실행하는 코드

토스앱이 설치되어 있으면 앱스킴을 실행하고 설치 되어있지 않다면 앱 스토어로 이동해서 토스앱 설치를 유도하는 코드입니다.

Swift
if let appURL = URL(string: "universal link string") {
UIApplication.shared.open(appURL) { success in
if success {
print("The URL was delivered successfully.")
} else {
print("The URL failed to open.")
}
}
} else {
print("Invalid URL specified.")
}
애플 공식 문서 참고하기
  • authUrl String

    토스 표준창을 호출할 수 있는 URL

    토스 인증 서버가 응답한 URL 링크를 직접 호출하는 방식이 아닌 표준창 SDK 연동 후에 이용해 주세요.

개인정보를 입력하지 않는 원터치 인증 연동하기

토스 사용자 식별을 위해 필요한 개인정보를 전송하지 않고 고객사 앱에서 바로 토스 앱을 호출해서 한 번에 인증을 끝내고 싶다면 아래 원터치 인증 방식을 사용하세요.

  • 원터치 인증 방식은 모바일 앱을 서비스 하는 고객사에서만 사용할 수 있습니다.

  • 원터치 인증 API를 연동하기 전에 본인확인 요청 API를 통해 txId를 획득해야 합니다. 본인확인 요청에서 requestType: USER_NONE 으로 선언하고 토스인증 완료 후 돌아갈 고객사 앱을 위해 successCallbackUrl, failCallbackUrl 을 함께 전달해주세요.

  • 원터치 인증 API는 고객사 서버의 호출이 아닌 고객사 앱(클라이언트) 에서 GET 방식으로 호출해야 합니다.

EndPoint

GET /api-client/v1/transactions/{txId}

요청

curl --location --request GET 'https://cert.toss.im/api-client/v1/transactions/bf3031d9-f2ad-4aa3-9db5-08f1e1ddf0ce' \

요청 파라미터

  • txId 필수 · String

    본인확인 요청으로 토스 서버에서 전달받은 요청 트랜잭션 아이디

성공응답

{
"resultType": "SUCCESS",
"success": {
"txId": "bf3031d9-f2ad-4aa3-9db5-08f1e1ddf0ce",
"status": "REQUESTED",
"appUri": {
"android": "intent://toss-cert/v2/sign/user/auth?txId=bf3031d9-f2ad-4aa3-9db5-08f1e1ddf0ce&_minVerAos=5.36.0&_minVerIos=5.10.0#Intent;scheme=supertoss;package=viva.republica.toss;end",
"ios": "https://ul.toss.im?scheme=supertoss%3A%2F%2Ftoss-cert%2Fv2%2Fsign%2Fuser%2Fauth%3FtxId%3Dbf3031d9-f2ad-4aa3-9db5-08f1e1ddf0ce%26_minVerAos%3D5.36.0%26_minVerIos%3D5.10.0"
}
}
}
JSON

실패응답

{
"resultType": "FAIL",
"error": {
"errorType": 0,
"errorCode": "CI3500",
"reason": "다시 시도해주세요",
"data": {},
"title": "인증에 실패했어요"
},
"success": null
}
JSON

응답 파라미터

  • resultType 필수 · String
    요청 결과 (성공: SUCCESS, 실패: FAIL)
  • errorCode String
    요청이 실패한 경우 반환되는 에러코드 (인증 에러코드 확인하기)
  • reason String
    에러코드에 해당하는 에러 메시지
  • txId String
    인증 요청 트랜잭션 아이디
  • status String
    인증 상태의 종류
    • REQUESTED : 토스 인증서버에서 사용자의 토스 앱으로 인증이 요청된 상태

    • IN_PROGRESS : 사용자가 인증을 진행 중인 상태

    • COMPLETED : 고객이 인증을 완료한 상태

    • EXPIRED : 유효시간 만료로 인증 진행이 불가한 상태

  • appUri Object

    원터치 인증을 할 수 있는 토스 앱 스킴입니다. OS에 맞게 앱 스킴을 호출해 주세요.

    단, 고객사 서버가 아닌 고객사 앱(클라이언트)에서 appUri를 호출해야 합니다.

  • android String

    안드로이드에서 호출할 수 있는 앱 스킴

  • ios String

    iOS에서 호출할 수 있는 앱 스킴

토스 앱으로 인증을 완료한 뒤 인증결과에 따라 토스 서버에서 고객사 앱스킴을 호출합니다. 본인확인 요청 API에서 successCallbackUrl, failCallbackUrl 앱 스킴을 전달해주세요.

3. 본인확인 상태조회 API

사용자의 인증 상태를 확인하기 위한 API로서 txId로 현재의 인증 진행 상태를 확인할 수 있습니다.

사용자의 인증 진행상태를 조회하는 용도로 사용할 수 있고, 상태조회 API의 결과 값으로 인증완료 상태를 처리하지 마세요. 토스에서는 결과조회 API를 기준으로 인증완료를 판별합니다.

EndPoint

POST /api/v2/sign/user/auth/id/status

요청

curl --location --request POST 'https://cert.toss.im/api/v2/sign/user/auth/id/status' \
--header 'Authorization: Bearer eyJraWQiOiJjZXJ0IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ0ZXN0X2E4ZTIzMzM2ZDY3M2NhNzA5MjJiNDg1ZmU4MDZlYjJkIiwiYXVkIjoidGVzdF9hOGUyMzMzNmQ2NzNjYTcwOTIyYjQ4NWZlODA2ZWIyZCIsIm5iZiI6MTY0OTIyMjk3OCwic2NvcGUiOlsiY2EiXSwiaXNzIjoiaHR0cHM6XC9cL2NlcnQudG9zcy5pbSIsImV4cCI6MTY4MDc1ODk3OCwiaWF0IjoxNjQ5MjIyOTc4LCJqdGkiOiI4MDNjNDBjOC1iMzUxLTRmOGItYTIxNC1iNjc5MmNjMzBhYTcifQ.cjDZ0lAXbuf-KAgi3FlG1YGxvgvT3xrOYKDTstfbUz6CoNQgvd9TqI6RmsGZuona9jIP6H12Z1Xb07RIfAVoTK-J9iC5_Yp8ZDdcalsMNj51pPP8wso86rn-mKsrx1J5Rdi3GU58iKt0zGr4KzqSxUJkul9G4rY03KInwvl692HU19kYA9y8uTI4bBX--UPfQ02G0QH9HGTPHs7lZsISDtyD8sB2ikz5p7roua7U467xWy4BnRleCEWO2uUaNNGnwd7SvbjhmsRZqohs9KzDUsFjVhSiRNdHL53XJQ5zFHwDF92inRZFLu6Dw8xttPtNHwAD1kT84uXJcVMfEHtwkQ' \
--header 'Content-Type: application/json' \
--data-raw '{
      "txId": "633f3e1b-1a11-4e7c-9b35-dd391f440be4"
    }'

요청 파라미터

  • txId 필수 · String

    상태 확인이 필요한 인증 요청 트랜잭션 아이디

성공응답

{
"resultType": "SUCCESS",
"success": {
"txId": "633f3e1b-1a11-4e7c-9b35-dd391f440be4",
"status": "REQUESTED",
"requestedDt": "2022-02-13T18:00:26+09:00"
}
}
JSON

실패응답

{
"resultType": "FAIL",
"error": {
"errorType": 0,
"errorCode": "CE3100",
"reason": "존재하지 않는 요청입니다",
"data": {},
"title": null
},
"success": null
}
JSON

응답 파라미터

  • resultType 필수 · String

    요청 결과 (성공: SUCCESS, 실패: FAIL)

  • errorCode String

    요청이 실패한 경우 반환되는 에러코드 (인증 에러코드 확인하기)

  • reason String

    에러코드에 해당하는 에러 메시지

  • txId 필수 · String
    상태를 조회한 인증 요청 트랜잭션 아이디
  • status String
    인증 상태의 종류
    • REQUESTED : 토스 인증서버에서 사용자의 토스 앱으로 인증이 요청된 상태

    • IN_PROGRESS : 사용자가 인증을 진행 중인 상태

    • COMPLETED : 고객이 인증을 완료한 상태

    • EXPIRED : 유효시간 만료로 인증 진행이 불가한 상태

  • requestedDt String
    최초 인증 요청된 시간

    ISO 8601 형식인 YYYY-MM-DDThh:mm:ss±hh:mm 를 사용합니다.

4. 본인확인 결과조회 API

사용자의 인증 결과를 확인하기 위한 API로서 txId로 결과를 조회할 수 있습니다.

결과조회 API는 성공 기준으로 최대 2회까지 조회가 가능합니다.
사용자 인증을 끝마친 후 60분(1시간) 이내 결과조회를 끝내야 합니다. 60분을 초과한 경우 결과조회가 제한되며 인증요청 API부터 다시 시작해야 합니다.

EndPoint

POST /api/v2/sign/user/auth/id/result

요청

curl --location --request POST 'https://cert.toss.im/api/v2/sign/user/auth/id/result' \
--header 'Authorization: Bearer eyJraWQiOiJjZXJ0IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ0ZXN0X2E4ZTIzMzM2ZDY3M2NhNzA5MjJiNDg1ZmU4MDZlYjJkIiwiYXVkIjoidGVzdF9hOGUyMzMzNmQ2NzNjYTcwOTIyYjQ4NWZlODA2ZWIyZCIsIm5iZiI6MTY0OTIyMjk3OCwic2NvcGUiOlsiY2EiXSwiaXNzIjoiaHR0cHM6XC9cL2NlcnQudG9zcy5pbSIsImV4cCI6MTY4MDc1ODk3OCwiaWF0IjoxNjQ5MjIyOTc4LCJqdGkiOiI4MDNjNDBjOC1iMzUxLTRmOGItYTIxNC1iNjc5MmNjMzBhYTcifQ.cjDZ0lAXbuf-KAgi3FlG1YGxvgvT3xrOYKDTstfbUz6CoNQgvd9TqI6RmsGZuona9jIP6H12Z1Xb07RIfAVoTK-J9iC5_Yp8ZDdcalsMNj51pPP8wso86rn-mKsrx1J5Rdi3GU58iKt0zGr4KzqSxUJkul9G4rY03KInwvl692HU19kYA9y8uTI4bBX--UPfQ02G0QH9HGTPHs7lZsISDtyD8sB2ikz5p7roua7U467xWy4BnRleCEWO2uUaNNGnwd7SvbjhmsRZqohs9KzDUsFjVhSiRNdHL53XJQ5zFHwDF92inRZFLu6Dw8xttPtNHwAD1kT84uXJcVMfEHtwkQ' \
--header 'Content-Type: application/json' \
# 세션 키는 매 요청 시 새로 생성해야 합니다.
--data-raw '{
       "txId" : "c1ce9214-9878-4751-b433-0c96641b0e13",
       "sessionKey" : "v1$71c3d6cd-6a74-48a8-8ab2-b48e6133ae6f$Q0U7Bdg4dWd0XXucjsM/mda89bFU7eHnoUhgQ3k+cGQ9gv37jvWC+8isrkO2CR4+qgoPg+U+K7/tQH2m+uU7L8Ab0gzbQo6ASX39NpcP6RHpI+VBi323ssYnBmJL7n0z4aNm6raUEsMoNwrOaMDe0DqfalgOeZgZUztWew1pfZul2Q3/WIBMdp+npS4sFnBRoBrzLroVsuNRTLK0XT6m5hak+ys+vBg5vZFoI0JN7j7zsr8lqGi6piSkygl1PLPugnSC9cOezxMoVN5c/csEVQxMsfkwqTIASaZVECnP50dO70TydYhBFCqxw3DpEDBHcXNDucOtdVOPslCPNx3NZv1i0IH0r92ULb3w2Y0Fncy4/xL1dPSS+TbA5540u2Wb3cxqVNHib7WwSMHBwQtXAnFSFZmcvQQPXtTeQ7SCvNnhA8k3gbboSpbDBg60RWn/1zF/ogBYRldO1BFtq7KP+jOm6I2OSSVpagH1Wu5MXhEtiTmsx7M8j/IM8EfnXbD9axJnlW2fKHZVvAj+5KNhqy90PUimBCKiXqjvUwOqb9hGGEzJ4JVKbIIiy1EYOaRkPTK9GurZwQaqM4o4c8pzOYRQR/3XIPWHxLv/jwsaMcfUIQFyKE+w898g+l1zO0jcck59/R64kZcirT9AsGFnRUWrsHGIkM95jdYlpUsnCXw="
  }'

요청 파라미터

  • txId 필수 · String

    결과 확인이 필요한 인증 요청 트랜잭션 아이디

  • sessionKey 필수 · String

    결과조회 요청에서는 인증수단 상관없이 txId와 함께 sessionKey를 필수로 전달해야 합니다. 토스 서버에서 결과조회의 응답으로 암호화된 개인정보를 전달하기 때문입니다.

    인증 요청과 응답을 AES 암복호화 할 때 사용되는 세션 키로 매 요청 시 마다 새로 생성해서 암호화된 개인정보와 함께 전달해야 하고, 인증 요청에서 사용된 세션 키를 재 사용하지 마세요.

    세션키 생성 방법 확인하기

성공응답

// 결과조회 응답에서는 인증을 호출하는 방식에 상관없이 동일한 바디 파라미터를 제공합니다.
{
"resultType": "SUCCESS",
"success": {
"txId": "c1ce9214-9878-4751-b433-0c96641b0e13",
"status": "COMPLETED",
"userIdentifier": null,
"userCiToken": null,
"signature": "MIIJCAYJKoZIhvcN...(생략)...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",
"randomValue": null,
"completedDt": "2022-02-13T18:01:53+09:00",
"requestedDt": "2022-02-13T18:00:26+09:00",
"personalData": {
"ci": "v1$b88f8717-8e76-4276-bed0-f769a8baf7be$X3g52aAyCBirz0UVp1oNRq0SfGtj66vGtUT3rp1aSdm1h//xmpm7vdf48fbGI2i7VTBj6TKG2rqanP6Yo9MiTQu63C8kLWayzWAMp+RLyXLovvnFb9SxxdblRtZbj5KRNlBWK9t2VXI=",
"name": "v1$b88f8717-8e76-4276-bed0-f769a8baf7be$9oiJBRei1KI/SgXtXGmkfNHu+pdAUHXBxA==",
"birthday": "v1$b88f8717-8e76-4276-bed0-f769a8baf7be$LQgw26ExChwWi8cQQz6GrdMAdMZGyaEI",
"gender": "v1$b88f8717-8e76-4276-bed0-f769a8baf7be$WnREqd1HM/Ci7p+3KIqROusVkYeSAQ==",
"nationality": "v1$b88f8717-8e76-4276-bed0-f769a8baf7be$UH5Kqd3dPV1daxw0i23eMWjeXcXC",
"ci2": null,
"di": "v1$2e161d9d-e620-443e-9a27-8db41cc96cf9$6GKr2zaUWWfI6rpJ6/AV9U4W0S4nhAMFIFLkt5CS6N8Gjb1Oc/dpitkMSSvLroDO5b6zdl9bufGSQ6SiVQdlYN2OWYFBr/Hb4e4AYwQpFxDbpi9ksYt52aFa3G2DwaNOQMUBkyQ1IWc=",
"ciUpdate": null
}
}
}
JSON

실패응답

{
"resultType": "FAIL",
"error": {
"errorType": 0,
"errorCode": "CE3102",
"reason": "요청이 아직 완료되지 않았습니다.",
"data": {},
"title": null
},
"success": null
}
JSON

응답 파라미터

  • resultType 필수 · String

    요청 결과 (성공: SUCCESS, 실패: FAIL)

  • errorCode String

    요청이 실패한 경우 반환되는 에러코드 (인증 에러코드 확인하기)

  • reason String

    에러코드에 해당하는 에러 메시지

  • txId 필수 · String
    결과를 조회한 인증 요청 트랜잭션 아이디
  • status String

    COMPLETED

    결과 조회가 정상적으로 됐을 때 반환되는 상태

  • userIdentifier String

    현재 버전에서 미사용으로 null 고정

  • userCiToken String

    현재 버전에서 미사용으로 null 고정

  • signature String
    사용자가 서명한 전자서명 정보

    Base64인코딩된 DER값으로 사용자 식별을 위해 txId와 함께 반드시 저장 관리 해야 합니다.

    인증서 유효성 확인하는 방법 알아보기
  • randomValue String

    현재 버전에서 미사용으로 null 고정

  • completedDt String
    사용자가 인증을 끝마친 시간

    ISO 8601 형식인 YYYY-MM-DDThh:mm:ss±hh:mm 를 사용합니다.

  • requestedDt String
    최초 인증 요청된 시각

    ISO 8601 형식인 YYYY-MM-DDThh:mm:ss±hh:mm 를 사용합니다.

  • personalData Object
    인증을 진행한 사용자의 개인정보입니다.
    토스 테스트 환경에서는 아래 모든 개인정보를 제공하지만 실제 토스 앱으로 테스트한 사용자의 개인정보가 아닌 토스가 생성한 가상 인물의 개인정보를 암호화해서 전달합니다.

    라이브 환경에서는 사업자가 바로 신청하기에서 신청한 개인정보만 String 형태로 전달됩니다.

  • ci String

    암호화된 사용자의 ci

  • name String

    암호화된 사용자의 이름

  • birthday String

    암호화된 사용자의 생년월일 8자리

  • gender String

    암호화된 사용자의 성별정보로 MALE 또는 FEMALE

  • nationality String

    암호화된 사용자의 국적정보로 LOCAL 또는 FOREIGNER

  • ci2 String

    예측할 수 없는 상황에서 ci 유출 대응을 위한 임시 파리미터로 null 고정

  • di String

    암호화된 사용자의 di

  • ciUpdate String

    예측할 수 없는 상황에서 ci 유출 대응을 위한 임시 파리미터로 null 고정

5. 토스 표준창을 통해 본인확인 UI 연동하기

토스 표준창이 무엇인가요?

토스 표준창 스크린샷

토스에서 만든 웹 인증 창입니다. 간단한 자바스크립트 코드를 통해 토스의 인증 팝업창을 띄울 수 있습니다.

PC 브라우저로 진입한 고객의 개인정보를 입력받고 제 3자 제공동의를 받아 인증을 진행합니다. 모바일에서는 이미 로그인 되어 있는 고객의 토스 앱을 곧바로 호출하거나 토스 앱이 설치되어 있지 않다면 앱 스토어로 이동 시킵니다.

토스 웹 페이지에서 고객 정보를 직접 입력받고 있어서 더욱 안전하고, 고객사 서버와 토스 서버 간 고객 개인정보를 주고받지 않기 때문에 연동이 간편합니다.

QR코드 인증도 지원해요

QR코드 인증도 지원해요_1QR코드 인증도 지원해요_2

토스 표준창에 개인정보를 입력하기 불편하다면 생성된 QR코드를 카메라 앱으로 스캔하세요. 토스 앱에서 웹 화면과 동일한 문자열을 선택하면 인증이 완료됩니다.

연동 준비: JavaScript SDK 설치하기

표준창 JavaScript SDK를 통해 브라우저에서 인증 표준창을 여는 기본 코드를 설치합니다. 표준창을 연동할 HTML 파일에 script를 추가해주세요.

브라우저가 아닌 네이티브앱의 웹뷰에 표준창을 연동하고 싶다면 여기를 참고해주세요

<head>
<title>토스로 인증하기</title>
<script src="https://cdn.toss.im/cert/v1"></script>
</head>
HTML

Case 1. 브라우저에 표준창 띄우기

SDK로 설치한 TossCert객체의 메서드를 실행해 표준창을 띄웁니다.

서버 투 서버의 인증요청 API를 브라우저에서 비동기로 호출하는 분들은 팝업 차단 방지를 위해 preparePopup()을 먼저 호출하는 것을 유의해주세요.

var tossCert = TossCert(); // 1. TossCert객체를 초기화합니다.
tossCert.preparePopup(); // 2. 팝업차단 방지를 위해 비동기 호출 전에 빈 팝업을 먼저 띄웁니다.
// 3. 서버 투 서버의 인증요청 API를 호출합니다. 원하는 방식으로 적절히 수정해주세요.
getTxId().then(function (response) {
// 4. 표준창 팝업을 띄웁니다.
tossCert.start({
authUrl: response.authUrl,
txId: response.txId,
onSuccess: function () {
location.href = '/complete.html'; // 인증 성공시 행동을 정의해주세요.
},
onFail: function (error) {
console.error('에러가 발생했습니다', error); // 인증 실패시 행동을 정의해주세요.
},
});
});
JavaScript
연동 코드 전체 보기

Case 2. Redirect 방식으로 표준창 띄우기

팝업 방식이 아니라 리다이렉트 방식으로 표준창을 띄우고 싶다면 아래 코드를 참고해주세요.

function startTossCertWithRedirect() {
var tossCert = TossCert(); // 1. TossCert객체를 초기화합니다.
// 2. 서버 투 서버의 인증요청 API를 호출합니다. 원하는 방식으로 적절히 수정해주세요.
getTxId().then(function (response) {
tossCert.start({
type: 'redirect',
authUrl: response.authUrl,
txId: response.txId,
successUrl: location.origin + '/complete.html', // 인증 성공시 행동을 정의해주세요.
failUrl: location.origin + '/fail.html', // 인증 실패시 행동을 정의해주세요.
});
});
}
JavaScript
연동 코드 전체 보기

Case 3. 네이티브 앱의 웹뷰에 표준창 띄우기

안드로이드

표준창 인증진행 상태에 따라 콜백을 호출하는 JavaScript 인터페이스 정의합니다.

enum class WindowMessages {
TOSS_AUTH_POPUP_ONLOAD,
TOSS_AUTH_SUCCESS,
TOSS_AUTH_FAIL,
TOSS_AUTH_CLICK_NAVBAR_CLOSE
}
class TossAuthInterface {
@JavascriptInterface
fun sendMessage(message: String) {
when (WindowMessages.valueOf(message)) {
WindowMessages.TOSS_AUTH_POPUP_ONLOAD -> { /* 표준창 로딩 */ }
WindowMessages.TOSS_AUTH_SUCCESS -> { /* 인증 성공 */ }
WindowMessages.TOSS_AUTH_FAIL -> { /* 인증 실패 */ }
WindowMessages.TOSS_AUTH_CLICK_NAVBAR_CLOSE -> { /* 표준창 닫기 */ }
}
}
}

표준창을 실행하는 웹뷰 설정

  1. tossAuthWebView 이름으로 JavaScript 인터페이스 추가
  2. JavaScript 사용 가능하도록 설정
  3. 표준창에서 Chrome Intent를 실행할 수 있도록 shouldOverrideUrlLoading 함수 오버라이딩
webView.apply {
addJavascriptInterface(TossAuthInterface(), "tossAuthWebView") // (1)
settings.javaScriptEnabled = true // (2)
webViewClient = object : WebViewClient() { // (3)
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
return when (request?.url?.scheme) {
"intent" -> {
val schemeIntent = Intent.parseUri(request.url.toString(), Intent.URI_INTENT_SCHEME)
try {
context.startActivity(schemeIntent)
} catch (e: ActivityNotFoundException) {
val marketIntent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=${schemeIntent.`package`}"))
context.startActivity(marketIntent)
}
true
}
"market" -> {
val marketIntent = Intent(Intent.ACTION_VIEW, Uri.parse(request.url.toString()))
context.startActivity(marketIntent)
true
}
else -> false
}
}
}
}

표준창 실행

POST 방식으로 txId 를 전달하면서 웹뷰에 표준창을 띄웁니다.

val postData = "txId=${URLEncoder.encode(txId, "UTF-8")}"
webView.postUrl(url, postData.toByteArray())

iOS

웹뷰 생성시 JavaScript Bridge 연결

func setWebView() {
let contentController = WKUserContentController()
contentController.add(self, name: "tossAuthWebView")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
webview = WKWebView(frame: view.bounds, configuration: configuration)
webview.navigationDelegate = self
}

표준창 실행

iOS는 POST인 경우 기본 헤더값이 없기 때문에 무조건 헤더 세팅 필요합니다.

/// txID, authURLString: 서버에서 받은 값
func loadWebView(txID: String, authURLString: String) {
guard let url = URL(string: authURLString) else { return }
// 1. 토스인증 표준창 실행하기 전에 request를 아래와 같이 생성한 후에
var request = URLRequest(url: url)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = "txId=\(txID)".data(using: .utf8)
// 2. 만들어진 request를 이용해 웹뷰를 실행해주세요. 애플이 제공하는 웹뷰 호출 기본 함수입니다.
webview.load(request)
}

Universal Link 실행

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url, (url.scheme == "itms-appss" || url.host == "ul.toss.im") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}

호출된 자바스크립트 동작 처리

extension WebViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let body = message.body as? String else { return }
switch body {
case "TOSS_AUTH_POPUP_ONLOAD": // 표준창 로드 완료
break
case "TOSS_AUTH_SUCCESS": // 표준창 완료
break
case "TOSS_AUTH_FAIL": // 표준창 에러
break
case "TOSS_AUTH_CLICK_NAVBAR_CLOSE": // 취소
break
default:
break
}
}
}

Case 4. React Native 앱의 웹뷰에 표준창 띄우기

안드로이드

표준창에서 인증 앱스킴(Chrome Intent)을 실행하도록 Native Module 을 등록합니다.

Native Module을 정의합니다.

class ReactNativeIntentModule(
private val reactContext: ReactApplicationContext
) : ReactContextBaseJavaModule(reactContext) {
@ReactMethod
fun openChromeIntent(intentUri: String) {
if (intentUri.startsWith("intent")) {
val schemeIntent = Intent.parseUri(intentUri, Intent.URI_INTENT_SCHEME).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
try {
reactContext.startActivity(schemeIntent)
} catch (e: ActivityNotFoundException) {
val marketIntent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("market://details?id=${schemeIntent.`package`}")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
reactContext.startActivity(marketIntent)
}
}
}
override fun getName(): String = "ReactNativeIntentModule"
}

ReactPackageNative Module을 추가합니다.

class ReactNativePackages : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
return mutableListOf(
ReactNativeIntentModule(reactContext)
)
}
override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<View, ReactShadowNode<*>>> {
return mutableListOf()
}
}

ReactNativeNative Module을 등록합니다.

  • ReactNative 프로젝트인 경우
// MainApplicatin.kt
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(ReactNativePackages())
}
  • 기존 앱과 통합된 경우
val packages = PackageList(application).packages.apply {
add(ReactNativePackages())
}
val reactInstanceManager = ReactInstanceManager.builder()
...
.addPackages(packages)
.build()

iOS

표준창에서 인증 앱스킴을 실행하도록 Native Module 을 등록합니다.

JavaScript 코드에서 Native Module 을 사용하도록 설정합니다.

import { NativeModules } from 'react-native';
const { ReactNativeIntentModule } = NativeModules; /* ReactNativeIntentModule#getName */

본인확인 요청 API 호출

const requestUserAuth = async () => {
const response = await fetch('https://cert.toss.im/api/v2/sign/user/auth/id/request', {
method: 'POST',
headers: {
Authorization: 'Bearer {Token}',
'Content-Type': 'application/json',
},
body: JSON.stringify({
requestType: 'USER_NONE',
}),
});
const json = await response.json();
return json;
};

ReactNative 웹뷰에서 표준창 실행

  1. Whitelist 등록

  2. 표준창 URL 및 POST 방식으로 인증 요청 트랜잭션 ID 전달하도록 설정

    a. 표준창 URL : authUrl

    b. 인증 요청 트랜잭션 ID : txId

  3. 자바스크립트를 사용 가능하도록 설정

  4. 인증 진행 상태를 전달 받도록 앱브릿지 설정

  5. 인증 앱스킴을 실행하도록 콜백 설정

<SafeAreaView style={{ flex: 1 }}>
<WebView originWhitelist={['http://*', 'https://*', 'intent://*']} // (1) source={{ // (2) uri: authUrl, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: `txId=${txId}`, }} javaScriptEnabled={true} //(3) onMessage={event => { // (4) const message = event.nativeEvent.data; switch (message) { case 'TOSS_AUTH_POPUP_ONLOAD': /* 표준창 로딩 */ break; case 'TOSS_AUTH_SUCCESS': /* 인증 성공 */ break; case 'TOSS_AUTH_FAIL': /* 인증 실패 */ break; case 'TOSS_AUTH_CLICK_NAVBAR_CLOSE': /* 표준창 닫기 */ break; } }} onShouldStartLoadWithRequest={event => { // (5) if (Platform.OS === 'android') { ReactNativeIntentModule.openChromeIntent(event.url); return false; } else if (Platform.OS === 'ios') { return true; } return true; }} />
</SafeAreaView>
  • 더 궁금한 내용이 있나요?