Antoine Gallet
Antoine Gallet

Reputation: 53

Issue with empty queryProductDetailsAsync

I have an issue retrieving product details, I have setup in my google play console this way:

my google play config

And I have done the following:

myLog("MYAPP-TEST---1")
val purchasesUpdatedListener =
    PurchasesUpdatedListener { billingResult, purchases ->
        if (billingResult.responseCode ==
            BillingClient.BillingResponseCode.OK
            && purchases != null
        ) {
            for (purchase in purchases) {
                // Process the purchases
                myLog("MYAPP-TEST---")
            }
        } else if (billingResult.responseCode ==
            BillingClient.BillingResponseCode.USER_CANCELED
        ) {
            myLog("MYAPP-TEST---2")
            // Purchase cancelled by user
        } else {
            myLog("MYAPP-TEST---3")
            // Handle errors here
        }
    }

var billingClient = BillingClient.newBuilder(this)
    .setListener(purchasesUpdatedListener)
    .enablePendingPurchases()
    .build()

billingClient.startConnection(object : BillingClientStateListener {
    override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
            // Connection successful
            myLog("MYAPP-TEST---SUCCESS")
        } else {
            // Connection failed
        }
    }

    override fun onBillingServiceDisconnected() {
        // Connection to billing service lost
        myLog("MYAPP-TEST---DISCONNECTED")
    }
})


val productList: ArrayList<Product> = ArrayList()
productList.add(
    Product.newBuilder()
        .setProductId("poroductid1")
        .setProductType(BillingClient.ProductType.SUBS)
        .build()
);
productList.add(
    Product.newBuilder()
        .setProductId("poroductid2")
        .setProductType(BillingClient.ProductType.SUBS)
        .build()
);
productList.add(
    Product.newBuilder()
        .setProductId("poroductid3")
        .setProductType(BillingClient.ProductType.SUBS)
        .build()
);
productList.add(
    Product.newBuilder()
        .setProductId("poroductid4")
        .setProductType(BillingClient.ProductType.SUBS)
        .build()
);

val queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(productList)
        .build()

processPurchases()

billingClient.queryProductDetailsAsync(queryProductDetailsParams) { billingResult,
                                                                     skuDetailsList ->
    if (skuDetailsList.isNotEmpty()) {
        for (productDet in skuDetailsList) {
            myLog("MYAPP-TEST----")
            myLog(productDet.name)
        }
        // Process list of matching products
    } else {

        myLog("MYAPP-TEST---No product matches found")
        // No product matches found
    }
    // Process the result
}

And I keep getting those debugs

2022-09-16 16:04:17.983 13457-13457/com.my.app D/MainActivity: MYAPP-TEST---1

2022-09-16 16:04:18.012 13457-13457/com.my.app D/MainActivity: MYAPP-TEST---No product matches found

2022-09-16 16:04:18.470 13457-13700/com.my.app D/MainActivity: MYAPP-TEST---SUCCESS

I also have publish my app so products should be "publicly" available (don't know if it is still needed, but I seen elsewhere it was).

So has you can see my product details request seams to be empty.

I am pretty new in kotlin / app developpement so I will probably need a lot of explaining.

Upvotes: 5

Views: 4220

Answers (2)

Djek-Grif
Djek-Grif

Reputation: 1516

One more possibility to have this empty is missing right location configs for particular subscription in google console.

Upvotes: 0

Jeremy F
Jeremy F

Reputation: 56

Your setup succeeds after trying to retrieve the product list. You want to be sure that the setup succeeds before.

And this is what BillingClientStateListener is for.

First you should put all your products retrieval code in a separate function :

fun retrieveProducts() {
    val productList: ArrayList<Product> = ArrayList()
productList.add(
    Product.newBuilder()
        .setProductId("poroductid1")
        .setProductType(BillingClient.ProductType.SUBS)
        .build()
);
productList.add(
    Product.newBuilder()
        .setProductId("poroductid2")
        .setProductType(BillingClient.ProductType.SUBS)
        .build()
);
productList.add(
    Product.newBuilder()
        .setProductId("poroductid3")
        .setProductType(BillingClient.ProductType.SUBS)
        .build()
);
productList.add(
    Product.newBuilder()
        .setProductId("poroductid4")
        .setProductType(BillingClient.ProductType.SUBS)
        .build()
);

val queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(productList)
        .build()

billingClient.queryProductDetailsAsync(queryProductDetailsParams) { billingResult,
                                                                     skuDetailsList ->
    if (skuDetailsList.isNotEmpty()) {
        for (productDet in skuDetailsList) {
            myLog("MYAPP-TEST----")
            myLog(productDet.name)
        }
        // Process list of matching products
    } else {

        myLog("MYAPP-TEST---No product matches found")
        // No product matches found
    }
    // Process the result
}

Afterwards, call your function in the BillingClientStateListener onBillingSetupFinished function :

billingClient.startConnection(object : BillingClientStateListener {
    override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
            // Connection successful
            myLog("MYAPP-TEST---SUCCESS")
            // This is where you can start retrieving your products
            retrieveProducts()
        } else {
            // Connection failed
        }
    }

    override fun onBillingServiceDisconnected() {
        // Connection to billing service lost
        myLog("MYAPP-TEST---DISCONNECTED")
    }
})

The problem you faced is due to asynchronous actions : both your client setup and your purchases retrieval are asynchronous, meaning that they aren't executed on the same thread that your basic code.

To be sure to do things in the good order, you have to use those listeners the Billing library provides you with !

First, you want to setup your billing service. When done, the onBillingSetupFinished callback is called. That's when you are sure you can go further !

This is a classical "problem" you will have to face in Android development, and the listeners system is a classical way of doing actions resulting from another action done in another thread than the main thread.

Upvotes: 4

Related Questions