# 빌링계정 API - 결제 수단 생성 ## 1. API Overview ### Target Coverage
분류 결제 수단 (paymentMethod)
Instrument
(직접 수단)
[badge:CARD,indigo-subtle] [badge:DIRECT_DEBIT,indigo-subtle]
Agreement
(결제 계약)
[badge:NAVER_PAY,green-subtle] [badge:KAKAO_PAY,yellow-subtle] [badge:TOSS_PAY,blue-subtle] [badge:KFTC_CMS,red-subtle]
### Purpose [context] 재사용 가능한 결제 수단 또는 결제 계약을 등록하는 API입니다.
기존의 카드/계좌 직접 등록 방식뿐만 아니라, 간편결제 서비스(네이버/카카오/토스) 및 금융결제원과 같은 **계약 기반(Agreement)** 수단을 통합 관리할 수 있도록 설계되었습니다.

**핵심 설계 원칙:** - **인증 중심**: 요청 시 상세 정보를 받지 않고, 응답받은 `paymentUrl`을 통해 사용자 인증을 거친 후 시스템이 등록을 완료합니다. - **성격 분류**: `paymentMethodType` 필드를 통해 수단이 직접 관리 대상(`INSTRUMENT`)인지 권한 위임 계약(`AGREEMENT`)인지 구분합니다. - **데이터 유연성**: 수단별 특화 정보는 `details` JSON 필드를 통해 다형적으로 제공하며, 등록 시 전달한 부가 정보는 `metadata`를 통해 보존됩니다. [/context] ### Details [table:key-value] | 항목 | 값 | | :-------------- | :--------------------------- | | **API Name** | 결제 수단 생성 | | **API Path** | /api/v2/payment-methods | | **API ID** | EBP_API_120 | | **HTTP Method** | [badge:POST,blue,lg] | | **Region** | [badge:Global,green-subtle,lg] | [/table] ## 2. Request Specification ### 2.1 Request Data Schema 결제 수단 등록을 시작하기 위한 최소 정보만 전달합니다. 상세 정보는 이후 인증 단계에서 수집됩니다. | depth | Field | Details & Description | |:------|:--------------------|:---------------------------------------------------------------------------------------------------------------| | 0 | userNo | [type-ml:string,500] [req:Yes] [desc:사용자를 식별하는 고유 번호] | | 0 | email | [type-ml:string,128] [req:Optional] [desc:사용자 이메일 주소] | | 0 | paymentMethod | [type:string] [req:Yes] [desc:등록할 결제 수단 브랜드] [eg: CARD, NAVER_PAY, KFTC_CMS] | | 0 | successUrl | [type-ml:string,500] [req:Yes] [desc:등록 성공 후 리다이렉트할 URL] | | 0 | failureUrl | [type-ml:string,500] [req:Yes] [desc:등록 과정 실패 시 이동할 URL] | | 0 | billingAddress | [type:object] [req:Optional] [desc:청구지 주소 정보 (필요 시)] | | 0 | directDebitType | [type:string] [req:Conditional] [desc:결제 수단 DIRECT_DEBIT 이용 시 필수 항목입니다. 상세 내용은 하단의 **직불계좌 결제 정보 안내**를 참조하십시오.] | | 0 | metadata | [type:object] [req:Conditional] [desc:결제 수단별 추가 정보. 네이버페이 등 특정 브랜드 등록 시 필수입니다.] | | 1 | mid | [type:string] [req:Conditional] [desc:가맹점 식별 번호 (Merchant ID)] | | 1 | productCode | [type:string] [req:Conditional] [desc:등록 시 기준이 되는 상품 코드] | | 1 | productName | [type:string] [req:Conditional] [desc:등록 시 기준이 되는 상품 명칭] | | 1 | amount | [type:number] [req:Conditional] [desc:등록 또는 첫 결제 기준 금액] | > **직불계좌 결제 정보 (directDebitType) 안내** > > 태국 직불계좌 결제(`DIRECT_DEBIT`) 이용 시 다음의 지원 은행 코드를 확인하십시오. > - **direct_debit_bay** (Krungsri Bank) > - **direct_debit_kbank** (Kasikorn Bank) > - **direct_debit_ktb** (Krungthai Bank) > - **direct_debit_scb** (Siam Commercial Bank) ### 2.1.1 Metadata 메타데이터는 결제 수단에 따라 포함되는 필드가 다릅니다. #### 2.1.1.1 NAVER_PAY | Field | Details & Description | |:----------------------| :--- | | productCode | [req:Yes][type:string] [desc:등록 시 기준이 되는 상품 코드] | | productName | [req:Yes][type:string] [desc:등록 시 기준이 되는 상품 명칭] | | totalPayAmount | [req:Yes][type:number] [desc:등록 또는 첫 결제 기준 금액] | | purchaserName | [req:Conditional][type-ml:string,64] [desc:구매자 성명. 결제 상품이 보험 및 위험 업종 등인 경우에만 필수 값입니다.] | | purchaserBirthday | [req:Conditional][type-ml:string,8] [desc:구매자 생년월일(yyyyMMdd). 결제 상품이 보험 및 위험 업종 등인 경우에만 필수 값입니다.] | ## 2.2 Request Examples [tabs] [tab:CARD] ```json { "userNo": "AU1234567890", "email": "gildong.hong@example.com", "paymentMethod": "CARD", "successUrl": "https://example.com/success", "failureUrl": "https://example.com/failure" } ``` [tab:DIRECT_DEBIT] ```json { "userNo": "TH1234567890", "email": "gildong.hong@example.com", "paymentMethod": "DIRECT_DEBIT", "successUrl": "https://devkic-pgui.nebp.lge.com/pgui/v2/hpp/omise/success", "failureUrl": "https://devkic-pgui.nebp.lge.com/pgui/v2/hpp/omise/failure", "directDebitType": "direct_debit_bay" } ``` [tab:NAVER_PAY] ```json { "userNo": "AU1234567890", "email": "gildong.hong@example.com", "paymentMethod": "NAVER_PAY", "successUrl": "https://example.com/naver/success", "failureUrl": "https://example.com/naver/failure", "metadata": { "mid": "NAVER_MID_12345", "productCode": "SUBS_PROD_001", "productName": "정기 구독 서비스", "amount": 0 } } ``` [/tabs] ## 3. Response Specification ### 3.1 Response Data Schema @@include:standard-response.md@@ | -1 | data | [type:object] [req:Yes] [desc:응답 데이터] | | 0 | paymentMethodId | [type:string] [req:Yes] [desc:EBP 결제 수단 식별자] | | 0 | paymentMethod | [type:string] [req:Yes] [desc:결제 수단 브랜드 명칭] | | 0 | paymentMethodType | [type:string] [req:Yes] [desc:수단 성격 분류. `INSTRUMENT`(직접), `AGREEMENT`(계약)] | | 0 | pgChannel | [type:string] [req:Yes] [desc:등록 처리에 사용된 PG 채널 명칭] | | 0 | status | [type:string] [req:Yes] [desc:현재 상태] [eg:PENDING_AUTH] | | 0 | paymentUrl | [type:string] [req:Optional] [desc:사용자 인증을 진행할 EBP 표준 URL] | | 0 | paymentHeaderContext | [type:string] [req:Optional] [desc:EBP에서 발급한 암호화된 결제 헤더 컨텍스트. '결제 수단 등록 완료 API' 호출 시 x-ebp-context 헤더 값으로 전달해야 합니다.] | | 0 | details | [type:object] [req:Yes] [desc:수단별 상세 정보 [상세 규격](#1.4.2) 참조] | | 0 | metadata | [type:object] [req:Optional] [desc:등록 시 전달된 추가 정보 객체] | | 0 | pgResult | [type:object] [req:Optional] [desc:PG사 응답 결과 객체] | | 1 | resultCode | [type:string] [req:Yes] [desc:PG 응답 코드] | | 1 | pgResponse | [type:object] [req:Yes] [desc:PG사 원천 응답 데이터 상세] | | -1 | instructions | [type:object] [req:Yes] [desc:후속 처리를 위한 지침 (프로세스 제어)] | | 0 | nextStep | [type:string] [req:Yes] [desc:다음 행동 지시] [eg:CLIENT_ACTION] | | 0 | completionMethod | [type:string] [req:Yes] [desc:전체 프로세스의 최종 등록 완료 방식] [eg:WEBHOOK, API] | | 0 | requiresClientAction | [type:boolean] [req:Yes] [desc:클라이언트 추가 액션(HPP 이동, 토큰화 등)이 필요한지 여부] | | 0 | clientAction | [type:object] [req:Optional] [desc:클라이언트의 추가 액션 지시 정보 객체. `requiresClientAction`이 `true`인 경우 필수적으로 참조합니다.] | | 1 | type | [type:string] [req:Optional] [desc:클라이언트 액션 유형] [eg:TOKENIZE_CARD, CREATE_SOURCE] | | 1 | pgProvider | [type:string] [req:Optional] [desc:액션을 처리할 PG사] | | 0 | requiresFollowUpApi | [type:boolean] [req:Yes] [desc:후속 API(등록 완료 API) 호출이 필수인지 여부] | | 0 | followUpApi | [type:object] [req:Optional] [desc:Information for the follow-up API to be called after the client action. Mandatory if `requiresFollowUpApi` is `true`.] | | 1 | method | [type:string] [req:Optional] [desc:HTTP method of the follow-up API] [eg:POST] | | 1 | url | [type:string] [req:Optional] [desc:Call path of the follow-up API] | | 1 | description | [type:string] [req:Optional] [desc:Additional description of the follow-up API] | ### 3.2 결제 수단별 상세 정보 (details) `details` 객체는 각 결제 수단의 특성에 따라 다르게 구성됩니다. (보안상 민감한 Billing Key 등은 포함되지 않습니다.) #### CARD (Instrument) - `brand`: 카드 브랜드 (VISA, MASTER 등) - `last4`: 카드번호 끝 4자리 - `expiry`: 유효기간 (MM/YY) #### NAVER_PAY (Agreement) - `maskedIdentifier`: 마스킹된 계정 정보 (이메일 등) - `methodName`: 연결된 주 결제 수단 명칭 (예: 네이버페이 머니) #### KFTC_CMS (Agreement) - `bankName`: 은행 명칭 - `accountLast4`: 계좌번호 끝 4자리 - `holderName`: 예금주 성명 ## 3.3 Response Samples #### Case 1: Worldpay 카드 등록 (리다이렉트 + 웹훅 완료) PG사의 결제 페이지(HPP)를 통해 카드 정보를 입력받는 방식입니다. 사용자가 정보를 입력하면 PG사에서 EBP로 웹훅을 보내 등록이 최종 완료됩니다. * **Next Step**: `REDIRECT` (PG사 HPP로 이동 필요) * **Completion**: `WEBHOOK` (사용자 인증 완료 후 웹훅에 의해 비동기로 완료됨) ```json { "resultCode": "0", "message": "SUCCESS", "requestId": "06EFX0CRR7ME0KCSSTFEEDRDVG", "timestamp": "2026-01-27T04:39:47.222978900Z", "data": { "paymentMethodId": "3e104ef7b98f4123948a8c248d0da4c5", "orderNo": "ORD_7202603277730794", "status": "ACTION_REQUIRED", "paymentUrl": "https://devkic-pgui.nebp.lge.com/pgui/v2/hpp/worldpay/wrapping", "paymentHeaderContext": "eyJhY3Rpb24iOiJSRURJUkVDVF9UT19IUFAiLCJwcm92aWRlciI6IldPUkxEUEFZIiwiY29udGV4dCI6I...", "pgResult": { "resultCode": "PASS", "pgResponse": { "pgRefId": "3579052412", "returnUrl": "https://payments-test.worldpay.com/app/hpp/...", "referenceUrl": "https://payments-test.worldpay.com/app/hpp/..." } }, "successUrl": "http:///v2/ebp/test/token/success", "failureUrl": "http:///v2/ebp/test/token/fail" }, "instructions": { "nextStep": "REDIRECT", "completionMethod": "WEBHOOK", "requiresClientAction": true, "clientAction": { "type": "REDIRECT_TO_HPP", "pgProvider": "WORLDPAY" }, "requiresFollowUpApi": false } } ``` #### Case 2: Omise 카드 등록 (클라이언트 액션 + API 완료) 클라이언트가 Omise JS SDK를 사용하여 카드 정보를 직접 토큰화하는 방식입니다. 획득한 토큰을 EBP의 등록 완료 API로 전달하여 등록을 확정합니다. * **Next Step**: `CLIENT_ACTION` (JS SDK 호출 및 토큰 획득 필요) * **Completion**: `API` (획득한 토큰을 사용하여 [결제 수단 등록 완료 API](/docs/api-endpoints/billing/complete-payment-methods) 호출 필요) ```json { "resultCode": "0", "message": "SUCCESS", "requestId": "06EFWD32XT8HCBDCYR9RK7R0PC", "timestamp": "2026-01-27T04:39:50.123456700Z", "data": { "paymentMethodId": "3a9437e612ac4ccb9beaa6585e899321", "orderNo": "ORD_7202603277730795", "status": "ACTION_REQUIRED", "paymentUrl": "https://devkic-pgui.nebp.lge.com/pgui/v2/hpp/omise/card/wrapping", "paymentHeaderContext": "eyJhY3Rpb24iOiJUT0tFTklaRV9DQVJEIiwicHJvdmlkZXIiOiJPTUlTRSIsImNvbnRleHQiOiI...", "pgResult": { "resultCode": "SUCCESS", "pgResponse": { "publicKey": "pkey_test_46lmsec8z06uh..." } } }, "instructions": { "nextStep": "CLIENT_ACTION", "completionMethod": "API", "requiresClientAction": true, "clientAction": { "type": "TOKENIZE_CARD", "pgProvider": "OMISE" }, "requiresFollowUpApi": true, "followUpApi": { "method": "POST", "url": "/api/v2/payment-methods/3a9437e612ac4ccb9beaa6585e899321/complete", "description": "카드 토큰화 완료 후 이 API를 호출하여 등록을 확정해야 합니다." } } } ``` #### Case 3: Omise 직불계좌 등록 (클라이언트 액션 + 인증 필요) 클라이언트가 Omise JS SDK를 사용하여 계좌 정보를 소스화(SourceId 생성)하는 방식입니다. 획득한 소스 ID를 사용하여 응답받은 `paymentUrl`을 통해 사용자가 은행 인증을 진행하게 됩니다. 최종 등록은 인증 완료 후 웹훅을 통해 처리됩니다. * **1단계 (현재 API)**: `CLIENT_ACTION` (JS SDK 호출 및 SourceId 획득 필요) * **2단계 (인증)**: 응답받은 `paymentUrl`로 사용자 리다이렉트 및 은행 인증 * **최종 완료**: `WEBHOOK` (사용자 인증 완료 후 웹훅에 의해 비동기로 활성화됨) ```json { "resultCode": "0", "message": "SUCCESS", "requestId": "06EP2JWT2DHQEQGBC3W3RRK72W", "timestamp": "2026-04-06T05:37:22.536Z", "data": { "paymentMethodId": "1fc83f065287471987ee754ef01e7d40", "orderNo": "ORD_7202603277730796", "status": "ACTION_REQUIRED", "paymentUrl": "https://devkic-pgui.nebp.lge.com/pgui/v2/hpp/omise/direct-debit/wrapping", "paymentHeaderContext": "eyJhY3Rpb24iOiJDUkVBVEVfU09VUkNFIiwicHJvdmlkZXIiOiJPTUlTRSIsImNvbnRleHQiOiI...", "successUrl": "https://devkic-pgui.nebp.lge.com/pgui/v2/hpp/omise/success", "failureUrl": "https://devkic-pgui.nebp.lge.com/pgui/v2/hpp/omise/failure", "pgResult": { "resultCode": "OK", "pgProvider": "OMISE", "pgResponse": { "type": "OMISE_DIRECT_DEBIT", "linkedAccountId": "lnac_test_679grm7z8axarr41r6m", "registrationUri": "https://pay.omise.co/registrations/linked_accounts/lnac_test_679grm7z8axarr41r6m/authorize" } } }, "instructions": { "nextStep": "CLIENT_ACTION", "completionMethod": "WEBHOOK", "requiresClientAction": true, "clientAction": { "type": "CREATE_SOURCE", "pgProvider": "OMISE" }, "requiresFollowUpApi": false } } ``` #### Case4: 네이버페이 등록 시작 (Agreement) ```json { "resultCode": "0", "data": { "paymentMethodId": "pm_20260427_001", "paymentMethod": "NAVER_PAY", "paymentMethodType": "AGREEMENT", "pgChannel": "NAVER_PAY_BILLING", "status": "PENDING_AUTH", "paymentUrl": "https://devkic-pgui.nebp.lge.com/pgui/v2/hpp/naver/auth", "details": { "methodName": "네이버페이" }, "metadata": { "mid": "NAVER_MID_12345", "productCode": "SUBS_PROD_001", "productName": "정기 구독 서비스", "amount": 0 }, "pgResult": { "resultCode": "OK", "pgResponse": { "applyId": "REQ_20260427_XXX" } } }, "instructions": { "status": "ACTION_REQUIRED", "requiresClientAction": true, "clientAction": { "type": "REDIRECT_TO_HPP", "pgProvider": "NAVER_PAY" } } } ```