2026-08-09
정기결제 웹훅, 어떤 이벤트를 설계해야 할까
자체 구독결제 시스템을 만들 때 "웹훅 서명 검증"은 자료가 꽤 있지만, "애초에 어떤 이벤트를 내보내야 하는가"는 의외로 정리된 자료가 적습니다. 이 글은 슈퍼빌링이 실제로 채택한 4가지 필수 이벤트를 기준으로, 정기결제 웹훅 이벤트를 설계할 때 고려할 것들을 정리합니다.
왜 결제 성공/실패만으로는 부족한가
가장 단순한 설계는 "결제 성공"과 "결제 실패" 두 이벤트만 내보내는 것입니다. 하지만 구독은 결제 시점 외에도 상태가 바뀌는 지점이 여러 곳입니다. 고객사 입장에서 결제 이벤트만 받으면, 구독이 생성됐는지, 최종적으로 해지됐는지는 별도로 API를 폴링해서 확인해야 합니다. 웹훅의 목적이 "폴링 없이 상태 변화를 실시간으로 안다"는 것이므로, 이 갭을 그대로 두면 웹훅을 도입하는 의미가 반감됩니다.
슈퍼빌링의 4가지 필수 이벤트
- `subscription.created`: 구독이 새로 생성됐을 때. 고객사가 자체 DB에 구독 상태를 동기화하는 시작점입니다.
- `invoice.payment_succeeded`: 정기 청구가 성공했을 때. 결제 확인 후 서비스 이용 권한을 갱신하는 트리거로 씁니다.
- `invoice.payment_failed`: 정기 청구가 실패했을 때. 매 던닝 재시도마다 발생하므로, 고객사는 이 이벤트를 받아 "카드를 갱신해달라"는 알림을 사용자에게 보낼 수 있습니다.
- `subscription.canceled`: 구독이 해지됐을 때. 사용자가 직접 해지하거나, 던닝 재시도가 모두 소진돼 자동으로 해지되는 경우 모두 포함합니다.
이 4개를 고른 기준은 단순합니다 — 구독 생명주기에서 고객사가 "이 순간엔 반드시 알아야 하는" 지점만 남겼습니다. 지불수단 등록, 요금제 조회 같은 부수적인 동작은 웹훅 이벤트로 만들지 않았습니다. 이벤트 종류가 늘어날수록 고객사가 처리해야 할 핸들러도 늘어나기 때문에, 최소 집합으로 시작하는 편이 연동 난이도를 낮춥니다.
이벤트 페이로드 설계 원칙
슈퍼빌링 웹훅은 아래 형태로 발송됩니다.
{
"type": "invoice.payment_failed",
"data": {
"invoiceId": "...",
"subscriptionId": "...",
"amountKrw": 29000
}
}리소스 전체가 아니라 리소스 ID 위주로 최소한의 정보만 담습니다. 고객사가 상세 정보가 더 필요하면 해당 ID로 REST API를 호출해 조회하는 구조입니다. 이렇게 설계한 이유는 두 가지입니다.
- 페이로드가 가벼워 전송·파싱 비용이 낮습니다.
- API 응답 스키마가 바뀌어도 웹훅 페이로드는 영향받지 않아, 두 인터페이스의 변경 주기를 분리할 수 있습니다.
다만 이 설계는 "웹훅만 받고 추가 API 호출 없이 바로 처리하고 싶다"는 시나리오에는 불리합니다. 처리 속도가 중요하다면 이벤트에 필요한 필드를 몇 개 더 실어주는 절충안도 고려할 만합니다.
재시도와 멱등성
이벤트 설계와 별개로, 웹훅은 언제든 중복 전송될 수 있다는 전제를 깔아야 합니다. 슈퍼빌링은 전송 실패 시 최대 3회까지 재시도합니다. 이 재시도 정책과 수신 측 멱등 처리 설계는 웹훅 서명 검증 글에서 자세히 다뤘습니다.
정리
- 결제 성공/실패 이벤트만으로는 구독 생명주기 전체를 커버하지 못합니다. 생성·해지 시점도 포함하세요.
- 이벤트 종류는 "고객사가 반드시 알아야 하는 순간"만 최소로 유지하는 게 연동 난이도를 낮춥니다.
- 페이로드는 리소스 ID 위주로 가볍게 설계하고, 상세 정보는 API 조회로 분리하는 방식을 검토하세요.
- 슈퍼빌링은 이 4가지 이벤트와 재시도·서명 검증까지 이미 구현돼 있습니다. Quickstart 문서에서 확인할 수 있습니다.