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.
Related
The Apple docs on receipt validation say to perform receipt validation immediately after launch. This amounts to checking for data at the path returned by [[NSBundle mainBundle] appStoreRecieptURL], refreshing via SKReceiptRefreshRequest if it's not there, and validating it. The aforelinked docs reference both iOS and macOS.
Is it actually necessary on iOS? If so, why? Is it to prevent users from using my app on a jailbroken device, or without having bought it from the app store (in which case I probably don't care if my app is free)? Or does it have implications for other operations like restoring or validating in-app purchases? For example, does the receipt data have to be there already in order to validate a transaction for an in-app purchase?
Note: I am not using in-app subscriptions. I have in-app purchases, but I don't use the receipts from them after verifying them and recording the purchase server-side.
you don't need to do so, that is optional only and could be done on iOS7+, if you interested in doing that.
briefly, implementing the validation is purely a financial decision, and even if you validate recipes you are recommended not to disable the content in case of failure as the validation could fail in standard environment as well anytime (e.g. in case of no connection), and such overreaction may ruin your consumers' experience.
altogether, doing the validation'd rather make sense on OSX in practice when you are allowed to disable the content in case of failure, regardless the reason; but if you feel you have more consumers than your income suggests or the amount of the stolen content is way beyond your margin, it may be worth to do it on iOS as well.
NOTE: in general you can read more about the technical details of receipt validation in Apple's Documentation.
The simple answer to your question is NO, It is not necessary.
Here is the detailed explanation for you.
According to Apple Documentation here, apple has given two methods of validating the receipts, they are given it as guidance only to prevent unauthorized copies of your application from running. For more guidance apple has pointed towards the apple review guidelines.
In the review guidelines here, Apple does not mention anything related to the verification of receipts as mandatory.
If at all the receipt validation is mandatory, Apple would have provided a simple API to validate it.
Titbit: You can see a lot of rejections when it comes to app store receipt validations. But all those issues are because the receipt validation is not done properly.
But my personal recommendations will differ from the above answer. First thing for you to consider is "Never underestimate hackers". Validate the receipts several times apart from the start of the app. Read this article for more information.
You would generally only validate the receipt to prevent piracy by users who haven't bought your app or if you are using auto renewing subscriptions.
Whilst you can interrogate the receipt for IAP information, its actually easier (and required by app review) to give a button somewhere to "restore previous purchases" and call
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
It is also a better UX for the user to possibly be prompted for their Apple ID and password having pressed a "restore purchases" button rather than unexplained at app startup.
The reasons I would give are:
The user can change their product if you are using subscription groups from outside of your app.
An auto-renewing subscription can renew outside of your app.
The app does not receive transactions initiated outside of the app until the app restarts.
The user can make purchases on another device using the same appleID whilst outside of your app on the current device.
Within our appDelegate:application didFinishLaunchingWithOptions we initialise a class that calls
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
to monitor for purchases initiated from within the app.
It's not necessary, but beneficial for the following reasons:
You'll always get the definite set of purchased products and not require the user to manually restore the products.
You have (limited) fraud protection, especially if you combine local and remote validation.
It's way more simple to compute the expiration date for automatically renewing subscriptions using the recipe.
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.
On the Mac version of my app I used to validate App Store receipts like so:
if (![[NSFileManager defaultManager] fileExistsAtPath:[[[NSBundle mainBundle] appStoreReceiptURL] path]]) {
exit(173);
}
While I did the same on iOS before, it seems that in iOS case this is not the right approach. The app is totally free, and has no IAP. So, is receipt validation even needed? And if yes, what is the right way of doing it for free apps?
There's only one reason to do any receipt validation - piracy. Plenty of apps don't do any validation I'm sure. There's really no reason to care about piracy with a free app. You are not losing any money (unless it has ads and the pirated version removes the ads). So unless the free app has ads and you don't want to lose any ad revenue, there is no reason to do receipt validation on a free app.
Even with a paid app, or an app with in-app purchases, or apps (free or not) with ads, you still don't have to do receipt validation. It's up to you. If you don't care about your app being pirated and the potential loss of sales or ad hits, don't validate the receipt. If you do care, then validate.
If the app is free and you don't use IAP there's no need to check the receipt.
It's only purpose is to validate in-app purchases.
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.
My application currently allows the user to purchase Non-consumable products. I have a Built-in product delivery model which do not involve a developer server.
Im thinking of adding more content which will expire after 1 year. Upon expiration user should have the option to buy the content again. I would prefer the content not being auto renewed. My questions are as follows.
Does the app store only allow magazines and news paper items to be
auto-renewable? The content Im focusing might not have an update
after renewing. Can I renew with the same content after 1 year?
Do I need a server to implement auto-renewable? Can I use the current
Built-in product delivery model to validate the receipts?
What would be the main disadvantage of Non-renewing subscriptions ?
Does it mean that the app store never provides transaction
information paymentQueueRestoreCompletedTransactionsFinished call
back?
Any help on this matter would be highly appreciated.
Thank you
1) It depends on whether you're planning to have your newspaper or magazine in the App Store, or in Newsstand. If you're wanting it to appear in Newsstand, it'll need to have an auto-renewable subscription. If you just want it listed in the regular App Store, you can use whatever IAPs you wish.
2) You don't need a server (despite what the documentation seems to imply), you can verify the receipts from within the app (I've done this in several Newsstand apps). You'll have the receipt as an NSData object, you then need to encode it as a Base64 string and embed it in an HTTP request to Apple's verification servers. You can find a method for doing the conversion from NSData to Base64 string here: How to encode NSData as base64? (iPhone/iPad)
3) Yes, the disadvantage with non-renewing subscriptions is that you have to do all the work in terms of restoring, cancelling, and making the subscription expire (you can't set a time limit in iTunes Connect for them). Check the In App Purchase guide for more details on what your app is responsible for with non-renewing subscriptions (scroll down to the Registering Products with the App Store section): http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/APIOverview/OverviewoftheStoreKitAPI.html