It's a signed file in a PKCS#7 format, which contains information about all in-app purchases in the app. It's located in the app bundle and can be easily read by calling Bundle.main.appStoreReceiptURL
.
If a user downloaded the app from the App Store – yes. However, in sandbox if your app was installed via Xcode or Testflight, then there won't be a receipt until you make a purchase or restore.
It basically means to decrypt encrypted receipt file, get JSON data and verify purchases.
There are two important reasons for iOS receipt validation. First, you can ensure that your customers are getting access to what they paid for. This is particularly important when they have a subscription to your app's services.
Secondly, Apple receipt validation from the server-side helps protect your app against piracy. Many people fraudulently try to get in-app purchases via fake receipts, but App Store receipt validation stops this in its tracks.
It's possible for iOS receipt validation to be done locally; however, it's more commonly done via a server-to-server configuration.
The typical App Store receipt validation flow consists of the following steps.
1. The customer makes an in-app purchase.
2. The app store notifies the app about the purchase.
3. The developer sends a receipt to the server for validation via SDK.
4. The server calls Apple’s “verifyReceipt” endpoint and receives a JSON response.
5. The server validates the purchase. While doing so, it either tags the purchase as fraudulent or creates a regular purchase event.
6. The response is transferred to the SDK.
7. The Apple receipt validation response is transferred as a success or failure to the app.
There are four types of in-app purchases: consumables, non-consumables, non-renewing subscriptions and auto-renewable subscriptions. You can restore all except consumables.
Consumable purchases are, for example, coins in your app – something that can be purchased many times. There is no way to restore the number of your coins, you should keep it on your server.
A non-consumable purchase, on the other hand, is used once but doesn't expire (like a song you purchase from a music app). This kind of purchase can be restored via in-app purchase receipt validation.
You could choose between three validation approaches:
Well, local validation is too complicated and it's hard to implement it. Besides that, you have to add OpenSSL library into your Xcode project. And in some cases you will need to refresh the receipt.
Apple doesn't recommend Online validation because of security reasons: HTTPS request can be intercepted via man-in-the-middle attack.
It's better to validate receipts using your own server. Especially, Apple adds new fields to the receipt, like grace_period_expires_date
or subscription_group_identifier
. You can apply changes on your server without app update. Besides that, user can easily hack those two methods just by changing system date on device.
It's a special string key, which a developer uses to validate receipts with auto-renewable subscriptions. You will put Shared Secret into HTTPS request parameters.
Go to App Store Connect, open your app, then go to "Functions" and in "In-App Purchases" tab you will see "App-Specific Shared Secret" button. Generate a new key, if it doesn't exist.
func validateReceipt(){ #if DEBUG let urlString = "https://sandbox.itunes.apple.com/verifyReceipt" #else let urlString = "https://buy.itunes.apple.com/verifyReceipt" #endif guard let receiptURL = Bundle.main.appStoreReceiptURL, let receiptString = try? Data(contentsOf: receiptURL).base64EncodedString() , let url = URL(string: urlString) else { return } let requestData : [String : Any] = ["receipt-data" : receiptString, "password" : "YOUR_SHARED_SECRET", "exclude-old-transactions" : false] let httpBody = try? JSONSerialization.data(withJSONObject: requestData, options: []) var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("Application/json", forHTTPHeaderField: "Content-Type") request.httpBody = httpBody URLSession.shared.dataTask(with: request) { (data, response, error) in // convert data to Dictionary and view purchases }.resume() }
This is a Swift receipt validation example from an iOS device. Don't forget to change YOUR_SHARED_SECRET
with your own shared secret.
After you get data
, convert it into Dictionary
:
DispatchQueue.main.async { if let data = data, let jsonData = try? JSONSerialization.jsonObject(with: data, options: .allowFragments){ // your non-consumable and non-renewing subscription receipts are in `in_app` array // your auto-renewable subscription receipts are in `latest_receipt_info` array } }
Here you can view an example of JSON receipt with two transactions for auto-renewable subscription.
latest_receipt_info
contains all transactions including subscription renewals.in_app
contains transactions for non-consumables and non-renewing subscriptions. There will also appear only the first receipt for your auto-renewable subscription. Consumables will also appear in this array but will disappear after developer finishes transaction.In Apphud we implemented App Store receipt validation for apps with auto-renewable subscriptions in our open-source SDK. Besides that, Apphud helps to track subscriptions, analyze key metrics, grow your revenue by reducing voluntary and involuntary churn, etc. You can start using Apphud for free.