모든 글
Deep dive7분 소요

당신의 앱은 인앱 환불 요청 시트를 띄울 수 있습니다. 고객이 제출을 누른 뒤 Apple이 무엇을 하는지 알려드립니다

Apple의 인앱 환불 요청은 고객이 앱을 떠나지 않고, Apple이 만들고 심사하는 시트에서 환불을 신청하게 해줍니다. beginRefundRequest가 무엇을 반환하는지, 서버에서 시작되는 CONSUMPTION_REQUEST와 48시간 타이머, 그리고 이 버튼을 출시할 가치가 있는지 정리합니다.

계정 설정 화면을 띄운 스마트폰을 든 손과 그 옆의 종이 영수증과 동전으로, 고객이 앱을 떠나지 않고 시작할 수 있는 인앱 환불 요청을 표현한 그림

핵심 요약

  • Apple의 beginRefundRequest는 앱 안에서 Apple 자체 환불 시트를 띄우는 StoreKit 2 메서드입니다. 고객은 자신의 구매 내역과 사유 코드 목록을 보고 하나를 고르며, 요청은 Apple로 전송됩니다. 양식을 만드는 것도 결과를 정하는 것도 당신이 아닙니다.
  • 이 호출은 success 또는 userCancelled 상태를 반환하거나, duplicateRequest 또는 failed를 던집니다. success 상태는 App Store가 요청을 받았다는 뜻일 뿐, 승인했다는 뜻이 아닙니다. success일 때 화면에 환불 확정을 절대 표시하지 마세요.
  • 고객이 제출한 뒤 Apple은 승인 또는 거절까지 최대 48시간이 걸립니다. 소모성 구매의 경우 먼저 서버로 CONSUMPTION_REQUEST를 보내며, 고객이 동의했다면 평소대로 12시간 안에 사용 데이터로 응답할 수 있습니다.
  • 결과는 App Store Server Notification으로 서버에 도착합니다. 이미 받고 있는 바로 그 피드입니다. 승인은 REFUND 알림, 거절은 REFUND_DECLINED입니다. 인앱 요청은 Apple의 reportaproblem 페이지에서 시작된 환불과 똑같이 이 피드로 흘러듭니다.
  • 이 버튼은 iOS 15와 iPadOS 15, Mac Catalyst 15, visionOS 1부터 사용할 수 있으므로, 그 버전을 대상으로 하는 앱이라면 오늘 바로 띄울 수 있습니다.
  • 재무적 논점은, 당신이 다툴 수 있는 환불이 다툴 수 없는 차지백보다 낫다는 것입니다. 고객을 Apple의 흐름 안에 붙잡아 두면 당신이 응답할 수 있는 CONSUMPTION_REQUEST가 촉발되며, 최종적이고 수수료까지 붙는 은행 차지백으로 가지 않습니다.
  • Apple의 배치 지침은 구매 화면이 아니라 계정 설정이나 도움말 메뉴에서 이를 호출하라는 것입니다. 그래야 불만인 고객이 그것을 찾되, 나머지 모두에게 환불을 광고하지 않게 됩니다.

Apple은 고객이 앱을 전혀 떠나지 않고도 환불을 신청할 수 있게 해줍니다. StoreKit 호출 하나, beginRefundRequest가 당신의 인터페이스 안에 바로 Apple 자체 환불 시트를 띄우고, 고객이 사유를 고르면 요청은 심사를 위해 Apple로 전송됩니다. 당신은 양식을 만들지도, 돈을 만지지도, 결과를 정하지도 않습니다. 당신이 얻는 것은, 좌절한 고객이 이미 있는 자리에 환불 경로를 두는 방법이며, 그들을 은행에 빼앗기지 않아도 됩니다. 이것이 인앱 환불 요청이며, 이 버튼을 출시할지 결정하기 전에 무엇인지 이해해 둘 가치가 있습니다.

여기부터가 매출과 직결되는 부분입니다. 이 버튼은 그 자체로는 아무것도 환불하지 않습니다. 요청을 열 뿐이고, Apple은 승인 또는 거절까지 최대 48시간을 쓰며, 소모성 구매의 경우 먼저 서버로 CONSUMPTION_REQUEST를 쏘아 보냅니다. 그러니 이 시트는 공짜 증정이 아닙니다. 그것은 당신이 이미 영향을 줄 수 있는 바로 그 환불 심사로 이어지는 깔때기이며, 분쟁이 당신이 다툴 수 없는 차지백으로 변하기 전에 그것을 카드 네트워크에서 끌어올 수 있습니다.

인앱 환불 요청 시트란 실제로 무엇인가

beginRefundRequest는 window scene 안에서 특정 트랜잭션에 대한 환불 요청 시트를 띄우는 StoreKit 2 메서드입니다. 시그니처는 짧습니다. func beginRefundRequest(in scene: UIWindowScene) async throws -> Transaction.RefundRequestStatus. 호출하면 시스템은 고객의 구매 내역과 고를 수 있는 사유 코드 목록이 담긴 시트를 보여줍니다. 그 UI를 만들고 통제하는 것은 Apple입니다. 당신이 넘기는 것은 scene과 트랜잭션뿐, 그 외에는 없습니다.

배치 위치에 관한 Apple의 지침은 명확합니다. 이 함수는 계정 설정이나 도움말 메뉴에서 호출하세요. 그래야 환불을 원하는 고객이 지원을 찾는 바로 그 자리에서 그것을 발견합니다. iOS 15와 iPadOS 15, Mac Catalyst 15, visionOS 1에서 출시되었으므로, 그 버전을 대상으로 하는 앱이라면 오늘 바로 띄울 수 있습니다.

시트를 여는 두 가지 방법

진입점은 둘입니다. 이미 보유한 특정 트랜잭션에 대해 beginRefundRequest(in:)를 호출하면 시트를 그 한 건의 구매로 한정할 수 있습니다. 특정 제품의 구매를 고객이 환불하도록 하고 싶을 때는 제품 식별자로 시트를 열 수도 있습니다. 어느 쪽이든 시트, 사유 목록, 그리고 판단은 Apple의 몫입니다. 당신의 일은 그것을 띄우고 결과를 읽는 데서 끝납니다.

호출이 무엇을 반환하고, 무엇이 잘못될 수 있는가

이 메서드는 async throws이므로, 상태를 반환하거나 오류를 던집니다. 둘 다 짧은 목록이고, 둘 다 처리할 가치가 있습니다. 그래야 시트가 닫힌 뒤 당신의 화면이 사실을 말할 수 있습니다.

결과타입의미
successRefundRequestStatusApp Store가 환불 요청을 받음. 제출됨이지 승인됨이 아님
userCancelledRefundRequestStatus고객이 제출하지 않고 시트를 닫음. 아무것도 전송되지 않음
duplicateRequestRefundRequestErrorApp Store에 이 구매에 대한 환불 요청이 이미 있음
failedRefundRequestError제출 자체가 실패함. 고객에게 다시 시도하게 함

고객이 제출을 누른 뒤 서버에서 무슨 일이 벌어지는가

시트가 닫히는 것은 과정의 시작이지 끝이 아닙니다. Apple은 요청을 심사하며 승인 또는 거절까지 최대 48시간이 걸립니다. 소모성 인앱 구매의 경우, 판단하기 전에 App Store가 사용 데이터를 요청하는 CONSUMPTION_REQUEST 알림을 서버로 보냅니다. 고객이 그 데이터 공유에 동의했다면 Send Consumption Information 엔드포인트를 통해 응답합니다. 동의하지 않았다면, Apple 자신의 지시는 그 알림에 아예 응답하지 말라는 것입니다.

Apple이 판정을 내리면 결과는 App Store Server Notification으로 서버에 도착합니다. 이미 받고 있는 바로 그 피드이며, 인앱 요청은 고객이 Apple의 reportaproblem 페이지에서 시작하는 환불과 똑같이 그리로 흘러듭니다. 요청이 당신의 앱 안에서 시작되었다고 해서 처리 방식이 달라지는 것은 전혀 없습니다.

단계무엇이 발생하는가당신의 행동타이머
고객이 시트를 제출beginRefundRequest가 success 반환기록하고, 환불됨이 아니라 대기 중으로 표시즉시
소모성만, Apple이 먼저 문의CONSUMPTION_REQUEST 알림고객이 동의했으면 소비 데이터를 보내고, 아니면 침묵을 지킴12시간 안에 응답
Apple이 승인REFUND 알림해당 트랜잭션의 권리를 취소판단까지 최대 48시간
Apple이 거절REFUND_DECLINED 알림판매를 유지하고 아무것도 바꾸지 않음판단까지 최대 48시간
모래시계를 스마트폰과 종이 영수증 옆에 놓아, 고객이 Apple에 인앱 환불 요청을 제출한 뒤 최대 48시간의 대기를 표현한 그림

이 버튼이 당신에게 무엇을 치르게 하고, 무엇을 아껴주는가

당신이 다툴 수 있는 환불이 다툴 수 없는 차지백보다 낫다

앱 안에서 환불 경로를 찾지 못한 고객은 포기하지 않습니다. 은행으로 갑니다. 카드 차지백은 은행과의 사이에서 최종적이고, 분쟁 수수료가 붙으며, 판단을 당신과 Apple 양쪽 손에서 모두 앗아갑니다. 인앱 환불 요청은 그 같은 고객을 Apple의 시스템 안에 붙잡아 둡니다. 거기서는 소모성 구매가 당신이 응답할 수 있는 CONSUMPTION_REQUEST와 당신이 영향을 줄 수 있는 판단을 촉발합니다. 다툴 수 없는 차지백을 다툴 수 있는 Apple 심사와 맞바꾸는 것, 그것이 이 버튼의 재무적 논거 전부입니다.

당신은 환불의 마찰을 낮추고 있다

정직한 반대편은, 눈에 잘 띄는 원탭 환불 경로가 깊이 묻힌 지원 이메일보다 더 많은 환불 요청을 만들어낸다는 점입니다. 그중 일부는 원래 결코 일어나지 않았을 것입니다. 그것은 실재하는 비용이며, 그래서 Apple은 진입점을 구매 화면이 아니라 계정 설정이나 도움말 메뉴에 두라고 말합니다. 당신은 이미 불만인 고객이 그것을 찾기를 원하지, 그저 호기심에 찬 고객이 찾기를 원하지 않습니다.

처음부터 끝까지 계속 나가는 비용은, 환불된 계정에 서비스를 계속 제공하는 것

판단이 어느 쪽으로 가든, 당신이 결과에 따라 움직이기 전까지 전달 쪽 미터기는 계속 돕니다. 환불된 권리가 살아 있는 매 시간마다, 당신은 그 뒤의 실제 비용을 계속 지불합니다. 연산, 모델 API 호출, 저장, 그리고 그 고객의 사용량에 묶인 크리에이터나 파트너 정산금입니다. 인앱 요청은 그것을 바꾸지 않습니다. REFUND 알림에 즉시 취소하는 것이 바꿉니다. 이 버튼이 얼마나 저렴한지는, 결국 그것이 만들어내는 알림을 당신이 어떻게 처리하느냐에 달려 있습니다.

인앱 환불 요청을 출시해야 하는가

판매가 사는 곳이 아니라 지원이 사는 곳에 두어라

Apple의 배치 지침을 따르세요. 계정 설정과 도움말 메뉴가 알맞은 자리입니다. 페이월 옆의 환불 링크는 사람들이 돈을 돌려받길 기대하도록 길들이고, 굳이 제공할 필요가 없었던 호기심성 환불을 불러들입니다.

신뢰하기 전에 샌드박스에서 전체 흐름을 테스트하라

샌드박스와 Xcode의 StoreKit 테스트에서 요청을 대기 중에서 승인됨 또는 거절됨으로 옮기며 전체 경로를 시뮬레이션할 수 있습니다. 승인은 서버에 REFUND 알림을 보내고 거절은 REFUND_DECLINED를 보내므로, 실제 고객이 제출을 누르기 전에 당신의 핸들러가 올바르게 반응하는지 증명할 수 있습니다.

모든 결과를 처리하고, 결코 과장하지 마라

success에서는 대기 중을 표시하고, failed에서는 재시도를 제공하고, userCancelled에서는 아무것도 바뀌지 않았다고 말하고, duplicateRequest는 고객의 이전 요청이 여전히 유효하다는 조용한 메모로 다루세요. 해가 되는 단 하나의 실수는, 손에 쥔 것이 제출된 요청뿐인데도 고객에게 환불이 끝났다고 말하는 것입니다.

RefundHalt는 그 뒷일을 어떻게 처리하는가

인앱 시트는 Apple의 것입니다. 그 뒤에 오는 것은 당신의 것이고, 그 부분을 RefundHalt가 돌립니다. 고객이 당신의 앱 안에서 환불을 제출하면, RefundHalt는 소모성 구매에 대한 CONSUMPTION_REQUEST를 붙잡아 Apple의 판단에 도움이 되는 사용 증거와 함께 12시간 창 안에서 그것에 응답합니다. Apple이 판정하면, REFUND에서는 취소하고 REFUND_DECLINED에서는 접근 권한을 그대로 유지하며, 각각을 정확히 그 트랜잭션에 연결합니다. 당신은 심사도 증거도 취소도 손으로 허둥대는 일에 맡기지 않으면서, 더 친절한 인앱 환불 경로를 제공할 수 있습니다.

자주 묻는 질문

beginRefundRequest는 무엇을 하나요?
특정 트랜잭션에 대해 앱 안에 Apple의 환불 요청 시트를 띄웁니다. 고객은 자신의 구매 내역과 사유 코드 목록을 보고 하나를 고르며, 요청은 Apple로 전송됩니다. 이 메서드는 success 또는 userCancelled 상태를 반환하거나 duplicateRequest 또는 failed를 던집니다. 구매 자체를 환불하지는 않는데, Apple이 요청을 심사하며 판단까지 최대 48시간이 걸리기 때문입니다.
인앱 환불 요청은 곧바로 돈을 환불하나요?
아니요. success 결과는 App Store가 요청을 받았다는 뜻이지 승인했다는 뜻이 아닙니다. Apple은 승인 또는 거절까지 최대 48시간이 걸리며, 소모성 구매의 경우 먼저 CONSUMPTION_REQUEST 알림으로 서버에 사용 데이터를 요청합니다. success일 때 고객에게 대기 상태를 보여주고, 환불 확정은 절대 표시하지 마세요.
어느 iOS 버전이 인앱 환불 요청을 지원하나요?
iOS 15와 iPadOS 15, Mac Catalyst 15, visionOS 1입니다. StoreKit 2 메서드 beginRefundRequest(in:)는 이 버전들부터 사용할 수 있으므로, iOS 15 이상을 대상으로 하는 앱이라면 앱 안에서 Apple의 환불 시트를 띄울 수 있습니다.
인앱 환불 버튼을 어디에 두어야 하나요?
Apple의 지침은 구매나 페이월 화면이 아니라 계정 설정이나 도움말 메뉴에서 호출하라는 것입니다. 그러면 환불 경로가 불만인 고객이 지원을 찾는 자리에 놓이고, 신청할 생각이 없던 고객에게 환불을 광고하지 않게 됩니다.
인앱 환불이 고객이 은행에 연락하는 것보다 나은가요?
당신의 매출에는 대개 그렇습니다. 은행 차지백은 최종적이고 수수료가 붙으며 Apple과 당신을 모두 판단에서 배제합니다. 인앱 환불 요청은 고객을 Apple의 흐름 안에 붙잡아 두고, 거기서 소모성 구매는 당신이 응답할 수 있는 CONSUMPTION_REQUEST와 당신이 영향을 줄 수 있는 심사를 촉발합니다. 다툴 수 있는 환불이 다툴 수 없는 차지백보다 낫습니다.

출처 및 추가 자료

RefundHalt

App Store와 Google Play 환불 자동 처리

계속 읽기

다음 환불 요청은 이미 오고 있습니다.

이의를 제기하지 못한 환불에 관한 지원 이메일을 하나 더 읽는 시간이면 RefundHalt 설정을 마칠 수 있습니다.