Auto-renewing subscriptions in iOS are confusing. These are my notes on how the process works and other tips.
NOTE: This doc is only for iOS7 and up.
All current subscription information is stored in one big receipt – found at
You need to “verify” this receipt to see whether their subscription is active.
When the app is first installed, that receipt probably doesnt exist at that URL yet – so you have to load it using
The receipt will always be created once you refresh it.
Even if the user purchased a subscription on another device, refreshing it would load that subscription onto the current device.
When you refresh the receipt, you are likely prompting the user to sign in, so dont refresh without a good reason.
If you refresh and its still invalid, then that means they either dont have an internet connection or they dont have an active subscription.
NOTE: To test in-app purchases, setup a test sandbox account in iTunes Connect and test on a real device (simulator wont work). Dont sign in via the settings app. Sign out via the settings app. Then just login when your app prompts you.
appStoreReceiptURL receipt is super encrypted so you can decrypt it locally (hard) or send it to Apples server for decryption.
If you do it locally, youll probably want to use RMStore as a reference. The decryption process is intentionally very difficult and its bad to use an open source library because that would make it easy for someone to
noop your validation process (thus getting a free subscription).
The other bad thing about local verification is that any time it goes out of date, youd need to refresh it (which triggers the user to enter their password).
It appears that the local receipt will be updated automatically without a refresh in some situations, but not always.
Apple recommends you verify the receipt by sending it to their server. You send the receipt and your iap secret key and Apple will send back the decrypted receipt in a nice JSON format.
The big bonuses are (1) you dont have to mess with the difficult decryption process and (2) the Apple server will return a
latest_receipt_info field which includes the updated receipt. So with the server you are guaranteed to be working with the latest version of the receipt – and the user doesnt have to enter their password.
Ive also noticed that even after a refresh, the local receipt has incorrect fields. For example, the
is_trial_period field is false on the local receipt after a refresh but is correctly “true” on the latest receipt from the server.
So what does the actual receipt look like? How do subscriptions, trial periods, and cancellations look in the actual receipt?
Apple lists the Receipt Fields in the docs but its difficult to understand what the full server response will look. So Ive included example receipts below for reference:
Heres what my server-verified receipt looks like before I purchased anything:
If the users begins the payment process (via
SKPaymentQueue.addPayment) but cancels before confirmation then there will be no record in the receipt.
Regardless of whether the payment cancels/confirms, the
paymentQueue updatedTransactions callback will be called. UpdatedTransactions will contain an errored transaction if cancelled. I ignore the transactions in the callback, and just use this event to tell me I need to check the receipt again.
Distant Future Receipt
After a lot of time passes (multiple re-subscription periods), the receipt file (within the app) didnt have any of the new receipts for the auto purchases. Its possible that this file will be automatically updated, but not guaranteed. As always, you can refresh the receipt to get the latest info or you can verify your old file via Apples server and get the new info from
So it appears that once you have the original receipt, you never need to refresh it (unless the user signs in with a different account, i guess).
Heres the result of validating that local receipt file with Apples service:
App Version Changes
Part of the validation process is making sure the receipts
application_version matches the current apps version.
When the user installs an update, the receipt file is not automatically updated with the new application version. You have to refresh the receipt locally to get the version number to match (server verification wont change the version number).
My feeling is that you should just ignore the application_version validation. Otherwise youll have to prompt the user for their password on every version update.
Say you have an app that requires an active subscription:
App Loads If CACHED_EXPIRATION is in the future Grant Access! If LOCAL_RECEIPT is missing Refresh LOCAL_RECEIPT If LOCAL_RECEIPT fails to load Show error "unable to connect" Use LOCAL_RECEIPT to load SERVER_RECEIPT If SERVER_RECEIPT fails to load Show error "unable to connect" Find EXPIRATION_DATE in server receipt Update CACHED_EXPIRATION to EXPIRATION_DATE If CACHED_EXPIRATION is in the future Grant Access! Otherwise Show "Buy Subscription" Button
I referenced the following articles when learning this myself: