This guide covers testing iOS in-app purchases and subscriptions using both Sandbox and StoreKit local testing methods.
Before Apple approves in-app purchases for release, complete these compliance steps in App Store Connect:
- Accept the Paid Applications Agreement under Agreements, Tax, and Banking and finish the tax interviews. Apple blocks IAP review until this contract is active. Apple documentation
- Add at least one Bank Account for payouts in the same Agreements, Tax, and Banking area. Apple documentation
- Provide your Company information (legal entity name, address, and primary contact) and keep it in sync under App Information → General Information. Apple documentation
- Publish a Privacy Policy URL in App Information → Additional Information; subscriptions require this link to appear both in metadata and in-app. Apple documentation
- Publish a Terms of Service / EULA URL alongside the privacy policy. Subscriptions must expose this link inside the app and in the store listing. Apple documentation
- Mirror the Privacy Policy and Terms of Service inside your paywall UI so users see them before purchasing. See Paywall compliance template for an example layout you can adapt.
⚠️ CRITICAL: Apple requires product information (names, prices) to be fetched from StoreKit, not hardcoded. See Apple's Product Display Requirements section.
There are two primary testing options for iOS in-app purchases:
- Runs only on real devices
- Requires creating Sandbox accounts on App Store Connect
- More tedious setup process
- Required for testing pricing tables with automatic currency conversion
- Uses real App Store receipt validation
- Can test in Xcode simulator and device
- No need for Sandbox accounts
- Faster setup and testing process
- Can be automated and used in UI tests
- Works at early development stages (before App Store Connect setup)
- Uses local receipt validation through Xcode
- In Xcode, select File → New → File
- Choose StoreKit Configuration file template
- Specify a name (e.g.,
Products.storekit) - Save the file
You can sync the configuration file with your App Store Connect app definition to automatically import your products, or manually add them as described below.
- At the bottom of the
.storekitfile, click "+" - Select the product type you want to test:
- Auto-renewable subscriptions
- Non-renewable subscriptions
- Consumable purchases
- Non-consumable purchases
- Create Subscription Group: Groups multiple subscriptions that provide the same access level
- Configure Subscription Details:
- Subscription name: Display name for internal use
- Product ID: Unique identifier used in your code
- Price: Required field
- Subscription duration: Required (e.g., 1 month, 1 year)
- Family sharing: Enable/disable
- Introductory offer: Optional promotional pricing
- Offer codes: Optional promotional codes
- Promotional offers: Optional upgrade/downgrade offers
- Localization: Title and description for different regions
- In Xcode, select Product → Scheme → Edit Scheme
- Go to Run tab → Options
- Under StoreKit Configuration, select your
.storekitfile - To disable testing, select "none"
- Run your app in Xcode
- Navigate to your paywall/purchase screen
- Attempt to purchase a product
- The system will use your local StoreKit configuration instead of App Store
Starting with Xcode 12, you can manage test purchases:
- Run your application
- Select Debug → StoreKit → Manage Transactions
- View all test transactions performed locally
- Delete transactions to re-test them
- Simulate refunds by right-clicking transactions
Access the Editor menu in your StoreKit configuration file for advanced options:
- Switch between countries and currencies
- Test international pricing
- Test different languages for product names and descriptions
- Accelerate subscription renewals for testing
- Example: Test annual subscription renewal in minutes instead of years
- Test purchases requiring additional user action
- Simulate SCA (Strong Customer Authentication) for transactions over €30
- Test billing retry scenarios for failed auto-renewals
- Test continued access during payment collection attempts
- Explicitly trigger specific error scenarios
- Test Family Sharing purchase approval flow
- Configure promotional offer signing
- Used for local receipt validation
- Select Editor → Enable Ask to Buy
- Restart your application
- Attempt a purchase
- You'll see an alert requesting permission
- Click "Ask" button
- In transaction history window, find "pending approval" transaction
- Click approve button to complete the transaction
- Select transaction in history window
- Press Delete key to remove and re-test
- Right-click on transaction in history window
- Select refund option
- Handle revoked entitlements in your code:
for status in statuses {
switch status.state {
case .revoked:
if let transaction,
let revocationDate = transaction.revocationDate {
handleRevokedEntitlement(for: transaction.productID)
}
case .expired:
continue
default:
// Handle other states
}
}- Go to App Store Connect
- Navigate to Users and Access → Sandbox Testers

- Create a new sandbox tester account with unique email
- Set country/region and password
Method 1: Through App Store Settings (Traditional)
- On your iOS device, go to Settings → App Store
- Sign out of your regular Apple ID
- Install and run your app
- When prompted for Apple ID during purchase, use sandbox credentials
Method 2: Through Developer Menu (Recommended)
- With Developer Mode enabled, go to Settings → Developer
- Look for "StoreKit Sandbox Account" or "App Store Sandbox" section
- Sign in with your sandbox test account credentials
- This allows you to test without signing out of your main Apple ID
- Ensure your products are created and approved in App Store Connect
- Bundle ID must match exactly between app and App Store Connect
- Products must be available in your test region
- Test with real device (Sandbox doesn't work in simulator)
- Developer Mode: Ensure Developer Mode is enabled on your iOS device
- Verify Bundle ID: Must match exactly in App Store Connect
- Check Product Status: Products must be approved in App Store Connect
- Region Availability: Ensure products are available in test region
- Sandbox Account: Verify you're signed in with correct sandbox account
-
"Hardcoded product information detected"
- Problem: Using static text for product names/prices
- Solution: Always use
product.title,product.priceString,product.descriptionfrom the plugin
-
"Price format incorrect for region"
- Problem: Manual price formatting
- Solution: The plugin handles all currency formatting automatically
-
"Product information doesn't match App Store Connect"
- Problem: Displaying different text than configured in App Store Connect
- Solution: Use exact plugin product properties without modification
Add detailed logging to track issues:
console.log('🔍 Requesting product:', productIdentifier);
console.log('📦 Products returned:', products.length);
products.forEach(product => {
console.log(`✅ Found product: ${product.identifier} - ${product.title}`);
});- File Not Selected: Ensure
.storekitfile is selected in scheme options - Product ID Mismatch: Verify product IDs match between code and configuration
- Missing Localization: Add at least one localization for each product
Apple REQUIRES that all product information (names, prices, descriptions) be displayed using data from the plugin (which fetches from StoreKit), NOT hardcoded in your app.
-
App Store Review Compliance
- Hardcoded prices will cause App Store rejection
- Apple reviewers specifically check for this violation
- Your app must pass review to reach users
-
International Markets
- Prices automatically convert to local currencies
- Product names can be localized per region
- Compliance with local pricing regulations
-
Price Changes
- Apple may adjust prices due to currency fluctuations
- Promotional pricing changes automatically
- Tax changes are handled automatically
// This will be REJECTED by App Store Review
function renderProduct() {
return `
<div class="product">
<h3>Premium Subscription</h3> <!-- Hardcoded name -->
<p class="price">$9.99/month</p> <!-- Hardcoded price -->
<button onclick="purchaseProduct()">Subscribe</button>
</div>
`;
}// This will PASS App Store Review
import { NativePurchases } from '@capgo/native-purchases';
async function renderProduct() {
try {
// Get product info from store
const { product } = await NativePurchases.getProduct({
productIdentifier: 'com.yourapp.premium.monthly'
});
return `
<div class="product">
<h3>${product.title}</h3> <!-- From StoreKit -->
<p class="price">${product.priceString}</p> <!-- From StoreKit -->
<p class="description">${product.description}</p>
<button onclick="purchaseProduct('${product.identifier}')">Subscribe</button>
</div>
`;
} catch (error) {
console.error('Failed to load product:', error);
return '<p>Product unavailable</p>';
}
}-
Always Query Plugin First
import { NativePurchases } from '@capgo/native-purchases'; // Load products from App Store via Capacitor plugin const { products } = await NativePurchases.getProducts({ productIdentifiers: ["your.product.id"] });
-
Use Product Properties
const product = products[0]; // Required: Use these exact properties const name = product.title; // Localized name const price = product.priceString; // Formatted price with currency const description = product.description; // Product description
-
Handle Loading States
// Show loading while fetching products if (products.length === 0) { document.getElementById('product-list').innerHTML = 'Loading products...'; } else { // Display actual product data displayProducts(products); }
During testing, verify:
- Product names display correctly in different languages
- Prices show in correct local currency format
- Descriptions appear as configured in App Store Connect
- Loading states are handled gracefully
- No hardcoded text appears for products
-
"Your app displays hardcoded prices"
- Solution: Use
product.priceStringfrom the plugin
- Solution: Use
-
"Product information doesn't match App Store Connect"
- Solution: Use
product.titleandproduct.descriptionfrom the plugin
- Solution: Use
-
"Price format doesn't follow local conventions"
- Solution: The plugin handles this automatically via StoreKit
When you use plugin product data:
- Automatic currency conversion (USD → EUR, JPY, etc.)
- Proper number formatting (1,000.00 vs 1.000,00)
- Right-to-left language support
- Regional tax inclusion (prices include VAT where required)
💡 Pro Tip: Test your app in different regions using StoreKit configuration files to see how prices and names appear in various markets. The plugin automatically handles all regional formatting.
- Use StoreKit local testing for rapid iteration
- Test all purchase flows and edge cases
- Automate testing with UI tests where possible
- Test different subscription durations and offers
- Use Sandbox testing to validate real App Store integration
- Test with multiple Sandbox accounts
- Verify receipt validation works correctly
- Test in different regions and currencies
- Monitor transaction logs and error rates
- Implement proper error handling for all scenarios
- Use server-side receipt validation
- Handle edge cases like network failures and interrupted purchases
This guide is part of a comprehensive series on iOS in-app purchases:
- App Store Connect and project configuration
- Initialization and purchase processing
- Testing purchases in Xcode (this guide)
- Server-side receipt validation
- SKError codes and error handling
All screenshots and images are for educational purposes. Some images adapted from Adapty's comprehensive testing guide.
🎉 ALL IMAGES INCLUDED! Complete visual testing guide.
StoreKit Local Testing (9 images from Adapty blog):
- ✅
create-storekit-file.png- Xcode new file dialog - ✅
sync-app-store-connect.png- App Store Connect sync option - ✅
add-products-storekit.png- Adding products interface - ✅
subscription-setup.png- Subscription configuration form - ✅
edit-scheme-storekit.png- Xcode scheme options - ✅
transaction-manager.png- StoreKit transaction manager - ✅
editor-menu-options.png- StoreKit editor menu - ✅
ask-to-buy-flow.png- Ask to buy permission dialog - ✅
pending-approval.png- Pending transaction approval
Sandbox Testing (4 images):
- ✅
enable_dev_mode_ios.jpg- Enable iOS Developer Mode (prerequisite) - ✅
sandbox-tester-setup.png- App Store Connect sandbox tester list - ✅
sandbox-tester-setup_2.png- New tester creation form - ✅
septup_sandbox_ios.jpg- iOS device Settings → App Store (traditional method)
See docs/ios-images/DOWNLOAD_IMAGES.md for technical details.










