IAPDemo
Implementing and testing In-App Purchases in Xcode 12 and iOS 14, including local receipt validation.
Install / Use
/learn @russell-archer/IAPDemoREADME
In-App Purchases with Xcode 12 and iOS 14
Implementing and testing In-App Purchases with StoreKit1 in Xcode 12 and iOS 14.
See also StoreHelper for details on implementing and testing in-app purchases with StoreKit2 and StoreHelper in Xcode 13, Swift 5.5, iOS 15.
Disclaimer. The source code presented here is for educational purposes. You may freely reuse and amend this code for use in your own apps. However, you do so entirely at your own risk.
See on IAPHelper on GitHub for IAPHelper source.
Updated 31st December 2020
Added notes on Support for Strong Customer Authentication transactions in the European Economic Area with reference to new a Apple Support Document.
See IAPDemo on GitHub for source code. The HelloIAPWorld source is also available on GitHub.
References:
- In-App Purchase Overview (Apple)
- Receipt Validation Programming Guide (Apple archive but still useful)
- In-App Purchase (Apple)
- Choosing a Receipt Validation Technique (Apple)
- Validating Receipts with the App Store (Apple)
- In-App Purchases: Receipt Validation Tutorial (Ray Wenderlich)
- Local Receipt Validation for iOS in Swift From Start to Finish (Andrew Bancroft)
- Swifty Local Receipt Validator (Andrew Bancroft)
- Receipt Validation – Verifying a Receipt Signature in Swift (Andrew Bancroft)
- Receipt Validation (Laurent Etiemble, Objc)
Contents
- Overview
- Receipt validation options
- Sandbox accounts
- Basic Steps
- HelloIAPWorld Example
- How to Validate Receipts Locally
- IAPDemo Example
- StoreKit Automated Testing
- Support for Strong Customer Authentication transactions in the European Economic Area
- Future Enhancements
Overview
The code we write to manage in-app purchases is critically important to the success of our apps. However, if you’ve not tackled it before, implementing and testing in-app purchases is daunting, complex and seems way more involved than you’d expect!
Anybody wanting to support in-app purchases faces a similar set of challenges:
- How do you define the set of products that can be purchased in your app?
- Defining your in-app purchases in App Store Connect
- Working with
StoreKitto request localized product data from the App Store and initiate purchases - Implementing
StoreKitdelegate methods to process async notifications for purchase success, failure, restoring purchases, etc. - Handling edge-cases, like when a purchase is deferred because it requires parental permissions, or when entitlements for a user have changed and access to the specified IAPs has been revoked
- Should you handle App Store receipt validation on-device or server-side?
- Should you write your own receipt validation code or use a service like RevenueCat?
- Working with OpenSSL and the arcane PKCS #7 and ASN.1 data structures found in receipts
- Writing code to validate the receipt and read in-app purchase data
- Creating and managing sandbox accounts used for testing
When I first implemented in-app purchases in one of my iOS apps in 2016 the two main pain-points were:
Receipt validation options
The App Store issues an encrypted receipt when in-app purchases are made or restored (when an app’s first installed, no receipt is present). This receipt contains a complete list of all in-app purchases made in the app.
There are four receipt validation approaches available:
- Server-side receipt validation
- On-device receipt validation
- Third-party receipt validation service
- No receipt validation
Server-side validation
This is probably the easiest option, but you need an app server to send requests to the App Store server. Apple specifically says you should not create direct connections to the App Store server from your app because you can’t guard against man-in-the-middle attacks.
Despite this clear warning, the web has many examples (including commercial offerings) of using direct app-to-App Store connections. The advantage of using server-side validation is that you can retrieve easily decoded JSON payloads that include all the in-app purchase data you need. We don’t cover server-side validation in this example.
On-device validation
On-device validation is somewhat tricky and requires use of the C-based OpenSSL library to decrypt and read the receipt data. Note that including the required two OpenSSL libraries adds nearly 50MB to your app.
I first started supporting in-app purchases in 2016. I fully expected StoreKit or some other Apple framework to provide ready-to-use abstractions allowing for easy access to the low-level cryptographic data structures in the receipt. However, as I looked deeper into the “where’s the receipt processing framework?” conundrum the more the answer became clear: having a ready-to-use framework creates a security risk because “hackers” wishing to access your in-app purchases for-free know in advance where and how to concentrate their attacks. Apple’s answer was (and still is): create your own custom receipt validation solution because a unique solution will be harder to hack.
Clearly a custom solution (if done correctly) will be more secure. But, as all developers know that have attempted it, writing security-critical cryptographic-related code is hard and if you get it wrong disasters will happen! In my opinion, surely it would be better for Apple to provide something that enables correct and reasonably secure receipt validation for the general app developer?
However, at present (November 2020) you have no choice if you want to validate and read receipt data on-device: you must develop your own OpenSSL-based solution. If you don’t feel confident doing this feel free to adapt (or use as-is) the code presented herein.
Third-party receipt validation service
A number of third parties provide receipt validation services, normally as part of a larger in-app purchase, subscription an
