# point3 개발자 센터 > Complete documentation for Large Language Models --- ## Document: 문제 해결 결제창과 API 문제를 단계별로 확인하고, 결과를 알 수 없을 때의 처리와 문의 방법을 안내합니다. URL: /troubleshooting # 문제 해결 ## 증상별 확인 ### 결제창 | 현상 | 확인할 내용 | | --- | --- | | 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 temporarilyUnavailable` | `error.eob`가 있을 때만 EOB로 처리하고 `endsAt` 확인 | | 위임 인증 생성이 `404 PAYER_NOT_FOUND` | 회원 정보에 저장된 `payerId`를 지금은 사용할 수 없음(탈퇴, 다른 가맹점 값 등). 위임 인증 없이 결제창을 열고, 결제 후 새로 받은 `payerId`로 회원 정보 갱신 | | 결제 승인 API가 호출되지 않음 | `event.source`, `event.origin`, 메시지 필드, 현재 `sessionId`, 중복 처리 상태 | | 결제 결과를 확인할 수 없음 | 승인 요청에 사용한 `sessionId`와 결제 세션 조회 응답의 `status` | | 결제 세션이 만료됨 | 기존 세션을 재사용하지 않고 새 결제 세션 생성 | | 승인 또는 취소 결과를 확인할 수 없음 | 성공·실패로 추정하지 않고 식별값과 주문 상태를 보존하며 각각의 상태 조회 API로 확인 | 응답 코드별 의미는 [에러 코드](/error-codes)에서 확인하세요. ## 결과를 확인할 수 없을 때 응답 대기 시간 초과, 네트워크 오류, 응답 유실, 승인 응답의 세션 불일치 또는 결제 취소의 `REFUND_TEMPORARY_UNAVAILABLE`·`REFUND_DUPLICATE_REQUEST`는 결과가 확정되지 않은 상황입니다. 다른 `409`는 [에러 코드](/error-codes#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.code` | `outcome.type` | 의미 | 처리 | | --- | --- | --- | --- | | `PAYMENT_METHOD_DECLINED` | `declined` | 결제수단 승인 거절. 잔액 부족, 출금 가능 잔액 부족 등이 해당하며 잔액 부족만 뜻하지는 않음 | 구매자에게 잔액 확인 또는 다른 계좌 선택 안내 | | `PAYMENT_METHOD_LIMIT_EXCEEDED` | `failed` | 이체한도 초과 | 구매자에게 이체한도 확인 또는 다른 계좌 선택 안내 | | `PAYMENT_CAPTURE_REJECTED` | `rejected` | 세부 사유가 분류되지 않은 이체 실패 | 주문·결제 상태 확인, 필요하면 [문의](#문의하기) | | `PAYMENT_CAPTURE_ALREADY_FAILED` | `failed` | 이미 실패한 결제를 다시 승인 요청함. 원래 사유는 포함되지 않음 | 처음 받은 실패 응답의 기록 확인 | 잔액 부족으로 승인이 거절된 경우: ```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를 반복 호출하지 마세요. ```ts title="TypeScript" 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 담당자에게 문의](#문의하기)하세요. ## 문의하기 [시스템 상태](https://status.point3.io)에서 장애 여부를 먼저 확인한 뒤, point3 담당자에게 연락해주세요. 어떤 문제가 언제 발생했는지 정리하고, 아래 항목 중 확인할 수 있는 정보를 함께 전달하면 원인을 더 빠르게 파악할 수 있습니다. - 발생 시각 - 문제가 발생한 단계 (결제 세션 생성, 결제창, 결제 승인, 결제 취소 등) - `sessionId` - 호출한 API와 받은 HTTP 상태 코드, 오류 코드 - 브라우저 또는 WebView·OS 정보 API 키, PIN, 계좌번호, 주민등록번호, 인증번호를 문의 내용에 포함하지 마세요. --- ## Document: 세금 처리 결제 세션의 총액, 공급가액, 면세 금액과 부가세 처리 규칙을 확인합니다. URL: /tax-processing # 세금 처리 가맹점 서버는 주문의 최종 결제 금액과 세금 구성을 확인한 뒤 [결제 생성](/payment-creation)의 세션 생성 API를 호출합니다. ## 요청 필드 | 필드 | 요청 여부 | 규칙 | | --- | --- | --- | | `amount` | 필수 | 구매자가 지불할 결제 총액입니다. 단위는 대한민국 원(KRW)이며 정수여야 합니다. | | `taxFreeAmount` | 선택 | `amount`에 포함된 면세 금액입니다. 기본 값은 0입니다. | | `vat` | 선택 | `amount`에 포함된 부가세 금액입니다. 입력하지 않으면 point3가 자동 계산합니다. | | `tradeOpt` | 선택 | 도서공연, 대중교통 상품 분류의 결제 시 설정하는 값입니다. | ### 세금 용어 정리 | 용어 | 필드 | 설명 | | --- | --- | --- | | **결제 총액** | `amount` | 구매자가 지불하는 결제의 전체 금액입니다. | | **공급가액** | `supplyAmount` | 과세 대상 공급가액입니다. point3가 자동으로 계산합니다. | | **면세금액** | `taxFreeAmount` | 결제 총액에 포함된 면세 금액입니다. | | **부가세** | `vat` | 결제 총액에 포함된 부가세 금액입니다. | 모든 요청과 응답에는 다음 불변식이 성립합니다. ```javascript amount = supplyAmount + taxFreeAmount + vat supplyAmount = amount - taxFreeAmount - vat ``` ## VAT를 생략했을 때의 계산 `vat`를 생략하면 point3 서버는 다음 순서로 값을 계산합니다. ```javascript taxableGrossAmount = amount - taxFreeAmount vat = Math.round(taxableGrossAmount / 11) supplyAmount = amount - taxFreeAmount - vat ``` `vat`를 직접 전달하면 서버는 자동 계산값으로 바꾸지 않고 검증을 통과한 입력값을 그대로 사용합니다. `taxFreeAmount`가 `amount`와 같은 전액 면세 거래에서는 `vat`를 생략하거나 0으로 전달해야 합니다. ## 계산 예제 | 시나리오 | 요청 | 응답 세금 구성 | | --- | --- | --- | | 일반 과세 | `amount: 10000` | `supplyAmount: 9091`, `vat: 909`, `taxFreeAmount: 0` | | 전액 면세 | `amount: 10000`, `taxFreeAmount: 10000` | `supplyAmount: 0`, `vat: 0`, `taxFreeAmount: 10000` | | 혼합 과세 | `amount: 11000`, `taxFreeAmount: 1000` | `taxableGrossAmount: 10000`, `supplyAmount: 9091`, `vat: 909` | | VAT 직접 지정 | `amount: 12000`, `taxFreeAmount: 1000`, `vat: 500` | `supplyAmount: 10500`, `vat: 500` | 면세 거래를 위해서는 `taxFreeAmount`가 `amount`와 동일하게 설정하여야 합니다. 세율이 10%가 아닌 상품의 경우 `vat`를 직접 입력하여 정확한 세금 구성을 전달할 수 있습니다. 대부분의 가맹점은 `vat`를 직접 입력할 필요가 없습니다. 면세 여부, 영세율과 면세의 구분, 간이과세자·특수세율 적용, 할인 배분과 복합 상품 분류는 가맹점과 세무 전문가의 확인이 필요합니다. 해당 문서에서는 point3가 결제 세션 생성 시 세금 필드를 처리하는 기술적 동작을 안내합니다. ## 결제 취소의 세금 필드 결제 취소의 `refundAmount`, `refundTaxFreeAmount`, `refundVat`는 취소하려는 부분의 금액과 세금 구성을 반영해야 하며, 결제 세션 생성과 달리 자동으로 세금 값을 계산하지 않으므로 호출 시 주의해야 합니다. 필수 필드와 결과 처리 방법은 [결제 취소](/payment-cancellation)를 참고하세요. --- ## Document: 서버 연동 개요 가맹점 서버가 담당하는 결제 생성·승인·결과 확인·취소와 API 호출 규칙을 확인합니다. URL: /server-integration # 서버 연동 개요 가맹점 서버는 결제 세션을 만들고, 결제 승인을 요청하고, 최종 결제 결과를 확인합니다. 결제창은 가맹점 화면이 열지만 결제의 시작과 끝은 모두 가맹점 서버가 결정합니다. API 키를 가맹점 화면이나 앱의 코드, URL 또는 로그에 포함하지 마세요. ## 서버가 하는 일 | 순서 | 작업 | 호출 API | 안내 | | --- | --- | --- | --- | | 1 | 주문 금액을 확인하고 결제 세션 생성 | `POST /payment/v3/session` | [결제 생성](/payment-creation) | | 2 | 가맹점 화면에서 결제 승인 준비 신호를 받으면 승인 요청 | `POST /capture/v2/{sessionId}` | [결제 승인](/payment-capture) | | 3 | 승인 응답이 불확실하면 결제 세션 조회 | `GET /payment/v3/session/{sessionId}` | [결제 승인](/payment-capture#최종-결과-확인) | | 4 | 승인된 결제의 전부 또는 일부 취소 | `POST /refunds/v1/{sessionId}` | [결제 취소](/payment-cancellation) | | 선택 | 로그인한 회원이면 결제창을 열기 전에 위임 인증 생성 | `POST /authn/v2/delegation` | [위임 인증 생성](/delegated-creation) | 전체 흐름과 결제 세션 상태는 [결제 흐름과 상태](/payment-flow)에서 한 번에 확인할 수 있습니다. ## API 호출 규칙 ### 주소 모든 API는 `https://api.point3.io`를 사용합니다. 별도의 테스트 환경 주소는 제공하지 않습니다. ### 인증 대시보드 - 개발정보 관리에서 발급받은 API 키를 `Authorization` 헤더에 담아 보냅니다. ```http Authorization: Bearer {API 키} Content-Type: application/json ``` 브라우저에서 사용하는 `client_id`는 결제창을 열 때 가맹점을 식별하는 값이며, API 키와 다릅니다. ### 재시도 - 결제 승인 API는 같은 `sessionId`로 다시 호출해도 결제가 중복되지 않습니다. 네트워크 오류, 타임아웃, `5xx` 뒤에는 시간을 두고 다시 호출하거나 결제 세션을 조회하세요. - 결제 취소 API는 `Idempotency-Key` 헤더로 24시간 동안 같은 요청의 중복 처리를 막을 수 있습니다. 자세한 내용은 [결제 취소](/payment-cancellation#선택적-멱등-키)를 참고하세요. - `400`, `401`, `403`, `404`는 요청을 고치기 전까지 같은 결과가 반환되므로 그대로 재시도하지 마세요. 응답 코드별 처리 기준은 [에러 코드](/error-codes)에서 확인하세요. ### EOB 점검 시간 매일 23:30부터 다음 날 00:30까지(KST)는 점검 시간으로 결제 세션 생성, 결제 진행, 결제 취소가 제한됩니다. 점검 중 응답 형식은 [EOB 점검 시간](/operations#eob-점검-시간)을 참고하세요. --- ## Document: 보안 점검 서버 인증, 결과 URL, 앱 WebView, 세션과 민감 정보 처리 규칙을 확인합니다. URL: /security # 보안 점검 ## 서버 인증 정보 - API 키는 가맹점 서버의 비밀 저장소에만 보관합니다. - API 키를 브라우저·앱 코드, URL, 로그, 메신저, 이메일 또는 공개 저장소에 포함하지 않습니다. - 브라우저에서 사용하는 `client_id`는 point3가 가맹점을 식별하는 값으로, API 키와 별개의 값입니다. ## 결과 URL - `successUrl`의 `orderId`, `payerId`, `amount`는 브라우저를 거쳐 전달되므로 조작될 수 있습니다. 가맹점 서버가 주문의 `sessionId`와 대조한 뒤 승인합니다. - 결과 URL에 결제 시도 식별값을 추가했다면 현재 시도와 같은지 확인하고, 다른 시도의 결과는 처리하지 않습니다. ## 앱 WebView - HTML의 base URL은 가맹점이 소유한 HTTPS 도메인으로 지정합니다. - 구매자 아이디는 가맹점 서버가 회원 정보에서 꺼내 전달합니다. 기기 저장소에 저장하지 않습니다. - 앱은 지정한 `successUrl`, `failUrl`로 이동하는 경우만 결과로 처리합니다. ## 서버에서 다시 확인할 내용 브라우저 검증을 통과했더라도 가맹점 서버는 현재 구매자의 주문 권한과 주문에 저장된 `sessionId`를 다시 확인합니다. 결제 승인과 결과 조회에 다른 주문이나 세션의 값을 섞어서는 안 됩니다. 화면에서 받은 `payerId`도 조작될 수 있으므로 결제 세션 조회 응답의 값과 대조하세요. 자세한 내용은 [구매자 아이디](/payer-id)를 참고하세요. [위임 인증](/delegated-authentication)을 사용한다면 `payer_id`는 로그인 세션으로 확인한 회원의 회원 정보에서 꺼냅니다. 위임 인증은 문자 인증을 생략하므로, 로그인한 회원 본인의 `payerId`인지 반드시 확인하세요. `successUrl` 이동만으로 상품을 지급하거나 성공 화면을 표시하지 마세요. 결제 승인 API 또는 결제 세션 조회 API 응답으로 최종 결과를 판단합니다. 배포 전에 [라이브 전환 체크리스트](/go-live-checklist)를 함께 확인하세요. --- ## Document: JavaScript SDK 결제 연동 point3 JavaScript SDK로 결제창을 열고 결과를 처리합니다. URL: /sdk-payment-window # JavaScript SDK 결제 연동 point3 JavaScript SDK의 결제창 연동 방법을 알아봅니다. 먼저 [JavaScript SDK 설정 및 초기화](/sdk-environment)을 완료하세요. `widgets()`로 결제 객체를 만들고, 토스페이먼츠 SDK 호환을 위한 금액을 설정한 뒤 `requestPayment()`로 결제창을 엽니다. 실제 결제 금액은 가맹점 서버에서 생성한 point3 결제 세션을 기준으로 결정됩니다. ## 결제 연동 ### point3.widgets() `widgets()` 메서드로 결제 서비스를 초기화합니다. 저장된 point3 `payerId`가 없다면 `ANONYMOUS`를 사용하세요. ```js title="HTML" const customerKey = savedPayerId || Point3.ANONYMOUS const widgets = point3.widgets({ customerKey }) ``` ```js title="패키지 (JavaScript)" import { ANONYMOUS } from '@tosspayments/tosspayments-sdk' const customerKey = savedPayerId || ANONYMOUS const widgets = point3.widgets({ customerKey }) ``` ```ts title="패키지 (TypeScript)" import { ANONYMOUS } from '@tosspayments/tosspayments-sdk' const customerKey: string = savedPayerId ?? ANONYMOUS const widgets = point3.widgets({ customerKey }) ``` `payerId`는 point3가 발급하는 구매자 식별값입니다. 임의로 만들거나 형식을 바꿀 수 없습니다. 자세한 내용은 [구매자 아이디](/payer-id)를 참고하세요. `customerKey`는 토스페이먼츠 SDK 호환을 위해 유지한 파라미터 이름입니다. 가맹점이 임의로 만든 고객 키가 아니라 point3 결제 화면에서 받은 `payerId`를 전달하고, 저장된 `payerId`가 없을 때만 `ANONYMOUS`를 사용하세요. #### 파라미터 | 이름 | 필수 여부 | 타입 | 설명 | | ------------- | --------- | -------- | -------------------------------------------------------------------- | | `customerKey` | 필수 | `string` | 저장된 point3 `payerId`입니다. 값이 없으면 `ANONYMOUS`를 사용합니다. | 로그인한 회원의 결제에서 문자 인증을 생략하려면 `brandpay.redirectUrl` 옵션을 함께 전달합니다. 자세한 내용은 [JavaScript SDK 위임 인증](/sdk-delegated)을 참고하세요. #### 응답 결제 금액을 설정하고 결제창을 열 수 있는 `widgets` 객체를 반환합니다. ### widgets.setAmount() `setAmount()` 메서드로 결제 금액을 전달합니다. `requestPayment()`보다 먼저 호출하세요. ```js title="JavaScript" await widgets.setAmount({ currency: 'KRW', value: 10000 }) ``` ```ts title="TypeScript" await widgets.setAmount({ currency: 'KRW', value: 10000 }) ``` 구매자에게 청구되는 금액은 가맹점 서버가 생성한 결제 세션의 금액입니다. `setAmount()`에는 결제 세션과 같은 금액을 전달하세요. #### 파라미터 | 이름 | 필수 여부 | 타입 | 설명 | | ---------- | --------- | -------- | ------------------------------------------------------- | | `currency` | 필수 | `string` | `KRW`만 지원합니다. | | `value` | 필수 | `number` | 결제 금액입니다. 1원 이상의 정수입니다. | 할인 등으로 실제 결제 금액이 바뀌었다면 변경된 주문 금액으로 새 결제 세션을 생성하세요. 브라우저의 `setAmount()` 값으로 서버가 확정한 결제 금액을 변경할 수 없습니다. #### 응답 설정이 완료되면 별도의 값을 반환하지 않습니다. ### widgets.requestPayment() `requestPayment()` 메서드로 전체 화면 결제창을 엽니다. ```js title="JavaScript" await widgets.requestPayment({ orderId: sessionId, orderName, successUrl, failUrl }) ``` ```ts title="TypeScript" const sessionId: string = paymentSession.id const orderName: string = '아메리카노 외 1건' const successUrl: string = 'https://merchant.example.com/payments/success' const failUrl: string = 'https://merchant.example.com/payments/fail' await widgets.requestPayment({ orderId: sessionId, orderName, successUrl, failUrl }) ``` `successUrl`과 `failUrl`에는 가맹점이 사용하는 결과 페이지의 절대 URL을 전달하세요. point3가 특정 경로를 정하지 않으며, SDK 예제의 변수도 가맹점 환경에서 직접 구성해야 합니다. #### 파라미터 | 이름 | 필수 여부 | 타입 | 설명 | | ------------ | --------- | -------- | ------------------------------------------------------------------------------------ | | `orderId` | 필수 | `string` | 결제 세션 생성 API 응답의 `id`인 point3 `sessionId`입니다. | | `orderName` | 필수 | `string` | 결제 세션의 상품명과 일치하는 공백이 아닌 100자 이하의 주문 상품명입니다. | | `successUrl` | 필수 | `string` | 결제 승인 준비가 끝나면 이동할 가맹점의 HTTPS 절대 URL입니다. | | `failUrl` | 필수 | `string` | 결제 요청을 완료하지 못하면 이동할 가맹점의 HTTPS 절대 URL입니다. | `orderId`는 토스페이먼츠 SDK 호환을 위해 유지한 파라미터 이름입니다. `orderId`에는 point3 결제 세션 생성 API가 반환한 `id`를 입력하세요. 매일 23:30부터 다음 날 00:30까지(KST)는 점검 시간입니다. 결제 세션 생성이 거절되면 구매자에게 `error.eob.endsAt`으로 종료 시각을 안내하고, 그 이후 다시 요청하세요. 응답 예시는 [EOB 점검 시간](/operations#eob-점검-시간)을 참고하세요. 로컬 개발에서는 `localhost`, `127.0.0.1`, `[::1]`에 한해 HTTP 결과 URL을 사용할 수 있습니다. 운영 환경에서는 HTTPS 주소를 사용하세요. #### 응답 결제 승인 준비가 끝나면 SDK가 `successUrl`에 다음 쿼리 파라미터를 추가해 이동합니다. | 이름 | 설명 | | ----------- | ---------------------------------------------------- | | `orderId` | point3 결제 세션 생성 API가 반환한 `id` | | `payerId` | 결제를 진행한 구매자의 [구매자 아이디](/payer-id) | | `amount` | `setAmount()`에 전달한 값 | 토스페이먼츠 SDK와의 호환을 위해 성공 URL에서도 `orderId`라는 이름을 사용합니다. point3 연동에서 `orderId`의 값은 `requestPayment()`에 전달한 결제 세션 `id`, 즉 point3 `sessionId`입니다. 별도의 `sessionId` 쿼리 파라미터는 제공하지 않습니다. 가맹점 서버에 저장한 현재 주문의 `sessionId`와 결과 URL의 `orderId`가 같은지 확인한 뒤 [결제 승인 API](/payment-capture)를 호출하세요. `amount`는 실제 결제 금액의 검증 기준으로 사용하지 말고, 서버에 저장한 주문과 결제 세션의 금액을 기준으로 처리하세요. `payerId`는 다음 결제의 `customerKey`로 다시 사용할 수 있도록 가맹점 회원 정보에 저장하세요. URL에서 받은 값은 조작될 수 있으므로 결제 세션 조회 응답의 `payerId`와 대조하세요. 자세한 내용은 [구매자 아이디](/payer-id)를 참고하세요. 최종 결제 결과는 가맹점 서버가 결제 승인 API 응답을 확인한 뒤 결정하세요. 사용자가 결제창을 닫거나 결제 요청을 완료하지 못하면 SDK가 다음 쿼리 파라미터를 추가한 `failUrl`로 이동합니다. 앱의 WebView에서도 동일하게 동작합니다. 앱에서 결과를 받는 방법은 [연동 방식 선택](/integration-methods#react-native-flutter-네이티브-앱)을 참고하세요. | 이름 | 설명 | | --------- | ------------------------------- | | `code` | 실패 결과를 구분하는 코드 | | `message` | 실패 사유를 설명하는 메시지 | 결제창은 SDK가 자동으로 닫습니다. 실패 결과 화면에서는 `code`에 맞는 안내를 표시하고, 결과 처리는 `message`가 아닌 `code`를 기준으로 하세요. | 코드 | 안내 문구 예시 | | ----------------------- | -------------------------------------------------------------- | | `PAYMENT_WINDOW_CLOSED` | 결제창이 닫혔습니다. | | `PAYER_DEACTIVATED` | 탈퇴가 완료되어 결제가 종료되었습니다. | | `SESSION_EXPIRED` | 결제 세션이 만료되었습니다. 결제를 다시 시도해 주세요. | | `INVALID_SESSION` | 결제 세션 정보를 확인할 수 없습니다. 결제를 다시 시작하세요. | | `PAYMENT_FAILED` | 결제 요청이 거절되었습니다. 예: 결제 금액은 적용 수수료보다 커야 합니다. | SDK는 가맹점이 결과 URL에 미리 추가한 쿼리 파라미터를 유지합니다. 현재 주문이나 결제 시도 식별값이 필요하면 `failUrl`에 직접 추가하세요. `PAYMENT_FAILED`는 결제 요청이 거절됐음을 나타냅니다. 수수료 조건을 충족하지 못한 경우 `message`에 `결제 금액은 적용 수수료보다 커야 합니다.`가 전달됩니다. ## point3 SDK 오류 아래 오류는 `failUrl`로 전달되지 않으며, 오류 객체의 `code`로 확인할 수 있습니다. | 오류 코드 | 설명 | | ----------------------------- | --------------------------------------------------------------------------------------------- | | `INVALID_CLIENT_KEY` | `Point3()`에 전달한 `clientId`가 `client-...` 형식이 아닙니다. | | `INVALID_PAYMENT_REQUEST` | 요청값 또는 SDK 호출 순서가 올바르지 않습니다. | | `PAYMENT_REQUEST_IN_PROGRESS` | 결제나 등록이 진행 중인 상태에서 다시 요청했습니다. | --- ## Document: JavaScript SDK 설정 및 초기화 point3 JavaScript SDK를 설치하고 초기화합니다. URL: /sdk-environment # JavaScript SDK 설정 및 초기화 point3 JavaScript SDK 설치와 초기화 방법을 알아봅니다. 스크립트 태그 또는 npm 패키지로 SDK를 추가하고, point3 `client_id`로 `Point3` 객체를 초기화하면 결제 연동을 시작할 수 있습니다. ## SDK 설치 ### HTML HTML 페이지에 스크립트 태그로 point3 SDK 파일을 추가합니다. 스크립트가 로드되면 전역 객체인 `window.Point3`를 사용할 수 있습니다. ```html ``` HTML 방식에는 npm 패키지 설치와 `import`가 필요하지 않습니다. SDK 주소에 `.js`를 추가하지 마세요. ### 패키지 매니저 point3 SDK는 토스페이먼츠 JavaScript SDK의 npm 로더와 호환됩니다. 현재 호환이 확인된 버전을 버전 고정으로 설치하세요. ```bash title="npm" icon="npm" npm install --save-exact @tosspayments/tosspayments-sdk@2.7.1 ``` ```bash title="pnpm" icon="pnpm" pnpm add --save-exact @tosspayments/tosspayments-sdk@2.7.1 ``` ```bash title="bun" icon="bun" bun add --exact @tosspayments/tosspayments-sdk@2.7.1 ``` ## SDK 초기화 SDK를 초기화하면 반환된 객체로 point3 결제 서비스를 사용할 수 있습니다. HTML에서는 전역 `Point3()` 함수를 호출하고, 패키지로 설치했다면 `loadTossPayments()`의 `src`에 point3 SDK 주소를 지정합니다. ```js title="HTML" const clientId = 'client-...' const point3 = Point3(clientId) ``` ```js title="패키지 (JavaScript)" import { loadTossPayments } from '@tosspayments/tosspayments-sdk' const clientId = 'client-...' const point3 = await loadTossPayments(clientId, { src: 'https://widget.point3.io/v2/standard?loader=toss' }) ``` ```ts title="패키지 (TypeScript)" import { loadTossPayments } from '@tosspayments/tosspayments-sdk' const clientId: string = 'client-...' const point3 = await loadTossPayments(clientId, { src: 'https://widget.point3.io/v2/standard?loader=toss' }) ``` `clientId`에는 point3에서 발급받은 `client-...` 형식의 `client_id`를 입력합니다. #### 파라미터 | 이름 | 필수 여부 | 타입 | 설명 | | ---------- | --------- | -------- | ---------------------------------------------------------- | | `clientId` | 필수 | `string` | point3에서 발급받은 `client-...` 형식의 `client_id`입니다. | #### 응답 초기화된 `point3` 객체를 반환합니다. | 이름 | 타입 | 설명 | | --------- | ---------- | ------------------------------------------------------------------------------------------ | | `widgets` | `function` | 결제를 초기화합니다. 자세한 사용법은 [SDK 결제 연동](/sdk-payment-window)을 참고하세요. | point3는 별도 테스트 환경을 제공하지 않습니다. SDK 주소는 항상 `https://widget.point3.io/v2/standard`를 사용하고, 연동을 확인할 때는 소액 결제로 테스트한 뒤 결제를 취소하세요. 자세한 방법은 [라이브 전환 체크리스트](/go-live-checklist#소액-결제로-테스트하기)를 참고하세요. --- ## Document: JavaScript SDK 위임 인증 가맹점 서버에서 받은 authorization_url로 문자 인증 없이 결제창을 엽니다. URL: /sdk-delegated # JavaScript SDK 위임 인증 JavaScript SDK 결제창에 위임 인증을 적용하는 방법을 알아봅니다. 먼저 [JavaScript SDK 결제 연동](/sdk-payment-window)을 완료하고, 가맹점 서버에서 [위임 인증을 생성](/delegated-creation)해 `authorization_url`을 받으세요. 결제창 연동과 달라지는 것은 `widgets()` 옵션 하나입니다. `setAmount()`, `requestPayment()`와 결과 URL 처리는 그대로 사용합니다. ## 위임 인증 연동 ### point3.widgets() `widgets()`의 `brandpay.redirectUrl`에 `authorization_url`을 전달합니다. `customerKey`에는 위임 인증을 생성한 회원의 `payerId`를 전달합니다. ```js title="HTML" const widgets = point3.widgets({ customerKey: savedPayerId, brandpay: { redirectUrl: authorizationUrl } }) ``` ```js title="패키지 (JavaScript)" const widgets = point3.widgets({ customerKey: savedPayerId, brandpay: { redirectUrl: authorizationUrl } }) ``` ```ts title="패키지 (TypeScript)" const customerKey: string = savedPayerId const redirectUrl: string = authorizationUrl const widgets = point3.widgets({ customerKey, brandpay: { redirectUrl } }) ``` #### 파라미터 | 이름 | 타입 | 설명 | | --- | --- | --- | | `customerKey` | `string` | 위임 인증을 생성한 회원의 `payerId`입니다. | | `brandpay.redirectUrl` | `string` | [위임 인증 생성](/delegated-creation) 응답의 `authorization_url`입니다. 생략하면 위임 인증 없이 결제창이 열립니다. | 위임 인증을 완료하지 못하면 결제창은 `customerKey`의 구매자로 문자 인증을 진행합니다. `customerKey` 필드를 비우거나 `ANONYMOUS`를 전달하지 마세요. #### 응답 [JavaScript SDK 결제 연동](/sdk-payment-window#point3widgets)과 같은 `widgets` 객체를 반환합니다. 이어서 `setAmount()`와 `requestPayment()`를 호출하세요. ### 위임 인증 생성에 실패했을 때 가맹점 서버가 위임 인증을 생성하지 못했다면 `brandpay` 옵션 없이 결제창을 엽니다. ```js title="JavaScript" const widgets = point3.widgets({ customerKey: savedPayerId, ...(authorizationUrl ? { brandpay: { redirectUrl: authorizationUrl } } : {}) }) await widgets.setAmount({ currency: 'KRW', value: 10000 }) await widgets.requestPayment({ orderId: sessionId, orderName, successUrl, failUrl }) ``` ```ts title="TypeScript" const authorizationUrl: string | null = checkout.authorizationUrl const widgets = point3.widgets({ customerKey: savedPayerId, ...(authorizationUrl ? { brandpay: { redirectUrl: authorizationUrl } } : {}) }) await widgets.setAmount({ currency: 'KRW', value: 10000 }) await widgets.requestPayment({ orderId: sessionId, orderName, successUrl, failUrl }) ``` ### 전달 규칙 - `authorization_url`은 받은 문자열을 그대로 전달하세요. 주소, 경로, 쿼리를 바꾸면 SDK가 결제창을 열지 않고 `INVALID_PAYMENT_REQUEST` 오류를 발생시킵니다. - 위임 인증은 결제에만 사용할 수 있습니다. `orderId`를 빈 문자열로 보내는 [등록 전용 위젯](/registration-integration)과 함께 사용하면 `INVALID_PAYMENT_REQUEST` 오류가 발생합니다. - 위임 인증은 한 번의 결제창 실행에만 사용할 수 있습니다. 결제를 다시 시도할 때에는 가맹점 서버에서 새로 생성한 `authorization_url`을 사용하세요. ## 결과 처리 결과 URL은 [JavaScript SDK 결제 연동](/sdk-payment-window#응답)과 같습니다. 결제 승인 준비가 끝나면 `successUrl`로, 결제를 완료하지 못하면 `failUrl`로 이동합니다. 성공 URL의 `orderId`와 `payerId`는 위임 인증을 사용하지 않을 때와 같은 방법으로 확인하고 [결제 승인 API](/payment-capture)를 호출하세요. 위임 인증을 사용해도 최종 결제 결과는 가맹점 서버가 결제 승인 API 응답을 확인한 뒤 결정하세요. ## point3 SDK 오류 아래 오류는 `failUrl`로 전달되지 않으며, 오류 객체의 `code`로 확인할 수 있습니다. | 오류 코드 | 원인 | | --- | --- | | `INVALID_PAYMENT_REQUEST` | `brandpay.redirectUrl`이 위임 인증 생성 응답의 `authorization_url`이 아니거나, 빈 `orderId`와 함께 전달됨 | | `BROWSER_NOT_READY` | 문서 준비 상태, 보안 컨텍스트 또는 `crypto.randomUUID()` 미지원 | 그 밖의 오류는 [JavaScript SDK 결제 연동](/sdk-payment-window#point3-sdk-오류)과 같습니다. --- ## Document: JavaScript SDK 등록 연동 point3 JavaScript SDK로 결제 수단을 등록하고 결과를 처리합니다. URL: /registration-integration # JavaScript SDK 등록 연동 point3 JavaScript SDK로 등록 전용 위젯을 연동합니다. 먼저 [JavaScript SDK 설정 및 초기화](/sdk-environment)을 완료하세요. 등록에는 결제 세션이나 금액 설정이 필요하지 않습니다. **등록 전용 위젯은 필수가 아닌 선택적 기능**으로, 결제 위젯만 연동해도 구매자가 결제 수단을 등록하는 데에 문제가 없습니다. 다만, 사용자 경험을 위하여 선택적으로 등록 전용 위젯을 연동하여 결제 퍼널을 개선할 수 있습니다. 등록 전용 위젯은 결제 위젯과 달리 세션이나 금액 설정이 필요하지 않으며, 구매자가 등록만 진행할 수 있는 별도의 등록창입니다. 앱에서는 [결제창과 같은 방법](/integration-methods#react-native-flutter-네이티브-앱)으로 등록창을 연동한 HTML을 WebView로 띄웁니다. ## 등록 연동 ### point3.widgets() `widgets()` 메서드로 등록 서비스를 초기화합니다. ```js title="JavaScript" const widgets = point3.widgets({ customerKey: Point3.ANONYMOUS }) ``` ```ts title="TypeScript" const widgets = point3.widgets({ customerKey: Point3.ANONYMOUS }) ``` `point3`는 [SDK 설정 및 초기화](/sdk-environment)에서 초기화한 객체입니다. #### 파라미터 | 이름 | 필수 여부 | 타입 | 설명 | | --- | --- | --- | --- | | `customerKey` | 필수 | `string` | `Point3.ANONYMOUS` 고정 | #### 응답 등록창을 열 수 있는 `widgets` 객체를 반환합니다. ### widgets.requestPayment() `requestPayment()`에 `orderId: ''`를 전달하면 등록창을 엽니다. 등록에는 `setAmount()` 호출이나 결제 세션 생성이 필요하지 않습니다. ```js title="JavaScript" await widgets.requestPayment({ orderId: '', orderName: '등록', successUrl: 'https://merchant.example/registration/success', failUrl: 'https://merchant.example/registration/fail' }) ``` ```ts title="TypeScript" await widgets.requestPayment({ orderId: '', orderName: '등록', successUrl: 'https://merchant.example/registration/success', failUrl: 'https://merchant.example/registration/fail' }) ``` #### 파라미터 | 이름 | 필수 여부 | 타입 | 설명 | | --- | --- | --- | --- | | `orderId` | 필수 | `string` | 빈 문자열 `''`을 고정 사용합니다. | | `orderName` | 필수 | `string` | `'등록'` 문자열을 고정 사용합니다. | | `successUrl` | 필수 | `string` | 등록 성공 후 이동할 가맹점의 HTTPS 절대 URL입니다. | | `failUrl` | 필수 | `string` | 등록을 완료하지 못했을 때 이동할 가맹점의 HTTPS 절대 URL입니다. | 로컬 개발에서는 `localhost`, `127.0.0.1`, `[::1]`에 한해 HTTP 결과 URL을 사용할 수 있습니다. 운영 환경에서는 HTTPS 주소를 사용하세요. #### 응답 사용자가 등록창에서 등록을 마치면 SDK가 `successUrl`에 `payerId`를 추가해 이동합니다. 예를 들어 다음과 같이 이동합니다. ```text https://merchant.example/registration/success?payerId=payer:00000000000000000000000000 ``` 결과 페이지에서는 다음과 같이 값을 읽을 수 있습니다. ```js title="JavaScript" const params = new URLSearchParams(window.location.search) const payerId = params.get('payerId') ``` ```ts title="TypeScript" const params = new URLSearchParams(window.location.search) const payerId: string | null = params.get('payerId') ``` 받은 `payerId`를 가맹점 회원 정보에 저장하고, 다음 결제의 `customerKey`로 사용하세요. 이미 결제 세션 조회를 통해 저장된 값이 있다면, 해당 값을 신뢰하고 화면에서 받은 값으로 덮어쓰지 마세요. 자세한 내용은 [구매자 아이디](/payer-id#등록-전용-위젯)를 참고하세요. ## 취소와 오류 처리 취소하거나 등록을 완료하지 못하면 SDK가 `failUrl`에 `code`와 `message`를 추가해 이동합니다. 가맹점 화면은 `message`가 아닌 `code`를 기준으로 처리하고, 다음과 같은 안내를 표시하세요. | 코드 | 안내 문구 예시 | | --- | --- | | `REGISTRATION_CANCELLED` | 등록이 취소됐습니다. | | `AUTHENTICATION_EXPIRED` | 인증이 만료됐습니다. 등록을 다시 시작하세요. | | `REGISTRATION_FAILED` | 등록을 완료하지 못했습니다. 안내를 확인한 뒤 다시 시도하세요. | | `REGISTRATION_RESULT_UNAVAILABLE` | 등록 결과를 확인할 수 없습니다. | 위 표에 없는 `code`는 "등록을 완료하지 못했습니다."로 안내하세요. ### SDK 초기화·시작 전 오류 다음 오류는 `failUrl`로 전달되지 않습니다. SDK 초기화와 등록 요청을 `try/catch`로 감싸고 오류 객체의 `code`를 확인하세요. | 오류 코드 | 처리 | | --- | --- | | `INVALID_CLIENT_KEY` | 발급받은 `client_id`와 `client-...` 형식을 확인하세요. | | `INVALID_PAYMENT_REQUEST` | 빈 `orderId`, `orderName`, 결과 URL을 확인하세요. | | `PAYMENT_REQUEST_IN_PROGRESS` | 이미 열린 등록창의 진행을 마무리하세요. | | `BROWSER_NOT_READY` | 문서 준비 상태, 보안 컨텍스트와 `crypto.randomUUID()` 지원을 확인하세요. | 등록창은 SDK가 닫으므로 가맹점에서 직접 제거하지 마세요. 취소·오류로 종료된 뒤에는 사용자가 버튼을 눌러 다시 시작할 수 있게 하세요. 인증이 만료됐다면 본인인증부터 다시 진행합니다. ## 등록 후 결제 등록 후 결제하려면 가맹점 서버에서 별도의 결제 세션을 생성하세요. [결제 생성](/payment-creation), [SDK 결제 연동](/sdk-payment-window), [결제 승인](/payment-capture)을 참고하세요. --- ## Document: 지급 요청 방법 지급대행으로 입점 셀러에게 정산금을 보내는 흐름, 엑셀 작성법, 지급 실패와 입점 셀러 본인 확인을 안내합니다. URL: /payout # 지급 요청 방법 지급대행은 입점 셀러에게 보낼 정산금을 point3가 가맹점 대신 셀러 계좌로 보내는 서비스입니다. 셀러별 지급 금액은 가맹점이 계산하고, point3는 가맹점이 엑셀로 알려 준 금액을 그대로 보냅니다. 지급대행을 이용하려면 지급대행 계약을 먼저 맺어야 합니다. [지급대행 이용 안내](/contract/payout-overview)를 참고하세요. 지급대행은 API가 아니라 엑셀로 요청합니다. ## 지급 흐름 1. **결제** (구매자) 구매자가 point3로 결제합니다. 1. **지급대행 계좌에 적립** (point3) 정산일에 point3 수수료를 뺀 금액이 point3 지급대행 계좌에 가맹점 몫으로 적립됩니다. 1. **지급 요청 엑셀 제출** (가맹점) 지급일 전날 24시까지 엑셀을 보냅니다. 1. **확인 및 지급** (point3) point3가 금액과 예금주, 지급 예정일을 확인한 뒤 입점 셀러 계좌로 지급합니다. 주말과 공휴일에도 지급합니다. ## 가맹점이 할 일 1. 누구에게 얼마를 언제 보낼지 엑셀에 적습니다. 가맹점 몫도 한 줄로 적으면 함께 지급합니다. 2. 엑셀 제출과 입금을 모두 지급일 전날 24시까지 마칩니다. 엑셀 서식은 아래 [엑셀 작성법](#엑셀-작성법)에서 내려받을 수 있고, 제출처는 point3 담당자가 안내드립니다. 이미 지급된 금액은 되돌리기 어려우므로 보내기 전에 금액과 예금주명을 다시 확인하세요. ## 엑셀 작성법 point3가 제공하는 서식을 내려받아 작성합니다. [지급대행 작성 서식 다운로드](/downloads/payout-request-template.xls) ### 항목 | 항목 | 필수 | 작성 방법 | | --- | --- | --- | | 지급 일자 | 필수 | 입점 셀러 계좌에 입금되어야 하는 날짜입니다. 줄마다 달라도 됩니다. | | 입금은행 | 필수 | 은행 코드 또는 은행 이름을 적습니다. 예: 하나은행은 `081`, `하나`, `하나은행` 중 하나 | | 입금계좌번호 | 필수 | 하이픈(-) 없이 숫자만 적습니다. | | 입금액 | 필수 | 입점 셀러에게 실제로 보낼 금액입니다. 가맹점이 수수료를 빼고 계산한 금액을 적습니다. | | 예금주명 | 선택 | 입금계좌의 예금주 이름입니다. 적으면 실제 예금주 확인 결과와 비교합니다. | | 입금통장표시 | 선택 | 입점 셀러 통장에 표시될 이름입니다. 셀러가 알아볼 수 있게 가맹점 이름 등을 적습니다. | 입점 셀러마다 수수료율이 달라도 됩니다. point3는 적힌 금액을 그대로 지급합니다. ### 작성 예 입점 셀러 한 명에게 지급하고 가맹점 몫도 함께 받는 예입니다. | 지급 일자 | 입금은행 | 입금계좌번호 | 입금액 | 예금주명 | 입금통장표시 | | --- | --- | --- | --- | --- | --- | | 2026-11-14 | 신한은행 | 110123456789 | 1240000 | 김민수 | 우리마켓정산 | | 2026-11-14 | 기업은행 | 01012345678901 | 2150000 | ㈜우리마켓 | 중개수수료 | 두 번째 줄이 가맹점 몫입니다. 열을 추가하거나 순서를 바꾸면 지급 요청을 처리할 수 없습니다. 작성을 마치면 `.xls`, `.csv`, `.txt`(탭으로 분리) 중 하나로 저장해 보내주세요. ## 입점 셀러가 할 일 - 입점 셀러는 가입하거나 서류를 제출하지 않습니다. 다만, 큰 금액을 받는 입점 셀러는 본인 확인이 필요합니다. [입점 셀러 본인 확인](#입점-셀러-본인-확인)을 참고하세요. - 입점 셀러 통장에는 가맹점이 정한 입금통장표시가 표시됩니다. - 사업자등록이 없는 개인 셀러에게도 지급할 수 있습니다. - 주말과 공휴일에도 지급받을 수 있습니다. ## 입점 셀러 본인 확인 특정금융정보법에 따라 큰 금액을 받는 입점 셀러는 본인 확인이 필요합니다. 확인 요청은 point3가 셀러에게 직접 보냅니다. | 조건 | 지급 순서 | | --- | --- | | 한 번에 1,000만 원 이상 지급 | 입점 셀러가 본인 확인을 마친 뒤 지급합니다. | | 같은 입점 셀러에게 7일 동안 2,000만 원 이상 지급 | 먼저 지급하고, 이후 본인 확인을 요청합니다. | 두 조건에 해당하지 않는 입점 셀러는 본인 확인을 하지 않습니다. 본인 확인에 필요한 셀러 연락처는 point3 담당자가 별도로 요청드립니다. 큰 금액을 받을 셀러에게 미리 알려 두면 지급이 늦어지지 않습니다. ## 정산 대기 기간과 지급대행 지급대행으로 보낼 수 있는 금액은 정산이 끝나 지급대행 계좌에 적립된 금액입니다. 아직 정산 대기 중인 결제분은 지급할 수 없습니다. 보증보험 없이 시작하면 정산 대기 기간은 21일입니다. 보증보험에 가입해 표준계약을 맺으면 정산 대기 기간을 최소 5일까지 줄여 입점 셀러에게 더 빨리 지급할 수 있습니다. [시작 방식 선택](/contract/start-options)을 참고하세요. ## 다음 단계 - 계약 방법과 이용료는 [지급대행 이용 안내](/contract/payout-overview)를 참고하세요. --- ## Document: 결제 흐름과 상태 가맹점 화면, 가맹점 서버, point3 사이에서 결제가 진행되는 순서와 결제 세션 상태를 설명합니다. URL: /payment-flow # 결제 흐름과 상태 point3 결제는 가맹점 서버, 가맹점 화면, point3 사이에서 진행됩니다. 세 주체가 주고받는 값과 그 과정에서 결제 세션이 거치는 상태를 확인하세요. ## 전체 흐름 1. 가맹점 화면 → 가맹점 서버: 주문 요청 2. 가맹점 서버 → point3: 결제 세션 생성 — `POST /payment/v3/session` 3. point3 → 가맹점 서버: sessionId 발급 — `status: created` 4. 가맹점 서버 → 가맹점 화면: sessionId 전달 5. 가맹점 화면 → point3: 결제창 열기 - (구매자의 결제 의사 확정) 6. point3 → 가맹점 화면: 승인 준비 신호 — `successUrl로 이동` 7. 가맹점 화면 → 가맹점 서버: sessionId, payerId 전달 8. 가맹점 서버 → point3: 결제 승인 — `POST /capture/v2/{sessionId}` 9. point3 → 가맹점 서버: 승인 결과 — `captured · failed` 결제 세션 생성과 결제 승인은 웹과 앱 모두 같습니다. 결제창은 JavaScript SDK로 열고, 앱에서는 SDK를 연동한 HTML을 WebView로 띄웁니다. 차이는 [연동 방식 선택](/integration-methods)에서 확인하세요. ## 서버에서 승인하는 이유 결제창이 닫히거나 성공 URL로 이동했다고 해서 결제가 완료된 것은 아닙니다. 구매자가 결제 의사를 확정하면 point3는 결제를 승인할 준비가 되었다는 신호만 가맹점 화면에 보냅니다. 실제 결제는 가맹점 서버가 결제 승인 API를 호출할 때 일어납니다. 결제의 최종 결정권을 가맹점 서버에 두기 위해서입니다. 브라우저에서 오는 값은 조작될 수 있으므로, 가맹점 서버는 화면에서 받은 `sessionId`가 현재 구매자의 주문에 저장된 값과 같은지 확인한 뒤에만 승인을 요청합니다. 그리고 승인 API 응답이나 결제 세션 조회 응답의 `status`로만 결제 성공 여부를 판단합니다. `successUrl`로 이동했다고 해서 결제가 완료된 것이 아닙니다. 상품을 지급하거나 성공 화면을 표시하지 마세요. ## 결제 세션 상태 결제 세션은 생성부터 확정까지 다음 상태를 거칩니다. `created` → `identified` → `initiated` → `committed` → 결제 승인 요청 → `processing` / `captured` / `failed` | 상태 | 의미 | 가맹점 서버의 처리 | | --- | --- | --- | | `created` | 세션이 생성되었습니다. | `sessionId`를 가맹점 화면에 전달합니다. | | `identified` | 구매자 본인인증이 끝났습니다. | | | `initiated` | 결제가 진행 중입니다. | | | `committed` | 구매자가 결제 의사를 확정했습니다. | 결제 승인 API를 호출합니다. | | `processing` | 승인 처리 중입니다. | 잠시 기다렸다가 다시 조회합니다. | | `captured` | 결제가 성공했습니다. | 주문을 완료합니다. **최종 상태** | | `failed` | 결제가 실패했습니다. | 실패를 안내합니다. **최종 상태** | 승인 기한이 지나면 승인 API가 `422`로 응답하고, 결제 세션 조회 결과는 `expired`가 됩니다. 이 세션으로는 더 이상 결제할 수 없으므로 새 결제 세션을 만들어 처음부터 다시 시작합니다. `processing`은 잠시 뒤 `captured` 또는 `failed`로 확정됩니다. 최종 상태는 `captured`와 `failed` 두 가지입니다. ## 시간 제약 결제 흐름에는 두 가지 시간 제약이 있습니다. ### 승인 기한 결제 승인 요청은 구매자가 결제 의사를 확정한 당일 자정(KST)까지 유효합니다. 예를 들어 15일 22:00에 구매자가 결제 의사를 확정했다면 16일 00:00 전까지 승인해야 합니다. 구매자가 결제 의사를 확정하면 곧바로 승인을 요청하세요. ### EOB 점검 시간 매일 23:30부터 다음 날 00:30까지(KST)는 점검 시간으로 결제 세션 생성, 결제 진행, 결제 취소가 제한됩니다. 이 시간에 결제창을 열면 구매자가 결제를 마칠 수 없으므로, 결제창을 열기 전에 구매자에게 점검 시간을 안내해야 합니다. 응답 형식은 [EOB 점검 시간](/operations#eob-점검-시간)에서 확인하세요. ## 결과를 알 수 없을 때 네트워크 오류나 타임아웃으로 승인 응답을 받지 못하면 결제가 성공했는지 알 수 없습니다. 이때 같은 주문으로 새 결제를 시작하거나 실패로 처리하면 이중 결제나 누락이 생길 수 있습니다. 결제 승인 API는 같은 `sessionId`로 여러 번 호출해도 결제가 한 번만 일어나도록 설계되어 있습니다. 결과를 알 수 없으면 시간을 두고 같은 `sessionId`로 승인을 다시 요청하거나 결제 세션을 조회하고, `captured` 또는 `failed`가 확인될 때까지 주문을 확정하지 마세요. ## 다음 단계 - 서버 연동 방법은 [서버 연동 개요](/server-integration)를 참고하세요. - 결제창 연동 방법은 [연동 방식 선택](/integration-methods)과 [JavaScript SDK 설정 및 초기화](/sdk-environment)를 참고하세요. --- ## Document: 결제 생성 주문 정보를 사용해 결제 세션을 만들고 결제창에 전달할 값을 준비합니다. URL: /payment-creation # 결제 생성 결제창을 열기 전에 가맹점 서버에서 결제 세션을 생성합니다. API 키와 주문 정보를 보호하기 위해 브라우저에서 직접 호출하지 마세요. ## 요청 값 | 필드 | 필수 | 설명 | | --- | --- | --- | | `amount` | 필수 | 구매자가 결제할 총액 | | `productName` | 필수 | 결제창에 표시할 상품명 | | `displayMerchantName` | 선택 | 결제 UI에 표시할 가맹점 상호명 (미지정 시 `productName` 사용) | | `taxFreeAmount` | 선택 | 총액에 포함된 면세 금액 | | `vat` | 선택 | 총액에 포함된 부가세 | | `tradeOpt` | 선택 | 현금영수증 상품 분류 | ```json title="기본 요청" { "amount": 10000, "productName": "아메리카노 외 1건", "displayMerchantName": "포인트쓰리 스토어" } ``` 10% 세율이 적용되는 일반 상품의 경우 `amount`와 `productName`만 전달하세요. 면세 상품이나 영세율이 적용되는 상품의 경우 [세금 처리](/tax-processing)를 확인하여 결제 세션을 생성하세요. ## 결제 금액과 EOB 점검 시간 적용 수수료보다 큰 금액으로 요청하세요. 일반적인 최소 수수료는 77원이며, 실제 적용 요율과 최소 수수료는 가맹점 대시보드의 계약사항에서 확인할 수 있습니다. 수수료 조건은 결제 최종 확인 단계에서 검증됩니다. 매일 23:30부터 다음 날 00:30까지(KST)는 점검 시간으로 결제 세션을 생성할 수 없습니다. 점검 중에는 `503 temporarilyUnavailable`과 함께 `error.eob`가 반환됩니다. 점검 시작·종료 시각은 `startsAt`과 `endsAt`에 날짜와 UTC 오프셋을 포함합니다. ## 응답 처리 성공하면 `status: created`인 결제 세션을 반환합니다. ```json title="확인할 응답 값" { "id": "pymt_sess-019f0000-0000-7000-9000-000000000000", "status": "created", "amount": 10000 } ``` 1. 응답의 `id`는 해당 결제의 식별자로서, `sessionId`로 사용합니다. 2. 가맹점 서버는 주문에 `sessionId`를 저장한 뒤 가맹점 화면에 전달합니다. 시간 초과나 네트워크 오류, `5xx` 등으로 응답을 받지 못했다면 같은 주문으로 결제 세션을 새로 생성하고, 새로 받은 `sessionId`를 주문에 저장하세요. 결제 세션은 구매자가 결제 의사를 확정하고 가맹점 서버가 결제를 승인해야 결제가 이루어지므로, 응답을 받지 못한 세션은 사용되지 않고 결제되지 않습니다. ## 결제 세션 조회 가맹점 서버는 발급한 세션 식별자로 결제 세션의 진행 상태와 상세 정보를 언제든 조회할 수 있습니다. 결제 승인 API를 호출한 뒤에 상태 조회가 필요할 경우 이 조회 결과로 최종 결제 상태를 확인할 수 있습니다. ```http GET https://api.point3.io/payment/v3/session/{sessionId} ``` 결제 세션의 상태는 다음과 같습니다. | 상태 값 | 의미 | 처리 | | --- | --- | --- | | `created`, `identified`, `initiated` | 결제 진행 중 | | | `committed` | 결제 승인 준비 완료 | 결제 승인 API 호출 | | `processing` | 결제 처리 중 | 잠시 기다렸다가 다시 조회 | | `captured` | 결제 성공 | 결과 확인 완료 | | `failed` | 결제 실패 | 결과 확인 완료 | | `expired` | 결제 승인 기한 만료 | 새 결제 세션으로 다시 시작 | 전체 필드와 오류 응답은 [결제 API 레퍼런스](/api-reference/payment)에서 확인하세요. 결제 세션이 생성되었으면 세션 아이디를 가맹점 화면으로 전달하여 point3 결제창을 표시하도록 해야합니다. [연동 방식 선택](/integration-methods)을 참고하세요. --- ## Document: 결제 승인 결제 승인 API를 호출하고 승인 응답과 결제 세션 조회로 최종 결과를 확인합니다. URL: /payment-capture # 결제 승인 가맹점 화면이 point3 결제창으로부터 결제 승인 준비 신호를 받으면, 가맹점 서버가 결제 승인 API를 호출해 결제를 확정합니다. | 연동 방식 | 승인 준비 신호 | | --- | --- | | 웹, 하이브리드 앱 | `successUrl`로 이동하며 `orderId`, `payerId` 전달 | | 앱 | `successUrl`로 이동하려는 것을 앱이 받아 `orderId`, `payerId` 전달 | 성공 URL 이동만으로 상품을 지급하거나 성공 화면을 표시하지 마세요. ## 승인 요청 ```http POST https://api.point3.io/capture/v2/{sessionId} Authorization: Bearer {API 키} ``` - `{sessionId}`에는 주문에 저장한 `sessionId`를 사용합니다. 화면에서 받은 값은 주문에 저장한 값과 같은지 확인한 뒤에만 사용합니다. - 요청 본문은 없습니다. `payerId`는 승인 API에 전달하지 않습니다. `payerId`는 승인 응답에 포함되지 않습니다. 결제 세션 조회 응답의 `payerId`를 회원 정보에 저장하세요. 자세한 내용은 [구매자 아이디](/payer-id)를 참고하세요. ### 승인 기한 결제 승인 API는 **구매자가 결제 의사를 확정한 당일 자정(KST)까지** 호출해야 합니다. 기한이 지나면 승인 API가 `422` `paymentCaptureExpired`를 반환하며, 새 결제 세션으로 처음부터 다시 시작해야 합니다. 매일 23:30부터 다음 날 00:30까지(KST)는 EOB 점검 시간으로 결제 진행이 제한됩니다. 자세한 내용은 [EOB 점검 시간](/operations#eob-점검-시간)을 참고하세요. ## 승인 응답 | 응답 | `status` | 처리 | | --- | --- | --- | | `200` | `captured` | 결제 성공. 주문 완료 처리 | | `200` | `failed` | 결제 실패. `outcome.code`를 기록하고 실패 안내 | | `202` | `processing` | 처리 중. 잠시 기다렸다가 결제 세션 조회 | | `5xx`, 네트워크 오류, 타임아웃 | - | 결과 미확정. 같은 `sessionId`로 다시 승인 요청하거나 결제 세션 조회 | | `422` | - | 승인 기한 만료. 새 결제 세션으로 다시 시작 | | `400`, `401`, `404` | - | 연동 오류. 요청값과 인증 정보 확인 | `outcome.code`별 의미는 [에러 코드](/error-codes#결제-승인-결과-코드)에서 확인하세요. ## 최종 결과 확인 승인 응답이 `processing`이거나 응답을 받지 못했다면 결제 세션을 조회합니다. 정상 조회는 HTTP `200`으로 반환되며, 응답 본문의 `status`로 최종 결과를 판단합니다. ```http GET https://api.point3.io/payment/v3/session/{sessionId} ``` | 응답 | 처리 | | --- | --- | | `200` + `status: captured` | 결제 성공 | | `200` + `status: failed` | 결제 실패 | | `200` + `status: processing` | 잠시 기다렸다가 같은 세션을 다시 조회 | | `200` + `status: created`, `identified`, `initiated`, `committed` | 결제가 끝난 상태가 아니므로 상품을 지급하지 않음 | | `5xx`, 네트워크 오류, 타임아웃 | 잠시 기다렸다가 같은 세션을 다시 조회 | | `400` | 요청 형식을 수정한 뒤 요청 | | `401` | API 키 확인 | | `404` | 존재하지 않는 결제 세션 | 결제 승인 API는 멱등합니다. 같은 `sessionId`로 다시 호출해도 결제가 중복되지 않습니다. `captured`를 확인하기 전에는 상품을 지급하지 마세요. `captured` 확인 후 취소가 필요하면 [결제 취소](/payment-cancellation)를 확인하세요. 요청·응답 전체 스키마는 [결제 API 레퍼런스](/api-reference/payment)에 있습니다. --- ## Document: 결제 취소 승인된 결제를 전부 또는 일부 취소하고 결과를 안전하게 처리합니다. URL: /payment-cancellation # 결제 취소 가맹점 서버는 승인 완료된 결제를 전부 또는 일부 취소해야 할 때 [결제 취소 API](/api-reference/refund)를 호출합니다. API 키를 가맹점 화면의 코드, URL 또는 메시지에 포함하지 마세요. ## 요청 전 확인 1. 취소할 금액과 세금 구성을 계산합니다. 2. 같은 취소 요청이 중복 처리되지 않도록 가맹점 주문과 요청 식별값을 저장합니다. 다음 조건에서는 새 취소를 요청할 수 없습니다. - 기존 취소가 `PROCESSING` 상태인 경우 - 결제가 취소 가능 상태가 아니거나 취소 가능 잔액이 없는 경우 - 취소 총액·면세액·부가세가 각각의 취소 가능 잔액을 초과하는 경우 - 매일 23:30 이상 00:30 미만(KST)의 EOB 차단 시간대인 경우 ## 금액과 사유 | 필드 | 요청 여부 | 규칙 | | --- | --- | --- | | `refundAmount` | 필수 | 1 이상의 정수인 취소 총액 | | `refundTaxFreeAmount` | 필수 | 0 이상의 정수인 취소 면세 금액 | | `refundVat` | 필수 | 0 이상의 정수인 취소 부가세 금액 | | `reason` | 필수 | 최대 200자인 취소 사유 | `refundTaxFreeAmount + refundVat`은 `refundAmount`를 초과할 수 없습니다. 결제 취소의 `refundAmount`, `refundTaxFreeAmount`, `refundVat`는 취소하려는 부분의 금액과 세금 구성을 반영해야 하며, 결제 세션 생성과 달리 자동으로 세금 값을 계산하지 않으므로 호출 시 주의해야 합니다. 원 결제의 구성은 [세금 처리](/tax-processing)를 참고하세요. ```json title="요청 예시" { "refundAmount": 10000, "refundTaxFreeAmount": 0, "refundVat": 909, "reason": "고객 요청" } ``` 결제 취소 API는 `refundAmount`, `refundTaxFreeAmount`, `refundVat`를 자동으로 계산하지 않습니다. 원 결제의 세금 구성과 취소할 금액을 확인한 뒤 요청하세요. ## 선택적 멱등 키 `Idempotency-Key` 헤더는 선택 사항이며 최대 300자입니다. 키는 가맹점별로 24시간 동안 중복 요청 방지에 사용됩니다. 서버는 같은 키를 다시 받으면 기존 응답을 재생하지 않고 `409 REFUND_DUPLICATE_REQUEST`를 반환합니다. 하나의 논리적 취소 요청에는 하나의 키만 만들고 요청 정보와 함께 저장하세요. 타임아웃이나 응답 유실처럼 결과가 불확실할 때 새 키로 바꾸면 같은 취소가 다시 실행될 수 있습니다. `Idempotency-Key`는 기존 결과를 돌려주는 재실행 기능이 아닙니다. `REFUND_TEMPORARY_UNAVAILABLE`, `REFUND_DUPLICATE_REQUEST`, 타임아웃 또는 응답 유실 뒤에는 새 키로 바꾸거나 같은 취소 생성 요청을 자동으로 다시 호출하지 마세요. 먼저 상태 조회 API를 사용하세요. ## 결과 처리 | 응답 | 처리 방법 | | --- | --- | | `200` | 응답의 `paymentSessionId`와 요청값을 확인하고 환불 항목 `id`, 금액, 세금, 시각을 저장한 뒤 취소 완료 처리 | | `409` | `result.code`를 확인해 결과 미확정, 중복 요청 또는 정책상 요청 불가를 구분 | | `422` | 취소가 명시적으로 거절된 상태로 처리하고 응답 오류 코드와 메시지 기록 | | `400`, `401`, `404` | 요청값, 인증 정보, 결제 세션 연결을 확인하는 연동 오류로 처리 | | `500`, `503`, 네트워크 오류 또는 응답 유실 | 성공·실패를 추정하지 않고 상태 조회 API로 확인 | ## 상태 조회와 처리 재개 취소 생성 결과가 불확실하거나 현재 취소 가능 금액을 확인해야 할 때 다음 조회 API를 호출합니다. ```http title="결제 취소 상태 조회" GET https://api.point3.io/refunds/v1/{sessionId} ``` 조회 응답은 원 결제 금액, 남은 취소 가능 금액, 새 취소 가능 여부와 개별 취소 이력을 반환합니다. | 상태값 | 설명 | | --- | --- | | `status: fullyRefunded` | 원 결제 금액이 모두 취소된 상태 | | `status: partiallyRefunded` | 요청된 부분 취소가 완료된 상태 | | `status: refundable` | 처리 중이거나 완료된 취소가 없는 일반적인 상태 | | `status: processing` | 취소가 모두 완료되지 않은 상태 | | `refunds[].status: completed` | 해당 취소 항목이 완료 처리된 상태 | | `refunds[].status: failed` | 해당 취소 항목이 실패한 상태 | 조회 결과에 `processing` 상태인 활성 취소가 있으면 다음 API로 기존 처리를 재개해야 합니다. ```http title="결제 취소 처리 재개" POST https://api.point3.io/refunds/v1/{sessionId}/resume ``` 상태 조회 후 재개 API를 호출하기 전에 취소 처리가 완료될 수 있습니다. 이 경우 별도 처리를 재개하지 않고 최신 취소 상태를 200으로 반환합니다. ### 주요 409 오류 코드 | `result.code` | 의미 | 처리 방법 | | --- | --- | --- | | `REFUND_TEMPORARY_UNAVAILABLE` | 외부 처리 결과 미확정. 일시 장애 시에는 `503`으로 반환 | 원래 키와 응답 식별값을 보존하고 상태 조회 후 처리 중인 항목만 재개 | | `REFUND_DUPLICATE_REQUEST` | 동일 키를 24시간 안에 재사용 | 상태 조회로 기존 요청 결과를 확인하고 새 취소를 만들지 않음 | | `REFUND_ACTIVE_REQUEST_EXISTS` | 현재 처리 중인 취소 존재 | 새 취소를 만들지 않고 상태 조회 후 기존 처리를 재개 | | `REFUND_NOT_IN_REFUNDABLE_STATE` | 취소 가능한 상태가 아님 | 결제, 취소 상태를 확인하고 요청 종료 | | `EOB_WINDOW_BLOCKED` | 23:30 이상 00:30 미만(KST)의 요청 차단 | 00:30 이후 별도의 새 취소 시도로 요청 | 전체 요청·응답 스키마는 [결제 취소 API 레퍼런스](/api-reference/refund)에서 확인하세요. --- ## Document: 구매자 아이디 (payerId) point3가 구매자를 식별하는 payerId를 받고, 확인하고, 다음 결제에 전달하는 방법을 설명합니다. URL: /payer-id # 구매자 아이디 (payerId) `payerId`는 point3가 제공하는 가맹점 자체 페이에 등록한 구매자를 식별하는 값입니다. 구매자가 계좌를 등록하면 발급되며, 같은 구매자는 항상 같은 값으로 식별됩니다. 1. **가맹점에서 결제하기**: point3 결제를 시작합니다. 2. **휴대폰에서 계좌 등록**: 가맹점 자체 페이에 계좌를 등록합니다. 3. **구매자 아이디 발급**: 등록한 구매자를 payerId로 식별합니다. - 같은 구매자는 다시 결제해도 같은 payerId로 식별됩니다. - 가맹점은 이 값을 회원 정보에 저장해 다음 결제에 사용합니다. | 값 | 형식 | 사용하는 곳 | | --- | --- | --- | | 구매자 아이디 | `payer:...` | 가맹점 화면: 다음 결제의 결제창을 열 때 전달 | 가맹점 서버의 회원 정보에 `payerId`를 저장해 두었다가 다음 결제에 전달하면, 구매자는 일부 인증 단계를 건너뛰고 더 빠르게 결제할 수 있습니다. `payerId`는 point3가 발급하는 값이므로 가맹점이 직접 만들거나 형식을 바꿀 수 없습니다. ## payerId를 받는 곳 | 흐름 | 받는 곳 | 위치 | | --- | --- | --- | | JavaScript SDK 결제 | `successUrl`의 `payerId` 쿼리 파라미터 | 가맹점 화면 | | 등록 전용 위젯 | `successUrl`의 `payerId` 쿼리 파라미터 | 가맹점 화면 | | 결제 세션 조회 API | 응답의 `payerId` | 가맹점 서버 | ## 화면에서 받은 값과 서버에서 조회한 값 가맹점 화면에서 받은 `payerId`는 바로 저장하고 사용할 수 있습니다. 다만 브라우저를 거쳐 전달되는 값이므로, 결제에서는 결제 세션 조회 응답의 `payerId`로 한 번 더 확인하세요. 결제 세션 조회 API는 가맹점 서버가 API 키로 point3에 직접 요청하므로 조작될 수 없습니다. 결제 세션 조회 응답의 `payerId`가 가장 정확한 값입니다. ```http GET https://api.point3.io/payment/v3/session/{sessionId} ``` ```json title="결제 세션 조회 응답 (일부)" { "id": "pymt_sess-019f0000-0000-7000-9000-000000000000", "status": "captured", "payerId": "payer:00000000000000000000000000" } ``` 결제 승인 API 응답에는 `payerId`가 포함되지 않습니다. 결제 결과를 확인할 때 결제 세션 조회 API를 함께 호출하고, 응답의 `payerId`를 사용하세요. ## 저장하기 ### 결제 1. 가맹점 화면에서 받은 `payerId`를 가맹점 서버에 전달합니다. 2. 결제 승인 후 결제 세션을 조회해 응답의 `payerId`가 화면에서 받은 값과 같은지 확인합니다. 3. 결제 세션 조회 응답의 `payerId`를 해당 회원의 값으로 저장합니다. 정상적인 결제에서는 두 값이 같습니다. 값이 다르다면 화면에서 받은 값이 조작된 것이므로, 결제 세션 조회 응답의 `payerId`를 사용하세요. `payerId`는 가맹점 서버의 회원 정보에 저장하고, 다음 결제에서는 로그인한 회원에게 저장된 값을 서버가 전달하세요. 브라우저 저장소나 앱 내부 저장소에 두는 것은 보안 상의 이유로 권장하지 않습니다. ### 등록 전용 위젯 등록 전용 위젯은 결제 세션 없이 진행되므로, 결제처럼 서버에서 결제 세션을 조회해 `payerId`를 확인할 방법이 없습니다. 등록을 마치면 화면에서 받은 `payerId`를 가맹점 서버에 전달하고, 회원 정보에 저장된 값이 없을 때 저장하세요. 이미 결제 세션 조회를 통해 저장된 값이 있다면, 해당 값을 신뢰하고 화면에서 받은 값으로 덮어쓰지 마세요. 결제 세션 조회로 확인한 값은 가맹점 서버가 point3에서 직접 받은 값이지만, 화면에서 받은 값은 브라우저를 거쳐 전달되므로 조작될 수 있습니다. ## 다음 결제에 전달하기 **첫 결제**: 결제창 열기 → 구매자 계좌 등록 → payerId 수신 → 회원 정보에 저장 **다음 결제**: payerId로 결제창 열기 → 일부 인증 단계 생략 → 같은 payerId 수신 → 회원 정보 갱신 저장된 `payerId`가 있으면 다음 결제에 전달합니다. 결제 세션 조회로 확인한 값이 있으면 그 값을 우선 사용하세요. | 연동 방식 | 전달하는 곳 | 저장된 값이 없을 때 | | --- | --- | --- | | JavaScript SDK | `widgets({ customerKey })`의 `customerKey` | `ANONYMOUS` 사용 | ```js title="JavaScript" const widgets = point3.widgets({ customerKey: savedPayerId || Point3.ANONYMOUS }) ``` ```ts title="TypeScript" const customerKey: string = savedPayerId ?? Point3.ANONYMOUS const widgets = point3.widgets({ customerKey }) ``` 자세한 사용법은 [SDK 결제창 연동](/sdk-payment-window#point3widgets)을 참고하세요. 앱에서도 가맹점 서버가 회원 정보의 `payerId`를 전달합니다. 로그인한 회원이라면 가맹점 서버가 저장한 `payerId`로 [위임 인증](/delegated-authentication)을 생성해 결제창의 문자 인증까지 생략할 수 있습니다. ## 주의사항 - 결제 승인 API에는 `payerId`를 전달하지 않습니다. - 구매자가 point3에서 탈퇴하면 결제창이 `PAYER_DEACTIVATED` 코드로 종료됩니다. [에러 코드](/error-codes#javascript-sdk-결제)를 참고하세요. --- ## Document: 운영 정보 운영 주소, EOB 점검 시간, 결제 결과 구분과 문의 방법을 안내합니다. URL: /operations # 운영 정보 ## 결제 결과 구분 | 구분 | 판단 기준 | 처리 방법 | | -------------- | ------------------------------------------------------------------------------ | ---------------------------------------- | | 사용자 종료 | SDK 실패 URL의 `PAYMENT_WINDOW_CLOSED` | 결제 승인 없이 종료 | | 연동 오류 | SDK·메시지 필수값 누락, 세션 불일치 또는 API 인증·권한·요청 형식 오류 | 설정과 요청값 확인 | | 결제 실패 | 결제 승인 API가 명시적인 실패 응답 반환 | 실패 화면 표시 | | 결과 확인 필요 | 응답 대기 시간 초과, 네트워크 오류, 응답 누락 또는 세션 불일치 | 성공·실패로 표시하지 않고 결제 내역 확인 | | 결제 성공 | 승인 응답 또는 결제 세션 조회 결과가 `status: captured` | 주문 성공 처리 | 결제 결과를 확인할 수 없다면 같은 주문을 바로 다시 결제하거나 상품을 지급하지 마세요. 주문 상태와 결제 내역을 먼저 확인하세요. ## 운영 주소 | 용도 | 주소 | | --- | --- | | 본인인증 화면 | `https://auth.point3.io/` | | 결제 화면 | `https://widget.point3.io` | | JavaScript SDK | `https://widget.point3.io/v2/standard` | | 결제 세션 생성 | `POST https://api.point3.io/payment/v3/session` | | 결제 세션 조회 | `GET https://api.point3.io/payment/v3/session/{sessionId}` | | 결제 승인 | `POST https://api.point3.io/capture/v2/{sessionId}` | | 결제 취소 상태 조회 | `GET https://api.point3.io/refunds/v1/{sessionId}` | | 결제 취소 | `POST https://api.point3.io/refunds/v1/{sessionId}` | | 결제 취소 처리 재개 | `POST https://api.point3.io/refunds/v1/{sessionId}/resume` | ## EOB 점검 시간 매일 **23:30부터 다음 날 00:30까지** (KST, 한국 표준시)는 점검 시간으로, 결제 세션 생성과 결제 진행이 제한됩니다. 00:30부터 다시 이용할 수 있습니다. 구매자에게는 “시스템 점검 중입니다. 9월 16일 00:30 이후 다시 이용해주세요.”처럼 종료 시각을 안내하세요. 점검 중에는 HTTP `503` 응답의 `error.eob`에 점검 시작·종료 시각과 시간대가 담깁니다. ```json title="2026-09-16 00:10(KST)에 반환되는 예" { "error": { "type": "serviceUnavailableError", "code": "temporarilyUnavailable", "message": "시스템 점검 시간에는 결제를 진행할 수 없습니다. 2026-09-16T00:30:00+09:00 (Asia/Seoul) 이후 다시 시도해주세요.", "eob": { "startsAt": "2026-09-15T23:30:00+09:00", "endsAt": "2026-09-16T00:30:00+09:00", "timeZone": "Asia/Seoul" } } } ``` `startsAt`은 점검 시작 시각, `endsAt`은 이용 재개 시각입니다. 구매자 안내에는 `endsAt`과 `timeZone`을 사용하세요. 결제 세션 생성이 거절됐다면 종료 시각 이후 다시 요청하세요. 배포 전 확인 항목은 [라이브 전환 체크리스트](/go-live-checklist), 문제가 생겼을 때의 확인 방법과 문의는 [문제 해결](/troubleshooting)을 참고하세요. --- ## Document: 빠른 시작 point3 결제를 연동하는 순서를 한눈에 확인합니다. URL: /introduction # 빠른 시작 point3 결제는 결제 세션 생성, 결제창 열기, 결제 승인의 세 단계로 진행돼요. 가맹점 서버에서 결제 세션을 생성하고, 환경에 따라 [연동 방식을 선택하여](/integration-methods) 결제창을 열고, 결제를 승인하여 완료합니다. 1. **결제 세션 생성** (가맹점 서버) — 주문 금액으로 결제 세션을 만들고 sessionId를 화면에 전달합니다. `POST /payment/v3/session` 2. **결제창 열기** (가맹점 화면) — sessionId로 결제창을 열고, 구매자가 결제 의사를 확정하면 받은 값을 서버에 전달합니다. 3. **결제 승인** (가맹점 서버) — 결제 승인 API를 호출하고 응답의 status로 결과를 확정합니다. `POST /capture/v2/{sessionId}` ## 준비하기 1. 담당자 안내에 따라 대시보드 가입을 완료해주세요. 아직 도입을 결정하지 않았다면 홈페이지에 문의를 남겨주세요. 2. 대시보드의 현금영수증 정보 페이지에서 현금영수증 자동 발급을 활성화 하세요. 3. 대시보드의 개발정보 관리 페이지에서 결제 모듈을 활성화 하세요. 4. 같은 페이지에서 API 키를 발급받고 가맹점 아이디를 확인하세요. | 값 | 형식 | 사용하는 곳 | | --- | --- | --- | | 가맹점 아이디 | `client-...` | 가맹점 화면: 결제창을 열 때 `client_id`로 전달 | | API 키 | `s2scred-...` | 가맹점 서버: API를 호출할 때 `Authorization` 헤더로 전달 | API 키는 가맹점 서버에만 보관하고 브라우저 코드에 넣지 마세요. 에이전트로 연동한다면 다음 프롬프트를 붙여 넣으세요. 자세한 내용은 [AI로 시작하기](/ai-tools)를 참고하세요. ```text Retrieve and follow the instructions at: https://docs.point3.io/agent-integration.md ``` ## 1. 결제 세션 생성 (서버) 결제 세션은 결제 금액, 상품명 등 결제 한 건을 처리할 때 필요한 정보를 담는 point3의 결제 처리 단위입니다. 가맹점 서버가 결제창을 열기 전에 생성하고, 결제 세션의 아이디인 `sessionId`로 이후 결제창 열기부터 승인, 조회, 취소를 진행하게 됩니다. ```bash curl -X POST https://api.point3.io/payment/v3/session \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 10000, "productName": "아메리카노 외 1건" }' ``` 가맹점에서 결제 금액과 상품명을 지정하여 결제 세션 생성을 요청하면 응답으로 세션 아이디를 받습니다. 해당 세션 아이디를 가맹점의 주문과 연결하여 저장해두세요. 자세한 요청 값과 세금 처리는 [결제 생성](/payment-creation)에서 확인하세요. ## 2. 결제창 열기 (화면) 이전 단계에서 생성한 결제 세션의 `sessionId`를 서버에서 화면으로 전달하고, 결제창을 열어야합니다. 결제창을 열면 구매자가 결제 내용을 확인하고 결제 의사를 확정할 수 있습니다. 결제창은 JavaScript SDK로 엽니다. React Native, Flutter, 네이티브 앱은 SDK를 연동한 HTML을 WebView로 띄웁니다. ### JavaScript SDK [SDK를 설치](/sdk-environment)하고 `client_id`로 초기화한 뒤 `requestPayment()`를 호출합니다. `orderId`에는 가맹점 주문번호가 아닌 결제 세션의 `sessionId`를 넣습니다. ```js title="JavaScript" const point3 = Point3(clientId) const widgets = point3.widgets({ customerKey: Point3.ANONYMOUS }) await widgets.setAmount({ currency: 'KRW', value: 10000 }) await widgets.requestPayment({ orderId: sessionId, orderName: '아메리카노 외 1건', successUrl: 'https://merchant.example.com/payments/success', // 결제 요청이 성공하면 이동할 URL failUrl: 'https://merchant.example.com/payments/fail' // 결제 요청이 실패하면 이동할 URL }) ``` ```ts title="TypeScript" const clientId: string = 'client-...' const sessionId: string = paymentSession.id const point3 = Point3(clientId) const widgets = point3.widgets({ customerKey: Point3.ANONYMOUS }) await widgets.setAmount({ currency: 'KRW', value: 10000 }) await widgets.requestPayment({ orderId: sessionId, orderName: '아메리카노 외 1건', successUrl: 'https://merchant.example.com/payments/success', // 결제 요청이 성공하면 이동할 URL failUrl: 'https://merchant.example.com/payments/fail' // 결제 요청이 실패하면 이동할 URL }) ``` 구매자가 결제를 확정하면 SDK가 `successUrl`로 이동하며 `orderId`와 `payerId`를 전달합니다. 두 값은 가맹점 서버가 결제를 최종으로 처리하기 위해 필요합니다. 화면이 값을 받으면 서버에 데이터를 전달하도록 구현해주세요. SDK에 대한 자세한 내용은 [JavaScript SDK 결제 연동](/sdk-payment-window)을 참고하세요. ### 앱 결제하기 화면이 네이티브 앱 화면이라면 결제창을 연동한 HTML 단일 페이지를 앱의 정적 에셋에 포함하고 WebView로 띄우세요. 하이브리드 앱이나 웹 앱이라면, 별도의 HTML 없이 JavaScript SDK를 연동한 결제하기 페이지를 그대로 띄우면 됩니다. 자세한 내용은 [연동 방식 선택](/integration-methods)을 참고하세요. `successUrl`로 이동했다고 해서 결제가 완료된 것이 아닙니다. 상품을 지급하거나 성공 화면을 표시하지 마세요. ## 3. 결제 승인 (서버) 화면에서 받은 값이 주문에 저장한 `sessionId`와 같은지 확인한 뒤 결제 승인 API를 호출합니다. ```bash curl -X POST https://api.point3.io/capture/v2/$SESSION_ID \ -H "Authorization: Bearer $API_KEY" ``` 결과는 HTTP 상태 코드가 아니라 응답 본문의 `status`로 판단합니다. 실패가 확정된 경우에도 HTTP `200`이 반환됩니다. - `captured`: 결제 완료 - `failed`: 결제 실패. 잔액 부족, 이체한도 초과 같은 사유는 `outcome.code`로 구분합니다. 처음 받은 `outcome.code`를 주문에 저장해 두세요. - `processing`(HTTP `202`): 아직 처리 중이며 실패가 아닙니다. 응답을 받지 못했을 때와 같이 `sessionId`로 결제 세션을 조회합니다. 이미 실패한 결제를 다시 승인 요청하면 원래 사유 대신 `PAYMENT_CAPTURE_ALREADY_FAILED`가 반환됩니다. 실패 사유를 확인하려고 승인 API를 다시 호출하지 마세요. 결제 승인 요청은 구매자가 결제 의사를 확정한 당일 자정까지 유효합니다. 실패 코드별 처리 방법은 [문제 해결](/troubleshooting#결제-승인-실패-코드)에서 확인하세요. 자세한 처리 방법은 [결제 승인](/payment-capture)에서 확인하세요. ## 다음 단계 - 승인된 결제의 취소는 [결제 취소](/payment-cancellation)를 참고하세요. - 결제창을 열기 전에 구매자에게 [EOB 점검 시간](/operations#eob-점검-시간)을 안내해주세요. - 구매자에게 결제 수단을 미리 등록받으려면 [등록 전용 위젯](/integration-methods#등록-전용-위젯)을 참고하세요. - 로그인한 회원의 결제에서 문자 인증을 생략하려면 [위임 인증](/delegated-authentication)을 참고하세요. --- ## Document: 연동 방식 선택 결제하기 화면이 웹 페이지인지 앱 화면인지에 따라 JavaScript SDK로 결제창을 연동하는 방법을 고릅니다. URL: /integration-methods # 연동 방식 선택 point3 결제창은 웹과 앱 모두 JavaScript SDK로 연동합니다. 결제하기 화면을 무엇으로 만들었는지에 따라 SDK를 연동하는 위치만 달라집니다. ## 서비스 형태로 고르기 | 서비스 형태 | 연동 방법 | | --- | --- | | 웹, 하이브리드 앱 | 결제하기 페이지에 SDK를 연동합니다. | | React Native, Flutter, 네이티브 앱 | SDK를 연동한 HTML을 WebView로 띄웁니다. | 결제 세션 생성과 결제 승인은 웹과 앱 모두 같습니다. ## 웹, 하이브리드 앱 결제하기 페이지에 [JavaScript SDK](/sdk-environment)로 결제창을 연동합니다. 결제 버튼을 누르면 SDK가 전체 화면 결제창을 띄우고, 구매자가 결제를 확정하면 가맹점이 정한 `successUrl`로 이동합니다. 가맹점은 성공·실패 결과 페이지만 만들면 됩니다. 앱 안의 WebView에서 결제하기 페이지를 보여 주는 하이브리드 앱도 같은 방법으로 연동합니다. 앱에서는 아래 [WebView 화면](#webview-화면) 기준만 확인하세요. 시작하기: [JavaScript SDK 설정 및 초기화](/sdk-environment) → [JavaScript SDK 결제 연동](/sdk-payment-window) ## React Native, Flutter, 네이티브 앱 앱에서는 JavaScript SDK로 결제창을 연동한 HTML을 WebView로 띄워 결제를 진행할 수 있습니다. 결제하기 화면은 앱 화면 그대로 두고, 결제가 필요할 때만 WebView를 띄우면 됩니다. HTML은 간단한 단일 페이지로 충분합니다. 웹 연동과 같은 방법으로 SDK를 호출하고, `sessionId` 등 결제에 필요한 값은 가맹점 서버에서 받아 전달합니다. 결제가 끝나면 결제창이 `successUrl` 또는 `failUrl`로 이동하므로, 앱은 WebView에서 이 이동을 감지해 결과를 처리할 수 있습니다. 결과 값은 [웹과 같습니다](/sdk-payment-window#응답). HTML을 앱의 정적 에셋으로 포함할 경우, `file://`로 불러오면 결제창이 열리지 않습니다. 가맹점 도메인(예: `https://merchant.example.com`)을 base URL로 지정해 불러오면 됩니다. 이때 `successUrl`과 `failUrl`은 앱이 이동을 감지해 처리한다면 실제로 존재하는 페이지가 아니어도 됩니다. HTML을 가맹점 서버에서 제공할 경우에는 그 주소를 WebView로 열면 됩니다. 시작하기: [JavaScript SDK 설정 및 초기화](/sdk-environment) → [JavaScript SDK 결제 연동](/sdk-payment-window) ### WebView 화면 결제창을 WebView로 띄울 경우 다음을 확인하세요. 하이브리드 앱도 같습니다. - 결제창 상단에는 뒤로가기 버튼과 메뉴가 있습니다. WebView가 상태바나 노치에 가려지지 않도록 safe area 안에 띄워 주세요. - 구매자는 결제창의 뒤로가기로 결제를 취소할 수 있고, 이때 `failUrl`로 `PAYMENT_WINDOW_CLOSED`가 전달됩니다. 결제창 위에 별도의 닫기 버튼을 두지 마세요. - 결제 중에 시스템 뒤로가기나 스와이프로 WebView가 닫히거나 이전 페이지로 이동하면 결과 없이 결제가 끝납니다. 결제창이 떠 있는 동안에는 막아 두는 것을 권장합니다. - WebView에서 JavaScript를 활성화해 주세요. ## 등록 전용 위젯 결제 없이 구매자의 결제 수단만 미리 등록받는 위젯입니다. 등록이 끝나면 `payerId`를 받고, 이 값을 다음 결제에 전달하면 구매자가 일부 인증 단계를 건너뛸 수 있습니다. 등록 전용 위젯은 필수가 아닙니다. 결제창만 연동해도 구매자는 첫 결제 과정에서 결제 수단을 등록할 수 있습니다. 회원가입 직후처럼 결제 전에 등록을 유도해 결제 단계를 짧게 만들고 싶을 때 선택적으로 연동하세요. 앱에서는 결제창과 같은 방법으로 등록창을 연동한 HTML을 WebView로 띄웁니다. 시작하기: [JavaScript SDK 등록 연동](/registration-integration) --- ## Document: 라이브 전환 체크리스트 운영 배포 전에 가맹점 서버·화면·운영 환경에서 확인할 항목과 필수 시나리오를 점검합니다. URL: /go-live-checklist # 라이브 전환 체크리스트 point3는 별도의 테스트 환경을 제공하지 않습니다. 운영 배포 전에 아래 항목을 모두 확인하고, 필수 시나리오가 기대 결과대로 동작하는지 점검하세요. ## 소액 결제로 테스트하기 모든 결제는 실제로 처리되므로 연동 테스트는 운영 환경에서 소액 결제로 진행합니다. 1. 적용 수수료보다 큰 소액(예: 100원)으로 결제 세션을 생성합니다. 수수료 이하의 금액은 결제가 거절됩니다. 2. 결제창에서 결제를 마치고 결제 승인까지 확인합니다. 3. 확인이 끝나면 [결제 취소](/payment-cancellation)로 결제를 취소합니다. 최소 수수료와 적용 요율은 대시보드의 계약사항에서 확인할 수 있습니다. ## 배포 전 확인하기 ### 가맹점 서버 - [ ] API 키를 서버에만 보관했습니다. - [ ] 서버에 저장된 주문 금액으로 결제 세션을 생성합니다. - [ ] 주문과 `sessionId`를 연결하고, 해당 `sessionId`로 결제 승인 API를 호출합니다. - [ ] EOB 점검 시간과 시간대를 결제창을 열기 전에 구매자에게 안내합니다. - [ ] 승인 요청 후 응답 결과가 불확실하면 같은 `sessionId`로 결제 세션을 조회하여 최종 결과를 확인합니다. - [ ] 성공·실패·결과 확인 필요 상태를 구분합니다. - [ ] 결제 후 결제 세션 조회 응답의 `payerId`로 화면에서 받은 값을 확인하고 회원 정보에 저장합니다. - [ ] 위임 인증을 사용한다면 로그인한 회원의 회원 정보에 저장된 `payerId`로만 생성하고, 생성에 실패하면 위임 인증 없이 결제창을 엽니다. ### JavaScript SDK - [ ] SDK를 `https://widget.point3.io/v2/standard`에서 불러오고, 대시보드에서 확인한 가맹점 아이디를 `clientId`로 사용합니다. - [ ] `requestPayment()`의 `orderId`에 주문의 `sessionId`를 전달합니다. - [ ] `setAmount()`에 결제 금액을 전달합니다. - [ ] 저장된 `payerId`가 있으면 `customerKey`로, 없으면 `ANONYMOUS`를 전달합니다. - [ ] 위임 인증을 사용한다면 `authorization_url`을 가공하지 않고 `brandpay.redirectUrl`로 전달하며, `customerKey`에도 같은 `payerId`를 전달합니다. - [ ] `successUrl`과 `failUrl`에는 HTTPS 절대 URL을 사용합니다. 결제 시도를 구분할 값이 필요하면 가맹점이 URL에 직접 추가합니다. - [ ] 성공 URL의 `orderId`가 현재 주문의 `sessionId`와 같은지 확인합니다. - [ ] `failUrl`에서는 `code`에 맞는 안내를 표시하고 결제 승인 API를 호출하지 않습니다. - [ ] 결제창은 SDK가 닫으므로 가맹점이 직접 제거하지 않습니다. ### 앱 - [ ] SDK를 연동한 HTML을 가맹점 HTTPS 도메인을 base URL로 지정해 불러옵니다. - [ ] WebView를 safe area 안에 띄우고, 결제창 위에 네이티브 버튼을 두지 않습니다. - [ ] 결제창이 떠 있는 동안 시스템 뒤로가기를 막습니다. - [ ] `successUrl`, `failUrl`로의 이동을 받아 WebView를 닫고 결과를 처리합니다. ### 운영 환경 - [ ] 대시보드에서 확인한 가맹점 아이디와 API 키를 사용합니다. - [ ] CSP를 사용한다면 SDK는 `script-src`에 `https://widget.point3.io`를, 결제창은 `frame-src`에 `https://auth.point3.io`와 `https://widget.point3.io`를 허용했습니다. - [ ] API 키, PIN, 계좌정보, 인증번호를 로그에 남기지 않습니다. ## 필수 테스트 | 시나리오 | 기대 결과 | | --- | --- | | 신규 구매자 | `customerKey`에 `ANONYMOUS`를 전달해 결제창을 열고 결제 완료 | | 기존 구매자 | 저장된 `payerId`를 전달해 결제창을 열고 결제 완료 | | 결제 완료 후 `payerId` 저장 | 결제 세션 조회 응답의 `payerId`와 화면 값이 같은지 확인하고 회원 정보에 저장 | | 등록 전용 위젯으로 등록 완료 | 저장된 `payerId`가 없을 때만 받은 값을 저장하고, 결제 세션 조회로 확인한 값은 덮어쓰지 않음 | | 위임 인증 (사용하는 경우) | 로그인한 회원의 `payerId`로 위임 인증을 생성해 문자 인증 없이 결제 완료 | | 위임 인증 생성 실패 (사용하는 경우) | `brandpay` 옵션 없이 결제창을 열고 결제 완료 | | 성공 URL의 `orderId`가 주문의 `sessionId`와 다름 | 결제 승인 API를 호출하지 않음 | | 같은 성공 URL을 다시 받음 | 결제 승인 한 번만 요청 | | 앱: 결제창에서 뒤로가기를 눌러 결제 취소 | `failUrl`의 `PAYMENT_WINDOW_CLOSED`를 받아 WebView를 닫고 안내 | | 앱: 결제창이 떠 있을 때 시스템 뒤로가기 | 결제창이 그대로 유지됨 | | SDK가 `failUrl`로 이동 | 승인 API 호출 없이 `code`에 맞는 안내 표시 | | 결제창이 `SESSION_EXPIRED`로 종료 | 새 결제 세션을 생성해 다시 시작 | | EOB 점검 시간에 결제 세션 생성 | `503`과 `error.eob`를 받고, `endsAt`으로 이용 재개 시각을 안내 | | EOB 점검 시간에 결제창에서 결제 확정 | 결제창이 점검 안내를 표시하고, 가맹점 화면에 승인 준비 신호가 오지 않음 | | EOB 점검 시간에 결제 취소 요청 | `409 EOB_WINDOW_BLOCKED`를 받고 00:30(KST) 이후 다시 요청 | | 결제 의사 확정 당일 자정(KST)이 지난 뒤 승인 요청 | `422` `paymentCaptureExpired`를 확인하고 새 결제 세션으로 다시 시작 | | 조회 응답 `status: created`, `identified`, `initiated`, `committed` | 아직 결제가 끝나지 않은 상태이므로 상품을 지급하지 않음 | | 조회 응답 `status: processing` | 잠시 기다렸다가 같은 `sessionId`로 다시 조회 | | 조회 응답 `status: captured` | 결제 성공으로 처리하고 결과 확인을 마침 | | 조회 응답 `status: failed` | 결제 실패로 처리하고 결과 확인을 마침 | | 조회 응답 `5xx`, 응답 대기 시간 초과 또는 네트워크 오류 | 성공·실패로 추정하거나 결제 요청을 무조건 반복하지 않고, 식별값을 보존한 뒤 상태 조회 재시도 | | 결제 취소 `REFUND_TEMPORARY_UNAVAILABLE`, 중복 키 또는 응답 유실 | 원래 키와 식별값을 보존하고 상태 조회 API로 확인한 뒤, 처리 중인 취소만 재개 | 문제가 생기면 [문제 해결](/troubleshooting)과 [에러 코드](/error-codes)를 참고하세요. --- ## Document: 에러 코드 point3 API 응답과 결제창·SDK가 전달하는 코드의 의미와 가맹점의 처리 방법을 정리합니다. URL: /error-codes # 에러 코드 point3가 반환하는 코드를 출처별로 정리했습니다. 처리 기준은 HTTP 상태 코드와 응답 본문의 코드를 함께 보고 판단하세요. 사용자 안내 문구는 `message` 원문 대신 코드별로 가맹점이 정한 문구를 사용하는 것을 권장합니다. `5xx`, 타임아웃, 네트워크 오류, 응답 유실은 결과가 확정되지 않은 상태입니다. 같은 요청을 무조건 반복하거나 실패로 처리하지 말고 상태 조회 API로 확인하세요. ## HTTP 상태 코드 공통 기준 | 상태 코드 | 의미 | 재시도 | 처리 | | --- | --- | --- | --- | | `200` | 정상 응답 | - | 응답 본문의 `status`로 결과 판단 | | `202` | 처리 중 | 조회 | 잠시 기다렸다가 상태 조회 | | `400` | 요청 형식 오류 | 안 함 | 요청값 수정 | | `401` | 인증 실패 | 안 함 | API 키 확인 | | `404` | 리소스 없음 | 안 함 | `sessionId` 확인 | | `409` | 현재 상태에서 처리 불가 (결제 취소 API) | 코드별 | 본문의 `result.code` 확인 | | `422` | 요청 거절 | 안 함 | 결제 승인: 승인 기한 만료. 결제 취소: 거절로 처리하고 코드 기록 | | `500` | 서버 오류 | 조회 | 결과 미확정. 상태 조회로 확인 | | `503` | 일시 사용 불가 | 조회 | 결제 세션 API: `error.eob`가 있으면 EOB 점검. 결제 취소 API: 결과 미확정이므로 상태 조회 | ## 결제 세션·결제 승인 API 결제 세션 API와 결제 승인 API의 오류 응답은 `error.type`과 `error.code`를 포함합니다. ```json title="오류 응답 형식" { "error": { "type": "authenticationError", "code": "authenticationRequired", "message": "인증에 실패했습니다." } } ``` | HTTP | `error.type` | `error.code` | 처리 | | --- | --- | --- | --- | | `400` | `invalidRequestError` | `invalidRequest` | 요청 형식과 필수 필드 확인 | | `401` | `authenticationError` | `authenticationRequired` | `Authorization` 헤더와 API 키 확인 | | `404` | `notFoundError` | `resourceMissing` | `sessionId` 확인 | | `422` | `invalidRequestError` | `paymentCaptureExpired` | 결제 승인 기한 만료. 새 결제 세션으로 다시 시작 | | `500` | `apiError` | `internalError` | 결과 미확정. 결제 세션 조회 | | `503` | `serviceUnavailableError` | `temporarilyUnavailable` | `error.eob`가 있으면 `endsAt` 이후 재요청, 없으면 잠시 후 결제 세션 조회 | `error.eob`는 결제 세션 API에서만 반환되며 `startsAt`, `endsAt`, `timeZone`을 포함합니다. EOB 점검 중 응답 예시는 [EOB 점검 시간](/operations#eob-점검-시간)을 참고하세요. ### 결제 승인 결과 코드 결제 승인 API가 `200`으로 응답하면 `outcome.code`로 결과를 구분합니다. | `status` | `outcome.type` | `outcome.code` | 의미 | 처리 | | --- | --- | --- | --- | --- | | `captured` | `succeeded` | `PAYMENT_CAPTURED` | 결제 성공 | 주문 완료 | | `failed` | `declined` | `PAYMENT_METHOD_DECLINED` | 결제 수단 승인 거절 | 다른 결제 수단 안내 | | `failed` | `paymentMethodUnavailable` | `PAYMENT_METHOD_UNAVAILABLE` | 결제 수단 사용 불가 | 다른 결제 수단 안내 | | `failed` | `serviceUnavailable` | `PAYMENT_SERVICE_UNAVAILABLE_HOURS` | 은행·기관 점검 또는 운영시간 외 | 다른 계좌 선택 또는 점검 종료 후 새 결제 | | `failed` | `rejected` | `PAYMENT_CAPTURE_REJECTED` | 승인 요청 거절 | 실패 안내 | | `failed` | `failed` | `PAYMENT_METHOD_LIMIT_EXCEEDED` | 결제 수단의 이체 한도 초과 | 다른 결제 수단 또는 금액 안내 | | `failed` | `failed` | `PAYMENT_CAPTURE_ALREADY_FAILED` | 이미 실패한 결제 | 실패로 처리하고 새 결제 | `failed`의 `outcome.message`에는 은행이 반환한 사유가 담길 수 있습니다. 화면 분기는 `outcome.code`로 하세요. `202` 응답은 `PAYMENT_CAPTURE_PROCESSING`이며, 잠시 기다렸다가 결제 세션을 조회합니다. ## 결제 취소 API 결제 취소 API의 오류 응답은 `result.code`를 포함합니다. ```json title="오류 응답 형식" { "status": 409, "timestamp": "2026-08-21T00:00:00.000Z", "result": { "code": "REFUND_DUPLICATE_REQUEST", "message": "동일한 Idempotency-Key로 요청이 이미 접수되었습니다" } } ``` ### 409 응답 | `result.code` | 의미 | 처리 | | --- | --- | --- | | `REFUND_TEMPORARY_UNAVAILABLE` | 외부 처리 결과 미확정. 일시 장애 시에는 `503`으로 반환 | 원래 키와 응답 식별값을 보존하고 상태 조회 후 처리 중인 항목만 재개 | | `REFUND_DUPLICATE_REQUEST` | 동일 `Idempotency-Key`를 24시간 안에 재사용 | 상태 조회로 기존 요청 결과를 확인하고 새 취소를 만들지 않음 | | `REFUND_ACTIVE_REQUEST_EXISTS` | 현재 처리 중인 취소 존재 | 새 취소를 만들지 않고 상태 조회 후 기존 처리를 재개 | | `REFUND_NOT_IN_REFUNDABLE_STATE` | 취소 가능한 상태가 아님 | 결제·취소 상태를 확인하고 요청 종료 | | `EOB_WINDOW_BLOCKED` | 23:30 이상 00:30 미만(KST) 요청 | 00:30 이후 새 취소 요청 | ### 그 외 오류 코드 | HTTP | `result.code` | 의미 | | --- | --- | --- | | `400` | `REFUND_VALIDATION_FAILED` | 요청 형식 오류 | | `400` | `NON_POSITIVE_REFUND_AMOUNT` | 취소 금액이 0 이하 | | `400` | `REFUND_TAX_BREAKDOWN_INVALID` | 면세 금액·부가세 구성이 올바르지 않음 | | `404` | `REFUND_NOT_FOUND` | 취소할 결제를 찾을 수 없음 | | `422` | `AMOUNT_EXCEEDS_REFUNDABLE` | 취소 금액이 취소 가능 금액을 초과 | | `422` | `TAX_FREE_AMOUNT_EXCEEDS_REFUNDABLE` | 면세 금액이 취소 가능 면세 금액을 초과 | | `422` | `VAT_EXCEEDS_REFUNDABLE` | 부가세가 취소 가능 부가세를 초과 | ### 취소 실패 코드 취소 상태 조회 결과에서 `failed`인 취소 항목은 실패 사유 코드를 포함합니다. | 코드 | 의미 | | --- | --- | | `refundMethodDeclined` | 환불 수단 승인 거절 | | `refundMethodUnavailable` | 환불 수단 사용 불가 | | `refundServiceUnavailable` | 은행 서비스 운영시간 외 | | `refundRejected` | 환불 요청 거절 | | `refundFailed` | 환불을 완료할 수 없음 | 취소 처리 흐름은 [결제 취소](/payment-cancellation)를 참고하세요. ## 위임 인증 API 위임 인증 생성 API의 오류 응답은 `result.code`를 포함합니다. 어떤 오류든 위임 인증 없이 결제창을 열면 결제를 계속할 수 있습니다. | HTTP | `result.code` | 처리 | | --- | --- | --- | | `400` | `HTTP_ERROR` | `payer_id` 형식과 추가 필드 확인 | | `401` | `HTTP_ERROR` | `Authorization` 헤더와 API 키 확인 | | `404` | `PAYER_NOT_FOUND` | 저장된 `payerId`를 사용할 수 없음. 위임 인증 없이 결제창을 열고, 결제 후 새로 받은 `payerId` 저장 | | `503` | | 일시적 장애. 위임 인증 없이 결제창 열기 | 자세한 내용은 [위임 인증 생성](/delegated-creation#오류-처리)을 참고하세요. ## 결제창 코드 ### JavaScript SDK 결제 `failUrl`의 `code` 쿼리 파라미터로 전달됩니다. 결제 승인 API를 호출하지 않습니다. | 코드 | 의미 | 안내 문구 예시 | | --- | --- | --- | | `PAYMENT_WINDOW_CLOSED` | 사용자가 결제창을 닫음 | 결제창이 닫혔습니다. | | `PAYER_DEACTIVATED` | 구매자 탈퇴 완료 | 탈퇴가 완료되어 결제가 종료되었습니다. | | `SESSION_EXPIRED` | 결제 세션 만료 | 결제 세션이 만료되었습니다. 결제를 다시 시도해 주세요. | | `INVALID_SESSION` | 결제 세션 확인 불가 | 결제 세션 정보를 확인할 수 없습니다. 결제를 다시 시작하세요. | | `PAYMENT_FAILED` | 결제 요청 거절 (예: 금액이 수수료 이하) | 결제 요청이 거절되었습니다. | ### JavaScript SDK 등록 `failUrl`의 `code` 쿼리 파라미터로 전달됩니다. | 코드 | 의미 | | --- | --- | | `REGISTRATION_CANCELLED` | 사용자가 등록을 취소함 | | `AUTHENTICATION_EXPIRED` | 인증 만료. 본인인증부터 다시 시작 | | `REGISTRATION_FAILED` | 등록 실패 | | `REGISTRATION_RESULT_UNAVAILABLE` | 등록 결과 확인 불가 | ### SDK 초기화·호출 오류 `failUrl`로 전달되지 않고 SDK 호출에서 발생하는 오류 객체의 `code`로 확인합니다. `try/catch`로 처리하세요. | 코드 | 원인 | | --- | --- | | `INVALID_CLIENT_KEY` | `client_id`가 `client-...` 형식이 아님 | | `INVALID_PAYMENT_REQUEST` | 요청값 누락 또는 SDK 호출 순서 오류. 위임 인증의 `authorization_url`을 가공했거나 등록 전용 위젯에 전달한 경우 포함 | | `PAYMENT_REQUEST_IN_PROGRESS` | 결제나 등록이 진행 중인 상태에서 다시 요청 | | `BROWSER_NOT_READY` | 문서 준비 상태, 보안 컨텍스트 또는 `crypto.randomUUID()` 미지원 (등록) | --- ## Document: 위임 인증 생성 로그인한 회원의 payerId로 위임 인증을 생성하고 결제창에 전달할 authorization_url을 준비합니다. URL: /delegated-creation # 위임 인증 생성 로그인한 회원의 결제에서 문자 인증을 생략하려면 결제창을 열기 전에 가맹점 서버에서 위임 인증을 생성합니다. 위임 인증의 개념과 흐름은 [위임 인증](/delegated-authentication)을 참고하세요. 위임 인증은 API 키로만 생성할 수 있습니다. 브라우저에서 직접 호출하지 마세요. ## 요청 값 ```http POST https://api.point3.io/authn/v2/delegation ``` | 필드 | 필수 | 설명 | | --- | --- | --- | | `payer_id` | 필수 | 로그인한 회원의 `payerId`. `payer:`로 시작하는 128자 이하의 값 | ```json title="기본 요청" { "payer_id": "payer:00000000000000000000000000" } ``` `payer_id`는 로그인 세션으로 확인한 회원의 회원 정보에서 꺼내세요. 가맹점은 API 키로 식별하므로 요청에 가맹점 정보를 넣지 않습니다. `payer_id` 외의 필드를 보내면 `400`으로 거절됩니다. 결제 세션 생성과의 순서는 상관없습니다. 구매자가 결제 버튼을 누를 때 결제 세션과 함께 생성하세요. ## 응답 처리 ```json title="응답" { "delegation_id": "dlg_3q2-7wE9bXk1Lr0Zt8pVnYc4uJmHs6aQfGdOiKe5NBw", "expires_in": 300, "authorization_url": "https://auth.point3.io/delegated?delegation_id=dlg_3q2-7wE9bXk1Lr0Zt8pVnYc4uJmHs6aQfGdOiKe5NBw" } ``` 1. 응답의 `authorization_url`을 `sessionId`와 함께 가맹점 화면에 전달합니다. 값을 바꾸지 말고 그대로 전달하세요. 2. 위임 인증은 `expires_in`초(5분) 동안 유효하고 한 번만 사용할 수 있습니다. 결제를 다시 시도할 때에는 새로 생성하세요. ## 오류 처리 위임 인증을 생성하지 못해도 결제는 진행할 수 있습니다. 오류가 발생하면 `authorization_url` 없이 결제창을 열고, 구매자가 문자 인증으로 결제하게 하세요. | HTTP | `result.code` | 의미 | 처리 | | --- | --- | --- | --- | | `400` | `HTTP_ERROR` | 요청 형식 오류 | `payer_id` 형식과 추가 필드 확인 | | `401` | `HTTP_ERROR` | 인증 실패 | `Authorization` 헤더와 API 키 확인 | | `404` | `PAYER_NOT_FOUND` | 구매자를 찾을 수 없음 | 위임 인증 없이 결제창 열기 | | `503` | | 일시적 장애 | 위임 인증 없이 결제창 열기 | `404`는 회원 정보에 저장된 `payerId`를 지금은 사용할 수 없다는 뜻입니다. 구매자가 탈퇴했거나, 다른 가맹점에서 받은 값을 저장한 경우 등이 해당합니다. 이때는 위임 인증 없이 결제창을 열고, 결제가 끝나면 새로 받은 `payerId`를 회원 정보에 저장하세요. 다음 결제부터는 이 값으로 다시 위임 인증을 사용할 수 있습니다. ```json title="오류 응답" { "status": 404, "timestamp": "2026-10-26T00:00:00.000Z", "result": { "code": "PAYER_NOT_FOUND", "message": "Payer not found in identity plane" } } ``` 전체 필드는 [위임 인증 API 레퍼런스](/api-reference/delegated)에서 확인하세요. 위임 인증을 생성했으면 `authorization_url`을 가맹점 화면에 전달해 결제창을 열도록 해야 합니다. [JavaScript SDK 위임 인증](/sdk-delegated)을 참고하세요. --- ## Document: 위임 인증 가맹점에 로그인한 회원이 point3 결제창의 문자 인증 없이 결제하도록 하는 위임 인증의 개념과 흐름을 설명합니다. URL: /delegated-authentication # 위임 인증 위임 인증은 가맹점에 로그인한 회원이 point3 결제창의 문자 인증 없이 결제하도록 하는 기능입니다. 가맹점 서버가 회원 정보에 저장된 [구매자 아이디](/payer-id)로 위임 인증을 생성해 결제창에 전달하면, 구매자는 문자 인증 없이 바로 결제를 진행할 수 있습니다. **point3 결제창에서 구매자가 보는 화면** - 기존: 구매자가 휴대폰 문자 인증번호 입력 → 문자 인증 완료 → 결제 금액·결제 계좌 확인 → 결제하기 → 결제 비밀번호 입력 - 위임 인증: 가맹점 서버가 로그인한 회원 확인 → 위임 인증 → 문자 인증 없이 바로 결제 화면 → 결제하기 → 결제 비밀번호 입력 결제 비밀번호 입력은 기존과 같습니다. ## 흐름 [결제 흐름과 상태](/payment-flow#전체-흐름)의 전체 흐름 중 결제창을 열기 전까지가 달라집니다. 주황색으로 표시한 단계가 위임 인증으로 추가되거나 달라지는 부분입니다. 1. 가맹점 화면 → 가맹점 서버: 주문 요청 (로그인한 회원) 2. 가맹점 서버 → point3: 위임 인증 생성 — `POST /authn/v2/delegation` **(위임 인증)** 3. point3 → 가맹점 서버: authorization_url 발급 **(위임 인증)** 4. 가맹점 서버 → point3: 결제 세션 생성 — `POST /payment/v3/session` 5. point3 → 가맹점 서버: sessionId 발급 — `status: created` 6. 가맹점 서버 → 가맹점 화면: sessionId, authorization_url 전달 **(위임 인증)** 7. 가맹점 화면 → point3: 결제창 열기 — `brandpay.redirectUrl` **(위임 인증)** - (문자 인증 없이 결제 의사 확정) **(위임 인증)** - (이후 승인 과정은 위임 인증 없이 결제할 때와 같습니다) 위임 인증을 사용하면 가맹점이 추가로 할 일은 두 가지입니다. - **가맹점 서버**: 로그인한 회원의 `payerId`로 위임 인증을 생성하고, 응답의 `authorization_url`을 `sessionId`와 함께 화면에 전달합니다. [위임 인증 생성](/delegated-creation)을 참고하세요. - **가맹점 화면**: `widgets()`의 `brandpay.redirectUrl`에 `authorization_url`을 넣어 결제창을 엽니다. [JavaScript SDK 위임 인증](/sdk-delegated)을 참고하세요. ## 로그인한 회원의 payerId 사용 위임 인증을 생성하면 point3는 문자 인증 없이 해당 `payerId`의 구매자로 결제창을 엽니다. 따라서 가맹점 서버는 로그인 세션으로 회원을 확인한 뒤, 그 회원 정보에 저장된 `payerId`로 위임 인증을 생성해야 합니다. 저장하는 방법은 [구매자 아이디](/payer-id#저장하기)를 참고하세요. 위임 인증 생성 요청에 담긴 `payerId`가 로그인한 회원의 것이 확실한지는 가맹점이 확인해야 합니다. ## 위임 인증에 실패하면 위임 인증이 만료됐거나 오류로 완료되지 않으면 결제창이 구매자에게 문자 인증 화면을 보여 줍니다. 구매자가 문자 인증을 마치면 결제가 그대로 이어지므로 가맹점이 따로 처리할 일은 없습니다. ## 사용 조건과 제약 - 저장된 `payerId`가 있는 회원에게만 사용할 수 있습니다. 등록되지 않은 구매자는 위임 인증 없이 결제해 `payerId`를 받으세요. - 웹과 [앱](/integration-methods#react-native-flutter-네이티브-앱) 모두 사용할 수 있습니다. [등록 전용 위젯](/registration-integration)은 결제 세션이 없으므로 사용할 수 없습니다. - 위임 인증은 생성 후 5분 동안 유효하고, 한 번의 결제창 실행에만 사용할 수 있습니다. --- ## Document: AI로 시작하기 Claude Code, ChatGPT, OpenCode 같은 AI 도구에 point3 연동 지침을 전달해 결제 연동을 진행하는 방법을 안내합니다. URL: /ai-tools # AI로 시작하기 AI 도구에 point3 연동 지침을 전달하면 결제 연동을 함께 진행할 수 있습니다. 에이전트에 다음 프롬프트를 붙여 넣으세요. ```text Retrieve and follow the instructions at: https://docs.point3.io/agent-integration.md ``` ## 제공하는 문서 | 문서 | 주소 | 내용 | | --- | --- | --- | | agent-integration.md | https://docs.point3.io/agent-integration.md | 에이전트용 연동 가이드 | | llms.txt | https://docs.point3.io/llms.txt | 문서 목록과 연동 규칙 | | llms-full.txt | https://docs.point3.io/llms-full.txt | 모든 가이드와 API 레퍼런스 전문 | | openapi.yaml | https://docs.point3.io/openapi.yaml | 서버 API 요청·응답 스키마 | 웹 페이지를 가져오지 못하는 도구라면 문서를 내려받아 파일로 첨부하세요. 각 페이지는 오른쪽 위의 **Markdown으로 복사** 버튼으로 가져오거나, 주소 끝에 `.md`를 붙여 Markdown으로 볼 수 있습니다. ## AI가 작성한 코드 확인하기 AI가 작성한 코드는 [라이브 전환 체크리스트](/go-live-checklist)로 점검하세요. 특히 다음 항목을 확인하세요. | 하지 말 것 | 이렇게 하세요 | | --- | --- | | successUrl 이동을 결제 성공으로 처리 | 결제 승인 API 응답과 결제 세션 조회 응답의 status로 판단 | | API 키를 브라우저 코드나 앱에 포함 | API 키는 가맹점 서버에만 보관 | | 화면에서 받은 금액으로 결제 세션 생성 | 가맹점 서버에 저장된 주문 금액으로 생성 | | 테스트 환경이나 웹훅이 있다고 가정 | point3는 테스트 환경과 웹훅을 제공하지 않음 | AI 도구는 문서에 없는 엔드포인트나 필드를 만들어 내기도 합니다. AI가 작성한 결제 코드를 운영에 반영하기 전에 이 문서와 대조해 확인하세요. --- ## Document: 시작 방식 선택 보증보험 없이 시작할 때와 표준계약으로 시작할 때의 차이, 표준계약을 맺고 바꾸는 방법을 안내합니다. URL: /contract/start-options # 시작 방식 선택 가맹점은 보증보험 없이 시작할 수도 있고, 보증보험에 가입해 표준계약으로 시작할 수도 있습니다. 표준계약은 [가맹점 이용계약](/contract/contracts#가맹점-이용계약)의 일 입금 한도와 정산 대기 기간을 거래 규모에 맞게 바꾸는 계약입니다. | 항목 | 보증보험 없이 시작 | 표준계약으로 시작 | | --- | --- | --- | | 일 입금 한도 | 30만 원 | 예상 일 거래액에 맞춰 협의 | | 정산 대기 기간 | 21일 | 협의에 따라 최소 5일 | - 일 입금 한도는 결제 한도가 아닙니다. 한도를 넘는 결제도 모두 처리되고, 정산금이 나뉘어 입금됩니다. 정산 대기 기간과 일 입금 한도가 무엇인지는 [정산](/contract/settlement)을 참고하세요. ## 시작 시점 두 방식 모두 금융결제원 보고가 완료된 뒤에 결제를 시작합니다. - **처음부터 표준계약으로 시작**: 보증보험에 가입해 첫 결제부터 표준계약으로 시작합니다. - **보증보험 없이 시작**: 보증보험 없이 결제를 시작하고, 필요할 때 보증보험에 가입해 표준계약으로 바꿉니다. ## 표준계약이 필요한 경우 다음에 해당한다면 표준계약을 검토하세요. - 자체페이를 통한 하루 결제 금액이 일 입금 한도 30만 원을 자주 넘을 것으로 예상될 때 - 정산금을 더 빨리 받아야 할 때 거래액이 크다면 처음부터 표준계약으로 시작하는 것이 좋습니다. 보증보험 없이 시작하면 일 입금 한도 30만 원을 넘는 금액이 다음 영업일 이후로 나뉘어 입금됩니다. ## 가입과 변경 표준계약을 맺으려면 SGI서울보증의 보증보험에 가입하고 증권을 제출해야 합니다. 순서는 [표준계약 절차](/contract/standard-contract-process)를 참고하세요. 표준계약의 일 입금 한도와 정산 대기 기간은 한 번 정한 뒤에도 다시 신청해 바꿀 수 있습니다. 보증보험 가입금액을 조정해 새 계약 정보를 적용합니다. 변경된 계약 정보는 대시보드의 [계약 관리](https://docs.point3.io/dashboard/contracts-operations/contract-management)에서 확인할 수 있습니다. 대시보드 가입이 승인되면 모든 가맹점에 '기본계약'이 기본으로 적용되며, 표준계약을 체결하는 가맹점도 마찬가지입니다. 표준계약은 기본계약 상태에서 계약을 변경하는 방식으로 적용됩니다. ## 정산 후 취소 정산 대기 기간을 줄여도 결제 취소는 그대로 할 수 있으며, 정산이 끝난 결제도 취소할 수 있습니다. [정산 이해하기](/contract/settlement#취소)을 참고하세요. ## 다음 단계 - 보증보험 가입부터 표준계약 적용까지의 순서와 보증보험 비용은 [표준계약 절차](/contract/standard-contract-process)를 참고하세요. --- ## Document: 표준계약 절차 표준계약을 맺거나 일 입금 한도와 정산 대기 기간을 바꾸는 순서를 안내합니다. URL: /contract/standard-contract-process # 표준계약 절차 표준계약에는 SGI서울보증의 보증보험 증권이 필요합니다. 보증보험은 point3가 아니라 SGI서울보증과 맺는 계약입니다. 표준계약은 point3 담당자에게 요청해주세요. 보증보험 가입은 금융결제원 보고가 완료된 뒤에 point3에서 안내드립니다. 보증보험과 표준계약은 필수가 아니며 선택사항입니다. 관련 내용은 [시작 방식 선택](/contract/start-options)에서 확인해주세요. 1. **원하는 한도와 기간 요청** (가맹점) point3 담당자에게 원하는 일 입금 한도와 정산 대기 기간을 알려주세요. 1. **가입 링크 전달** (point3) 요청한 일 입금 한도와 정산 대기 기간에 맞춰 보증보험 가입금액을 정하고 SGI서울보증 가입 링크를 보내드립니다. 1. **보험료 납부** (가맹점) 가입 링크의 안내에 따라 SGI서울보증에 보험료를 납부하고 보증보험 증권을 발급받습니다. 1. **증권 제출 및 계약 변경** (가맹점) 발급받은 증권을 point3 정산 담당자에게 보내고, 협의한 내용대로 대시보드에서 [계약 정보 변경](https://docs.point3.io/dashboard/contracts-operations/contract-management#계약-정보-변경)을 요청해주세요. 1. **새 계약 적용** (point3) point3가 계약 변경을 승인한 다음 날부터 새 일 입금 한도와 정산 대기 기간이 적용됩니다. 예를 들어 11월 1일에 신청하고 같은 날 승인되면 11월 2일부터 적용됩니다. 변경된 계약은 대시보드의 [계약 관리](https://docs.point3.io/dashboard/contracts-operations/contract-management)에서 확인할 수 있습니다. ## 보증보험 비용 | 항목 | 내용 | | --- | --- | | 가입금액 | 보험이 보증하는 금액입니다. point3가 원하는 일 입금 한도와 정산 대기 기간에 맞춰 정합니다. 가맹점이 내는 금액이 아닙니다. | | 보험료 | 가맹점이 SGI서울보증에 내는 금액입니다. 금액은 가입 링크에서 SGI서울보증이 안내합니다. point3가 받는 비용이 아닙니다. | 보증보험은 point3가 제공하는 자체페이의 예상 거래 금액만큼만 가입하므로 보험료 부담이 크지 않습니다. 가입금액과 일 입금 한도는 같은 값이 아니며, 일 입금 한도는 point3와 협의한 값으로 적용됩니다. ## 다음 단계 - 보증보험 없이 시작할 때와의 차이는 [시작 방식 선택](/contract/start-options)을 참고하세요. --- ## Document: 정산 이해하기 결제된 금액이 정산계좌로 입금되기까지의 흐름과 정산 대기 기간, 일 입금 한도, 취소를 안내합니다. URL: /contract/settlement # 정산 이해하기 결제된 금액은 정산 대기 기간이 지나면 가맹점 정산계좌로 입금됩니다. 하루에 입금되는 금액은 일 입금 한도를 넘지 않습니다. **결제** → 정산 대기 기간(N일) → **정산 확정** → **입금** ## 용어 - **정산 대기 기간(N일)**: 결제일부터 정산금이 확정되기까지의 기간입니다. 결제 후 발생할 수 있는 취소에 대비해 point3가 결제 금액을 보관합니다. - **일 입금 한도**: 하루에 정산계좌로 입금되는 금액의 상한입니다. 한도를 넘는 금액은 다음 영업일 이후 순서대로 입금됩니다. **일 입금 한도는 입금을 나누어 할 뿐 결제를 막지 않습니다.** 각 수치는 가맹점마다 다르며 대시보드의 [계약 관리](https://docs.point3.io/dashboard/contracts-operations/contract-management)에서 확인할 수 있습니다. 보증보험에 가입해 표준계약을 맺으면 정산 대기 기간과 일 입금 한도를 거래 규모에 맞게 바꿀 수 있습니다. [시작 방식 선택](/contract/start-options)을 참고하세요. ## 예시 정산 대기 기간 21일, 일 입금 한도 30만 원인 가맹점에서 하루에 100만 원이 결제된 경우입니다. | 날짜 | 내용 | | --- | --- | | D | 100만 원 결제 | | D+21 | 30만 원 입금 | | D+22 | 30만 원 입금 | | D+23 | 30만 원 입금 | | D+24 | 10만 원 입금 | 1. 결제일(D)에 100만 원이 결제됩니다. 2. 정산 대기 기간 21일이 지난 D+21부터 수수료를 뺀 정산금이 입금됩니다. 예시에서는 계산을 쉽게 하려고 수수료를 빼지 않았습니다. 3. 일 입금 한도가 30만 원이므로 D+21, D+22, D+23에 30만 원씩, D+24에 남은 10만 원이 입금됩니다. ## 취소 결제 금액의 전부 또는 일부를 취소할 수 있습니다. 취소한 금액은 구매자가 결제한 계좌로 돌려줍니다. - **정산 전 취소**: 정산 대기 기간 중에 취소하면 취소한 금액만큼 정산금이 줄어듭니다. - **정산 후 취소**: 정산이 끝나 입금된 결제도 취소할 수 있습니다. 취소한 금액은 이후 정산금에서 차감됩니다. - **취소 수수료**: 취소할 때마다 취소 금액의 0.3%(VAT 포함 0.33%), 최소 70원(VAT 포함 77원)이 부과됩니다. 취소 수수료는 결제 수수료와 중복하여 부과됩니다. --- ## Document: 지급대행 이용 안내 point3 지급대행의 지급 방식, 계약 방법, 지급 속도와 이용료를 안내합니다. URL: /contract/payout-overview # 지급대행 이용 안내 point3 지급대행은 엑셀 한 장으로 입점 셀러에게 정산금을 보냅니다. 가맹점은 누구에게 얼마를 언제 보낼지만 정하고, point3는 엑셀에 적힌 금액을 그대로 셀러에게 지급합니다. 1. **결제** (구매자) — 구매자가 point3로 결제합니다. 2. **지급대행 계좌에 적립** (point3) — 정산일에 수수료를 뺀 금액이 가맹점 몫으로 적립됩니다. 3. **지급 요청** (가맹점) — 지급일 전날 24시까지 엑셀을 보냅니다. 4. **입점 셀러에게 지급** (point3) — 지급 예정일에 입점 셀러 계좌로 보냅니다. 주말과 공휴일에도 지급합니다. - 셀러별 수수료율이 달라도 됩니다. 금액 계산은 가맹점이 하고, point3는 적힌 금액을 보냅니다. - 입점 셀러는 가입하거나 서류를 제출하지 않습니다. 다만, 큰 금액을 지급받는 입점 셀러는 특정금융정보법에 따라 본인 확인이 필요합니다. - 사업자등록이 없는 개인 셀러에게도 지급할 수 있습니다. - 주말과 공휴일에도 지급합니다. 엑셀 작성법과 지급 실패 처리 등 운영 방법은 연동 가이드의 [지급 요청 방법](/payout)을 참고하세요. ## 이용료 | 항목 | 금액 | | --- | --- | | 월 이용료 | 7만 원 (월 1,000건까지) | | 1,001건째부터 | 건당 70원 | 예를 들어 한 달에 800건을 지급하면 7만 원, 1,200건을 지급하면 7만 원에 200건 × 70원을 더한 8만 4천 원입니다. 월 이용료는 계약한 달이 아니라 첫 지급대행이 발생한 달부터 부과됩니다. ## 다음 단계 - 엑셀 양식과 작성법은 연동 가이드의 [지급 요청 방법](/payout)을 참고하세요. --- ## Document: 자주 묻는 질문 도입을 검토하는 가맹점이 자주 묻는 질문을 단계별로 모았습니다. URL: /contract/faq # 자주 묻는 질문 도입을 검토하는 가맹점이 실제로 물어본 질문을 단계별로 모았습니다. ## 검토할 때 ### 우리 서비스도 이용할 수 있나요? [문의하기](/contract/contact)에서 서비스 주소를 알려 주시면 확인해 드립니다. 웹과 앱(WebView) 모두 연동할 수 있습니다. ### 카드 결제를 없애야 하나요? 아니요. 사용 중인 결제수단은 그대로 두고, 서비스 이름의 자체페이를 결제수단으로 추가하시면 됩니다. ### 정기결제(빌링 결제)도 되나요? 지원하지 않습니다. ### 구매자가 앱을 설치해야 하나요? 아니요. 별도의 앱 설치 없이 서비스의 결제 화면 안에서 결제를 마칩니다. ## 계약할 때 ### 결제를 이용하려면 계약서를 따로 쓰지 않아도 되나요? 네. 대시보드에서 가입을 신청할 때 가맹점 이용계약을 전자계약으로 체결합니다. 지급대행을 이용하는 중개플랫폼만 지급대행 계약서를 별도로 제출합니다. [계약 종류](/contract/contracts)를 참고하세요. ### 표준계약은 언제든 맺을 수 있나요? 네. 금융결제원 보고가 완료된 이후라면 언제든 보증보험에 가입해 표준계약을 맺을 수 있습니다. 첫 결제부터 표준계약으로 시작할 수도 있습니다. [시작 방식 선택](/contract/start-options)을 참고하세요. ### 일 입금 한도가 30만 원이면 결제도 30만 원까지만 되나요? 아니요. 일 입금 한도는 하루에 입금되는 금액의 상한이며 결제는 제한하지 않습니다. 예를 들어 하루에 100만 원이 결제되면 결제는 모두 처리되고, 정산금은 하루 30만 원씩 나뉘어 입금됩니다. [정산 이해하기](/contract/settlement)을 참고하세요. ### 표준계약은 한 번 맺고 나서 변경할 수 있나요? 네. 다시 신청해 일 입금 한도와 정산 대기 기간을 바꿀 수 있습니다. [표준계약 절차](/contract/standard-contract-process)를 참고하세요. ### 보증보험료는 누구에게 내나요? SGI서울보증에 냅니다. point3가 받는 비용이 아닙니다. ### 보증보험 가입금액이 일 입금 한도인가요? 아니요. 가입금액은 보험이 보증하는 금액으로, 일 입금 한도와 같은 값이 아닙니다. 일 입금 한도는 point3와 협의한 값으로 적용됩니다. ### 보증보험료 부담이 큰가요? point3가 제공하는 자체페이의 예상 거래 금액만큼만 가입하므로 보험료 부담이 크지 않습니다. 정확한 금액은 가입 링크를 통해 안내드립니다. ## 도입할 때 ### 사업자등록증명을 보내도 되나요? 아니요. 사업자등록증을 보내 주세요. 홈택스에서 발급받을 수 있으며, 3개월 이내에 발급받았다면 그대로 사용해도 됩니다. ### 금융결제원 보고는 얼마나 걸리나요? 금융결제원에서 보고가 완료되기까지 통상적으로 1주가 소요됩니다. 추가 자료가 필요하면 디스코드 채널로 안내드립니다. ### 보증보험은 언제 가입하나요? 금융결제원 보고가 완료된 뒤에 표준계약 체결이 필요한 가맹점에 한하여 가입합니다. 가입은 point3 담당자가 별도로 안내드립니다. ### 테스트는 어떻게 하나요? 연동을 마치고 100원 정도의 소액 결제를 실제로 진행한 뒤 바로 취소하여 확인을 부탁드립니다. ## 운영할 때 ### 인증 문자와 1원 입금에 '바이올렛페이'라고 표시돼요. 바이올렛페이는 point3를 운영하는 회사 이름입니다. ### 구매자가 '자동이체 등록' 문자를 받았다고 해요. 계좌를 등록하면 구매자의 은행이 보내는 안내 문자입니다. 구매자가 결제하지 않으면 출금되지 않습니다. ### 정산이 끝난 결제도 취소할 수 있나요? 네. 정산이 끝난 결제도 취소할 수 있습니다. 취소 방법은 개발 담당자에게 [결제 취소](/payment-cancellation) 문서를 전달하세요. ### 사업자등록이 없는 입점 셀러에게도 지급대행으로 보낼 수 있나요? 네. 사업자등록이 없는 개인 셀러에게도 다른 셀러와 같은 방법으로 지급할 수 있습니다. 연동 가이드의 [지급 요청 방법](/payout)을 참고하세요. ### 입점 셀러가 가입하거나 서류를 제출해야 하나요? 아니요. 다만 큰 금액을 받는 입점 셀러만 본인 확인이 필요합니다. [입점 셀러 본인 확인](/payout#입점-셀러-본인-확인)을 참고하세요. ### 지급대행 월 이용료는 언제부터 부과되나요? 첫 지급대행이 발생한 달부터 부과됩니다. ## 다음 단계 - 여기에 없는 질문은 [문의하기](/contract/contact)를 참고하세요. --- ## Document: 계약 종류 가맹점이 point3와 맺는 계약과, 표준계약에 필요한 보증보험의 관계를 안내합니다. URL: /contract/contracts # 계약 종류 | 계약 | 대상 | 맺는 방법 | | --- | --- | --- | | [가맹점 이용계약](#가맹점-이용계약) | 모든 가맹점 | 대시보드 가입 시 전자계약 | | [표준계약](#표준계약) | 일 입금 한도와 정산 대기 기간을 바꾸려는 가맹점 | 보증보험 증권 제출 | | [지급대행 계약](#지급대행-계약) | 입점 셀러에게 정산금을 나눠 주는 중개플랫폼 | 도입 시 다른 서류와 함께 제출 | [보증보험](/contract/standard-contract-process): 표준계약에 필요한 보험입니다. point3가 아니라 SGI서울보증에 가입합니다. ## 가맹점 이용계약 point3를 이용하는 모든 가맹점이 맺는 계약입니다. 대시보드에서 가입을 신청하면 전자계약의 형태로 체결되며, point3가 가입을 승인하면 계약이 성립합니다. 계약이 성립하면 일 입금 한도, 정산 대기 기간, 수수료 등의 계약 정보가 대시보드의 [계약 관리](https://docs.point3.io/dashboard/contracts-operations/contract-management)에 표시됩니다. 보증보험 없이 시작하면 일 입금 한도 30만 원, 정산 대기 기간 21일이 적용됩니다. ## 표준계약 보증보험 증권을 제출해 가맹점 이용계약의 일 입금 한도와 정산 대기 기간을 거래 규모에 맞게 바꾸는 계약입니다. 처음부터 표준계약으로 시작할 수도 있고, 보증보험 없이 시작한 뒤 언제든 바꿀 수도 있습니다. [시작 방식 선택](/contract/start-options)을 참고하세요. 보증보험은 point3와 맺는 계약이 아닙니다. 표준계약을 맺기 위해 가맹점이 SGI서울보증에 가입하는 보험입니다. [표준계약 절차](/contract/standard-contract-process)를 참고하세요. ## 지급대행 계약 입점 셀러에게 정산금을 나눠 주는 중개플랫폼이 가맹점 이용계약과는 별도로 맺는 계약입니다. 지급 방식과 이용료는 [지급대행 이용 안내](/contract/payout-overview)를 참고하세요. ## 다음 단계 - 보증보험 없이 시작할지, 표준계약으로 시작할지는 [시작 방식 선택](/contract/start-options)에서 비교하세요. --- ## Document: 문의하기 도입 단계별 point3 문의 창구를 안내합니다. URL: /contract/contact # 문의하기 | 단계 | 문의 창구 | | --- | --- | | 도입 문의 | [sy@point3.team](mailto:sy@point3.team) 또는 [point3 홈페이지](https://point3.io) | 처음 문의할 때 회사 이름, 서비스 주소, 지급대행이 필요한지 함께 알려 주시면 더 빠르게 안내할 수 있습니다. ## 다음 단계 - 처음이라면 [도입 가이드](/contract)부터 확인하세요. --- ## Document: AI로 알아보기 ChatGPT, Claude 같은 AI에 point3 도입 지침을 전달해 우리 서비스에 필요한 계약과 절차만 안내받는 방법을 설명합니다. URL: /contract/ai # AI로 알아보기 AI에 point3 도입 지침을 전달하면, 서비스 상황에 맞는 계약과 절차만 골라 안내받을 수 있습니다. AI에 다음 프롬프트를 붙여 넣으세요. ```text Retrieve and follow the instructions at: https://docs.point3.io/agent-onboarding.md ``` ## 이렇게 물어보세요 서비스 형태와 하루 거래액을 함께 알려 주면 더 정확하게 안내받을 수 있습니다. - 하루 거래액이 500만 원 정도인 쇼핑몰인데 무엇을 해야 하나요? - 입점 셀러에게 정산금을 지급해야 하는데 어떻게 하나요? - 가입하려면 무엇을 준비해야 하나요? AI가 사용하는 지침은 [agent-onboarding.md](https://docs.point3.io/agent-onboarding.md)에서 확인할 수 있습니다. 결제 연동 코드를 AI로 작성하려면 연동 가이드의 [AI로 시작하기](/ai-tools)를 참고하세요. AI의 안내는 이 문서를 바탕으로 합니다. 수수료, 일 입금 한도, 정산 대기 기간 같은 계약 조건은 point3 담당자와 확정하세요. --- ## Document: 도입 가이드 point3 도입에 필요한 계약과 문의부터 실결제까지의 순서를 안내합니다. URL: /contract # 도입 가이드 point3 도입에 필요한 계약과 순서를 안내합니다. 도입 문의부터 실결제까지 다음 순서로 진행합니다. 서비스 상황에 맞는 절차만 알고 싶다면 AI에 다음 프롬프트를 붙여 넣으세요. 자세한 내용은 [AI로 알아보기](/contract/ai)를 참고하세요. ```text Retrieve and follow the instructions at: https://docs.point3.io/agent-onboarding.md ``` 1. **도입 문의** (가맹점) [sy@point3.team](mailto:sy@point3.team) 또는 [point3 홈페이지](https://point3.io)로 문의합니다. 회사 이름, 서비스 주소, 지급대행이 필요한지 함께 알려 주시면 더 빠르게 안내할 수 있습니다. 1. **디스코드 채널 초대** (point3) point3가 서비스를 확인한 뒤 가맹점별 비공개 디스코드 채널에 초대합니다. 가맹점과 point3 팀만 참여하는 채널이며, 다른 가맹점은 볼 수 없습니다. 이후 서류 제출, 연동 문의, 보증보험 안내는 이 채널에서 진행합니다. 연동 문의를 바로 주고받을 수 있도록 개발 담당자도 함께 참여하는 것이 좋습니다. 1. **대시보드 가입 신청과 서류 제출** (가맹점) 디스코드 채널의 안내에 따라 대시보드 가입을 신청하고, 금융결제원 보고에 필요한 다음 내용을 채널에 올립니다. - **사업자등록증**: 홈택스에서 발급받은 사업자등록증입니다. 3개월 이내에 발급받았다면 그대로 사용해도 됩니다. 사업자등록증명은 받지 않습니다. - **서비스 이용약관**: 결제를 연동할 서비스의 이용약관입니다. 서비스가 여러 개라면 서비스별로 올립니다. - **자체페이 이름**: 결제창에서 구매자에게 표시할 결제수단 이름(OO페이)입니다. - **지급대행 계약서**: 입점 셀러에게 정산금 지급이 필요한 중개플랫폼만 선택적으로 제출합니다. [지급대행 이용 안내](/contract/payout-overview)를 참고하세요. 1. **금융결제원 보고** (point3) point3가 OO페이를 금융결제원에 보고합니다. 금융결제원에서 보고 확인이 완료되기까지 통상적으로 1주가 소요되며, 보고 과정에서 추가적인 자료 요청이 필요할 경우 디스코드 채널로 안내드립니다. 1. **표준계약 여부 결정** (가맹점) 보증보험 없이 시작할지, 표준계약으로 시작할지 정해 디스코드 채널에 알려 주세요. 두 방식의 차이는 [시작 방식 선택](/contract/start-options)을 참고하세요. - **표준계약으로 시작**: 원하는 일 입금 한도와 정산 대기 기간을 알려 주시면 보증보험 가입을 안내합니다. 보험료를 내고 증권을 제출하면 표준계약을 맺습니다. 절차는 [표준계약 절차](/contract/standard-contract-process)를 참고하세요. - **보증보험 없이 시작**: 보증보험 가입 없이 다음 단계로 넘어갑니다. 실결제를 시작한 뒤에도 언제든 표준계약으로 바꿀 수 있습니다. | 항목 | 보증보험 없이 시작 | 표준계약으로 시작 | | --- | --- | --- | | 일 입금 한도 | 30만 원 | 예상 일 거래액에 맞춰 협의 | | 정산 대기 기간 | 21일 | 협의에 따라 최소 5일 | - 일 입금 한도는 결제 한도가 아닙니다. 한도를 넘는 결제도 모두 처리되고, 정산금이 나뉘어 입금됩니다. 1. **대시보드 가입 승인과 실결제** (point3) point3가 대시보드 가입을 승인하면 가맹점 이용계약이 성립합니다. 승인이 완료된 다음 날부터 실결제가 활성화되며, point3와 협의한 일 입금 한도와 정산 대기 기간이 적용됩니다. ## 연동 연동 방법은 [연동 가이드](/introduction)를 참고하세요. 연동 중 막히는 부분은 디스코드 채널로 문의해주세요. ## 실결제 시작 후 - 결제 건과 정산 데이터는 대시보드에서 확인할 수 있습니다. - 지급대행을 이용한다면 입점 셀러에게 지급할 금액을 엑셀로 작성해 point3에 보내주세요. 연동 가이드의 [지급 요청 방법](/payout)을 참고하세요. - 표준계약으로 전환하거나, 표준계약의 일 입금 한도와 정산 대기 기간을 바꾸려면 point3 담당자에게 요청해주세요. [표준계약 절차](/contract/standard-contract-process)를 참고하세요. ## 다음 단계 - 가맹점이 맺는 계약에 대한 자세한 내용은 [계약 종류](/contract/contracts)에서 확인하세요. - 문의 창구는 [문의하기](/contract/contact)를 참고하세요. --- # API 레퍼런스 서버 주소: `https://api.point3.io` 인증: point3가 발급한 API 키를 가맹점 서버에만 보관하고 `Authorization: Bearer {token}` 형식으로 사용합니다. 전체 명세: /openapi.yaml ## API: 결제 결제창을 열기 전에 결제 세션을 만들고, 구매자가 결제를 진행한 뒤 결제 결과를 확인합니다. ### 결제 세션 조회 `GET https://api.point3.io/payment/v3/session/{sessionId}` 결제 세션 아이디로 결제 세션 정보를 조회합니다. 정상 조회는 HTTP 200으로 반환되며 응답 본문의 `status`로 진행 상태와 최종 결과를 판단합니다. `processing`이면 잠시 기다렸다가 같은 세션을 다시 조회하세요. `captured` 또는 `failed`가 확인되면 결제 결과가 확정된 것입니다. **파라미터** - `sessionId` (path) · string · 필수 - 조회할 결제 세션 식별자 **응답** - `200` 결제 세션 조회 성공 - `400` 잘못된 요청입니다. - `401` 인증에 실패했습니다. - `403` 요청이 허용되지 않았습니다. - `404` 리소스를 찾을 수 없습니다. - `500` 서버 내부 오류가 발생했습니다. - `503` 결제 상태를 일시적으로 확인할 수 없습니다. 잠시 기다렸다가 같은 세션을 다시 조회하세요. **200 응답 필드** - `id` · string · 필수 - 결제 세션 식별값입니다. - `status` · string · 필수 · 값: `created`, `identified`, `initiated`, `committed`, `processing`, `captured`, `failed`, `expired` - 현재 결제 세션 상태입니다. processing이면 다시 조회하고, captured 또는 failed가 확인되면 결제 결과가 확정된 것입니다. expired는 결제 승인 기한이 지나 더 이상 결제할 수 없는 세션입니다. - `clientId` · string · 필수 - 결제 세션을 생성한 가맹점 클라이언트 식별값 - `amount` · integer · 필수 - VAT를 포함한 최종 결제 총액(원) - `currency` · string · 필수 · 값: `KRW` - 결제 통화 - `supplyAmount` · integer · 필수 - 과세 공급가액(원) - `taxFreeAmount` · integer · 필수 - 총액에 포함된 면세 금액(원) - `vat` · integer · 필수 - 확정된 부가세 금액(원) - `productName` · string · 필수 - 결제 상품명 - `displayMerchantName` · string · 선택 - 결제 UI에 표시되는 가맹점 상호명 - `tradeOpt` · string · 필수 · 값: `GENERAL`, `BOOK_CULTURE`, `PUBLIC_TRANSPORT` - 현금영수증 분류 - `createdAt` · string · 필수 - 결제 세션 생성 시각 - `updatedAt` · string · 필수 - 결제 세션 최종 수정 시각 - `payerId` · string · 선택 - 본인인증 완료(identified 이상) 후 공개되는 결제자 식별자입니다. ```json { "id": "pymt_sess-019f0000-0000-7000-9000-000000000000", "status": "captured", "clientId": "client-019f0000-0000-7000-9000-000000000001", "amount": 10000, "currency": "KRW", "supplyAmount": 9091, "taxFreeAmount": 0, "vat": 909, "productName": "아메리카노 외 1건", "displayMerchantName": "포인트쓰리 스토어", "tradeOpt": "GENERAL", "createdAt": "2026-08-21T00:00:00.000Z", "updatedAt": "2026-08-21T00:01:40.000Z", "payerId": "payer:00000000000000000000000000" } ``` ### 결제 세션 생성 `POST https://api.point3.io/payment/v3/session` 가맹점 서버가 주문과 결제 금액을 확인한 뒤 결제창을 열기 전에 호출합니다. 매일 23:30 이상 다음 날 00:30 미만(Asia/Seoul)에는 EOB 점검으로 생성을 차단합니다. 차단된 요청은 세션을 저장하지 않습니다. 성공하면 생성된 결제 세션 리소스를 직접 반환합니다. 응답의 `id`는 `sessionId`로서 주문과 함께 저장하세요. **요청 본문** - `amount` · integer · 필수 - 사용자가 지불할 결제 총액입니다. 대한민국 원(KRW) 단위입니다. - `productName` · string · 필수 - 결제창에 표시할 상품명입니다. - `displayMerchantName` · string · 선택 - 결제 UI에 표시할 가맹점 상호명입니다. 미지정 시 `productName` 을 사용합니다. - `vat` · integer · 선택 - 부가세 금액입니다. 서버가 자동으로 계산하며, 수동 설정이 필요할 경우 지정합니다. 자세한 내용은 [세금 처리](/tax-processing)를 확인하세요. - `taxFreeAmount` · integer · 선택 - 면세 금액입니다. 면세 상품의 경우 지정합니다. 자세한 내용은 [세금 처리](/tax-processing)를 확인하세요. - `tradeOpt` · string · 선택 · 값: `GENERAL`, `BOOK_CULTURE`, `PUBLIC_TRANSPORT` - 세금 처리를 위한 결제 분류입니다. 세법 상 도서·문화 또는 대중교통 분류 상품의 경우 지정합니다. ```json { "amount": 10000, "productName": "아메리카노 외 1건", "displayMerchantName": "포인트쓰리 스토어" } ``` **응답** - `200` 결제 세션 생성 성공 - `400` 요청값 오류 - `401` 인증 실패 또는 허용되지 않은 접근 - `503` 현재 EOB 점검 구간 **200 응답 필드** - `id` · string · 필수 - 결제 세션 식별값입니다. 가맹점 화면과 결제 세션 조회 요청에서 `sessionId`로 사용합니다. - `status` · string · 필수 · 값: `created` - 생성 직후의 공개 결제 세션 상태 - `clientId` · string · 필수 - 결제 세션을 생성한 가맹점 클라이언트 식별값 - `amount` · integer · 필수 - VAT를 포함한 최종 결제 총액(원) - `currency` · string · 필수 · 값: `KRW` - 결제 통화 - `supplyAmount` · integer · 필수 - point3에서 계산한 과세 공급가액 입니다. 계산 규칙은 [세금 처리](/tax-processing)를 확인하세요. - `taxFreeAmount` · integer · 필수 - 총액에 포함된 면세 금액(원) - `vat` · integer · 필수 - 확정된 부가세 금액(원) - `productName` · string · 필수 - 결제 상품명 - `displayMerchantName` · string · 선택 - 결제 UI에 표시되는 가맹점 상호명 - `tradeOpt` · string · 필수 · 값: `GENERAL`, `BOOK_CULTURE`, `PUBLIC_TRANSPORT` - 현금영수증 분류입니다. - `createdAt` · string · 필수 - 결제 세션 생성 시각 - `updatedAt` · string · 필수 - 결제 세션 최종 수정 시각 ```json { "id": "pymt_sess-019f0000-0000-7000-9000-000000000000", "status": "created", "clientId": "client-019f0000-0000-7000-9000-000000000001", "amount": 10000, "currency": "KRW", "supplyAmount": 9091, "taxFreeAmount": 0, "vat": 909, "taxationType": "TAXABLE", "productName": "아메리카노 외 1건", "displayMerchantName": "포인트쓰리 스토어", "tradeOpt": "GENERAL", "createdAt": "2026-08-21T00:00:00.000Z", "updatedAt": "2026-08-21T00:00:00.000Z" } ``` ### 결제 승인 `POST https://api.point3.io/capture/v2/{sessionId}` 가맹점 화면이 `successUrl`로 이동한 뒤 가맹점 서버가 호출합니다. 결제가 커밋된 날의 다음 날 00:00(KST)부터는 승인할 수 없습니다. 주문의 최종 결제 결과는 응답의 `status` 또는 결제 세션 조회 API를 호출하여 확인하세요. 실패가 확정된 경우에도 HTTP `200`을 반환합니다. `status`가 `failed`이면 `outcome.code`로 사유를 구분하세요. `outcome.message` 문구는 바뀔 수 있으므로 분기 조건으로 쓰지 마세요. | `outcome.code` | `outcome.type` | 의미 | 처리 | | --- | --- | --- | --- | | `PAYMENT_METHOD_DECLINED` | `declined` | 결제수단 승인 거절. 잔액 부족, 출금 가능 잔액 부족 등 | 잔액 확인 또는 다른 계좌 선택 안내 | | `PAYMENT_METHOD_LIMIT_EXCEEDED` | `failed` | 이체한도 초과 | 이체한도 확인 또는 다른 계좌 선택 안내 | | `PAYMENT_CAPTURE_REJECTED` | `rejected` | 세부 사유가 분류되지 않은 이체 실패 | 주문·결제 상태 확인, 필요하면 문의 | | `PAYMENT_CAPTURE_ALREADY_FAILED` | `failed` | 이미 실패한 결제를 다시 승인 요청함. 원래 사유는 포함되지 않음 | 처음 받은 실패 응답의 기록 확인 | 처음 받은 실패 응답의 `outcome.code`를 주문·`sessionId`와 함께 저장하고, 이후 응답으로 덮어쓰지 마세요. HTTP `202`(`status: processing`)는 아직 처리 중이라는 뜻이며 실패가 아닙니다. 응답을 받지 못했을 때와 같이 `sessionId`로 결제 세션을 조회해 결과를 확인하세요. **파라미터** - `sessionId` (path) · string · 필수 - 결제 세션 생성 응답으로 받은 현재 주문의 세션 식별값 **응답** - `200` 승인 완료 또는 확정된 실패 - `202` 현재 승인 요청의 이체 처리 중 - `400` 요청 형식 오류 - `401` 인증 실패 - `403` 접근 거부 - `404` 결제를 찾을 수 없음 - `422` 승인 기한 만료. 새 결제 세션으로 다시 시작해야 합니다. - `500` 서버 내부 오류 **200 응답 필드** - `id` · string · 필수 - 결제 세션 식별값입니다. - `object` · string · 필수 · 값: `paymentCapture` - 응답 객체 종류 - `status` · string · 필수 · 값: `captured`, `failed` - 승인 요청의 결과 상태. `failed`는 확정된 실패입니다. - `outcome` · object · 필수 - `type` · string · 필수 · 값: `succeeded`, `declined`, `paymentMethodUnavailable`, `serviceUnavailable`, `rejected`, `failed` - `code` · string · 필수 · 값: `PAYMENT_CAPTURED`, `PAYMENT_METHOD_DECLINED`, `PAYMENT_METHOD_LIMIT_EXCEEDED`, `PAYMENT_METHOD_UNAVAILABLE`, `PAYMENT_SERVICE_UNAVAILABLE_HOURS`, `PAYMENT_CAPTURE_REJECTED`, `PAYMENT_CAPTURE_ALREADY_FAILED` - 결과 코드. 분기 조건으로는 `message`가 아니라 이 값을 사용하세요. - `PAYMENT_METHOD_DECLINED`: 결제수단 승인 거절. 잔액 부족, 출금 가능 잔액 부족 등 - `PAYMENT_METHOD_LIMIT_EXCEEDED`: 이체한도 초과 - `PAYMENT_CAPTURE_REJECTED`: 세부 사유가 분류되지 않은 이체 실패 - `PAYMENT_CAPTURE_ALREADY_FAILED`: 이미 실패한 결제의 재요청. 원래 사유는 포함되지 않음 - `message` · string · 필수 - 가맹점에 공개되는 결제 승인 결과 설명 - `committedAt` · string · 필수 - 결제 커밋 기준일(KST) ```json { "id": "pymt_sess-019f0000-0000-7000-9000-000000000000", "object": "paymentCapture", "status": "captured", "outcome": { "type": "succeeded", "code": "PAYMENT_CAPTURED", "message": "결제승인 성공적으로 완료되었습니다." }, "committedAt": "2026-08-21" } ``` --- ## API: 결제 취소 승인 완료된 결제를 가맹점 서버에서 전부 또는 일부 취소합니다. ### 결제 취소 상태 조회 `GET https://api.point3.io/refunds/v1/{sessionId}` 결제 세션의 취소 상태, 취소 이력과 취소 가능 금액을 조회합니다. 취소 요청의 응답을 받지 못했거나 결과가 확실하지 않을 때 사용하세요. **파라미터** - `sessionId` (path) · string · 필수 - 승인 완료된 원 결제의 세션 식별값 **응답** - `200` 결제 취소 상태 조회 성공 - `401` 인증 실패 또는 허용되지 않은 접근 - `404` 취소 정보를 찾을 수 없음 - `500` 서버 내부 오류 **200 응답 필드** - `paymentSessionId` · string · 필수 - 원 결제 세션 식별값 - `clientId` · string · 필수 - 가맹점 클라이언트 식별값 - `status` · string · 필수 · 값: `refundable`, `processing`, `partiallyRefunded`, `fullyRefunded` - 결제 세션 단위의 전체 취소 상태 - `originalAmount` · integer · 필수 - 원 결제 총액 - `currency` · string · 필수 · 값: `KRW` - `refundableAmount` · integer · 필수 - 현재 추가로 취소할 수 있는 금액 - `productName` · string · 필수 - 원 결제 상품명 - `canCreateRefund` · boolean · 필수 - 새 취소 요청 가능 여부 - `refunds` · object[] · 필수 - 개별 취소 이력 - `id` · string · 필수 - 개별 취소 항목 식별값 - `status` · string · 필수 · 값: `processing`, `completed`, `failed` - `amount` · integer · 필수 - 개별 취소 총액 - `currency` · string · 필수 · 값: `KRW` - `taxFreeAmount` · integer · 필수 - 개별 취소의 면세 금액 - `vat` · integer · 필수 - 개별 취소의 부가세 금액 - `fee` · integer · 필수 - 개별 취소 처리 수수료 - `reason` · string · 필수 - 취소 사유 - `failure` · object · 필수 - `code` · string · 필수 · 값: `refundMethodDeclined`, `refundMethodUnavailable`, `refundServiceUnavailable`, `refundRejected`, `refundFailed` - 취소 실패 코드 - `message` · string · 필수 - 취소 실패 설명 - `createdAt` · string · 필수 - `updatedAt` · string · 필수 ```json { "paymentSessionId": "pymt_sess-019f0000-0000-7000-9000-000000000000", "clientId": "client-019f0000-0000-7000-9000-000000000001", "status": "partiallyRefunded", "originalAmount": 10000, "currency": "KRW", "refundableAmount": 5000, "productName": "아메리카노 외 1건", "canCreateRefund": true, "refunds": [ { "id": "ref-019f0000-0000-7000-9000-000000000000", "status": "completed", "amount": 5000, "currency": "KRW", "taxFreeAmount": 0, "vat": 455, "fee": 100, "reason": "고객 요청", "failure": null, "createdAt": "2026-08-21T00:00:00.000Z", "updatedAt": "2026-08-21T00:00:01.000Z" } ] } ``` ### 결제 취소 `POST https://api.point3.io/refunds/v1/{sessionId}` 승인 완료된 결제를 전부 또는 일부 취소합니다. 세금을 자동으로 계산하지 않으므로 `refundAmount`, `refundTaxFreeAmount`, `refundVat`를 모두 지정해야 합니다. 취소 결과가 확실하지 않으면 취소 상태 조회 API로 확인하세요. **파라미터** - `sessionId` (path) · string · 필수 - 승인 완료된 원 결제의 세션 식별값 - `Idempotency-Key` (header) · string · 선택 - 24시간 동안 같은 취소 요청의 중복 처리를 막는 선택 값입니다. **요청 본문** - `refundAmount` · integer · 필수 - 취소할 총 금액 - `refundTaxFreeAmount` · integer · 필수 - 취소 금액에 포함된 면세 금액 - `refundVat` · integer · 필수 - 취소 금액에 포함된 부가세 - `reason` · string · 필수 - 취소 사유 ```json { "refundAmount": 10000, "refundTaxFreeAmount": 0, "refundVat": 909, "reason": "고객 요청" } ``` **응답** - `200` 결제 취소 완료 - `400` 요청값 또는 세금 구성 오류 - `401` 인증 실패 또는 허용되지 않은 접근 - `404` 결제 세션 또는 취소 대상 없음 - `409` 중복 요청, 처리 중인 취소 또는 정책상 요청 불가. `result.code`를 확인하세요. - `422` 취소 가능 금액 초과 또는 취소 요청 거절 - `500` 서버 내부 오류 - `503` 취소 처리 서비스 일시 사용 불가 **200 응답 필드** - `id` · string · 필수 - 생성된 환불 항목 식별값 - `paymentSessionId` · string · 필수 - 원 결제 세션 식별값 - `clientId` · string · 필수 - 가맹점 클라이언트 식별값 - `productName` · string · 필수 - 원 결제 상품명 - `status` · string · 필수 · 값: `completed` - `amount` · integer · 필수 - 완료된 취소 금액 - `currency` · string · 필수 · 값: `KRW` - `taxFreeAmount` · integer · 필수 - 완료된 취소의 면세 금액 - `vat` · integer · 필수 - 완료된 취소의 부가세 금액 - `fee` · integer · 필수 - 취소 처리 수수료 - `reason` · string · 필수 - 취소 사유 - `createdAt` · string · 필수 - `updatedAt` · string · 필수 ```json { "id": "ref-019f0000-0000-7000-9000-000000000000", "paymentSessionId": "pymt_sess-019f0000-0000-7000-9000-000000000000", "clientId": "client-019f0000-0000-7000-9000-000000000001", "productName": "아메리카노 외 1건", "status": "completed", "amount": 10000, "currency": "KRW", "taxFreeAmount": 0, "vat": 909, "fee": 100, "reason": "고객 요청", "createdAt": "2026-08-21T00:00:00.000Z", "updatedAt": "2026-08-21T00:00:01.000Z" } ``` ### 결제 취소 처리 재개 `POST https://api.point3.io/refunds/v1/{sessionId}/resume` `processing` 상태인 취소 처리를 재개합니다. 먼저 취소 상태를 조회하고 처리 중인 취소가 있을 때만 호출하세요. **파라미터** - `sessionId` (path) · string · 필수 - 원 결제의 세션 식별값 **응답** - `200` 결제 취소 처리 재개 성공 - `401` 인증 실패 또는 허용되지 않은 접근 - `404` 취소 정보를 찾을 수 없음 - `500` 서버 내부 오류 - `503` 취소 처리 서비스 일시 사용 불가 **200 응답 필드** - `paymentSessionId` · string · 필수 - 원 결제 세션 식별값 - `clientId` · string · 필수 - 가맹점 클라이언트 식별값 - `status` · string · 필수 · 값: `refundable`, `processing`, `partiallyRefunded`, `fullyRefunded` - 결제 세션 단위의 전체 취소 상태 - `originalAmount` · integer · 필수 - 원 결제 총액 - `currency` · string · 필수 · 값: `KRW` - `refundableAmount` · integer · 필수 - 현재 추가로 취소할 수 있는 금액 - `productName` · string · 필수 - 원 결제 상품명 - `canCreateRefund` · boolean · 필수 - 새 취소 요청 가능 여부 - `refunds` · object[] · 필수 - 개별 취소 이력 - `id` · string · 필수 - 개별 취소 항목 식별값 - `status` · string · 필수 · 값: `processing`, `completed`, `failed` - `amount` · integer · 필수 - 개별 취소 총액 - `currency` · string · 필수 · 값: `KRW` - `taxFreeAmount` · integer · 필수 - 개별 취소의 면세 금액 - `vat` · integer · 필수 - 개별 취소의 부가세 금액 - `fee` · integer · 필수 - 개별 취소 처리 수수료 - `reason` · string · 필수 - 취소 사유 - `failure` · object · 필수 - `code` · string · 필수 · 값: `refundMethodDeclined`, `refundMethodUnavailable`, `refundServiceUnavailable`, `refundRejected`, `refundFailed` - 취소 실패 코드 - `message` · string · 필수 - 취소 실패 설명 - `createdAt` · string · 필수 - `updatedAt` · string · 필수 --- ## API: 위임 인증 가맹점에 로그인한 회원이 point3 결제창의 문자 인증 없이 결제하도록 합니다. 가맹점 서버가 회원 정보에 저장된 구매자 아이디(payerId)로 위임 인증을 생성해 결제창에 전달하면, 구매자는 문자 인증 없이 바로 결제를 진행할 수 있습니다. 자세한 내용은 [위임 인증](/delegated-authentication)과 [위임 인증 생성](/delegated-creation)을 확인하세요. ### 위임 인증 생성 `POST https://api.point3.io/authn/v2/delegation` 로그인한 회원의 `payerId`로 위임 인증을 생성합니다. 응답의 `authorization_url`을 JavaScript SDK `widgets()`의 `brandpay.redirectUrl`로 전달하세요. 위임 인증은 300초 동안 유효하며 한 번의 결제창 실행에만 사용할 수 있습니다. 가맹점은 API 키로 식별하므로 요청 본문에 가맹점 정보를 넣지 않습니다. **요청 본문** - `payer_id` · string · 필수 - 로그인한 회원의 `payerId`입니다. 호출한 가맹점 소속의 활성 구매자여야 합니다. ```json { "payer_id": "payer:00000000000000000000000000" } ``` **응답** - `200` 위임 인증 생성 성공 - `400` 요청 본문 형식 오류 또는 허용되지 않은 필드 포함 - `401` API 키 누락 또는 검증 실패 - `404` 구매자가 없거나, 비활성이거나, 다른 가맹점 소속 (구분하지 않음) - `503` 일시적 장애. 위임 인증 없이 결제창을 여세요. **200 응답 필드** - `delegation_id` · string · 필수 - 위임 인증 식별값입니다. - `expires_in` · integer · 필수 - 위임 인증의 유효 시간(초)입니다. - `authorization_url` · string · 필수 - 결제창에 전달할 URL입니다. 값을 바꾸지 말고 `widgets()`의 `brandpay.redirectUrl`로 그대로 전달하세요. ```json { "delegation_id": "dlg_3q2-7wE9bXk1Lr0Zt8pVnYc4uJmHs6aQfGdOiKe5NBw", "expires_in": 300, "authorization_url": "https://auth.point3.io/delegated?delegation_id=dlg_3q2-7wE9bXk1Lr0Zt8pVnYc4uJmHs6aQfGdOiKe5NBw" } ```