Detect first time install in iOS NOT NSUserDefaults - ios

How it works - The app is a subscription to a service that works cross platform. On the iphone the initial purchase will get you 30 days of full functionality then after 30 days you can't do ceirtain things until you buy a subscription with an in app purchase.
What I need - I need to know if it was the first run so that I can add the users info to the database. Or because it is cross platform they may have already paid for a subscription so I need to add 30 days to the user's already existing subscription.
What I am doing now - Currently I am storing the identifierForVendor in the remote database and in NSUserDefaults there is a bool checked to see 1. if it is the first run and 2. if it is the first run to allow the user to add themselves to the database for a 30 day full use, or to add 30 days to their existing subscription.
The Problem - The problem is that if the user deletes the app then reinstalls it the NSUserDefaults will be deleted too, and the identifierForVendor will change and that will allow the user to add another 30 days to their subscription for free just by deleting and reinstalling the app.
Ideal Solution I originally wanted to do a subscription for the app, but I didn't know how to check the receipt when the app is originally purchased and insert the user into the remote database. Also I don't really know how to setup a subscription that isn't an In App Purchase. I also thought about making the app free and then they can use the In App Purchase to buy the service but the app is useless without being able to connect to the remote server to build up data in the first place.
Bottom Line I would like to do one of the following
Make the subscription on the first purchase (I need to be able to detect if the purchase is valid so I can enter the user info into the remote database)
Make the app free then use In App Purchase to add subscription (Is it acceptable to have a free app that won't do anything until you subscribe?)
Leave it the way it is now (but find a more permanent solution to the identifierForVendor, and the identifierForAdvertising won't work either because it can be turned off)

This will help you so much. Basically it's a wrapper for Keychain that is patterned after NSUserDefaults. It heled me a lot. Storing things in the Keychain makes them stay past app reinstalls.
https://github.com/carlbrown/PDKeychainBindingsController
This project is intended to make using the Mac OSX and iOS Keychains
as easy as NSUserDefaults.
It is a KVO-compliant Cocoa wrapper around the Mac OSX and iOS
Keychains, and the model for this wrapper is NSUserDefaults, so the
intent is that for the common cases you would normally want to call:
[NSUserDefaultsController sharedUserDefaultsController] You should be
able to call
[PDKeychainBindingsController sharedKeychainBindingsController] And
for the common cases you normally would have called:
[NSUserDefaults standardUserDefaults] You should be able to call
[PDKeychainBindings sharedKeychainBindings]

Related

Check whether in-app payment item has been purchased

I now spent about 24 hours implementing Apple's StoreKit IAP system. After 3 hours I got "purchasing" itself working but the last 21 hours I wasted on implementing all the receipt communication. Now I am struggling to get openSSL working (some bitcode compiler error) and I reached the point of giving up.
I don't care about people cracking my apps or things like this, so is there a simple way of just asking the App Store servers whether an in-app purchase has been made? Without any encryptions, keys, certificates or likewise?
The flow would be like this:
set a NSUserDefault to "true" after purchasing
on every app launch, if there is an internet connection, check again
unless the device is connected to the internet, the NSUserDefault stays on "true"
if a user tempered with the NSUserDefault, it will be overwritten as soon as the app connects to the internet again
If the user finds his way around it, he deserves to keep the item, no user on the world can pay me enough to spend one more hour on that
So if you know a simple way of achieving this, please let me know. I noticed that after buying a (non-consumable) product with the test account, the popup tells you that you've already bought this item before. Maybe this can be used for this purpose?
I am sorry for the rather informal way of posting right now, but this is the first ever time where I think Google did a better job with Android than Apple did with iOS. There are cracks for literally every app anyway, Google's system is far from being bulletproof but in the end of the day, what difference does it make if a cracker takes 10 minutes or 2 hours to manipulate the app?
It took me like 1 hour to get in app payments working in my Android apps but what Apple requires here is ridiculous. Their "instructions" aren't helping at all, it's like they want to make it a secret how to implement all that and you gotta take a guess.
If you don't want to check receipts, you only need to implement the SKPaymentTransactionObserver protocol and add whatever object you implemented it in as an observer using SKPaymentQueue's addTransactionObserver. Ideally, you want this done early in your App Delegate to ensure you receive any transactions sent to your app on launch (this is most important for subscriptions).
In the paymentQueue:updatedTransactions: method, check the transactionState of each transaction. If it's SKPaymentTransactionStatePurchased or SKPaymentTransactionStateRestored, then the purchase is (supposed to be) valid. You can then store the status in NSUserDefaults or a database.
Don't forget to implement restoring purchases.
This works quite well, but anyone who has jailbroken their device and installed whatever software it is that bypasses IAPs will get IAPs for free: it does not need to be specific to your app, they just return "purchased" for any IAP.

iOS in-app purchase for a paid app

I created an app which is a paid app. Now I want to make it free so more user can download the app and use in app purchase to limit some features. But some user already paid to buy my app. How can I implement in app purchase for new user at the same time keep full feature access to old user?
If you connect to your server for registering user info, you can always create an API which executes on app launch to verify that user is full access user or not.
But I am afraid your case is not the above one.
In that case you would require to sync your data (some encrypted key in this context) with iCloud and when application is launched you can verify the type of user.
Using data in iCloud is more safer as compared to keychain as it covers device format scenario. But definitely not foolproof.
Other solution can be using Apple Purchase Receipt to verify the version of previous purchase. But this is only supported since iOS7.
Checkout some opensource libs to understand the parsing of receipts:
https://github.com/rmaddy/VerifyStoreReceiptiOS
So combining multiple strategies is the only answer for your question.
You can do this by reading the App Store receipt. The receipt contains the version number and date of the original purchase.
There are two main caveats: first, this only works on iOS 7 and above. Secondly, Apple don't include code for parsing the receipt (so it's not too easy for users to hack I understand). There are, however, onen source libraries, though using a common one will be less secure.
There are no perfect solutions to this scenario.
Suggestion 1:
Roll out one last paid update. In this update, use keychain to store those IAP flags. Then in the free version, check for these flags in keychain. This will work even if app was deleted and reinstalled with the free version later. But it will not work if the device is being reset completely whether due to some iOS version updates or user's unless a backup and restore also is involved.
Suggestion 2:
Not quite a suggestion. But I have seen similar apps on AppStore have just rollout free version. Then app incurred bad reviews from those previous users!
This is a simple example, but if you're working with a database on a server (not on the phone itself), can't you use a boolean for each feature you plan on selling, and just set that boolean to true for all users currently in the database. This is assuming true means they've bought the feature, and false means they haven't bought it.
You could run this query once after releasing your updated app, and then every user after that would have a default value of false for these features you're selling.

iOS Verify previous app purchase/install

Is there a way to verify with the apple servers when a user first installed an app?
Let's say you have a payed app and want to make it free with ads but not send ads to those who previously bought.
Since it's not an IAP i assume Store Kit is out of the question.
Later edit: My problem being the ones that buy, delete and reinstall.
Nope, you can't do that! The best way is to remove your current app from the App Store and relaunch using a different Bundle ID. People who bought it previously can keep it how it is, although they won't get any updates.
A possible workaround
Here's a workaround I just thought of. When you send out your next update, set a flag in NSUserDefaults to mark everyone who has it now as paid user. That would be for your next update only; you would remove that code right after the update. Then, when you send out your version with ads, check that flag to determine if you should display them. The only problem is that if a paid user ever re-installed the app, they would see ads from then on.

How to 'fake' a restore in-app purchase for previous app users when moving from paid to free

Our app is moving from paid to free, and in the process, moving a key functionality from being included to activating via In-App purchase. Obviously, we don't want current users who paid for the app functionality to be charged again in the In-App purchase for functionality they already had. So on the update by the user, we want to 1) identify current users and 2) make it so they don't see the In-App purchase in the first place, sort of 'faking' the In-App purchase so that the app will appear to them exactly as it did before.
The app does not have a backend, so we have to determine current users from new by examining the saved user data fields for certain values. I do understand that if a previous user has deleted the app from their device that nothing can be done, and I don't mind charging them for the In-App purchase, since they never used the app anyway.
But for those current users who update and assuming we can examine the saved user data and determine that they are current users, what would be a good way to bypass the In-App purchase and make the app look like they already got it, when in fact they never paid for it? Thanks!
Here's what I would do - keep in mind this will take some time:
Set up a server (I prefer EC2) with mySQL on it. Plenty of tutorials about this.
Submit an update to your app that sends the user's UUID to your server.
Wait. This is the hardest part. You'll need to wait until satisfactory majority has updated to your app. That majority percentage is up to you to figure out. It could take months for this to happen.
Make your new, free, app send the UUID to the server.
Check to see if the UUID is in the DB.
If it is, set whatever you would have set when an in-app purchase was made to true.
You have several options:
Free in-app purchase for a limited time:
You would create a free tier in-app purchase content and release an update that somehow makes the user sign up for it. This way, when your user switches devices they can restore the purchase and regain the functionality.
Wait for a period so most people use the in-app purchase content
Change the tiers and release your app as free
Dual versions
Make a demo version of your app. Note this can be rejected by Apple.
Create a file in the filesystem
Make a file in the filesystem and save into iCloud. The app will check for the file and thats how you would determine if the user has paid for the app (or should buy the in-app purchase).
iCloud will synchronise the file between user's devices and it will make sure that whatever device the user uses the app will see the user as 'paid'.
I hope this helps, currently having this problem myself.

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