B2B SaaS 결제 실패·dunning 자동화 체크리스트 2026, invoice.payment_failed와 고객 알림을 연결하기 전 확인할 기준
B2B SaaS 결제 실패·dunning 자동화 체크리스트 2026, invoice.payment_failed와 고객 알림을 연결하기 전 확인할 기준
B2B SaaS에서 결제 실패는 단순히 카드가 막힌 사건이 아닙니다. 무료체험에서 유료로 넘어간 고객, 이미 제품을 쓰는 팀, 연간 계약 검토 중인 계정, 기존 고객의 추가 workspace가 모두 다른 방식으로 결제 실패를 겪을 수 있습니다.
이 흐름을 제대로 나누지 않으면 invoice.payment_failed 하나가 마케팅 전환 실패, billing 문제, 고객 이탈, 제품 접근 차단, 영업 handoff를 동시에 흔듭니다. 결제 재시도는 자동으로 돌지만 CRM에는 아무 표시가 없고, 고객에게는 이메일이 중복 발송되며, 제품 화면에서는 여전히 active처럼 보이는 일이 생깁니다.
이 글은 회계, 세무, 법률, 채권 회수 자문이 아닙니다. B2B SaaS 운영팀이 결제 실패와 dunning 자동화를 제품, billing, CRM, 고객 알림, 분석 보고서와 연결하기 전 확인할 운영 데이터 체크리스트입니다. 실제 청구, 환불, 매출 인식, 계약 조건은 회사 기준과 전문가 검토를 따라야 합니다.
무료체험이 유료 전환으로 이어지는 기준은 B2B SaaS trial-to-paid 전환 추적 감사 체크리스트에서 먼저 정리했습니다. 무료체험 품질 필터는 B2B SaaS 무료체험 남용·trial 품질 필터 체크리스트를 보면 됩니다. 결제 관리자와 카드 owner가 흩어져 있다면 SaaS 결제수단·관리자 인수인계 체크리스트를 먼저 점검하고, 갱신 일정은 SaaS 갱신 캘린더 체크리스트와 같이 봐야 합니다. 결제 실패 뒤 실제 취소와 churn reason을 보고서에서 분리하려면 B2B SaaS 구독 취소·churn reason 추적 감사 체크리스트를 이어서 확인하면 됩니다. GA4와 GTM 권한이 흔들린 상태라면 GA4·Google Tag Manager 권한 감사 체크리스트부터 정리하는 편이 안전합니다.
먼저 결제 실패 상태를 하나로 뭉치지 않습니다
결제 실패라고 모두 같은 상태는 아닙니다. 첫 결제 실패, 갱신 결제 실패, 카드 만료, 은행 거절, 인증 실패, 고객 취소 직전 상태가 서로 다른 후속 조치를 요구합니다.
| 상태 | 운영상 의미 | 바로 확인할 점 |
|---|---|---|
| first_invoice_failed | trial-to-paid 전환 실패 후보 | 유료 전환으로 집계했는지 확인 |
| renewal_payment_failed | 기존 고객 갱신 결제 실패 | 접근 제한, CS 알림, 관리자 이메일 확인 |
| payment_method_expired | 카드 만료 가능성 | 결제수단 업데이트 안내 경로 확인 |
| authentication_required | 추가 인증 필요 | 고객이 결제 인증을 완료할 수 있는지 확인 |
| past_due | 결제 기한 경과 상태 | 제품 접근 정책과 CRM 상태 동기화 |
| unpaid | 장기 미납 또는 회수 실패 후보 | 자동화 중단, 수동 검토 기준 확인 |
| canceled_after_failed_payment | 실패 후 취소 | 이탈 보고서와 결제 실패 보고서 중복 제거 |
대시보드에는 payment_failed 하나만 두기보다 billing_failure_stage, failure_reason, retry_status, customer_notice_status, access_status를 나눠 두는 편이 좋습니다.
Stripe invoice와 subscription 상태를 같이 봅니다
Stripe Billing을 쓰는 팀은 invoice와 subscription 상태를 함께 확인해야 합니다. invoice가 실패했다고 해서 subscription 상태가 즉시 같은 의미로 바뀌는 것은 아니고, 재시도와 고객 알림 설정에 따라 흐름이 달라집니다.
출처: Stripe Docs – Smart Retries
출처: Stripe Docs – Customer emails
출처: Stripe API Reference – Invoice object
출처: Stripe Docs – Subscriptions overview
| 확인 항목 | 감사 질문 |
|---|---|
| invoice status | open, paid, void, uncollectible 상태를 구분하는가 |
| subscription status | active, past_due, unpaid, canceled를 별도 저장하는가 |
| attempt_count | 재시도 횟수가 보고서에 남는가 |
| next_payment_attempt | 다음 재시도 예정 시점을 운영 화면에서 볼 수 있는가 |
| hosted invoice URL | 고객이 안전하게 결제수단을 업데이트할 경로가 있는가 |
| customer email | 알림 대상 이메일이 현재 관리자 또는 billing owner인가 |
| price ID | 실패한 invoice가 어떤 플랜과 연결되는가 |
| account ID | 제품 account와 Stripe customer가 정확히 연결되는가 |
invoice.payment_failed를 받았다고 바로 고객 접근을 끊는 구조는 위험할 수 있습니다. 실패 원인, 재시도 정책, 고객 등급, 계약 조건, 제품 접근 기준을 분리해야 합니다.
webhook은 중복 처리와 순서 문제를 전제로 둡니다
결제 실패 자동화는 webhook에 크게 의존합니다. 하지만 webhook 이벤트는 재전송될 수 있고, 처리 순서가 기대와 다를 수 있습니다. 따라서 이벤트 ID와 최종 billing 상태를 함께 봐야 합니다.
출처: Stripe Docs – Subscription webhooks
| 이벤트 또는 신호 | 처리 기준 |
|---|---|
invoice.payment_failed |
실패 이벤트를 만들되, 같은 invoice 중복 처리 방지 |
invoice.payment_succeeded |
recovery 성공으로 연결하고 실패 상태 닫기 |
customer.subscription.updated |
past_due, unpaid, canceled 상태 전환 반영 |
payment_method.updated |
결제수단 변경 후 재시도 흐름과 연결 |
| event_id | 중복 webhook 처리 방지 |
| invoice_id | 같은 청구서의 실패와 성공을 하나의 흐름으로 묶기 |
| subscription_id | 구독 단위 이력 추적 |
| event created time | 이벤트 발생일과 처리일 구분 |
운영 자동화는 webhook 도착 순서보다 billing 시스템의 최종 상태를 기준으로 계산해야 합니다. 실패 이벤트를 먼저 받고 성공 이벤트를 뒤늦게 받았다면, 고객 상태는 recovered로 바뀌어야 합니다.
dunning 캠페인의 목적을 단계별로 나눕니다
dunning은 단순히 "미납 고객에게 계속 메일 보내기"가 아닙니다. 결제수단 갱신, 결제 재시도, 고객 관리자 확인, CS handoff, 제품 접근 정책, 취소 또는 보류 처리를 단계별로 나누는 운영 흐름입니다.
출처: Chargebee Docs – Dunning v2
출처: Recurly Docs – Dunning management
| 단계 | 목적 | 확인할 기준 |
|---|---|---|
| 실패 직후 | 결제 실패 사실 확인 | 실패 사유, invoice ID, 고객 관리자 이메일 |
| 1차 재시도 전 | 결제수단 업데이트 유도 | 중복 이메일 방지, 안전한 결제 링크 |
| 재시도 중 | 자동 회수 가능성 유지 | 재시도 횟수, 다음 시도일, 제품 접근 상태 |
| CS handoff | 중요한 고객 보호 | ARR/MRR, seats, account owner, support 이력 |
| 접근 제한 전 | 업무 중단 리스크 확인 | 관리자 공지, 데이터 보존, grace period |
| 회수 실패 후 | 취소 또는 수동 검토 | CRM 상태, billing 상태, 보고서 반영 |
모든 고객에게 같은 dunning 흐름을 적용하면 안 됩니다. self-serve 소액 플랜, 팀 플랜, enterprise 계약, 영업 담당자가 붙은 계정은 알림 대상과 수동 검토 기준이 다를 수 있습니다.
고객 알림은 billing owner와 product admin을 구분합니다
B2B SaaS에서는 제품을 매일 쓰는 관리자와 결제수단을 가진 사람이 다를 수 있습니다. 결제 실패 알림이 퇴사자 이메일이나 개인 카드 owner에게만 가면 복구가 늦어집니다.
| 수신자 | 받아야 할 정보 |
|---|---|
| billing owner | 결제 실패, 결제수단 업데이트, invoice 링크 |
| workspace admin | 제품 접근 영향, 조치 마감일, 내부 전달 요청 |
| account owner | 중요한 고객의 수동 follow-up 필요 여부 |
| customer success | 좌석 수, 사용량, 계약 위험도 |
| finance contact | invoice, 세금계산서, 계약상 청구 흐름 |
| support team | 고객 문의가 들어왔을 때 현재 billing 상태 |
이메일에는 카드 정보나 민감 정보를 넣지 않아야 합니다. 고객이 확인해야 할 정보는 안전한 결제 페이지나 billing portal로 연결하고, 제품 내 알림은 "결제수단 확인 필요" 수준으로 제한하는 편이 낫습니다.
제품 접근 정책은 결제 실패 단계와 분리합니다
결제 실패가 발생했다고 즉시 제품 접근을 차단하면 고객 경험이 크게 나빠질 수 있습니다. 반대로 장기간 past_due 상태인데도 모든 기능을 열어 두면 회수와 운영 리스크가 커집니다.
| 접근 상태 | 적용 예시 |
|---|---|
| no_change | 실패 직후, 첫 재시도 전 |
| admin_banner_only | 관리자에게만 결제 확인 배너 표시 |
| write_limited | 새 프로젝트 생성, 내보내기, 고비용 기능 제한 |
| read_only | 데이터 조회는 가능하지만 신규 작업 제한 |
| suspended | 장기 미납 또는 계약 기준에 따른 접근 중단 |
| manual_review | enterprise, 큰 고객, 분쟁 가능성 있는 계정 |
제품 접근 정책은 공개 도움말과 약관, 계약 조건, 고객 커뮤니케이션 기준에 맞아야 합니다. 이 글에서는 접근 정책 자체를 추천하지 않고, billing 상태와 제품 접근 상태를 분리해서 기록해야 한다는 점만 다룹니다.
CRM lifecycle과 billing 상태를 따로 저장합니다
결제 실패가 곧바로 lost deal이나 churn은 아닙니다. 결제수단 만료, 인증 실패, 담당자 변경, 내부 구매 승인 지연일 수도 있습니다. CRM에는 결제 실패 자체와 다음 액션을 따로 보내야 합니다.
출처: HubSpot Knowledge Base – Use lifecycle stages
| CRM 필드 | 예시 값 |
|---|---|
| billing_status | active, past_due, unpaid, recovered, canceled |
| dunning_stage | failed_1, retrying, final_notice, manual_review |
| payment_failure_reason | card_declined, expired_card, authentication_required |
| next_retry_at | 다음 자동 재시도 시점 |
| customer_notice_status | sent, opened, clicked, bounced |
| account_owner_action | none, cs_followup, sales_followup, finance_review |
| recovery_status | unresolved, recovered, canceled, written_off_candidate |
| product_access_status | full, admin_banner, limited, suspended |
CRM에는 상세 결제 이벤트를 전부 밀어 넣기보다, 담당자가 다음 행동을 정할 수 있는 요약 필드만 보내는 편이 관리하기 쉽습니다. 원본 invoice와 webhook 이벤트는 billing 시스템이나 데이터 웨어하우스에 남겨야 합니다.
GA4에는 결제 실패 이벤트를 전환처럼 보내지 않습니다
결제 실패와 복구 흐름을 GA4에 보낼 수는 있지만, paid conversion과 같은 의미로 두면 안 됩니다. 광고 최적화나 주요 이벤트 설정에 섞이면 실제 유료 전환 수와 recovery 지표가 뒤엉킬 수 있습니다.
출처: Google Analytics Help – Events and key events
출처: Google for Developers – GA4 event reference
| GA4 이벤트 | 권장 역할 |
|---|---|
first_invoice_paid |
paid conversion 후보 |
invoice_payment_failed |
billing failure 분석 이벤트 |
payment_method_update_started |
복구 시도 micro event |
payment_method_updated |
결제수단 업데이트 완료 |
invoice_recovered |
결제 복구 완료 후보 |
subscription_past_due |
상태 변화 분석 |
subscription_canceled_after_failure |
실패 후 이탈 후보 |
결제 실패 이벤트는 광고 성과를 키우는 이벤트가 아니라 병목을 설명하는 이벤트입니다. 주요 이벤트로 올릴지 여부는 내부 분석 목적과 광고 계정 연결 구조를 확인한 뒤 제한적으로 정해야 합니다.
Measurement Protocol은 서버 기준 이벤트에만 씁니다
결제 실패, 재시도, 복구 성공은 서버에서 발생하는 경우가 많습니다. GA4 Measurement Protocol을 쓰면 서버 이벤트를 보낼 수 있지만, 중복 제거와 개인정보 제외 기준을 먼저 확인해야 합니다.
출처: Google for Developers – GA4 Measurement Protocol
| 확인 항목 | 감사 질문 |
|---|---|
| event_id | 같은 invoice 실패가 중복 전송되지 않는가 |
| user_id 또는 account_id | 개인 사용자보다 회사 계정 기준으로 묶을 수 있는가 |
| timestamp_micros | 실패 시점과 전송 시점을 구분하는가 |
| invoice_id | 내부 분석용 ID만 보내고 민감 정보는 제외하는가 |
| failure_stage | failed, retrying, recovered, canceled 상태를 구분하는가 |
| retry policy | Measurement Protocol 재전송이 중복 이벤트를 만들지 않는가 |
| consent 기준 | 회사의 데이터 처리 기준에 맞는가 |
서버 이벤트는 정확도를 높일 수 있지만, 이메일, 카드 정보, 청구 주소, 상세 결제 실패 메시지를 분석 도구로 보내면 안 됩니다. 필요한 최소 ID와 상태값만 보내는 구조가 좋습니다.
recovery 성공과 churn을 같은 보고서에서 분리합니다
dunning 자동화의 성과를 보려면 실패 수만 볼 것이 아니라 복구 성공과 취소를 따로 봐야 합니다. 특히 결제 실패 후 회수된 고객을 churn으로 계산하거나, 취소된 고객을 recovered로 남기면 보고서가 무너집니다.
| 지표 | 정의 예시 |
|---|---|
| failed_invoice_count | 지정 기간 결제 실패 invoice 수 |
| affected_account_count | 결제 실패가 발생한 고유 account 수 |
| recovery_rate | 실패 후 정해진 기간 안에 결제 성공한 비율 |
| median_recovery_time | 결제 실패에서 복구까지 걸린 중앙값 |
| failed_to_canceled_rate | 결제 실패 후 취소로 이어진 비율 |
| manual_save_count | CS 또는 영업 개입 후 복구된 계정 수 |
| notice_bounce_rate | 결제 실패 알림이 반송된 비율 |
| duplicate_failure_rate | 같은 invoice가 중복 집계된 비율 |
이 지표는 재무제표나 매출 인식 판단이 아니라 운영 상태를 보는 기준입니다. 숫자 이름에도 revenue 같은 단어를 남발하기보다 invoice_recovered_amount_candidate, billing_recovery_status처럼 의미를 좁히는 편이 안전합니다.
CS handoff 기준은 금액만 보지 않습니다
중요 고객을 놓치지 않으려면 결제 실패 알림을 단순 금액순으로만 보지 않아야 합니다. 작은 금액이어도 핵심 기능을 매일 쓰는 팀, 큰 팀의 일부 workspace, 곧 갱신을 앞둔 고객은 수동 확인이 필요할 수 있습니다.
| handoff 기준 | 예시 |
|---|---|
| MRR 또는 ARR | 일정 금액 이상이면 CS follow-up |
| seat count | 좌석 수가 많으면 업무 중단 영향 큼 |
| usage level | 최근 14일 활성 사용량 높음 |
| lifecycle | customer, expansion, renewal opportunity |
| account owner | 담당자 있는 고객인지 확인 |
| support history | 최근 billing 문의 또는 장애 문의 존재 |
| renewal date | 갱신 직전 결제 실패인지 확인 |
| failed attempts | 재시도 2회 이상 실패 |
자동화는 사람의 판단을 없애는 장치가 아니라, 사람이 봐야 할 계정을 놓치지 않게 정렬하는 장치에 가깝습니다.
실행 전 7일 비교표를 남깁니다
dunning 자동화를 바꾸기 전에는 기준 기간을 남겨야 합니다. 그래야 이메일 문구, 재시도 횟수, billing portal 링크, CS handoff 기준을 바꾼 뒤 성과가 나아졌는지 볼 수 있습니다.
| 항목 | 변경 전 | 변경 후 |
|---|---|---|
| 결제 실패 account 수 | 기록 | 기록 |
| 첫 실패 후 7일 내 recovery 비율 | 기록 | 기록 |
| 평균 recovery 시간 | 기록 | 기록 |
| 알림 이메일 반송률 | 기록 | 기록 |
| 결제수단 업데이트 클릭률 | 기록 | 기록 |
| CS handoff 계정 수 | 기록 | 기록 |
| 실패 후 취소 계정 수 | 기록 | 기록 |
| 중복 실패 이벤트 수 | 기록 | 기록 |
변경 후 지표가 좋아졌다고 바로 모든 원인을 dunning 자동화로 돌리면 안 됩니다. 가격 변경, 고객군 변화, 카드 만료 시즌, 제품 장애, 이메일 도달률 문제도 함께 봐야 합니다.
마지막 점검표
invoice.payment_failed와invoice.payment_succeeded가 같은 invoice 흐름으로 연결되는가- subscription의
past_due,unpaid,canceled,active상태를 제품 account와 CRM에 분리 저장하는가 - 결제 실패 알림 수신자가 billing owner, product admin, account owner로 나뉘어 있는가
- 고객 이메일과 제품 내 배너가 중복·상충하지 않는가
- 결제수단 업데이트 링크가 안전한 billing portal 또는 공식 결제 페이지로 연결되는가
- GA4에는 결제 실패를 paid conversion처럼 보내지 않는가
- 서버 이벤트를 보낼 때 개인정보와 카드 정보를 제외하는가
- recovery 성공, 취소, 수동 follow-up, 접근 제한을 각각 다른 상태로 기록하는가
- CS handoff 기준이 금액, 사용량, 좌석 수, lifecycle을 함께 보는가
- 변경 전후 7일 비교표가 남아 있는가
B2B SaaS 결제 실패 자동화의 목표는 고객을 압박하는 것이 아니라, 결제 문제를 빨리 발견하고 올바른 담당자에게 연결하며 보고서 숫자를 오염시키지 않는 것입니다. invoice.payment_failed를 하나의 이벤트로만 보지 말고, invoice, subscription, account, CRM, 고객 알림, 제품 접근 상태를 한 흐름으로 묶어야 합니다.