Testing In-App Purchase Transactions

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 SKProductsRequest. 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 paymentQueue(_:updatedTransactions:) 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 SKPaymentTransactionObserver 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.

Locate the call to the add(_:) method of SKPaymentQueue in your code. Verify that your app calls this method at app launch.

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 paymentQueue(_:updatedTransactions:) method, and inspect the transaction to verify that its status is SKPaymentTransactionState.purchased.

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 paymentQueue(_:updatedTransactions:) 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 paymentQueue(_:updatedTransactions:) 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 finishTransaction(_:) 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 Sandbox.

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.