Offering 가져오기 오류
0:08:00Offering 가져오기 오류는 개발자가 가장 자주 마주치는 문제에 속합니다. 주로 RevenueCat이 앱 스토어에서 상품 정보를 가져오지 못할 때 발생합니다.
문제 1: "None of the products could be fetched" 오류
오류 메시지
[RevenueCat] 🍎‼️ Error fetching offerings - The operation couldn't be completed.
(RevenueCat.OfferingsManager.Error error 1.)
There's a problem with your configuration. None of the products registered in the RevenueCat dashboard
could be fetched from App Store Connect (or the StoreKit Configuration file if one is being used).
More information: https://rev.cat/why-are-offerings-empty흔한 원인
- 상품 미구성: RevenueCat 대시보드에 상품이 설정되지 않았습니다.
- Product ID 불일치: RevenueCat의 ID가 스토어 상품과 일치하지 않습니다.
- 현재 스토어프런트에서 상품 사용 불가: 지원되지 않는 지역에서 테스트하고 있습니다.
- bundle ID / 패키지 이름 불일치: 앱 식별자가 대시보드와 일치하지 않습니다.
- 상품 미승인(iOS): App Store Connect에서 상품이 심사 대기 중입니다.
- 서비스 계정 문제(Android): Google Play 자격 증명이 구성되지 않았습니다.
해결 단계
1단계: RevenueCat에 상품이 존재하는지 확인하기
- RevenueCat 대시보드로 이동합니다.
- Product Catalog → Products로 이동합니다.
- 상품이 목록에 표시되는지 확인합니다.
- 비어 있다면 상품을 가져오거나(import) 직접 생성합니다.
2단계: Product ID가 스토어와 일치하는지 확인하기
iOS의 경우
- App Store Connect에서 Product ID를 확인합니다.
- RevenueCat 대시보드(Product Catalog → Products)와 비교합니다.
- 대소문자까지 정확히 일치하는지 확인합니다.
Android의 경우
- Google Play Console에서 Product ID를 확인합니다.
- RevenueCat 대시보드와 비교합니다.
- 대소문자까지 정확히 일치하는지 확인합니다.
3단계: 상품이 Offering에 포함되어 있는지 확인하기
- RevenueCat 대시보드에서 Product Catalog → Offerings로 이동합니다.
- Offering을 선택합니다(보통 "default").
- Package가 존재하고 상품을 포함하는지 확인합니다.
- Package가 없다면 Edit을 클릭하여 상품이 포함된 Package를 추가합니다.
4단계: 플랫폼별 설정 확인하기
iOS의 경우
- RevenueCat에서 bundle ID가 일치하는지 확인합니다(Apps & providers → App Store).
- 상품이 승인되었는지 확인하거나 StoreKit Configuration File을 사용합니다.
- 샌드박스 테스터로 로그인되어 있는지 확인합니다(샌드박스에서 테스트하는 경우).
Android의 경우
- RevenueCat에서 패키지 이름이 일치하는지 확인합니다(Apps & providers → Google Play).
- 서비스 계정이 구성되어 있고 권한을 가지고 있는지 확인합니다.
- Google Play Console에서 상품이 Active 상태인지 확인합니다.
- 앱이 최소한 Internal Testing에는 게시되어 있는지 확인합니다.
문제 2: 빈 Offering
증상
offerings.all이 비어 있습니다.offerings.current가 nil입니다.- 오류 메시지는 없지만 offerings 객체에 데이터가 없습니다.
흔한 원인
- Offering에 Package 없음: Offering은 존재하지만 Package가 없습니다.
- 기본 Offering 미설정: 기본값으로 지정된 Offering이 없습니다.
- Package에 상품 미연결: Package는 존재하지만 상품이 없습니다.
- Offering 식별자 불일치: 코드가 잘못된 Offering 식별자를 요청합니다.
해결 단계
1단계: Offering에 Package가 있는지 확인하기
- RevenueCat 대시보드에서 Product Catalog → Offerings로 이동합니다.
- Offering을 클릭합니다(예: "default").
- Packages 섹션에 Package가 최소 하나 이상 있는지 확인합니다.
- 비어 있다면 Edit을 클릭하여 Package를 추가합니다.
2단계: Package에 상품이 있는지 확인하기
- Offering의 각 Package를 확인합니다.
- 사용하는 플랫폼(iOS 또는 Android)에 맞는 상품이 연결되어 있는지 확인합니다.
- 최소한 각 Package에는 하나의 Product ID가 필요합니다.
3단계: 기본 Offering 설정하기
- Product Catalog → Offerings로 이동합니다.
- 주요 Offering을 찾습니다.
- "Current" 배지가 표시되는지 확인합니다.
- 표시되지 않으면 해당 Offering을 클릭한 뒤 Make Current를 선택합니다.
4단계: 코드가 올바른 식별자를 사용하는지 확인하기
iOS
let offerings = try await Purchases.shared.offerings()
// 현재 Offering이 존재하는지 확인
if let offering = offerings.current {
print("Offering: \(offering.identifier)")
print("Packages: \(offering.availablePackages.count)")
} else {
print("No current offering set")
}Android
val offerings = Purchases.sharedInstance.awaitOfferings()
// 현재 Offering이 존재하는지 확인
offerings.current?.let { offering ->
Log.d("RC", "Offering: ${offering.identifier}")
Log.d("RC", "Packages: ${offering.availablePackages.size}")
} ?: Log.d("RC", "No current offering set")문제 3: Offering 구성 경고
오류 메시지
warning: The offerings 'default' have configuration issues that may prevent users from
seeing product options or making purchases.
Product Issues:
• Package 'monthly' references product 'premium_monthly' which is not available흔한 원인
- 플랫폼별 상품 누락: Package에 iOS 상품은 있지만 Android 상품이 없습니다(또는 그 반대).
- 잘못된 상품 연결: Package에 연결된 상품이 스토어에 존재하지 않습니다.
- 플랫폼 필터 문제: 상품은 존재하지만 현재 플랫폼에서 필터링되어 제외됩니다.
해결 단계
1단계: Package 구성 확인하기
- RevenueCat 대시보드에서 Offering으로 이동합니다.
- 각 Package에 iOS와 Android 양쪽 상품이 있는지 확인합니다(두 플랫폼을 모두 지원하는 경우).
- 한 플랫폼만 지원한다면 해당 플랫폼의 상품이 존재하는지 확인합니다.
2단계: 상품이 스토어에 존재하는지 확인하기
- 경고에서 문제가 되는 Product ID를 복사합니다.
- App Store Connect 또는 Google Play Console에 존재하는지 확인합니다.
- 존재하지 않는다면 다음 중 하나를 수행합니다.
- 스토어에 상품을 생성하거나,
- RevenueCat에서 상품 참조를 제거하거나 업데이트합니다.
문제 4: 네트워크 및 API 오류
오류 메시지
Error: Unable to fetch offerings
NetworkError: The Internet connection appears to be offline
Error: Request timed out흔한 원인
- 네트워크 연결 없음: 기기에 인터넷 접속이 없습니다.
- 방화벽/VPN 차단: 회사 네트워크나 VPN이 RevenueCat 서버를 차단합니다.
- 잘못된 API 키: 잘못된 API 키가 구성되었습니다.
- 서버 문제: 드물게 발생하는 RevenueCat 서비스 장애입니다.
해결 단계
1단계: 네트워크 연결 확인하기
- 기기 또는 시뮬레이터에서 인터넷 연결을 테스트합니다.
- 브라우저에서 웹사이트를 열어 봅니다.
- Wi-Fi 또는 셀룰러 데이터가 활성화되어 있는지 확인합니다.
2단계: API 키 확인하기
// 플랫폼에 맞는 올바른 API 키를 사용하는지 확인
// iOS: "appl_"로 시작해야 함
// Android: "goog_"로 시작해야 함
Purchases.configure(withAPIKey: "appl_xxxxxxxxxxx")3단계: VPN/프록시 없이 테스트하기
- VPN 또는 프록시를 일시적으로 비활성화합니다.
- Offering 가져오기가 정상적으로 동작하는지 테스트합니다.
- 정상 동작한다면 RevenueCat 도메인을 허용하도록 VPN/방화벽을 구성합니다.
4단계: 재시도 로직 구현하기
func fetchOfferingsWithRetry(maxRetries: Int = 3) async throws -> Offerings {
var lastError: Error?
for attempt in 1...maxRetries {
do {
return try await Purchases.shared.offerings()
} catch {
lastError = error
if attempt < maxRetries {
// 재시도 전 대기 (지수 백오프)
try await Task.sleep(nanoseconds: UInt64(pow(2.0, Double(attempt))) * 1_000_000_000)
}
}
}
throw lastError ?? NSError(domain: "Offerings", code: -1)
}문제 5: SampleCat에서는 Offering이 동작하지만 내 앱에서는 동작하지 않음
증상
RevenueCat의 SampleCat 앱(또는 REST API)으로는 Offering과 상품을 가져올 수 있지만, bundle ID가 동일한데도 직접 만든 앱에서는 빈 Offering이 반환되거나 상품을 가져오지 못합니다.
발생 원인
동일한 bundle ID로 SampleCat이 정상 동작한다면, RevenueCat 대시보드 구성과 스토어 상품이 올바르다는 것을 의미합니다. 문제는 앱이 SDK를 초기화하거나 통신하는 방식에 있습니다. bundle ID, API 키, 스토어가 완전한 사슬을 이뤄야 합니다.
Store (App Store / Google Play)
↕ (Product ID가 일치해야 함)
RevenueCat Project (여기에서 bundle ID 구성)
↕ (API 키 + bundle ID가 일치해야 함)
Your App (Xcode/Gradle의 bundle ID + 코드의 API 키)세 계층이 모두 정렬되어야 합니다. SampleCat에서는 멀쩡하지만 앱에서 어느 한 연결이라도 끊어져 있으면, SampleCat은 성공하고 앱은 실패합니다.
해결 단계
1단계: API 키 확인하기
가장 흔한 원인입니다. Purchases.configure() 호출이 SampleCat이 사용하는 것과 정확히 동일한 공개 API 키를 사용하는지 다시 확인하세요.
// iOS: 키는 "appl_"로 시작해야 함
Purchases.configure(withAPIKey: "appl_YOUR_PUBLIC_KEY")
// Android: 키는 "goog_"로 시작해야 함
Purchases.configure(this, "goog_YOUR_PUBLIC_KEY")sk_로 시작)를 사용하지 마세요. RevenueCat Dashboard → Project Settings → API Keys에서 제공하는 공개 키를 사용하세요.
2단계: SDK 초기화 시점 확인하기
Purchases.configure()는 Offering을 가져오기 전에, 앱 생명주기에서 가능한 한 일찍 호출해야 합니다. 너무 늦게 호출하거나 조건부로 호출하면, Offering을 요청할 때 SDK가 준비되지 않았을 수 있습니다.
// iOS: AppDelegate 또는 App init에서 구성
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Purchases.logLevel = .debug
Purchases.configure(withAPIKey: "appl_YOUR_KEY")
return true
}// Android: Application.onCreate()에서 구성
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
Purchases.logLevel = LogLevel.DEBUG
Purchases.configure(PurchasesConfiguration.Builder(this, "goog_YOUR_KEY").build())
}
}3단계: 콜백에서 오류 확인하기
Offering을 가져올 때는 항상 error 매개변수를 점검하세요. 여기서 조용히 실패하는 것이 "그냥 동작하지 않는다"의 가장 흔한 원인입니다.
Purchases.shared.getOfferings { offerings, error in
if let error = error {
print("❌ Error fetching offerings: \(error.localizedDescription)")
return
}
guard let current = offerings?.current else {
print("⚠️ No current offering found")
return
}
print("✅ Current offering: \(current.identifier)")
print(" Packages: \(current.availablePackages.map { $0.identifier })")
}4단계: StoreKit Configuration 확인하기(iOS 시뮬레이터 전용)
iOS 시뮬레이터에서 실행할 때는 Xcode 프로젝트에 StoreKit Configuration 파일이 필요합니다. SampleCat에는 기본으로 포함되어 있지만, 직접 만든 프로젝트에는 없을 수 있습니다.
- Xcode에서 Product → Scheme → Edit Scheme → Run → Options로 이동합니다.
- StoreKit Configuration 드롭다운을 확인합니다.
- "None"으로 되어 있다면, 일치하는 Product ID로 StoreKit Configuration 파일을 만들거나, 샌드박스 계정이 있는 실기기에서 테스트합니다.
5단계: 디버그 로그를 활성화하고 비교하기
configure() 앞에 Purchases.logLevel = .debug(iOS) 또는 Purchases.logLevel = LogLevel.DEBUG(Android)를 추가합니다. SampleCat과 앱을 모두 실행한 뒤 디버그 출력을 비교합니다. 다음 항목에서 차이가 있는지 확인하세요.
- 사용 중인 API 키
- 앱 사용자 ID
- 스토어에서 반환된 상품
- 초기화 중 발생한 오류 메시지
"Configuring Purchases with API key: appl_xxx"가 표시되는데 그 키가 SampleCat이 사용하는 것과 다르다면, 문제를 찾은 것입니다.
문제 6: 상품이 "READY_TO_SUBMIT" 상태임
증상
두 개의 로그 메시지가 연달아 나타납니다. 먼저, SDK는 올바르게 구성되었지만 상품에 문제가 있다는 경고가 표시됩니다.
WARN: ⚠️ RevenueCat SDK is configured correctly, but contains some issues you might want to address
Warnings:
• Your products are configured in RevenueCat but aren't approved in App Store Connect yet.
This prevents users from making purchases in production.
Product Issues:
⚠️ monthly (monthly): This product's status (READY_TO_SUBMIT) requires you to take action
in App Store Connect before using it in production purchases.
⚠️ yearly (yearly): This product's status (READY_TO_SUBMIT) requires you to take action
in App Store Connect before using it in production purchases.
Offering Issues:
⚠️ default
⚠️ $rc_monthly (monthly): status (READY_TO_SUBMIT)
⚠️ $rc_annual (yearly): status (READY_TO_SUBMIT)그다음, 어떤 상품도 가져올 수 없다는 오류가 표시됩니다.
ERROR: 😿‼️ There is an issue with your configuration. Check the underlying error for more details.
More information: https://rev.cat/sdk-troubleshooting
There's a problem with your configuration. None of the products registered in the RevenueCat
dashboard could be fetched from App Store Connect (or the StoreKit Configuration file if one
is being used).
More information: https://rev.cat/why-are-offerings-empty발생 원인
READY_TO_SUBMIT 상태는 상품이 App Store Connect에 존재하지만 심사용으로 제출된 적이 없다는 뜻입니다. Apple은 인앱 구매 상품을 앱 버전과 함께 제출하도록 요구합니다. 심사를 거쳐 승인되기 전까지는 App Store가 프로덕션에서 해당 상품을 앱에 제공하지 않습니다.
여기서 핵심 단서는 WARN 메시지 자체에 있습니다. "RevenueCat SDK is configured correctly." 이는 RevenueCat 대시보드 설정, API 키, bundle ID가 모두 정상임을 확인해 줍니다. 문제는 전적으로 App Store Connect 쪽에 있습니다.
상품 상태 이해하기
| 상태 | 의미 | 상품을 가져올 수 있나요? |
|---|---|---|
| Ready to Submit | 상품이 생성되었지만 심사용으로 제출된 적이 없습니다. | 불가(프로덕션). 가능(샌드박스/StoreKit Config). |
| Waiting for Review | 앱 버전과 함께 제출되어 Apple 심사 대기 중입니다. | 불가(프로덕션). 가능(샌드박스/StoreKit Config). |
| Approved | Apple의 심사를 거쳐 승인되었습니다. | 가능. |
| Developer Action Needed | Apple이 상품을 거부했거나 플래그를 지정했습니다. | 불가. |
| Missing Metadata | 상품이 미완성 상태입니다(설명, 가격, 스크린샷 누락). | 불가. |
해결 단계
1단계: App Store Connect에서 상품 메타데이터 완성하기
READY_TO_SUBMIT 상태인 각 상품에 필요한 메타데이터가 모두 있는지 확인하세요.
- App Store Connect → 앱 → Subscriptions(또는 In-App Purchases)로 이동합니다.
- 각 상품을 클릭하여 다음 항목이 있는지 확인합니다.
- Reference Name과 Product ID.
- 구성된 Subscription Price 또는 가격 티어 하나 이상.
- 표시 이름과 설명이 포함된 Localization.
- Review Screenshot(앱에서 상품을 보여 주는 스크린샷).
2단계: 상품을 앱 버전과 함께 제출하기
인앱 구매 상품은 새 앱 버전과 함께 제출해야 합니다.
- App Store Connect에서 앱으로 이동하여 새 버전을 생성합니다(또는 대기 중인 버전을 사용).
- In-App Purchases and Subscriptions 섹션으로 스크롤합니다.
- + 버튼을 클릭하고 상품을 선택합니다.
- 해당 버전을 심사용으로 제출합니다.
"구매를 테스트하려면 심사용으로 제출해야 하나요?"
완전히 별개인 두 가지가 있습니다.
| App Store 심사용 제출 | 개발 중 구매 테스트 | |
|---|---|---|
| 역할 | App Store에서 실제 사용자에게 상품을 제공합니다. | 출시 전에 연동이 잘 동작하는지 확인할 수 있습니다. |
| 필요 조건 | 스크린샷, 키워드, 설명, 심사 노트가 포함된 완전한 앱 버전. | StoreKit Configuration 파일 또는 샌드박스 테스터 계정만 있으면 됩니다. |
| 필요한 상품 상태 | 심사 후 상품이 "Approved" 상태여야 합니다. | "Ready to Submit"이면 충분합니다. |
| 필요한 빌드 | App Store Connect를 통해 빌드를 심사용으로 제출해야 합니다. | Xcode에서 실행하거나 TestFlight로 설치합니다. |
| 소요 시간 | Apple 심사에 24~48시간. | 즉시(StoreKit Config) 또는 몇 분(샌드박스). |
3단계: 승인을 기다리지 않고 테스트하기
"Ready to Submit" 상품으로 지금 바로 구매를 테스트할 수 있습니다. 다음 방법 중 하나를 선택하세요.
옵션 A: StoreKit Configuration File(권장, 가장 빠름)
가장 빠른 테스트 방법입니다. Apple 의존성 없이 오프라인에서 동작합니다.
- Xcode에서 File → New → File → StoreKit Configuration File로 이동합니다.
- App Store Connect와 정확히 동일한 Product ID로 각 상품을 추가합니다.
- 상품 유형, 가격, 기간을 App Store Connect 구성과 일치하도록 설정합니다.
- Product → Scheme → Edit Scheme → Run → Options로 이동합니다.
- StoreKit Configuration에서 새로 만든
.storekit파일을 선택합니다. - 빌드하고 실행합니다. 상품이 즉시 로드됩니다.
옵션 B: 샌드박스 테스트(실기기)
Apple의 샌드박스 서버를 대상으로 테스트하며, 실제 프로덕션 환경에 더 가깝습니다.
- App Store Connect → Users and Access → Sandbox Testers에서 샌드박스 테스터를 생성합니다.
- 테스트 기기에서 Settings → App Store → Sandbox Account로 이동하여 샌드박스 자격 증명으로 로그인합니다.
- Xcode scheme의 StoreKit Configuration이 "None"으로 설정되어 있는지 확인합니다(로컬 파일이 아닌 실제 샌드박스를 사용하도록).
- 기기에서 앱을 실행합니다. "Ready to Submit" 상품을 샌드박스에서 사용할 수 있습니다.
옵션 C: RevenueCat Test Store
Apple 인프라가 전혀 필요 없이 빠르고 결정론적인 테스트를 하려면 RevenueCat의 Test Store를 사용하세요. 샌드박스 계정도, StoreKit 파일도 필요 없습니다.
문제 7: RevenueCat 구성이 올바른데도 StoreKit이 상품을 확인하지 못함(iOS)
증상
RevenueCat 대시보드와 API에는 올바른 상품 식별자로 Offering이 정상 구성되어 있습니다. App Store Connect에도 상품이 존재합니다. 그런데 앱이 Purchases.shared.offerings()를 호출하면 availablePackages 배열이 비어서 돌아옵니다. 오류는 발생하지 않지만 상품이 표시되지 않습니다.
let offerings = try await Purchases.shared.offerings()
// offerings.current는 존재하지만:
print(offerings.current?.availablePackages.count) // 0핵심 진단 단서가 있습니다. RevenueCat REST API를 직접 조회하면 Offering과 상품이 올바르게 반환됩니다. 격차는 "RevenueCat 서버는 상품을 알고 있다"와 "StoreKit이 기기에서 상품을 확인하지 못한다" 사이에 있습니다.
발생 원인
getOfferings()가 호출되면 RevenueCat SDK는 두 단계를 수행합니다.
- RevenueCat 서버에서 Offering 구성을 가져옵니다(Offering 식별자, Package 식별자, Product ID). 대시보드가 올바르게 구성되어 있으므로 이 단계는 성공합니다.
- Apple의
Product.products(for:)(StoreKit 2) 또는SKProductsRequest(StoreKit 1)를 호출하여 StoreKit으로 상품 식별자를 확인합니다. StoreKit이 Product ID를 확인하지 못하면 해당 Package가 필터링되어 제외되고availablePackages가 비게 됩니다.
Apple의 StoreKit에는 바이너리 활성화 단계가 필요합니다. 앱에서 특정 Product ID를 처음 사용할 때, Apple이 해당 상품 식별자를 앱과 연결하려면 App Store Connect에 업로드된 앱 바이너리를 최소 하나는 처리해야 합니다. 이 처리가 완료되기 전까지는 App Store Connect에 상품이 존재하더라도 StoreKit이 상품을 확인하지 못합니다.
주로 발생하는 경우
- 완전히 새로운 상품: App Store Connect에서 인앱 구매 상품을 처음 막 생성했습니다.
- 최초 앱 제출: 앱에 App Store Connect로 업로드된 바이너리가 한 번도 없었습니다.
- 새 Product ID 추가: 기존 앱에 새 상품을 추가했지만 생성 이후 빌드를 제출하지 않았습니다.
다른 문제와 구분하는 방법
| 확인 항목 | 결과 | 해석 |
|---|---|---|
| RevenueCat REST API가 Offering을 반환 | 예, 상품 포함 | RevenueCat 구성이 올바릅니다. |
| App Store Connect에 상품이 존재 | 예, 대시보드에 표시됨 | 상품이 생성되어 있습니다. |
SDK에 offerings.current가 존재 | 예 | SDK가 Offering 구성을 정상적으로 가져왔습니다. |
availablePackages 개수 | 0 | StoreKit이 Product ID를 확인하지 못했습니다. |
| Xcode에 StoreKit Configuration 파일 설정됨 | 아니요 | 앱이 로컬 StoreKit이 아닌 Apple 샌드박스를 사용 중입니다. |
모든 행이 이 패턴과 일치한다면, StoreKit 상품 활성화가 문제입니다.
해결 단계
1단계: App Store Connect에 바이너리 업로드하기
인앱 구매 Product ID를 참조하는 빌드를 Xcode 또는 CI/CD로 제출합니다.
- Xcode에서 앱을 아카이브합니다(Product → Archive).
- Distribute App → App Store Connect를 통해 App Store Connect에 업로드합니다.
- 또는
xcodebuild나 Fastlane을 사용해 업로드합니다. - Apple이 빌드를 처리할 때까지 기다립니다(보통 15~30분, 더 오래 걸릴 수 있음).
빌드를 심사용으로 제출할 필요는 없습니다. App Store Connect에 업로드하기만 해도(TestFlight에서 사용 가능하게 만들기만 해도) 활성화를 트리거하기에 충분합니다.
2단계: Apple의 처리 기다리기
빌드가 업로드된 뒤에는 다음과 같이 진행됩니다.
- Apple이 바이너리를 처리하고 상품 식별자를 앱과 연결합니다.
- 15분에서 몇 시간까지 걸릴 수 있습니다.
- App Store Connect의 TestFlight에서 빌드 처리 상태를 확인할 수 있습니다.
- TestFlight에서 빌드가 "Ready to Test"로 표시되면 상품 확인이 시작됩니다.
3단계: 다시 테스트하기
빌드가 처리된 뒤에는 다음과 같이 진행합니다.
- TestFlight에서 빌드를 기기에 설치하거나, 샌드박스 계정이 있는 기기에서 Xcode로 실행합니다.
Purchases.shared.offerings()를 다시 호출합니다.- 이제
availablePackages가 채워져 있어야 합니다.
대안: 즉시 테스트하려면 StoreKit Configuration File 사용하기
바이너리 활성화를 기다리지 않고 지금 바로 테스트해야 한다면 다음과 같이 진행합니다.
- Xcode에서 StoreKit Configuration File을 생성합니다(File → New → File → StoreKit Configuration File).
- App Store Connect와 정확히 동일한 Product ID로 상품을 추가합니다.
- Product → Scheme → Edit Scheme → Run → Options로 이동합니다.
- StoreKit Configuration 파일을 선택합니다.
- 로컬 구성을 사용해 상품이 즉시 확인됩니다.
문제 8: iOS에서 "No Subscriptions Available"(Android에서 넘어온 경우)
증상
RevenueCat 구성(상품, Offering, Package, API 키)을 모두 완료했는데, iOS에서 테스트할 때 앱에 다음과 같은 오류가 표시됩니다.
Error: no subscriptions available. Try again later.이는 특히 Android/Google Play 경험이 있고 iOS를 처음 설정하는 개발자에게 흔히 나타납니다. RevenueCat 쪽은 올바르게 구성되어 있지만 Apple 쪽의 무언가가 상품 로드를 막고 있는 것입니다.
가장 흔한 원인: Paid Applications Agreement
iOS를 처음 다루는 개발자에게 가장 큰 원인은 Paid Applications Agreement가 서명되지 않았거나 미완성인 경우입니다. Google Play와 달리 Apple은 어떤 인앱 구매 상품이든 동작하기 전에, 샌드박스에서조차 사업자 정보와 은행 정보를 완성하도록 요구합니다.
이를 해결하려면 다음과 같이 진행합니다.
- App Store Connect로 이동합니다.
- Business(이전 명칭 "Agreements, Tax, and Banking")로 이동합니다.
- 페이지 상단에 배너나 경고가 있는지 확인합니다.
- 다음 섹션을 완성합니다.
- Paid Applications Agreement: Active 상태로 표시되어야 합니다. "Pending"이나 "New"로 표시되면 클릭하여 약관에 동의합니다.
- Banking Information: 은행 계좌 정보를 추가합니다.
- Tax Forms: 필요한 세금 양식을 작성합니다(미국은 W-9, 미국 외 개발자는 W-8BEN).
- Apple이 정보를 검증할 때까지 최대 24~48시간을 기다립니다(은행 정보 검증에는 시간이 걸릴 수 있습니다).
그 밖의 원인(Android 개발자가 놓치기 쉬운 iOS 특유의 함정)
Paid Applications Agreement가 이미 활성화되어 있다면, Android에는 대응되는 항목이 없는 iOS 특유의 요구 사항 체크리스트를 따라가 보세요.
| # | iOS 요구 사항 | Android 대응 항목 | 확인 방법 |
|---|---|---|---|
| 1 | Paid Applications Agreement | 대응 항목 없음(Google은 판매자 계정만 필요). | App Store Connect → Business에서 "Active"로 표시되어야 함. |
| 2 | StoreKit Configuration File(시뮬레이터 테스트용) | 대응 항목 없음(Google Play Billing은 Play Store가 있는 에뮬레이터에서 동작). | Xcode → Product → Scheme → Edit Scheme → Run → Options → StoreKit Configuration. |
| 3 | App Store Connect에 업로드된 바이너리(샌드박스용) | 대응 항목: Internal Testing 트랙에 게시된 앱. | App Store Connect → TestFlight에서 처리된 빌드 확인. |
| 4 | 샌드박스 테스터 계정 | 대응 항목: Play Console의 License Tester. | App Store Connect → Users and Access → Sandbox Testers. |
| 5 | In-App Purchase Key(SDK v5 이상/StoreKit 2용) | 대응 항목: 서비스 계정 JSON 키. | RevenueCat Dashboard → Apps & providers → App Store → In-App Purchase Key. |
| 6 | bundle ID 일치 | 대응 항목: 패키지 이름 일치. | Xcode target → General → Bundle Identifier가 RevenueCat → Apps & providers와 일치해야 함. |
흔한 문제 빠른 참조
| 문제 | 가능한 원인 | 해결 방법 |
|---|---|---|
| "No subscriptions available"(iOS 첫 설정) | Paid Applications Agreement 미서명 또는 은행 정보 미완성. | App Store Connect → Business로 이동합니다. Paid Applications Agreement, 은행 정보, 세금 양식을 완성합니다. 모든 섹션이 Active/완료 상태로 표시되어야 합니다. 검증에 최대 48시간을 기다립니다. 위의 상세 가이드를 참고하세요. |
RevenueCat API는 동작하지만 availablePackages가 비어 있음 |
StoreKit이 아직 Product ID를 활성화하지 않음(바이너리 미업로드). | App Store Connect에 빌드를 업로드합니다(TestFlight 용도만으로도 가능). Apple이 처리할 때까지 기다립니다(15분에서 몇 시간). 빌드가 "Ready to Test"가 되면 StoreKit이 상품 확인을 시작합니다. 위의 상세 가이드를 참고하세요. |
| "None of the products could be fetched" | RevenueCat의 Product ID가 스토어와 일치하지 않음. | App Store Connect 또는 Google Play Console에서 Product ID를 복사합니다. RevenueCat 대시보드 → Product Catalog → Products에서 각 ID가 대소문자까지 정확히 일치하는지 확인합니다. 끝에 공백이 하나만 있어도 불일치가 발생합니다. |
| "None of the products could be fetched" | 스토어에서 상품이 승인되지 않았거나 활성 상태가 아님. | iOS의 경우 상품이 Apple의 승인을 받았거나 StoreKit Configuration File로 테스트되어야 합니다. Android의 경우 Google Play Console에서 상품이 Active 상태이고 앱이 최소한 Internal Testing에 게시되어 있어야 합니다. |
| "None of the products could be fetched" | 플랫폼에 맞지 않는 API 키. | iOS 앱은 appl_로 시작하는 API 키를, Android 앱은 goog_로 시작하는 키를 사용해야 합니다. Purchases.configure() 호출을 확인하고 RevenueCat 대시보드 → API keys에서 키를 확인하세요. |
| offerings 객체가 비어 있음 | Offering에 Package가 추가되지 않음. | RevenueCat 대시보드 → Product Catalog → Offerings → 해당 Offering에서 Edit을 클릭하여 Package를 추가합니다(예: $rc_monthly, $rc_annual). 각 Package에는 최소 하나의 상품이 연결되어야 합니다. |
| offerings 객체가 비어 있음 | 기본 Offering 미설정. | Product Catalog → Offerings에서 주요 Offering에 "Current" 배지가 표시되는지 확인합니다. 표시되지 않으면 해당 Offering을 클릭하고 Make Current를 선택합니다. offerings.current 속성은 기본 Offering을 반환합니다. |
| offerings 객체가 비어 있음 | 상품은 연결되어 있지만 현재 플랫폼용 상품이 누락됨. | 각 Package에는 테스트 중인 플랫폼에 맞는 상품이 필요합니다. iOS를 테스트한다면 App Store 상품을, Android를 테스트한다면 Google Play 상품을 연결합니다. Offering 편집기에서 각 Package의 상품 목록을 확인하세요. |
| "offerings.current"가 nil 반환 | 코드에서 잘못된 Offering 식별자 사용. | offerings.offering(identifier:)를 사용한다면 식별자가 RevenueCat 대시보드와 일치하는지 확인합니다. 기본 Offering의 경우 대신 offerings.current를 사용하세요. 식별자 문자열에 오타가 없는지 확인합니다. |
| 상품 가격이 $0.00로 표시됨 | 스토어가 가격 정보를 가져오지 못함. | 보통 스토어가 상품을 확인하지 못한다는 뜻입니다. 상품이 스토어에 존재하고 활성 상태인지 확인합니다. iOS에서는 StoreKit Configuration에 적절한 가격이 포함되어 있는지 확인합니다. Android에서는 상품에 가격이 설정된 기본 요금제(base plan)가 있는지 확인하세요. |
| Offering 가져오기가 멈추거나 시간 초과됨 | 네트워크 연결 문제. | 기기의 인터넷 연결을 확인합니다. VPN이나 프록시를 일시적으로 비활성화합니다. api.revenuecat.com이 방화벽에 차단되지 않았는지 확인합니다. 일시적 실패에 대비해 앱에 지수 백오프를 적용한 재시도 로직을 추가하세요. |
| Offering 가져오기가 멈추거나 시간 초과됨 | 가져오기 전에 RevenueCat SDK가 구성되지 않음. | Offering을 가져오기 전에 Purchases.configure()가 호출되는지 확인합니다. iOS에서는 AppDelegate.application(_:didFinishLaunchingWithOptions:)에서, Android에서는 Application.onCreate()에서 구성하세요. |
| 스토어 업데이트 후 상품이 사라짐 | SDK 캐시가 오래된 데이터를 반환함. | Purchases.shared.invalidateCustomerInfoCache()를 호출하여 캐시를 비웁니다. 그런 다음 Offering을 다시 가져옵니다. 문제가 지속되면 앱을 강제 종료한 뒤 다시 엽니다. |
| 대시보드에 "Configuration warning" 표시 | Offering의 상품이 존재하지 않는 스토어 상품을 참조함. | RevenueCat 대시보드로 이동하여 구성 경고를 확인합니다. 각 Product ID를 스토어와 대조합니다. App Store Connect 또는 Google Play Console에 더 이상 존재하지 않는 상품은 제거하거나 업데이트하세요. |
| Android 에뮬레이터에서 Offering이 비어 있음 | 에뮬레이터에 Google Play Store 또는 결제 지원이 없음. | 테스트에는 실제 Android 기기를 사용하거나, Google Play Store가 설치된("Google APIs"만이 아닌) 에뮬레이터를 사용하세요. 라이선스가 부여된 테스터 Google 계정으로 로그인합니다. 로컬 디버그 빌드도 테스트 트랙에 업로드해야 할 수 있습니다. 커뮤니티 토론을 참고하세요. |
| 상품이 "READY_TO_SUBMIT" 상태임 | App Store Connect에서 상품이 생성되었지만 심사용으로 제출된 적이 없음. | 인앱 구매 상품은 Apple 심사를 위해 앱 버전과 함께 제출해야 합니다. 상품 메타데이터(가격, Localization, 스크린샷)를 모두 완성한 뒤 새 앱 버전에 추가하여 심사용으로 제출합니다. 테스트에는 StoreKit Configuration 파일이나 샌드박스 계정을 사용하세요. 위의 상세 가이드를 참고하세요. |
| SampleCat에서는 Offering이 동작하지만 내 앱에서는 안 됨 | 앱의 Purchases.configure() 호출에 잘못된 API 키 사용. |
SampleCat과 정확히 동일한 공개 API 키를 사용하는지 확인합니다. RevenueCat Dashboard → Project Settings → API Keys를 확인하세요. iOS는 appl_ 접두사를, Android는 goog_ 접두사를 사용하는지 확인합니다. 위의 상세 가이드를 참고하세요. |
| SampleCat에서는 Offering이 동작하지만 내 앱에서는 안 됨 | SDK가 구성되지 않았거나 너무 늦게 구성됨. | Offering을 가져오기 전에 Purchases.configure()가 AppDelegate.didFinishLaunching(iOS) 또는 Application.onCreate()(Android)에서 호출되는지 확인합니다. 디버그 로그를 활성화하여 초기화 순서를 확인하세요. |
| SampleCat에서는 Offering이 동작하지만 내 앱에서는 안 됨 | iOS 시뮬레이터에 StoreKit Configuration 파일 누락. | SampleCat에는 StoreKit Configuration 파일이 포함되어 있습니다. 직접 만든 프로젝트에는 없을 수 있습니다. Product → Scheme → Edit Scheme → Run → Options로 이동하여 StoreKit Configuration을 설정합니다. 또는 샌드박스 계정이 있는 실기기에서 테스트하세요. |
| TestFlight에서는 Offering이 동작하지만 Xcode 빌드에서는 안 됨 | 적절한 샌드박스 설정 없이 StoreKit Configuration이 "None"으로 설정됨. | StoreKit Configuration이 "None"인 상태로 Xcode에서 실행하면 Apple 샌드박스를 사용합니다. 기기에 샌드박스 테스터로 로그인되어 있는지 확인하세요. 또는 로컬 테스트를 위해 일치하는 Product ID로 StoreKit Configuration 파일을 연결합니다. 커뮤니티 토론을 참고하세요. |
| App Store 심사 중 Offering 시간 초과 | Apple 심사 환경에 네트워크 제한이 있음. | Apple 심사 인프라의 일시적 문제입니다. 앱이 Offering 로드 실패를 매끄럽게 처리하도록 하세요(빈 화면이 아닌 재시도 버튼 표시). 앱을 다시 심사용으로 제출합니다. 커뮤니티 토론을 참고하세요. |
Expo/React Native: getProducts()가 빈 값 반환 |
development build 대신 Expo Go 사용. | Expo Go는 RevenueCat 같은 네이티브 모듈을 지원하지 않습니다. EAS Build로 development build를 생성하세요. react-native-purchases가 올바르게 링크되어 있는지 확인합니다. Android의 경우 앱을 테스트 트랙에 업로드해야 합니다. 커뮤니티 토론을 참고하세요. |
Offering 문제 디버깅 체크리스트
- ☐ RevenueCat 대시보드에 상품이 존재한다.
- ☐ Product ID가 스토어와 대소문자까지 정확히 일치한다.
- ☐ Offering에 상품이 연결된 Package가 있다.
- ☐ 기본 Offering이 설정되어 있다.
- ☐ bundle ID / 패키지 이름이 일치한다.
- ☐ API 키가 플랫폼에 맞다(appl_ 또는 goog_).
- ☐ 네트워크 연결을 사용할 수 있다.
- ☐ 상품이 "Ready to Submit"이 아니라 승인됨(iOS) 또는 Active(Android) 상태다.
- ☐ 디버그 로깅이 활성화되어 있다.
- ☐ 유효한 자격 증명(샌드박스 또는 Test Store)으로 테스트하고 있다.