In this tutorial, youāll learn how receipts for In-App Purchases work and how to validate them to ensure your users have paid for the goodies you give them.
Hi @bmorefield, thanks for this tutorial! I have yet to implement IAP in my app but 2019 is the year Iāll make it happen
Question for you: In the second block of code under Loading the Receipt,
private func loadReceipt() ā UnsafeMutablePointer? {
// Load the receipt into a Data object
guard
let receiptUrl = Bundle.main.appStoreReceiptURL,
let receiptData = try? Data(contentsOf: receiptUrl)
else {
receiptStatus = .noReceiptPresent
return nil
}
}
Is there a return statement missing? Iām getting compile errors. I tried putting return receiptData after the guard, but it complained about converting Data to UnsafeMutablePointer.
There is no sens to validate receipt on the client. The only reasonable way to do that is on the server and then the server can deliver some premium content or not. If you do validation on client anyone can modify your application code and then omit this validation.
@tomasstraus@afinque Youāre right that for some apps, doing receipt validation can be more secure. If you already have a custom server infrastructure in place, then it can be another process running on it. But server validation does require you to build and maintain a server infrastructure (or outsource that infrastructure at additional cost).
For some apps that may not be worth the upkeep, and client validation provides additional protection. Adding an on device check in addition to a server check also increases the difficulty. In the end, a determined and skilled hacker can often figure out a way to beat the system. The goal is to make it more difficult and how difficult it needs to be varies depending on the appās user base, type, popularity, and similar factors.
But could you maybe improve the style of your Swift code? Especially the use of ā!ā with āreceiptPKCS7ā, for example, really makes me nervous. I know you check for ā!=ā before using that ā!ā, but why not use āguardā with that?
From my point of view especially tutorial code should be a an example for good Swift code. Using ā!ā is not good, from my POV.
Great tutorialā¦ I was so close to getting this to work, but ran into a problem with this lineā¦
OPENSSL_init_crypto(UInt64(OPENSSL_INIT_ADD_ALL_DIGESTS), nil )
This throws 2 errorsā¦
Use of unresolved identifier āOPENSSL_INIT_ADD_ALL_DIGESTSā
and
Use of unresolved identifier āOPENSSL_init_cryptoā
If I comment this out it will build.
I feel like I have my paths set up correctly. I do have the BoringSSL in the project from cocoa pods as it is necessary for Firebase. Not sure if that is causing problems or if it might be a wrapper issue. Any direction you could point me in would be much appreciated.
Iād guess that itās probably a missing header thatās not included in your ReceiptVerifier-Bridging-Header.h file. Different versions of the crypto libraries often move functions and code around (Iāve seen a similar issue moving between different versions of OpenSSL in the past). The definitions might be in a different place than in the sample project. If youāre using a new enough OpenSSL, the needed libraries should still load.
I am just wondering why OpenSSL library is taking 40MB? Receipt validation is necessary for my app (27MB) but adding 40MB to the app bundle is too much. Will CommonCrypto reduce this size? I canāt find a single resource to do receipt validation by using CommonCrypto. Can anyone point me to some resource?
I see that CommonCrypto is a C library. It is available to import from Xcode 10 onwards. Is it too complicated to write receipt validation using CommonCrypto in swift?
Using CommonCrypto would be an option since it wouldnāt require bundling the static library with your app. It does make a potential hackerās job a bit easier since they can look for CommonCrypto calls in the binary. Thereās a lot of tradeoffs like that in this kind of code. Itās finding the balance thatās best for your needs.
There are also some pre-compiled OpenSSL libraries you can find in Cocoa Pods and build scripts on Github that can produce smaller libraries. Another option would be to do a custom compilation of OpenSSL that would reduce the size by excluding unused components. Doing so would be a tutorial in itself, but the documentation at Compilation and Installation - OpenSSLWiki would be a starting point.
There are also other libraries that do encryption and some are already included in other modules or projects (see the earlier comment referencing BoringSSL for example). One of those might work for you depending on your app and the licensing of the library.
I have implemented receipt validation successfully. When testing from Xcode, receipt is not present for the first launch of the app as it has to be downloaded from sandbox environment. Is it the only case that the receipt is missing? Will there be any case in production when receipt can be missing?
Appleās documentation is a bit vague on that, but I think it is possible in case of a restore from backup. The documentation does recommend requesting an updated receipt in the case of it being missing and not treating it as a failure.
Thanks, this was very useful and acted as a template for some of my code.
Reading this in conjunction with the WWDC sessions on receipt validation and IAP, makes it easier.
Thanks, I used the code to test my IAP subscription and I purchased successfully. But there were no expiration date on the receipt. The value is always nil. Iām wondering how to access the expiration date.
Are you looking at the in app purchase field with type 1708? The field with attribute type 21 isnāt used for subscriptions, but is named similar enough to confuse things.