이 문서는 발급받은 빌링키로 서버에서 결제를 요청하는 방법을 설명해요. 정기결제, 사용량 과금처럼 고객이 다시 결제창을 열지 않아도 되는 흐름을 구현할 때 사용해요.
여기서 다루는 API는 서버가 호출한 시점에 결제를 요청하는 POST /v2/subscribe/payment이에요. 미래 특정 시점에 한 번 실행할 결제는 같은 빌링키를 쓰더라도 예약결제에서 별도 예약 API로 등록해야 해요.
핵심 요약
billing_key로 현재 시점의 결제를 요청해요.- 매월·매주 청구 대상 선정과 호출 시점 결정은 가맹점 스케줄러가 담당해요.
- 성공 응답의
receipt_id는 결제 조회·취소·웹훅 멱등 처리의 기준이므로 반드시 저장해야 해요. - 미래 특정 시점에 한 번 실행하려면 이 API에
execute_at을 넣는 방식이 아니라 예약결제의reserve_execute_at으로 등록해야 해요.
결제 요청 흐름
API 엔드포인트
POST
https://api.bootpay.co.kr/v2/subscribe/paymentBasic Auth결제 요청과 예약결제 구분
| 목적 | API | 실행 시각 파라미터 |
|---|---|---|
| 지금 결제 요청 | POST /v2/subscribe/payment |
없음 |
| 미래 1회 결제 예약 | POST /v2/subscribe/payment/reserve |
reserve_execute_at |
execute_at은 이 문서의 결제 요청 API에 넣는 공식 요청 파라미터가 아니에요. 예약 실행 시각은 예약 API의 reserve_execute_at으로 전달해야 해요. 일부 SDK 내부 모델이나 응답 필드에서 execute_at이라는 이름이 보이더라도, 연동할 때는 어느 엔드포인트를 호출하는지를 기준으로 구분해요.
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
billing_key |
String | 필수 | 부트페이에서 발급한 빌링키 |
price |
Number | 필수 | 결제 금액 (0보다 커야 함) |
order_name |
String | 선택 | 상품명, 주문명 |
order_id |
String | 선택 | 가맹점 고유 주문번호 |
tax_free |
Number | 선택 | 비과세 금액 (price 이하) |
metadata |
Object | 선택 | 커스텀 데이터 (결제 완료/취소 시 반환) |
user |
Object | 선택 | 구매자 정보 (페이앱: 전화번호 필수) |
items |
Array | 선택 | 상품 정보 (페이코: 필수) |
card_quota |
String | 선택 | 카드 할부 개월 수 (00, 02, 03 등) |
card_interest |
String | 선택 | 무이자 할부 개월 수 (일부 PG만 지원) |
feedback_url |
String | 선택 | 웹훅 수신 URL (미입력 시 관리자 설정값) |
content_type |
String | 선택 | 웹훅 데이터 타입 (application/json 또는 application/x-www-form-urlencoded) |
코드 예제
import { Bootpay } from '@bootpay/backend-js'
Bootpay.setConfiguration({
client_key: '[ Client Key ]',
secret_key: '[ Secret Key ]'
})
try {
const response = await Bootpay.requestSubscribePayment({
billing_key: '[ billing_key ]',
order_name: '월 정기결제 - 2025년 1월',
order_id: 'monthly_' + Date.now(),
price: 9900,
tax_free: 0
})
console.log(response)
} catch (e) {
console.log(e)
}javascriptfrom bootpay_backend import BootpayBackend
import time
bootpay = BootpayBackend('APPLICATION_ID', 'PRIVATE_KEY')
response = bootpay.request_subscribe_payment(
billing_key='[ billing_key ]',
order_name='월 정기결제 - 2025년 1월',
order_id=f'monthly_{int(time.time())}',
price=9900,
tax_free=0
)
print(response)pythonuse Bootpay\ServerPhp\BootpayApi;
BootpayApi::setConfiguration('APPLICATION_ID', 'PRIVATE_KEY');
$response = BootpayApi::requestSubscribePayment([
'billing_key' => '[ billing_key ]',
'order_name' => '월 정기결제 - 2025년 1월',
'order_id' => 'monthly_' . time(),
'price' => 9900,
'tax_free' => 0,
]);
print_r($response);phpimport kr.co.bootpay.pg.Bootpay;
import kr.co.bootpay.pg.model.request.SubscribePayload;
Bootpay bootpay = new Bootpay("APPLICATION_ID", "PRIVATE_KEY");
SubscribePayload payload = new SubscribePayload();
payload.billingKey = "[ billing_key ]";
payload.orderName = "월 정기결제 - 2025년 1월";
payload.orderId = "monthly_" + System.currentTimeMillis();
payload.price = 9900;
payload.taxFree = 0;
var response = bootpay.requestSubscribe(payload);
System.out.println(response);javabootpay = Bootpay::Api.new(application_id: 'APPLICATION_ID', private_key: 'PRIVATE_KEY')
response = bootpay.request(
uri: 'subscribe/payment',
payload: {
billing_key: '[ billing_key ]',
order_name: '월 정기결제 - 2025년 1월',
order_id: "monthly_#{Time.now.to_i}",
price: 9900,
tax_free: 0
}
)
puts response.datarubyimport "github.com/bootpay/backend-go/v2"
api := bootpay.NewAPI("APPLICATION_ID", "PRIVATE_KEY", nil, "")
response, err := api.RequestSubscribe(bootpay.SubscribePayload{
BillingKey: "[ billing_key ]",
OrderName: "월 정기결제 - 2025년 1월",
OrderId: fmt.Sprintf("monthly_%d", time.Now().Unix()),
Price: 9900,
TaxFree: 0,
})
fmt.Println(response)gousing Bootpay;
var bootpay = new BootpayApi("APPLICATION_ID", "PRIVATE_KEY");
var response = await bootpay.RequestSubscribe(new SubscribePayload
{
billingKey = "[ billing_key ]",
orderName = "월 정기결제 - 2025년 1월",
orderId = $"monthly_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}",
price = 9900,
taxFree = 0
});
Console.WriteLine(response);csharp응답
빌링키 결제 요청 응답도 결제 상태에 따라 나눠서 처리해요. 성공 응답의 receipt_id는 이후 결제 조회·취소·웹훅 멱등 처리의 기준이에요.
{
"receipt_id": "6261104e1fc19202e6f9420e",
"order_id": "monthly_1705289520000",
"price": 9900,
"tax_free": 0,
"cancelled_price": 0,
"cancelled_tax_free": 0,
"order_name": "월 정기결제 - 2025년 1월",
"method_origin": "card",
"method_origin_symbol": "카드",
"purchased_at": "2025-01-15T14:32:00+09:00",
"status": 1
}json{
"receipt_id": "6261104e1fc19202e6f9420e",
"order_id": "monthly_1705289520000",
"price": 9900,
"method_origin": "bank",
"method_origin_symbol": "계좌이체",
"purchased_at": "2025-01-15T14:32:00+09:00",
"status": 1
}json{
"error_code": "RC_REQUEST_FAILED",
"pg_error_code": "F113",
"message": "한도초과 또는 잔액부족으로 결제가 거절되었습니다.",
"payload": {
"receipt_id": "6261104e1fc19202e6f9420e",
"order_id": "monthly_1705289520000"
}
}json에러 코드
공통 에러
인증·권한 관련 에러는 에러 코드표를 참고해요.
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
SUBSCRIBE_BK_NOT_FOUND (2309) |
빌링키 발급 내역을 찾지 못했다 | billing_key가 올바른지 확인해요 |
SUBSCRIBE_BK_EXPIRED (2310) |
빌링키 유효기간 만료 | 빌링키를 새로 발급받아요 |
RC_NAME_BLANK (2003) |
상품명을 입력한다 | order_name 값을 입력해요 |
RC_O_ID_BLANK (2005) |
order_id 값을 입력한다 | order_id를 유니크한 값으로 입력해요 |
RC_PRICE_LEAST_LT (2004) |
100원보다 큰 금액을 결제할 수 있다 | price를 100원 이상으로 설정해요 |
RC_PROVIDER_NOT_ALLOW (2104) |
가맹점이 탈퇴/차단 상태 | 부트페이 관리자에서 가맹점 상태를 확인해요 |
RC_PROVIDER_PAYMENT_NOT_ALLOW (2102) |
서비스 이용료 미납으로 결제 불가 | 부트페이 서비스 이용료를 납부해요 |
RC_NOT_CONFIRM_READY (2023) |
결제 승인 대기 상태가 아니다 | 결제 상태를 확인 후 재시도해요 |
RC_CONFIRM_METHOD_NOT_FOUND (2025) |
결제 승인 함수가 구현되지 않은 PG이다 | PG사에 문의해요 |
RC_CONFIRM_CRITICAL_FAILED (2085) |
서버에서 치명적인 오류 발생 | 부트페이 관리자에 문의해요 |
RC_REQUEST_FAILED (2013) |
결제 요청이 실패했다 | 결제 정보를 확인 후 재시도해요 |
