how to test subscriptions

Hi,

We are able to make a IAP for a subscription with a sandbox user. With the supplied receipt data for that sandbox user's subscription, how are we supposed to determine if their trial period is over, if their subscription is active? When we parse the receipt data successfully, we set nil values for expires_at, expires_date, is_free_trial. Also, if the user deletes the app and unsubscribes via iTunes account, how is our backend supposed to receive the cancellation notice?


Thanks.

For a non-renewing subscription the app determines when the subscription expires. For exxample, it might take 'now' and add 30 days:


expiresDate=[NSDate dateWithTimeIntervalSinceNow:30*24*3600];


For an autorenewable subscription you add the time interval associated with the subscription to the purchase_date.


At the end of this period you assume the subscription has expired unless the user repurchases. That repurchase is automatic in the case of an autorenewable.

How can we notified of the renewal if the user autorenews but doesn't open up the app in the next month? From our understanding, we can only receive receipt data for the latest purchases/renewal if the user re-opens our app. Also, how will we know they have cancelled if they unsubscribe on iTunes and delete the app? We would not receive the latest receipt then. THanks.

You have two paths 1) get and store any receipt from the user. Send that receipt to the Apple servers. The Apple servers will respond with the latest receipt. 2) rely on the app to keep track of its expiration date and request service from your server only if it has a valid subscription. You don't need to discover a cancellation. You only need to record a renewal and an expiration date.

Thank you for the information so far. What is the API and its documentation for sending a receipt to Apple?

Take a look at Tech Note 2413 <https://developer.apple.com/library/ios/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPTURL>

Make sure to implement the algorithm as described. As for sample code, please take a look at the Receipt Validation PG

<https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW3>

Also note, that as you have an auto-renewing subscription item, you will need to include a "shared-secret" with the request - which is not a part of the code sample in the Receipt Validation Guide. The format of the jsonObject is going to be something like


"password":"xxxx" "receipt-data": "xxxx"


You can obtain the "Shared Secret" from your iTunesConnect account. One important note - Once you generate the Shared Secret - and if you decide to have the app perform the receipt validation process firstly (which is not per the Receipt Validation Guidelines) - do no lose that Shared Secret and more importantly, do not regenerate another Shared Secret.


The preferred method of receipt validation is to have the app send the receipt to your server. Your server would append the Shared Secret to the payload, and forward the receipt to the Apple Receipt validation server for processing. If the Shared Secret is ever regenerated, the change can be applied at your server. This isn't possible if you had code the Shared Secret in your app.


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

Is it possible for an iTunes user to have mutliple base64 values for their receipts? Should we be storing User.hasMany(AppleReceipts) or User.hasOne(AppleReceipt)? Thanks!

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

Hi Rich,

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.

Would sending the outdated receipt to Apple result in an up-to-date list of all in app purchases or just the purchase history up to the time when the receipt was generated?


Here's an example to explain:


I purchase IAP A and C, send the receipt X to the server, the server keeps it for future validation. I now purchase IAP B and get a refund for purchase C. My current receipt is now Y. The server now sends X to Apple. Does it receive:


  1. A and C
  2. A, B and C (canceled)


Thanks in advance,

Omer

With one exception you will get #1; the server just decodes what is in the old receipt.

The exception is for an old style deprecated receipt (transaction.transactionReceipt) when the user purchased an autorenewable subscription. Then you will also get a field labelled "latest_receipts". That will be #2 to the extent that B and C are related to an autorenewable subscription. (But I don't know if B appears if B is not related to an autorenewable but A and C are.)


Your posting does not appear to be included in the forum itself, perhaps because the thread is so old. I received an email with your question.

Rich is welcome to comment at his leasure.

Omer,


In reviewing your question, I don't see a description for the in app purchase type associated with iAP items A and C. Since the topic was about the applicationReceipt, then I agree with PBK - you'll see case 1. IF item C were an auto-renewing subscription and the refund was issued within the subscription period of item C, then I would expect the in_app array element for item C to include the cancellation_date field to be set. However, if item C were to have renewed one period - item C2, then I would not expect the cancellation_date field to be set for C1 (the original purchase of the auto-renewing subscription).


It's my experience that the Receipt Validation server only works with data that is present in the receipt that is passed to it. There is no making the receipt "current" as I understand. The content returned by the receipt validation process is managed by iTunes and not by API so the support of this issue is a bit trickier for me.


One experiment which you migth do is to implement an test app which has the option to validate the appleication receipt immediatelty on launch, before the transactionObserver is installed. There won't be a receipt the first time the app is tested. Make a purchase of a short term auto-renewing subscription - like 1 month (which renews in 5 minutes in the sandbox). Check the applicationReceipt after the purchase to verify that the subscription is present. Then kill the app. Wait an hour - by which time the remaining 5 auto-renewals should have occurred. Launch the app again and see what's in the validated applicationReceipt - before the app has been notified of the auto-renewals. I suspect there will only be the single item in the in app array. This will simulate the validation of the applicationReceipt where the contents differ from the current in app purchase history for the user/app combination.


You will also be able to see how the latest_receipt_info section of the validated receipt was handled in the sandbox. In this case, I suspect that the production management of the receipt will be handled in the same manner as you observe in the sandbox.


I'm planning to try this myself later on to see what results.


rich kubota - rkubota@apple.com

developer technical support CoreOS/Hardware/MFI

how to test subscriptions
 
 
Q