Group Group Group Group Group Group Group Group Group

In-App Purchases: Receipt Validation Tutorial | raywenderlich.com


#1

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.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/9257-in-app-purchases-receipt-validation-tutorial

#2

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 :slight_smile:

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.


#3

@bmorefield Can you please help with this when you get a chance? Thank you - much appreciated! :]


#4

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.


#5

Hi @tomasstraus, are there any estimates on how big of a problem this is? Any ideas?


#6

@afinque Those lines are the start of the method. You’ll continue it with more code in the next two blocks of code.


#7

@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.


#8

This ‘not continuing to read’ thing nabs me every once in a while, and once again it’s time, I suppose. Thanks, and sorry for the noise!


#9

This is a nice tutorial, thanks!

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.


#10

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.


#11

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.


#12

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?


#13

@bmorefield Can you please help with this when you get a chance? Thank you - much appreciated! :]


#14

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 https://wiki.openssl.org/index.php/Compilation_and_Installation 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.


#15

Hi @bmorefield,

I need one more help

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?


#16

@bmorefield Can you please help with this when you get a chance? Thank you - much appreciated! :]


#17

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.