To answer your question - an iTunes user will have only one "current" application receipt. That receipt must be base64 encoded before it's sent to the Apple Receipt Validation server for processing. If the base64 encoded receipt is saved on the developer's server, then there may be "outdated" copies of the base64 encoded receipt accumulating on the server - but there can only be one "current' version on the server.
Now for some clarification - there is the orginal transactionReceipt which was(is still) returned with a successful transaction state to the updatedTransaction delegate method, and there is the application receipt - referenced by appStoreReceiptURL. The transactionReceipt is considered deprecated as of iOS 7, however this style receipt is still returned with a successful transaction to the updatedTransactions delegate method.
One of the benefits of using this transactionReceipt for auto-renewing subscriptions, was that it could be passed to the developer server and stored with an account - periodically, the receipt on the server could be validated from the server. In the case for auto-renewing subscription transaction receipts, the Apple Validation server would also check the store to determine whether the subscription had renewed or return status 21006 - the subscription had expired. Very handy - unfortunately, the transactionReceipt was an insecure process.
Things changed starting with iOS 7. All StoreKit transactions were encoded - which made capturing Charles logs no longer as useful as they were under iOS 6. The StoreKit API also deprecated the transactionReceipt in favor of using the application receipt referenced by appStoreReceiptURL.
The application receipt process changes some things, specifically with regard to how auto-renewing subscriptions are supported. One issue with the application receipt is that for auto-renewing subscriptions, as the subscription renews, a new receipt item is added to the in_app array. So if the subscription renews on a monthly basis, the in_app array grows by one element every month. So how does the app find which in_app array element describes an active subscription - Let’s first state that each receipt item in the in_app array has to be treated on it’s own. To find that active item, the app needs to iterate through each item - check the in-app purchase identifier to determine the product type. For all items, check first for a cancellation_date field and if set, then move to the next item. Keep in mind that the presence of a cancellation_date field applies to the one in_app array item only where the date is set.
Next check the in-app item identifier - is this non-consumable, auto-renewing subscription, consumable, etc - treat the item accordingly. For the case the in-app identifiers are for auto-renewing subscriptions - the app must check the expiration date field to see if the current date is earlier - indicating an active subscription.
One other note, do not make an assumption as to the order of the items in the in_app array. It is a mistake to assume that the most recent item in the in_app array is the last item.
Lets also look at the utility of the application receipt to the transactionReceipt. I noted one advantage of the transactionReceipt where the same receipt for an auto-renewing item could be validated numerous times and the expiration date field updated. Not so with the application receipt items or at least those items in the in_app array.
For the application receipt, when an auto-renewing subscription renews, the StoreKit posts an incompleteTransaction to the records for that user. When the app launches, a well behaved in app purchase app calls addTransactionObserver in the didFinishLaunchingWithOptions delegate method. The transactionObserver will check for an incompleteTransaction and detect the renewal item as new purchase which results in a call to the updatedTransactions delegate method.
So lets assume that the app can’t find an active auto-renewing subscription item in the app array. If in previous use of the app, there was an active subscription, the app can advise the user that the receipt appears out of date and ask whether the user would like to “refresh” the receipt. If the user agrees, then the app issues the SKREceiptRefreshRequest. When ths process completes, the app rescans the app receipt.
rich kubota - rkubota@apple.com
developer technical support CoreOS/Hardware/MFI