연동 가이드

문제 해결

증상별 확인

결제창

현상확인할 내용
SDK가 로드되지 않음SDK 주소 https://widget.point3.io/v2/standard의 응답, npm 로더의 loader=toss, CSP의 script-src
SDK가 결제창을 열지 않음SDK 오류 코드, client_id, customerKey, setAmount() 호출 여부와 결과 URL
본인인증 화면이 열리지 않음CSP의 frame-src, 앱이면 WebView의 JavaScript 설정
인증 후 결제 화면이 열리지 않음Referrer-Policy, 결제 세션 상태와 금액
앱에서 결제창의 뒤로가기·메뉴가 눌리지 않음WebView가 safe area 안에 있는지, 결제창 위에 네이티브 버튼이 겹치지 않는지
앱에서 결제 후 결과를 받지 못함앱이 비교하는 successUrl·failUrl 주소
앱에서 결제창이 열리지 않음HTML을 HTTPS base URL로 불러왔는지, WebView의 JavaScript 설정
위임 인증을 전달했는데 문자 인증 화면이 열림위임 인증 만료(5분)나 재사용 여부. 정상적인 전환이므로 결제는 계속 진행
위임 인증 실패 후 결제가 종료됨customerKey에 ANONYMOUS를 전달했는지 확인. 같은 payerId를 전달해야 문자 인증으로 전환

가맹점 서버

현상확인할 내용
결제 세션을 생성하지 못함API 주소, API 키와 HTTP 상태 코드
결제 세션 API가 503 temporarilyUnavailableerror.eob가 있을 때만 EOB로 처리하고 endsAt 확인
위임 인증 생성이 404 PAYER_NOT_FOUND회원 정보에 저장된 payerId를 지금은 사용할 수 없음(탈퇴, 다른 가맹점 값 등). 위임 인증 없이 결제창을 열고, 결제 후 새로 받은 payerId로 회원 정보 갱신
결제 승인 API가 호출되지 않음event.source, event.origin, 메시지 필드, 현재 sessionId, 중복 처리 상태
결제 결과를 확인할 수 없음승인 요청에 사용한 sessionId와 결제 세션 조회 응답의 status
결제 세션이 만료됨기존 세션을 재사용하지 않고 새 결제 세션 생성
승인 또는 취소 결과를 확인할 수 없음성공·실패로 추정하지 않고 식별값과 주문 상태를 보존하며 각각의 상태 조회 API로 확인

응답 코드별 의미는 에러 코드에서 확인하세요.

결과를 확인할 수 없을 때

응답 대기 시간 초과, 네트워크 오류, 응답 유실, 승인 응답의 세션 불일치 또는 결제 취소의 REFUND_TEMPORARY_UNAVAILABLE·REFUND_DUPLICATE_REQUEST는 결과가 확정되지 않은 상황입니다. 다른 409는 에러 코드의 처리 기준을 따르세요. 결과가 불확실한 승인이나 취소 요청을 무조건 다시 보내거나 상품 지급·환불 완료 처리를 하지 마세요.

확인되지 않은 결과를 실패로 처리하지 마세요

결제 결과를 확인할 수 없다면 같은 주문을 바로 다시 결제하거나 상품을 지급하지 마세요. 주문 상태와 결제 내역을 먼저 확인하세요.

결제 승인 결과가 불확실하면 같은 sessionId로 GET /payment/v3/session/{sessionId}를 호출해 status를 확인합니다. captured 또는 failed가 확인될 때까지 주문을 확정하지 마세요.

취소 요청의 결과가 불확실하면 GET /refunds/v1/{sessionId}로 결제 세션의 취소 상태를 조회합니다. refunds에 요청한 항목이 completed로 확인되면 완료 처리하고, failed면 실패 정보를 기록합니다. processing인 활성 항목이 하나 있으면 POST /refunds/v1/{sessionId}/resume으로 처리를 재개할 수 있습니다. 조회 결과만으로 원래 요청을 식별할 수 없거나 조회·재개도 실패하면 요청 식별값과 Idempotency-Key를 보존한 채 point3 담당자에게 문의하세요.

결제 승인 실패 코드

결제 승인 API는 실패가 확정된 경우에도 HTTP 200과 status: "failed"를 반환합니다. 사유는 outcome.code로 구분하세요. outcome.message 문구는 바뀔 수 있어 분기 조건으로 적합하지 않습니다.

outcome.codeoutcome.type의미처리
PAYMENT_METHOD_DECLINEDdeclined결제수단 승인 거절. 잔액 부족, 출금 가능 잔액 부족 등이 해당하며 잔액 부족만 뜻하지는 않음구매자에게 잔액 확인 또는 다른 계좌 선택 안내
PAYMENT_METHOD_LIMIT_EXCEEDEDfailed이체한도 초과구매자에게 이체한도 확인 또는 다른 계좌 선택 안내
PAYMENT_CAPTURE_REJECTEDrejected세부 사유가 분류되지 않은 이체 실패주문·결제 상태 확인, 필요하면 문의
PAYMENT_CAPTURE_ALREADY_FAILEDfailed이미 실패한 결제를 다시 승인 요청함. 원래 사유는 포함되지 않음처음 받은 실패 응답의 기록 확인

잔액 부족으로 승인이 거절된 경우:

JSON코드
{ "id": "pymt_sess-019f0000-0000-7000-9000-000000000000", "object": "paymentCapture", "status": "failed", "outcome": { "type": "declined", "code": "PAYMENT_METHOD_DECLINED", "message": "결제수단 승인이 거절되었습니다." }, "committedAt": "2026-08-21" }

이체한도를 초과한 경우:

JSON코드
{ "id": "pymt_sess-019f0000-0000-7000-9000-000000000000", "object": "paymentCapture", "status": "failed", "outcome": { "type": "failed", "code": "PAYMENT_METHOD_LIMIT_EXCEEDED", "message": "결제수단의 이체한도를 초과했습니다." }, "committedAt": "2026-08-21" }

첫 실패 사유 저장

실패한 결제를 다시 승인 요청하면 원래 사유 대신 PAYMENT_CAPTURE_ALREADY_FAILED가 반환되고, 결제 세션 조회 응답에 원래 사유가 남아 있다고 보장할 수도 없습니다. 그래서 처음 받은 실패 응답의 outcome.code를 주문과 sessionId 기준으로 저장해 두어야 합니다.

  • 저장한 코드는 이후 받은 processing이나 PAYMENT_CAPTURE_ALREADY_FAILED 응답으로 덮어쓰지 않습니다.
  • 같은 주문에서 새 결제 세션을 만들면 실패 기록도 세션별로 따로 둡니다. 이전 세션의 사유를 새 세션에 적용하지 마세요.
  • 사유를 다시 확인하려고 승인 API를 반복 호출하지 마세요.
TypeScriptTypeScript
const sessionId = order.sessionId const res = await capturePayment(sessionId) if (res.status === 'captured') { await markOrderPaid(order.id) } else if (res.status === 'processing') { // 아직 처리 중입니다. 결제 세션을 조회해 결과를 확인합니다. await scheduleSessionStatusCheck(sessionId) } else if (res.status === 'failed') { const code = res.outcome.code // 실패 기록은 주문과 sessionId 단위로 관리합니다. const stored = await findCaptureFailure(order.id, sessionId) // 이 세션의 첫 실패만 저장합니다. if (!stored && code !== 'PAYMENT_CAPTURE_ALREADY_FAILED') { await saveCaptureFailure(order.id, sessionId, code) } switch (stored?.code ?? code) { case 'PAYMENT_METHOD_DECLINED': showMessage('잔액 또는 출금 가능 잔액을 확인하거나 다른 계좌를 선택해주세요.') break case 'PAYMENT_METHOD_LIMIT_EXCEEDED': showMessage('이체한도를 확인하거나 다른 계좌를 선택해주세요.') break default: // PAYMENT_CAPTURE_REJECTED, 기록이 없는 PAYMENT_CAPTURE_ALREADY_FAILED 등 await reviewOrder(order.id) } }

PAYMENT_CAPTURE_ALREADY_FAILED를 받았는데 해당 sessionId의 기록이 없다면 이 코드만으로 원인을 판단하지 말고 point3 담당자에게 문의하세요.

문의하기

시스템 상태에서 장애 여부를 먼저 확인한 뒤, point3 담당자에게 연락해주세요. 어떤 문제가 언제 발생했는지 정리하고, 아래 항목 중 확인할 수 있는 정보를 함께 전달하면 원인을 더 빠르게 파악할 수 있습니다.

  • 발생 시각
  • 문제가 발생한 단계 (결제 세션 생성, 결제창, 결제 승인, 결제 취소 등)
  • sessionId
  • 호출한 API와 받은 HTTP 상태 코드, 오류 코드
  • 브라우저 또는 WebView·OS 정보

민감 정보는 보내지 마세요

API 키, PIN, 계좌번호, 주민등록번호, 인증번호를 문의 내용에 포함하지 마세요.

마지막 수정: