Check whether in-app payment item has been purchased - ios

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.

Related

How is the Apple subscription working with apps?

I am planning to make an app on iOS. The app will be free. This app will work without the internet. The app should not be able to query my database if the subscription is not paid.
However the app will still receive "notification" or RSS links even without subscription. The subscription will be monthly minimum.
I did some research but some people are saying it is not possible and some are saying this has been changed by apple and it is now possible.
Edit
I would like to add that the app will be as much secured as possible. I will have an SQLCypher database inside - so the key will be stored there too (hidden).
Here is the problem that someone told me: The user can use the app only if it paid the monthly/annual subscription, so the key has to be revocable. It seems not compatible with that because the app will have the database deciphered with the key. And if it is deciphered one day, then it will be deciphered next month too.
Why exactly people tell you is not possible?
The only problem I see from what you write is if the free version of your app doesn't do anything. As a general note Apple doesn't allow "demo" versions (even if that concept is not always clear or enforced consistently): a free app must do something not trivial (and of course lots more if the customers pay).

StoreKit: Is it OK to keep unfinished transactions around on purpose?

I'm working on an iOS app and am currently trying to come up with strategies of how to manage in-app purchases:
Android has the nice capability that it caches product ownership information locally on the device and can therefore check ownership without having to prompt the user for a password. iOS, on the other hand, cannot restore purchases at all without asking the user to log in and Apple therefore specifically discourages ownership-checks at app-launch in their Restoring Purchased Products documentation (last sentence of first paragraph).
Now, technically, you can get iOS to provide the same "ownership caching" for non-consumable items as Android does, by just never calling finishTransaction:, except in the case of failed transactions. That way, at every app launch, as soon as you call addTransactionObserver:, you get a list of owned products without a user login request.
Clearly, I can also provide this cache myself using something like NSUserDefaults, but that would be a bit more work and just adds more opportunities to make mistakes in implementing the ownership tracking. And since I need to handle randomly reported transactions from the transaction observer anyways for good measure, I would basically have to implement the above idea and then duplicate its functionality again using NSUserDefaults or such.
Thus my questions: Is it OK to (mis-)use unfinished transactions in this way? Will they ever expire automatically? Might Apple reject my app if they notice that this is what I'm doing? Are there any negative implications of this idea (reliability, performance, wasted memory/storage)?

Storing trial period date

I'm implementing a trial then subscribe model into my app which allows the user to use the app for free for 14 days, then at the end of the trial it'll alert them that the trial ended and they need to subscribe to continue use.
I'm doing this settings an NSDate object in standardDefaults the first time the app is opened. Then it compares the current date with the stored date, and compares the returned time interval to the number of seconds in 14 days.
My question is this: Do standardDefaults stay in memory if the user deletes the app? If not, how can I prevent a user from deleting then redownloading the app to restart the trial period?
Apple is quite proud about their sandboxing concept and the fact, that if a app is deleted, all the data will be deleted.
The only way of ensuring this information won't be lost will be to save it to a Database on a server and periodical check from your app.
This as technical solution. But make sure, if Apple will allow this in there AppStore if you plan to release it there.
Apple forbids trial versions, though I am not sure, if you could deliver in-app purchased content free for a while and later start billing.
But the main rule is: what ever was given to the user for free must stay free.
There are some apps that have trial periods for certain features. Look at the Puffin Browswer or ChessFree they both offer features that expire. Puffin allows for flash support (actually really cool feature!) and after a few days that feature is removed. Although the rest of the browser is still accessible the main purpose of it is removed.
ChessFree gives you points that you can use for different things, lessons, playing games, taking moves back, etc... I am not sure what happens when you run out of points but it appears that you cannot really do anything anymore.
If I were to implement something like this I would just save a variable in the keychain telling how many days they have left or the date they installed it (as a string). That way it will not be deleted if the app is uninstalled (keychain items stay stored even if the app is removed, so just always check/decrement/increment that as you wish). I would probably keep some features available to users that reach the expired time limit (like Puffin) but I honestly believe that is up to you.

Detect first time install in iOS NOT NSUserDefaults

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]

In app purchase - method to allow full control of adding products from personal server = allowed?

I have a very strait forward question (and yes I've looked though apple documentation to see if this has an answer but no luck... I may have accidentally missed it though)
Here's my plan:
The problem I've been trying to find a workaround for is that if the admin would ever want to add a product, he would have to log into iTunes connect to add it and also add it in a custom control panel. We, obviously, don't want to make him suffer that so I've been looking for a solution but I need you guys to tell me if it's allowed by apple. Basically I will take over most of the product handling on our servers and will only go to apple with the transactions. This means that apple will not have an in-app purchase set up for ALL the products... only one for each length of subscription (1 month, 3 month... etc) and a few consumable in-app purchases for the various prices of the issues/singles
Side note: I will be selling monthly issues that contain multiple singles for each day of the month. The user will be able to download a full month or a single day at a time if they like.
DEFINITION OF CONSUMABLE PURCHASE - products must be purchased each time the user needs that item. For example, one-time services are commonly implemented as consumable products.
So I will store all the information in our server about the products and if someone chooses to buy a single month's issue that was set to 4.99 (on our server, not apples) then the app will run the in-app purchase with apple that is listed for the 4.99 tier. Whenever a person opens the app for the first time, their app will send some information to our server and they will have a row set aside for them where all the information about their purchases will be recorded so that they can restore them if they switch over to another device.
If you guys think i'm safe in doing this, please let me know so that I can proceed. Also, if this method helps anyone, feel free to use it!
Thanks,
Matt
I think your restore process might be flawed. You talk about the app sending some information up to your server, but what is that information? There is no reliable way to uniquely identify a user across different devices.
If you want to continue on this path you'll want to make sure that your recovery and failover process is very solid. Try out every imaginable scenario. From an app store submission point of view, you'll want to consider a token/coin-based approach. Of course, Apple's guidelines are fairly loose and subject to change so it's always possible you'll get rejected, but tokens are certainly more solid than simply using the same generic in-app purchase.
In a token system, you would set up in-app purchases for different numbers of tokens that the user can purchase as a sort-of virtual currency only valid within your application. Then users can spend these tokens on any items that you dynamically create.
Server-side this means you'll need some way to store how many tokens a user currently has and a way to uniquely identify a user across devices, which is a fairly uncertain proposition. Instead of storing the number of tokens each user has, you could implement some sort of hashing algorithm that generates a hash from an in-app purchase receipt and then sends it up to your server. If the app crashes or the network dies after purchase but before sending your hashes up, next time they open the app you can recalculate all of the hashes, send 'em up, and if the server doesn't recognize a hash it just adds it to the database. Then if a user wants to restore their purchases you simply recalculate the hashes on the device using the in-app purchase receipts you'll receive and then send them up to your server and ask the server to figure out for each of those hashes, how many tokens the user has left. You could think of it like a gift card system, where each hash is one gift card.
Again, app store rules change and if apple thinks you're trying to game the system and not provide a useful experience they have the right to reject you. It could be worthwhile to open a Developer Technical Support request and see if an apple engineer can provide you with a better solution or tell you if the reviewers are likely to accept your application.

Resources