참고

데이터 모델 설계

주문·고객·구독 저장 기준을 내부 DB 모델로 바로 옮겨요.

"내 DB에 뭘 저장해야 해?" — Bootpay 연동 시 가맹점이 관리해야 하는 데이터를 정리해요.

이럴 때 필요해요​: DB 설계 시 어떤 테이블이 필요한지, API 응답에서 뭘 저장해야 하는지 파악할 때

Bootpay ↔ 가맹점 모델 매핑

Bootpay API 사용 시 데이터를 가맹점 DB에 동기화해요. Receipt는 orders 테이블에 포함​​시키면 돼요.

흐름 Receipt / 결제 영수증 -> orders / 주문 + 결제 정보 : 검증 API Order / 주문 -> orders / 주문 + 결제 정보 : 조회 API / 웹훅 OrderSubscription / 구독 계약 -> subscriptions / 구독 기록 : 구독 API / 웹훅 BillingKey / 빌링키 -> billing_keys / 저장 결제수단 : 조회 API

노드

ID 라벨 위치 타입
order Order / 주문 col 0, row 0 process
receipt Receipt / 결제 영수증 col 0, row 1 process
subscription OrderSubscription / 구독 계약 col 0, row 2 process
billingkey BillingKey / 빌링키 col 0, row 3 process
orders orders / 주문 + 결제 정보 col 1, row 0 success
subscriptions subscriptions / 구독 기록 col 1, row 2 success
billing_keys billing_keys / 저장 결제수단 col 1, row 3 success
Bootpay 모델 가맹점 테이블 동기화 방법 사용하는 API
Order (주문) orders 주문 조회 API + 웹훅 수신 커머스 API
Receipt (결제 영수증) orders에 결제 필드로 포함 결제 검증 API 응답 저장 결제 API
OrderSubscription subscriptions 구독 조회 API + 웹훅 수신 구독관리
BillingKey billing_keys 빌링키 조회 API 응답 저장 정기결제
어떤 테이블이 필요한가요?
  • 결제 API만 사용​: orders (결제 정보 포함) + billing_keys (정기결제 시)
  • 커머스 API 사용​: orders + subscriptions (구독 시)
Receipt를 별도 테이블로 분리해야 하는 경우

하나의 주문에 여러 결제가 발생하는 경우(부분결제, 추가결제)에는 order_payments 서브테이블로 분리할 수 있어요. 대부분의 서비스에서는 1주문 = 1결제이므로 orders에 포함시키는 것으로 충분해요.


API별 데이터 책임 경계

사용하는 API에 따라 데이터 책임 범위가 달라져요.

결제 API 커머스 API
Bootpay가 관리 결제 트랜잭션 결제 + 주문 + 구독 + 이행
가맹점이 관리 주문, 구독, 비즈니스 로직 전부 상품, 회원, 서비스 접근 제어
적합한 서비스 자체 주문 시스템이 있는 서비스 커머스 기능을 빠르게 구축하려는 서비스

결제 API — 데이터 책임 경계 {#payment-api}

결제 API는 결제 트랜잭션만 Bootpay가 처리해요. 주문, 구독, 이행 등 비즈니스 로직은 가맹점이 직접 구현해요.

가맹점이 직접 관리

데이터 설명
상품 · 회원 이름, 가격, 재고, Auth, 프로필
주문 관리 주문 생성, 상태 관리, 취소/환불 로직
구독 관리 구독 정책 설계, 회차 관리, 서비스 접근 제어
Bootpay ID 매핑 receipt_id, billing_key 등

Bootpay가 관리 → 가맹점이 동기화

데이터 Bootpay가 하는 일 가맹점이 하는 일
결제 상태 PG 승인 · 취소 · 환불 처리 웹훅으로 결제 상태 동기화
빌링 결제 저장된 카드로 결제 실행 웹훅으로 결제 결과 수신

Bootpay만 관리

데이터 설명
PG 트랜잭션 원본 카드사 승인번호, PG 응답 전문
빌링키 저장 결제수단, 토큰 (PCI DSS 준수)
웹훅 발송 이력 이벤트 전송 로그, 재시도 상태

결제 API는 결제 처리에 특화​​되어 있어요. 자체 주문 시스템이 이미 있거나, 주문/구독 로직을 직접 구현하려는 경우에 적합해요.

주문 테이블에 추가할 결제 필드

결제 API만 사용하는 경우, 자체 주문 시스템이 이미 있을 가능성이 높아요. 기존 주문 테이블에 아래 결제 필드만 추가하면 돼요.

-- 기존 주문 테이블에 결제 필드 추가
ALTER TABLE orders
    ADD COLUMN bootpay_receipt_id VARCHAR(100),  -- Bootpay 영수증 ID (검증·취소용)
    ADD COLUMN amount          INTEGER,          -- 결제 금액
    ADD COLUMN method          VARCHAR(20),      -- card, bank, phone 등
    ADD COLUMN paid_at         DATETIME,
    ADD INDEX idx_receipt (bootpay_receipt_id);sql
주문 테이블이 아직 없어요면 — 전체 CREATE 문
CREATE TABLE orders (
    id              BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id         BIGINT NOT NULL,
    order_id        VARCHAR(100) NOT NULL,    -- 가맹점 주문번호
    order_name      VARCHAR(200),
    bootpay_receipt_id VARCHAR(100),           -- Bootpay 영수증 ID
    amount          INTEGER NOT NULL,
    method          VARCHAR(20),
    status          VARCHAR(20) DEFAULT 'pending',  -- pending → paid → refunded
    paid_at         DATETIME,
    created_at      DATETIME DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_user (user_id),
    INDEX idx_order (order_id),
    INDEX idx_receipt (bootpay_receipt_id)
);sql

저장 시점​: 결제 검증 성공 후 — 이후 상태 변경(취소, 환불)은 웹훅으로 동기화

// 결제 검증 성공 → 주문에 결제 정보 업데이트
if (receipt.status === 1 && receipt.price === expectedPrice) {
    await db.orders.update({
        bootpay_receipt_id: receipt.receipt_id,
        amount: receipt.price,
        method: receipt.method_symbol,  // "card", "bank", "phone"
        status: 'paid',
        paid_at: new Date(receipt.purchased_at)
    }, { where: { order_id: orderId } })
}javascript

결제 검증 응답에서 저장할 필드

응답 필드 저장 여부 용도
receipt_id 필수 결제 조회, 취소 시 필요
order_id 권장 가맹점 주문번호 매핑
price 필수 결제 금액 기록
method_symbol 권장 결제수단 표시 (card, bank 등)
card_data.card_company 선택 "신한카드" 등 표시용
purchased_at 권장 결제 완료 시각
status 필수 결제 상태 (1: 완료)

커머스 API — 데이터 책임 경계 {#commerce-api}

커머스 API는 주문 · 구독 · 이행까지 Bootpay가 관리해요. 가맹점은 상품과 회원만 관리하면 돼요.

가맹점이 직접 관리

데이터 설명
상품 · 회원 이름, 가격, 재고, Auth, 프로필
Bootpay ID 매핑 order_id, subscription_id 등
비즈니스 로직 주문 메모, 내부 태그, CRM 데이터

Bootpay가 관리 → 가맹점이 동기화

데이터 Bootpay가 하는 일 가맹점이 하는 일
결제 상태 PG 승인 · 취소 · 환불 처리 웹훅으로 상태 동기화 (CS 조회용)
주문 상태 주문 → 결제 → 이행 상태머신 관리 웹훅으로 상태 동기화 (운영 대시보드)
이행 상태 발주 확인 → 배송 → 배달 추적 웹훅으로 배송 현황 동기화
구독 상태 활성 · 정지 · 해지 전환, 자동 과금 웹훅으로 서비스 접근 제어 on/off
취소/환불 취소 요청 → 승인 → 금액 계산 → PG 환불 웹훅으로 취소 상태 동기화

Bootpay만 관리

데이터 설명
PG 트랜잭션 원본 카드사 승인번호, PG 응답 전문
빌링키 저장 결제수단, 토큰 (PCI DSS 준수)
구독 과금 이력 회차별 결제 시도 · 성공 · 실패 · 재시도
상품 스냅샷 주문 시점의 가격 · 옵션 고정
웹훅 발송 이력 이벤트 전송 로그, 재시도 상태

상태 데이터의 정본(source of truth)은 항상 Bootpay​​예요. 가맹점 DB의 상태는 캐시 역할이며, 불일치 시 API로 최신 상태를 확인해요.

Bootpay 내부 모델 구조

커머스 API가 내부적으로 관리하는 데이터 모델이에요. 가맹점이 직접 접근하지는 않지만, API 응답과 웹훅 데이터가 이 구조를 따르므로 이해해두면 연동이 수월해요.

Project (가맹점 프로젝트)
├── User (고객, 개인정보 암호화)
│   └── UserWallet (저장 결제수단, 빌링키)

├── Product (상품)
│   ├── ProductOption (옵션: 색상, 사이즈 등)
│   ├── ProductSnapshot (주문 시점 가격 고정)
│   └── SubscriptionSetting (구독 정책: 주기, 재시도, 해지)

├── OrderPre (결제 전 임시 주문)
│   └── Order (결제 완료 확정 주문)
│       ├── OrderPurchase (판매자별 이행/배송 단위)
│       └── OrderCancellationRequestHistory (취소/환불 이력)

├── OrderSubscription (구독 계약)
│   └── OrderSubscriptionBill (회차별 과금)
│       └── OrderSubscriptionTransaction (결제 실행)

└── WebhookJob → WebhookLog (이벤트 발송/이력)
3단계 주문 구조

주문은 OrderPre(결제 전)Order(결제 완료)OrderPurchase(이행) 3단계로 처리돼요. 결제 전 주문서는 일정 시간(기본 31분) 후 자동 만료되며, 결제가 완료되면 Order로 확정돼요. 상세는 주문 흐름 설계에서 확인해요.

도메인별 상세

주문 영역

데이터 Bootpay 가맹점 동기화
주문번호 ↔ 내부 ID 매핑 - 직접 관리 -
주문 메모, 내부 태그 - 직접 관리 -
주문 상태 (결제대기 → 완료 → 취소) 관리 동기화 웹훅 order.*
결제 트랜잭션 (PG 응답, 카드사) 관리 - API 조회
이행 상태 (확인 → 배송 → 배달) 관리 동기화 웹훅 order.purchase.*
취소/환불 처리 관리 동기화 웹훅 order.cancelled
배송 추적 (택배사, 운송장) 관리 - API 조회

구독 영역

데이터 Bootpay 가맹점 동기화
구독 ID ↔ 서비스 정책 매핑 - 직접 관리 -
서비스 접근 제어 (active/inactive) - 직접 관리 -
구독 상태 (대기 → 활성 → 정지 → 해지) 관리 동기화 웹훅 subscription.*
회차별 과금 + 자동 재시도 관리 - API 조회
구독 정책 (주기, 재시도, 해지 조건) 관리 - API 설정

고객 영역

데이터 Bootpay 가맹점 동기화
자체 Auth (로그인, 비밀번호) - 직접 관리 -
고객 프로필 (이름, 이메일, 전화번호) 암호화 보관 선택 저장 -
저장 결제수단 (빌링키) 관리 - -
결제/주문/구독 이력 관리 - API 조회

최소 DB 스키마

주문 + 결제

CREATE TABLE orders (
    id              BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id         BIGINT NOT NULL,
    bootpay_order_id    VARCHAR(100),     -- Bootpay 주문 ID
    bootpay_order_number VARCHAR(100),    -- Bootpay 주문 번호
    order_name      VARCHAR(200),
    total_amount    INTEGER NOT NULL,
    status          VARCHAR(20) DEFAULT 'pending',
    -- pending → paid → confirmed → completed → refunded
    paid_at         DATETIME,
    created_at      DATETIME DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_user (user_id),
    INDEX idx_bootpay_order (bootpay_order_id)
);

CREATE TABLE order_items (
    id              BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_id        BIGINT NOT NULL,
    product_id      BIGINT NOT NULL,      -- 가맹점 상품 ID
    product_name    VARCHAR(200),
    quantity        INTEGER DEFAULT 1,
    unit_price      INTEGER NOT NULL,

    FOREIGN KEY (order_id) REFERENCES orders(id)
);sql

저장 시점​:

  1. 주문서 요청 전 → orders 레코드 생성 (status: pending)
  2. 서버 검증 성공 후 → status를 paid로 변경, bootpay_order_id 저장
  3. 웹훅 order.cancelled 수신 → status를 refunded로 변경

주문 + 이행/배송 관리

물리적 배송이 있는 쇼핑몰이라면 이행(Fulfillment) 상태를 추적하는 테이블을 추가해요.

CREATE TABLE order_fulfillments (
    id                  BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_id            BIGINT NOT NULL,
    bootpay_order_number VARCHAR(100) NOT NULL,   -- Bootpay 주문 번호
    progress_status     VARCHAR(20) DEFAULT 'ready',
    -- ready → confirmed → preparing → shipped → delivered
    confirmed_at        DATETIME,                 -- 발주 확인
    shipped_at          DATETIME,                 -- 배송 시작
    delivered_at        DATETIME,                 -- 배송 완료
    delivery_company    VARCHAR(50),              -- 택배사명
    tracking_number     VARCHAR(100),             -- 운송장 번호

    FOREIGN KEY (order_id) REFERENCES orders(id),
    INDEX idx_order (order_id),
    INDEX idx_status (progress_status)
);sql

Bootpay 이행 상태와의 매핑​:

Bootpay 내부 상태 코드 가맹점 progress_status
PRE_ORDER 0 ready
CONFIRMED 2 confirmed
PREPARING 3 preparing
SENT 4 shipped
DELIVERED 5 delivered

디지털 상품이라면 이행 테이블 없이 orders.statuspaidcompleted로 바로 전환하면 돼요.

구독 결제

CREATE TABLE subscriptions (
    id              BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id         BIGINT NOT NULL,
    bootpay_subscription_id VARCHAR(100) NOT NULL,  -- Bootpay 구독 ID
    plan_name       VARCHAR(100),         -- "Pro", "Enterprise" 등
    amount          INTEGER NOT NULL,     -- 회차 결제 금액
    service_active  BOOLEAN DEFAULT FALSE,-- 서비스 접근 허용 여부
    status          VARCHAR(20) DEFAULT 'pending',
    -- pending → active → paused → terminated
    current_period_end DATETIME,          -- 현재 구독 기간 종료일
    created_at      DATETIME DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_user (user_id),
    INDEX idx_bootpay_sub (bootpay_subscription_id)
);sql

저장 시점​:

  1. 구독 신청 시 → subscriptions 레코드 생성 (status: pending)
  2. 웹훅 subscription.approvedservice_active = true, status: active
  3. 웹훅 subscription.terminatedservice_active = false, status: terminated
중요

구독 회차별 결제 이력은 Bootpay가 관리해요. 가맹점은 서비스 접근 제어​(active/inactive)만 관리하면 돼요. 회차 상세는 회차 조회 API로 확인해요.

주문 조회 응답에서 저장할 필드

응답 필드 저장 여부 용도
order_id 필수 Bootpay 주문 ID
order_number 필수 Bootpay 주문 번호
price 필수 결제 금액
status 필수 주문 상태
purchased_at 권장 결제 시각
result_data.receipt_id 권장 결제 영수증 ID

저장하면 안 되는 것

PCI DSS 준수

아래 데이터는 절대 저장하면 안 돼요​:

  • 카드 번호 전체 (full PAN)
  • CVV/CVC
  • 카드 비밀번호
  • 유효기간 (월/년)

마스킹된 카드번호(557042******1074)와 카드사명은 저장해도 돼요.


데이터 흐름 상세

결제 API — Receipt → orders

참여자: 가맹점 DB | 서버 | BOOTPAY

단계 흐름 유형 비고
1 서버 -> BOOTPAY: ① 결제 검증 / GET /receipt/{receipt_id} 실선
2 BOOTPAY -> 서버: ② Receipt 응답 / receipt_id, price, status 점선
3 서버: ③ 금액·상태 검증 실선 ✓ price 일치 확인 / ✓ status=1 확인
4 서버 -> 가맹점 DB: ④ orders에 저장 실선
5 BOOTPAY -> 서버: ⑤ 웹훅 (취소/환불 시) 실선
6 서버 -> 가맹점 DB: ⑥ status 업데이트 실선

필드 매핑

Bootpay Receipt 필드 가맹점 orders 컬럼 저장 용도
receipt_id bootpay_receipt_id 필수 결제 조회·취소 시 사용
price amount 필수 결제 금액 (검증용)
tax_free tax_free 필수 비과세 금액 (회계)
status status 필수 0:대기, 1:완료, 2:취소
method_symbol method 권장 "card", "bank" 등
purchased_at paid_at 권장 결제 완료 시각
order_id merchant_order_id 권장 가맹점 주문번호 매핑
card_data.card_company card_name 선택 "신한카드" 등 표시용
cancelled_price cancelled_amount 선택 부분취소 금액 추적

커머스 API — Order → orders

참여자: 가맹점 DB | 서버 | BOOTPAY

단계 흐름 유형 비고
1 BOOTPAY -> 서버: ① onDone 콜백 / order_id 전달 점선
2 서버 -> BOOTPAY: ② 주문 검증 / GET /orders/{order_id} 실선
3 BOOTPAY -> 서버: ③ Order 응답 / order_id, price, status, products 점선
4 서버: ④ 금액·상태 검증 실선 ✓ price 일치 / ✓ status=2 (결제완료)
5 서버 -> 가맹점 DB: ⑤ orders에 저장 실선
6 BOOTPAY -> 서버: ⑥ 웹훅 (order.done / cancelled) 실선
7 서버 -> 가맹점 DB: ⑦ status 업데이트 실선

필드 매핑

Bootpay Order 필드 가맹점 orders 컬럼 저장 용도
order_id bootpay_order_id 필수 주문 조회·취소 시 사용
order_number bootpay_order_number 필수 주문 번호
price total_amount 필수 결제 금액 (검증용)
status status 필수 주문 상태
purchased_at paid_at 권장 결제 완료 시각
result_data.receipt_id receipt_id 권장 결제 영수증 ID
user.user_id customer_id 권장 구매 고객 연결
products[].product_id order_items.product_id 권장 주문 상품
products[].quantity order_items.quantity 권장 수량

주문 상태 코드

상태

ID 라벨 위치 타입
init col 0, row 0 initial
created 주문 생성 (0) col 1, row 0 normal
paying 결제 진행 (1) col 2, row 0 normal
paid 결제 완료 (2) col 3, row 0 active
cancel_req 취소 요청 (3) col 2, row 1 normal
cancelled 취소 완료 (4) col 3, row 1 end

전이 -> 주문 생성 (0) : 주문서 생성 주문 생성 (0) -> 결제 진행 (1) : 결제 시작 결제 진행 (1) -> 결제 완료 (2) : 결제 성공 결제 완료 (2) -> 취소 요청 (3) : 취소 요청 취소 요청 (3) -> 취소 완료 (4) : 취소 승인

status 의미 가맹점 처리
0 주문 생성 (결제 대기)
PROCESS_DUPLICATED 이미 처리된 요청이에요
2 결제 완료 orders에 저장, 서비스 제공
3 취소 요청 취소 승인/거절 판단
4 취소 완료 status 업데이트, 환불 처리

커머스 API — OrderSubscription → subscriptions

참여자: 가맹점 DB | 서버 | BOOTPAY

단계 흐름 유형 비고
1 서버 -> BOOTPAY: ① 구독 승인 / POST /approve 실선
2 BOOTPAY -> 서버: ② Subscription 응답 / id, status, next_billing_at 점선
3 서버 -> 가맹점 DB: ③ subscriptions에 저장 / service_active = true 실선 구독 시작
4 BOOTPAY: ④ 매 회차 자동 과금 / 빌링키로 결제 실선
5 BOOTPAY -> 서버: ⑤ 웹훅 (갱신 성공) 실선
6 서버 -> 가맹점 DB: ⑥ 기간 연장 / next_billing_at 업데이트 실선
7 BOOTPAY -> 서버: ⑦ 웹훅 (해지/정지) 실선
8 서버 -> 가맹점 DB: ⑧ 서비스 차단 / service_active = false 실선

필드 매핑

Bootpay Subscription 필드 가맹점 subscriptions 컬럼 저장 용도
id bootpay_subscription_id 필수 구독 조회·해지 시 사용
order_id bootpay_order_id 필수 원 주문 연결
status status 필수 구독 상태
price amount 필수 회차별 결제 금액
billing_cycle billing_cycle 권장 monthly, yearly 등
next_billing_at next_billing_at 권장 다음 결제 예정일
service_start_at started_at 권장 서비스 시작일
approved_at approved_at 선택 승인 시각

구독 상태 코드

상태

ID 라벨 위치 타입
init col 0, row 0 initial
pending 대기 (0) col 1, row 0 normal
active 구독 중 (1) col 2, row 0 active
paused 일시정지 (2) col 1, row 1 normal
terminated 해지 (3) col 2, row 1 end
expired 만료 (4) col 3, row 1 end

전이 -> 대기 (0) : 주문서 결제 대기 (0) -> 구독 중 (1) : 승인 구독 중 (1) -> 일시정지 (2) : 정지 요청 일시정지 (2) -> 구독 중 (1) : 재개 구독 중 (1) -> 해지 (3) : 해지 구독 중 (1) -> 만료 (4) : 기간 만료

status 의미 가맹점 service_active
0 대기 (승인 전) false
1 구독 중 true
2 일시정지 false (일시)
3 해지 false
4 만료 false
가맹점의 핵심 역할

구독 과금은 Bootpay가 자동으로 해요. 가맹점은 웹훅을 수신하여 service_active 플래그만 on/off​​하면 돼요.

빌링 — BillingKey → billing_keys

참여자: 가맹점 DB | 서버 | BOOTPAY

단계 흐름 유형 비고
1 BOOTPAY -> 서버: ① 빌링키 발급 완료 / billing_key 반환 점선
2 서버 -> 가맹점 DB: ② billing_keys에 저장 실선
3 서버 -> BOOTPAY: ③ 결제 요청 / billing_key로 결제 실선 필요한 시점에 / 결제 실행
4 BOOTPAY -> 서버: ④ Receipt 응답 점선
5 서버 -> 가맹점 DB: ⑤ orders에 저장 실선

필드 매핑

Bootpay 필드 가맹점 billing_keys 컬럼 저장 용도
billing_key billing_key 필수 결제 요청 시 사용
pg pg_name 권장 PG사 이름
method_symbol method 권장 card, bank 등
card_data.card_company card_name 선택 "신한카드" 등
card_data.card_no card_last4 선택 마스킹 카드번호

Bootpay 내부 모델 구조

커머스 API가 내부적으로 관리하는 모델의 관계예요. 가맹점이 직접 접근하지는 않지만, API 응답과 웹훅이 이 구조를 따르므로 이해해두면 연동이 수월해요.

흐름 Product / 상품 -> Order / 주문 : 주문서 결제 Product / 상품 -> OrderSubscription / 구독 계약 : 구독 상품 Order / 주문 -> Receipt / 결제 영수증 : 결제 Order / 주문 -> OrderPurchase / 이행·배송 : 이행 OrderSubscription / 구독 계약 -> SubscriptionBill / 회차 과금 : 매 회차

노드

ID 라벨 위치 타입
product Product / 상품 col 0, row 0
order Order / 주문 col 1, row 0 process
receipt Receipt / 결제 영수증 col 2, row 0 success
purchase OrderPurchase / 이행·배송 col 2, row 1
subscription OrderSubscription / 구독 계약 col 1, row 2 process
bill SubscriptionBill / 회차 과금 col 2, row 2 success
모델 설명 API 응답에서 확인
Product 상품 (일반/구독) 상품 조회
Order 결제 완료된 확정 주문 주문 상세
OrderPurchase 판매자별 이행/배송 단위 주문 상세 응답 내 purchases
Receipt PG 결제 영수증 결제 검증 · 결제 조회
OrderSubscription 구독 계약 계약 조회
SubscriptionBill 회차별 과금 기록 회차 조회