일회성 결제를 구현해 본 개발자라면 정기 결제가 얼마나 복잡한지 체감하게 됩니다. 단순히 매달 카드를 긁는 것이 아닙니다. 결제 실패 시 재시도 로직, 요금제 업그레이드와 다운그레이드 시 일할 계산, 무료 체험에서 유료 전환, 해지와 환불 정책, 세금 처리와 영수증 발행까지 고려할 요소가 한두 가지가 아닙니다.
Zuora의 보고서에 따르면 구독 경제는 전통 비즈니스 대비 5배 빠르게 성장하고 있어 이 영역의 기술적 역량은 점점 더 중요해지고 있습니다. 구독 시스템은 결제 기술과 비즈니스 로직이 복잡하게 얽힌 영역입니다.
결제 게이트웨이 선택
한국 시장에서 정기 결제를 구현할 때 주요 선택지는 세 가지입니다. 토스페이먼츠는 빌링키 방식으로 정기 결제를 지원하며 개발자 친화적인 API 문서와 테스트 환경을 제공합니다. 샌드박스 환경에서 실제 결제 없이 전체 플로우를 테스트할 수 있어 개발 효율이 높습니다.
NicePay는 다양한 결제 수단을 지원하며 안정적인 운영 이력을 가지고 있습니다. 국내 PG사 중 오래된 이력을 보유하고 있어 레거시 시스템과의 호환성이 좋습니다. Stripe는 글로벌 서비스를 운영할 때 최적의 선택으로 구독 관리 기능이 API 레벨에서 내장되어 있어 별도의 구독 로직을 구현할 필요가 대폭 줄어듭니다.
빌링키 방식은 사용자의 카드 정보를 토큰화하여 저장하고, 정해진 주기에 서버에서 자동으로 결제를 요청하는 구조입니다.
PCI 컴플라이언스와 보안
카드 번호를 직접 저장하는 것은 절대 해서는 안 됩니다. PCI DSS 규정을 위반하는 것은 물론이고 보안 사고 발생 시 막대한 법적 책임이 따릅니다. 반드시 결제 게이트웨이가 제공하는 토큰화 서비스를 사용해야 합니다.
빌링키는 해당 결제사에서만 유효한 토큰이므로 유출되더라도 직접적인 피해가 제한됩니다. 백엔드에서는 빌링키를 암호화하여 데이터베이스에 저장하고, 결제 요청 시에만 복호화하여 사용하는 패턴을 적용합니다.
결제 관련 로그에도 카드 번호 마스킹을 철저히 적용하여 개발자도 원본 데이터에 접근할 수 없도록 해야 합니다.
결제 실패와 Dunning Management
정기 결제에서 가장 골치 아픈 부분은 결제 실패 처리입니다. 카드 한도 초과, 카드 만료, 일시적 네트워크 오류 등 다양한 원인으로 결제가 실패합니다. 업계 평균 비자발적 이탈률(involuntary churn)은 전체 이탈의 20-40%를 차지합니다.
이때 즉시 서비스를 중단하면 이탈률이 급증합니다. 업계 표준은 다음과 같은 재시도 스케줄을 적용합니다. 첫 번째 실패 후 3일 뒤 재시도, 두 번째 실패 후 5일 뒤 재시도, 세 번째 실패 후 7일 뒤 최종 시도. 각 재시도 전에 사용자에게 이메일과 푸시 알림으로 결제 수단 업데이트를 요청합니다.
유예 기간 동안은 서비스를 유지하되 기능 제한을 두는 방식이 효과적입니다. 적절한 dunning 전략만으로도 비자발적 이탈의 30-50%를 회수할 수 있습니다.
요금제 변경과 일할 계산
사용자가 월 중간에 상위 요금제로 업그레이드하면 남은 기간에 대한 차액을 청구해야 합니다. 예를 들어 월 1만원 요금제를 사용하다가 15일차에 월 3만원으로 업그레이드하면 남은 15일분의 차액 1만원을 즉시 청구하는 방식입니다.
반대로 다운그레이드 시에는 다음 결제 주기부터 변경 요금을 적용하는 것이 일반적입니다. 이 일할 계산(proration) 로직은 서버 사이드에서 정확하게 처리해야 합니다. 클라이언트 사이드에서 결제 상태를 판단하는 것은 매우 위험하며 절대로 신뢰해서는 안 됩니다.
JavaScript를 조작하면 프론트엔드의 결제 검증은 쉽게 우회할 수 있기 때문입니다.
프로덕트 메이커의 구현 경험
저희는 연매출 300억에 가까운 주차권 쇼핑몰 케이엠파크(km-park.com)의 결제 시스템을 직접 구축·운영한 경험이 있습니다. 케이엠파크가 다루는 주차장은 수요가 공급을 크게 앞서는 과밀 지구에 위치해 있어 자리(TO)가 한정되어 있고, 미리 결제한 사용자에게 다음 기간의 쿼터를 우선 배정하는 구조로 운영됩니다. 결과적으로 빌링키 기반 원클릭 결제, 만료 전 자동 연장 결제, 정기 사용자에 대한 자동 결제까지 정기 결제 시스템이 다뤄야 할 시나리오 대부분이 실제로 그대로 들어왔습니다.
이 과정에서 본문에 적은 시나리오 대부분이 실전으로 들어왔습니다. 자동 연장이 카드 한도 초과·만료로 실패할 때 유예 기간을 두고 단계적으로 재시도·알림하는 dunning 처리, 주차권을 더 긴 기간으로 중간에 바꿀 때의 일할 계산, 카드가 만료되는 시점에 빌링키 재등록을 자연스럽게 유도하는 플로우, 환불·세금계산서·현금영수증 같은 부수 기능까지 한 코드베이스 안에서 운영했습니다.
특히 원클릭 결제는 사용자 입장에서 한 번의 탭으로 끝나지만, 그만큼 가격·기간·권한 검증을 백엔드에서 다시 하지 않으면 어떤 형태로든 우회 시도가 들어옵니다. 빌링키 암호화 저장과 사용 시점에만 복호화, 결제 상태를 active·past_due·grace·canceled·refunded 의 상태 머신으로 관리, 실제 청구·환불은 비동기 큐로 분리하는 식의 패턴은 본문에 정리한 일반론을 이 시스템에서 직접 운영하면서 정착시킨 결과물입니다.