You spent weeks building your subscription app. Sandbox testing passed. Your TestFlight testers loved it. You hit Submit for Review — and two days later, Apple's review team sends it back with a cryptic message about a broken purchase flow.
This is one of the most demoralizing moments for an indie developer. The good news: the vast majority of IAP rejections are preventable, and most of them cluster around the same handful of fixable issues. This guide covers the real rejection vectors — the ones RevenueCat's own support team sees most often — and shows exactly how RevenueCat's SDK and dashboard features let you address them before review.
The Rejection Landscape
According to Apple's own App Review Center, over 40% of all rejections fall under Guideline 2.1 — App Completeness: something didn't work during the review itself. Source
For subscription apps, the three most common purchase-related failure points are:
- Products fail to load during the reviewer's session
- An error occurs during the purchase (often
STORE_PROBLEM) - Content is not unlocked after a successful purchase
Both Apple and Google also scrutinize paywalls for disclosure and UI compliance. Let's go through each vector and the corresponding fix.
Rejection Vector 1: Products Fail to Fetch
The problem
App Review runs in an environment separate from your Sandbox. If your in-app purchases are not in "Ready to Submit" status in App Store Connect — or if they were never explicitly submitted with your first binary — the reviewer will see an empty paywall and reject your app immediately.
The fix
Before submitting any build that includes IAP for the first time, verify:
- Every product you intend to use shows "Ready to Submit" status in App Store Connect.
- On the app version submission page, look for the option to include in-app purchases with the binary. This option only appears the first time you ship IAP. Don't skip it.

For existing apps adding new products: new products must reach "Approved" status via an independent product review before they will be purchasable on the live App Store. Source
RevenueCat dashboard check
The RevenueCat dashboard will show you if a product is missing or misconfigured in your Offering. Navigate to your Product Catalog → Offerings, verify all packages resolve to real products, and confirm that empty offerings are not being served to users.
Important: Empty offerings are almost always a configuration issue in App Store Connect or the Google Play Console, not a RevenueCat bug. Source
Rejection Vector 2: Missing Restore Purchases Button
The problem
Apple's App Review Guidelines require that all apps selling non-consumable or auto-renewable subscription products provide a visible Restore Purchases mechanism. Reviewers will test this. If it's missing or broken, your app gets rejected.
The fix with RevenueCat
The restorePurchases() method re-syncs all transactions from the user's Apple or Google account with their current RevenueCat App User ID:
// Swift — iOS
// Wire this to a "Restore Purchases" button in your UI
Button("Restore Purchases") {
Purchases.shared.restorePurchases { customerInfo, error in
if let error = error {
// Show error to user
print("Restore failed: \(error.localizedDescription)")
return
}
// Check if the expected entitlement is now active
if customerInfo?.entitlements["premium"]?.isActive == true {
// Unlock content
}
}
}
// Kotlin — Android
// Wire this to a "Restore Purchases" button in your UI
Purchases.sharedInstance.restorePurchases(
onSuccess = { customerInfo ->
if (customerInfo.entitlements["premium"]?.isActive == true) {
// Unlock content
}
},
onError = { error ->
// Show error to user
}
)
⚠️ Do not call
restorePurchases()programmatically on app launch. It can trigger OS-level sign-in prompts, which is itself a rejection reason. Only call it from an explicit user action (e.g., tapping a "Restore Purchases" button). If you need to programmatically sync purchases without user interaction, usesyncPurchases()instead. Source
Special case: Consumables and non-renewing subscriptions
Consumables and non-renewing subscriptions do not appear on the underlying store receipt after the transaction is consumed. RevenueCat can only restore them if you use a custom App User ID system (i.e., your users log in). Anonymous users cannot recover consumed purchases through receipt inspection alone. Source
If your app has lifetime or one-time unlocks, model them as non-consumable IAPs in App Store Connect and attach them to an entitlement in RevenueCat. This ensures they are always present on the receipt and can be restored reliably. Source
Rejection Vector 3: No Subscription Management Link
The problem
Apple requires apps with auto-renewable subscriptions to provide a way for users to manage (and cancel) their subscription from within the app. Google Play has a similar requirement. Reviewers will check this during the review process.
The fix: RevenueCat Customer Center
RevenueCat's Customer Center is a pre-built, configurable UI that handles subscription management in a few lines of code. It includes:
- Cancel subscription
- Request a refund (iOS)
- Restore missing purchases
- Change plans (iOS)
iOS — present Customer Center modally:
import RevenueCatUI
// In your settings or account screen
struct AccountView: View {
@State private var showCustomerCenter = false
var body: some View {
Button("Manage Subscription") {
showCustomerCenter = true
}
.presentCustomerCenter(isPresented: $showCustomerCenter)
}
}
Android — launch Customer Center:
Add the RevenueCatUI dependency to your build.gradle, then:
// In your settings or account Activity/Fragment
CustomerCenter.show(activity = this)
The Customer Center is configured entirely from the RevenueCat dashboard — no app update needed to adjust paths, feedback surveys, or promotional retention offers. Source
Rejection Vector 4: Paywall Metadata and Disclosure Issues
Paywalls are one of the most scrutinized areas of your app during review on both platforms. Source
Here are the four most common paywall rejection reasons and how to fix them:
4a. Full billed amount not clearly shown
If you normalize an annual price to a monthly equivalent (e.g., "$4.16/mo"), you must also display the full annual charge ($49.99/yr). Only showing the per-month breakdown can result in rejection.
Use RevenueCat's paywall variables: product.price_per_period or product.price_per_period_abbreviated to ensure the correct full price is always surfaced. Using product.price_per_month alone will not satisfy this requirement. Source
4b. Introductory offer not clearly disclosed
If you offer a free trial or discounted first period, the nature of that offer must be unambiguously clear on your paywall — because that's what the customer is actually signing up for.
Use product.offer_price and product.offer_period variables in your CTA or package description. India has particularly strict rules on offer disclosure and is a common source of region-specific rejections. Source
4c. No cancellation language
Both stores may flag paywalls that don't clearly tell users they can cancel. Add explicit copy such as: "Try free for 7 days, then $9.99/mo. Cancel anytime." Source
4d. Missing Terms & Privacy Policy
While Apple no longer requires T&C links directly on the paywall, they must be accessible somewhere in your app. Reviewers who can't easily find them may reject. The safest approach is to include both links directly on your paywall.
RevenueCat Paywalls automatically include Restore Purchases, Terms of Service, and Privacy Policy components, and these will be automatically localized. Source
Rejection Vector 5: Sandbox Testing Gaps
The problem
Purchases that work perfectly in RevenueCat's Test Store or even Apple Sandbox can still fail during App Review if your integration isn't complete. The most common traps:
- Using a Test Store API key in your release build. This will crash your app in production. Source
- Only testing with Test Store, never with real platform sandbox. Test Store doesn't exercise StoreKit, billing grace periods, or platform-specific edge cases.
- Not signing tax forms and banking agreements in App Store Connect / Google Play Console. Products will silently fail to fetch if these are missing. Source
The recommended testing workflow
RevenueCat recommends a three-phase approach before submitting: Source
| Phase | Environment | Goal |
|---|---|---|
| Development | RevenueCat Test Store | Fast iteration, verify entitlement logic |
| Pre-launch | Apple Sandbox / Google Play testing | End-to-end with real store APIs |
| Submission | Platform-specific API key only | Never submit with Test Store key |
Create an Apple Sandbox tester account in App Store Connect → Users and Access → Sandbox Testers, then add it to your device under:
- iOS 18+: Settings > Developer > Sandbox Apple Account
- iOS 13–17: Settings > App Store > Sandbox Account
⚠️ When testing in platform sandboxes, focus on validating the flow of purchases — not prices or metadata, which are often inaccurate in sandbox environments. That inconsistency is expected and not a sign of a broken integration. Source
Content must actually unlock
After a sandbox purchase completes, verify that the entitlement is active by checking CustomerInfo:
// Swift — check entitlement after purchase
Purchases.shared.getCustomerInfo { customerInfo, error in
if customerInfo?.entitlements["premium"]?.isActive == true {
// Show premium content
showPremiumContent()
}
}
// Kotlin — check entitlement after purchase
Purchases.sharedInstance.getCustomerInfo(
onSuccess = { customerInfo ->
if (customerInfo.entitlements["premium"]?.isActive == true) {
showPremiumContent()
}
},
onError = { error -> /* handle */ }
)
If your products aren't attached to entitlements in the RevenueCat dashboard, isActive will never return true, and your app will be rejected for failing to unlock content. Source
Rejection Vector 6: The STORE_PROBLEM Error
The problem
This is the most common RevenueCat error that causes unexplained rejections. It occurs when Apple's sandbox environment is down and cannot verify a purchase. Apple uses this error internally for multiple failure modes, so the message "There was a problem with the App Store" may appear to you or a reviewer with no other explanation. Source
What to do
- Check status.revenuecat.com for known Apple sandbox outages (these can last hours to days).
- If you can reproduce the purchase successfully on your own device, the issue is likely transient in the reviewer's environment.
- Re-submit the same binary. Some developers have had to resubmit up to a dozen times during major sandbox outages before getting approved.
- Optionally, include an explanation in your Review Notes field describing that this is a known Apple sandbox instability and the flow works correctly in production.
Pre-Submission Checklist
Use this alongside RevenueCat's official App Subscription Launch Checklist:
- [ ] All IAP products are "Ready to Submit" in App Store Connect / approved in Google Play Console
- [ ] In-app purchases are included with the first binary submission
- [ ] Tax forms, agreements, and banking info are complete on both platforms
- [ ] A visible "Restore Purchases" button calls
restorePurchases()— not wired to app launch - [ ] Subscription management is accessible in-app (use RevenueCat Customer Center)
- [ ] Paywall shows full billed amount, intro offer terms, and cancellation language
- [ ] Terms & Privacy Policy links are present and accessible
- [ ] Tested end-to-end with platform sandbox (not just Test Store) using a platform-specific API key
- [ ]
getCustomerInfo()/ entitlement check is called after purchase to unlock content - [ ] App is configured with the correct platform-specific API key — never the Test Store key — for the release build
Summary
Store rejections feel arbitrary, but most IAP rejections are systematic and fixable. RevenueCat's SDK handles the most error-prone parts of the purchase flow — sandbox/production environment detection, receipt validation, entitlement unlocking — automatically. The remaining rejections come down to UI disclosure and pre-submission hygiene.
Get the paywall copy right, wire up a restore button, add the Customer Center for subscription management, and run one full end-to-end test against a real platform sandbox before you submit. That combination eliminates the overwhelming majority of rejection risk for indie subscription apps.
Sources
- Apple App Store Rejections — RevenueCat Docs
- Getting Your Paywall Approved Through App Review
- Restoring Purchases
- Getting Subscription Status (CustomerInfo)
- App Subscription Launch Checklist
- Sandbox Testing Overview
- Apple App Store & TestFlight Sandbox Testing
- Configure Customer Center
- Integrating Customer Center on iOS
- Non-Subscription Purchases
- Displaying Products
- SDK Quickstart