Intro To Testing In-App Purchases on IOS

An in-app purchase (IAP) can give users access to extra features and content. Apple does not allow apps to use their own mechanisms to unlock content or functionality. Because these exclusive features are inherently attached to the payment system, it is important to test the method of obtaining them.

What will be discussed in this article
  • In-app purchasing infrastructure.
  • Guidelines for testing in-app purchases in a developer sandbox.

The in-app purchase flow is different for every app: purchases can be made in the App Store or within the app using whatever UI the developers created. Regardless of how a purchase is made, the StoreKit API is required for making the transaction. Every transaction has to be processed using Apple’s StoreKit API which is able to read and write data to a cryptographically signed receipt that was created by the App Store and bundled with the app. This receipt will serve as a detailed record of all the purchase transactions a user has made in an app.

An app can have various types of purchasable items:
  • Consumables
    • Can deplete and are re-purchasable
  • Non-consumables
    • Purchased only once; doesn’t expire
  • Auto-renewable subscriptions
    • Purchase once; renews automatically until canceled
  • Non-renewing subscriptions
    • Limited time access; does not renew automatically

It is the job of the developer to present the correct product information in the app. If it is unclear how to make an IAP, Apple has the right to delay or reject the application. This is important for everyone in the SDLC to know since it could impact project timelines and deliverables.

Getting Started

Before the app is released, it should be thoroughly tested in a development environment. To get started, you will have to create a developer sandbox account through App Store Connect. A developer sandbox account is an account that has the same privileges as an iTunes account except it only works within the developer’s sandbox environment. Besides being able to make purchases without spending actual money, this environment accelerates the duration of purchased auto-renewable and non-renewable subscriptions to allow testing long subscription periods in a shorter amount of time. Once the sandbox Apple ID has been created and assigned to the app project in App Store Connect, testing can commence. To test, a real device is required because simulators do not allow making in-app purchases; but in-app purchasable items can be viewed in simulators. Receipt validation does not work in simulators either.

If you have to make multiple sandbox accounts (and you probably will), make sure to keep track of all accounts in a spreadsheet.

In-App Purchase Receipts

Before testing in-app purchases, it is helpful to know what data an application receipt has. When the app is opened, the App Store receipt should immediately be requested and processed. This is only a best practice and is not enforced by Apple, but it is encouraged by QA. The receipt should be processed immediately after opening the app because it will help determine what features purchased by the user need to be made available. What information does a receipt have?

  • Real-time information about a subscriber’s status.
  • Consent status
  • App version
  • Opaque Value
  • SHA-1 hash
  • In-app purchase receipt
  • Original Application Version
  • Receipt Creation Date
  • Receipt Expiration Date
  • Quantity
  • Product Identifier
  • Transaction Identifier
  • Original Transaction Identifier
  • Purchase Date
  • Original Purchase Date
  • Subscription Expiration Date
  • Subscription Expiration Intent
  • Subscription Retry Flag
  • Subscription Trial Period
  • Subscription Introductory Price Period
  • Cancellation Date
  • Cancellation Reason
  • App Item ID
  • External Version Identifier
  • Web Order Line Item ID
  • Subscription Auto Renew Status
  • Subscription Auto Renew Preference
  • Subscription Price Consent Status

These are the application receipt’s key values presented in the JSON object delivered from the App Store. As you can see, there is enough information in a receipt to determine whether a user should have access to an app’s feature; that’s why it is important to request and validate the receipt immediately after the app is opened. To improve performance and provide functionality offline, the app receipt can be retrieved from the device, but it may not have the latest information. Detailed information about the receipt fields can be found here.

To get the latest receipt from the App Store, the app receipt on the device needs to be submitted to the app’s server which will then communicate with the App Store. This is basically what the app should be doing:

  1. Retrieve the app receipt from local storage.
  2. Encode the app receipt in Base64.
  3. Send the Base64-encoded data to the app server.
  4. Create a JSON object on the app server.
  5. Submit the JSON object in an HTTP POST request to the App Store.
  6. Receive the App Store response on the app’s server.
  7. Receive the response from the app’s server containing the latest receipt.

This process should be repeated whenever the user makes an in-app purchase or when an auto-renewing subscription is updated. For security, all this data should be transported using the ATS protocol. If lower-level networking interfaces are used, the app could potentially be exploited. Developers also have the choice of using StoreKit API to read from local data sources for quicker testing.

Purchase receipts can be viewed from within the app or through the App Store. Developers are able to access more receipt data in a JSON formatted response from the App Store.


Receipts are not created for items that are free.

Making a Purchase

On the first purchase attempt, the user will be presented with a dialog asking them to sign in to make a purchase. In the production environment, the credentials for the App Store must be provided. If you attempt to submit those credentials in the sandbox environment, you will not be able to sign in. In the sandbox environment, you must use a sandbox Apple ID that was created in App Connect.

You can offer a discounted price or a free trial to new subscribers of an auto-renewable subscription. Users are eligible for one introductory price within a subscription group.

Test Cases

The following is a general set of test cases that should be validated against the in-app purchasing model.

  • Subscription handling is available and compatible across all iOS 12+ devices.
  • Upgrade to a different subscription plan within the same subscription group.
    • Users should be immediately upgraded.
    • Users should receive a refund of the prorated amount of their original subscription.
  • Downgrade to a different subscription plan within the same subscription group.
    • The current subscription should continue until the next renewal date.
  • Crossgrade subscription plans within the same subscription group.
    • If the new and old subscription plans have the same duration (same start and end date?), then the new subscription should begin immediately.
    • If the new and old subscription plans’ duration is different, then the new subscription should go into effect the next renewal date.
  • Subscribers from different locations receive the correct pricing information.
  • Introductory subscription offer automatically appears for eligible users.
  • Introductory subscription offer has an end date? If no end date is provided, the offer will be available indefinitely.
  • App Store server notifications about auto-renewable subscriptions are displayed.
  • One of the following introductory deals is only offered once per subscription, per territory:
    • Free Trial
    • Pay as you go
    • Pay up front
  • Non-consumable purchases can be restored, and only non-consumable purchases can be restored. Purchases should be restored automatically; if not, then there should be a button for manual restoration.
  • (Production only test) If the app is not installed, starting an in-app purchase from the App Store should prompt the user to install the app. The appropriate purchase flow should kick-off once the app is opened.
  • When a user starts an in-app purchase on the App Store, they are taken into your app to continue the transaction.
  • Advertisements are removed from the app if an in-app purchase intends to have that effect.
  • The subscription item selected by the user via the app UI matches the subscription requested from the App Store.
    • It’s possible for the app UI to say the subscription item is for a yearly subscription while the app requests a monthly subscription from the App Store. This would happen if there is an error in the app’s code.
  • Subscription data is dynamically linked to App Store Connect.
    • If a subscription item is updated in App Store Connect, the changes should be reflected in the app. Following best practices, the subscription data should not be hard-coded in the app.
  • Purchased items are accessible if the device is offline.
  • Purchased items are available on other devices the app is installed on.
  • Content related to the free trial is no longer available once the free trial has ended.
  • (iOS 14 only) Test the app while purchases are set up to be interrupted for a sandbox tester.

For Apple’s latest guidelines, please see Reference [1].

Conclusion

Not every app that enables users to make a purchase will require the type of testing discussed above. If the purchase made through the app is for an external resource, other payment methods must be provided that are not integrated with the App Store. Furthermore, only apps that are running on iOS 12+, macOS, and tvOS are able to process in-app purchases.

It is important to test applications with in-app purchases before launch, because if there are any issues with the business model or technical intricacies, users may be harmed, business will be hurt, and Apple may remove the app from the App Store.


Terminology

These are terms covered in this article which are important to know or anything related to in-app purchases on iOS.

In-App Purchase (IAP)

Purchases made from within the app.

iTunes Connect

This is where developers previously performed the setup process now done in App Store Connect.

App Store Connect

This where app distribution is managed.

StoreKit

This is the API that provides a medium of communication between the app and the App Store.

Subscription Group

A collection of subscription products. Every subscription needs to be part of a subscription group. App Store Connect allows only one subscription within a group can be purchased.

Promotional Offers

  • Primarily used for retaining and winning back subscribers.
  • These offers are only available to existing or previous subscribers within the app.
  • Can be configured so that one customer can redeem x amount of promotional offers.
  • App Store Connect only allows up to 10 active offers per subscription.
  • These offers cannot be displayed on the App Store.
  • Available only on iOS 12+.

Introductory Offers

  • Primarily used for acquiring new subscribers.
  • These offers are only available to new subscribers.
  • Only one of these offers can be redeemed per subscription group.
  • App Store Connect allows only one offer per subscription, per territory.
  • These offers can be displayed on the App Store.
  • Available on iOS 10+.

Sandbox Account

This is the Apple ID added to the testing accounts for a project on App Store Connect.

Application Receipt

This is a cryptographically signed receipt from the App Store that is tied to the app binary. It contains all purchase transaction receipts.

Resources

The content this article provides was inspired and gathered from the following sources.

[3]

Getting Started with In App Purchases

[6]

Set an introductory offer for an auto-renewable subscription

[7]

If you don’t see your in-app purchases

[12]

Prevent in-app purchases from the App Store

[18]

RevenueCat – The Ultimate Guide to iOS Subscription Testing

FoxholeQA logo

Businesses that trust Foxhole QA