I am working on In-App Prurchase in Android. After Purchasing an item, i set boolean value to be true using Shared Preference Api in Android.It works fine. Once i un-install and re-install my application,it make boolean value to be false.This is my code:
boolean buyUfo = false;
private static final String BUYUFO ="buyUfo";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preference);
sharedPreference = this.getSharedPreferences(SHARED_PREFERENCE_ID, MODE_PRIVATE);
buyUfo = sharedPreference.getBoolean(BUYUFO, false);
}
//After In-App Purchase, when finish listener is called.
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener1 = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d("ufo", "Purchase finished: " + result + ", purchase: " + purchase);
if (result.isFailure()) {
if (result.getResponse() == IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED) {
Editor editor = sharedPreference.edit();
editor.putBoolean(BUYUFO, true);
editor.commit();
return;
}
}
I want this value to be true always for those who bought the item?..Please help
You can store it on the sdCard making a file for the same. But that can be deleted externally also.
For your concern, regarding:
I want this value to be true always for those who bought the item?
with the In-App purchase, after purchasing an item, you don't really Need to store whether it has been purchased or not. The API would do it for you, through which you can fetch the status of an item for a particular id.
To retrieve the list of product's owned by the user, your application
sends a getPurchases call to Google Play. Your application can make a
consumption request by sending a consumePurchase call. In the request
argument, you must specify the in-app product's unique purchaseToken
String that you obtained from Google Play when it was purchased.
Google Play returns a status code indicating if the consumption was
recorded successfully.
Read more on:
Consuming In-app Products, In-app Billing V3
OR
RESTORE_TRANSACTIONS in V2
See Storage options android. I recommend saving sensitive data in your server(also google recommends it). In device you could save data in Shared Preference(not secure in rooted device is deleted on uninstall) or internal storage(deleted on uninstall) or external storage and sqlite databases. Whatever you choose if your data is sensitive then always encrypt it.
Note There is no reliable way to save data in device that persists between multiple installations. If you save it in external storage it is always prone to user deletion.
That's not possible. If you tried implementing it that way, users would abuse your system by manually editing SharedPreferences.
User can clear his phone memory any time. You shouldn't rely on that. Instead, save value in your back end, and if user reinstalls app, check if it's the same user.
you have to store this value other than the sharedprefrences as,sharedprefrences are app specific and they are deleted when you uninstall and re-install the app.
What you can do is store this value on server and when the user installs the app again then you can retrieve the value and manipulate it according to its value
Saving the purchase information on sharedPreference is not the correct way. You should query the server to check if the user has purchased the item or not.
Related
I have an app with a subscription in Google Play.
When the user starts the app, I need to know if the user has an active subscription. This would seem an obvious thing to do, but from searching and trying to implement it, it seems impossible?
I am using Google's newer billing 2/3, following Google's tutorials,
class BillingManager implements PurchasesUpdatedListener
...
public void checkAsync() {
Log.e(TAG, "checkAsync");
billingClient.queryPurchaseHistoryAsync(BillingClient.SkuType.SUBS, new PurchaseHistoryResponseListener() {
#Override
public void onPurchaseHistoryResponse(BillingResult billingResult, List<PurchaseHistoryRecord> list) {
Log.e(TAG, "checkCached result: " + list);
if (list == null) {
return;
}
for (PurchaseHistoryRecord ps : list) {
//System.out.println("PAYMENT: " + ps.getSku() + " : " + ps.getPurchaseTime());
}
}
});
}
public void checkCached() {
Log.e(TAG, "checkCached");
List<Purchase> result = billingClient.queryPurchases(BillingClient.SkuType.SUBS).getPurchasesList();
Log.e(TAG, "checkCached result: " + result);
if (result == null) {
return;
}
for (Purchase purchase : result) {
handlePurchase(purchase);
}
}
This is how I think you're supposed to get a user's purchases. But it does not work at all, both calls return null always. It only returns the correct purchases when you reinstall the app or clear the data.
So how exactly is an app supposed to do this?
Purchasing works for the app once I enter internal testing, and download it through the Google Play link. (before that subscriptions do not work at all).
*** updated
So to further clarify:
I am using a valid test user, and subscriptions are working correctly. My question is on the what the API queryPurchases() or queryPurchaseHistoryAsync() are suppose to do.
What I am seeing, is that these only return purchases that have not be processed by the app. They seem to store that the purchase was processed in the apps data.
After the purchase these return null, after the app restarts these return null.
If I clear the app datam or reinstall the app then they return the purchase (once), then again null after restart.
From what I see, these are only useful to detect when a user reinstalls your app, or installs on a different phone. They cannot be used to determine the status of a subscription.
So my question is,
1 - is this something that just does not work in internal testing and will magically work differently when the app is release?
2 - is there a different API that your suppose to use to check the status of a subscription?
3 - are you suppose to manage subscriptions yourself in your app by storing a user preference/cookie when you acknowledge the subscription the first time so you know when the subscription expires?
You need "licenced testers". They would allow you to "sideload" your app on devices, even for debug builds. My interpretation of sideload in this case would cover installing from Android Studio build tools as well as adb install .... and other methods that don't involve the play store.
https://developer.android.com/google/play/billing/test
Ordinarily, the Google Play Billing API is blocked for apps that aren't signed and uploaded to Google Play. License testers can bypass this check, meaning you can sideload apps for testing, even for apps using debug builds with debug signatures without the need to upload to the new version of your app. Note that the package name must match that of the app that is configured for Google Play, and the Google account must be a license tester for the Google Play Console account.
I also don't see how you're using startConnection. Until that's completed successfully I wouldn't be sure you have the latest data. I wouldn't be surprised if that makes you get stale values. I would check that carefully to make sure there's no silent errors happening, by both looking at onBillingSetupFinished and onBillingServiceDisconnected. And for the time being avoid trusting queryPurchases():
https://medium.com/#NandagopalR/integrating-google-play-billing-into-an-android-application-b6eb6af176a7
The queryPurchases() method uses a cache of the Google Play Store app without initiating a network request. If you need to check the most recent purchase made by the user for each product ID, you can use queryPurchaseHistoryAsync(), passing the purchase type and a PurchaseHistoryResponseListener to handle the query result.
By the way what's the value of isReady() right before queryPurchaseHistoryAsync, and what's the value of BillingResult::getDebugMessage and BillingResult::getResponseCode?
Also, use isFeatureSupported, though it seems it's not like your problem is coming from here. But I'd advise not testing with subscriptions until you get all the moving parts working: https://developer.android.com/reference/com/android/billingclient/api/BillingClient#isFeatureSupported(java.lang.String)
Okay, figured it out, was my mistake.
I was calling queryPurchases() in my main activity onCreate(), but the BillingClient was not ready yet.
I moved it to onBillingSetupFinished() and it now returns the correct purchases.
Everything is now working as expected. You get the active subscriptions when you call queryPurchases() after an app restart.
EDIT: since some people are still checking out this thread, I want to mention that this is highly obsolete, because it is about v2 in app purchase, which is now deprecated. Please check out the latest (currently v3) documentation, it is pretty straightforward
There are loads of threads about this issue, and I think I understand the problem, however, at the moment I cannot test with real purchases, since I don't currently have a valid credit card, what Google accepts, only a Maestro, which is not accepted. This is why I ask for help (not purchase verification, but verifying that my thought process is good).
First, the problem comes from the new verifyPurchase method. The new method checks for signatures, which should be fine. However, Google does not provide any signature for test ids, such as android.test.purchased. This causes the method below to always fail, and it will always return false on the verification, even if the fake purchase has been completed, and I had a confirmation dialog, that the purchase is indeed succesful. The method:
public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
//if(BuildConfig.DEBUG) return true;
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
TextUtils.isEmpty(signature)) {
Log.e(TAG, "Purchase verification failed: missing data.");
return false;
}
PublicKey key = Security.generatePublicKey(base64PublicKey);
return Security.verify(key, signedData, signature);
}
The problem comes from the TextUtils.isEmpty() checks, since the signature will be empty, the code will return false. My temporary, testing workaround is in the code, commented out. If I add the if(BuildConfig.DEBUG) return true; at the beginning of the method for testing, everything is working fine, the user gets the premium features up and running, so the problem is indeed about signatures.
Another problem is, if I purchase android.test.purchased, I cannot query the inventory of the user, so I cannot be sure, that my checker method, which checks if the user has purchased the premium feature, works.
My two questions:
If I remove the if(BuildConfig.DEBUG) return true; line from verifyPurchase, and replace the android.test.purchased id with the real id of the real SKU I am offering, can I be sure everything will work correctly in my case? I repeat, with the added debug functionality everything is working fine. If you need to see any more code, let me know!
The method below checks if the user has purchased the premium feature, if he/she has, then it sets the corresponding preferences, otherwise, if anything goes south, it leaves everything as it is. Is this method correct? I am doing this in the Application class, on every app start up, to prevent tinkering.
private void checkPremium()
{
//get the helper up and running
final IabHelper helper=new IabHelper(INSTANCE, getBase64Key());
helper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result)
{
//if the helper is in a correct state, check the inventory
if(result.isSuccess())
{
helper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener()
{
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv)
{
//if the inventory is reachable, check if we got the premium
if(result.isSuccess())
{
setPremium(inv.hasPurchase(ActWidgetSearch.SKU));
}
}
});
}
}
});
}
Thanks in advance!
I was facing the same question as you a few days ago. For those who download your live application from Google Play, if the test id ("android.test.purchased") works as you said, they will be able to buy your in-app purchases. Google play treats it the same as a 'real purchase' and it goes through the same channel as your personal SKU's. See Below:
Test Purchases (In-app Billing Sandbox)
Once authorized with testing access, those users can side-load your app and test the full merchandising, purchase, and fulfillment flow for your products. Test purchases are real orders and Google Play processes them in the same way as other orders. When purchases are complete, Google Play prevents the orders from going to financial processing, ensuring that there are no actual charges to user accounts, and automatically canceling the completed orders after 14 days.
http://developer.android.com/google/play/billing/billing_testing.html#test-purchases
I just tested my own personal SKU's now and I can say for a fact they're working! (I saw the order in my Google Merchant account). So go out and there, and make some money :)
On a side note, developer to developer... could you show me any examples or links to tutorials on how to run my purchase verification in the Application class? I think that is an idea I definitely want to steal from you. Cheers!
Make sure that you are logged in with the right user on your phone or e.g. add your phone's google account as a test user in the developer console.
http://developer.android.com/google/play/billing/billing_testing.html#billing-testing-static:
In some cases, the reserved items may return signed static responses, which lets you test signature verification in your application. The reserved items only return signed responses if the user running the application has a developer or test account.
I have implement in app purchase (managed) in my application using billing services when ever user wants to buy item he will make purchase request that required internet even if user has already bought the item. so for user convenience i am doing that if user has bought the item then a value will be save in shared pref. and when ever user click on that particular item then 1st it will check shared pref. value that either user has bought item or not if not then go for purchase request else show him/her item.
My Question is that is it safe way? or i have to do something else?
This is not safe. I would discourage you from implementing such a check this way. You should rather go for standard approach and use getPurchases() method. You can call this method at any time (even offline) and if a user has purchases, they will be returned back from that method. Here is a sample code:
IInAppBillingService service; // initialize it first
Bundle response = service.getPurchases(3, "you app package", "inapp", null);
int responseCode = response.getInt(KEY_RESPONSE_CODE);
if (responseCode == RESPONSE_OK) {
ArrayList<String> purchases = response.getStringArrayList(KEY_INAPP_PURCHASE_ITEM_LIST);
...
}
Of curse you need to verify that purchases are signed with correct certificate and purchase state is not cancelled. But this is much safer than storing data in shared properties. Another advantage of this approach is that after user reinstalls the app, all purchases will be automatically available there too.
Complete example of this technique is within TrivialDrive sample app and it's work for me, but Im thinking about offline case. Google Play app caches purchases and gives responses to the getPurchases(3, "pkg", "inapp", null) offline but is this works infinitly or there is something like a timeout for this cache...
Is it possible to receive an event, if the user execute the "Clear Data" on the global app screen?
I create an account in my Application and save the name of the account as preference:
AccountManager accountManager = AccountManager.get(this.context);
Account account = new Account("my account name", "com.mchammer.cantouchthis");
accountManager.addAccountExplicitly(account, "", null);
now, if the user clears the data, the account on the system (used for synchronisation) have to removed too...
thanks for your help
You can't. Your app gets FCed on Clear Data, so you can't run any code.
BUT. You can use SharedPreferences to store a boolean pref, say isAccountInitialized.
You set it to true and store after your AccountManager logic has done creating the account. Then, every time you use the account for sync (or every time you launch your main activity, or your service is up do do something - this you'll have to figure out based on your app specifics) you pull the isAccountInitialized from shared prefs and if it's false and your account is present in AccountManager - that's your sign that user has executed Clear Data and it's time to remove the account from AccountManager. If it's false and there's no account - that's first launch.
// OFFTOP Nice package name :)
It is not possible as As soon as you press clear data. Your application process will be stopped. You can check this link for answer.
I integrated inappPurchase code to my android application.its working fine but i need handle
purchase data because if user buy my products after he uninstalled,after some days he again installed in the senario no need to purchase again.For this purpose how to handle data
Most of the data that you place is removed when user Uninstalls the file, so basically create a file or folder with details of his accounts in an encrypted format either on the phone or on the SD card (preferably use Phone storage)
but again if user does a factory reset on his phone then all information will be lost
So
Either create a web service and pass all the account information , you can use the Phones IMEI number to act as a unique ID or use login info for the user so you can identify him or her even if he or she changes her phone
or use Google's GCM to target individual customers
EDIT
Ya restoring is there Android In-app billing RESTORE_TRANSACTION usage
but what if your not using in built, but are using paypal SDK or some other methods then we have such the above mentioned options
You should maintain all the purchase data on the server side for each device.
You have to use RestoreTransactions function for getting the purchase data of the particular user.
mBillingService.restoreTransactions();
And in ResponseHandler.java file you will get all details in
public static void purchaseResponse(final Context context,
final PurchaseState purchaseState, final String productId,
final String orderId, final long purchaseTime,
final String developerPayload, final String purchaseToken) {
}
Items that are managed per user account can be purchased only once per user account. When an item is managed per user account, Google Play permanently stores the transaction information for each item on a per-user basis. This enables you to query Google Play with the RESTORE_TRANSACTIONS request and restore the state of the items a specific user has purchased.
Refer to this link:
http://developer.android.com/guide/google/play/billing/billing_admin.html
And read the section 'Choosing a Purchase Type'.