IAP Sandbox Purchase Fails — Even With Sandbox Account

Sandbox IAP has five moving parts that must line up — device Apple ID, sandbox tester region, agreements, product status, StoreKit config. The diagnostic order.

You wire up your first in-app purchase, run the app on a real device (sandbox can’t be exercised in the simulator for real purchases), tap Buy, and either the purchase sheet appears then immediately errors out with “Your purchase could not be completed”, or the sheet doesn’t appear at all and you get “Cannot connect to iTunes Store,” or the prompt asks for an Apple ID password and rejects your sandbox tester’s credentials. Nothing changed in your code; the integration that worked yesterday is broken today.

Sandbox IAP has more wiring than the API surface suggests: the device’s signed-in Apple ID, the sandbox tester’s region and password state, the App Store Connect agreements, the IAP product status, and the StoreKit configuration file all need to line up. When any one of them drifts, the error message is usually too vague to point to the cause.

Common causes

Ordered by hit rate.

1. Real Apple ID is still signed in to App Store on the device

Pre-iOS 12 you had to sign out your real Apple ID in Settings → iTunes & App Store. iOS 12+ added a separate Sandbox Account section under Settings → App Store → Sandbox Account, but if neither is set, iOS uses your real ID for the purchase attempt, and it fails because real IDs can’t buy sandbox products.

How to spot it: Settings → App Store. Scroll to the Sandbox Account section (iOS 12+). If it’s empty or shows your real ID, this is the cause.

2. App Store Connect agreements, banking, or tax info incomplete

Until the Paid Apps agreement is signed, banking and tax forms are valid, and your status is Active, no IAP product — even sandbox — can transact. Apple silently returns “product not available” on requestProducts.

How to spot it: App Store Connect → Agreements, Tax, and Banking. Look for any row in Action Items or status other than Active. Missing W-9 / W-8BEN, expired bank info, or unsigned agreement amendments all block sandbox.

3. IAP product in “Missing Metadata” or unsubmitted state

You created the product, named it, but didn’t add a localization, screenshot for review, or pricing. The product never reaches a state where StoreKit can return it. Products.products(for:) returns an empty array for that ID.

How to spot it: App Store Connect → your app → In-App Purchases. Status must be Ready to Submit or Approved. Missing Metadata means the product isn’t queryable.

4. StoreKit configuration file doesn’t match production

You added a .storekit file for local testing. Xcode is using it instead of querying App Store Connect, and the IDs in the file diverged from production. Your code thinks a product exists; the real sandbox doesn’t have it.

How to spot it: Xcode → scheme settings → Run → Options → StoreKit Configuration. If a file is selected, StoreKit reads from there. Either clear the selection or sync the file’s product IDs with App Store Connect.

5. Bundle ID mismatch between IAP and build

Your IAP products are registered under com.acme.app but the build’s bundle ID is com.acme.app.dev or com.acme.app.staging. StoreKit only returns products registered under the running bundle ID.

How to spot it: Open Info.plist and compare CFBundleIdentifier against the App ID under which your IAP products were created in App Store Connect. Any mismatch (including case) breaks the lookup.

6. Sandbox tester is in the wrong region or has a stale password

Sandbox testers are region-locked. If you created the tester for US but the device’s region is set to Japan, the tester can’t buy. Stale passwords (sandbox passwords expire faster than production) also fail.

How to spot it: App Store Connect → Users and Access → Sandbox → Testers. Check region and last password reset date. Test by signing in via Settings → App Store → Sandbox Account first; if password fails there, reset it in App Store Connect.

7. Capability not enabled on App ID

In Apple Developer → Identifiers → your App ID → Capabilities, In-App Purchase must be checked. If it’s missing, the build can’t transact even though the products exist in App Store Connect.

How to spot it: Apple Developer portal → your App ID → look at the Capabilities list. If In-App Purchase is unchecked, that’s your problem.

Information to collect

  • The exact error message from the purchase prompt and from Console.app filtered to your bundle ID.
  • The sandbox tester email, region, and password reset date.
  • App Store Connect agreement / tax / banking status.
  • IAP product status (must be at least Ready to Submit).
  • Your build’s bundle ID and the App ID in Developer Portal.
  • Whether a .storekit configuration file is in use.

Shortest path to fix

Step 1: Configure the device’s sandbox account

Settings → App Store → Sandbox Account → Sign In. Use a sandbox tester you created in App Store Connect → Users and Access → Sandbox → Testers. Use a unique email that isn’t tied to any real Apple ID (a +sandbox alias is fine).

If you get “This Apple ID has not yet been used in the iTunes Store”, on the device tap the prompt to accept the Sandbox terms once.

Step 2: Verify App Store Connect agreements

In App Store Connect → Business → Agreements, Tax, and Banking:

  • Paid Apps agreement status must be Active.
  • Banking info: complete and not expired.
  • Tax info: complete (US developers: W-9; non-US: W-8BEN with treaty if applicable).
  • No outstanding action items in the “Action Items” panel.

Sandbox transactions fail silently when any of these are red. Fix them before debugging code.

Step 3: Verify each IAP product is ready

App Store Connect → your app → In-App Purchases. Each product needs:

  • Display Name, Description (per locale).
  • Pricing tier set.
  • Screenshot for review (any image works; doesn’t need to be the actual UI).
  • Status: Ready to Submit or Approved.

Missing Metadata products are invisible to Products.products(for:).

Step 4: Match the product IDs in code

// Hardcoded set or fetched from your server
let productIDs: Set<String> = [
    "com.acme.pro_monthly",
    "com.acme.coins_100"
]

let products = try await Product.products(for: productIDs)
print("Returned \(products.count) products of \(productIDs.count) requested")

If Returned is less than requested, list the missing IDs and check spelling against App Store Connect — com.acme.pro_monthly vs com.acme.proMonthly is a silent miss.

Step 5: Test directly in the build

Run your app from Xcode on a real device. Trigger the purchase flow. When iOS prompts for the Apple ID, sign in with the sandbox tester (or accept the auto-fill from Step 1).

If you see “Cannot connect to iTunes Store,” check:

  • Device network reachability.
  • Date and time set correctly (TLS cert validation depends on it).
  • VPN off (some VPNs route through regions that block sandbox).

Step 6: Validate with App Store Connect after a successful purchase

App Store Connect → Sales and Trends → Sandbox Transactions. A successful sandbox purchase appears within minutes. If it doesn’t, the transaction didn’t complete on Apple’s side and your client got a false success.

How to confirm the fix

  • The purchase sheet appears with the correct product name and price (sandbox shows tier prices in your sandbox tester’s region currency).
  • After tapping Buy and authenticating, the sheet dismisses with a green success.
  • Transaction.updates fires with a verified transaction containing the expected product ID.
  • App Store Connect → Sandbox Transactions lists the purchase within a few minutes.
  • Re-running the test with the same sandbox tester triggers the “re-purchase” flow (for subscriptions, auto-renews; for non-consumables, immediate restore).

If it still fails

  1. Wipe the sandbox tester’s purchase history in App Store Connect → Users and Access → Sandbox → Testers → click tester → Clear History.
  2. Sign out and back in to the Sandbox Account in iOS Settings.
  3. Delete the .storekit configuration from your scheme to force a real sandbox query.
  4. Create a fresh sandbox tester in a region matching the device’s region; old testers can get into stale states.
  5. Test with a second sandbox tester to rule out a single-tester issue.

Prevention

  • Set up agreements, tax, and banking before writing any IAP code; nothing downstream works without them.
  • Create sandbox testers for every region you’ll support, with documented passwords stored in your team password manager.
  • Maintain a STOREKIT.md in your repo listing every product ID and where it’s defined (App Store Connect + .storekit file); diff on every release.
  • Run a CI step that lists products via Products.products(for:) against the sandbox and fails if any expected ID is missing.
  • Test purchases on a real device weekly; sandbox state drifts and silent failures get caught earlier.

Tags: #Troubleshooting #App Store #App review #IAP sandbox #IAP