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.
Related
I have a side project and I recently worked on my receipt manager to make it stronger and to relies more on the receipt of the app rather than persistently storing a value after a transaction.
However, there are 2 main points which although I read Apple docs and other answers on the web, I'm still confused about:
1. When a user restore their purchase, does the receipt get refreshed?
I made several tests in sandbox, and I have seen that when restoring, the receipt gets refreshed, and when I verify the receipt through the iTunes server verification, it returns a JSON including the latest transactions. This is very helpful because even if I close/open the app, the app receipt is updated and I can always verify it without refreshing it.
However, in production, this didn't work. The app receipt wasn't refreshed after restoring purchases and my users got asked to restore purchases continuously. Can anyone answer on this point?
2. Does the refresh receipt request triggers an alert asking for the Apple ID's password in production?
From the previous point, I thought ok, I will force receipt refresh after a user restores their purchases. However, in development / sandbox, I get asked to insert my sandbox user's pass every time I try to refresh the receipt (although I could restore purchases without a password request before asking for the refresh). I read a lot about this and someone says that might not happen in production. Does any of you have a clarification on this?
Note:
I know that when restoring / purchasing I get back a transaction with the receipt, however, I need to use the App Receipt to verify transactions (and this is also what Apple says to do).
Thank you in advance.
1. Refreshing the receipt
In theory, calling restore purchases should get the latest receipt. In the instances where you are experiencing issues, take a look at SKReceiptRefreshRequest. Typically, I use this in production when a call to restore purchases has encountered errors.
Use it wisely, triggering that API can lead to showing the Sign In prompts for the App Store.
2. When is the user asked to sign in?
Sadly, I have seen this vary so I cannot give a definitive answer. More often than not, a call to restore purchases should not trigger a sign in. Explicitly using SKReceiptRefreshRequest will.
If the user is not signed in to the store, calling any Store API like attempting a purchase or restoring purchases could trigger the sign in flow.
What Apple Says
From the docs
Refreshing a receipt doesn't create new transactions; it requests the latest copy of the receipt from the App Store. Refresh the receipt only once; refreshing multiple times in a row has the same result.
Restoring completed transactions creates a new transaction for every transaction previously completed, essentially replaying history for your transaction queue observer. Your app maintains its own state to keep track of why it’s restoring completed transactions and how to handle them. Restoring multiple times creates multiple restored transactions for each completed transaction.
My Recommendation
Store the hash of the last receipt you used on device. You can use this hash to check against the latest receipt so you know if anything has changed. Whenever your App resumes you can always check if the current receipt hash is different from the last cached value.
Try and submit the receipt as soon as possible. Typically when the App has launched.
If a user tries to manually restore purchases, I would start with a call to restoreCompletedTransactions. This can trigger an App Store sign in but is often less likely. Most of the time this is enough as the receipt on the device is often pretty up to date.
If the user tries another restore purchases, OR if the call failed, then move to SKReceiptRefreshRequest to guarantee a fresh receipt.
When using SKReceiptRefreshRequest, I would recommend wrapping this behind UIAlertController. I normally show something that indicates it has failed and have a "Retry" button that uses the request. This will trigger a new store sign in.
Use restoreCompletedTransactions to get a playback of all completed transactions the device is aware of.
When a user restore their purchase, does the receipt get refreshed?
Yes, it should. But it also sounds like you're doing some server-side validation? If that's the case, you can send any receipt from the user to the /verifyReceipt endpoint to get the latest status. You don't need to send the latest receipt, since /verifyReceipt will also refresh it.
Does the refresh receipt request triggers an alert asking for the Apple ID's password in production?
There's no clear Apple documentation on this, but it definitely will if there's no receipt file present in the app (rare in production). But if you're doing server-side validation (see #1), then you can send up any receipt you have, no need to refresh it. So you're only refreshing the receipt if nothing is present, which will trigger the sign-in. Keep in mind a receipt file is not present on the device after installing in sandbox - only after a purchase. This differs a lot from production where a receipt file is generated after installation.
From what it sounds like you're trying to do, my recommendation would be to check if any receipt file is present at launch, send it to /verifyReceipt to get the latest status for the user and cache the result. You can do this on every app launch.
In a perfect world you're storing the receipt server-side and keeping it up-to-date there, but you mentioned side project so that sounds like overkill. However, an out-of-the box solution that correctly implements all of this and will scale with you - such as RevenueCat - is another alternative (Disclaimer: I work there).
After many tests and after I sent my app in production, I'm now able to answer my questions properly:
1. When a user restores their purchase, does the receipt get refreshed?
YES, this is immediate as for Sandbox, BUT the problem is that the receipt DOESN'T include non-consumable purchases.
This means in other words that the receipt will include the purchased subscriptions, but you won't find purchases of non-consumable products.
However, when the user restores or purchases, you get the transactions in return, and you can extract the non-consumable products, and store this info somewhere like UserDefaults or Keychain, so you can use them when the user opens your app.
For the rest, the best approach is to always validate and check the receipt when the app is opened.
2. Does the refresh receipt request trigger an alert asking for the Apple ID's password in production?
YES. For sure it does the first time.
Thank you to Daniel and enc for their answers that can still be useful.
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.
I am in the process of updating my app to new iOS7/8 in-app purchase libraries. I see now that as of iOS7, we have access to appStoreReceiptURL as part of NSBundle.
It appears I can access this URL, and its concomitant data, at any time, without interacting or interfacing with the SKPaymentQueue.
Previously, when a customer installed our app and wanted to restore his in-app subscription, the app would call the restoreCompletedTransactions method of [SKPaymentQueue defaultQueue], and from that obtain receipt information, one transaction at a time, that the app would upload to our server one transaction at a time.
However, while testing in the iOS App Store Sandbox, now I appear to be able to obtain one piece of master receipt data from [[NSBundle mainBundle] appStoreReceiptURL], upload that to my server, obtain a complete history of every in-app transaction this user has made, and then record transactions on my server as appropriate and send notification back to my client.
Consequently, why or when would anyone need to call restoreCompletedTransactions? In my app, I sell a single, auto-renewing in-app subscription; are there other use cases today in iOS7/8 for which this API is still helpful?
If a user has made a purchase on a different device, it can be useful (or necessary) to "restore" that purchase on a new device. restoreCompletedTransactions could enable this restore operation. By default, on this new device, I believe the >= iOS7 style receipt will not have those prior purchases. On >= iOS7, SKReceiptRefreshRequest will work in many cases.
From Apple:
In most cases, all your app needs to do is refresh its receipt and
deliver the products in its receipt. The refreshed receipt contains a
record of the user’s purchases in this app, on this device or any
other device. However, some apps need to take an alternate approach
for one of the following reasons:
If you use Apple-hosted content, restoring completed transactions
gives your app the transaction objects it uses to download the
content.
If you need to support versions of iOS earlier than iOS 7,
where the app receipt isn’t available, restore completed transactions
instead.
If your app uses non-renewing subscriptions, your app is
responsible for the restoration process.
See https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Restoring.html#//apple_ref/doc/uid/TP40008267-CH8-SW9
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.
The following extract is from the Apple documents how to restore in App purchases in iOS 7. They say that you can store the receipts and find out later what they user already bought or you can refresh the App Receipt. My questions are:
1) Is there the one app receipt or are there many single receipt?
2) Does it make more sense to store these receipts instead of just storing directly which features the user has already bought
3) How can I examine the receipt that I get from that refresh? All my tries to parse it failed so far.
4) Is there any framework that has a lot of features (saving receipts / bought products on iCloud, making purchases easier, handling downloads, handling all kinds of connection problems, etc.) already? Maybe even with remote server support (and validation).
I hope somebody can help! :)
Refreshing the App Receipt
Create a receipt refresh request, set a delegate, and start the request. The request supports optional properties for obtaining receipts in various states during testing such as expired receipts—for details, see the values for the initWithReceiptProperties: method of SKReceiptRefreshRequest.
request = [[SKReceiptRefreshRequest alloc] init];
request.delegate = self;
[request start];
After the receipt is refreshed, examine it and deliver any products that were added.
1) Is there the one app receipt or are there many single receipt?
There is a single app receipt. Before that, Apple provided receipts for each transaction. Those receipts still exist but are deprecated.
2) Does it make more sense to store these receipts instead of just storing directly which features the user has already bought
Not anymore (for iOS 7 apps).
3) How can I examine the receipt that I get from that refresh? All my tries to parse it failed so far.
This is a complex issue. I suggest reading this unapologetically long answer: https://stackoverflow.com/a/20039394/143378.
4) Is there any framework that has a lot of features (saving receipts / bought products on iCloud, making purchases easier, handling downloads, handling all kinds of connection problems, etc.) already? Maybe even with remote server support (and validation).
There are 3 that I'm aware of:
CargoBay
RMStore
MKStoreKit
To date, MKStoreKit appears abandoned and CargoBay doesn't support app receipts. CargoBay has features that RMStore hasn't, and viceversa. I'd check them both and see which one fits your requirements better.
In any case, I recommend reading the StoreKit documentation before using any libraries. The libraries provide code, not understanding.
Disclaimer: I developed RMStore.