53

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?

Cœur
  • 37,241
  • 25
  • 195
  • 267
KennethJohansen
  • 579
  • 1
  • 4
  • 7
  • 1
    Hi, did you solved this?. i am also facing same scenario after doing test purchase. – Ramesh_D Aug 24 '16 at 02:50
  • 1
    you can open all the test purchase from the following link:https://play.google.com/store/account?feature=gp_receipt, on this page i found only way to cancel is using "report problem" button. – Ramesh_D Aug 24 '16 at 02:50
  • @Ramesh_D the Report an Issue gives me: "An unexpected error has occurred. Please try again later." Any other ideas? – galgo Aug 24 '16 at 23:26
  • 1
    I'd also like to know how to do this. Not being able to test a purchase multiple times makes it almost impossible to test purchasing at all... – Moritur Aug 25 '16 at 13:21
  • 1
    Did you find a solution to cancel it? – powder366 Nov 03 '16 at 19:03
  • Does anyone know how to cancel a test order? (I created a test order using ionic native in app purchases). – Richard Jun 20 '17 at 09:48
  • This needs to be reported exactly the question that I was about to post. yet another example of irresponsible documentation from google. – Utsav Gupta Jul 15 '17 at 06:22

8 Answers8

29

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.

cminus
  • 1,163
  • 12
  • 25
  • 3
    Brilliant, brilliant answer! Works perfectly. Once the transaction was flagged as "Refunded", I Force-Quitted Play Store and cleared its cache and application data. Then I was un-subscribed and could re-purchase the subscription. – Michael Rodrigues Jan 01 '18 at 08:12
  • I had to also consume the product (see UPDATE2 here: https://stackoverflow.com/questions/38130035/cancelling-orders-on-google-play-iab-test-purchases-after-june-20-2016) – franswa Oct 01 '18 at 15:17
  • 1
    Tried that, and it seemed to to work, as in the Play Console, the order appeared as "refunded". However inside the game, I still got a receit. This morning, however, it worked out again, ie the app received no receit. My guess is, that it took some time, for some caches to clear. Maybe that helps someone. – joergipoergi Jun 04 '19 at 07:10
  • 1
    There should be better ways to cancel in app test subscriptions, check their expiry time, renewal time etc. The whole API is a huge mess – Karanveer Singh Apr 27 '20 at 09:21
  • This still isn't working for me, even after force quitting Play Store and clearing cache and data. I have two purchases that always come through. They don't have transaction IDs. All my orders on the order management page are either refunded or payment declined (via a test card). I'm using the flutter_inapp_purchase plugin, so the problem might be there. But I can't remove these and I don't know how to distinguish them from real purchases. – user1978019 Mar 01 '21 at 00:09
  • Don't forget to also remove the buyer's entitlement otherwise the product will still be owned. See https://stackoverflow.com/a/67633608/5369519. – flauschtrud Jun 14 '21 at 08:59
  • I don't see an order management tab – giorgio79 Nov 13 '21 at 05:59
  • @giorgio79 Make sure you are in the view where you can see all of your apps and not on the specific app you want to cancel orders. In the All apps view you should see the Order management option near the top of the left hand menu under Users and Permissions and above Download reports. – cminus Nov 17 '21 at 14:30
20

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.

dev.bmax
  • 8,998
  • 3
  • 30
  • 41
12
  • 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.

user2167374
  • 121
  • 1
  • 3
  • 3
    I discovered you can actually revoke even if you forgot, but it's a pain because you need to use the api. The docs are here: https://developers.google.com/android-publisher/api-ref/orders/refund Make sure you include the ?revoke=true on the end of the url when performing the request. I made this request much later, had to wait up to 30 mins but it correctly revoked a purchase I'd previously forgotten to revoke in the Console – Cliff Cawley Jan 09 '19 at 00:18
  • Cliff, I tried your solution but I am getting error message that product with such ID does not exist. It took me couple of hours to setup everything. Could the delay be a reason? I am still getting a message that I own a product, even after: "adb shell pm clear com.android.vending". – Developer May 09 '22 at 13:00
6

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.

Moritur
  • 1,651
  • 1
  • 18
  • 31
1

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().

chitgoks
  • 311
  • 2
  • 6
  • 17
0

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.

Eli
  • 707
  • 8
  • 16
  • Where you still in Beta/Alpha? – powder366 Nov 03 '16 at 19:01
  • No, production. – Eli Nov 08 '16 at 12:48
  • Just an FYI, you can make a real purchase with an Alpha or Beta version. I'm not sure what Google's policy is regarding making real purchases then canceling them for testing so I can't recommend doing that. – Dave Rager May 15 '17 at 12:37
  • I've removed the user from the `License Testing` section but the test purchase still exists. – Srikar Reddy May 22 '17 at 05:35
  • I'm the owner of my application, When I try to purchase it always go to test purchase. But other people can purchase live. How to change this? Anybody have got any idea? – sreenadh Feb 12 '20 at 07:36
0

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);
}}}}});
Androidcoder
  • 4,389
  • 5
  • 35
  • 50
0

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

Turvy
  • 882
  • 1
  • 8
  • 23