이 문서는 발급받은 billing_key로 미래의 특정 시점에 결제될 예약을 등록하는 방법을 설명해요.
가맹점 서버가 reserve_execute_at으로 실행 시각을 지정하고 Bootpay API에 예약을 등록하면, 예약 시각에 PG사가 결제를 실행해요. 가맹점 서버는 예약 등록 응답의 reserve_id를 저장하고, 실제 결제 결과는 웹훅과 결제 조회로 확정해야 해요.
핵심 요약
- 이 API는
billing_key로 미래 결제 한 건을 예약해요. - 예약 등록 성공은 결제 성공이 아니에요. 실제 결제 결과는 예약 시각 이후 웹훅으로 확인해요.
reserve_id,order_id,billing_key,reserve_execute_at,price는 조회·취소·웹훅 매칭을 위해 저장해야 해요.- 같은 빌링키로 반복 예약을 만들 수 있지만, 매 회차 예약 등록과 상태 관리는 가맹점 서버가 해야 해요.
- 결제수단 변경이나 해지 가능성이 있다면 운영 주의사항을 먼저 읽어요.
예약 등록 성공은 결제 성공이 아니에요
예약 API 응답은 “미래 결제 예약이 등록됐다”는 뜻이에요. 주문을 결제 완료로 바꾸는 시점은 예약 실행 후 웹훅을 받고, receipt_id로 결제를 다시 조회한 뒤예요.
예약 등록 흐름
API 엔드포인트
POST
https://api.bootpay.co.kr/v2/subscribe/payment/reserveBasic Auth요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
billing_key |
String | 필수 | 부트페이에서 발급한 빌링키 |
price |
Number | 필수 | 결제 금액 (0보다 커야 함) |
reserve_execute_at |
Date | 필수 | 예약 실행 시간 |
order_name |
String | 선택 | 상품명, 주문명 |
order_id |
String | 선택 | 가맹점 고유 주문번호 |
tax_free |
Number | 선택 | 비과세 금액 (price 이하) |
metadata |
Object | 선택 | 커스텀 데이터 (결제 완료/취소 시 반환) |
user |
Object | 선택 | 구매자 정보 (일부 PG/결제수단에서 필요) |
items |
Array | 선택 | 상품 정보 (일부 PG/결제수단에서 필요) |
card_quota |
String | 선택 | 카드 할부 개월 수 |
card_interest |
String | 선택 | 무이자 할부 개월 수 |
feedback_url |
String | 선택 | 웹훅 수신 URL |
content_type |
String | 선택 | 웹훅 데이터 타입 |
reserve_execute_at 시간 형식
reserve_execute_at은 고객에게 안내한 결제 예정 시각과 정확히 맞춰야 해요. DB 저장 기준과 화면 표시 기준을 분리해 두면 운영이 편해요.
- UTC:
2024-06-01 12:00:00 UTC→ 한국시간 2024-06-01 21:00:00에 결제 - Timezone 포함:
2024-06-01T21:00:00 +0900→ 한국시간 2024-06-01 21:00:00에 결제
코드 예제
import { Bootpay } from '@bootpay/backend-js'
Bootpay.setConfiguration({
client_key: '[ Client Key ]',
secret_key: '[ Secret Key ]'
})
try {
const response = await Bootpay.subscribePaymentReserve({
billing_key: '[ billing_key ]',
order_name: '예약 잔금 결제',
order_id: 'reserve_' + Date.now(),
price: 9900,
reserve_execute_at: '2025-02-01T09:00:00 +0900'
})
// response.reserve_id를 데이터베이스에 저장해요.
console.log(response)
} catch (e) {
console.log(e)
}javascriptfrom bootpay_backend import BootpayBackend
import time
bootpay = BootpayBackend('APPLICATION_ID', 'PRIVATE_KEY')
response = bootpay.subscribe_payment_reserve(
billing_key='[ billing_key ]',
order_name='예약 잔금 결제',
order_id=f'reserve_{int(time.time())}',
price=9900,
reserve_execute_at='2025-02-01T09:00:00 +0900'
)
# response['reserve_id']를 데이터베이스에 저장해요.
print(response)pythonuse Bootpay\ServerPhp\BootpayApi;
BootpayApi::setConfiguration('APPLICATION_ID', 'PRIVATE_KEY');
$response = BootpayApi::subscribePaymentReserve([
'billing_key' => '[ billing_key ]',
'order_name' => '예약 잔금 결제',
'order_id' => 'reserve_' . time(),
'price' => 9900,
'reserve_execute_at' => '2025-02-01T09:00:00 +0900',
]);
// $response['reserve_id']를 데이터베이스에 저장해요.
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 = "예약 잔금 결제";
payload.orderId = "reserve_" + System.currentTimeMillis();
payload.price = 9900;
payload.reserveExecuteAt = "2025-02-01T09:00:00 +0900";
var response = bootpay.reserveSubscribe(payload);
// response.get("reserve_id")를 데이터베이스에 저장해요.
System.out.println(response);javabootpay = Bootpay::Api.new(application_id: 'APPLICATION_ID', private_key: 'PRIVATE_KEY')
response = bootpay.request(
uri: 'subscribe/payment/reserve',
payload: {
billing_key: '[ billing_key ]',
order_name: '예약 잔금 결제',
order_id: "reserve_#{Time.now.to_i}",
price: 9900,
reserve_execute_at: '2025-02-01T09:00:00 +0900'
}
)
# response.data['reserve_id']를 데이터베이스에 저장해요.
puts response.datarubyimport "github.com/bootpay/backend-go/v2"
api := bootpay.NewAPI("APPLICATION_ID", "PRIVATE_KEY", nil, "")
response, err := api.ReserveSubscribe(bootpay.SubscribePayload{
BillingKey: "[ billing_key ]",
OrderName: "예약 잔금 결제",
OrderId: fmt.Sprintf("reserve_%d", time.Now().Unix()),
Price: 9900,
ReserveExecuteAt: "2025-02-01T09:00:00 +0900",
})
if err != nil {
log.Fatal(err)
}
// response["reserve_id"]를 데이터베이스에 저장해요.
fmt.Println(response)gousing Bootpay;
using Bootpay.models;
var bootpay = new BootpayApi("APPLICATION_ID", "PRIVATE_KEY");
var response = await bootpay.ReserveSubscribe(new SubscribePayload
{
billingKey = "[ billing_key ]",
orderName = "예약 잔금 결제",
orderId = $"reserve_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}",
price = 9900,
reserveExecuteAt = "2025-02-01T09:00:00 +0900"
});
var content = await response.Content.ReadAsStringAsync();
// content에서 reserve_id를 추출해 데이터베이스에 저장해요.
Console.WriteLine(content);csharp응답
성공 응답
{
"reserve_id": "6261104e1fc19202e6f9420e",
"reserve_execute_at": "2025-02-01T09:00:00+09:00"
}json저장해야 하는 값
| 필드 | 이유 |
|---|---|
reserve_id |
예약 조회·취소의 기준 ID |
order_id |
가맹점 주문/예약과 결제 결과 매칭 |
billing_key 또는 내부 billing key ID |
어떤 결제수단으로 예약했는지 추적 |
reserve_execute_at |
고객 안내와 실행 전 취소 가능성 판단 |
price |
웹훅·결제 조회 시 금액 검증 |
status |
reserved, cancelled, paid, failed 등 내부 상태 관리 |
결제 실행 결과
예약된 시간에 결제가 진행되면 웹훅으로 결과가 전달돼요.
feedback_url과content_type을 요청에 넣으면 해당 값으로 웹훅이 전송돼요.- 요청값이 비어 있으면 부트페이 관리자에 설정된 웹훅 URL로 전송돼요.
- 관리자 설정도 없으면 웹훅은 전달되지 않아요.
- 웹훅을 받으면
receipt_id로 결제 조회을 호출하고, DB에 저장한 금액·주문 상태와 비교한 뒤 주문을 확정해야 해요.
에러 코드
공통 에러
인증·권한 관련 에러는 에러 코드표를 참고해요.
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
SUBSCRIBE_BK_NOT_FOUND (2309) |
빌링키 발급 내역을 찾지 못했다 | billing_key가 올바른지 확인해요 |
SUBSCRIBE_BK_EXPIRED (2310) |
빌링키 유효기간 만료 | 빌링키를 새로 발급받아요 |
SR_RESERVE_TIME_PAST (2500) |
예약 결제 시간이 과거이다 | reserve_execute_at을 미래 시간으로 설정해요 |
SR_ON_BLANK (2502) |
자동결제 예약 상품명을 입력한다 | order_name 값을 입력해요 |
SR_PRICE_LT_100 (2514) |
예약 결제 금액은 100원 이상 | price를 100원 이상으로 설정해요 |
SR_BK_RESERVE_OVER (2501) |
빌링키당 예약건이 10건 초과 | 기존 예약을 취소한 후 다시 등록해요 |
SR_EXIST (2508) |
동일 빌링키에 중복 order_id | 다른 order_id를 사용해요 |
SR_WEBHOOK_URL_BLANK (2515) |
웹훅 URL을 입력한다 | feedback_url을 입력하거나 관리자에서 웹훅 URL을 설정해요 |
RC_O_ID_BLANK (2005) |
order_id 값을 입력한다 | order_id를 유니크한 값으로 입력해요 |
다음 단계
더 읽을거리
- 예약결제 기획 전에 정할 기준 — 사용 사례와 예약·취소 정책 설계
