App Rejected In-App Purchase Policy 3.1 — Fix IAP Issues
Guideline 3.1 is Apple's rulebook for payments inside apps. Every digital good, subscription, or premium feature must go through Apple's In-App Purchase system unless you qualify for one of the narrow exceptions. When Apple rejects your app under 3.1, the resolution often involves both code-level changes and App Store Connect configuration fixes, making it one of the most frustrating rejection categories to deal with.
This guide covers every common 3.1 rejection scenario with concrete fixes. Whether you are shipping your first subscription app or migrating to StoreKit 2, you will find the specific steps to resolve your rejection and avoid it next time.
Apple's Payment Rules in 2026
Before diving into specific rejection fixes, here is the current state of Apple's payment policies.
Apple still requires In-App Purchase for all digital goods and services consumed within an app. This includes subscriptions, premium content, virtual currencies, and feature unlocks. The standard commission remains 30% for the first year and 15% for developers in the App Store Small Business Program (under $1M in annual proceeds).
The landscape has shifted in two important ways since 2024:
EU (Digital Markets Act): Apps distributed in the EU can use alternative payment processors. You must opt into Apple's alternative terms, which carry a Core Technology Fee of EUR 0.50 per first annual install above 1 million. Most indie developers will not hit this threshold, but you need to evaluate the math for your specific situation.
US External Purchase Links: Following the Epic v. Apple ruling, apps in the US can link out to external payment methods using the StoreKit External Purchase Link API. Apple charges a 27% commission on purchases made through external links. You must use Apple's entitlement and disclosure sheet API — you cannot just drop a URL in your app.
What is still exempt: Apps that sell physical goods (e-commerce, food delivery), real-world services (ride-sharing, hotel booking), or person-to-person services do not need to use IAP. Reader apps (Netflix, Spotify) can link to their website for account management without paying commission, though they cannot sell subscriptions in-app without using IAP.
"We Cannot Locate the In-App Purchases"
This is the single most common 3.1 rejection. You will receive a message from App Review that looks like this:
Guideline 3.1.1 - Business - Payments - In-App Purchase
We found in our review that your app offers in-app purchases that can be purchased, but we were unable to locate the in-app purchases in App Store Connect.
This rejection means your app's binary references IAP product IDs that Apple cannot find or cannot activate. The fix is almost always in App Store Connect, not in your code.
Check Your IAP Product Status
Open App Store Connect, go to your app, and navigate to Monetization > In-App Purchases (or Subscriptions if applicable). Each product has a status:
- Ready to Submit — This is what you want. The product is configured and will be submitted with your app.
- Missing Metadata — The product is incomplete. You need to add a screenshot, description, or localization.
- Developer Action Needed — Apple has flagged an issue. Check the product details for a resolution note.
- Waiting for Review — Already submitted and pending review. This is fine.
- In Review — Currently being reviewed. This is fine.
If any product your app references is in "Missing Metadata" or "Developer Action Needed" status, Apple cannot see it during review. Fix the product metadata, set its availability, and ensure it moves to "Ready to Submit" before submitting your app binary.
The Timing Trap
IAP products must be in "Ready to Submit" status when you submit your app for review. A common mistake is creating the IAP products but not clicking Submit on them. In App Store Connect, you need to explicitly include IAP products in your app submission. When you submit your app for review, verify that all IAP products appear in the submission summary.
Another timing issue: if you create IAP products after submitting your app binary, they will not be included in the review. You need to either cancel the submission, add the products, and resubmit, or wait for the rejection and resubmit with the products properly attached.
Product IDs Do Not Match
Double-check that the product identifiers in your code match exactly what is configured in App Store Connect. This is case-sensitive. A typo like com.yourapp.premium_monthly versus com.yourapp.premiumMonthly will cause Product.products() to return an empty array, and Apple will report they cannot locate your IAPs.
Silent IAP Rejection Alongside App Rejection
This is a subtle trap that catches many developers. When your app gets rejected for any reason — metadata issues, missing screenshots, a privacy policy problem — your IAP products can be silently rejected at the same time.
There is no separate notification for this. You will see the primary rejection reason in Resolution Center, but if you check your IAP products in App Store Connect, some or all of them may have moved from "Waiting for Review" back to "Developer Action Needed" with no clear explanation.
How to Handle This
After any rejection, regardless of the stated reason:
- Go to Monetization > In-App Purchases and Monetization > Subscriptions in App Store Connect.
- Check the status of every product.
- If any product is in "Developer Action Needed," open it and re-save it (sometimes just re-saving triggers it back to a submittable state).
- Resubmit the IAP products along with your fixed app binary.
If you fix only the primary rejection reason and resubmit without checking your IAPs, you will get rejected again for 3.1.1 because the IAP products are stuck in a broken state.
StoreKit 2 Migration Pitfalls
StoreKit 2 simplified the purchase API significantly, but the migration from the original StoreKit introduces several 3.1-adjacent rejection risks.
Product.products() Returns Empty
The most common StoreKit 2 issue is calling Product.products(for:) and getting back an empty array. In development, this usually means your StoreKit configuration file is missing or misconfigured. In production, it means your product IDs do not match App Store Connect.
let products = try await Product.products(for: ["com.yourapp.pro_monthly"])
// products is empty — no error thrown, just an empty array
StoreKit 2 does not throw an error when products are not found. It silently returns an empty collection. Your app needs to handle this gracefully — show a "products unavailable" state rather than crashing or showing a blank paywall.
Transaction.currentEntitlements Returning Empty
After migrating to StoreKit 2, existing subscribers may lose access if you rely solely on Transaction.currentEntitlements. This happens because:
- The user purchased through original StoreKit, and the transaction history may not be immediately available in the StoreKit 2 API.
- You need to call
AppStore.sync()to force a sync of the user's transaction history. - On first launch after migration,
currentEntitlementsmay be empty until the sync completes.
// Force sync on first launch after StoreKit 2 migration
try await AppStore.sync()
for await result in Transaction.currentEntitlements {
// Now you should see existing purchases
}
Sandbox vs. Production Environments
StoreKit testing in Xcode uses a local .storekit configuration file. This file is completely separate from your App Store Connect configuration. Your product IDs, pricing, and subscription groups in the StoreKit config file can diverge from what is in App Store Connect without any warning.
Before submitting, verify that:
- Your
.storekitfile product IDs match App Store Connect exactly. - Subscription group names and levels match.
- You have tested on a physical device with a Sandbox Apple ID, not just in the Xcode StoreKit testing environment.
- You have tested on TestFlight, which uses the sandbox server environment (closest to production).
Receipt Validation
If you are validating receipts server-side, note that the App Store Server API (v2) uses JWS (JSON Web Signature) signed transactions, not the legacy verifyReceipt endpoint. The verifyReceipt endpoint still works but is deprecated. If your server validates receipts and your validation is failing silently, Apple may see a broken purchase flow and reject under 3.1.
External Payment Link Compliance
If you are implementing external payment links under the US entitlement or EU DMA provisions, there are strict requirements that trigger 3.1 rejections if not followed.
StoreKit External Purchase Link API
You must use Apple's ExternalPurchaseLink API from the StoreKit framework. You cannot simply add a URL or button that links to your website's payment page. The API enforces Apple's required disclosure sheet, which informs the user they are leaving the app and that Apple is not responsible for the external transaction.
import StoreKit
// Request the external purchase link
if let url = URL(string: "https://yourapp.com/subscribe") {
try await ExternalPurchaseLink.open(url: url)
}
Required Entitlement
Your app must have the com.apple.developer.storekit.external-purchase-link entitlement configured in your provisioning profile. Without this entitlement, any attempt to link to external payments will be rejected. You request this entitlement through your Apple Developer account, and Apple reviews the request separately.
Common Rejection Scenarios
- Linking to external payments without the entitlement — instant 3.1 rejection.
- Bypassing the disclosure sheet by opening a Safari link directly — 3.1 rejection.
- Not reporting external purchases back to Apple (required for commission calculation) — 3.1 rejection.
- Showing external payment options prominently while burying IAP options — 3.1 rejection. Apple requires that IAP options are presented at least as prominently as external options.
Catch rejection issues before Apple does
Upload your .ipa or .apk and get an instant compliance report with 45+ automated checks. Free, no signup required.
Scan your app freeBundle ID Mismatch Issues
IAP products are permanently tied to the bundle identifier they were created under. This creates several rejection scenarios that are hard to debug.
Changing Your Bundle ID
If you change your app's bundle ID for any reason — rebranding, moving to a new developer account, restructuring your app suite — all existing IAP products become orphaned. They are still tied to the old bundle ID and will not appear for the new one.
There is no way to transfer IAP products between bundle IDs. You must:
- Create new IAP products under the new bundle ID.
- Update all product IDs in your code.
- Handle migration for existing subscribers (they will still have active subscriptions under the old bundle ID).
Team ID Confusion
Your product ID format is typically com.teamname.appname.productname. The team ID in your Apple Developer account is separate from the team name in your bundle ID. Make sure you are referencing the correct bundle ID as it appears in App Store Connect, not the team ID from your developer portal.
Development vs. Production
If you use different bundle IDs for development and production builds (a common pattern), make sure your production binary uses the production bundle ID. A development build accidentally submitted for review will fail to locate IAP products because they are registered under the production bundle ID.
Check your build configuration in Xcode:
- Select your target.
- Go to Build Settings > Packaging > Product Bundle Identifier.
- Verify the Release configuration uses your production bundle ID.
Testing IAPs Before Submission
A thorough testing process eliminates nearly all 3.1 rejections. Use this checklist before every submission that involves IAP.
Sandbox Testing
- Create dedicated Sandbox tester accounts in App Store Connect under Users and Access > Sandbox Testers. Never test IAP with your real Apple ID.
- Sign into the Sandbox account on your test device under Settings > App Store > Sandbox Account.
- Test a complete purchase flow: browse products, initiate purchase, confirm, and verify the content or feature unlocks.
- Test Restore Purchases on a second device or after deleting and reinstalling the app.
- For subscriptions, test renewal. Sandbox uses accelerated renewal timelines (a monthly subscription renews every 5 minutes in sandbox).
- Test cancellation through Settings > Subscriptions and verify your app handles the expired state correctly.
- Test the interrupted purchase flow (dismiss the payment sheet mid-purchase).
StoreKit Testing in Xcode
- Use the
.storekitconfiguration file for fast, local testing without network dependency. - Simulate edge cases: failed transactions, ask-to-buy, revoked purchases, offer codes, and grace periods.
- Verify your app handles
Transaction.updatesfor background transaction changes.
TestFlight Testing
- TestFlight uses the sandbox server environment, which is the closest to production.
- Have at least one external tester verify the purchase flow on TestFlight before submitting for review.
- Verify that product IDs resolve correctly in the TestFlight environment (this confirms App Store Connect configuration is correct).
Final Verification Checklist
- All product IDs in code match App Store Connect exactly (case-sensitive).
- All IAP products are in "Ready to Submit" status.
- IAP products are included in the app submission.
- Receipt validation endpoint (if used) is pointing to the production URL, not sandbox.
- Your app handles empty product arrays gracefully (shows an error state, not a blank screen).
- Subscription group configuration matches between code and App Store Connect.
- Your app includes a "Restore Purchases" button accessible without a current subscription.
- Terms of service and privacy policy URLs are valid and accessible.
Getting IAP right requires attention to both the code side and the App Store Connect configuration side. Most 3.1 rejections come from configuration mismatches, not broken code. Take the time to verify your setup end-to-end before each submission, and you will avoid the back-and-forth with App Review that can delay your release by weeks.