From d2866ae86cd7f1573cdb08834b16a04d6dbe24a1 Mon Sep 17 00:00:00 2001 From: hannanshahidfunsoltech Date: Tue, 31 Oct 2023 11:24:54 +0500 Subject: [PATCH] -billing lib updated to 6.0.1 --- .idea/compiler.xml | 2 +- .idea/deploymentTargetDropDown.xml | 10 + .idea/gradle.xml | 5 +- .idea/kotlinc.xml | 6 + .idea/migrations.xml | 10 + .idea/misc.xml | 2 +- README.md | 21 +- app/build.gradle | 10 +- .../funsolbillinghelper/MainActivity.kt | 17 +- funsol-billing-utils/build.gradle | 10 +- .../funsol/iap/billing/FunSolBillingHelper.kt | 300 ++++++++++++------ 11 files changed, 252 insertions(+), 141 deletions(-) create mode 100644 .idea/deploymentTargetDropDown.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/migrations.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fb7f4a8..b589d56 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..0c0c338 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 4a40d4c..d8a343a 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,10 +4,8 @@ diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..0e65cea --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 360e6d4..0ad17cb 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ - + diff --git a/README.md b/README.md index d2bbd44..5c69d2b 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Add Funsol Billing Helper dependencies in App level build.gradle. ```kotlin dependencies { - implementation 'com.github.Funsol-Projects:Funsol-Billing-Helper:v1.0.3' + implementation 'com.github.Funsol-Projects:Funsol-Billing-Helper:v1.0.4' } ``` @@ -104,24 +104,11 @@ Call this in first stable activity or in App class FunSolBillingHelper(this) .setSubKeys(mutableListOf("Subs Key", "Subs Key 2")) .setInAppKeys(mutableListOf("In-App Key")) - .enableLogging() - - -``` - -##### For both Debug/Release - -```kotlin - - FunSolBillingHelper(this) - .setSubKeys(mutableListOf("Subs Key", "Subs Key 2")) - .setInAppKeys(mutableListOf("In-App Key")) - .enableLogging(isEnableWhileRelease = true) + .enableLogging(isEnableLog = true) ``` - ### Buy In-App Product Subscribe to a Subscription @@ -413,8 +400,8 @@ This Method used for Releasing the client object and save from memory leaks ## License -#### MIT License -#### Copyright (c) 2023 +#### MIT License +#### Copyright (c) 2023 Funsol Technologies Pvt Ltd Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, diff --git a/app/build.gradle b/app/build.gradle index fd12f4c..49dff79 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,12 +5,12 @@ plugins { android { namespace 'com.billing.funsolbillinghelper' - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "com.billing.funsolbillinghelper" minSdk 21 - targetSdk 33 + targetSdk 34 versionCode 1 versionName "1.0" @@ -34,14 +34,14 @@ android { dependencies { - implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.8.0' + implementation 'com.google.android.material:material:1.10.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation project(path: ':funsol-billing-utils') testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' - implementation 'com.android.billingclient:billing:6.0.0' + implementation 'com.android.billingclient:billing:6.0.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' } \ No newline at end of file diff --git a/app/src/main/java/com/billing/funsolbillinghelper/MainActivity.kt b/app/src/main/java/com/billing/funsolbillinghelper/MainActivity.kt index 312ca23..2066545 100644 --- a/app/src/main/java/com/billing/funsolbillinghelper/MainActivity.kt +++ b/app/src/main/java/com/billing/funsolbillinghelper/MainActivity.kt @@ -13,16 +13,17 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - FunSolBillingHelper(this).setSubKeys(mutableListOf("basic")).enableLogging(isEnableWhileRelease = true).setBillingClientListener(object : BillingClientListener { - override fun onClientReady() { - Log.i("billing", "onClientReady: ") - } + FunSolBillingHelper(this).setInAppKeys(mutableListOf("android.test.purchase")).setSubKeys(mutableListOf("basic")).enableLogging(true) + .setBillingClientListener(object : BillingClientListener { + override fun onClientReady() { + Log.i("billing", "onClientReady: ") + } - override fun onClientInitError() { - Log.i("billing", "onClientInitError: ") - } + override fun onClientInitError() { + Log.i("billing", "onClientInitError: ") + } - }) + }) } } \ No newline at end of file diff --git a/funsol-billing-utils/build.gradle b/funsol-billing-utils/build.gradle index ad02744..c472d75 100644 --- a/funsol-billing-utils/build.gradle +++ b/funsol-billing-utils/build.gradle @@ -6,12 +6,12 @@ plugins { } android { - compileSdkVersion 33 + compileSdkVersion 34 buildToolsVersion "33.0.0" defaultConfig { minSdkVersion 21 - targetSdkVersion 33 + targetSdkVersion 34 multiDexEnabled true @@ -39,7 +39,7 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.android.billingclient:billing:6.0.0' + implementation 'com.android.billingclient:billing:6.0.1' } afterEvaluate { @@ -49,13 +49,13 @@ afterEvaluate { from components.release groupId = 'com.github.Funsol-Projects' artifactId = 'Funsol-Billing-Helper' - version = 'v1.0.3' + version = 'v1.0.4' } debug(MavenPublication) { from components.debug groupId = 'com.github.Funsol-Projects' artifactId = 'Funsol-Billing-Helper' - version = 'v1.0.3' + version = 'v1.0.4' } } } diff --git a/funsol-billing-utils/src/main/java/com/funsol/iap/billing/FunSolBillingHelper.kt b/funsol-billing-utils/src/main/java/com/funsol/iap/billing/FunSolBillingHelper.kt index 319d3b7..45af6d8 100644 --- a/funsol-billing-utils/src/main/java/com/funsol/iap/billing/FunSolBillingHelper.kt +++ b/funsol-billing-utils/src/main/java/com/funsol/iap/billing/FunSolBillingHelper.kt @@ -33,7 +33,6 @@ class FunSolBillingHelper(private val context: Context) { private var enableLog = false - private var enableLogWhileRelease = false } init { @@ -53,53 +52,65 @@ class FunSolBillingHelper(private val context: Context) { billingEventListener?.onProductsPurchased(purchasedProductList) } } + BillingClient.BillingResponseCode.USER_CANCELED -> { Log("User pressed back or canceled a dialog." + " Response code: " + billingResult.responseCode) billingEventListener?.onBillingError(ErrorType.USER_CANCELED) } + BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE -> { Log("Network connection is down." + " Response code: " + billingResult.responseCode) billingEventListener?.onBillingError(ErrorType.SERVICE_UNAVAILABLE) } + BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> { Log("Billing API version is not supported for the type requested." + " Response code: " + billingResult.responseCode) billingEventListener?.onBillingError(ErrorType.BILLING_UNAVAILABLE) } + BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> { Log("Requested product is not available for purchase." + " Response code: " + billingResult.responseCode) billingEventListener?.onBillingError(ErrorType.ITEM_UNAVAILABLE) } + BillingClient.BillingResponseCode.DEVELOPER_ERROR -> { Log("Invalid arguments provided to the API." + " Response code: " + billingResult.responseCode) billingEventListener?.onBillingError(ErrorType.DEVELOPER_ERROR) } + BillingClient.BillingResponseCode.ERROR -> { Log("Fatal error during the API action." + " Response code: " + billingResult.responseCode) billingEventListener?.onBillingError(ErrorType.ERROR) } + BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> { Log("Failure to purchase since item is already owned." + " Response code: " + billingResult.responseCode) billingEventListener?.onBillingError(ErrorType.ITEM_ALREADY_OWNED) } + BillingClient.BillingResponseCode.ITEM_NOT_OWNED -> { Log("Failure to consume since item is not owned." + " Response code: " + billingResult.responseCode) billingEventListener?.onBillingError(ErrorType.ITEM_NOT_OWNED) } + BillingClient.BillingResponseCode.SERVICE_DISCONNECTED, BillingClient.BillingResponseCode.SERVICE_TIMEOUT -> { Log("Initialization error: service disconnected/timeout. Trying to reconnect...") billingEventListener?.onBillingError(ErrorType.SERVICE_DISCONNECTED) } + else -> { Log("Initialization error: ") billingEventListener?.onBillingError(ErrorType.ERROR) } } } - billingClient = BillingClient.newBuilder(context).setListener(purchasesUpdatedListener!!).enablePendingPurchases().build() + billingClient = + BillingClient.newBuilder(context).setListener(purchasesUpdatedListener!!) + .enablePendingPurchases().build() startConnection() } else { Log("Client already connected") @@ -114,20 +125,28 @@ class FunSolBillingHelper(private val context: Context) { if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { Log("Connected to Google Play") isClientReady = true - billingClientListener?.onClientReady() fetchAvailableAllSubsProducts(subKeys) fetchAvailableAllInAppProducts(inAppKeys) CoroutineScope(Dispatchers.IO).launch { fetchActivePurchases() } + // callback with Main thread because billing throw it in IO thread + CoroutineScope(Dispatchers.Main).launch { + billingClientListener?.onClientReady() + } + } } override fun onBillingServiceDisconnected() { Log("Fail to connect with Google Play") - billingClientListener?.onClientInitError() isClientReady = false + + // callback with Main thread because billing throw it in IO thread + CoroutineScope(Dispatchers.Main).launch { + billingClientListener?.onClientInitError() + } } }) } @@ -138,9 +157,13 @@ class FunSolBillingHelper(private val context: Context) { productListKeys.forEach { Log("keys List ${productListKeys.size} $it") - productList.add(QueryProductDetailsParams.Product.newBuilder().setProductId(it).setProductType(BillingClient.ProductType.SUBS).build()) + productList.add( + QueryProductDetailsParams.Product.newBuilder().setProductId(it) + .setProductType(BillingClient.ProductType.SUBS).build() + ) } - val queryProductDetailsParams = QueryProductDetailsParams.newBuilder().setProductList(productList).build() + val queryProductDetailsParams = + QueryProductDetailsParams.newBuilder().setProductList(productList).build() if (billingClient != null) { billingClient!!.queryProductDetailsAsync(queryProductDetailsParams) { billingResult, productDetailsList -> @@ -164,10 +187,12 @@ class FunSolBillingHelper(private val context: Context) { if (productInfo != null) { val productDetailsParamsList = ArrayList() if (productInfo.productType == BillingClient.ProductType.SUBS && productInfo.subscriptionOfferDetails != null) { - val offerToken = getOfferToken(productInfo.subscriptionOfferDetails, productId, offerId) + val offerToken = + getOfferToken(productInfo.subscriptionOfferDetails, productId, offerId) if (offerToken.trim { it <= ' ' } != "") { productDetailsParamsList.add( - BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(productInfo).setOfferToken(offerToken).build() + BillingFlowParams.ProductDetailsParams.newBuilder() + .setProductDetails(productInfo).setOfferToken(offerToken).build() ) } else { billingEventListener?.onBillingError(ErrorType.OFFER_NOT_EXIST) @@ -176,10 +201,12 @@ class FunSolBillingHelper(private val context: Context) { } } else { productDetailsParamsList.add( - BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(productInfo).build() + BillingFlowParams.ProductDetailsParams.newBuilder() + .setProductDetails(productInfo).build() ) } - val billingFlowParams = BillingFlowParams.newBuilder().setProductDetailsParamsList(productDetailsParamsList).build() + val billingFlowParams = BillingFlowParams.newBuilder() + .setProductDetailsParamsList(productDetailsParamsList).build() billingClient!!.launchBillingFlow(activity, billingFlowParams) } else { billingEventListener?.onBillingError(ErrorType.PRODUCT_NOT_EXIST) @@ -191,19 +218,31 @@ class FunSolBillingHelper(private val context: Context) { } } - fun upgradeOrDowngradeSubscription(activity: Activity, updateProductId: String, updateOfferId: String, OldProductID: String, policy: Int) { + fun upgradeOrDowngradeSubscription( + activity: Activity, + updateProductId: String, + updateOfferId: String, + OldProductID: String, + policy: Int + ) { if (billingClient != null) { - val productInfo = getProductDetail(updateProductId, updateOfferId, BillingClient.ProductType.SUBS) + val productInfo = + getProductDetail(updateProductId, updateOfferId, BillingClient.ProductType.SUBS) if (productInfo != null) { val oldToken = getOldPurchaseToken(OldProductID) if (oldToken.trim().isNotEmpty()) { - val productDetailsParamsList = ArrayList() + val productDetailsParamsList = + ArrayList() if (productInfo.productType == BillingClient.ProductType.SUBS && productInfo.subscriptionOfferDetails != null) { - val offerToken = getOfferToken(productInfo.subscriptionOfferDetails, updateProductId, updateOfferId) + val offerToken = getOfferToken( + productInfo.subscriptionOfferDetails, updateProductId, updateOfferId + ) if (offerToken.trim { it <= ' ' } != "") { productDetailsParamsList.add( - BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(productInfo).setOfferToken(offerToken).build() + BillingFlowParams.ProductDetailsParams.newBuilder() + .setProductDetails(productInfo).setOfferToken(offerToken) + .build() ) } else { billingEventListener?.onBillingError(ErrorType.OFFER_NOT_EXIST) @@ -212,12 +251,17 @@ class FunSolBillingHelper(private val context: Context) { } } else { productDetailsParamsList.add( - BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(productInfo).build() + BillingFlowParams.ProductDetailsParams.newBuilder() + .setProductDetails(productInfo).build() ) } - val billingFlowParams = BillingFlowParams.newBuilder().setProductDetailsParamsList(productDetailsParamsList).setSubscriptionUpdateParams( - BillingFlowParams.SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldToken).setReplaceProrationMode(policy).build() - ).build() + val billingFlowParams = BillingFlowParams.newBuilder() + .setProductDetailsParamsList(productDetailsParamsList) + .setSubscriptionUpdateParams( + BillingFlowParams.SubscriptionUpdateParams.newBuilder() + .setOldPurchaseToken(oldToken).setReplaceProrationMode(policy) + .build() + ).build() billingClient!!.launchBillingFlow(activity, billingFlowParams) } else { Log("old purchase token not found") @@ -253,7 +297,11 @@ class FunSolBillingHelper(private val context: Context) { return "" } - private fun getOfferToken(offerList: List?, productId: String, offerId: String): String { + private fun getOfferToken( + offerList: List?, + productId: String, + offerId: String + ): String { for (product in offerList!!) { if (product.offerId != null && product.offerId == offerId && product.basePlanId == productId) { return product.offerToken @@ -296,7 +344,8 @@ class FunSolBillingHelper(private val context: Context) { fun areSubscriptionsSupported(): Boolean { return if (billingClient != null) { - val responseCode = billingClient!!.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS) + val responseCode = + billingClient!!.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS) responseCode.responseCode == BillingClient.BillingResponseCode.OK } else { Log("billing client null while check subscription support ") @@ -308,7 +357,8 @@ class FunSolBillingHelper(private val context: Context) { fun unsubscribe(activity: Activity, SubId: String) { try { - val subscriptionUrl = "http://play.google.com/store/account/subscriptions?package=" + activity.packageName + "&sku=" + SubId + val subscriptionUrl = + "http://play.google.com/store/account/subscriptions?package=" + activity.packageName + "&sku=" + SubId val intent = Intent() intent.action = Intent.ACTION_VIEW intent.data = Uri.parse(subscriptionUrl) @@ -328,9 +378,12 @@ class FunSolBillingHelper(private val context: Context) { val productInfo = getProductDetail(productId, "", BillingClient.ProductType.INAPP) if (productInfo != null) { val productDetailsParamsList = listOf( - BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(productInfo).build() + BillingFlowParams.ProductDetailsParams.newBuilder() + .setProductDetails(productInfo).build() ) - val billingFlowParams = BillingFlowParams.newBuilder().setProductDetailsParamsList(productDetailsParamsList).setIsOfferPersonalized(isPersonalizedOffer).build() + val billingFlowParams = BillingFlowParams.newBuilder() + .setProductDetailsParamsList(productDetailsParamsList) + .setIsOfferPersonalized(isPersonalizedOffer).build() billingClient!!.launchBillingFlow(activity, billingFlowParams) Log("Buying IN-APP : $productId") @@ -345,7 +398,8 @@ class FunSolBillingHelper(private val context: Context) { } private suspend fun fetchActiveInAppPurchasesHistory() { - val params = QueryPurchaseHistoryParams.newBuilder().setProductType(BillingClient.ProductType.INAPP) + val params = + QueryPurchaseHistoryParams.newBuilder().setProductType(BillingClient.ProductType.INAPP) billingClient!!.queryPurchaseHistoryAsync(params.build()) { billingResult, purchases -> if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { Log("in-APP History item already buy founded list size ${purchases?.size}") @@ -365,9 +419,13 @@ class FunSolBillingHelper(private val context: Context) { productListKeys.forEach { Log("in-App keys List ${productListKeys.size} $it") - productList.add(QueryProductDetailsParams.Product.newBuilder().setProductId(it).setProductType(BillingClient.ProductType.INAPP).build()) + productList.add( + QueryProductDetailsParams.Product.newBuilder().setProductId(it) + .setProductType(BillingClient.ProductType.INAPP).build() + ) } - val queryProductDetailsParams = QueryProductDetailsParams.newBuilder().setProductList(productList).build() + val queryProductDetailsParams = + QueryProductDetailsParams.newBuilder().setProductList(productList).build() if (billingClient != null) { billingClient!!.queryProductDetailsAsync(queryProductDetailsParams) { billingResult, productDetailsList -> @@ -416,67 +474,90 @@ class FunSolBillingHelper(private val context: Context) { fun getAllProductPrices(): MutableList { val priceList = mutableListOf() - AllProducts.forEach { - if (it.productType == BillingClient.ProductType.INAPP) { - val productPrice = ProductPriceInfo() - productPrice.title = it.title - productPrice.type = it.productType - productPrice.subsKey = it.productId - productPrice.productBasePlanKey = "" - productPrice.productOfferKey = "" - productPrice.price = it.oneTimePurchaseOfferDetails?.formattedPrice.toString() - productPrice.duration = "lifeTime" - priceList.add(productPrice) - } else { - it.subscriptionOfferDetails?.forEach { subIt -> + + // Place try catch because billing internal class throw null pointer some time on ProductType + try { + AllProducts.forEach { + + if (it.productType == BillingClient.ProductType.INAPP) { val productPrice = ProductPriceInfo() productPrice.title = it.title productPrice.type = it.productType productPrice.subsKey = it.productId - productPrice.productBasePlanKey = subIt.basePlanId - productPrice.productOfferKey = subIt.offerId.toString() - productPrice.price = subIt.pricingPhases.pricingPhaseList.first().formattedPrice - productPrice.duration = subIt.pricingPhases.pricingPhaseList.first().billingPeriod + productPrice.productBasePlanKey = "" + productPrice.productOfferKey = "" + productPrice.price = it.oneTimePurchaseOfferDetails?.formattedPrice.toString() + productPrice.duration = "lifeTime" priceList.add(productPrice) - } + } else { + it.subscriptionOfferDetails?.forEach { subIt -> + val productPrice = ProductPriceInfo() + productPrice.title = it.title + productPrice.type = it.productType + productPrice.subsKey = it.productId + productPrice.productBasePlanKey = subIt.basePlanId + productPrice.productOfferKey = subIt.offerId.toString() + productPrice.price = + subIt.pricingPhases.pricingPhaseList.first().formattedPrice + productPrice.duration = + subIt.pricingPhases.pricingPhaseList.first().billingPeriod + priceList.add(productPrice) + } + } } + } catch (e: java.lang.Exception) { + return mutableListOf() + } catch (e: Exception) { + return mutableListOf() } return priceList } fun getProductPriceByKey(BasePlanKey: String, offerKey: String): ProductPriceInfo? { - AllProducts.forEach { - if (it.productType == BillingClient.ProductType.SUBS) { - it.subscriptionOfferDetails?.forEach { subIt -> - if (offerKey.trim().isNotEmpty()) { - if (subIt.basePlanId == BasePlanKey && subIt.offerId == offerKey) { - val productPrice = ProductPriceInfo() - productPrice.title = it.title - productPrice.type = it.productType - productPrice.subsKey = it.productId - productPrice.productBasePlanKey = subIt.basePlanId - productPrice.productOfferKey = subIt.offerId.toString() - productPrice.price = subIt.pricingPhases.pricingPhaseList.first().formattedPrice - productPrice.duration = subIt.pricingPhases.pricingPhaseList.first().billingPeriod - return productPrice - } - } else { - if (subIt.basePlanId == BasePlanKey && subIt.offerId == null) { - val productPrice = ProductPriceInfo() - productPrice.title = it.title - productPrice.type = it.productType - productPrice.subsKey = it.productId - productPrice.productBasePlanKey = subIt.basePlanId - productPrice.productOfferKey = subIt.offerId.toString() - productPrice.price = subIt.pricingPhases.pricingPhaseList.first().formattedPrice - productPrice.duration = subIt.pricingPhases.pricingPhaseList.first().billingPeriod - return productPrice + // Place try catch because billing internal class throw null pointer some time on ProductType + try { + AllProducts.forEach { + if (it.productType == BillingClient.ProductType.SUBS) { + it.subscriptionOfferDetails?.forEach { subIt -> + if (offerKey.trim().isNotEmpty()) { + if (subIt.basePlanId == BasePlanKey && subIt.offerId == offerKey) { + val productPrice = ProductPriceInfo() + productPrice.title = it.title + productPrice.type = it.productType + productPrice.subsKey = it.productId + productPrice.productBasePlanKey = subIt.basePlanId + productPrice.productOfferKey = subIt.offerId.toString() + productPrice.price = + subIt.pricingPhases.pricingPhaseList.first().formattedPrice + productPrice.duration = + subIt.pricingPhases.pricingPhaseList.first().billingPeriod + return productPrice + } + } else { + if (subIt.basePlanId == BasePlanKey && subIt.offerId == null) { + val productPrice = ProductPriceInfo() + productPrice.title = it.title + productPrice.type = it.productType + productPrice.subsKey = it.productId + productPrice.productBasePlanKey = subIt.basePlanId + productPrice.productOfferKey = subIt.offerId.toString() + productPrice.price = + subIt.pricingPhases.pricingPhaseList.first().formattedPrice + productPrice.duration = + subIt.pricingPhases.pricingPhaseList.first().billingPeriod + return productPrice + } } } } + } + } catch (e: java.lang.Exception) { + ///leave blank because below code auto handle this + } catch (e: Exception) { + ///leave blank because below code auto handle this } Log("Product Price not found because product is missing") billingEventListener?.onBillingError(ErrorType.PRODUCT_NOT_EXIST) @@ -484,20 +565,29 @@ class FunSolBillingHelper(private val context: Context) { } fun getProductPriceByKey(productKey: String): ProductPriceInfo? { - AllProducts.forEach { - if (it.productType == BillingClient.ProductType.INAPP) { - if (it.productId == productKey) { - val productPrice = ProductPriceInfo() - productPrice.title = it.title - productPrice.type = it.productType - productPrice.subsKey = it.productId - productPrice.productBasePlanKey = "" - productPrice.productOfferKey = "" - productPrice.price = it.oneTimePurchaseOfferDetails?.formattedPrice.toString() - productPrice.duration = "lifeTime" - return productPrice + // Place try catch because billing internal class throw null pointer some time on ProductType + try { + AllProducts.forEach { + if (it.productType == BillingClient.ProductType.INAPP) { + if (it.productId == productKey) { + val productPrice = ProductPriceInfo() + productPrice.title = it.title + productPrice.type = it.productType + productPrice.subsKey = it.productId + productPrice.productBasePlanKey = "" + productPrice.productOfferKey = "" + productPrice.price = + it.oneTimePurchaseOfferDetails?.formattedPrice.toString() + productPrice.duration = "lifeTime" + return productPrice + } } + } + } catch (e: java.lang.Exception) { + ///leave blank because below code auto handle this + } catch (e: Exception) { + ///leave blank because below code auto handle this } Log("IN-APP Product Price not found because product is missing") billingEventListener?.onBillingError(ErrorType.PRODUCT_NOT_EXIST) @@ -510,13 +600,18 @@ class FunSolBillingHelper(private val context: Context) { if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) { if (!purchase.isAcknowledged) { - val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.purchaseToken) + val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() + .setPurchaseToken(purchase.purchaseToken) billingClient!!.acknowledgePurchase(acknowledgePurchaseParams.build()) { if (it.responseCode == BillingClient.BillingResponseCode.OK) { if (productType.trim().isNotEmpty()) { if (productType == BillingClient.ProductType.INAPP) { Log("IN-APP item buy after acknowledge ") - purchasedInAppProductList.add(PurchaseHistoryRecord(purchase.originalJson, purchase.signature)) + purchasedInAppProductList.add( + PurchaseHistoryRecord( + purchase.originalJson, purchase.signature + ) + ) } else { Log("SUBS item buy after acknowledge ") purchasedProductList.add(purchase) @@ -541,7 +636,9 @@ class FunSolBillingHelper(private val context: Context) { if (consumeAbleKeys.contains(purchase.products.first())) { Log("this purchase is consumable") - val consumeParams = ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken).build() + val consumeParams = + ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken) + .build() billingClient?.consumeAsync(consumeParams) { result, str -> if (result.responseCode == BillingClient.BillingResponseCode.OK) { Log("Purchase consumed") @@ -579,7 +676,10 @@ class FunSolBillingHelper(private val context: Context) { private suspend fun fetchActiveSubsPurchases() { if (billingClient != null) { - billingClient!!.queryPurchasesAsync(QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS).build()) { billingResult: BillingResult, purchases: List -> + billingClient!!.queryPurchasesAsync( + QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS) + .build() + ) { billingResult: BillingResult, purchases: List -> Log("BillingResult $billingResult") @@ -600,7 +700,8 @@ class FunSolBillingHelper(private val context: Context) { } } billingClient!!.queryPurchasesAsync( - QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build() + QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP) + .build() ) { billingResult: BillingResult, purchases: List -> @@ -626,7 +727,9 @@ class FunSolBillingHelper(private val context: Context) { } } - fun getProductDetail(productKey: String, offerKey: String = "", productType: String): ProductDetails? { + fun getProductDetail( + productKey: String, offerKey: String = "", productType: String + ): ProductDetails? { val offerKeyNew = if (offerKey.trim().isNotEmpty()) { offerKey } else { @@ -641,7 +744,10 @@ class FunSolBillingHelper(private val context: Context) { } } else { it.subscriptionOfferDetails?.forEach { subDetails -> - if (subDetails.basePlanId.equals(productKey, true) && subDetails.offerId.toString().equals(offerKeyNew, true)) { + if (subDetails.basePlanId.equals( + productKey, true + ) && subDetails.offerId.toString().equals(offerKeyNew, true) + ) { Log("sub product detail ${subDetails.basePlanId}== $productKey =====> ${subDetails.offerId}==$offerKeyNew") return it } else { @@ -680,22 +786,14 @@ class FunSolBillingHelper(private val context: Context) { return isClientReady } - fun enableLogging(isEnableWhileRelease: Boolean = false): FunSolBillingHelper { - enableLog = true - enableLogWhileRelease = isEnableWhileRelease + fun enableLogging(isEnableLog: Boolean = true): FunSolBillingHelper { + enableLog = isEnableLog return this } private fun Log(debugMessage: String) { - if (enableLog) { - if (enableLogWhileRelease) { - Log.d(TAG, debugMessage) - } else { - if (BuildConfig.DEBUG) { - Log.d(TAG, debugMessage) - } - } + Log.d(TAG, debugMessage) } }