자동결제

데이터 저장 설계

회차·재시도·해지 컬럼을 미리 정해 운영 사고를 줄여요.

자동결제는 빌링키 저장​​과 회차 관리 두 가지 데이터를 가맹점이 직접 보관해야 해요. Bootpay가 발급한 빌링키와, 가맹점이 운영하는 청구 스케줄·재시도 상태를 분리해서 저장하는 게 핵심이에요.

핵심 요약

  • billing_keys는 결제수단 토큰의 현재 상태를 관리해야 해요.
  • recurring_payments는 언제 얼마를 다시 청구할지 관리해야 해요.
  • 결제 실패 재시도와 해지는 빌링키가 아니라 회차 테이블의 상태 전이로 처리해야 해요.
  • 스케줄러 조회용 (next_payment_at, status) 인덱스는 처음부터 둬요.
Bootpay 모델 ↔ 가맹점 모델 매핑이 궁금하면

데이터 모델 설계를 먼저 봐요. 이 페이지는 자동결제 운영용 회차 테이블에 집중해요.

1빌링키 저장 — billing_keys

빌링키 발급 후 받은 billing_key 와 표시용 카드 정보를 저장해야 해요. billing_key 자체는 PCI 민감정보가 아니지만, 가맹점 외부로 유출되지 않도록 관리해야 해요.

CREATE TABLE billing_keys (
    id          BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id     BIGINT NOT NULL,
    billing_key VARCHAR(100) NOT NULL,
    card_name   VARCHAR(50),
    card_last4  VARCHAR(4),
    status      VARCHAR(20) DEFAULT 'active',  -- active / revoked
    created_at  DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user (user_id)
);sql
컬럼 의미
billing_key 결제 요청 시 사용. 노출 최소화
card_last4 UI 표시용 마스킹 카드번호
status revoked로 두면 청구 스케줄 대상에서 제외
같은 사용자에 빌링키가 여러 개일 때

카드를 새로 등록할 때마다 row를 추가하고, 기존 row의 statusrevoked로 바꿔요. 한 사용자에 활성 빌링키가 항상 1개이도록 유지하면 청구 로직이 단순해져요.

2회차 관리 — recurring_payments

매월 청구 회차의 상태와 재시도 카운트를 추적해요. 스케줄러가 next_payment_at <= NOW() AND status = 'active' 인 행을 골라 빌링키 결제를 요청해요.

CREATE TABLE recurring_payments (
    id              BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id         BIGINT NOT NULL,
    plan            VARCHAR(50) NOT NULL,     -- 'monthly', 'yearly' 등
    amount          INTEGER NOT NULL,
    next_payment_at DATETIME NOT NULL,
    receipt_id      VARCHAR(100),
    status          VARCHAR(20) DEFAULT 'active',
    retry_count     INTEGER DEFAULT 0,
    created_at      DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_next (next_payment_at, status)
);sql
컬럼 의미
next_payment_at 다음 청구 시각. 성공하면 다음 주기로 이동하고, 실패하면 재시도 시각으로 조정
status activepausedcancelled 단방향 전이
retry_count 연속 실패 횟수. 3회 도달 시 paused로 전환
idx_next 스케줄러 조회 인덱스. (next_payment_at, status) 복합

status 전이

paused로 떨어지면 카드 변경 안내 메일을 보내고, 사용자가 카드를 재등록한 시점에 명시적으로 active로 올려요. 자동 복구는 하지 않아요 — 카드 변경 의사가 확인된 후에만 결제를 재개해야 분쟁을 줄여요.

idx_next 가 없으면 청구 누락 위험

스케줄러가 매번 풀스캔해야 하므로 회원이 늘면 청구 시각을 놓칠 수 있어요. (next_payment_at, status) 복합 인덱스는 처음부터 걸어둬요.

다음 단계