이 문서는 Bootpay 결제 연동 중 자주 묻는 질문과 문제 해결 방법을 정리해요. 에러가 발생했거나 동작 방식이 헷갈릴 때 먼저 확인해요.
일반
Sandbox와 Production의 차이는 무엇인가요?
Sandbox는 테스트 환경으로, 실제 결제가 발생하지 않아요. 개발·테스트 단계에서 사용해요. Production은 실제 결제가 처리되는 운영 환경이에요.
- Sandbox와 Production은 별도 키를 사용하며 데이터가 분리돼요
- 테스트 완료 후 연동키를 Production용으로 교체하면 돼요
API 호출 제한이 있나요?
과도한 반복 호출은 제한될 수 있어요. 같은 결제 조회를 짧은 간격으로 무한 반복하지 말고, 재시도 간격과 최대 횟수를 정해요. 실제 제한 조건은 운영 정책에 따라 달라질 수 있으니 관리자 공지나 Bootpay 문의로 확인해요.
서버 인증은 토큰을 직접 관리해야 하나요?
결제 서버 SDK를 쓰면 Basic Auth 인증을 SDK가 처리해요. SDK 없이 직접 호출한다면 Authorization: Basic base64(client_key:secret_key) 헤더를 요청마다 포함해요. 자세한 내용은 API 인증을 봐요.
결제
테스트 결제는 어떻게 하나요?
- 관리자 > 개발자 설정 > API 연동키 (결제)에서 Sandbox 모드의 연동키를 복사해요
- SDK 초기화 시 Sandbox 키를 사용해요
- 결제위젯에서 테스트 결제를 진행해요
- Sandbox 결제는 실제 카드/계좌에서 금액이 차감되지 않아요
지원하는 결제수단은 무엇인가요?
Bootpay는 여러 결제수단을 지원해요. 실제 노출 여부는 계약한 PG사와 관리자에서 활성화한 결제수단에 따라 달라져요:
| 결제수단 | 설명 |
|---|---|
| 신용/체크카드 | 국내 주요 카드사 |
| 계좌이체 | 실시간 계좌이체 |
| 가상계좌 | 무통장 입금 |
| 휴대폰 결제 | 통신사 소액결제 |
| 간편결제 | 카카오페이, 네이버페이, 토스페이 등 |
separately_confirmed는 언제 사용하나요?
결제 승인 전에 서버에서 금액 검증이 필요한 경우 사용해요. extra.separately_confirmed: true로 설정하면:
- 결제 정보 입력 완료 시
confirm이벤트 발생 - 서버에서 주문 금액과 결제 금액 비교
- 서버에서 승인 API를 호출하여 최종 승인
이 방식에서는 클라이언트의 onDone이 호출되지 않아요. onConfirm에서 받은 receipt_id를 백엔드로 전달하고, 프론트엔드는 백엔드 주문 상태를 조회해 결과 화면을 보여줘야 해요.
재고·쿠폰·포인트처럼 승인 직전 서버 판단이 필요하면 extra.separately_confirmed: true를 우선 검토해요. 다만 일부 PG·결제수단은 서버 승인을 지원하지 않을 수 있어요.
주문·상품·구독·링크페이 관련 FAQ 는 FAQ 에서 봐요.
트러블슈팅
인증 문제
Basic Auth 인증 실패
증상: API 호출 시 인증 관련 에러가 발생
원인 및 해결:
- Client Key / Secret Key 확인 — 관리자 > 개발자 설정 > API 연동키 (결제)에서 서버용 키를 복사해요
- Sandbox / Production 모드 확인 — 테스트 시 sandbox 키, 운영 시 production 키를 사용해요
- 키 앞뒤 공백 제거 — 복사 시 포함된 공백을 확인해요
- Authorization 헤더 확인 — 직접 호출한다면
Authorization: Basic base64(client_key:secret_key)형식인지 확인해요
// SDK 사용 시 — SDK가 Basic Auth를 처리
Bootpay.setConfiguration({
client_key: 'YOUR_CLIENT_KEY',
secret_key: 'YOUR_SECRET_KEY'
})
const receipt = await Bootpay.receiptPayment(receiptId)javascript결제위젯 문제
위젯이 표시되지 않음
체크리스트:
client_key가 올바른지 확인- 위젯 렌더링 대상
<div>요소가 DOM에 존재하는지 확인 - 브라우저 콘솔에서 에러 메시지 확인
- CSP(Content-Security-Policy) 설정이 부트페이 도메인을 허용하는지 확인
결제 완료 후 콜백이 호출되지 않음
원인: extra.separately_confirmed: true 설정 시 done이 아니라 confirm 이벤트를 구현해야 해요.
Bootpay.requestPayment({
// ...
extra: { separately_confirmed: true }
})
.on('confirm', function(data) {
// 서버로 receipt_id 전달
// 서버에서 금액·주문 상태 검증 후 승인 API 호출
sendConfirmToServer(data.receipt_id)
})javascript웹훅 문제
웹훅을 수신하지 못함
체크리스트:
- HTTPS 필수 — HTTP URL은 지원하지 않아요
- 방화벽 확인 — 외부에서 웹훅 URL로 접근 가능한지 확인해요
- 200 응답 — 정상 수신한 요청은 빠르게 HTTP 200을 반환해요
- 처리 시간 — 무거운 비즈니스 로직은 비동기로 처리해 타임아웃을 피해야 해요
웹훅 중복 수신
원인: 200 응답을 제때 반환하지 않으면 재시도가 발생해요.
해결: 결제 웹훅은 receipt_id와 status 조합을 기준으로 중복 처리를 방지해요. 같은 결제 상태 변경을 이미 처리했다면 비즈니스 로직을 다시 실행하지 말고 바로 200으로 응답해요.
app.post('/webhook', async (req, res) => {
const { receipt_id, status } = req.body
const dedupeKey = `${receipt_id}:${status}`
// 이미 처리된 결제 상태 변경인지 확인
const exists = await db.webhookLogs.findOne({ where: { dedupe_key: dedupeKey } })
if (exists) {
return res.status(200).json({ success: true })
}
await db.webhookLogs.create({ dedupe_key: dedupeKey })
// 200 먼저 반환
res.status(200).json({ success: true })
// 비동기로 비즈니스 로직 처리
await processWebhook(req.body)
})javascript취소 문제
부분 취소 실패
원인: 일부 카드사/PG사는 부분 취소를 지원하지 않아요.
해결: 전체 취소 후 차액을 재결제하거나, PG사에 부분 취소 지원 여부를 확인해요.
