In App Purchase seems to be called multiple times
Asked Answered
A

5

6

I implemented an In-App Purchase with the Play Billing Library 1.0 according to Google's tutorial. I have only 1 item for purchase and when it gets unlocked, I show a Toast message with the length Toast.LENGTH_SHORT. However, the Toast stays there for like 10 seconds, so I assume it gets called multiple times. It does NOT happen when I unlock it via queryPurchases (if someone bought it earlier and reinstalled the app in the meantime).

Anyone have an idea why the Toast stays so long / why it gets called multiple times?

Inside my BillingManager class:

@Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) {
    if (responseCode == BillingClient.BillingResponse.OK) {
        for (Purchase purchase : purchases) {
            handlePurchases(purchase);
        }
        mBillingUpdatesListener.onPurchasesUpdated(mPurchases);
    } else if (responseCode == BillingClient.BillingResponse.USER_CANCELED) {

    } else {

    }
}

public void handlePurchases(Purchase purchase) {
    //here could be validation on own server

    mPurchases.add(purchase);
}

Main Activity implements BillingUpdatesListener:

@Override
public void onPurchasesUpdated(List<Purchase> purchases) {
    for (Purchase purchase : purchases) {
        switch (purchase.getSku()) {
            case "premium":
                unlockPremium();
                break;
        }
    }
}

public void unlockPremium() {
    mPremiumUnlocked = true;
    savePremiumUnlocked();
    Toast.makeText(this, getResources().getString(R.string.premium_congrats), Toast.LENGTH_SHORT).show();
    mAdView.setVisibility(GONE);
}
Antiphonal answered 21/9, 2017 at 12:7 Comment(5)
Debug it so you can confirm whether if it's called multiple times or norMainmast
How? I can only do the purchase with the real app downloaded from the playstore, not over Android Studio.Antiphonal
Rubbish. you can do test purchases too. RTFMKarren
How can i run the debugger on an app that i installed through google play? Test purchases are done over alpha testing too.Antiphonal
"RTFM" doesn't apply at all here. This behavior is undocumented. There's no reason to expect multiple success callbacks for a single successful transaction. We have to write ugly code in our apps to patch Google's secret API.Moralize
C
6

If I understand your correctly, you say that when you first purchase the in-app product, you are getting multiple Toasts?

In the current version (1.0) of the Billing library, this happens because multiple broadcasts are being made by the system.

For example, if you look at or breakpoint onPurchaseFinishedReceiver at line 120 in BillingClientImpl.java within the library, this is called at least twice after making a purchase. Both times, the in-app purchase data is attached but I noticed that the intent Action was different for each broadcast.

In the first broadcast, the Action was com.android.vending.billing.PURCHASES_UPDATED but in the second it was proxy_activity_response_intent_action. The library does not filter out the Action values and so all of these broadcasts result in your purchasesUpdatedListener being called.

I didn't investigate further but I think what we can take from this is that SOME sort of change occurred and it was deemed necessary to broadcast that change.

To avoid your multiple toasts, just do not display the toast unless your Premium functionality is unlocked. i.e. If it is already unlocked, simply ignore the change notification.

By the way, it is totally possible to debug the purchase flow in Android Studio. Just sign your debug apk with your release key and make sure the apk version is not higher than the one in Play Store.

buildTypes {

    debug {
        minifyEnabled false
        debuggable true
        signingConfig signingConfigs.release
    }


    release {
        minifyEnabled false
        signingConfig signingConfigs.release
    }
}
Code answered 21/9, 2017 at 14:36 Comment(3)
Maybe you have an idea for my other question too #46342050Antiphonal
Also for clarification: I dont have to care about the multiple calls of onPurchaseUpdated at all? I just surround my unlockPremium method with a check for Premium already being unlocked (so i can skip it).Antiphonal
Yes. I'm receiving multiple calls myself. There is probably a good reason for it but to be honest, I don't care enough to find out why. I just ignore the extra callsCode
L
2

I don't know if this applies to your exact scenario, but we are experiencing the same thing and it is a bug on Google's end.

See https://issuetracker.google.com/issues/66054158 for more information.

Edit: I just saw that @goRGon posted the same thing :)

The example of multiple people in Spain isn't the same situation as what's described above. In the Spain scenario, the users are actually purchasing two copies of the IAP, so they are two separate receipts and the users should be rewarded with two copies of whatever they purchased. In the bug scenario, one single receipt is presented to the user twice so duplicates can actually be caught. But either way, back-end validation systems need to accommodate hackers/bugs in code that might cause the same receipt to be sent twice in a row.

Latchstring answered 22/9, 2017 at 22:48 Comment(0)
C
2

If your subscription activity was closed and reopened multiple times within the same application process, then onPurchasesUpdated may be called multiple times if the PurchasesUpdatedListener somehow stays attached to the previous instances of billingClient and if the previous connections are still alive. I noticed that the number of times I closed and reopened the subscription activity, onPurchasesUpdated was called for the same amount after a successful launchBillingFlow.

To fix this I needed to end the connection when the activity is destroying, like this -

@Override
protected void onDestroy() {
    super.onDestroy();
    if (billingClient!= null) {
        billingClient.endConnection();
    }
}
Cleistogamy answered 12/2, 2022 at 8:35 Comment(2)
This is it. I'm wondering if this is documented in official android docs. And if not then WHY??Therefore
@Therefore They recommend calling this method in their documentation but without enough explanation.Cleistogamy
L
1

Great answer and looking deep by @Kuffs!

Google will fix multiple calls soon: https://issuetracker.google.com/issues/66054158

However, your integration with billing flow should work even when onPurchasesUpdate was triggered multiple times, since it could happen anyway. For example, if somebody was buying in parallel on another device with the same @gmail account. And people in some countries (e.g. Spain) do share their @gmail accounts rather frequently with many friends and family members.

Please, check TrivialDrive_v2 implementation to get an idea, how handle such situations gracefully.

Loam answered 22/9, 2017 at 22:29 Comment(2)
that's gotta be a bad joke, the only thing they do with purchases is verifying their signatureJapheth
The TrivialDrive_v2 URL 404s, does anyone have a current URL?Monatomic
R
0

I hope It will help you

        _purchaseUpdatedSubscription =
        FlutterInappPurchase.purchaseUpdated.listen((productItem) {
      print('purchase-updated: ${productItem}');
      getDetails(productItem);
    });

        String orderId = '';
      getDetails(PurchasedItem purchasedItem) {
        if (purchasedItem != null) {
          if (orderId != purchasedItem.orderId) {
            orderId = purchasedItem.orderId;
            print('productItem.transactionReceipt : ${purchasedItem.transactionReceipt}');
            var decodedData = jsonDecode(purchasedItem.transactionReceipt);
            print('purchaseState : ${decodedData['purchaseTime']}');
    
            if (decodedData['purchaseState'] == 0) {
    
              if(purchasedItem.productId == selected_package) {
                print("Purchased successfully");
                onPurchased(purchasedItem);
              }
            } else {
              ShowMsg('Transaction Failed !, Something went wrong.');
            }
          }
        }
      }
Ridglea answered 18/11, 2021 at 10:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.