2026-08-09

빌링키 결제는 스케줄러가 따로 없다 — 직접 구현할 때 필요한 것들

토스페이먼츠, 페이플 같은 국내 PG의 자동결제(빌링) API는 "빌링키"라는 결제수단 식별자를 발급해줄 뿐, 그 빌링키로 언제 얼마를 청구할지는 순전히 개발자 몫입니다. 토스페이먼츠 공식 개발자센터조차 "구독 결제 서비스 구현하기 (2) 스케줄링" 문서에서 node-cron 같은 라이브러리로 직접 배치를 짜는 방법을 안내합니다. 이 글은 빌링키 기반 정기결제를 직접 구현할 때 스케줄러에서 실제로 고려해야 할 것들을 정리합니다.

빌링키는 "결제수단"이지 "구독"이 아니다

PG가 제공하는 건 카드 정보를 암호화해 저장한 빌링키뿐입니다. 이 고객이 어떤 요금제를 구독 중인지, 다음 결제일은 언제인지, 결제가 실패하면 언제 재시도할지 같은 구독 상태 전체는 PG가 관리해주지 않습니다. 즉 최소한 아래 정보를 직접 설계한 스키마에 저장해야 합니다.

  • 고객별 빌링키
  • 구독 중인 요금제와 금액
  • 다음 결제 예정일(nextBillingAt)
  • 결제 실패 시 재시도 횟수

스케줄러가 매일 해야 하는 일

가장 단순한 구조는 매일 한 번 배치를 돌려 "오늘이 결제일인 구독"을 찾아 순회하며 청구하는 것입니다. 슈퍼빌링은 Vercel Cron으로 매일 자정 /api/cron/billing을 호출합니다.

// vercel.json
{
  "crons": [
    { "path": "/api/cron/billing", "schedule": "0 0 * * *" }
  ]
}

이 엔드포인트가 호출하는 로직은 대략 이런 흐름입니다.

const due = await db.select(/* ... */)
  .from(subscriptions)
  .where(
    and(
      eq(subscriptions.state, "active"),
      lte(subscriptions.nextBillingAt, new Date()),
      isNotNull(customers.paypleBillingKey),
    ),
  );

for (const row of due) {
  const chargeResult = await chargeBillingKey({
    billingKey: row.billingKey,
    amountKrw: row.plan.pricingModel.amountKrw,
    goodsName: row.plan.name,
    orderId: `inv-${invoice.id}`,
  });
  // 성공하면 다음 결제일로 nextBillingAt 갱신, 실패하면 던닝 로직으로
}

직접 구현할 때 놓치기 쉬운 것들

1. 월별 결제일 밀림 처리

setMonth로 단순히 한 달을 더하면 1월 31일 구독이 2월엔 3월 3일로 밀리는 식의 날짜 오버플로가 생길 수 있습니다(2월은 28~29일까지만 있기 때문). 결제 주기를 다루는 함수를 만들 땐 이 엣지 케이스를 반드시 테스트하세요.

2. 배치가 하루 안 돈 날의 처리

스케줄러가 특정 시각에 정확히 도는 게 아니라 "결제일이 지난 구독"을 조건으로 찾기 때문에, 배포 장애 등으로 하루 배치가 실행되지 않아도 다음 실행 때 자연스럽게 따라잡힙니다. nextBillingAt <= now 조건으로 조회하면 별도 보정 로직 없이도 복구됩니다.

3. 사용량 기반 요금제는 별도 취급

정액제(flat)는 매 주기 같은 금액을 청구하면 되지만, 사용량 기반이나 좌석 기반 요금제는 "이번 주기에 얼마를 썼는지" 집계가 선행돼야 합니다. 이 집계 로직이 없다면 자동 청구 대상에서 제외하고 수동 처리 경로를 따로 둬야 합니다.

4. 결제 실패는 별개의 문제

청구가 실패했을 때 무조건 구독을 끊을지, 재시도를 둘지는 스케줄러 설계와 분리해서 다뤄야 하는 주제입니다. 슈퍼빌링이 채택한 재시도 정책은 던닝 재시도 주기 글에서 다뤘습니다.

정리

  • 빌링키 발급까지는 PG API로 끝나지만, "언제 얼마를 청구할지"는 직접 스케줄러를 만들어야 합니다.
  • 최소한 구독 상태(빌링키, 요금제, 다음 결제일, 재시도 횟수)를 저장할 스키마와, 매일 도래한 구독을 찾아 청구하는 배치가 필요합니다.
  • 날짜 오버플로, 배치 장애 복구, 사용량 기반 요금제 처리는 놓치기 쉬운 엣지 케이스입니다.
  • 슈퍼빌링은 이 스케줄러 전체를 이미 구현해뒀습니다. Quickstart 문서로 3분 안에 연동해볼 수 있습니다.

3분만에 구독결제 연동해보기

요금제 설계부터 웹훅 등록까지, 슈퍼빌링으로 바로 시작할 수 있습니다.

무료로 시작하기