I'm trying to implement in-app billing, where the only thing my app sells is a monthly subscription. I've gone pretty much copied the Dungeons example that Android provides and it works, but it seems overly complicated for the one thing I have to sell (and I know I shouldn't be copying it anyway).
The Dungeons example has a PurchaseDatabase class that keeps track of all of the user's purchases. Since there is only one purchase in my app, rather than having a database, it seems like it would make more sense to just have something like a SharedPreference/boolean "isPurchased" and update/check for that when I start up the app... Now I don't really know much about security, but that just seems to ring warning bells and would probably be very hackable.
So, how should I store/restore this one piece of data? What do I need to save and keep track of, anyway?
Edit: Found this In-app billing. How to store information that user has paid? . It would seem that I should use a SharedPreference, but this question still stands: do I just save a boolean "isPurchased" or should I keep track of orderId, developerPayload, etc?
If you just save a flag in shared preferences, any user with a rooted device can flip the flag at will and be 'subscribed' without paying. So you should at least do some obfuscation. Here's a sample way to do it. Additionally, there is an API to check for subscription state, so you should check periodically to make sure the subscription is valid.
Related
I know that they are possible since I implemented consumable purchases and they work but:
If I upload a new version of the app or re install it the consumable purchases are gone. Does not matter if for iOS or android. I used the device ID but that is not reliable and does indeed change in some scenarios
I would like to offer inapp purchases (consumable) and keep track how many a user has. Ideally over multiple devices but for sure for multiple installations (meaning they keep them after updates or reinstalls)
Is this even possible without some kind of login? Is it possible to implement subscriptions without this problem? I use revenuecat if that matters but asking more generally
In case of consumable in app purchase it is definitely lost and cannot be recovered across devices without saving them for the relevant user. You may have to create your own saving and recovery system if you wish to make it available across devices.. maybe you can take a look at non consumable products or subscription
Good afternoon
Theoreticaly Possibly, but there are several ways that you need to use all together in order for everything to work correctly.
For example - It is necessary to store the user_id in the keyChain, then the saved id will not change when reinstalling (this only applies to iOS). It is also necessary to get information from the Receipt for different users (with different devices) but having one check and merge them. Therefore, it is desirable to have 2 ids for the user user_id and device_id. You need to receive and process webhooks from Apple/Google to understand the current status of a particular product. Doing this on the client is not the most pleasant experience. Some of this is generally impossible to do only on the client side.
Better use the ready-made solution Apphud, RC ...
If you have a free time and a team of backend developers, then you can implement all this yourself.
Look at the internal implementation of the services above (the code is open) how they work with Receipts and ids. You will understand that without the usage of unique identifiers you will encounter a lot of problem cases.
So, I have an app that has just been rejected by Apple because of not implementing a restorePurchases button for the user to restore any non-consumable items that they have bought.
I understand the logic behind that, and feel bad for missing it (or not understanding the blocking aspect of its implementation, at least), but I have an unusual case (which I'm not sure is all that unusual, but can't find a lot of best practices around it...)
My app is cross platform, and so the user can purchase the same item on Android as well as iOS, and those purchases need to persist across platforms... so, upon successful authentication, I update a value in my backend that both clients have read-write access to.
Because of this, I consider that I persist purchases across devices and platforms just fine... but the purchases are tied to an account on my applications User class.
Now, if I am to implement the button as required by Apple, what is stopping a User from restoring a purchase associated with an Apple ID to another User in my application database... potentially causing a 'revenue leak' so to speak - for example, signing in as a Friends User, restoring the purchase, which in my case would copy the purchase over to the User class... then I have two Users with only one piece of associated revenue...
So... Questions:
1) Am I being naive about the ability to cheat the system?
2) Does anyone have any best practices about how to solve / workaround this issue ?
Apple addresses this in the In-App Purchase Programming Guide
Firstly, you can provide your user's account name with the purchase information to assist Apple to detect irregular activity -
Detecting Irregular Activity
The App Store uses an irregular activity detection engine to help
combat fraud. Some apps can provide additional information to improve
the engine’s ability to detect unusual transactions. If your users
have an account with you, in addition to their App Store accounts,
provide this additional piece of information when requesting payment.
Then, when it comes to restoring purchases you can provide this same user account information to restoreCompletedTransactionsWithApplicationUsername: method -
Restoring Completed Transactions
Your app starts the process by calling the
restoreCompletedTransactions method of SKPaymentQueue. This sends a
request to the App Store to restore all of your app’s completed
transactions. If your app sets a value for the applicationUsername
property of its payment requests, as described in Detecting Irregular
Activity, use the restoreCompletedTransactionsWithApplicationUsername:
method to provide the same information when restoring transactions.
Finally, you can use the receipt information on your server to correlate the original transaction with the restored transaction for the same userid and not restore the functionality if it doesn't match.
I folloed this tutorial to implement in-app in my application:
http://blog.blundell-apps.com/simple-inapp-billing-payment/
The user can now purchase my item (com.myitem) with in-app system. After that, I set in a Shared Preference that the user has been purchased the item.
The problem is if the user uninstalla and reinstall the app.
So the question is: how can I check if the user has been already purchased the item?
I know that I should use restore BillingHelper.restoreTransactionInformation(...) on RESTORE_TRANSACTION...but where, and how?
I checked Dungeon example, but it seems to hard and complex to unterstand. There's a simple way to do that?
Thank you
Using RESTORE_TRANSACTION is the only way if you use managed purchases. If you use unmanagaed purchases, you can store the purchase state on your own server, but that is hardly simpler. There is nothing complex about RESTORE_TRANSACTIONS: you just fire the command and you get notified with transaction information that is in the exact same format as what you get when you first purchase an item. You should process it in the exact same way, and chances are you already have the code for that in your app. Testing this is somewhat harder, because it doesn't really work with test accounts, and you need a live app. Go over the official documentation again to understand how it works.
As Google stores your purchases of Android apps, I was wondering if they are somehow offering a webservice that can be used to check if a certain app has been purchased. To me this seems the most secure way of distinguishing free and paid users from within my app.
It would not only defeat piracy, but would also allow for managing a database of legally registered users, by a one-time check through this service.
What are your solutions to this matter?
Google provides a library for you. See here.
Note that this has been compromised in unprotected apps, so you should use something like ProGuard. The link above contains more information.
Finally, keep in mind that the Android Market is not the only app market for Android out there. Amazon has their own DRM.
At this particular moment there is one way to check.
The Android Market authenticates purchases through Google Checkout, but Google has not implemented the Checkout API to synchronize Market purchases. So calls like that check the status of an order do not work. Maybe Google doesn't want dev's to get a hold of customer emails? Either way, I do not know why it does not exist, it seems to me that if Google simply made this service available we could implement a far more superior security system that would better utilize the methods of obfuscation. As it is at the moment, a hacker can simply look for a specific class like the ILicenceService and hook it.
The only way at the current moment is to download a list of current purchases. You can do this using an authenticated http call. The drawbacks to doing this are pretty large though. First, the list it provides only extends back 31 days (so you've got to make sure you keep everything). Second, you would have to call and parse at least every ten minutes. Actually faster than that, most users want to play their games when they buy them. Third, if your service, or server goes down, that is precious information that is being requested almost instantly by your users.
I don't know how many dev's are currently utilizing this process, I considered it, but am just going to keep complaining to Google for a better method.
You could piggy back off of the LVL. I have just implemented a similar system.
Perform Check via LVL.
On success make a post to your own web server and store whatever details you need, i.e. DeviceID etc.
You could also perform checks to your own server even when the LVL check fails and allow things like trial periods etc.
For my Android application, I want to release it for free, but with ads. Then, I want to give the user an option to remove the ads for a nominal fee using the PayPal library. That way, I don't have to have 2 separate versions of my application that I have to monitor, develop, etc.
Anyways, after the user pays me to remove the ads, I need to securely store something that indicates that they paid so that it isn't easy to spoof my app. How should I do this? I was thinking of encrypting a string and storing that encrypted value in SharedPreferences. Is that a viable option? If one were to take the apk off of their device and give it to someone else, does SharedPreferences move with it, or is it saved outside of the apk? What about if someone has root access? Can they spoof SharedPreferences?
If possible, I'd like to avoid having to query a server every time the app is launched.
Thank you!
I would argue a few of points:
Refactor your application into a free, ad supported version and a paid version. You can package things such that you'll be able to share almost every piece of the application. Then just post the free and paid versions in the market.
Anyone willing to work their way around your copy protections was never going to pay for your app anyway.
You're offering a value proposition. If the only thing that separates your free and paid version is ads then your app better be something the user can't live without. If you're planning on expanding the paid version with more features then #1 above is probably a better idea anyway.
What you could do is take some unique data from the phone (device ID, app version...etc...) in your app and combine that into a message.
Then use a public/private key pair. The public part can be in your app and the private part you keep with you.
When a user pay for your app, it sends you the string with device ID and so on and when you got the payment, you send back a blob of data containing a certificate.
Your app can verify the certificate against the device ID or whatever your decided to put in there by using the public key.
There are still a lot of details you'd have to work out (how the data is sent back and forth...etc...), but with those kind of tools, you should be able to achieve what you are looking for.
You can look here about how to manage certificates : http://developer.android.com/reference/java/security/cert/package-summary.html
Of course, how sophisticated you want to be depends on your app and how much people will have to pay for. Reverse engineering to bypass all that is certainly possible, but I doubt many people would go through the trouble if they only have to pay $0.99 to get rid of the ads.
If the user is determined enough, they will be able to reverse-engineer your app and get the decryption key, or just patch away your protection altogether. SharedPreferences are stored in a file that is accessible to anyone (ok, maybe anyone will root access...).
In summary, save yourself the effort and don't even try to protect your app... who is going to reverse engineer your app to merely get rid of advertisements anyway?
You might want to skimp on the complicated protections for now and wait until in-app purchase arrives. I'd imagine there would be a secure, reliable solution at that point in time.