핵심 요약
- 토스페이먼츠 연동 문서의 예제 코드를 한 글자도 고치지 않고 올렸더니 결제위젯이 떴습니다. 회원가입도 계약도 하지 않았고, 문서 코드 블록에 그대로 적혀 있는 테스트 키만 썼습니다. (확인일 2026년 8월 21일)
- 결제를 끝까지 통과시키고 승인 API 까지 성공했습니다. 승인 응답은 HTTP 200 에
status=DONE, 필드가 37개였습니다. - 금액을 1원만 틀리게 보내면 403으로 막힙니다. 금액 위변조 방어가 실제로 동작합니다. 다만 응답은 "허용되지 않은 요청" 이라고만 하고 금액이 안 맞는다고 말해 주지 않습니다.
- 처음 붙일 때 헛다리를 짚기 쉬운 에러가 하나 있습니다. 존재하지 않는 paymentKey 를 보내면 "결제 시간이 만료되어 결제 진행 데이터가 존재하지 않습니다" 가 옵니다. 키를 잘못 만든 것과 시간이 지난 것이 같은 응답으로 옵니다.
- 비밀번호를 두 번 입력합니다. 계좌 확인용 한 번, 결제 승인용 한 번입니다. 보안 키패드의 숫자 배열은 두 화면에서 다르고, 다시 밟을 때마다 새로 섞입니다.
- 이 글은 테스트 환경 기준입니다. 결제창에 "테스트 환경이에요. 실제로 결제되지 않아요" 가 떠 있습니다.
문제 정의: 붙이기 전에 어디까지 해볼 수 있는가
결제를 붙이는 일은 대개 계약부터 시작합니다. 전자결제를 신청하고, 심사를 받고, 상점이 열리고, 그다음에 키를 받습니다. 그런데 개발 일정을 잡으려면 그 전에 알아야 하는 것이 있습니다. 코드가 얼마나 붙는지, 서버가 무엇을 해야 하는지, 어디서 막히는지입니다.
토스페이먼츠 문서는 이 순서를 뒤집을 수 있게 해 둡니다. API 키 문서에 이렇게 적혀 있습니다. "주문서형, 결제창형 연동 키는 토스페이먼츠 전자결제 신청 이후에만 확인할 수 있어요. 신청 전에는 문서에 있는 테스트 키로 결제를 연동해보세요."
그래서 그 말대로 해봤습니다. 회원가입도 하지 않고, 계약도 하지 않고, 문서에 박힌 키만으로 어디까지 가는지 확인했습니다.
어떻게 따라갔는지 먼저 적습니다
- 결제위젯 연동 문서의 전체 예제 코드를 그대로 복사해 파일 하나로 저장했습니다. 2,795바이트입니다.
- 로컬 웹서버에 올려 브라우저로 열었습니다. 코드를 한 글자도 고치지 않았습니다.
- 결제하기를 눌러 결제를 끝까지 통과시켰습니다.
- 돌아온 주소의 값으로 승인 API 를 실제로 호출했습니다.
- 승인 API 에 일부러 잘못된 요청을 넣어 어떤 응답이 오는지 확인했습니다.
쓴 키는 문서 코드 블록에 그대로 적혀 있는 공개 테스트 키 두 개입니다. 클라이언트 키가 test_gck_docs_ 로 시작하고 시크릿 키가 test_gsk_docs_ 로 시작합니다.
이 글이 답하지 못하는 것도 먼저 적습니다.
- 테스트 환경 기준입니다. 실제 상점 키의 동작이 같다고 단정할 수 없습니다.
- 결제수단은 계좌이체 하나만 끝까지 갔습니다. 카드는 카드사 선택 화면까지만 갔습니다.
- 웹훅, 취소 API, 정산은 확인하지 않았습니다.
- 확인일은 2026년 8월 21일 하루입니다. 문서와 SDK 는 바뀝니다.
1단계: 예제를 그대로 올리면 위젯이 뜹니다
문서의 예제는 HTML 파일 한 장입니다. 스크립트 태그로 SDK 를 불러오고, 결제 UI 와 약관 UI 를 넣을 빈 요소 두 개를 두고, 스크립트에서 위젯을 렌더합니다.
이 파일을 그대로 올려 열었더니 위젯이 렌더됐습니다. 콘솔 오류는 없었습니다.
눈에 띈 것은 시간이었습니다.
| 시점 | 걸린 시간 |
|---|---|
| 결제 UI 자리에 요소가 붙음 | 800ms |
| 위젯이 실제 높이를 확보하고 결제수단이 보임 | 2,408ms |
출처: 저희가 직접 측정한 값입니다. 확인일 2026년 8월 21일. 같은 기계에서 두 번 재었고 첫 실행에서는 요소가 붙는 데 1,611ms 였습니다. 회선과 기계에 따라 달라집니다.
이 두 시점이 다르다는 것이 실무에서 걸립니다. 그 사이 화면에는 회색 뼈대만 보입니다. 로딩 표시를 직접 만들 생각이라면 요소가 붙는 시점이 아니라 높이가 잡히는 시점을 기준으로 잡으셔야 합니다.
2단계: 결제창은 새 창이 아니라 같은 페이지에서 뜹니다
결제하기를 눌렀더니 결제창이 같은 페이지 안의 프레임으로 떴습니다. 새 창은 열리지 않았습니다. 팝업 차단을 걱정할 필요가 없다는 뜻입니다. 함께 붙는 프레임 중에는 reCAPTCHA 도 있었습니다.
기본으로 선택된 결제수단은 퀵계좌이체였습니다. 그 자리에 "3% 즉시 할인" 배지가 붙어 있었고, 결제수단 목록 아래에는 "신한카드 최대 3개월 무이자 할부" 안내가 따로 있었습니다. 혜택 문구가 결제수단마다 하나씩 붙는 구조는 아니었습니다.
테스트 값은 화면이 알려줍니다. 휴대폰번호 칸에는 010-0000-0000 이 이미 입력된 채로 뜨고 "테스트용 휴대폰번호가 입력되어 있어요" 가 적혀 있습니다. 비밀번호 화면에는 "테스트용 비밀번호는 '000000' 이에요" 가 적혀 있습니다. 문서를 다시 뒤져 테스트 값을 찾을 필요가 없습니다.
3단계: 비밀번호를 두 번 입력합니다
여기가 처음 밟을 때 예상과 달랐던 지점입니다. 비밀번호를 한 번이 아니라 두 번 입력합니다.
- "계좌 확인을 위해 비밀번호를 입력해주세요"
- "결제를 위해 비밀번호를 입력해주세요"
그리고 보안 키패드의 숫자 배열이 두 화면에서 다릅니다. 한 번의 결제 안에서만 다른 것이 아니라, 결제를 다시 밟을 때마다 배열이 새로 섞였습니다. 숫자가 놓이는 자리를 외워 두는 것이 의미가 없다는 뜻입니다. 테스트 환경인데도 실제와 같은 방식으로 동작합니다.
결제가 끝나자 문서에 적어 둔 successUrl 로 돌아왔고, 주소에 값 네 개가 붙어 있었습니다.
| 값 | 내용 |
|---|---|
| paymentType | NORMAL |
| orderId | 예제에 적혀 있던 주문번호 그대로 |
| paymentKey | tgen_ 으로 시작하는 문자열 |
| amount | 50000 |
출처: 저희가 실제로 결제를 통과시키고 돌아온 주소입니다. 확인일 2026년 8월 21일.
결제하기를 누른 시점부터 돌아오기까지는 두 번 밟아 26초와 58초가 나왔습니다. 다만 이 값은 화면마다 넉넉한 대기를 넣고 밟은 것이라 사람이 손으로 하면 더 빠릅니다. 속도 지표로 쓰실 값이 아닙니다.

4단계: 승인 API 를 부르지 않으면 결제가 끝난 것이 아닙니다
돌아온 값으로 승인 API 를 호출했습니다. 문서대로 paymentKey, orderId, amount 세 개를 보내고 시크릿 키를 인증 헤더에 넣었습니다.
한 번에 성공했습니다.
| 항목 | 값 |
|---|---|
| 응답 | HTTP 200, 914ms 와 1,040ms |
| status | DONE |
| method | 계좌이체 |
| totalAmount | 50000 |
| 응답 필드 수 | 37개 |
출처: 저희가 직접 호출한 결과입니다. 확인일 2026년 8월 21일. 응답 시간은 결제를 두 번 통과시켜 각각 잰 값입니다.
승인 시각이 결제창을 통과한 시각보다 뒤였습니다. 결제창에서 비밀번호를 다 눌렀다고 결제가 끝난 것이 아닙니다. 서버가 승인 API 를 불러야 끝납니다. 문서가 4단계로 나눠 둔 이유가 이것입니다.
일부러 틀리게 보내봤습니다
붙이는 사람이 실제로 만나는 것은 성공 응답이 아니라 에러입니다. 그래서 일부러 잘못된 요청을 넣었습니다.
| 보낸 것 | 응답 | 코드와 메시지 |
|---|---|---|
| 인증 헤더 없음 | 401 | UNAUTHORIZED_KEY / 인증되지 않은 시크릿 키 혹은 클라이언트 키 입니다. |
| 잘못된 시크릿 키 | 401 | UNAUTHORIZED_KEY / 같은 메시지 |
| 존재하지 않는 paymentKey | 404 | NOT_FOUND_PAYMENT_SESSION / 결제 시간이 만료되어 결제 진행 데이터가 존재하지 않습니다. |
| 필수 필드 누락 | 400 | INVALID_REQUEST / 필수 파라미터가 누락되었습니다. |
| 금액을 1원 틀리게 | 403 | FORBIDDEN_REQUEST / 허용되지 않은 요청입니다. |
| 성공한 요청을 한 번 더 | 400 | ALREADY_PROCESSED_PAYMENT / 이미 처리된 결제 입니다. |
출처: 저희가 직접 호출한 결과입니다. 확인일 2026년 8월 21일. 같은 요청을 두 번 재어 응답 시간은 231ms 에서 511ms 사이였습니다.
여기서 세 가지가 실무에 직접 걸립니다.
첫째, 금액 위변조 방어가 실제로 막습니다. 같은 paymentKey 와 orderId 에 금액만 49,999원으로 보냈더니 403 이 왔습니다. 클라이언트에서 넘어온 금액을 그대로 믿지 말고 주문서에 저장한 금액으로 승인을 부르라는 문서 안내가 빈말이 아닙니다. 다만 응답은 금액이 안 맞는다고 말해 주지 않습니다. "허용되지 않은 요청" 뿐입니다.
둘째, 존재하지 않는 키의 메시지가 오해를 부릅니다. 아무렇게나 만든 paymentKey 를 보냈더니 "결제 시간이 만료되어 결제 진행 데이터가 존재하지 않습니다" 가 왔습니다. 키를 잘못 만든 경우와 시간이 지난 경우가 같은 응답으로 옵니다. 처음 붙일 때 이 메시지를 보고 만료 쪽을 파면 시간을 버립니다. 키가 제대로 넘어왔는지부터 보시는 편이 빠릅니다.
셋째, 필수 필드 누락은 어느 필드인지 알려주지 않습니다. amount 만 뺀 경우와 paymentKey 까지 함께 뺀 경우의 응답이 같았습니다. 요청 본문을 찍어 보는 수밖에 없습니다.
이 셋을 결제 API 의 결함으로 읽으실 필요는 없습니다. 결제는 내부 상태를 덜 드러내는 편이 안전한 영역입니다. 다만 처음 붙이는 사람이 어디서 헤매게 되는지는 알고 시작하시는 편이 낫습니다.

카드로 결제하려면 카드사를 먼저 골라야 합니다
결제수단에서 신용·체크카드를 골랐더니 카드사 목록이 펼쳐졌습니다. 카드사를 고르지 않은 채로 결제하기를 눌렀더니 결제창이 열리지 않고 그 자리에 "카드사를 선택해주세요" 가 떴습니다.
목록에는 국민, 카카오뱅크, 하나, 토스뱅크, 산업, 비씨, 우리, 전북, 씨티, 우체국, 새마을이 있었고 더보기가 붙어 있었습니다.
저희는 카드 결제를 끝까지 가지 않았습니다. 계좌이체만 완주했습니다. 카드 흐름의 뒷단은 이 글에서 말하지 않겠습니다.
실무 관점: 계약 전에 할 수 있는 것과 없는 것
문서 키로 확인한 결과를 정리하면 경계가 뚜렷합니다.
| 계약 전에 되는 것 | 계약과 계정이 필요한 것 |
|---|---|
| 위젯 렌더와 화면 배치 | 실제 상점 키 |
| 결제창 흐름 확인 | 어드민의 결제 UI 설정 |
| successUrl, failUrl 처리 | 정산 |
| 승인 API 호출과 에러 처리 | 실제 결제 |
붙이는 순서로 옮기면 이렇습니다.
- 문서 예제를 그대로 올려 위젯부터 띄우십시오. 코드를 고치는 것은 그다음입니다. 예제가 한 블록에 다 들어 있어서 화면을 오갈 일이 없습니다.
- 승인 API 를 성공시키는 것까지 먼저 해보십시오. 결제창을 통과하는 것과 결제가 끝나는 것은 다릅니다. 서버 쪽 작업이 남아 있다는 것을 일정에 넣어야 합니다.
- 에러 경로를 먼저 짜십시오. 금액 불일치 403, 이미 처리됨 400, 인증 실패 401 세 가지는 코드로 갈라 두는 편이 낫습니다. 특히 이미 처리됨은 네트워크 오류로 재시도했을 때 앞의 요청이 성공했다는 신호입니다.
- 금액은 서버에 저장한 값으로 승인을 부르십시오. 1원만 달라도 막히는 것을 확인했습니다.
확인하지 못한 것
- 실제 상점 키의 동작을 확인하지 않았습니다. 회원가입과 계약이 필요합니다. 이 글의 값은 전부 문서 테스트 키 기준입니다.
- 카드 결제 완주를 하지 않았습니다. 카드사 선택 화면까지만 갔습니다.
- 웹훅과 취소 API 를 확인하지 않았습니다.
- 정산 흐름을 확인하지 않았습니다. 승인 응답에 정산 상태가 미완료로 담겨 있다는 것만 봤습니다.
- 서버 없이 파일을 바로 열었을 때 어떻게 되는지 확인하지 않았습니다. 처음부터 로컬 서버에 올렸습니다.
자주 묻는 질문
회원가입 없이 정말 되나요
저희가 확인한 범위에서는 위젯 렌더부터 승인 API 성공까지 됐습니다. 문서 코드 블록에 테스트 키가 그대로 적혀 있습니다. 다만 실제 상점 키와 정산은 계약이 필요합니다.
결제창을 통과하면 결제가 끝난 건가요
아닙니다. 서버에서 승인 API 를 호출해야 끝납니다. 저희 실행에서도 승인 시각이 결제창 통과 시각보다 뒤였습니다.
결론
토스페이먼츠 연동에서 계약 전에 확인할 수 있는 범위는 생각보다 넓습니다. 문서 예제를 고치지 않고 올리는 것만으로 위젯이 뜨고, 결제를 통과할 수 있고, 승인 API 까지 성공합니다.
일정을 잡으실 때 기억하실 것은 셋입니다.
- 위젯이 뜨는 데 걸리는 시간과 결제수단이 보이는 데 걸리는 시간이 다릅니다.
- 결제창 통과와 결제 완료가 다릅니다. 서버 작업이 남아 있습니다.
- 에러 메시지가 원인을 정확히 짚어 주지 않는 구간이 있습니다. 특히 존재하지 않는 키에 만료 메시지가 옵니다.
모든 값은 2026년 8월 21일에 직접 실행해 확인했습니다. 문서와 SDK 는 바뀌므로 붙이기 전에 다시 확인하시기를 권장합니다.
관련 글
같은 결제 계열의 다른 항목입니다.
- 결제 수수료 분해: 가상계좌가 카드보다 비싸지는 지점은 객단가 11,765원입니다
- 인증 서비스 MAU 요금 분해: 사용자 한 명 값이 스무 배 넘게 벌어집니다
- 국내 SaaS 가격 공개 실태를 21곳 직접 확인했습니다: 18곳은 어딘가에 금액을 적어 뒀습니다
- 노션 API 로 사이트를 만들며 부딪힌 상한: 한 번에 보낼 수 있는 블록은 100개입니다