환불 알림을 놓치면 이의를 제기할 수 있는 유일한 환불을 잃을 수 있지만, 제때 확인하면 두 스토어 모두 되찾을 수 있습니다
서버가 환불 알림을 놓치면, 증거를 요청하는 두 가지 환불 절차가 여러분 없이 마감됩니다. Apple과 Google Play가 어떻게 재시도하는지, 놓친 환불 알림을 얼마나 과거까지 복구할 수 있는지, 그리고 복구의 빈틈은 어디인지 정리했습니다.

핵심 요약
- App Store Server Notifications V2 전송에 실패하면 Apple은 이전 시도로부터 1, 12, 24, 48, 72시간 후에 총 5번 재시도합니다. 따라서 두 번째 재시도는 첫 시도 후 약 13시간이 지나서야 도착합니다.
- Apple은 CONSUMPTION_REQUEST를 받은 후 12시간 이내에 응답하라고 요구합니다. 첫 전송과 1시간 후의 재시도를 모두 놓친 서버는, 요청을 보게 될 때쯤이면 이미 응답 기한이 지났을 가능성이 큽니다.
- Apple의 Get Notification History 엔드포인트는 프로덕션 환경에서 지난 180일 동안의 V2 알림을 페이지당 20건씩 반환합니다. onlyFailures 플래그를 쓰면 서버에 도달하지 못한 알림과 아직 재시도 중인 알림만 조회할 수 있습니다.
- Google Play는 지불 거절 검토를 PendingRefundReviewNotification으로만 전달하며, 24시간 이내에 orders.reviewrefund를 호출하는 데 필요한 pendingRefundToken도 이 알림에 들어 있습니다. 대기 중인 검토를 조회하는 별도의 API는 Google 문서에 없습니다.
- Google Cloud Pub/Sub는 확인되지 않은 메시지를 기본 7일, 최대 31일 동안 보관합니다. 만료 기간을 바꾸지 않으면 31일 동안 구독자 활동이 없는 구독은 삭제됩니다.
- Google Play Voided Purchases API로 지난 30일 동안의 환불, 취소, 지불 거절을 복구할 수 있습니다. 할당량은 하루 6,000건, 30초당 30건입니다. 하지만 한 번도 응답하지 않은 지불 거절 검토는 복구할 수 없습니다.
- Apple 샌드박스는 알림마다 한 번만 보내고 재시도하지 않습니다. 따라서 샌드박스에서만 돌려 본 복구 루틴은 Apple의 실제 재시도 일정으로 검증된 적이 없는 셈입니다.
환불 알림을 놓치는 것은 개발자가 의견을 낼 수 있는 단 두 가지 환불 결정을 조용히 잃는 방식입니다. Apple의 CONSUMPTION_REQUEST와 Google Play의 지불 거절 검토는 모두 알림으로 시작하고, 둘 다 짧은 기한 안에 마감됩니다. Apple은 12시간, Google은 24시간입니다. 메시지가 도착했을 때 서버가 다운되었거나 느리거나 설정이 잘못되어 있어도 스토어는 기다려 주지 않습니다. 이미 가진 정보만으로 결정합니다.
좋은 소식은 두 스토어 모두 첫 실패에서 메시지를 버리지 않는다는 점입니다. Apple은 재시도하고, 조회할 수 있는 180일치 기록을 남깁니다. Google은 Pub/Sub를 통해 게시하며, 확인되지 않은 메시지를 며칠 동안 보관합니다. 나쁜 소식은 재시도 시점이 응답 기한과 맞지 않고, 두 절차 중 하나에는 복구용 API가 아예 없다는 점입니다.
각 스토어가 환불 알림을 전달하는 방식
Apple은 App Store Server Notifications를 App Store Connect에서 설정한 HTTPS URL로 직접 보냅니다. Google은 서버를 직접 호출하지 않습니다. 실시간 개발자 알림을 Google Cloud Pub/Sub 주제에 게시하고, 그 주제의 구독이 알림을 엔드포인트로 푸시하거나 서버가 가져가기를 기다립니다.
| 질문 | App Store | Google Play |
|---|---|---|
| 전송 경로 | 지정한 URL로 HTTPS POST | Cloud Pub/Sub 주제, 푸시 또는 풀 |
| 성공 기준 | HTTP 200~206 | Pub/Sub 메시지 확인, 또는 푸시 엔드포인트의 성공 코드 |
| 재시도 | V2는 5회, 이전 시도로부터 1, 12, 24, 48, 72시간 후 | 확인되거나 보관 기간이 끝날 때까지 Pub/Sub가 재전송 |
| 놓친 메시지의 보관 기간 | Get Notification History에 180일 | 구독에 기본 7일, 최대 31일 |
| 증거를 요청하는 환불 | CONSUMPTION_REQUEST, 12시간 이내 응답 | PendingRefundReviewNotification, 24시간 이내 응답 |
| 확정된 환불의 풀 방식 백업 | Get Notification History, Get Refund History | Voided Purchases API, 지난 30일 |
| 샌드박스 또는 테스트 동작 | 한 번만 시도, 재시도 없음 | Play Console의 Send Test Message |
Apple이 실패로 보는 경우
Apple 문서는 구체적입니다. 200에서 206 사이의 상태 코드를 반환하면 전송이 완료됩니다. 40x나 50x를 반환하면 Apple이 재시도합니다. 그 밖의 모든 코드도 실패로 간주합니다. 시도마다 sendAttemptResult가 기록되는데, 그 값은 실제 장애 체크리스트처럼 보입니다. TIMED_OUT, TLS_ISSUE, CIRCULAR_REDIRECT, NO_RESPONSE, PREMATURE_CLOSE, UNSUCCESSFUL_HTTP_RESPONSE_CODE 등이 있습니다. TIMED_OUT에 대해 Apple은 서버가 요청 안에서 메시지를 동기적으로 처리하고 있지 않은지 확인하라고 권합니다.
Google이 실패로 보는 경우
푸시 구독에서는 엔드포인트가 성공 응답 코드를 반환하는 것이 곧 확인입니다. 풀 구독에서는 서버가 가져온 메시지를 하나하나 확인해야 하며, 그렇지 않으면 Pub/Sub가 다시 전달합니다. 확인 기한은 기본 10초, 최대 600초입니다. Google의 RTDN 가이드에는 다시 강조할 만한 규칙이 하나 더 있습니다. 알림은 무언가가 바뀌었다는 사실만 알려 주므로, 전체 구매 상태는 여전히 Google Play Developer API를 호출해 가져와야 합니다.
Apple의 재시도 일정이 12시간 기한과 맞지 않는 이유
Apple의 재시도 간격은 첫 시도가 아니라 이전 시도를 기준으로 계산됩니다. 이를 합산하면, 서버가 계속 실패하는 V2 알림 하나는 다음과 같은 일정을 따릅니다.
| 시도 | 이전 시도 후 대기 시간 | 첫 시도 후 경과 시간 |
|---|---|---|
| 첫 전송 | 없음 | 0 |
| 재시도 1 | 1시간 | 1 |
| 재시도 2 | 12시간 | 13 |
| 재시도 3 | 24시간 | 37 |
| 재시도 4 | 48시간 | 85 |
| 재시도 5 | 72시간 | 157 |
Apple은 CONSUMPTION_REQUEST를 받은 후 12시간 이내에 응답하라고 합니다. 첫 전송과 1시간 후의 재시도 때 서버가 모두 다운되어 있었다면, 다음 시도는 첫 전송 후 13시간이 지나서야 도착합니다. 재시도로 처음 받은 요청에 새로 12시간이 주어지는지는 Apple이 밝히지 않았습니다. 신중하게 해석하면 첫 시도를 기한의 시작점으로 봐야 하며, 이는 한 시간 넘게 놓친 요청은 재시도만으로는 살릴 수 없다는 뜻입니다.
HTTP 400을 반환한 뒤에도 Apple이 재시도하나요?
문서에 따르면 그렇습니다. Apple은 재시도를 유발하는 코드로 40x와 50x를 명시합니다. 2026년 3월, 한 개발자가 Apple 포럼에서 400 응답 후 재시도가 멈춘 것 같다고 보고했다가, 하루 이틀 뒤 전송이 재개되었다고 덧붙였고, Apple은 Feedback Assistant로 보고해 달라고 요청했습니다. 문서에 나온 동작을 규칙으로 삼고, 여러분의 모니터링을 그 증거로 삼으세요.
Get Notification History로 놓친 App Store 알림 복구하기
Get Notification History는 지정한 기간 동안 Apple이 서버로 보내려고 시도한 버전 2 알림을 반환합니다. 각 레코드에는 서명된 페이로드 전체가 들어 있으므로, 실시간 알림과 같은 핸들러에 그대로 넘길 수 있습니다. 각 레코드에는 최대 6개 항목의 sendAttempts 배열도 있으며, 첫 시도와 다섯 번의 재시도에 하나씩 대응합니다.
- startDate와 endDate는 필수이며, startDate는 프로덕션에서는 지난 180일 이내, 샌드박스에서는 지난 30일 이내여야 합니다.
- onlyFailures를 true로 설정하면 서버에 도달하지 못한 알림과 Apple이 아직 재시도 중인 알림만 받습니다.
- CONSUMPTION_REQUEST나 REFUND 같은 notificationType 하나로 필터링하거나, 고객 한 명의 transactionId로 필터링할 수 있지만, 한 요청에서 둘을 함께 쓸 수는 없습니다.
- 호출당 최대 20건을 반환합니다. hasMore가 true인 동안 paginationToken으로 계속 호출하고, 매번 같은 요청 본문을 보내세요.
환불에 관해서는 주의할 점이 하나 있습니다. Apple에 따르면 기록의 레코드는 현재가 아니라 알림이 처음 전송된 시점의 구매 상태를 반영합니다. 따라서 복구한 REFUND는 과거의 사실입니다. 접근 권한을 회수하기 전에 거래 또는 구독 상태 엔드포인트로 현재 상태를 확인하세요.
V1 알림은 이 방법으로 복구할 수 없습니다
Get Notification History는 버전 2만 다룹니다. Apple은 V1 알림을 이전 시도로부터 6, 24, 48시간 후에 세 번 재시도하며, 모두 실패하면 다시 재생할 기록이 남지 않습니다. 아직 V1을 쓰는 앱을 V2로 옮겨야 할 이유가 하나 더 생기는 셈입니다.
놓친 Google Play 환불 알림 복구하기
Google의 안전망은 Pub/Sub 자체입니다. 구독이 존재하는 한, 아무도 확인하지 않은 메시지는 보관 기간 동안 그대로 남아 있습니다. 기본 7일이며, 늘리면 최대 31일입니다. 엔드포인트가 복구되면 전송도 다시 시작됩니다. 다만 두 가지 설정이 이 보호 장치를 조용히 무력화할 수 있습니다.
- 데드 레터 주제는 정해진 횟수만큼 전송에 실패한 메시지를 구독 밖으로 옮깁니다. 기본값은 5회이며, 즉시 재전송하는 기본 재시도 정책에서는 다섯 번의 시도가 금방 지나갈 수 있습니다. 데드 레터 주제를 쓴다면 두 번째 받은편지함처럼 지켜보세요.
- 열린 연결, 진행 중인 풀, 성공한 푸시 같은 구독자 활동이 없는 구독은 기본적으로 31일 후 만료됩니다. 한 달 동안 고장 난 채로 있는 엔드포인트는 구독 자체와 그 안에서 대기 중이던 모든 메시지를 잃을 수 있습니다.
무효화된 환불에는 30일 백업이 있습니다
이미 발생한 환불에는 Voided Purchases API가 풀 방식의 백업입니다. 지난 30일 동안 무효화된 구매를 페이지당 최대 1,000건씩 조회할 수 있으며, 할당량은 앱마다 하루 6,000건, 30초당 30건입니다. type 1을 전달하면 일회성 상품과 함께 구독도 포함됩니다. 매일 실행하면 알림 파이프라인이 놓친 환불을 잡아낼 수 있어, 이용 권한과 매출 수치를 정확하게 유지할 수 있습니다.
지불 거절 검토에는 백업이 없습니다
PendingRefundReviewNotification은 다릅니다. 이 알림에는 orders.reviewrefund에 전달해야 하는 pendingRefundToken이 들어 있으며, 알림을 받은 시점부터 24시간 안에 환불 선호 의견과 사용 증거를 보내야 합니다. Google은 첫 번째 호출만 기록하고 나머지는 무시합니다. 대기 중인 검토를 조회하는 엔드포인트는 Google 문서에 나와 있지 않으므로, 해당 Pub/Sub 메시지를 잃으면 그 지불 거절에 대한 증거도 함께 사라집니다.
환불 알림을 놓쳤을 때 드는 비용
이미 확정된 환불의 알림을 놓쳤다면, 그 비용은 장부 정리입니다. 나중에 Voided Purchases API나 Get Notification History에서 찾아내고, 정책에 따라 접근 권한을 회수하면 보고서도 따라잡습니다. 번거롭지만 비용은 적습니다.
이의를 제기할 수 있는 두 절차의 알림을 놓치면 실제로 돈을 잃습니다. 스토어가 여러분의 증거 없이 결정하기 때문입니다. Apple에서는 여러분의 소비 데이터 없이 환불 결정이 내려진다는 뜻입니다. Google Play에서는 2026년 8월 3일 이후 접수된 주문의 경우, 지불 거절에서 지면 구매 가격에서 Play 서비스 수수료를 뺀 금액에 구매자 은행이 부과하는 지불 거절 수수료까지 부담해야 합니다. Google은 자사 서비스 수수료만 부담합니다.
게다가 구매한 것을 제공하는 데 쓴 돈은 분쟁이 시작되기 전에 이미 나갔습니다.
- 컴퓨팅: 구매자가 이미 사용한 생성, 렌더링, 모델 응답 하나하나에 들어간 GPU와 서버 시간.
- API 호출: 호출하는 순간 과금되는 외부 서비스 요청. 대개 호출마다 과금됩니다.
- 스토리지: 구매자가 만든 파일과 미디어. 여전히 호스팅 요금에 포함되어 있습니다.
- 지급금: 해당 판매에 대해 크리에이터, 제휴사, 파트너에게 이미 지급한 수익 배분.
환불이 처리되면 이 중 어느 것도 돌아오지 않습니다. 그 12시간 또는 24시간 안에 보내는 증거가 여러분이 쓸 수 있는 유일한 수단이며, 알림을 놓치면 그 수단이 사라집니다.
믿을 수 있는 복구 루틴
- 먼저 확인하세요. 원본 페이로드를 저장하고, 성공 코드를 반환한 다음, 큐에서 처리합니다.
- 몇 분마다 onlyFailures를 켜고 Apple의 Get Notification History에서 CONSUMPTION_REQUEST를 폴링해, 장애가 나도 12시간 기한 중 몇 분만 잃도록 합니다.
- 매일 onlyFailures를 켜고 지난 며칠 동안의 모든 유형에 대해 Get Notification History를 훑어보고, 찾은 알림을 재생합니다.
- Google Play 앱마다 매일 type 1로 Voided Purchases API를 호출하고, 이미 기록한 환불과 대조합니다.
- Pub/Sub에서는 메시지 보관 기간을 7일 이상으로 유지하고, 데드 레터 주제가 있다면 알림을 설정하고, 전송되지 않은 메시지가 쌓일 때도 알림을 받도록 하세요.
- 프로덕션에서도 전체 경로를 테스트하세요. Apple 샌드박스는 알림마다 한 번만 보내고 재시도하지 않지만, Request a Test Notification과 Get Test Notification Status를 함께 쓰면 서버가 어떻게 응답했는지 정확히 확인할 수 있습니다.

RefundHalt가 두 스토어의 알림을 대신 받아 CONSUMPTION_REQUEST와 지불 거절 검토에 기한 안에 자동으로 응답하므로, 여러분이 시계를 지켜볼 필요가 없습니다.
자주 묻는 질문
- 실패한 App Store 서버 알림을 Apple은 얼마나 오래 재시도하나요?
- 버전 2는 약 6일 반입니다. Apple은 이전 시도로부터 1, 12, 24, 48, 72시간 후에 다섯 번 재시도하므로, 마지막 재시도는 첫 전송 후 157시간에 이뤄집니다. 버전 1 알림은 이전 시도로부터 6, 24, 48시간 후에 세 번 재시도됩니다.
- 놓친 App Store 서버 알림은 얼마나 과거까지 복구할 수 있나요?
- 프로덕션에서는 180일입니다. Apple의 Get Notification History 엔드포인트는 지난 180일, 샌드박스에서는 지난 30일 동안의 버전 2 알림을 호출당 20건씩 반환하며, onlyFailures 플래그로 서버에 도달하지 못한 알림만 조회할 수 있습니다.
- 서버가 HTTP 400을 반환하면 Apple이 알림을 재시도하나요?
- Apple 문서에 따르면 그렇습니다. 서버가 40x 또는 50x 상태를 반환하면 Apple이 재시도하며, 200에서 206까지를 성공으로 봅니다. 그 밖의 상태도 모두 실패로 간주됩니다.
- 놓친 Google Play 지불 거절 검토를 복구할 수 있나요?
- Pub/Sub 메시지가 아직 남아 있는 동안에만 가능합니다. orders.reviewrefund에 필요한 pendingRefundToken은 PendingRefundReviewNotification으로 전달되고, 대기 중인 검토를 조회하는 API는 Google 문서에 없습니다. 따라서 기본 7일 동안 보관되는 구독의 미확인 메시지가 유일한 사본입니다.
- 서버가 놓친 Google Play 환불은 어떻게 찾나요?
- Voided Purchases API를 호출하세요. 환불, 취소, 지불 거절을 포함해 지난 30일 동안 무효화된 구매를 페이지당 최대 1,000건씩 반환하며, 할당량은 앱마다 하루 6,000건입니다. type 1을 전달하면 구독도 포함됩니다.
출처 및 추가 자료
- Apple Developer: Responding to App Store Server Notifications
- Apple Developer: Get Notification History
- Apple Developer: NotificationHistoryRequest
- Apple Developer: Send Consumption Information
- Android Developers: Real-time developer notifications reference
- Android Developers: Help Google dispute chargebacks
- Google Play Developer API: Voided Purchases API
- Google Cloud: Pub/Sub subscription properties
- 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 메시지를 검증하는 방법, 각 스토어가 무엇에 서명하는지, 그리고 위조된 환불이나 잘못 거부된 환불이 어떤 비용을 초래하는지 설명합니다.