Adding multiple products to productlist for queryProductDetailsAsync in android billing 5.0.0
Asked Answered
U

2

12

In the old android billing implementation you would build an sku list to query products:

List<String> skuList = new ArrayList<>();
        skuList.add(SKU_POTION);
        skuList.add(SKU_SWORD);
        skuList.add(SKU_BOW);
        SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
        params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);

The new billing implementation is more involved, and appears to limit you to adding just one product to a query list:

ImmutableList<QueryProductDetailsParams.Product> productList = ImmutableList.from(QueryProductDetailsParams.Product.newBuilder()
                    .setProductId(SKU_POTION)
                    .setProductType(BillingClient.ProductType.INAPP)
                    .build());
    
            QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
                    .setProductList(productList)
                    .build();
    
            billingClient.queryProductDetailsAsync(
            params,
            new ProductDetailsResponseListener() {
                public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) {
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && productDetailsList != null) {
                        for (ProductDetails skuDetails : productDetailsList) {                    
                            mProductDetailsMap.put(skuDetails.getProductId(), skuDetails);                           
                        }
                    }
                   
                }
            }
    );

It makes you build the productList for the productDetailsList for the mProductDetailsMap that's needed to start the purchase flow:

puchasestring=SKU_POTION;
initiatePurchaseFlow(mProductDetailsMap.get(puchasestring));

How would I add multiple products to the productList that begins the implementation? I don't want to have to repeat the entire code segment for each item to add to the mProductDetailsMap, which is the Primitive Pete method I'm using for now.

Uppish answered 26/5, 2022 at 18:48 Comment(0)
U
7

For multiple products:

ImmutableList<QueryProductDetailsParams.Product> productList = ImmutableList.from(
QueryProductDetailsParams.Product.newBuilder()
                    .setProductId(SKU_POTION)
                    .setProductType(BillingClient.ProductType.INAPP)
                    .build(),
QueryProductDetailsParams.Product.newBuilder()
                    .setProductId(SKU_SWORD)
                    .setProductType(BillingClient.ProductType.INAPP)
                    .build(),
QueryProductDetailsParams.Product.newBuilder()
                    .setProductId(SKU_BOW)
                    .setProductType(BillingClient.ProductType.INAPP)
                    .build());
Uppish answered 26/5, 2022 at 20:44 Comment(1)
Why do product types need to be the same? Why I can't query to both in apps and subs with a single queryEndpaper
N
20

You don't actually have to use an ImmutableList. The official examples use an ImmutableList for some reason to build the query, but it isn't necessary. The setProductList method just takes List<Product> as its input, so you could just do something like this:

List<String> skuList = Arrays.asList(SKU_POTION, SKU_SWORD, SKU_BOW);

ArrayList<Product> productList = new ArrayList<>();
for(String sku : skuList) {
    productList.add(
        Product.newBuilder()
            .setProductId(sku)
            .setProductType(BillingClient.ProductType.SUBS)
            .build()
    );
}

QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
        .setProductList(productList)
        .build();

billingClient.queryProductDetailsAsync(params, new ProductDetailsResponseListener() {
    public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) {
        // handle response
    }
}

Anything that implements the List interface will work - ArrayList, ImmutableList, etc...

UPDATE

If you want to query a mix of INAPP and SUBS types, you can't do this with a single call. However, you can use flows to combine the two async calls and run them at the same time (faster than running them one-at-a-time and easier to deal with the results)

First, make a helper function to return a flow for each call to queryProductDetails (this requires the billing -ktx extension in gradle)

private fun getDetailsFlow(productIds: List<String>, type: String) : Flow<List<ProductDetails>> {

    val productList = productIds.map { productId ->
        QueryProductDetailsParams.Product.newBuilder()
            .setProductId(productId)
            .setProductType(type)
            .build()
    }

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

    return flow {
        emit(billingClient?.queryProductDetails(params))
    }.map { result ->
        result?.productDetailsList ?: emptyList()
    }
}

Then zip together two flows to get a single list of products as a result.

fun queryProductDetails() {

    val inAppFlow = getDetailsFlow(listOf(Product.GOLD, Product.SILVER), BillingClient.ProductType.INAPP)
    val subsFlow = getDetailsFlow(listOf(Product.UNLIMITED), BillingClient.ProductType.SUBS)

    val allDetailsFlow = inAppFlow.zip(subsFlow) { inAppResult, subsResult ->
        return@zip inAppResult + subsResult
    }

    scope.launch {
        allDetailsFlow.collect { productList ->
            // handle the unified list of all products, subscription and in-app
        }
    }
}
Neoprene answered 16/7, 2022 at 1:35 Comment(3)
Great answer just please note that BillingClient.ProductType.SUBS is actually BillingClient.ProductType.INAPP if you would like to query in app purchase products and not subscirptions.Duffy
I was facing this same issue. But your answer help me a lot. Great answerRhone
I have a prod app working and I was trying to migrate so, only issue I was facing is trying to query billing client with mix of SUB and IAP. So annoying. Thank you.Lyophilic
U
7

For multiple products:

ImmutableList<QueryProductDetailsParams.Product> productList = ImmutableList.from(
QueryProductDetailsParams.Product.newBuilder()
                    .setProductId(SKU_POTION)
                    .setProductType(BillingClient.ProductType.INAPP)
                    .build(),
QueryProductDetailsParams.Product.newBuilder()
                    .setProductId(SKU_SWORD)
                    .setProductType(BillingClient.ProductType.INAPP)
                    .build(),
QueryProductDetailsParams.Product.newBuilder()
                    .setProductId(SKU_BOW)
                    .setProductType(BillingClient.ProductType.INAPP)
                    .build());
Uppish answered 26/5, 2022 at 20:44 Comment(1)
Why do product types need to be the same? Why I can't query to both in apps and subs with a single queryEndpaper

© 2022 - 2024 — McMap. All rights reserved.