누군가 '문제 신고'를 열고 여러분의 앱을 선택한 뒤 Apple에 환불을 요청합니다. Apple은 곧바로 결정하지 않습니다. 먼저 여러분의 서버에 해당 구매에 대해 아는 것이 있는지 묻는 신호를 보내고, 약 12시간 동안 답을 기다립니다.
그 신호가 바로 Apple CONSUMPTION_REQUEST입니다. 실제 IAP 매출이 있는 팀 중에도 이걸 들어본 적 없는 곳이 많습니다. 로그에서 한 번 보고 그대로 방치한 팀은 더 많습니다. 여러분도 그렇다면, 저희
Apple CONSUMPTION_REQUEST 해설 글에서 '무엇'과 '왜'를 다룹니다. 이 글은 응답하는 부분에 관한 것입니다. 무엇을 보내야 하고, 무엇을 보내면 안 되는지, 그리고 실제로 시도한 사람들에게 어떤 일이 있었는지.
핵심 요약
고객이 환불을 요청하면 Apple이 CONSUMPTION_REQUEST를 보냅니다. 주어진 시간은 약 12시간. 그 뒤에는 여러분 없이 결정합니다.
응답은 다섯 개 필드입니다. 50개가 아니라 다섯 개.
환불 선호도는 말 그대로 선호도일 뿐입니다. Apple은 이전에도 이를 뒤집었고 앞으로도 그럴 겁니다.
고객 동의가 없으면 응답도 없습니다. 저희가 아니라 Apple이 하는 말입니다.
한 앱은 응답을 시작한 것만으로 약 2주 만에 환불률이 3%에서 1.9%로 떨어졌습니다. Apple에 거절해 달라고 요청한 적은 한 번도 없었습니다.
이 일을 오래 수작업으로 하는 사람은 없습니다. 시간은 너무 짧고 요청은 불편한 시간에 옵니다.
그래서 Apple CONSUMPTION_REQUEST란 정확히 무엇인가
App Store Server Notifications V2를 통해 Apple이 보내는 여러 서버 알림 중 하나입니다. 누군가 인앱 구매에 대해 환불을 요청하면 발송됩니다. 예전에는 소모성 상품에만 해당됐지만, WWDC24 이후로는 자동 갱신 구독도 포함됩니다. 매출 대부분이 거기서 나오죠.
페이로드 안에는 서명된 트랜잭션, 상품 ID, 고객이 선택한 사유가 들어 있습니다. Apple의
consumption Request Reason 문서에는 다섯 가지가 나열되어 있습니다: UNINTENDED_PURCHASE, FULFILLMENT_ISSUE, UNSATISFIED_WITH_PURCHASE, LEGAL, OTHER.
이름이나 Apple ID는 없습니다. 트랜잭션 식별자뿐입니다. 이것을 "이 사람은 사용자 48213이고 구매 후 앱을 40번 열었다"로 바꾸는 건 여러분의 몫이며, 결제 시점에 appAccountToken을 붙여 두었을 때만 가능합니다. 이걸 건너뛰는 사람이 너무 많아서 저희는
appAccountToken에 관한 글 을 따로 썼습니다.
Apple이 묻고, 여러분이 답하고, Apple이 결정합니다. 개발자 입장에서 본 Apple 환불 요청의 구조가 이렇습니다.
Apple CONSUMPTION_REQUEST에 응답하는 방법
Apple의 Send Consumption Information 엔드포인트에 트랜잭션 ID를 경로에 넣고 작은 JSON 본문을 PUT합니다. 그 본문이 바로 환불 시스템이 읽는 Apple 소비 정보입니다.
Customer Consented가 첫 번째입니다. true 아니면 false. false라면 거기서 멈추세요. 아무것도 보내지 마세요. Apple 문서에는 동의가 없으면 아예 응답하지 말라고 되어 있습니다. 이상하지만 그게 규칙입니다.
그다음은 delivery status. 정상 전달됐다면 DELIVERED. 아니라면 품질 문제, 잘못된 상품, 서버 장애, 기타에 해당하는 UNDELIVERED 변형이 있습니다.
Sample Content Provided는 고객이 구매 전에 체험할 수 있었는지에 대한 예/아니오입니다. 무료 체험이 있었다면 yes. 콘텐츠 미리보기가 있었다면 yes. 불릿 세 줄짜리 페이월이라면 아마 no.
Consumption Percentage에서 많이들 실수합니다. 단위가 밀리유닛이라서 100000이 전부 소비, 50000이 절반입니다. 자동 갱신 구독에서는 빼세요. Apple이 결제 주기로부터 직접 계산합니다.
마지막으로 refund Preference. DECLINE, GRANT_FULL, GRANT_PRORATED 중 하나. 선택 사항입니다. 그리고 여러분이 원하는 바를 말할 수 있는 유일한 자리이기도 합니다.
이미 다 써버린 코인 팩에 대한 응답 예시:
Apple은 202로만 답합니다. 판정은 없습니다. 2021년 개발자 포럼에서 Apple 엔지니어가 밝힌 바로는, 202는 여러분의 데이터가 "고려될 것"이라는 뜻입니다. 약속은 그게 전부입니다.
모든 요청에 DECLINE을 보내지 마세요
유혹적이라는 건 압니다. 참으세요.
먼저 사유를 읽으세요. FULFILLMENT_ISSUE라면 전달 로그를 확인하세요. 구매가 정말 전달되지 않았다면 해당하는 UNDELIVERED 상태와 GRANT_FULL을 보내고 넘어가세요. 어차피 이길 수 없는 건이고, 억지로 버티면 나중에 보내는 DECLINE의 신뢰도만 떨어집니다.
UNINTENDED_PURCHASE는 사용량의 문제입니다. 9:02에 구매하고 9:05에 환불 요청, 그 사이 세션은 0? 아마 잘못 누른 겁니다. 보내주세요. 일주일 동안 매일 사용했다면? 실제 소비 수치를 첨부해 DECLINE.
UNSATISFIED_WITH_PURCHASE는 Sample Content Provided가 제 몫을 하는 곳입니다. 체험판을 써 보고 80%를 소비했다면? 거절. 거의 열어보지도 않았다면? GRANT_PRORATED가 공정한 중간 지점입니다.
LEGAL과 OTHER는 정확한 데이터를 보내되, 기록상 답이 명백한 경우가 아니라면 선호도는 생략하세요.
95% 소비와 무료 체험 이력 위에 얹은 DECLINE은 강력한 응답입니다. 90초 사용한 상품에 대한 DECLINE은 반사적으로 보입니다.
실제로 이렇게 한 사람들에게 일어난 일
Dipsea는 RevenueCat이 2024년 9월에 인수해 자사 환불 핸들러를 테스트하는 데 사용한 오디오 앱입니다. 10월 23일부터 선호도를 "Apple이 결정하도록"으로 설정한 채 소비 요청에 응답하기 시작했습니다. DECLINE 없이. 데이터만. 약 15일 만에 환불률이 3%로 고정되어 있던 상태에서 1.9%로 떨어졌습니다.
차트도 공개했습니다.제가 보기에 이 주제에서 가장 유용한 데이터 포인트입니다. 데이터만으로 숫자가 움직였습니다.
이번엔 반대 사례입니다. 2024년 3월, 한 게임 스튜디오가 Apple Developer Forums에 글을 올렸습니다. 소비 정보를 보내고 있는데도 Apple이 플레이어가 이미 써버린 코인에 대한 환불을 "거의 전부" 승인하고 있다는 내용이었습니다. 소모성 상품은 어려운 케이스입니다. 코인이 이미 사라졌다면 Apple이 회수할 방법이 없습니다. REFUND 알림 후 직접 잔액을 회수하지 않으면 돈도 코인도 잃습니다.
그리고 2025년 5월, r/iOSProgramming의 한 스레드에 "항상 거절 선호"로 설정해 두었다가 갑자기 모든 환불이 승인되는 걸 목격한 RevenueCat 사용자들이 몰렸습니다. RevenueCat은 Apple 쪽의 정책 변경이라고 설명했습니다. 원인이 무엇이든, 오래된 논쟁 하나는 정리됐습니다. Refund Preference는 스위치가 아닙니다. 일괄 DECLINE은 하나의 패턴이고, Apple은 그걸 걸러낼 수 있습니다.
400을 받는 방법들
Apple은 본문 형식에 엄격합니다. 저희가 반복해서 보는 사례들입니다.
밀리유닛 문제. 누군가 "percentage"라는 단어만 보고 100을 보내면, Apple에는 고객이 0.1%를 사용했다고 말한 셈이 됩니다. 범위는 0부터 100000까지입니다.
미전달 상품에 0이 아닌 퍼센티지. delivery Status가 DELIVERED가 아니면 Consumption Percentage는 0이어야 합니다. 아니면 요청이 반려됩니다.
자동 갱신 구독에 퍼센티지를 넣는 것. Apple에는 이를 위한 전용 에러가 있습니다. 빼세요.
잘못된 키. 이 호출에는 App Store Connect의 Users and Access, 그다음 Integrations에서 생성하는 In-App Purchase 키가 필요합니다. 겉모습은 똑같지만 App Store Connect API 키가 아닙니다. 잘못 쓰면 401이 돌아오고, 한 시간 동안 JWT를 의심하게 됩니다.
동의 생략. HTTP 에러가 아니라 컴플라이언스 에러입니다. 라이브로 가기 전에 약관에 해당 문구를 넣으세요.
먼저 샌드박스에서 테스트하세요. 한 가지 함정: Apple의 테스트 문서에 따르면 샌드박스에서는 12시간이 아니라 5분이 주어집니다. 서버가 6분 걸리면 테스트는 데이터를 무시합니다. 강제로 거절을 내려면 환불 시트에서 Other를 고르고 DECLINE이라고 입력하세요.
요청이 많을 때 개발자들은 App Store 환불 요청을 어떻게 처리하나
솔직히 말하면, 대부분 처리하지 않습니다. 요청은 새벽 3시에 옵니다. 아니면 토요일. 아니면 파이프라인을 이해하는 유일한 사람이 오프라인인 휴일. 12시간이 지나고, Apple은 고객의 말만 듣고 판정합니다.
개발자로서 Apple 환불 요청을 처리하는 방법은 결국 하나의 결정으로 귀결됩니다. 체인을 직접 구축하고(JWS 검증, 사용자 조회, 사용량 조회, 퍼센티지 계산, JWT 발급, 엔드포인트 호출, 로깅, 재시도) 영원히 운영하거나, 아니면 이미 그 일을 해주는 Apple 환불 관리 소프트웨어를 연결하거나.
Refund Sensor는 두 번째 그룹에 속하는 선택지 중 하나입니다. App Store Connect에 저희 알림 URL을 붙여 넣고 키를 연결하면, 응답이 몇 초 안에 나갑니다. 현재 연결된 앱 전체 기준으로 대상 요청의 77%가 방어됐고, 건당 약 $33가 보호됐습니다. 특별한 기술이 있는 건 아닙니다. 실제 데이터를 담은 응답이 매번, 마감 전에 나갈 뿐입니다. 다른 옵션도 비교해 보고 싶다면 Apple 환불 관리 도구
가이드에서 무엇을 물어봐야 하는지 다룹니다.
개발자를 위한 Apple 환불 자동화가 계속 화두가 되는 이유가 이것입니다. 12시간, 모든 요청, 매주. 사람이 할 일이 아닙니다. 무엇을 고르든 하나는 고르세요. 침묵은 Apple이 한쪽 말만 듣게 된다는 뜻입니다.
자주 묻는 질문
프로덕션에서는 12시간, 샌드박스에서는 5분입니다. 둘 다 Apple 문서에 명시되어 있습니다.
아닙니다. Apple은 개발자가 보낸 소비 정보를 "여러 요소 중 하나"라고 설명합니다. 확률을 높여줄 뿐, 결정을 내리는 것은 아닙니다.
고객이 동의한 요청이라면 모두 응답해야 합니다. 서버 장애 때문에 GRANT_FULL을 보내는 경우까지도요. 쉬운 케이스에서 정직하게 답해 두었기 때문에 나중에 보내는 DECLINE을 Apple이 신뢰하는 것입니다.
deliveryStatus와 sampleContentProvided를 정확하게 보내세요. 퍼센티지는 생략하세요. 그런 다음 트래킹부터 고치세요.
네. DECLINE과 GRANTFULL은 모든 상품 유형에서 동작합니다. GRANTPRORATED도 가능하며, 자동 갱신 구독의 경우 비례 배분 계산은 Apple이 직접 처리합니다.





