Does a completely free iOS app need app store receipt validation? - ios

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.

Related

iOS in-app purchases verify receipts for consumables

in iOS with in-app purchases, it is necessary to verify receipts with subscriptions. is it necessary to verify receipts for consumables also?
No you don't need to verify receipts for consumables.
You don't need to verify receipts for subscriptions either. What's important here is that you can restore running subscriptions; but for that you can use purchase ID.
As always make 100% sure you've given the user what was paid for before telling iOS that the purchase was successful!
(There are all kinds of other considerations why you would like to keep purchase related information on a server, but that's not what you ask about.)
Necessary, no. Recommended, yes.
All purchases; consumables, un-consumables and subscriptions are susceptible to fraudulent attacks. Often though iap crackers or network spoofing. Validating the receipt can mitigate this problem.
You can validate the receipt locally in the app (see Validating Receipts Locally), though it is still susceptible to cracking.
The safest way to prevent fraud is to perform server-side validation by sending the receipt to your server then sending it on to Apple (see Validating Receipts With the App Store).
However, do not validate the receipt with the app store in the app itself. It's really easy to spoof the network request and return a positive (valid) result.
If you notice a large discrepancy between the actual purchases in your reports from Apple and your in-app purchase analytics (assuming you have that), you may want to invest in server-side validation, otherwise, if it's not a problem, don't worry about it.

Is it necessary to validate / refresh an app store receipt on launch in iOS?

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.

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.

iOS In-App Purchase No Back-end

I am investigating the use of in-app purchase for what essentially would be a "pro" version of my app.
The app itself would be free but once in the user has the option to purchase the pro content (only 1 thing). The "pro" content would already be on the app and there is no need to download it, it would simply "unlock" it.
Is this allowed from the Apple Guidelines?
As only 1 non-consumable would be purchased I think the use of a back-end server isn't required.
Again is that allowed from the guidelines?
And is it safe and simple to just store the result in NSUserDefaults and if installed on another device pull it from SKPayment restore purchased and such?
I've looked at several other questions.
In-App Purchasing?
Retrieve purchased information in In-App purchase
How do I add consumable In App Purchases using NSUserDefaults and not my own server?
And those seem to suggest that my approach is valid, but as I know those things have changed recently I want to make sure I'm taking the right approach.
Thanks!
No problem having the content built in.
Best practice is to perform receipt verification on a server with an authentication protocol between the app and server (this is also true for several other mobile app stores). If you perform the verification on the device, people can use existing tools to get around your IAP checking and steal content. Take a look at https://developer.apple.com/library/ios/#releasenotes/StoreKit/IAP_ReceiptValidation/ for some information.
So while a server is not required, it is recommended. Only you can say if protecting your content is worth the hassle of maintaining a server.
I agree with J. Freeman that straight storage in NSUserDefaults seems weak. I store things in a local file but the format is tied to the device and requires a server computed key to create it. Finally, yes you should use SKPaymentQueue restoreCompletedTransactions to get things purchased on another device. Realize that the restored transactions should also have their receipts verified on your server.
Yes that is fine. You do not need a backend to do in-app purchases, and it is ok to ship with your content built in.
The one thing I would say be careful with though is storing the unlock information in NSUserDefaults as someone will easily be able to forge purchases that way. You should store the unlock information in the keychain.

Convert existing iOS paid app to freemium model with in-app purchase

I currently have a paid app in the store. Apple have not allowed a 'lite' version to be submitted as well, so I have no choice but to update the current paid version to a freemium (with in app purchase) model. I have the problem of not loosing functionality for v1 users that have purchased the app the first time round.
Is there any way to determine if an application have been updated from a previously installed version so I can unlock the paid parts of the app?
Two similar questions (from a few months ago):
Transition an existing paid for app to free version with In App Purchase
iPhone + upgrade existing paid application on app store to free application with In App purchase + what about the customers who have already purchased the paid application
There is now an Apple-approved way to do this on both iOS and macOS. The originally downloaded version of the app can be obtained from the receipt using the info key Original Purchased Version. You can then decide whether to unlock features if that version predates the switch to IAP.
For instance, once you have retrieved the receipt info:
NSArray *versionsSoldWithoutIAP = #[#"1.0", #"1.1", #"1.2", #"1.3"];
NSString *originalPurchasedVersion = [receiptInfoDict objectForKey:#"Original Purchased Version"];
for (NSString *version in versionsSoldWithoutIAP) {
if ([version isEqualToString:originalPurchasedVersion]) {
// user paid for the currently installed version
}
}
For more info see the WWDC 13 video Using Receipts to Protect Your Digital Sales. At 5:40 the presenter comments: "I think the most exciting thing that's in the receipt this year, especially for you guys if you have a paid app in the store is that we've included information in the receipt that's going to let you do a transition from being a paid app to being a free app with in-app purchases without leaving behind all the customers that have already paid for your app."
With iOS7, iOS app can verify app store receipt, which contains app download date.
By using this donwload date, you could determine if a customer is previously purchased or not
First, I just want to say I personally think the freemium model is great. It has worked out very well for many developers. People love to download free apps, and will do it on a whim, but pay much more attention to an app before spending $0.99 (Which is due to the effect of free - for more info on that, check out Dan Ariely's book Predictably Irrational)
For more info on freemium, google it - There have been tons of articles written about the success of it.
Ok, back to the actual question:
Theres a couple ways you can handle a situtation like this, although the unfortunate matter here is none of them are fool proof.
The best solution would probably be for your users to have accounts. Without knowing the specifics of your app, I can't say whether or not user accounts are appropiate for your app. User accounts stored on your server have many additional benefits, including user management, and tracking what purchases a user has made. This will allow users who delete the app, and then re-install it, or get a new device, to maintain their purchased content. Furher, whenever you use in-app purchase, you should validate the purchase on your own server (or with Apple), which a server based user manegment system can all do. If your totally in over your head with creating your own user management server, check out Parse. Its dead simple to create an amazing backend server (for basically free)
iCloud Key/Value type of system. I'm not very familiar with how this would work - so I'll move on.
Another, not nearly as fool proof solution (but much quicker/easier to implement) is to use NSUserDefaults. You can store an object when the user makes a purchase, or with the date a user installs your app. Then if you issue an update converting your app to freemium. Then in the new update, check which purchases the user has made or the date they installed it, and react accordingly. For info on how to do that with NSUserDefaults, check out my answer to another question on implementing that: NSUserDefaults and app versions.
But this solution does present the following pitfalls:
If the user deletes your app, the NSUserDefaults are lost forever
If the user didn't install the update setting up the NSUserDefault system, but then installed the update with the new freemium model, the app would treat them as if they hadn't purchased the content.
In summery, this is a difficult question, with not a lot of easy/perfect options.
Anyway,
Hope that helped!
I'm dealing with the same thing and came up with the following idea: Create the freemium version under a new name and app ID. Keep the existing paid app in the app store, but raise the price to something absurd and clearly state in the description that the app is there to maintain support for existing users and that new users should try the freemium version instead.
Existing paid users won't lose support for their existing app and can delete and install any time it without re-purchasing.
You won't have to keep updating the old paid app, either. Just keep it in the app store.
The downside is that existing paid users will not be able to migrate smoothly to the freemium version to get any extra features you add in the future without re-paying for what they already have.
Still trying to decide if this will work for me but it could be a good option for others. Comments appreciated.
I've been thinking about this problem for some time now. I have a substantial amount of customers that paid for my (in App Store terms) high-price niche-App and I'd hate having to tell them to re-purchase as I plan to migrate to an In-App Purchase model.
The idea I came up with (and I'll ask Apple support whether it's legal) is to phase out the current paid App but ship a last update for it that allows "unlocking" the In-App purchases of the new App based on the In-App model. I was thinking about a challenge response scheme:
User has installed paid App on his device
User installs new In-App App and opens it. The new App detects the paid version and offers to unlock the In-App purchases (on this device only of course and as long as the App isn't deleted)
The new App generates a nonce, signs it and calls the old App with it via an URL Scheme
The old App decrypts the nonce, adds +1 one to it and signs it again. Calls back to the new App via URL scheme
The new App validates the nonce and unlocks the features
The scheme can be easily implemented using a pre-shared key. It's of course a weakness on jail-broken devices, but then every App storing In-App receipts has those problems.
You can check the 'original_application_version' of the receipt. All iOS downloaded from the appStore have a receipt even if it is a free app.
TPInAppReceipt is a simple swift library that can help you with this.
import TPInAppReceipt
do {
/// Initialize receipt
let receipt = try InAppReceipt.localReceipt()
let originalAppVersion = receipt.originalAppVersion
let buildSoldWithoutIAP = 22
let originalAppVersionInt = Int(originalAppVersion) ?? 23
if originalAppVersionInt <= buildSoldWithoutIAP {
// unlock all features
UserDefaults.standard.set(true, forKey: "isPaid")
}
} catch {
print(error)
}
Note: The receipt.originalAppVersion returned is the build number as at the time the user first purchased the app from the appSore. Also, the receipt won't be available in the sandbox environment until you purchase or restore an inAppPurchase first.

Resources