Background
I've written a game which I wish to run on multiple devices (tablets, phones, etc.). I've implemented an in-app currency, and have come to the stage where I need to store currency in the cloud so it can be accessed across devices and I can deal with conflicts etc.
I would like to go for the easiest option which, to me at least, means using some kind of available API provided, say, by Google, and not using my own server. I've come across the following possibilities:
Google AppState API (now deprecated)
Google Saved Games API
Google Drive API
Google Cloud Platform.
(1) is now deprecated and developers are directed to use the Saved Games API instead.
(2) actually seems to be a great solution because it deals with game Achievements, Leaderboards, and storage of custom data (such as in app currency). However, I see a problem with this in that in the Play Games App, there is an option to delete the player's profile, which would also delete in-game currency ! So, while (2) seems great, there is the possibility that currency data could be potentially deleted. Maybe this could be put in ther T&C's, something like "if you delete your profile you will lose any unspent currency and any purchased items"...
(3) Using the Drive API seems like an option, but then after reading online there seems to be many problems such as duplicate file names, potential for user to delete files, etc.
(4) I'm not sure about this option, but it looks like I may have to pay?
There also seems to be these deprecation schedules on some API's too.
My question
So faced with these (and possibly other not listed) options, what is a good solution to achieve cloud based in app currency? I am trying to avoid having my own server which records each user's currency/data, but maybe that's the only good solution? I want to try to reduce the ability to cheat the system.
Additional thoughts
I could use a Google Saved Games API and Saved Preferences (stored on the device) approach. I would update both Saved Preferences and Google Saved Games. I could keep both data storage methods synchronized, and if the Saved Games data did not match the Saved Preferences, then merge the Saved Prefs with the Saved Games data (e.g. set Saved Games and Saved Prefs to the maximum currency of either). This would enable me to keep a backup of currency on the device in case the user ever deleted their Saved Games profile. This would fail, however, if the user deleted their Saved Games profile, and removed the app from all devices... so many options...
Related/Interesting posts
Ricket's answer here is interesting.
Possible Compromise Solution
Due to the constraints imposed by the IAB systems and the API's available, I think I'm going to settle for this: use managed items for permanent purchases, e.g. different level themes, obtaining new characters, etc. That way these will always be available no matter what happens. Once they are purchased they are purchased forever, i.e. I will never consume these items. Have a separate virtual money (gem) system with a maximum of 100 gems say, which I keep track of through Saved Games. That way I get leaderboard/achievements too. If the player deletes their profile, that's their problem, but at least the core managed items will persist. Hopefully T&C's will cover the potential gem loss, and the maximum of 100 gems will hopefully help reduce any fall out.
You may want to checkout firebase one of its features is a real time database.
You may want to try Implementing In-app Billing. As mentioned in the documentation:
In-app Billing on Google Play provides a straightforward, simple interface for sending In-app Billing requests and managing In-app Billing transactions using Google Play.
Since, as you've also mentioned that you are trying to avoid having your own server to record your user's currency/data, it will be within your advantage. Here are a few advantages given in the documentation:
In-app billing relies on the Google Play application, which handles all communication between your application and the Google Play server. You just need to make your application request the proper permission to be able to use the Google Play application.
You can use the In-app Billing Version 3 API to track the ownership of purchased in-app products in Google Play.
You even have the option to secure your application because Google Play signs the JSON string that contains the response data for a purchase order to help ensure the integrity of the transaction information that is sent to your application.
Now, if you so decide to use in-app billing, may I suggest to please go through the given documentation and do take note of the important notes, cautions/warning and recommendations. I would also suggest that you read the In-app Billing Overview to familiarize yourself with concepts that will make it easier for you to implement In-app Billing.
Lastly, you may also add Security and Design as one of your references for more information about best practices for security and design. The suggestion in this SO post might also help.
Related
I am developing an Android application to solve operational research problems. Right now it has a free mode and a paid pro mode using google play in-app purchases. Pro mode allows a user to solve problems of any dimension and using any method available in the app.
Now there is a following problem, which I don't know how to solve best:
I want to provide each user an ability to solve any kind of problem 3 times without paying when a user first installed the app. I'd like to somehow link it to a google account, so the state will be single across different devices with the same account. Good bonus would be if a user should not explicitly log in to the account in the app itself to identify.
What is the best way to do it? I'll need a backend server I suppose.
I'm thinking of two ways, the easy one and the right one:
The Easy one: you could use google play games services, which lets you store users game data without any backend. This is not the right way because what you are developing isn't really a game, thus when the user is prompted to sign in to google play games account will seem strange...
The right one: implement a google sign-in process and store your information on a database. I suggest you to use google's firebase which is free for limited usage and very easy to integrate. It offers an easy way to implement google sign-in procedure and a really cool database called firestore
I'd like to be able to link users in one game to users in another game such that users that unlock certain achievements in game 1 are able to receive special items in game 2. This is very similar to how Blizzard does it where buying like X in Hearthstone gives you a special mount in WoW.
I know we could manage our own account system but we're hoping there's an easy way around that.
I've been researching Google Play Games and it seems like that could be the ticket but I can't seem to figure out a good way to identify that this new user linked in Google Play Games is the same user as in Game 1. Is this something I can accomplish with the SDK? Basically wondering if playerId is unique to the game or to the player themselves and across different devices.
(another option would be using the Google Play Advertising ID, but that isn't great for users that want to enabled limited ad tracking).
developer.android.com has a whole article on user identifiers. What you've got is a hard problem. I can give you a few tips:
playerId is the same for the same user across different devices, but is different between different games. This is deliberate to prevent Ad tracking that the user didn't intend. So playerId would probably be the wrong choice.
The Advertising id is "for user profiling or ads use-cases". So I definitely wouldn't use it for this use case.
In this case, my choice would be to use something like Firebase, especially Firebase Auth to manage a very simple account system. You won't need to create your own servers or write much sign-in code as Firebase handles this, and you get sign-in with Google or Facebook for free. And once you have it, it will give you a nice flexible way to do other things in future, eg experiments or configuration with Firebase Remote Config. And it is cross-platform if you ever write an iOS version, and from Google.
Before I ask the question, I have gone through the Android In-App billing documentation, and some similar questions on StackOverflow like this. Also I am guessing this question may be closed or down-voted, as its not a direct programming question and is subjective in nature. But still, here it goes.
The implementation works fine. My question is what would be a good way to store the in-app purchases locally. In my case, there is only one one-time unlockable item which unlocks full functionality of the app, and does not expire or get consumed.
Shared Preferences seems a little too easy for piracy. Even though querying the Google Play works fine and seems relatively secure, is it good enough to rely on? Since the documentation recommends to make the call asynchronously, it doesn't sound like a good idea that the user who has paid for the unlocked features, might have to wait to get access to the unlocked features. Also, when the GP cache is cleared, it takes longer than usual (which might be a rare scenario).
But seeing that there are so many apps and developers, I am wondering how other developers handle it?
Actually i think that your question will be closed for a few different reasons, but i will give you some insights.
You have a few options, depending on the nature of the products, and in some other things.
If the product is valid for life time, or depending on time (for instante, updates for a year) you can rely in google play, and that could be good.
If the product is something that the user will expend in different ways inside the application, such as in-game currency, extra lives, etc, you should save it locally, in a sqlite database, or in a remote database if you want to use a server to centralize some services.
If you take a look at the section "Consume a Purchase" of the guide purchase iab products you can read:
Once an item is purchased, it is considered to be "owned" and cannot
be purchased again from Google Play while in that state. You must send
a consumption request for the item before Google Play makes it
available for purchase again. All managed in-app products are
consumable. How you use the consumption mechanism in your app is up to
you. Typically, you would implement consumption for products with
temporary benefits that users may want to purchase multiple times (for
example, in-game currency or replenishable game tokens). You would
typically not want to implement consumption for products that are
purchased once and provide a permanent effect (for example, a premium
upgrade).
For consumable products:
It's your responsibility to control and track how the in-app product
is provisioned to the user. For example, if the user purchased in-game
currency, you should update the player's inventory with the amount of
currency purchased.
Security Recommendation: You must send a consumption request before
provisioning the benefit of the consumable in-app purchase to the
user. Make sure that you have received a successful consumption
response from Google Play before you provision the item.
Anyways, at any range i would recommend to keep track of all the products that an user buys and all the privileges that he has by yourself, since it is faster, and it could be useful for some other things like custom stats, reporting, etc.
You should use a backend web server if you're concerned about piracy. Google has a web api for checking if a purchase is legit. Have the device contact your web server. Have the web server validate with Google. If valid then let the device download your assets from the server.
If the in app item is not an asset, but a consumable item like in a game maybe have your server return an encrypted string that your app knows how to decipher.
If you rely completely on locally stored purchase info it will be very easy to spoof.
is that OK and safe to set a value in SharedPreference to flag that the user have purchased this item? What if user hack this value in SharedPreference. Or I need to connect IAP service everytime to check that before user can use it?
(1) What is the best practice when I use Google Android IAP V3?
(2) And also if user's device have no Google Play installed, I may want to use paypal to make the payment, but how to track the purchase and unlock the features for users if I ask user to use simple paypal payment to get a license key? I do not want to use any other billing SDK, if with Paypal web page to buy the license, How to implement this?
(1) What is the best practice when I use Google Android IAP V3?
--> official document says that only payment transaction will be handle by google play itself, but in the application you have to set your business logic how you handle UI integration and other things after product purchase. You can also go with the in app purchase v3.
(2) And also if user's device have no Google Play installed, I may want to use paypal to make the payment, but how to track the purchase and unlock the features for users if I ask user to use simple paypal payment to get a license key? I do not want to use any other billing SDK, if with Paypal web page to buy the license, How to implement this?
--> You can ask user to update google play version dynamically. Google developer doc says more than 90% device using 2.2 os with installed google play store. I could not say any thing about paypal transaction because I haven't use it before, but yes in app purchase using v3 is very simple to implement and understand the payment process.
How to use in your application
Three way to manage your application's product data.
1) SharedPrefrence:
you can use the share prefrence value and check whether it is purchased or not. if in case user uninstalled the app and then re-install the app then you can check whether user has purchased or not, at this you get the item is already purchased. And you have to manage the user to access your application data.
2) local database:
you can also use local sqlite database to store the purchase detail and purchase status. and same as above if user clear data or uninstall the app then request for the purchase item again and check whether user purchased item or not.
or
2) Server database:
It is the better way compare to above if you are using web server to store the user data. In this type, you doesn't even need to manage for the second time for the case if user uninstall the app or clear the application data.
3) obfuscation: (Most efficient way compare to shared prefrence)
EDIT:
is that OK and safe to set a value in SharedPreference to flag that the user have purchased this item? What if user hack this value in SharedPreference. Or I need to connect IAP service everytime to check that before user can use it?
While I am searching on internet I found Nikolay Elenkov's answer like below:
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.
more information check Nikolay Elenkov's answer
What is the best for billing Either In app purchase or Paypal?
It is depends on the product type,
--> In app billing: Best for google in app billing,
For the digital products including downloadable content such as media
files or
photos, virtual content such as game levels or potions, premium
services and features, and more.
http://developer.android.com/google/play/billing/index.html
--> Paypal: Best for Paypal billing,
For physical content or product do you want to share. You are not
permitted to sell physical goods or services using 'In-App Purchasing'
since the goods purchased via this method must relate directly to the
app using them.
Purchase physical product from iPhone app without Apple in app purchase
Hope it will help you.
from the documentation:
Because the Google Play client now caches In-app Billing information
locally on the device, you can use the Version 3 API to query for this
information more frequently, for example through a getPurchases call.
Unlike with previous versions of the API, many Version 3 API calls
will be serviced through cache lookups instead of through a network
connection to Google Play, which significantly speeds up the API's
response time.
Which basically means you can look up the purchase each time and the Play Store app will respond pretty much right away.
From my experience I can assure you of one thing.
** In fact it's bad to put a flag with a bool saying if it's premium or not **.
What I do is obfuscate the shared code
After I create some strange strings or numbers that only identify through the code inside the app if the user is a premium user.
Along with this, except for a numeric code within the database that identifies the type of purchase. So by checking both I can make sure the user is premium.
At this point if they want to cheat me with the root of the phone they should first understand how the code of my app works and then understand where to interact, because if only the shared preferences change, nothing will change and they will be whipped.
** This doesn't translate to high security, but at least the security level is higher and the root won't be able to get a reward that easily. Also because they should understand what are the exact codes to insert in the shared, in the database and look for them by removing the obfuscation. I honestly don't think it's worth it for them. **
As what Kuffs has mentioned, it is best to query the app-side implementation of the In-App Billing library which in turn queries the device's Google Play client. This will ensure that the purchase history most recently obtained from the Google Play servers would be reliable and relatively fresh information.
Also, keep in mind that if you are distributing the app on Google Play you MUST use the Google Play payment mechanism via In-App Billing. As it stands, Google Play and Wallet do NOT yet support Paypal or wire/bank transfer methods so you should not integrate the option if you are releasing it on Play.
http://play.google.com/about/developer-content-policy.html#payments
I have a few questions connected to Android In-App Billing:
Is it possible to make a purchase from non-Market app? I understand that it would be a vulnerability, but I have no opportunity to find out if it's possible or not.
How can I get purchase state for a particular product? As far as I understand it can be done using RESTORE_TRANSACTIONS request, but it's not recommended to use very often. That's not a theoretical problem. My application allows users to buy content using in-app billing. Content can be downloaded from a server, and server must allow content downloading only if it was purchased. But it can't check if content was purchased or not without using signed response from Android Market.
How can I get price and description of an item from Android Market? Seems that I know the answer and it's "there's no way it can be done", but maybe I'm wrong. It would be very useful to have a possibility of retrieving item's price.
It's very interesting to me how you solved/are going to solve these problems in your apps. Answer to any of these questions will be appreciated.
In order:
1- Nope. The in-app billing process is part of Market. If the app comes from elsewhere, there's no way for Market to verify the origin/authenticity of the application.
2- It's your responsibility to store the purchase state for a particular product. From the doc:
You must set up a database or some other mechanism for storing users' purchase information.
RESTORE_TRANSACTIONS should be reserved for reinstalls or first-time installs on a device.
3- Unfortunately, at this time you're right. File a feature request!
In the meantime, one option is to set up a website with appengine, store listings of all your content & pricing there, and then manually sync prices listed on your appengine server with the updated prices in Market. Then have your Android app pull the data from the AppEngine server. This is much better than hardcoding price values into the app itself, since you don't need to have everyone update the app immediately to see accurate pricing whenever you change something. The only caveat of this method is that if the user is in a different country, in-app billing will display an approximated price in their native currency, and there's no way for you to determine exactly what price will be displayed to them.
Related, One of the Android Developer Advocates is giving a talk on LVL/IAP at IO, called "Evading Pirates and Stopping Vampires using License Verification Library, In-App Billing, and App Engine." - It would definitely be worth your while to watch when they release the session videos on the website.