What are IAP rejections?
Apple and Google have strict rules for payments Any app that sells digital goods or services must use the platform’s native billing system. Apple takes 30% (15% for small businesses). Google takes the same. There’s no way around this for digital content. Common rejection messages:- “Your app uses a payment mechanism other than in-app purchase”
- “The app does not include a restore purchases mechanism”
- “In-app purchase products are not available for purchase”
- “The purchase flow does not complete successfully”
Why this happens
Payment rules are non-negotiable App stores reject apps that try to bypass their billing systems or implement purchases incorrectly. Common mistakes:- Using Stripe or web checkout for digital goods
- Linking to external payment pages
- Missing restore purchases button
- Products not configured in App Store Connect or Google Play Console
- Sandbox testing not working
- No way to access paid features after purchase
- Mentioning prices without using StoreKit/Play Billing
How to fix it
Use RevenueCat for native payments
Despia integrates with RevenueCat for App Store and Google Play billing RevenueCat handles the complexity of native purchases across both platforms. You configure products once and Despia displays native paywalls.Configure products correctly
Products must exist in both App Store Connect and Google Play Console Before your app can sell anything, you need to create the products in each store’s developer console. For iOS (App Store Connect):- Go to App Store Connect > Your App > In-App Purchases
- Create products (subscriptions or one-time purchases)
- Add pricing and descriptions
- Submit for review
- Go to Google Play Console > Your App > Monetize > Products
- Create in-app products or subscriptions
- Add pricing and descriptions
- Activate the products
- Add your App Store and Play Store apps
- Import products from both stores
- Create offerings that group products
- Configure entitlements for access control
Implement restore purchases
This is required by both Apple and Google Users who reinstall your app or switch devices must be able to restore their purchases. Without a restore button, your app will be rejected.- Settings screen (most common)
- Paywall screen
- Account or profile screen
- Onboarding flow for returning users
Use web payments only for web
Stripe on web, RevenueCat on native If your app also runs as a web PWA, you can use Stripe there. But the native app must use in-app purchases. Use user agent detection to show the right payment flow:- Link to web checkout pages
- Show Stripe payment forms
- Mention “pay on our website”
- Display credit card input fields
Handle purchase webhooks
Verify purchases on your backend Client-side purchase confirmation is not enough. Users can manipulate app state. Always verify purchases server-side using RevenueCat webhooks. We’ve created easy-to-follow templates for handling RevenueCat webhooks correctly. The official docs can be confusing, so we simplified it. See: RevenueCat Webhooks Template Basic webhook handler:Handle the purchase callback
Update UI after successful purchase The Despia runtime callsonRevenueCatPurchase() when a purchase completes on the client side. Use this to update your UI, but always verify with your backend.
Test in sandbox mode
Both platforms have testing environments Before submitting, verify purchases work in sandbox mode. iOS Sandbox Testing:- Create sandbox tester in App Store Connect
- Sign out of App Store on device
- Make purchase in app (will prompt for sandbox login)
- Subscriptions renew quickly (monthly = 5 minutes)
- Add license testers in Google Play Console
- Upload to internal testing track
- Install via Play Store (not sideload)
- Purchases use test cards
- Products not showing: Check product IDs match exactly
- Purchase fails: Verify sandbox user is set up correctly
- Subscription doesn’t renew: Sandbox renewals have different timing
- “Cannot connect to App Store”: Network or configuration issue
Price display rules
Never hardcode prices Prices vary by region and can change. Always fetch prices from the store. Wrong:- Mentioning prices in screenshots
- Hardcoding currency symbols
- Showing prices that don’t match the store
Quick checklist
Configuration:- Products created in App Store Connect
- Products created in Google Play Console
- Products imported into RevenueCat
- Offerings configured in RevenueCat
- Entitlements mapped to products
- Paywall launches with
revenuecat://launchPaywall - Restore purchases button exists and works
getpurchasehistory://called on app launch- Webhook endpoint handles RevenueCat events (use our template)
- Cron job syncs subscription status (use our template)
- Access granted only after backend verification
- No Stripe/web payments in native app
- No links to external payment pages
- No hardcoded prices
- No mention of “pay on website”
- Sandbox purchases complete successfully
- Restore purchases finds previous purchases
- Entitlements unlock correct features
- Subscription renewal works in sandbox
Common rejection reasons
| Rejection | Fix |
|---|---|
| ”Uses external payment” | Replace Stripe with RevenueCat in native |
| ”No restore purchases” | Add restore button calling getpurchasehistory:// |
| ”Products not available” | Check App Store Connect / Play Console configuration |
| ”Purchase doesn’t complete” | Test in sandbox, check product IDs |
| ”Doesn’t unlock features” | Verify entitlement checks after purchase |
Still stuck?
If you keep getting rejected:- Test purchases in sandbox mode on a real device
- Verify products are approved in both stores
- Check RevenueCat dashboard for errors
- Contact support: support@despia.com with:
- Your rejection notice in full
- Screenshot of your paywall
- RevenueCat product configuration
- Whether sandbox testing works