이 문서는 커머스 체크아웃을 가장 짧게 붙이는 매뉴얼이에요. 전체 파라미터, 할인, 배송비, 프로모션은 주문서 요청에서 더 자세히 다뤄요.
상품을 등록해 product_id 를 확보하고, 프론트엔드가 그 ID와 수량으로 체크아웃을 열고, 결제가 끝나면 서버가 order_number 로 주문을 다시 조회해 검증하고, 마지막으로 웹훅이 상태를 한 번 더 보정해요. 아래 네 STEP 이 그 순서예요.
내가 하는 것
- 상품 등록 또는 기존 상품 ID 매핑
- 장바구니 금액·수량 검증
- 서버에서
order_number기준 주문 조회 - 웹훅 중복 처리와 주문 상태 저장
Bootpay가 알아서 하는 것
- 주문서 UI와 결제 진입
- 결제 처리와 주문 객체 생성
- 주문·취소·구독 이벤트 웹훅 발송
STEP 1. 상품을 준비해요
Bootpay 관리자 → 상품 관리에서 상품을 등록하고 product_id를 받아요.
API로 등록하려면 상품 등록을 사용해요. 기존 상품 DB가 있으면 내부 상품 ID와 Bootpay product_id를 함께 저장해 두면 이후 주문 검증이 쉬워져요.
STEP 2. 체크아웃을 열어요
아래 코드는 최소 흐름만 보여줘요. 실제 서비스에서는 서버에서 장바구니 금액과 재고를 먼저 검증하고, 검증된 상품 ID와 수량만 체크아웃에 넘겨요.
커머스 클라이언트 SDK는 @bootpay/bp-commerce-sdk 예요. 결제 SDK(@bootpay/client-js)와 다른 패키지고, default export 라 중괄호 없이 가져와요.
// npm install @bootpay/bp-commerce-sdk
import BootpayCommerce from '@bootpay/bp-commerce-sdk'
const CLIENT_KEY = 'your-client-key'
await BootpayCommerce.requestCheckout({
client_key: CLIENT_KEY,
request_id: 'your-order-draft-id', // 가맹점 주문 초안 ID (웹훅 매칭에 그대로 실려 와요)
name: '프로 플랜',
price: 29900, // 서버에서 계산한 금액
redirect_url: `${window.location.origin}/checkout/result`,
user: {
membership_type: 'guest', // 'member' 면 user_id 는 부트페이에 등록된 고객의 user_id 여야 해요
user_id: 'user_1234', // guest 면 가맹점 쪽 회원 식별자
name: '홍길동',
phone: '01012345678',
email: 'user@example.com',
},
products: [
{ product_id: 'your-product-id', quantity: 1 },
],
extra: {
open_type: 'redirect', // 'popup' | 'iframe' | 'redirect' — 필수예요
},
})javascript<script src="https://js.bootpay.co.kr/commerce/bp-commerce-sdk-1.0.7.min.js"></script>
<script>
// CDN 으로 넣으면 window.BootpayCommerce 로 잡혀요
BootpayCommerce.requestCheckout({
client_key: 'your-client-key',
request_id: 'your-order-draft-id',
name: '프로 플랜',
price: 29900,
redirect_url: location.origin + '/checkout/result',
user: { membership_type: 'guest', user_id: 'user_1234', name: '홍길동' },
products: [{ product_id: 'your-product-id', quantity: 1 }],
extra: { open_type: 'redirect' },
})
</script>html성공하면 쿼리스트링에 order_number(주문 고유번호)와 event(done, 분리 승인이면 confirm)가 실려요. 실패·취소면 event 없이 error_code·message 가 실려요.
전체 파라미터와 팝업·iframe 모드는 주문서 요청에서 다뤄요.
event=done 은 사용자의 브라우저에서 받은 신호예요. 서버가 주문 조회 API와 웹훅으로 같은 결과를 확인한 뒤 주문 상태를 바꿔야 해요.
STEP 3. 서버에서 주문을 검증해요
서버는 커머스 키로 주문을 조회하고, 내부 주문 기준과 비교해요.
GET /v1/orders/{order_number} 는 주문 고유번호(예: 25071182085082524116)로만 찾아요.
order_id(24자리 ObjectId)를 넣으면 ORDER_NOT_FOUND 가 돌아와요.
const auth = Buffer.from(
`${process.env.BOOTPAY_COMMERCE_CLIENT_KEY}:${process.env.BOOTPAY_COMMERCE_SECRET_KEY}`
).toString('base64')
const response = await fetch(`https://api.bootapi.com/v1/orders/${orderNumber}`, {
headers: {
Authorization: `Basic ${auth}`,
'Content-Type': 'application/json',
},
})
const order = await response.json()
if (order.price !== expectedPrice) {
throw new Error('주문 금액이 일치하지 않아요')
}
// 결제 완료 상태는 'payment_completed' 예요
if (order.status !== 'payment_completed') {
throw new Error('결제 완료 주문이 아니에요')
}
await markOrderPaid(order.order_number)javascriptimport os
import requests
response = requests.get(
f'https://api.bootapi.com/v1/orders/{order_number}',
auth=(os.environ['BOOTPAY_COMMERCE_CLIENT_KEY'], os.environ['BOOTPAY_COMMERCE_SECRET_KEY']),
)
order = response.json()
if order['price'] != expected_price:
raise ValueError('주문 금액이 일치하지 않아요')
# 결제 완료 상태는 'payment_completed' 예요
if order['status'] != 'payment_completed':
raise ValueError('결제 완료 주문이 아니에요')
mark_order_paid(order['order_number'])pythonorder_pending · payment_pending · payment_completed · cancellation_completed 등 전체 값은
커머스 Enum — 주문 상태에서 확인해요.
STEP 4. 웹훅으로 한 번 더 맞춰요
주문 상태가 바뀌면 웹훅으로 알림을 받아요.
| 이벤트 | 시점 | 서버가 할 일 |
|---|---|---|
order.done |
결제 완료 | 주문 완료 처리 또는 서비스 활성화 |
order.cancelled |
주문 취소 | 환불·서비스 비활성화 처리 |
order.confirm_error |
결제 승인 실패 | 주문 실패 처리 |
subscription.approved |
구독 시작 | 구독 권한 활성화 |
subscription.hold_on |
회차 결제 실패로 자동 중단 | 서비스 제한 + 복구 결제 안내 |
subscription.terminated |
구독 종료 | 구독 권한 해제 |
웹훅 설정은 주문 웹훅 설정, 이벤트별 처리 기준은 웹훅 처리 가이드를 이어서 확인해요. 실제 결제 없이 수신 경로를 확인하려면 웹훅 테스트 API의 POST /v1/webhook/test를 사용해요.
