What to do with transactionReceipt deprecation for IAP? - ios

I am currently validating transaction.transactionReceipt base64encoded as a string server side. The problem is that transaction.transactionReceipt is deprecated now and I don't know what to replace this with for in app purchases.
The new documentation suggests using NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
, but the docs say that it is only for app purchase validation and not for in app purchases.
What do I do in this case?

As can be read in other parts of the documentation the receipt does contain the data for IAP as well. I've also verified this from my own experience, as this is the way we did it where I worked. Basically the only change is that instead of having a receipt for each IAP there is a single one for both the app and all IAPs.
My guess however is that the URL remains the same, although I haven't verified it, but the data contained at the URL changes.

Related

Why is the Apple AppStore in-app receipt empty on iOS?

In application I need to implement auto-renewable subscriptions purchases.
As I understand documentation, I should check AppStore receipt on app launch and then validate that receipt with my server.
In my code I do following to retrieve receipt:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
Sometimes receipt is becoming nil.
For example:
I've launched an app and made purchase. After that the code above returns non empty receipt.
App is stopped with Xcode.
On next app launch receipt is nil.
I do not understand, why this happens. receiptURL points to file with receipt, but [NSData dataWithContentsOfURL:receiptURL] returns nil.
If I request receipt refresh, then app shows dialog for entering Itunes credentials. This is not good, because this dialog will appear on app launch and this is unexpected behavior.
Am I doing something wrong?
Or maybe this is sandbox-specific case?
In production there will always be a receipt (even if the app is free), that can contain your additional purchase, in case you have bought something or restored from an already purchased item.
In test environment until you buy something the receipt is nil, if you want to test a case close to production, you should first try a restore (now you will have a receipt) and do what you need.
Here it is suggested that your payment procedure is being hacked, by calling the SKPaymentTransactionStatePurchased regardless. You need to validate the receipt on a server before accepting any receipts.
You can find a great description of receipts and hacks here.

iOS 7+ server verified inapp purchasing sending many receipts

I am trying to get the new inapp purchasing API (ios 7+) to work and I see that the receipt data is so much larger when I read receipts in this way:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
I am doing this inside of:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
So what I noticed is I send the receipt to
"https://buy.itunes.apple.com/verifyReceipt";
And the data I'm sending is much larger than transaction.transactionReceipt.
In fact, the apple response seems to have dozens of receipts in the "in_app" array. Is this normal? I have finished these transactions yet there is so many receipts in the unified receipt. Is that right? Would my big spenders (those who buy thousands of IAPs) be sending huge receipts with thousands of transactions in them?
Also what is SKReceiptRefreshRequest for?
Update! I found out that in a live environment, all consumable receipts are removed after they calling finishTransaction. On the sandbox environment this is not the case, and it just keeps them, so the number of receipts can get into the hundreds.
iOS7 introduced many IAP changes. Amongst those:
1) receipts are stored in the app bundle (yes, plural on receipts)
2) whether a particular receipt persists in the bundle depends on the kind of product. Eg, I think receipts for consumables don't get persisted
What kind of products do you have?
Look up the API for the receipt refresh. Sometimes the receipt is not in the bundle, and you can refresh it. The user gets prompted for apple id/password so it can be annoying for the user if/when you use it.
Yes, this is normal
Apply sends you a bunch of all receipts. You have to figure out your own what to do with these. That's because even if the transaction is finished you might need the receipt to handle the content of the purchase.

Auto-Renewable In-App Purchases in iOS

I am currently stuck implementing IAP for my App. Here is the basic information on how it works
I am offering a one-year auto-renewable subscription to a service. The user will pay the IAP and they'll have access to the service.
It should be possible for a user to purchase multiple subscriptions. It is possible (although unlikely) that the user will want to connect to two different services, so he should be able to pay for two subscriptions at the same time
That's the basic idea, sounds pretty simple. So I have followed Apple's instructions with great success, up until the point where paymentQueue:updatedTransactions gets called with the SKPaymentTransactionStatePurchased state. Here, several questions arise.
Apple's documentation shows that you should store transaction.transactionReceipt in NSUserDefaults. So I'm storing an array of these (since I want to support multiple, simultaneous subscriptions). The problem is that this has been deprecated in iOS7, so I don't know what to use instead.
The next problem is that I don't know what to do with this data. Once my App is killed and re-started, all I know is the size of my array in NSUserDefaults (e.g. there are two receipts, so two subscriptions have been purchased). However, how can I read the data in the receipt number to do some verification on my own server? How to I get the date range for which the subscription is valid? (I need to block access once the subscription is no longer valid, assuming the stop the auto-renew and it expires). All I have are NSData objects that I have no idea what to do with.
Thank you for your help!
You have a way to go. The purpose of saving the transaction.transactionReceipt was to submit it at some point in the future to the Apple servers. The Apple servers respond to a receipt by sending the latest receipt for that subscription. Now that transaction.transactionReceipt is deprecated, you get the receipt from the onboard receipt:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
and either decode that (using OpenSSL and other C++ stuff) or, once again, send it to the Apple servers for decoding and responding.
An easier approach, and perhaps one more acceptable to App Review for a 'service', would be to use a non-renewing subscription. Non-renewing subscriptions can overlap so your problem with 2 services can be easily handled by the app.

What's the correct way to use RMStore to get the original purchase date of an app?

I'm releasing an updated version of my app, and I'm transitioning from paid to freemium. To give existing users an ad-free experience, I'm looking to track when they've originally purchased the app.
I'm looking into RMStore, but it's not clear to me how to test reading the original purchase date out of the receipt. I've come up with some simple code which I think should work, but I don't have a good way to test it.
[[RMStore defaultStore] refreshReceiptOnSuccess:^{
NSURL *url = [RMStore receiptURL];
NSData *data = [NSData dataWithContentsOfURL:url];
RMAppReceipt* r =[[RMAppReceipt alloc] initWithASN1Data:data];
// Cheap and easy conversion to a float...
// IRL do a real comparison with the strings...
if ([[r originalAppVersion] floatValue] < 2.0)
{
// Do something for early-adopters
}
} failure:^(NSError *error) {
// Ruh-roh!
}];
I have two issues:
I don't have a valid receipt. What's the procedure for getting one? Do I need an app bundle ID that's already live? Are there test receipts somewhere?
If I want to base the logic on dates instead of version numbers, can I do that? There's no originalPurchaseDate property on RMAppReciept. (I did file an issue on GitHub.)
What's the correct way to use RMStore to get the original purchase date of an app?
There is no such information in the receipt. You can get the purchase date of in-app purchases, though. RMStore helps you with this via the RMAppReceiptIAP object.
To give existing users an ad-free experience, I'm looking to track when they've originally purchased the app.
As suggested by #sergio, you can read the original app version from the app receipt. Bear in mind that receipts are only available in iOS 7.
I don't have a valid receipt. What's the procedure for getting one? Do I need an app bundle ID that's already live? Are there test receipts somewhere?
Apps in sandbox environment will have a test receipt, but you can't manipulate its fields.
As an alternative, you can configure RMAppReceipt or mock it to test your various workflows.
If I want to base the logic on dates instead of version numbers, can I do that? There's no originalPurchaseDate property on RMAppReceipt.
Not with the app receipt, as there is no such field at app level.
BTW, avoid refreshing the receipt on startup as it tends to show the Apple ID password prompt. Only refresh if you don't find the receipt or if the info is missing.
Maybe you want to try with:
RMAppReceipt* appReceipt = [RMAppReceipt bundleReceipt];
if ([[appReceipt originalAppVersion] floatValue] < 2.0)
/**
Returns the app receipt contained in the bundle, if any and valid. Extracts the receipt in ASN1 from the PKCS #7 container, and then parses the ASN1 data into a RMAppReceipt instance. If an Apple Root certificate is available, it will also verify that the signature of the receipt is valid.
#return The app receipt contained in the bundle, or nil if there is no receipt or if it is invalid.
#see refreshReceipt
#see setAppleRootCertificateURL:
*/
+ (RMAppReceipt*)bundleReceipt;

Is it sufficient validate iOS 7 receipt in my own server?

I'm implementing security in my iOS 7 application for iOS In-App purchases. When testing with LocalIAPStore hack I see that when I call the method [[NSBundle mainBundle] appStoreReceiptURL] the returned receipt is null, so it's all right here.
The problem comes when the user purchases an option without having activated LocalIAPStore. If this is done, a receipt is generated. Then, I send it to my server to check that everything is correct and show purchase, but if active again LocalIAPStore, I keep returning a correct receipt, but has actually purchased another option and this is returned as a calidate.
To verify receipt, what else can I do? What should I check locally to avoid it? Any tips? How do you do it?
Thanks!

Resources