예약결제

운영 주의사항

예약 취소와 빌링키 삭제 순서를 나눠 오결제를 막아요.

예약결제는 API 호출 자체보다 운영 순서에서 사고가 나기 쉬워요. 특히 예약 취소, 결제 취소, 빌링키 삭제​​는 서로 다른 작업이에요. 고객 화면에서는 모두 “결제수단 해지”처럼 보일 수 있지만, 서버에서는 어떤 예약을 먼저 멈추고 어떤 토큰을 삭제할지 순서를 정해야 해요.

핵심 요약

  • 빌링키 삭제는 결제수단 토큰 삭제이고, 예약 취소는 미래 결제 예약 제거예요.
  • 고객이 결제수단을 바꾸면 기존 예약을 취소하고 새 빌링키로 다시 예약해야 해요.
  • 예약 실행 전에는 예약 취소 API, 실행 후에는 일반 결제 취소 API를 사용해요.
  • 웹훅은 결과 신호로만 보고, 최종 처리는 receipt_id 조회와 내부 주문 비교로 확정해야 해요.
  • 시간대는 DB 저장 기준과 고객 표시 기준을 분리해요.

취소·변경 흐름

1빌링키 삭제 전에 예약을 먼저 확인해요

빌링키 삭제는 결제수단 토큰을 삭제하는 작업이고, 예약 취소는 미래 결제 예약을 제거하는 작업이에요. 둘은 같은 작업이 아니에요.

빌링키 삭제와 예약 취소는 별개예요

빌링키를 삭제해도 이미 등록된 예약은 그대로 남아 있어요. 예약 시각이 되면 실행이 시도되고, 삭제·만료된 빌링키라 결제가 안 되므로 SUBSCRIBE_BK_EXPIRED로 실패하면서 예약 상태가 -1이 돼요. 즉 결제는 막히지만 실패 기록이 쌓이고 고객에게는 실패 알림이 나갈 수 있어요.

고객이 예약 결제를 중단하거나 결제수단을 해지하는 경우에는 먼저 대기 중인 예약을 조회·취소하고, 그 다음 필요하면 빌링키를 삭제해요.

순서 작업 관련 문서
1 고객의 대기 중 예약을 조회해요 예약 조회
2 실행 전 예약을 취소해요 예약 취소
3 서비스 DB에서 예약 상태를 cancelled로 바꿔요
4 더 이상 결제수단을 쓰지 않으면 빌링키를 삭제해요 빌링키 삭제
5 서비스 DB의 빌링키 상태를 revoked로 바꿔요 데이터 저장 설계

2실행 전 취소와 실행 후 취소를 나눠요

예약 시간이 지나 실제 결제가 실행된 뒤에는 예약 취소 API로 되돌릴 수 없어요. 그때는 일반 결제 취소 API를 사용해야 해요.

상황 사용 API 저장 상태 예시
예약 등록만 됨 예약 취소 reservedcancelled
예약 실행 여부가 불확실함 예약 조회 후 판단 reserved / paid / failed 확인
결제가 이미 완료됨 결제 취소 paidrefunded 또는 cancelled
웹훅은 왔지만 내부 처리 실패 결제 조회 후 멱등 처리 paid_pending_syncpaid
운영 UI 문구

고객 화면의 “예약 취소”와 “결제 환불” 문구를 분리해요. 예약 실행 전에는 결제 자체가 아직 발생하지 않았으므로 환불이 아니라 예약 취소예요.

실행에 실패한 예약은 자동으로 재시도되지 않아요

예약 실행 작업은 재시도 없이 한 번만 돌아요. 잔액 부족·한도 초과·빌링키 만료 등으로 결제가 실패하면 예약 상태는 -1로 고정되고, 부트페이가 나중에 다시 결제를 시도하지 않아요.

다시 청구하려면 가맹점 서버가 직접 둘 중 하나를 해야 해요.

재청구 시점과 횟수 정책은 가맹점이 정해야 하므로, 실패 감지 후 어떤 흐름을 탈지 미리 설계해 두는 게 좋아요.

3결제수단 변경은 기존 예약 취소 후 재예약해요

고객이 카드를 바꾸면 새 빌링키가 생겨요. 기존 예약이 예전 빌링키로 등록되어 있다면 새 빌링키로 자동 변경된다고 가정하면 안 돼요.

순서 작업
1 새 결제수단으로 빌링키 발급
2 기존 reserve_id예약 취소
3 billing_key결제 예약 재등록
4 reserve_id 저장
5 기존 빌링키를 더 쓰지 않으면 빌링키 삭제

이 순서를 지키면 “카드는 바꿨는데 예약은 예전 카드로 실행되는” 상황을 줄일 수 있어요.

4reserve_idorder_id를 둘 다 저장해요

예약 등록 응답은 나중에 조회·취소·웹훅 매칭에 필요해요. 최소한 아래 값은 저장해야 해요.

필드 이유
reserve_id 예약 조회·취소의 기준 ID
order_id 가맹점 주문/예약과 Bootpay 결제를 매칭
billing_key 또는 내부 billing key ID 어떤 결제수단으로 예약했는지 추적
reserve_execute_at 고객 안내와 실행 전 취소 가능성 판단
price 웹훅·결제 조회 시 금액 검증
status reserved, cancelled, paid, failed 등 내부 상태 관리

예약 등록의 하드 제약

예약을 만들 때 서버가 강제하는 제약이에요. 설계 단계에서 미리 확인해야 운영 중에 등록이 막히지 않아요.

제약 내용 초과 시
실행 시각 상한 현재로부터 2개월 이내만 등록 가능 SR_RESERVE_TIME_SO_FAR_FUTURE (2516)
빌링키당 대기 예약 수 실행 전(status 0) 예약을 10건까지만 보유 SR_BK_RESERVE_OVER (2501)
order_id 재사용 같은 빌링키 안에서 중복 불가. 취소·완료된 예약의 값도 재사용 불가 SR_EXIST (2508)

매월 반복 청구처럼 예약을 길게 쌓아야 하는 구조라면 2개월 상한과 10건 제한에 먼저 걸려요. 그런 경우에는 자동결제로 설계하는 게 맞아요.

5웹훅을 최종 처리 기준으로 삼지 말고 다시 조회해요

예약 등록 성공은 결제 성공이 아니에요. 예약 시간이 지나면 결제 결과가 웹훅으로 전달되지만, 웹훅 payload만 믿고 주문을 확정하면 안 돼요.

단계 서버 처리
웹훅 수신 빠르게 HTTP 200 응답
영수증 확인 receipt_id결제 조회 호출
내부 주문 비교 금액, 주문번호, 상태, 중복 처리 여부 확인
상태 확정 검증 성공 시 내부 예약/주문 상태 업데이트

실패한 예약은 부트페이가 다시 실행하지 않아요. 실행은 재시도 없이 한 번만 돌고, 실패하면 예약 상태가 -1로 고정돼요. 그래서 실패를 감지한 뒤의 재청구는 전적으로 가맹점 서버 몫이에요. 새 order_id로 예약을 다시 등록하거나 빌링키 결제 요청을 직접 호출해야 해요.

웹훅 유실에 대비해 실행 예정 시각이 지난 예약은 예약 조회로 상태를 한 번 더 확인하는 보정 배치를 두면 안전해요.

웹훅 처리의 공통 설계는 웹훅을 함께 봐요.

6시간대 기준을 하나로 고정해요

reserve_execute_at은 사람에게 보이는 날짜와 서버가 저장하는 시간이 어긋나기 쉬워요.

  • DB에는 UTC 기준으로 저장해요.
  • 고객 화면에는 서비스 기준 시간대(KST 등)로 변환해 보여줘요.
  • API 요청 값과 고객 안내 문구가 같은 기준인지 로그에 남겨요.
  • 예약은 부트페이 스케줄러 큐에서 처리되므로, 실행 시각 직전에 등록하는 흐름은 피해요. 등록과 실행 사이에 여유를 두는 편이 안전해요.

다음 단계