Verify that you have implemented in-app purchases correctly by testing your code in the sandbox environment.
Use the Apple sandbox environment to test your implementation of the StoreKit framework without incurring fees from in-app transactions. Comprehensive testing can help you:
Ensure a seamless purchase flow to provide a positive customer experience in your app.
Implement sound logic that covers all scenarios, such as restores.
Validate that purchases will behave correctly in production once your app is available on the App Store.
Sign In to the App Store with Your Test Account
Create a sandbox or test user account in App Store Connect.
To run your app using your sandbox account, do one of the following, depending on your development device and operating system:
For iOS 11 or earlier — Sign out of the App Store in Settings, then build and run your app from Xcode.
For iOS 12 or later — Don't sign out of the App Store; simply build and run your app from Xcode. Sandbox accounts are stored separately, and you can control your sandbox account directly on-device in Settings.
For macOS — Sign out of the Mac App Store, then build your app in Xcode and launch it from the Finder.
Make an In-App Purchase
When prompted to sign in to the App Store, use your test account. Note that the text
[Environment: Sandbox] appears as part of the prompt, indicating that you’re connected to the test environment. If
[Environment: Sandbox] does not appear, you’re using the production environment. Make sure you’re running a development-signed build of your app; production-signed builds use the production environment.
Test Fetching the List of Product Identifiers
If your product identifiers are embedded in your app, set a breakpoint in your code after the identifiers are loaded, and verify that the instance of
NSArray contains the expected list of product identifiers.
If your product identifiers are fetched from a server, manually fetch the JSON file using a web browser such as Safari, or a command-line utility such as
curl. Then verify that the data your server returns contains the expected list of product identifiers and that your server correctly implements standard HTTP caching mechanisms.
Test Handling Invalid Product Identifiers
Intentionally include an invalid identifier in your app’s list of product identifiers. Then do one of the following:
In a production build, verify that the app displays the rest of its store UI and that users can purchase other products.
In a development build, verify that the app brings the issue to your attention.
Check the console log and verify that you can correctly identify the invalid product identifier. Make sure you remove it after testing.
Test a Products Request
Using the list of product identifiers that you tested, create and submit an instance of
SKProducts. Set a breakpoint in your code, and inspect the lists of valid and invalid product identifiers. If there are invalid product identifiers, review your products in App Store Connect and correct your JSON file or property list.
Test a Payment Request
Create an instance of
SKPayment using a valid product identifier that you’ve already tested. Set a breakpoint and inspect the payment request. Add the payment request to the transaction queue, and set a breakpoint to confirm that the
payment method of your observer is called.
Though you can finish the transaction immediately without providing the content during testing, failing to finish the transaction can cause problems. Unfinished transactions remain in the queue indefinitely, which could interfere with later testing.
Verify Your Observer Code
Review the transaction observer’s implementation of the
SKPayment protocol. Verify that it can handle transactions even if you aren’t currently displaying your app’s store UI and even if you didn’t recently initiate a purchase.
Test a Successful Transaction
Sign in to the App Store with a test user account, and make a purchase in your app. Set a breakpoint in your implementation of the transaction queue observer’s
payment method, and inspect the transaction to verify that its status is
Set a breakpoint at the point in your code that persists the purchase, and confirm that this code is called in response to a successful purchase. Inspect the user defaults or iCloud key-value store, and confirm that the correct information has been recorded.
Test an Interrupted Transaction
Set a breakpoint in your transaction queue observer’s
payment method so you can control whether it delivers the product. Then make a purchase as usual in the test environment, and use the breakpoint to temporarily ignore the transaction, for example, by returning from the method immediately using the
thread return command in the LLDB debugger.
Terminate and relaunch your app. StoreKit calls the
payment method again shortly after launch. This time, let your app respond normally. Verify that your app correctly delivers the product and completes the transaction.
Verify That Transactions Are Finished
Locate where your app calls the
finish method. Verify that all work related to the transaction has been completed before the method is called and that the method is called for every transaction, whether it succeeded or failed.
Test Server-to-Server Notifications
If you have server-to-server notifications enabled for your app, test your logic for transactions in the test environment. To determine if a notification for a subscription event is in the test environment, check whether the value of the
environment field in the server-to-server JSON responseBody object equals
For more information on server-to-server notification fields, see App Store Server Notifications.
Test an Auto-renewable Subscription
The behavior of auto-renewable subscriptions differs between the testing environment and the production environment.
In the testing environment, subscription renewals happen at an accelerated rate, and auto-renewable subscriptions renew up to five times after the initial purchase. This enables you to test how your app handles a subscription renewal, a subscription lapse, and a subscription history that includes gaps. For a complete list of subscription durations within Sandbox, see Test in-app purchases.
Because of the accelerated expiration and renewal rates in the test environment, a subscription can expire before the system tries to renew it, resulting in a short lapse in the subscription period. Such lapses are also possible in production for a variety of reasons; verify that your app handles them correctly.