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.
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.
Until june 20th 2016 i was able to cancel test purchases done in my app.
Doing multiple in-app purchases (not consumable) from the same test account made it easy to develop and test the code without too much hazzle.
After 20th june 2016, the purchases did not show in my merchant account and
i was unable to do more than 1 purchase from my test account. All i got was the: "you already own this item" message.
I logged a request to the google developer support group and the answer was:
Beginning June 20, 2016, we changed test purchases for one-time in-app purchases (IAPs).
Previously, test purchases for one-time IAPs generated order IDs. Starting June 20, 2016, one-time IAPs do not generate official order IDs (if at all) and will not appear in the Merchant Center. This behavior already applies to subscription IAPs.
You can learn more about testing in-app billing in the Android Developers Help Center: https://developer.android.com/google/play/billing/billing_testing.html#testing-purchases
allright.. so i go to the mentioned link and theres a section there:
Canceling completed test purchases
which states:
Google Play accumulates completed test purchases for each user but does not pass them on to financial processing.
In some cases, you might want to manually cancel a test purchase to continue testing. To do so, open the app page in the Play Store. If the test purchase that you want to cancel is a subscription, you can also use the cancel() method of the Purchases.subscriptions API.
Important: The refund() and revoke() methods of the Purchases.subscriptions API don't support test purchases.
So I go to the app page in play store...and do what exactly? the webpage does not state what i am supposed to do there. anyone know?
it does say: you can also use the cancel() method of the Purchases.subscriptions API.
which indicates that using the cancel() method is not the only method.
How to solve this without adding additional code in my app?
I went into the main Google Play Console page and clicked on Order Management. Under that I was able to select all test purchases and Refund them. I'm the primary developer of the app so I have access. If you are a tester you'd probably have to contact the support team and request that they refund your order.
All managed in-app products are consumable.
as stated in the docs.
That means that you can consume an owned item instead of cancelling the purchase and buy it all over again.
I suggest querying the inventory at the app launch time:
mIabHelper.queryInventoryAsync(this);
You can then consume the owned item in the callback:
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Purchase purchase = inventory.getPurchase(MY_SKU);
boolean isBought = (purchase != null && verifyDeveloperPayload(purchase));
if (isBought) {
mIabHelper.consumeAsync(purchase, new OnConsumeFinishedListener() {
#Override
public void onConsumeFinished(Purchase purchase, IabResult result) {
//Clear the purchase info from persistent storage
}
});
}
}
This is OK for testing the IAB flow but make sure to remove this code from the release version.
On Play Console, go to Developer Account -> Account Details, set the license testers (you are one by default)
Purchase items
Go to Order Management, choose those test orders, select: refund, but DON'T FORGET to check REVOKE when you are refunding. (I forgot to revoke, and now can't find a way to take them back)
Anyway, another test account will do.
I found a solution which isn't very convenient, but works. It seems like you can consume uncomsumable products and that way you can buy them again. I'm working with phonegap so I only have example code for the cordova-plugin-purchase plugin:
store.when("your.product.id").updated(product => {
if(product.owned) {
var transaction = product.transaction;
product.transaction = null;
store.inappbilling.consumePurchase(
function() { // success
alert("consume success");
},
function(err, code) { // error
alert("consume error " + err)
},
product.id,
transaction.id
);
}
});
The updated callback gets called when you call store.refresh() or buy the product. So depending on your use case you'd want to implement an additional method of checking when to consume the product.
I have no experience with the native Android in-app payments, but obviously you will be able to consume the products there as well.
Edit: Sorry, I just read that you didn't want to include additional code in your project. I don't think that's possible at the moment, but would like to keep my answer here because it might help other people trying to test in-app payments.
What worked for me was a combination of both:
Go to order management and refund
clear cache/data in Play Store app (as well as your app in you placed some shared prefs).
Also, in case you get an item already owned status, you can consume the purchase using the purchase token and calling billingClient.consumeAsync().
Didn't find a solution for this.
My workaround is simply remove the current test user from the test users list, make a real purchase, then cancel it using the merchant console.
The queryPurchaseHistoryAsync method still finds test orders I've made over the last year, despite having long ago consumed, refunded, and revoked them. One precaution I've found helpful is clearing the data in the Google Play Store app (settings/apps/google play store/storage/clear data). queryPurchaseHistoryAsync pulls obsolete purchase data from here, although only the non-networking (and completely unreliable I've found) queryPurchases is supposed to do this. You may have to add additional code to your app after all, but it doesn't have to be much.
With the dropping of support for Trivial Drive 2 (the link in the docs takes you to a '404 page does not exist' error, the github files are archived, and updating to billing:2.1.0 will give you a vending import compile error in the IabHelper class), answers to this popular question involving IabHelper might be considered obsolete. Billing is a lot simpler now if you follow the basic code pieces in the docs starting here https://developer.android.com/google/play/billing/billing_overview with no messy helper classes. One persistent issue is a 'Both methods have same erasure, yet neither overides the other' method clash error you may run into with this implementation, see my solution here 'Both methods have same erasure, yet neither overides the other' method clash error in SkuDetailsResponseListener().
Once you have the newest billing code implemented, you can create a hidden trigger in your production app to call queryPurchaseHistoryAsync to get a purchaseHistoryRecordList. Then call consumeAsync for each item in this list. Here is the barebones code to consume all test orders, allowing multiple tests of your nonconsumables:
billingClient.queryPurchaseHistoryAsync(BillingClient.SkuType.INAPP,
new PurchaseHistoryResponseListener() {
#Override
public void onPurchaseHistoryResponse(BillingResult billingResult,
List<PurchaseHistoryRecord> purchaseHistoryRecordList){
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchaseHistoryRecordList != null) {
for (PurchaseHistoryRecord purchaserecord : purchaseHistoryRecordList) {
if(purchaserecord!=null){
ConsumeParams consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchaserecord.getPurchaseToken())
.setDeveloperPayload(purchaserecord.getDeveloperPayload())
.build();
billingClient.consumeAsync(consumeParams, consumelistener);
}}}}});
For people using a way based on the new TrivialDriveKotlin, consumable products are consumed during the installation of the app in the method
handleConsumablePurchasesAsync
If your purchase is not consumable, you can make it consumable by adding the corresponding sku into CONSUMABLE_SKUS in the GameSku object. Exemple:
val CONSUMABLE_SKUS = listOf(GAS, PREMIUM_CAR)
Then uninstall your app from your device and install it again, and your non consumable purchase is available again. Fast and simply.
Of course, don't forget to remove your
I've found similar questions to this one and followed the advice without much success.
I'm writing my first app and I'm adding inapp purchases.
When 'mHelper.launchPurchaseFlow' is called the app displays the message box 'Error, Authentication is required. You need to sign in to your Google account'
I am signed in. I tried removing the account and reinstating it. i tried creating a new account and using it instead. I tried the app on two different tablets with the same result.
Here is a sample of my code. The purchase item is set up in my google developer console. My code calls 'buy_two_stars' only after mHelper.startSetup() returns a success.
The value in the purchaseprogress variable indicates it never gets past the buy_two_stars() function:
public void buy_two_stars()
{
mHelper.launchPurchaseFlow(this,"item_stars",1001,purchasedit,"");
PurchaseProgress=0;
}
IabHelper.OnIabPurchaseFinishedListener purchasedit=new IabHelper.OnIabPurchaseFinishedListener() {
#Override
public void onIabPurchaseFinished(IabResult result, Purchase info)
{
if(result.isFailure())
{
PurchaseProgress=-1;
}
else if(info.getSku().equals("item_stars") )
{
purchaseditem=info;
mHelper.consumeAsync(info,consumerfunc);
PurchaseProgress=1;
}
}
};
IabHelper.OnConsumeFinishedListener consumerfunc=new IabHelper.OnConsumeFinishedListener()
{
#Override
public void onConsumeFinished(Purchase purchase, IabResult result) {
if(result.isFailure())
{
PurchaseProgress=-1;
}
else
{
PurchaseProgress=0;
}
}
};
many thanks.
Have you published your app yet?
According to Google documentation:
"Draft Apps are No Longer Supported
Previously, you could publish a "draft" version of your app for testing. This functionality is no longer supported. Instead, there are two ways you can test how a pre-release app functions on the Google Play store:
You can publish an app to the alpha or beta distribution channels. This makes the app available on the Google Play store, but only to the testers you put on a "whitelist".
In a few cases, you can test Google Play functionality with an unpublished app. For example, you can test an unpublished app's in-app billing support by using static responses, special reserved product IDs that always return a specific result (like "purchased" or "refunded")."
Source: http://developer.android.com/google/play/billing/billing_testing.html#draft_apps
So to test with real product IDs, I'm afraid you need to publish the app.
I was having the same problem and I didn't see anything outstanding in your code. I bet you did the same as me. Uploaded your APK to alpha, beta, or production and then tried to make it work. You must PUBLISH it in the top right corner. Then wait about 12 hours for it to become fully functional.
Meanwhile there is a new tab in the Play Console called "License Testing"
... it looks about like that:
I set up a beta account to test IAP for google app that I am working on, the issue I have is, once I have purchased One-time products(non-recurring charge) the test IAP, I cannot 'remove it' as such, so now, even when I delete the app and re-install, it remembers the purchase, that's great in the real world for a user, but not great when trying to fix the bugs!
Is there any way (short of making a ton of gmail accounts to test with) to remove the purchase from the account?
This is an old question but if someone is still looking for a solution then go to:
Google Play console and open the Order Management tab
There you can refund / cancel test purchases. Then clear the purchase state using this command:
adb shell pm clear com.android.vending
The only way I know is to force a consume in your app. You can then remove that code.
I am using cc.fovea.cordova.purchase plugin for cordova to manage my IAP purchases. To get my test Non-Consumables to be deleted I changed my registration from Non-consumable to Consumable.
store.register({
id: this.predatorID,
alias: 'Predator Pack',
type: store.CONSUMABLE //store.NON_CONSUMABLE
});
Also, apparently there are reserved keywords you could use instead (if you're into that). - https://developer.android.com/google/play/billing/billing_testing.html
I encountered the same situation and started to research. Unfortunately, the directions made here did not produce a solution.
I want to share the solution that worked for me.
If you call the method below in the right place, the solution will be produced. Source : Link
/**
* Recall that Google Play Billing only supports two SKU types:
* [in-app products][BillingClient.SkuType.INAPP] and
* [subscriptions][BillingClient.SkuType.SUBS]. In-app products are actual items that a
* user can buy, such as a house or food; subscriptions refer to services that a user must
* pay for regularly, such as auto-insurance. Subscriptions are not consumable.
*
* Play Billing provides methods for consuming in-app products because they understand that
* apps may sell items that users will keep forever (i.e. never consume) such as a house,
* and consumable items that users will need to keep buying such as food. Nevertheless, Google
* Play leaves the distinction for which in-app products are consumable entirely up to you.
*
* If an app wants its users to be able to keep buying an item, it must call
* [BillingClient.consumeAsync] each time they buy it. This is because Google Play won't let
* users buy items that they've previously bought but haven't consumed. In Trivial Drive, for
* example, consumeAsync is called each time the user buys gas; otherwise they would never be
* able to buy gas or drive again once the tank becomes empty.
*/
private fun clearIapHistory() {
billingClient!!.queryPurchases(BillingClient.SkuType.INAPP).purchasesList
.forEach {
val params =
ConsumeParams.newBuilder().setPurchaseToken(it.purchaseToken).build()
billingClient!!.consumeAsync(params) { responseCode, purchaseToken ->
when (responseCode.responseCode) {
BillingClient.BillingResponseCode.OK -> {
}
else -> {
Log.w(LOG_TAG, responseCode.debugMessage)
}
}
}
}
}
if (inventory.getPurchase(ITEM_SKU) != null ) {
try {
mIabHelper.consumeAsync(premiumPurchase, new IabHelper.OnConsumeFinishedListener() {
#Override
public void onConsumeFinished(Purchase purchase, IabResult result) {
Toast.makeText(MainActivity.this, "Consumed the test purchase successfully", Toast.LENGTH_SHORT).show();
}
});
} catch (IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
}
}
However refund() and revoke() methods don't support test purchases and you are left with only consumeAsync() option.
Just:
Purchase unlockedPurchase = inventory.getPurchase(SKU_UNLOCKED);
// Log unlockedPurchase.getOrderId();
Go to your Google Play panel, Order management, look for that order id and refund it (it should say Test order if it is your own order).
I guess the only method working is to...
Consume It!
For further info, get to the consuming document and search for "consume": https://developer.android.com/google/play/billing/integrate
Here are the important steps for you:
Dependence setup.
Billing client connection.
Query the Purchase.
Consume(Purchase).
Good Luck~
Test non-consumable products
To perform multiple test purchases for the same non-consumable product, you can refund and revoke purchases using Google Play Console.
I had a similar issue. Fortunately, the app I'm working with is WebView-based, so I can easily inject a link or button to trigger some Javascript to call back into the application to consume the test orders. Since test orders have an empty string for the orderId, it is easy to identify them to consume them. Once consumed, the item can be "purchased" again. Removing the button requires commenting out one line of code BUT if the button accidentally makes it into the final published app, it won't cause any problems since the code only consumes test orders - that is, real orders are not affected. That button will just be embarrassing instead of a disaster.
I'm working on a device without a credit card associated with it. I set up some promo codes and use the "Redeem Code" option for my test orders. Promo codes result in no risk of money exchanging hands and I'm able to completely verify IAB functionality in my app with real products without having to resort to the IAB test codes.
Nothing shows up for me in Google Wallet as per the post by Martin Kool.
You can refund the test purchase of non-consumable product and it will work just fine.
Using the Play Console website
Open Play Console.
On All Apps page left side, select Order Management
Use the "Search orders" box to search by order ID or the user's full email address.
Open order, click on Refund button. Select Entitlement checkbox and refund.
More details here about refunds: https://support.google.com/googleplay/android-developer/answer/2741495
Google Play Purchases are stored in Google Wallet.
https://wallet.google.com
When signed, go to "Transactions" on the left. Test purchases can be cancelled from there.
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...