Why is processPurchases() launuched two times in play-billing-samples?

Viewed 623

Figure 3 and Code A is from the project play-billing-samples, you can see here.

From the document, launchBillingFlow may be triggered by a button click when the user wants to buy something, then onPurchasesUpdated will be launched.

I'm confused with the comment of override fun onBillingSetupFinished(billingResult: BillingResult){ }, onBillingSetupFinished (include processPurchases()) will be launched when BillingClient has been successfully established.

Normally in a case, I need to init BillingClient first, onBillingSetupFinished be launched, processPurchases() will be launched too, then I click a Buy button, onPurchasesUpdated will be launched, and processPurchases() will be launched again.

Are there some problems with the code?

* Figure 3 -- Server-reliant billing integration with offline access to some entitlements
 *
 *  _____                        _________________
 * |Start|----------------------|launchBillingFlow|
 *  -----                        -----------------
 *                                        |
 *                                  ______v____________
 *                                 |onPurchasesUpdated |
 *                                  -------------------
 *                                 /      |
 *                   ITEM_ALREADY_OWNED   |
 *                               /        |
 *  _____       ________________v__       |
 * |Start|-----|queryPurchasesAsync|      OK
 *  -----       -------------------       |
 *                               \        |
 *                               v________v_______
 *                              |processPurchases |
 *                               -----------------
 *                                        |
 *   

                                 |

Code A

    /**
     * This is the callback for when the connection to the Play [BillingClient] has been successfully
     * established. It might make sense to get [SkuDetails] and [Purchases][Purchase] at this point.
     */
    override fun onBillingSetupFinished(billingResult: BillingResult) {
        when (billingResult.responseCode) {
            BillingClient.BillingResponseCode.OK -> {
                Log.d(LOG_TAG, "onBillingSetupFinished successfully")
                querySkuDetailsAsync(BillingClient.SkuType.INAPP, GameSku.INAPP_SKUS)
                querySkuDetailsAsync(BillingClient.SkuType.SUBS, GameSku.SUBS_SKUS)
                queryPurchasesAsync()
            }
            BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> {
                //Some apps may choose to make decisions based on this knowledge.
                Log.d(LOG_TAG, billingResult.debugMessage)
            }
            else -> {
                //do nothing. Someone else will connect it through retry policy.
                //May choose to send to server though
                Log.d(LOG_TAG, billingResult.debugMessage)
            }
        }
    }



    override fun onPurchasesUpdated(
            billingResult: BillingResult,
            purchases: MutableList<Purchase>?
    ) {
        when (billingResult.responseCode) {
            BillingClient.BillingResponseCode.OK -> {
                // will handle server verification, consumables, and updating the local cache
                purchases?.apply { processPurchases(this.toSet()) }
            }
            BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> {
                // item already owned? call queryPurchasesAsync to verify and process all such items
                Log.d(LOG_TAG, billingResult.debugMessage)
                queryPurchasesAsync()
            }
            BillingClient.BillingResponseCode.SERVICE_DISCONNECTED -> {
                connectToPlayBillingService()
            }
            else -> {
                Log.i(LOG_TAG, billingResult.debugMessage)
            }
        }
    }


   fun queryPurchasesAsync() {
       ...
       processPurchases(purchasesResult)
   }
1 Answers

Everytime it establishes a new connection to Google Play BillingClient before you click the "buy" button, processPurchases is called (from inside queryPurchasesAsync() to process any past purchases which have been purchased but not yet consumed. This may happen because of intermittent internet connection disruption or other reasons. You may treat this processPurchases call as to complete and consume previous purchases, if there is any.

Naturally, when you click the "buy" button, processPurchases is also invoked immediately. This may be the reason why you see two calls to processPurchases in the code.

Related