checking if an auto-rewew subscription expired

to check if an auto-renew subscription expired, it seems like it should be like 1 function call


everywhere i look there are pages and pages of complicated libraries and flowcharts and callbacks and crypto functions invoked


it just seems like there is a level of API totally missing: "verify if this subscription feature (text string input) has expired for this user today, return Yes or No"


is there a way that apple recommends without rewriting super-secret crypto codes and debugging all this?

You have to ask this question from the perspective of the app or from the perspective of a server that services the app. I assume you are asking from the perspective of the app. There are two approaches - one is documented and the other is 'recommended on this forum by an Apple expert' but not documented


Documented:

1) If the app detect that the subscription has expired it adds a transaction observer and awaits a call to updatedTransactions with a 'purchased' transaction. If it does not come in the background in a few seconds then the app tells the user that it needs to check the subscription to see if it has expired. The user taps 'ok' or 'cancel'

2) the app does a 'restoreCompletedTransaction' - (the user will be asked to log into the App Store)

3) the app awaits a call to updatedTransactions or to paymentQueueRestoreCompletedTransactionsFinished. If it gets a call to updatedTransactions with a 'restored' status then the subscription is current.

4) you are welcome (and advised) to verify the receipt to protect against a fraudulent call to updatedTransactions. That requires decoding the receipt or transmitting the coded receipt to Apple and receiving a decoded receipt back. This is optional and for your protection so don't complain about how hard it is. Hackers can push a call into your updatedTransaction method but they can't fake a coded receipt.


Undocumented

1) the app grabs the receipt and sends it to the apple servers for decoding. The decoded receipt is alledged to contain latest_receipt_info which will indicate whether or not there is a current subscription. This field is not documented for iOS7 and later receipts but it seems to be there anyway and Rich Kobata on this forum has suggested that it is a permanent fixture in the receipt response - I would not rely on it.

hmm, we are looking to check auto-renewable subscription state within the app, e.g. "has this subscription expired"


we dont have a server for this. we were wanting the app to communicate directly with apple's itunes services to check for auto-renew subscription

status, rather than duplicating all that subscription information on our own server. it seems like a bad practice to maintain a duplicate database of all

the subscription information when apple already has it.

I assumed that was the case. Follow what I wrote above. I recommend the documented approach.

The circular logic problem seems to occur in step 1:


"If the app detect that the subscription has expired"...


this is the task we are trying to perform. we dont know if the subscription has expired.

Ok...will this do - "If the app does not indicate that the subscription is active".


The app will start out assuming that there is no subscription. You will go through this procedure and discover that the subscription, purchased on a different device, is active until, let us say, March 13th. The app will write that date in its memory somewhere as its expiration date. Each time it enters foreground it will check the current date against the expiration date and if it indicates that it has expired it will go through this procedure.


Once a device registers at the app store through a restoreCompletedTransactions it 'should' receive regular purchase transactions at each renewal. Sometimes this fails.


Apple recommends that you always maintain an observer so that you can detect the renewal if it comes in. However, this doesn't change the above procedure in those cases where the renewal, for whatever reason, is not sent to this device. And 'always maintaining an observer' is, IMHO, not best practice. Others disagree.

so as a start on this, I tried this code:


after making a sandbox purchase (success) i restart the app and run this code at Next app launch in app delegate at start


NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];

NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];

if (!receipt) { / No local receipt -- handle the error. */

printf("no local receipt\n");

}

else

{

NSString * strrec = [[NSString alloc] initWithData:receipt encoding:NSUTF8StringEncoding];

printf("recp = %s\n", [strrec UTF8String]);

}


i get this back in xcode console

recp = (null)

so it shows a receipt stored, but the receipt is a null string

it seems that sandbox purchase receipts are not automatically stored in-app...or are All receipts not automatically stored?


also does anyone know why the sandbox environment keeps prompting for password every time we query for a receipt?


if this is the production behavior, it is going to annoy the end users and disrupt workflow.

fy,i if this may help anyone, we found this module which helps pull the receipt/s and decrypts it/them:


https://github.com/rmaddy/VerifyStoreReceiptiOS


or if you cant see the link, look for VerifyStoreReceiptiOS on github


we were able to test this code, and here we can see the expiration dates of the subscription in the receipt in plain text in our app

which should be convertable into NSDate w/ the proper date formatter.


it seems to return all transactions, so we would have to sort thru this next to determine the most recent...so there is that work to do

and to obfuscate the code as the original author suggests. there is a ton of crypto stuff in there already, but i guess that's not enough to avert hacking.

>so it shows a receipt stored, but the receipt is a null string

I'm not sure that your attempt to turn an NSData object that is not an encoded string into a UTF8String is correct. You alloc an NString but you may not be filling it with anything - hence Null.
>it seems that sandbox purchase receipts are not automatically stored in-app

The sandbox purchase receipt gets stored there only if you do a receipt referesh or you get a transaction sent into updatedTransactions

>why the sandbox environment keeps prompting for password

that is to be sure that the person asking for the receipt is entitled to get a receipt from that iTunes Account. You can't share your subscription with your friends unless you are willing to let your friends log into your iTunes Account.

>if this is the production behavior,

yes

>it is going to annoy the end users and disrupt workflow.

if the device has ever called restoreCompletedTransactions it should receive a transaction at each renewal and there will be no need to log in. But don't rely on that. If you detect an expired subscription, and only when you detect an expired subscription, you need to ask the user to log into their account. So the disruption occurs only when the subscription needs to be renewed and for some reason the App Store hasn't sent the renewal transaction.

The App Store Server API's Get All Subscription Statuses endpoint returns the statuses (active, expired, billing retry, grace period, and revoked) for all of a customer’s auto-renewable subscriptions in your app. For more information, see the WWDC24 session Explore App Store server APIs for In-App Purchase , WWDC23 session Meet the App Store Server Library., and Simplifying your implementation by using the App Store Server Library.

To determine the status of an auto-renewable subscription in your app, call the endpoint with a transaction identifier or original transaction identifier from your server. To retrieve the transaction identifier in the Original API for In-App Purchase, call the transactionIdentifier property of your SKPaymentTransaction object.

To retrieve the original transaction identifier, see originalTransactionId. If you have a current receipt, use the App Store Server Library to extract the transaction identifiers. For an example, see the WWDC21 session Manage in-app purchases on your server starting at 08:00.

Important: The Original API for In-App Purchase is deprecated. For more information, see the WWDC24 session What’s new in StoreKit and In-App Purchase.

checking if an auto-rewew subscription expired
 
 
Q