모든 App Store 구매에 appAccountToken을 붙여라. 그러지 않으면 환불에 반박할 수 없다
고객이 환불을 요청하면 Apple은 당신의 서버에 CONSUMPTION_REQUEST를 보내지만, 그 거래에는 상대가 누구인지 전혀 담겨 있지 않습니다. appAccountToken은 구매를 당신의 사용자에게 연결하는 UUID입니다. 이것을 설정하면 실제 데이터로 Apple에 답할 수 있습니다. 건너뛰면 그저 추측할 뿐입니다.

핵심 요약
- appAccountToken은 App Store 구매에 붙이는 UUID로, 그 결과 생성되는 거래가 당신 자신의 시스템 안의 정확한 사용자를 가리키게 합니다. Apple은 이를 거래에 저장하고, 그 거래가 나타나는 모든 곳에서 반환합니다.
- Apple이 강제하는 유일한 형식 규칙은 그 값이 유효한 UUID여야 한다는 것입니다. 그 밖의 것, id, 이메일, 이어붙인 문자열 등을 전달하면 StoreKit은 조용히 그것을 버리고 appAccountToken을 nil로 반환합니다.
- StoreKit 2에서는 구매 옵션 하나, Product.PurchaseOption.appAccountToken(_:)으로 이를 설정하며, 그 계정을 위해 생성해 저장해 둔 안정적인 UUID를 사용합니다.
- 최초 구매 때 한 번만 설정하면, Apple은 구독 체인 안의 모든 갱신, 청구 재시도, 업그레이드에 걸쳐 동일한 토큰을 이어갑니다.
- 2025년부터 Set App Account Token 엔드포인트를 통해, 인앱 흐름으로는 결코 닿을 수 없었던 앱 외부 구매(오퍼 코드 사용이나 프로모션 구매 등)에도 서버에서 토큰을 붙일 수 있습니다.
- appAccountToken이야말로 Apple의 CONSUMPTION_REQUEST에 답할 수 있게 해주는 것입니다. 이것이 없으면 12시간 창 안에 환불을, 당신이 사용 내역을 설명해야 할 그 고객에게 연결할 수 없습니다.
- 식별할 수 없는 환불은 반박할 수 없는 환불입니다. 지킬 증거가 있었던 구매에 대해 돈을 돌려주게 되고, 게다가 그것들을 제공하느라 이미 쓴 연산 자원, API 호출, 지급액까지 잃습니다.
고객이 Apple에 환불을 요청하면 Apple은 당신의 서버에 CONSUMPTION_REQUEST를 보내고, 당신에게는 그 사람이 제품을 어떻게 사용했는지 실제 데이터로 답할 열두 시간이 주어집니다. 그런데 알림을 열어보면 상대가 누구인지 전혀 알 수 없다는 것을 깨닫습니다. 거래에는 originalTransactionId와 제품 id가 담겨 있지만, 당신 자신의 데이터베이스 안의 계정을 가리키는 것은 아무것도 없습니다. 바로 그 간극을 appAccountToken이 메우며, 구매 시점에 설정하지 않았다면 그 판매에 대해서는 나중에 메울 수 없습니다.
appAccountToken은 구매에 붙이는 UUID로, 그 결과 생성되는 App Store 거래가 당신 시스템 안의 정확한 사용자를 가리키는 포인터를 갖게 합니다. 설정하면, 그 고객에 대해 Apple이 앞으로 던지는 모든 환불 질문이 상대의 신원과 함께 도착합니다. 건너뛰면 그저 추측할 뿐입니다. 아래에서는 이 필드가 무엇인지, 어떻게 설정하는지, 앱 외부 구매를 구제하는 새 엔드포인트, 그리고 환불이 닥쳤을 때 이 빠진 연결이 실제로 얼마의 대가를 치르게 하는지 설명합니다.
appAccountToken이란 실제로 무엇인가
appAccountToken은 당신이 생성해 구매 순간에 StoreKit에 전달하는 불투명한 UUID입니다. Apple은 이를 거래에 저장하고 그 구매의 거래 정보 안에서 반환하며, 그 자리에 계속 남습니다. Apple의 표현으로는 그것은 「거래를 당신 자신의 서비스에 있는 사용자의 계정과 연결하는 UUID」입니다. 유일한 형식 규칙은 그것이 UUID여야 한다는 것입니다. Apple은 그것을 읽지 않고, 무엇에 매핑되는지 검증하지 않으며, 당신 쪽에서 그것이 무엇을 의미하는지 신경 쓰지 않습니다. 그것은 당신이 통제하는 연결입니다.
그것은 거래 위에 존재하기 때문에, 거래가 나타나는 모든 곳에서 함께 돌아옵니다. 서버 알림의 서명된 거래, App Store Server API의 Get Transaction Info 응답, 그리고 구독 체인 안의 모든 갱신은, 당신이 최초 구매에서 설정했다면 모두 동일한 토큰을 지닙니다. 한 번 붙인 하나의 UUID가, 그 관계가 지속되는 내내 고객의 청구를 따라다닙니다.
진짜 UUID여야 하며, 아니면 조용히 사라진다
Apple이 강제하는 유일한 규칙은 형식입니다. StoreKit 2는 RFC 4122 UUID를 요구합니다. 이어붙인 문자열, 정수 id, 이메일 주소를 전달해도 StoreKit은 오류를 던지지 않습니다. 그 값을 버리고, 거래는 appAccountToken이 nil로 설정된 채 돌아옵니다. 개발자들은 끊임없이 이 문제에 부딪히며, 증상은 늘 같아서 Apple 자체 포럼에 「appAccountToken is missing in the transaction payload」 같은 표현으로 나타나는데, 거의 언제나 전달된 값이 유효한 UUID가 아니었기 때문입니다. 서버 측에서 진짜 UUID를 생성하고, 그것을 계정에 대응시켜 저장하며, StoreKit에는 결코 다른 것을 건네지 마십시오.
구매 시점에 어떻게 설정하는가
StoreKit 2에서는 단일 구매 옵션입니다. 사용자가 가입할 때 또는 처음 결제에 이르렀을 때 서버에서 UUID를 생성하고, 그들의 계정 레코드에 저장한 뒤, 그 같은 값을 구매 호출에 전달합니다.
그 시그니처는 Product.PurchaseOption.appAccountToken(_ token: UUID)이며, 구매는 try await product.purchase(options: [.appAccountToken(token)])처럼 보입니다. 거래가 App Store Server API를 통해 검증되어, 또는 서버 알림으로 전달되어 돌아올 때, 그것은 그 UUID를 지니며, 당신의 서버는 한 번의 쿼리로 고객을 조회합니다.
계정마다 하나의 안정적인 토큰을 사용하라
같은 사용자의 구매마다 새 토큰을 생성하지 마십시오. Apple은 같은 체인 안의 갱신, 청구 재시도, 업그레이드에서 토큰을 반환하므로, 계정마다 안정적인 UUID가 있으면 첫 구매부터 미래의 모든 이벤트까지 깔끔한 하나의 실이 이어집니다. 구매마다 바뀌는 토큰은 그 실을 끊고 목적 자체를 무너뜨립니다. 한 계정, 하나의 토큰, 그 계정이 구매할 때마다 재사용합니다.
앱 외부 구매를 구제하는 엔드포인트
2025년까지는 구멍이 하나 있었습니다. 고객이 오퍼 코드를 사용하거나 App Store에서 곧바로 프로모션된 인앱 구매를 샀다면, 당신의 앱은 구매 흐름을 한 번도 실행하지 않았으므로 appAccountToken을 설정할 곳이 어디에도 없었습니다. 그 거래들은 익명으로 도착했고 그대로 남았습니다.
WWDC 2025는 Set App Account Token 엔드포인트로 이 간극을 메웠습니다. 당신의 서버가 App Store Server API에서 PUT /inApps/v1/transactions/{originalTransactionId}/appAccountToken을 본문에 UUID를 담아 호출하면, Apple은 그 거래에 토큰을 설정합니다. 모든 제품 유형에서 작동하고, 오퍼 코드 사용과 프로모션 구매를 포괄하며, 당신이 보낸 값은 거래에 이미 있는 어떤 토큰이든 덮어씁니다. 이제 앱을 한 번도 거치지 않고, 서버에서 나중에 구매를 연결할 수 있습니다.

| 구매 채널 | appAccountToken을 설정하는 곳 | 비고 |
|---|---|---|
| 인앱 구매 | 구매 시점의 StoreKit 구매 옵션 | Product.PurchaseOption.appAccountToken(UUID) |
| 오퍼 코드 사용 | Set App Account Token 엔드포인트, 서버 측 | 연결할 인앱 흐름이 없으므로 나중에 설정 |
| App Store의 프로모션 인앱 구매 | Set App Account Token 엔드포인트, 서버 측 | 구매가 당신의 앱 밖에서 일어남 |
| 구독 갱신 | 할 일 없음 | 최초 구매에서 자동으로 이어짐 |
빠진 연결이 어디서 당신의 돈을 잃게 하는가
토큰의 요점은 깔끔한 기록이 아닙니다. Apple이 개발자에게 던지는 유일한 환불 질문, 즉 CONSUMPTION_REQUEST는, 그것이 관련된 고객을 찾을 수 있어야만 답할 수 있다는 점입니다.
구매자가 소모성 항목이나 비갱신 구독에 대해 환불을 요청하면, Apple은 당신의 서버에 CONSUMPTION_REQUEST 알림을 보내고 Send Consumption Information으로 답할 열두 시간을 줍니다. 당신의 답은 그 특정 고객에 관한 데이터입니다. 그들이 제품을 얼마나 소비했는지, 계정 유지 기간, 평생 지출, 제공 상태. appAccountToken 자체가 그 요청 안의 필드 중 하나이며, 더 중요하게는, 알림의 거래를 당신이 이제 사용 내역을 설명하려는 계정에 매핑하는 방법이 바로 그것입니다. 토큰이 없으면 조회도 없고, 정확한 답도 없습니다.
빈 답이 실제로 얼마의 대가를 치르는가
식별할 수 없는 환불은 나쁜 선택을 강요합니다. 소비 요청에 아무것도 없이 답할 수 있는데, 그것은 낮은 소비로 읽혀 Apple을 환불 승인 쪽으로, 제품을 많이 사용한 고객까지 포함해 기울게 합니다. 아니면 추측할 수도 있습니다. 어느 쪽이든 당신은 반박할 증거가 있었던 구매에 대해 환불하고 있으며, 그것들을 제공하는 실제 비용은 이미 치렀습니다.
그 비용은 판매 가격이 아닙니다. 일련의 모델 API 호출을 실행하고, 이미지를 생성하고, 동영상을 내보내거나, 크리에이터 지급을 촉발한 소모성 항목은 제공되는 순간 실제 돈을 썼습니다. 환불은 고객의 결제를 돌려줍니다. 그러나 공급자의 청구서는 돌려주지 않습니다. 식별할 수 없는 한 고객에게 그가 제기하는 모든 환불을 곱하고, 다시 당신이 상대를 모른다는 데 기대는 모든 연쇄 환불자를 곱하면, 당신이 건너뛴 토큰은 당신이 결코 쓰지 않은 가장 값비싼 한 줄의 코드가 됩니다.
같은 발상이 Android에도, 다른 이름으로 존재한다
Google Play는 setObfuscatedAccountId로 동일한 문제를 해결합니다. 이것은 구매에 계정 식별자를 붙여, orders.reviewrefund를 통한 Google의 지불 거절 심사를 실제 사용자에 대해 답할 수 있게 합니다. 스토어가 다르고 방식이 달라도 교훈은 같습니다. 구매 시점에 신원을 붙이거나, 아니면 나중에 분쟁에 반박할 수 없거나입니다. App Store에서 그 도구는 appAccountToken이며, UUID여야 합니다.
토큰을 제자리에 유지하는 세 가지 습관
- 계정마다 UUID를 생성해 저장하라. 고객마다 하나의 안정적인 토큰을, 가입 시점이나 첫 결제 때 만들어 그들의 레코드에 저장하고, 모든 구매에서 재사용한다.
- 전달하기 전에 검증하라. 구매 코드에서 그 값이 진짜 UUID인지 확인해, 잘못된 형식의 id가 결코 거래에서 조용히 nil 토큰이 되지 않게 한다.
- 앱 외부 구매를 보완하라. 토큰이 없는 오퍼 코드 사용이나 프로모션 구매에 대해 서버 알림이 도착하면, Set App Account Token 엔드포인트를 호출해 올바른 것을 붙인다.
이 세 가지를 하면, Apple이 앞으로 당신에게 보내는 모든 거래는, 환불 요청을 포함해, 그것이 속한 고객에 이미 묶인 채로 도착합니다.
이것이 RefundHalt가 의존하는 기반입니다. 우리는 모든 거래와 서버 알림에서 appAccountToken을 읽어, 그 계정에 대해 우리가 이미 추적하는 사용 내역과 연결하고, 열두 시간 창 안에 고객의 실제 수치로 Apple의 소비 요청에 답합니다. 토큰이 그 실입니다. 한 번 설정하면, 당신의 환불 방어는 붙잡을 무언가를 갖게 됩니다.
자주 묻는 질문
- App Store에서 appAccountToken이란 무엇인가요?
- appAccountToken은 당신이 생성해 StoreKit을 통해 구매에 붙이는 UUID로, 그 결과 생성되는 App Store 거래가 당신 자신의 시스템 안의 특정 사용자 계정을 가리키게 합니다. Apple은 이를 거래에 저장하고 거래 정보, 서버 알림, 같은 체인 안의 모든 갱신에서 반환하므로, 환불 요청을 포함한 어떤 미래의 이벤트든 올바른 고객에 연결할 수 있습니다.
- 왜 제 appAccountToken이 nil이거나 없나요?
- 거의 언제나 당신이 전달한 값이 유효한 UUID가 아니었기 때문입니다. StoreKit 2는 RFC 4122 UUID를 요구하고 그 밖의 것은 조용히 버리므로, 이어붙인 문자열, 정수 id, 이메일은 nil인 appAccountToken으로 반환되면서도 구매는 여전히 성공합니다. 또 다른 흔한 원인은 오퍼 코드 사용처럼 당신의 앱 밖에서 이루어진 구매로, 토큰을 설정할 인앱 흐름이 실행되지 않은 경우입니다.
- 오퍼 코드를 위해, 구매 후에 appAccountToken을 설정할 수 있나요?
- 네, 2025년부터 가능합니다. App Store Server API의 Set App Account Token 엔드포인트를 통해, 당신의 서버는 PUT /inApps/v1/transactions/{originalTransactionId}/appAccountToken을 본문에 UUID를 담아 호출함으로써 기존 거래에 토큰을 붙이거나 덮어쓸 수 있습니다. 모든 제품 유형에서 작동하며, 오퍼 코드 사용이나 프로모션된 인앱 구매처럼 당신의 앱 밖에서 이루어진 구매를 위해 만들어졌습니다.
- appAccountToken은 반드시 UUID여야 하나요?
- 네. Apple이 강제하는 유일한 형식 규칙은 그 값이 유효한 UUID여야 한다는 것입니다. 그 외에는 불투명하므로 당신 쪽에서는 원하는 어떤 계정 키에든 매핑할 수 있지만, UUID가 아니면 StoreKit은 그것을 저장하지 않고 거래는 appAccountToken이 nil로 설정된 채 돌아옵니다.
- appAccountToken은 환불에 어떻게 도움이 되나요?
- 고객이 환불을 요청하면 Apple은 CONSUMPTION_REQUEST를 보내고, 그 특정 구매자에 관한 데이터로 응답할 열두 시간을 줍니다. appAccountToken은 알림의 거래를, 당신이 사용 내역을 보고해야 할 계정에 맞추는 방법이며, 소비 요청 자체의 필드 중 하나이기도 합니다. 이것이 없으면 실제 사용 내역으로 답할 수 없어, Apple은 당신에게 다툴 증거가 있었던 환불을 승인하는 쪽으로 기웁니다.
- appAccountToken은 구매마다 달라야 하나요?
- 아니요. 계정마다 하나의 안정적인 UUID를 사용하고 그 사용자가 하는 모든 구매에서 재사용하십시오. Apple은 구독 체인 안의 갱신과 업그레이드에 걸쳐 토큰을 이어가므로, 안정적인 토큰은 시간에 걸친 깔끔한 연결을 줍니다. 구매마다 바뀌는 토큰은 그 연결을 끊고 거래를 같은 고객에 묶기 더 어렵게 만듭니다.
출처 및 추가 자료
- Apple Developer: appAccountToken (StoreKit Transaction)
- Apple Developer: Product.PurchaseOption.appAccountToken(_:)
- Apple Developer: Set App Account Token (App Store Server API)
- Apple Developer: appAccountToken (App Store Server API)
- Apple Developer: ConsumptionRequest (Send Consumption Information)
- Apple Developer: Send Consumption Information
- WWDC25: Dive into App Store server APIs for In-App Purchase
RefundHalt
App Store와 Google Play 환불 자동 처리
계속 읽기
Google Play 구매를 사흘 안에 확인하지 않으면 Google이 환불합니다. 그것이 당신에게 얼마의 손실을 주는지
서버가 사흘 안에 확인하지 않은 구매를 Google Play는 자동으로 환불하고 취소합니다. 이것은 고객의 결정이 아니라 통합 실수이며, 완전히 예방할 수 있습니다. 정확한 규칙과 그것이 왜 발동하는지, 잃어버린 매출 한 건이 실제로 얼마의 비용인지 설명합니다.
연쇄 환불 악용은 두 번 손해를 입힙니다. 스토어가 마련해 둔 반격 방법을 소개합니다
몇 번이고 반복해서 환불을 요구하는 고객은 우연이 아닙니다. 환불 악용은 금액을 되돌려주게 할 뿐 아니라 이미 쓴 연산 자원까지 앗아갑니다. 그리고 두 스토어 모두 그 패턴을 엮어낼 신원 신호, 즉 Apple 의 appAccountToken 과 Google 의 난독화 계정 ID 를 제공합니다.