From 8575b3b8c03190196302341389bdd5698881e3b2 Mon Sep 17 00:00:00 2001 From: Max Albright Date: Fri, 30 Aug 2024 14:22:30 -0700 Subject: [PATCH] Automatically query product details with results of querypurchases/querypurchasehistory Summary: In order to query product details, we need to pass in a list of product Ids ([docs](https://developer.android.com/reference/com/android/billingclient/api/QueryProductDetailsParams.Builder)). In order to know what those productIds are, we need to have first queried for purchases or purchase history. Therefore, we need to make the call to queryProductDetailsAsync in the listener of queryPurchasesAsync and queryPurchaseHistoryAsync. Reviewed By: jjiang10 Differential Revision: D60979363 fbshipit-source-id: 13d73a04dfc937658a1f7b60123f182e1421e13f --- ...InAppPurchaseBillingClientWrapperV5Plus.kt | 31 ++++++++++++++++--- ...pPurchaseBillingClientWrapperV5PlusTest.kt | 10 ++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/facebook-core/src/main/java/com/facebook/appevents/iap/InAppPurchaseBillingClientWrapperV5Plus.kt b/facebook-core/src/main/java/com/facebook/appevents/iap/InAppPurchaseBillingClientWrapperV5Plus.kt index 23961c73b3..3163ebed1c 100644 --- a/facebook-core/src/main/java/com/facebook/appevents/iap/InAppPurchaseBillingClientWrapperV5Plus.kt +++ b/facebook-core/src/main/java/com/facebook/appevents/iap/InAppPurchaseBillingClientWrapperV5Plus.kt @@ -270,7 +270,7 @@ private constructor( val listenerObj = Proxy.newProxyInstance( purchasesResponseListenerClazz.classLoader, arrayOf(purchasesResponseListenerClazz), - ListenerWrapper(null) + ListenerWrapper(arrayOf(productType)) ) invokeMethod( billingClientClazz, @@ -288,7 +288,7 @@ private constructor( val listenerObj = Proxy.newProxyInstance( purchaseHistoryResponseListenerClazz.classLoader, arrayOf(purchaseHistoryResponseListenerClazz), - ListenerWrapper(null) + ListenerWrapper(arrayOf(productType)) ) invokeMethod( billingClientClazz, @@ -301,7 +301,7 @@ private constructor( executeServiceRequest(runnableQuery) } - fun queryProductDetailsAsync( + private fun queryProductDetailsAsync( productType: InAppPurchaseUtils.IAPProductType, productIds: List ) { @@ -355,10 +355,15 @@ private constructor( @AutoHandleExceptions private fun onQueryPurchasesResponse(wrapperArgs: Array?, listenerArgs: Array?) { + val productType = wrapperArgs?.get(0) + if (productType == null || productType !is InAppPurchaseUtils.IAPProductType) { + return + } val purchaseList = listenerArgs?.get(1) if (purchaseList == null || purchaseList !is List<*>) { return } + val productIds = mutableListOf() for (purchase in purchaseList) { val purchaseJsonStr = invokeMethod( @@ -369,17 +374,28 @@ private constructor( val purchaseJson = JSONObject(purchaseJsonStr) if (purchaseJson.has(PRODUCT_ID)) { val productId = purchaseJson.getString(PRODUCT_ID) + if (productId !in productDetailsMap) { + productIds.add(productId) + } purchaseDetailsMap[productId] = purchaseJson } } + if (productIds.isNotEmpty()) { + queryProductDetailsAsync(productType, productIds) + } } @AutoHandleExceptions private fun onPurchaseHistoryResponse(wrapperArgs: Array?, listenerArgs: Array?) { + val productType = wrapperArgs?.get(0) + if (productType == null || productType !is InAppPurchaseUtils.IAPProductType) { + return + } val purchaseHistoryRecordList = listenerArgs?.get(1) if (purchaseHistoryRecordList == null || purchaseHistoryRecordList !is List<*>) { return } + val productIds = mutableListOf() for (purchaseHistoryRecord in purchaseHistoryRecordList) { try { val purchaseHistoryRecordJsonStr = invokeMethod( @@ -392,17 +408,24 @@ private constructor( purchaseHistoryRecordJson.put(PACKAGE_NAME, packageName) if (purchaseHistoryRecordJson.has(PRODUCT_ID)) { val productId = purchaseHistoryRecordJson.getString(PRODUCT_ID) + if (productId !in productDetailsMap) { + productIds.add(productId) + } purchaseDetailsMap[productId] = purchaseHistoryRecordJson } } catch (e: Exception) { /* swallow */ } } + if (productIds.isNotEmpty()) { + queryProductDetailsAsync(productType, productIds) + } } @AutoHandleExceptions private fun onProductDetailsResponse(wrapperArgs: Array?, listenerArgs: Array?) { val productDetailsList = listenerArgs?.get(1) + if (productDetailsList == null || productDetailsList !is List<*>) { return } @@ -419,13 +442,13 @@ private constructor( val productId = productDetailJson.getString(PRODUCT_ID) productDetailsMap[productId] = productDetailJson } - } catch (e: Exception) { /* swallow */ } } } + @AutoHandleExceptions private fun onBillingSetupFinished(wrapperArgs: Array?, listenerArgs: Array?) { if (listenerArgs.isNullOrEmpty()) { diff --git a/facebook-core/src/test/kotlin/com/facebook/appevents/iap/InAppPurchaseBillingClientWrapperV5PlusTest.kt b/facebook-core/src/test/kotlin/com/facebook/appevents/iap/InAppPurchaseBillingClientWrapperV5PlusTest.kt index 2125de0000..cbac822147 100644 --- a/facebook-core/src/test/kotlin/com/facebook/appevents/iap/InAppPurchaseBillingClientWrapperV5PlusTest.kt +++ b/facebook-core/src/test/kotlin/com/facebook/appevents/iap/InAppPurchaseBillingClientWrapperV5PlusTest.kt @@ -208,6 +208,8 @@ class InAppPurchaseBillingClientWrapperV5PlusTest : FacebookPowerMockTestCase() val billingResult: Any = mock() val proxy: Any = mock() val purchase: Any = mock() + val productType: Any = InAppPurchaseUtils.IAPProductType.INAPP + val wrapperArgs = arrayOf(productType) val purchaseList: List<*> = listOf(purchase) val args = arrayOf(billingResult, purchaseList) @@ -218,10 +220,11 @@ class InAppPurchaseBillingClientWrapperV5PlusTest : FacebookPowerMockTestCase() eq(purchase), ) ).thenReturn(purchaseJsonStr) + val mockContext: Context = mock() val inAppPurchaseBillingClientWrapperV5Plus = InAppPurchaseBillingClientWrapperV5Plus.getOrCreateInstance(mockContext) - inAppPurchaseBillingClientWrapperV5Plus?.ListenerWrapper(null)?.invoke( + inAppPurchaseBillingClientWrapperV5Plus?.ListenerWrapper(wrapperArgs)?.invoke( proxy, Class.forName(exampleClassName) .getMethod(METHOD_ON_QUERY_PURCHASES_RESPONSE), @@ -236,6 +239,8 @@ class InAppPurchaseBillingClientWrapperV5PlusTest : FacebookPowerMockTestCase() val billingResult: Any = mock() val proxy: Any = mock() val purchaseHistoryRecord: Any = mock() + val productType: Any = InAppPurchaseUtils.IAPProductType.INAPP + val wrapperArgs = arrayOf(productType) val purchaseHistoryRecordList: List<*> = listOf(purchaseHistoryRecord) val args = arrayOf(billingResult, purchaseHistoryRecordList) @@ -246,10 +251,11 @@ class InAppPurchaseBillingClientWrapperV5PlusTest : FacebookPowerMockTestCase() eq(purchaseHistoryRecord), ) ).thenReturn(purchaseHistoryRecordJsonStr) + val mockContext: Context = mock() val inAppPurchaseBillingClientWrapperV5Plus = InAppPurchaseBillingClientWrapperV5Plus.getOrCreateInstance(mockContext) - inAppPurchaseBillingClientWrapperV5Plus?.ListenerWrapper(null)?.invoke( + inAppPurchaseBillingClientWrapperV5Plus?.ListenerWrapper(wrapperArgs)?.invoke( proxy, Class.forName(exampleClassName) .getMethod(METHOD_ON_PURCHASE_HISTORY_RESPONSE),