구독 취소 사유는 대개 환불 요청보다 먼저 도착하며, Google Play는 Apple보다 훨씬 많은 정보를 넘겨준다
Google Play는 취소 설문으로 구독 취소 사유를 기록하고, Apple은 만료 코드 하나만 보냅니다. 각 스토어가 무엇을 알려 주는지, 어떤 신호가 다가올 환불이나 지불 거절을 예고하는지, 각각 비용이 얼마나 드는지 정리했습니다.

핵심 요약
- Google Play는 구독자가 취소할 때 떠나는 이유를 묻고, 그 답은 purchases.subscriptionsv2의 canceledStateContext.userInitiatedCancellation.cancelSurveyResult를 통해 개발자에게 전달됩니다.
- Google Play 취소 설문에는 개발자가 읽을 수 있는 답이 다섯 가지 있습니다. 사용량 부족, 기술적 문제, 비용 관련, 더 나은 앱 발견, 기타입니다. reasonUserInput의 자유 입력 텍스트는 사용자가 기타를 선택했을 때만 채워집니다.
- Google Play의 SUBSCRIPTION_CANCELED 알림에는 구매 토큰만 들어 있습니다. 취소 사유는 purchases.subscriptionsv2.get으로 가져와야 하며, 사용자가 구독을 복원하면 Google은 모든 취소 관련 필드를 지웁니다.
- Apple의 App Store Server API에는 취소 설문이 없습니다. expirationIntent 필드는 다섯 가지 코드 중 하나를 제공합니다. 고객 취소, 결제 오류, 가격 인상 또는 혜택 전환에 미동의, 제품 판매 불가, 기타입니다.
- Apple의 DID_CHANGE_RENEWAL_STATUS 알림에 AUTO_RENEW_DISABLED 하위 유형이 붙어 있다면, 고객이 직접 자동 갱신을 끈 것일 수도 있지만 고객이 환불을 요청한 뒤 App Store가 자동 갱신을 끈 것일 수도 있습니다.
- 취소 사유가 있다고 해서 개발자가 환불 결정에 참여할 수 있는 것은 아닙니다. 증거를 요청하는 것은 12시간 안에 응답해야 하는 Apple의 CONSUMPTION_REQUEST와 24시간 안에 응답해야 하는 orders.reviewrefund를 통한 Google Play 지불 거절 검토뿐입니다.
- 기술적 문제를 이유로 취소한 Google Play 구독자가 환불을 원할 가능성이 가장 높습니다. 2026년 8월 3일 이후 주문의 경우, 이들이 지불 거절에서 이기면 개발자는 구매 가격에서 Play 서비스 수수료를 뺀 금액에 은행 수수료까지 부담합니다.
결국 환불을 받는 구독자 대부분은 먼저 구독을 취소합니다. 그 취소에는 사유가 따라오는데, 두 스토어가 넘겨주는 정보의 양은 크게 다릅니다. Google Play는 짧은 설문을 진행하고 자유 입력 텍스트를 포함한 답을 서버로 전달합니다. Apple은 숫자 코드 하나만 보내는데, 이 코드는 구독이 어떻게 끝났는지를 말할 뿐 고객이 왜 떠나고 싶었는지는 알려 주지 않습니다. 둘 다 제대로 읽으면, 아직 손쓸 수 있을 때 환불 요청이 다가오는 것을 미리 알 수 있습니다.
Google Play가 알려 주는 구독 취소 사유
사용자가 Play 구독 센터에서 취소하면 Google이 이유를 묻습니다. 답은 purchases.subscriptionsv2.get으로 받는 구독 리소스의 canceledStateContext 아래에 들어갑니다. 이 객체는 구독이 취소 또는 만료 상태일 때만 존재하며, 누가 구독을 끝냈는지 알려 줍니다. 사용자, Google 시스템, 개발자, 또는 요금제 변경 같은 대체 중 하나입니다.
사용자가 취소한 경우 userInitiatedCancellation 객체에 두 필드가 있습니다. cancelTime은 취소 시점이고, cancelSurveyResult에는 설문 답이 들어 있습니다.
| 설문 사유 | Google의 설명 | 흔히 의미하는 것 |
|---|---|---|
| CANCEL_SURVEY_REASON_NOT_ENOUGH_USAGE | 구독을 충분히 사용하지 않음 | 사용량이 적어 계정에 들어간 비용도 적음 |
| CANCEL_SURVEY_REASON_TECHNICAL_ISSUES | 앱 사용 중 기술적 문제 | 가장 강한 환불 및 지불 거절 신호 |
| CANCEL_SURVEY_REASON_COST_RELATED | 비용 관련 문제 | 가격에 민감함. 예상하지 못한 갱신인 경우도 있음 |
| CANCEL_SURVEY_REASON_FOUND_BETTER_APP | 사용자가 더 나은 앱을 찾음 | 경쟁사로 이동, 분쟁은 드묾 |
| CANCEL_SURVEY_REASON_OTHERS | 기타 사유 | 사용자가 직접 쓴 reasonUserInput 확인 |
| CANCEL_SURVEY_REASON_UNSPECIFIED | 지정되지 않은 취소 설문 사유 | 활용할 답이 없음 |
맨 오른쪽 열은 Google이 아니라 저희의 해석입니다. 앞의 두 열은 API 레퍼런스에서 그대로 가져왔습니다.
알림에는 사유가 담겨 있지 않다
구독이 취소되면 SUBSCRIPTION_CANCELED 실시간 개발자 알림(type 3)이 전송됩니다. Google은 이 알림이 구매 상태가 바뀌었다는 사실만 알려 준다고 분명히 밝힙니다. 페이로드에는 구매 토큰 외에 쓸 만한 정보가 없으므로, 설문 답을 읽으려면 핸들러에서 purchases.subscriptionsv2.get을 호출해야 합니다.
Play Console에서 보기
합계를 보는 데는 코드가 필요 없습니다. Play Console의 구독 보고서에는 이탈을 자발적, 비자발적, 기타로 나누는 취소 카드와, 답변을 선택한 사용자를 보여 주는 취소 설문 응답 카드가 있습니다. 기타를 선택한 사용자의 자유 입력 답변은 기타 응답 카드에서 CSV로 내려받을 수 있습니다. 다운로드에는 View financial data 권한이 필요하며, 주문을 읽는 도구에 부여하는 것과 같은 권한입니다.
Apple이 알려 주는 것과 빠뜨리는 것
Apple에는 개발자에게 결과를 전달하는 취소 설문이 없습니다. 가장 가까운 것은 서명된 갱신 정보의 expirationIntent인데, 이 필드는 고객의 생각이 아니라 구독이 어떻게 끝났는지를 설명합니다.
| expirationIntent | Apple의 정의 | 대응하는 EXPIRED 하위 유형 |
|---|---|---|
| 1 | 고객이 구독을 취소함 | VOLUNTARY |
| 2 | 잘못된 결제 정보 같은 결제 오류 | BILLING_RETRY |
| 3 | 동의가 필요한 가격 인상 또는 혜택 전환에 동의하지 않음 | PRICE_INCREASE |
| 4 | 갱신 시점에 제품을 구매할 수 없었음 | PRODUCT_NOT_FOR_SALE |
| 5 | 기타 사유 | 하위 유형 없음 |
고객의 선택을 나타내는 코드는 1뿐이며, 그마저도 동기에 대해서는 아무것도 말해 주지 않습니다. 앱이 계속 충돌해서 떠난 고객과 그저 프로젝트를 마친 고객이 똑같아 보입니다.
AUTO_RENEW_DISABLED에 숨은 신호
자세히 볼 만한 Apple 알림이 하나 있습니다. AUTO_RENEW_DISABLED 하위 유형의 DID_CHANGE_RENEWAL_STATUS는 보통 고객이 자동 갱신을 껐다는 뜻입니다. 그런데 Apple 문서에는 두 번째 경우도 나옵니다. 고객이 환불을 요청한 뒤에는 App Store도 자동 갱신을 끕니다.
따라서 iOS에서는 갱신 해제 이벤트가 환불 요청의 첫 흔적일 수 있습니다. 구매 항목이 소모성 항목이나 자동 갱신 구독이라면 이어서 CONSUMPTION_REQUEST가 올 수 있고, 그 순간부터 소비 데이터를 보낼 시간은 12시간입니다. 앱에서 눈에 띄는 동작 없이 갱신 해제 이벤트가 들어오면, 해당 계정의 사용 데이터를 준비하라는 신호로 받아들이는 것이 좋습니다.
Apple과 Google 나란히 비교
| Google Play | Apple App Store | |
|---|---|---|
| 사용자에게 이유를 묻는가 | 예, 취소 설문 | 개발자가 볼 수 있는 설문 없음 |
| 사유가 담기는 곳 | purchases.subscriptionsv2의 canceledStateContext | 갱신 정보의 expirationIntent |
| 자유 입력 텍스트 | reasonUserInput, 기타일 때만 | 없음 |
| 누가 취소했는가 | 사용자, 시스템, 개발자 또는 대체 | 코드와 하위 유형으로 추론 |
| 알림 | SUBSCRIPTION_CANCELED 후 get 호출 | DID_CHANGE_RENEWAL_STATUS, EXPIRED |
| 재구독 시 사라지는가 | 예, 복원 시 삭제 | 갱신 정보는 현재 상태를 반영 |
| 대시보드 | 취소 설문 응답 카드 | 사유 관련 화면 없음 |

취소가 환불로 바뀌는 지점
취소는 다음 결제를 멈출 뿐, 지난 결제를 돌려주지는 않습니다. 돈이 돌아가는지는 고객이 다음에 어떤 환불 흐름에 닿느냐에 달려 있고, 그 흐름 대부분은 개발자 없이 진행됩니다.
- Google Play에서는 구매자가 구매 후 48시간 이내에 스스로 환불을 받을 수 있으며, 결정은 Google이 합니다.
- Google 또는 Apple 고객 지원을 통한 환불은 스토어가 결정합니다.
- Apple의 CONSUMPTION_REQUEST는 소비 데이터를 요청하며, 응답 기한은 12시간입니다.
- Google Play의 지불 거절 검토는 orders.reviewrefund를 통해 증거를 요청하며, 응답 기한은 24시간입니다. 지불 거절에 대한 은행의 결정은 최종적입니다.
취소 사유는 이 규칙들을 하나도 바꾸지 않습니다. 바뀌는 것은 어떤 고객이 불만을 품고 있는지 얼마나 일찍 아느냐, 그리고 증거를 요청하는 두 흐름 중 하나가 도착했을 때 증거가 준비되어 있느냐입니다.
사유별 비용
설문 답은 해당 계정에 이미 들어간 서비스 비용을 가늠하는 대략적인 지표이기도 합니다. 환불이 일어나면 그 비용이 고스란히 개발자 몫으로 남습니다.
기술적 문제
가장 비싼 경우입니다. 문제를 겪을 만큼 제품을 썼다는 뜻이므로, 이미 세션을 위한 컴퓨팅, 요청이 일으킨 모델 API 호출, 생성한 결과물의 스토리지, 그리고 이용한 콘텐츠의 크리에이터에게 줄 정산금까지 지불한 상태입니다. 그 뒤에 환불을 받으면 대금은 돌아가고 비용은 그대로 남습니다. Google Play에서 은행에 이의를 제기하면, 2026년 8월 3일 이후 주문은 구매 가격에서 Play 서비스 수수료를 뺀 금액에 은행 수수료까지 부담하게 됩니다.
비용 관련
가격 불만은 예상하지 못한 갱신이 숨어 있지 않다면 보기보다 비용이 적습니다. 예상하지 못한 갱신은 전형적인 지불 거절 시나리오이므로, 갱신 결제 직후에 취소했는지, 갱신 안내가 제대로 발송됐는지 확인하세요.
사용량 부족
사용량이 적으면 서비스 비용도 적으므로, 이 경우의 환불은 대부분 큰 비용 없이 번 돈을 돌려주는 셈입니다. Apple이 소비 데이터를 요청한다면, 정직한 데이터가 고객이 무엇을 쓰고 무엇을 쓰지 않았는지 정확히 보여 주는 사유이기도 합니다.
더 나은 앱 발견
이런 고객은 분쟁을 거의 일으키지 않습니다. 경쟁사로 옮겨 갔을 뿐입니다. 이 사유는 제품 결정에는 유용하지만 환불 방어에는 거의 도움이 되지 않습니다.
취소 사유를 읽는 루틴
- Google Play의 SUBSCRIPTION_CANCELED를 받을 때마다 purchases.subscriptionsv2.get을 호출하고, canceledStateContext를 구매 토큰 및 cancelTime과 함께 저장합니다.
- 사유가 기타라면 reasonUserInput을 원문 그대로 저장합니다. 두 스토어를 통틀어 받을 수 있는 유일한 자유 입력 텍스트입니다.
- AUTO_RENEW_DISABLED가 붙은 Apple DID_CHANGE_RENEWAL_STATUS를 받을 때마다 이벤트를 기록하고, CONSUMPTION_REQUEST가 이어질 경우에 대비해 해당 계정의 사용 데이터를 가져옵니다.
- Apple EXPIRED 알림을 받을 때마다 하위 유형과 expirationIntent를 기록해 자발적 이탈과 결제 실패가 섞이지 않게 합니다.
- 기술적 문제를 이유로 한 Google Play 취소는 해당 계정의 오류 로그를 확인하고, 고객이 은행을 찾기 전에 먼저 연락합니다.
- 계정별 사용 로그를 보관해 CONSUMPTION_REQUEST에는 12시간 안에, 지불 거절 검토에는 24시간 안에 응답할 수 있게 합니다.
자주 묻는 질문
- Google Play 구독자가 취소한 이유는 어떻게 확인하나요?
- purchases.subscriptionsv2.get을 호출해 canceledStateContext.userInitiatedCancellation.cancelSurveyResult를 읽으면 됩니다. 사유 필드에는 설문 답이, 사용자가 기타를 선택한 경우 reasonUserInput에는 자유 입력 텍스트가 들어 있습니다. Play Console의 취소 설문 응답 카드에서도 합계를 볼 수 있습니다.
- Apple은 고객이 구독을 취소한 이유를 개발자에게 알려 주나요?
- 아니요. Apple에는 개발자가 볼 수 있는 취소 설문이 없습니다. expirationIntent 필드는 구독이 어떻게 끝났는지를 고객 취소, 결제 오류, 가격 인상 미동의, 제품 판매 불가, 기타 코드로 알려 줄 뿐, 고객의 동기는 알려 주지 않습니다.
- SUBSCRIPTION_CANCELED 알림에 취소 사유가 포함되나요?
- 아니요. Google Play 실시간 개발자 알림은 구매 상태가 바뀌었다는 사실만 알려 줍니다. 사유를 읽으려면 purchases.subscriptionsv2.get으로 구독을 가져오고 바로 저장하세요. 사용자가 구독을 복원하면 Google이 취소 관련 필드를 지우기 때문입니다.
- Apple AUTO_RENEW_DISABLED 알림이 환불 요청을 뜻할 수도 있나요?
- 네. Apple 문서에 따르면 AUTO_RENEW_DISABLED 하위 유형은 고객이 자동 갱신을 끈 경우와, 고객이 환불을 요청한 뒤 App Store가 자동 갱신을 끈 경우를 모두 포함합니다.
- 구독자가 취소하면 액세스를 해제해도 되나요?
- Google Play에서는 사용자가 아직 이용 자격이 있는 동안에는 안 됩니다. Google은 취소한 구독자가 결제 주기가 끝날 때까지 액세스를 유지하며, 액세스를 미리 해제하면 Google Play 구독 정책 위반이라고 밝힙니다. 액세스 종료 시점은 expiryTime으로 판단하세요.
출처 및 추가 자료
- Google Play Developer API: purchases.subscriptionsv2
- Google Play Billing: Subscription lifecycle
- Google Play Billing: Real-time developer notifications reference
- Play Console Help: Review in-app subscription performance
- Apple Developer: expirationIntent
- Apple Developer: notificationType
- Apple Developer: Send Consumption Information
- Play Console Help: Updates to refund protection and chargeback cost responsibility
RefundHalt
App Store와 Google Play 환불 자동 처리
계속 읽기
과도한 환불 요청은 환불액보다 더 큰 손실을 부릅니다. 두 앱 스토어 모두 이를 품질 신호로 보기 때문입니다
Apple 가이드라인 5.6.4는 과도한 환불 요청을 앱이 품질 기준에 미달한다는 징후로 꼽고, Google의 개발자 배포 계약은 환불률을 근거로 신규 개발자의 앱을 삭제할 수 있게 합니다. 각 스토어가 무엇이라고 규정하는지, 삭제되면 무엇을 잃는지, 위험을 어떻게 피하는지 정리했습니다.
환불을 처리하기 전에 App Store 서버 알림을 검증하세요. 누구나 웹훅 URL로 요청을 보낼 수 있습니다
환불 알림은 서명을 확인하기 전까지는 하나의 HTTPS 요청에 불과합니다. App Store 서버 알림과 Google Play RTDN 메시지를 검증하는 방법, 각 스토어가 무엇에 서명하는지, 그리고 위조된 환불이나 잘못 거부된 환불이 어떤 비용을 초래하는지 설명합니다.