# Sandbox/test environment configured for IAP testing

- **Pattern:** `ab-000403` (`app-store-iap-subscriptions.iap-integration.sandbox-testing`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000403
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000403)

## Why it matters

Shipping IAP with no sandbox configuration means the purchase flow has never actually run end-to-end without a real card charge. Any regression in product ID casing, entitlement mapping, receipt validation, or SDK key selection ships straight to production, where the first signal is a user support ticket or a drop in activation-to-subscription conversion. A `.storekit` file or an environment-split RevenueCat/Adapty/Qonversion key turns this into a local test that runs on every build instead of a launch-week fire.

## Severity rationale

Low because sandbox gaps cause launch-time regressions rather than runtime failures for paying users in production.

## Remediation

Add a StoreKit configuration file in Xcode (File → New → File → StoreKit Configuration File) that mirrors your App Store Connect products, and wire it into the Run scheme under Edit Scheme → Run → Options → StoreKit Configuration. Then split IAP SDK keys by build configuration so sandbox and production cannot cross-contaminate:

```swift
#if DEBUG
Purchases.logLevel = .debug
Purchases.configure(withAPIKey: "appl_sandbox_key_here")
#else
Purchases.configure(withAPIKey: "appl_production_key_here")
#endif
```

Provision sandbox Apple IDs under Users and Access → Sandbox Testers, and use `android.test.purchased` for early Play Billing smoke tests.

## Detection

- **ID:** `sandbox-testing`
- **Severity:** `low`
- **What to look for:** Count all relevant instances and enumerate each. Look for IAP sandbox/test configuration signals. For iOS: the presence of `.storekit` configuration files in the project (Xcode StoreKit testing — enables local IAP simulation without a network call to App Store Connect); or comments/documentation indicating sandbox Apple ID usage for testing; or `SKPaymentQueue` sandbox environment flags. For Android: `BillingClient.newBuilder()` with test purchase token handling; presence of `purchases_test` or `billing_test` in product ID strings. For RevenueCat: look for `Purchases.logLevel = .debug` in dev builds and `Purchases.configure(withAPIKey: "appl_xxxxx")` where the key differs between dev/prod (a common RevenueCat pattern is to use a sandbox API key in debug and production key in release). For Adapty: `Adapty.activate("PUBLIC_SDK_KEY", observerMode: false)` with environment-specific keys. For Qonversion: `Qonversion.initWithConfig(QNConfig(projectKey: "YOUR_KEY", launchMode: .subscriptionManagement))`. Also check if there is any CI/mock setup that bypasses IAP entirely for automated tests — this is fine if gated properly, but dangerous if it leaks into production builds.
- **Pass criteria:** Evidence that sandbox/test IAP has been considered: `. At least 1 implementation must be verified.storekit` test config file present, sandbox Apple ID credentials referenced in README or environment docs, RevenueCat/Adapty/Qonversion configured with environment-specific API keys, or clear documentation of IAP testing approach.
- **Fail criteria:** No evidence of any IAP test configuration. Production IAP SDK keys appear to be the only configuration present. No `.storekit` file, no test product IDs, no sandbox credentials documentation.
- **Skip (N/A) when:** No IAP detected in the app.
- **Detail on fail:** `"No Xcode StoreKit configuration file found and no sandbox testing setup detected — IAP purchases cannot be tested without triggering real charges"` or `"Single RevenueCat API key used in both debug and release builds with no sandbox distinction"`
- **Remediation:** Testing IAP without sandbox configuration leads to shipping untested purchase flows — a common source of post-launch revenue loss.
  1. For iOS, create a StoreKit configuration file in Xcode:
     - File → New → File → StoreKit Configuration File
     - Add your subscription products matching your App Store Connect products
     - Set the scheme to use this configuration: Edit Scheme → Run → Options → StoreKit Configuration
  2. For RevenueCat sandbox testing:
     ```swift
     // iOS — AppDelegate.swift
     #if DEBUG
     Purchases.logLevel = .debug
     Purchases.configure(withAPIKey: "appl_sandbox_key_here")
     #else
     Purchases.configure(withAPIKey: "appl_production_key_here")
     #endif
     ```
  3. Create sandbox Apple IDs in App Store Connect under Users and Access → Sandbox Testers
  4. For Android, use test product IDs (`android.test.purchased`, `android.test.canceled`) for early testing

Taxons: operational-readiness

HTML version: https://auditbuffet.com/patterns/ab-000403
