페이월 구성 문제
0:06:00페이월 문제는 보통 화면 표시 문제, 상품 누락, 구매 버튼 작동 불능과 관련이 있습니다. 이 단계에서는 흔히 발생하는 페이월 구성 문제를 다룹니다.
문제 1: 페이월이 표시되지 않음
증상
- 페이월 화면이 나타나지 않습니다.
- 페이월이 있어야 할 자리에 빈 화면이 표시됩니다.
- 페이월을 표시하려 할 때 크래시가 발생합니다.
일반적인 원인
- 게시된 페이월 없음: 페이월이 초안 상태로만 존재하고 게시되지 않았습니다.
- RevenueCatUI 의존성 누락: UI 라이브러리를 import하지 않았습니다.
- 페이월이 Offering에 연결되지 않음: Offering에 연결된 페이월이 없습니다.
- 잘못된 Offering 요청: 페이월이 없는 Offering을 코드에서 요청합니다.
해결 단계
1단계: 페이월이 게시되었는지 확인
- RevenueCat 대시보드로 이동합니다.
- Paywalls로 이동합니다.
- 페이월 상태를 확인합니다.
- Draft: 아직 라이브 상태가 아닙니다.
- Published: 라이브 상태이며 사용할 수 있습니다.
- 아직 초안 상태라면 Publish를 클릭합니다.
2단계: RevenueCatUI 의존성 확인
iOS (SPM):
swift
// 코드에서 다음을 import할 수 있는지 확인합니다.
import RevenueCat
import RevenueCatUI // ← 페이월을 사용하려면 반드시 필요합니다iOS (CocoaPods):
ruby
pod 'RevenueCat'
pod 'RevenueCatUI' # ← 이 줄을 추가합니다Android (Gradle):
kotlin
// purchases만 사용하지 말고 purchases-ui를 사용합니다
implementation("com.revenuecat.purchases:purchases-ui:10.8.0")3단계: 페이월이 Offering에 연결되었는지 확인
- RevenueCat 대시보드에서 Paywalls로 이동합니다.
- 해당 페이월을 클릭합니다.
- Offering 필드에 올바른 offering 식별자가 표시되는지 확인합니다.
- 설정되어 있지 않다면 편집하여 offering을 선택합니다.
4단계: 코드 구현 확인
iOS (SwiftUI):
swift
import RevenueCatUI
struct ContentView: View {
@State private var showPaywall = false
var body: some View {
Button("Show Paywall") {
showPaywall = true
}
.presentPaywallIfNeeded(
requiredEntitlementIdentifier: "premium",
isPresented: $showPaywall
)
}
}Android (Compose):
kotlin
import com.revenuecat.purchases.ui.revenuecatui.Paywall
@Composable
fun MyScreen() {
var showPaywall by remember { mutableStateOf(false) }
if (showPaywall) {
Paywall(
onDismiss = { showPaywall = false }
)
}
}문제 2: 페이월에 상품이 표시되지 않음
증상
- 페이월은 표시되지만 상품 카드가 비어 있습니다.
- 가격이 $0.00 또는 빈 값으로 표시됩니다.
- 상품 제목이나 설명이 누락됩니다.
일반적인 원인
- 패키지 식별자 불일치: 페이월이 다른 패키지 식별자를 기대합니다.
- 상품을 사용할 수 없음: 스토어 상품을 가져올 수 없습니다.
- 현지화 누락: 사용자 로캘에 맞는 상품 정보가 구성되지 않았습니다.
- 비어 있는 Offering: Offering에 상품이 없습니다.
해결 단계
1단계: 패키지 식별자가 일치하는지 확인
- Paywall Editor에서 어떤 패키지 식별자가 사용되는지 확인합니다.
- 흔히 쓰이는 패키지 식별자는 다음과 같습니다.
$rc_monthly$rc_annual$rc_lifetime
- Product Catalog → Offerings에서 패키지가 이 식별자를 사용하는지 확인합니다.
- 사용자 지정 식별자를 사용한다면 페이월을 일치하도록 업데이트합니다.
2단계: 상품 가져오기 테스트
swift
// 상품이 정상적으로 가져와지는지 테스트합니다
let offerings = try await Purchases.shared.offerings()
if let offering = offerings.current {
for package in offering.availablePackages {
print("Package: \(package.identifier)")
print(" Product: \(package.storeProduct.localizedTitle)")
print(" Price: \(package.storeProduct.localizedPriceString)")
}
} else {
print("No current offering")
}이 코드는 상품을 정상적으로 출력하는데 페이월에는 상품이 표시되지 않는다면, 페이월 구성 문제입니다.
3단계: 상품 현지화 확인
iOS의 경우 (App Store Connect):
- App Store Connect에서 해당 상품으로 이동합니다.
- Localization 섹션을 확인합니다.
- 사용자의 언어 및 지역에 맞는 현지화를 추가합니다.
- 표시 이름과 설명을 입력합니다.
Android의 경우 (Google Play Console):
- Google Play Console에서 해당 상품으로 이동합니다.
- Translations를 확인합니다.
- 대상 언어에 대한 번역을 추가합니다.
문제 3: 구매 버튼이 작동하지 않음
증상
- 구매 버튼을 눌러도 아무 일도 일어나지 않습니다.
- 버튼을 누르면 로딩이 표시되지만 끝나지 않습니다.
- 구매를 시도할 때 오류가 나타납니다.
일반적인 원인
- 구매 델리게이트 미설정: 구매 완료를 처리하는 핸들러가 없습니다.
- Entitlement 확인 실패: 사용자가 이미 entitlement를 보유하고 있지만 UI에 반영되지 않습니다.
- 트랜잭션이 완료되지 않음: 트랜잭션을 자동으로 완료하지 않도록 SDK가 구성되어 있습니다.
- 네트워크 문제: 스토어 서버와 통신할 수 없습니다.
해결 단계
1단계: 구매 콜백 구현
iOS:
swift
.presentPaywallIfNeeded(
requiredEntitlementIdentifier: "premium",
purchaseCompleted: { customerInfo in
// 구매 성공 처리
print("Purchase completed! Access granted.")
},
restoreCompleted: { customerInfo in
// 복원 처리
print("Restore completed!")
}
)Android:
kotlin
Paywall(
onPurchaseCompleted = { customerInfo ->
// 구매 성공 처리
Log.d("Paywall", "Purchase completed!")
},
onRestoreCompleted = { customerInfo ->
// 복원 처리
Log.d("Paywall", "Restore completed!")
},
onDismiss = { showPaywall = false }
)2단계: 자동 완료가 활성화되었는지 확인
SDK 구성에서 트랜잭션이 자동으로 완료되는지 확인합니다.
swift
// iOS - 기본값이므로 별도 구성이 필요 없습니다
Purchases.configure(withAPIKey: "appl_xxx")kotlin
// Android - 이 설정이 되어 있는지 확인합니다
val builder = PurchasesConfiguration.Builder(this, "goog_xxx")
Purchases.configure(
builder
.purchasesAreCompletedBy(PurchasesAreCompletedBy.REVENUECAT) // ← 중요!
.build()
)3단계: 디버그 로깅 활성화
구매 버튼을 눌렀을 때 무슨 일이 일어나는지 확인합니다.
swift
// iOS
Purchases.logLevel = .debugkotlin
// Android
Purchases.logLevel = LogLevel.DEBUG흔한 문제 빠른 참조
| 문제 | 가능한 원인 | 해결 방법 |
|---|---|---|
| 페이월이 표시되지 않음 | 페이월이 아직 초안 상태입니다. | RevenueCat 대시보드 → Paywalls에서 상태 열을 확인합니다. Draft로 표시되면 페이월을 열고 Publish를 클릭합니다. 게시된 페이월만 SDK에 전달됩니다. |
| 페이월이 표시되지 않음 | RevenueCatUI 의존성이 추가되지 않았습니다. | iOS는 SPM이나 CocoaPods로 RevenueCatUI를 추가합니다. Android는 build.gradle에 com.revenuecat.purchases:purchases-ui를 추가합니다. 메인 SDK에는 페이월 UI 컴포넌트가 포함되어 있지 않습니다. |
| 페이월이 표시되지 않음 | 페이월이 Offering에 연결되지 않았습니다. | RevenueCat 대시보드 → Paywalls에서 페이월을 열고 Offering 필드를 확인합니다. 올바른 offering 식별자를 선택합니다. 페이월을 표시하려면 반드시 offering에 연결되어 있어야 합니다. |
| 맞춤 디자인 대신 기본/대체 페이월이 표시됨 | SDK 버전이 Paywalls v2를 지원하지 않습니다. | 최신 SDK 버전으로 업데이트합니다. Paywalls v2는 purchases-ios v5.x 이상과 purchases-android v8.x 이상이 필요합니다. 의존성 버전을 확인하고 필요하면 업데이트합니다. |
| 맞춤 디자인 대신 기본/대체 페이월이 표시됨 | "default"라는 이름의 페이월이 충돌을 일으킵니다. | RevenueCat 대시보드에서 페이월 이름을 "default"가 아닌 다른 이름으로 변경합니다. "default"라는 이름은 SDK 내부 로직과 충돌하여 대체 동작을 유발할 수 있습니다. 커뮤니티 논의를 참고하세요. |
| 맞춤 디자인 대신 기본/대체 페이월이 표시됨 | Paywall Editor에서 문자열 현지화가 누락되었습니다. | 디버그 로그에서 "Missing string localization for property" 경고를 확인합니다. Paywall Editor에서 사용자 로캘에 맞는 모든 텍스트 필드가 입력되어 있는지 확인합니다. 현지화가 누락되면 유효성 검사 오류가 발생하여 대체 페이월이 표시됩니다. |
| 페이월의 상품 카드가 비어 있음 | 페이월과 offering 간에 패키지 식별자가 일치하지 않습니다. | Paywall Editor에서 어떤 패키지 식별자가 사용되는지 확인합니다(예: $rc_monthly, $rc_annual). Product Catalog → Offerings에서 패키지가 동일한 식별자를 사용하는지 확인합니다. 식별자가 일치하지 않으면 상품 카드가 비어 있게 됩니다. |
| 페이월의 상품 카드가 비어 있음 | 사용자 스토어프런트에서 상품을 사용할 수 없습니다. | 일부 지역에서는 상품을 사용할 수 없을 수 있습니다. App Store Connect나 Google Play Console에서 테스터 국가에 대한 상품 사용 가능 여부를 확인합니다. 필요하면 누락된 지역을 추가합니다. |
| 가격이 $0.00 또는 빈 값으로 표시됨 | 스토어가 상품 가격을 확인하지 못합니다. | 스토어에서 상품이 활성 상태이고 가격이 구성되어 있는지 확인합니다. StoreKit Configuration을 사용하는 iOS에서는 각 상품에 가격과 로캘이 설정되어 있는지 확인합니다. Android에서는 기본 요금제에 가격이 설정되어 있는지 확인합니다. |
| 구매 버튼을 눌러도 아무 일도 일어나지 않음 | 트랜잭션이 자동으로 완료되지 않습니다. | 맞춤 구매 처리를 사용한다면 Purchases.shared.purchase(package:)를 호출하고 완료를 처리하고 있는지 확인합니다. RevenueCatUI를 사용하면 구매가 자동으로 처리됩니다. finishTransactions가 false로 설정되어 있지 않은지 확인합니다. |
| 구매 버튼을 눌러도 아무 일도 일어나지 않음 | 사용자가 이미 활성 entitlement를 보유하고 있습니다. | 사용자가 이미 상품을 보유하고 있으면 SDK가 조용히 성공 처리할 수 있습니다. 페이월을 표시하기 전에 customerInfo.entitlements.active를 확인합니다. Purchases.shared.getCustomerInfo()로 현재 entitlement 상태를 확인합니다. |
| 페이월의 복원 버튼이 작동하지 않음 | 해당 계정에 복원할 이전 구매 내역이 없습니다. | 복원은 스토어 계정에 이전 구매 내역이 있을 때만 작동합니다. 샌드박스 테스트에서는 원래 구매를 진행한 동일한 샌드박스 테스터를 사용합니다. 커뮤니티 논의를 참고하세요. |
| 페이월이 나타난 직후 바로 닫힘 | 사용자의 entitlement 확인이 통과하여 자동 닫힘이 발생합니다. | entitlement에 따라 자동으로 닫히도록 PaywallView나 PaywallDialog를 사용한다면, 사용자가 이미 활성 구독을 보유하고 있을 때 페이월이 닫힙니다. 표시하기 전에 사용자의 entitlement 상태를 확인합니다. |
| 페이월이 Android와 iOS에서 다르게 보임 | 플랫폼별 렌더링 차이입니다. | 페이월 템플릿은 플랫폼마다 조금씩 다르게 렌더링될 수 있습니다. Paywall Editor의 Preview 기능을 사용하여 iOS와 Android 레이아웃을 모두 확인합니다. 플랫폼 간 일관성을 위해 필요하면 디자인 요소를 조정합니다. |
| 페이월에 "No available packages" 메시지가 표시됨 | Offering에 패키지가 없거나 기본 offering이 설정되지 않았습니다. | RevenueCat 대시보드 → Product Catalog → Offerings에서 offering에 패키지가 연결되어 있는지 확인합니다. 이를 기본 offering으로 설정합니다(Actions → Make Default). 상품이 승인(iOS) 또는 활성(Android) 상태인지 확인합니다. 커뮤니티 논의를 참고하세요. |
| "Subscription options are not available at the moment" | Current Offering이 설정되지 않았거나 offering에 패키지가 없습니다. | RevenueCat 대시보드 → Product Catalog → Offerings에서 하나의 offering이 Current로 표시되어 있는지 확인합니다. 상품이 연결된 패키지가 최소 하나는 있는지 확인합니다. 아래 상세 가이드를 참고하세요. |
| "Subscription options are not available at the moment" | 스토어 상품이 올바른 상태가 아닙니다. | iOS에서는 상품이 App Store Connect에서 Ready to Submit 또는 Approved 상태여야 합니다. Android에서는 상품이 Active 상태(Draft 아님)여야 하며, 앱이 최소한 비공개 테스트 트랙에 게시되어 있어야 합니다. |
| "Subscription options are not available at the moment" | 잘못된 API 키 또는 번들 ID 불일치입니다. | 플랫폼에 맞는 올바른 API 키를 사용하고 있는지 확인합니다(iOS에는 iOS 키, Android에는 Android 키). 앱의 번들 식별자가 RevenueCat 프로젝트 설정에 구성된 값과 일치하는지 확인합니다. |
| "Subscription options are not available at the moment" | 유료 앱 계약이 만료되었습니다(iOS). | App Store Connect → Agreements, Tax, and Banking에서 Paid Applications Agreement가 서명되어 활성 상태인지 확인합니다. 계약이 만료되었거나 없으면 모든 상품을 사용할 수 없게 됩니다. |
| 구매 후 PaywallView가 닫히지 않음(iOS) | 샌드박스 트랜잭션이 제대로 완료되지 않습니다. | 최신 RevenueCat SDK로 업데이트합니다. 새 샌드박스 계정으로 테스트하고 기기를 재시작합니다. Purchases.shared.delegate가 올바르게 설정되어 있는지 확인합니다. 디버그 로그에서 트랜잭션 처리 오류를 확인합니다. 커뮤니티 논의를 참고하세요. |
| RevenueCatUI.PaywallError 2 | 대시보드에 기본 offering이 구성되지 않았습니다. | RevenueCat 대시보드에서 offering을 선택하고 Actions의 점 세 개 아이콘 → Make Default를 클릭합니다. offering에 상품이 포함된 유효한 패키지가 있는지 확인합니다. 페이월을 다시 게시합니다. 커뮤니티 논의를 참고하세요. |
| 페이월이 존재하지 않는 패키지를 참조함 | 페이월 템플릿이 offering에 없는 패키지(예: $rc_annual)를 사용합니다. |
대시보드에서 누락된 패키지를 offering에 추가하거나, 페이월 디자인에서 해당 패키지 컴포넌트를 제거합니다. Android는 iOS보다 이 규칙을 더 엄격하게 적용합니다. 커뮤니티 논의를 참고하세요. |
페이월 문제 해결 빠른 체크리스트
- RevenueCatUI 의존성을 추가했습니다.
- 페이월을 게시했습니다(초안 아님).
- 페이월을 offering에 연결했습니다.
- 페이월과 offering 간에 패키지 식별자가 일치합니다.
- 상품을 가져올 수 있습니다(offerings API로 테스트).
- 사용자 로캘에 맞는 상품 현지화를 구성했습니다.
- 구매 완료 콜백을 구현했습니다.
- 트랜잭션 자동 완료를 활성화했습니다.
- 디버그 로깅을 활성화했습니다.
- 유효한 스토어 구성으로 테스트하고 있습니다.
"Subscription options are not available at the moment"
이 오류는 SDK가 페이월에 표시할 구매 가능한 상품을 하나도 가져오지 못할 때 나타납니다. 전체 메시지는 다음과 같습니다. "Subscription options are not available at the moment. Please check your RevenueCat configuration."
근본 원인 (가능성이 높은 순서)
- Current Offering이 구성되지 않음: RevenueCat 대시보드 → Product Catalog → Offerings로 이동합니다. 하나의 offering이 Current로 표시되어 있는지 확인합니다. current로 설정된 offering이 없으면 SDK가 빈 offering을 반환하여 페이월이 상품을 렌더링할 수 없습니다.
- Offering에 패키지나 상품이 없음: offering은 존재하지만 연결된 패키지가 없거나, 패키지에 스토어 상품이 연결되어 있지 않습니다. offering을 열고 각 패키지에 유효한 상품 식별자가 있는지 확인합니다.
- 스토어 상품을 사용할 수 없음:
- iOS: 상품이 App Store Connect에서 "Ready to Submit" 또는 "Approved" 상태여야 합니다. "Missing Metadata"나 "In Review" 상태의 상품은 가져올 수 없습니다.
- Android: 상품이 Google Play Console에서 Active 상태(Draft 아님)여야 합니다. 앱이 최소한 비공개 테스트 트랙에 게시되어 있어야 합니다.
- 잘못된 API 키 또는 번들 ID 불일치: 플랫폼에 맞는 올바른 공개 API 키를 사용하고 있는지 확인합니다(iOS에는 iOS 키, Android에는 Android 키). 잘못된 프로젝트나 잘못된 플랫폼의 키를 사용하는 것이 흔한 실수입니다. 앱의 번들 식별자가 RevenueCat에 구성된 값과 정확히 일치하는지도 확인합니다.
- 유료 앱 계약 만료(iOS): App Store Connect → Agreements, Tax, and Banking에서 Paid Applications Agreement가 서명되어 활성 상태인지 확인합니다. 계약이 만료되면 모든 상품을 사용할 수 없게 됩니다.
- 전파 지연: App Store Connect의 새 상품은 전파되는 데 최대 24~48시간이 걸릴 수 있습니다. Google Play 상품은 보통 더 빠르지만, 최초 활성화 후 몇 시간이 걸릴 수 있습니다.
- 테스트 계정 미구성:
- iOS: 기기에서 샌드박스 Apple ID로 로그인되어 있어야 합니다(Settings → App Store → Sandbox Account). 시뮬레이터에서는 StoreKit Configuration 파일이 필요합니다.
- Android: 테스트 기기의 Google 계정이 Google Play Console → Settings → License testing에서 라이선스 테스터로 추가되어 있어야 합니다.
디버깅 단계
- 디버그 로깅을 활성화합니다.
Purchases.logLevel = .debug(iOS) 또는Purchases.logLevel = LogLevel.DEBUG(Android)를 사용합니다. - 로그 출력에서
"Offerings"를 찾습니다. 서버가 정확히 무엇을 반환했는지, 그리고 스토어에서 상품을 가져왔는지 보여 줍니다. - RevenueCat 대시보드 → Customers 페이지에서
app_user_id를 확인하여 고객 프로필이 존재하는지 확인합니다. - REST API를 통해 offering 가져오기를 직접 테스트하여 문제가 서버 측인지 클라이언트 측인지 구분합니다.
팁: 디버그 로그에
"No products found in store"가 표시되지만 대시보드 구성이 올바르게 보인다면, 문제는 거의 항상 RevenueCat 구성이 아니라 스토어 상품 사용 가능 여부나 테스트 계정 설정과 관련이 있습니다.