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분 안에 연동해볼 수 있습니다.