Member-only story
How to Validate iOS In-App Purchase Receipts Locally
A walk-through of local validation using the command line

I wrote an article the other week about subscriptions, receipts, and StoreKit in iOS14 (you can find it here). Towards the end of the article, I showed an example of a receipt I had validated through the Apple server API call.
Now I mentioned it in passing, but if you check the Apple documentation, you surely can’t help but notice this message in a red box.
And yes, that’s exactly what I did in the previous article to show you what a receipt looks like — and how I started to validate it. So why do Apple say this, and what’s wrong with that process? Here’s a quick table that briefly explains the difference.

Let’s walk through the validation of a receipt, learning how to locally validate it and, in doing so, understanding what could go wrong. Now I’m going to assume you’ve read the other article and have some receipts to play with. It makes no sense for me to play on my own — you need to join in, too.
I’m also going to assume in this article that you’re reasonably comfortable with the UNIX shell, too. The plan is to use some tools — like OpenSSL and others — to parse a receipt where I can. I aim do so in order to focus on the process and not so much on the code.
Get the Receipt
Now where do we start? Open the demo app (full code here), and change to the IAPManager
class. Within it, look for the verifyReceipt()
function, and make sure the two lines shown here are uncommented.
guard let receiptURL = Bundle.main.appStoreReceiptURL, let receiptString = try? Data(contentsOf: receiptURL).base64EncodedString() , let url = URL(string: verifyURL) else {
return
}
print("receiptURL ",Bundle.main.appStoreReceiptURL, verifyURL, receiptString)
What this will do is printout the location of the receipt you generate together with a Base64-encoded copy to the screen. Run the app, and check it out.