From 0bebb07424af015f90852e956e42494d18e8eb5e Mon Sep 17 00:00:00 2001 From: cb-amutha Date: Mon, 12 Jun 2023 13:01:30 +0530 Subject: [PATCH 1/5] Adds introductory offer price to validate receipt api --- .../billingservice/BillingClientManager.kt | 11 ++-- .../android/billingservice/CBPurchase.kt | 42 ++++++++++++- .../chargebee/android/models/OfferDetail.kt | 8 +++ .../android/network/CBReceiptRequestBody.kt | 24 ++++++-- .../android/resources/ReceiptResource.kt | 3 +- .../restore/CBRestorePurchaseManager.kt | 61 +++++++++++++------ 6 files changed, 115 insertions(+), 34 deletions(-) create mode 100644 chargebee/src/main/java/com/chargebee/android/models/OfferDetail.kt diff --git a/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt b/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt index c774fc3..e370531 100644 --- a/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt +++ b/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt @@ -309,10 +309,13 @@ class BillingClientManager(context: Context) : PurchasesUpdatedListener { queryPurchaseHistory { purchaseHistoryList -> val storeTransactions = arrayListOf() storeTransactions.addAll(purchaseHistoryList) - CBRestorePurchaseManager.fetchStoreSubscriptionStatus( - storeTransactions, - restorePurchaseCallBack - ) + mContext?.let { + CBRestorePurchaseManager.fetchStoreSubscriptionStatus( + it, + storeTransactions, + restorePurchaseCallBack + ) + } } } else { restorePurchaseCallBack.onError( diff --git a/chargebee/src/main/java/com/chargebee/android/billingservice/CBPurchase.kt b/chargebee/src/main/java/com/chargebee/android/billingservice/CBPurchase.kt index 3e756ff..3aff698 100644 --- a/chargebee/src/main/java/com/chargebee/android/billingservice/CBPurchase.kt +++ b/chargebee/src/main/java/com/chargebee/android/billingservice/CBPurchase.kt @@ -149,7 +149,8 @@ object CBPurchase { completion: (ChargebeeResult) -> Unit ) { try { - validateReceipt(purchaseToken, product.productId, completion) + val offerDetail = getOfferDetail(product) + validateReceipt(purchaseToken, product.productId, offerDetail, completion) } catch (exp: Exception) { Log.e(javaClass.simpleName, "Exception in validateReceipt() :" + exp.message) ChargebeeResult.Error( @@ -165,6 +166,7 @@ object CBPurchase { internal fun validateReceipt( purchaseToken: String, productId: String, + offerDetail: OfferDetail, completion: (ChargebeeResult) -> Unit ) { val logger = CBLogger(name = "buy", action = "process_purchase_command") @@ -172,7 +174,8 @@ object CBPurchase { purchaseToken, productId, customer, - Chargebee.channel + Chargebee.channel, + offerDetail ) ResultHandler.safeExecuter( { ReceiptResource().validateReceipt(params) }, @@ -282,4 +285,39 @@ object CBPurchase { } return billingClientManager as BillingClientManager } + + private fun convertIntroductoryPriceAmountInMicros(product: CBProduct): Long { + return product.skuDetails.introductoryPriceAmountMicros / 1_000_0 + } + + private fun getOfferDetail(product: CBProduct): OfferDetail { + var offerDetail = OfferDetail( introductoryPrice = "", + introductoryPriceAmountMicros = 0, + introductoryPricePeriod = 0, + introductoryOfferType = "") + val introductoryOfferType: String + val numberOfUnits: Int + if (product.skuDetails.introductoryPrice.isNotEmpty()) { + val subscriptionPeriod = product.skuDetails.introductoryPricePeriod + if (product.skuDetails.introductoryPriceCycles == 1) { + introductoryOfferType = "pay_up_front" + numberOfUnits = + subscriptionPeriod.substring(1, subscriptionPeriod.length - 1).toInt() + } else { + introductoryOfferType = "pay_as_you_go" + numberOfUnits = product.skuDetails.introductoryPriceCycles + } + val introductoryPriceAmountMicros = convertIntroductoryPriceAmountInMicros(product) + offerDetail = OfferDetail( + introductoryPrice = product.skuDetails.introductoryPrice, + introductoryPriceAmountMicros = introductoryPriceAmountMicros, + introductoryPricePeriod = numberOfUnits, + introductoryOfferType = introductoryOfferType + ) + return offerDetail + } + return offerDetail + } + + } \ No newline at end of file diff --git a/chargebee/src/main/java/com/chargebee/android/models/OfferDetail.kt b/chargebee/src/main/java/com/chargebee/android/models/OfferDetail.kt new file mode 100644 index 0000000..1eefab5 --- /dev/null +++ b/chargebee/src/main/java/com/chargebee/android/models/OfferDetail.kt @@ -0,0 +1,8 @@ +package com.chargebee.android.models + +data class OfferDetail( + val introductoryPrice: String?, + val introductoryPriceAmountMicros: Long?, + val introductoryPricePeriod: Int?, + val introductoryOfferType: String? +) diff --git a/chargebee/src/main/java/com/chargebee/android/network/CBReceiptRequestBody.kt b/chargebee/src/main/java/com/chargebee/android/network/CBReceiptRequestBody.kt index 470b626..de15581 100644 --- a/chargebee/src/main/java/com/chargebee/android/network/CBReceiptRequestBody.kt +++ b/chargebee/src/main/java/com/chargebee/android/network/CBReceiptRequestBody.kt @@ -1,16 +1,20 @@ package com.chargebee.android.network +import com.chargebee.android.models.OfferDetail + internal class CBReceiptRequestBody( val receipt: String, val productId: String, val customer: CBCustomer?, - val channel: String) { + val channel: String, + val offerDetail: OfferDetail) { companion object { fun fromCBReceiptReqBody(params: Params): CBReceiptRequestBody { return CBReceiptRequestBody( params.receipt, params.productId, params.customer, - params.channel + params.channel, + params.offerDetail ) } } @@ -24,6 +28,7 @@ internal class CBReceiptRequestBody( val receipt: String, ) } + fun toCBReceiptReqCustomerBody(): Map { return mapOf( "receipt" to this.receipt, @@ -32,14 +37,20 @@ internal class CBReceiptRequestBody( val receipt: String, "customer[first_name]" to this.customer?.firstName, "customer[last_name]" to this.customer?.lastName, "customer[email]" to this.customer?.email, - "channel" to this.channel + "channel" to this.channel, + "introductory_offer[price]" to this.offerDetail.introductoryPriceAmountMicros.toString(), + "introductory_offer[type]" to this.offerDetail.introductoryOfferType, + "introductory_offer[period]" to this.offerDetail.introductoryPricePeriod.toString() ) } - fun toMap(): Map { + fun toMap(): Map { return mapOf( "receipt" to this.receipt, "product[id]" to this.productId, - "channel" to this.channel + "channel" to this.channel, + "introductory_offer[price]" to this.offerDetail.introductoryPriceAmountMicros.toString(), + "introductory_offer[type]" to this.offerDetail.introductoryOfferType, + "introductory_offer[period]" to this.offerDetail.introductoryPricePeriod.toString() ) } } @@ -48,7 +59,8 @@ data class Params( val receipt: String, val productId: String, val customer: CBCustomer?, - val channel: String + val channel: String, + val offerDetail: OfferDetail ) data class CBCustomer( val id: String?, diff --git a/chargebee/src/main/java/com/chargebee/android/resources/ReceiptResource.kt b/chargebee/src/main/java/com/chargebee/android/resources/ReceiptResource.kt index 07f4921..fc0e84e 100644 --- a/chargebee/src/main/java/com/chargebee/android/resources/ReceiptResource.kt +++ b/chargebee/src/main/java/com/chargebee/android/resources/ReceiptResource.kt @@ -12,9 +12,8 @@ import com.chargebee.android.responseFromServer internal class ReceiptResource : BaseResource(baseUrl = Chargebee.baseUrl){ internal suspend fun validateReceipt(params: Params): ChargebeeResult { - var dataMap = mapOf() val paramDetail = CBReceiptRequestBody.fromCBReceiptReqBody(params) - dataMap = if (params.customer != null && !(TextUtils.isEmpty(params.customer.id))) { + val dataMap = if (params.customer != null && !(TextUtils.isEmpty(params.customer.id))) { paramDetail.toCBReceiptReqCustomerBody() } else{ paramDetail.toMap() diff --git a/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt b/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt index 1baced3..28f7e80 100644 --- a/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt +++ b/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt @@ -1,5 +1,6 @@ package com.chargebee.android.restore +import android.content.Context import android.util.Log import com.chargebee.android.ErrorDetail import com.chargebee.android.billingservice.CBCallback @@ -54,6 +55,7 @@ class CBRestorePurchaseManager { } internal fun fetchStoreSubscriptionStatus( + context: Context, storeTransactions: ArrayList, completionCallback: CBCallback.RestorePurchaseCallback ) { @@ -68,9 +70,9 @@ class CBRestorePurchaseManager { StoreStatus.Active.value -> activeTransactions.add(storeTransaction) else -> allTransactions.add(storeTransaction) } - getRestorePurchases(storeTransactions) + getRestorePurchases(context, storeTransactions) }, { _ -> - getRestorePurchases(storeTransactions) + getRestorePurchases(context, storeTransactions) }) } } else { @@ -78,7 +80,7 @@ class CBRestorePurchaseManager { } } - internal fun getRestorePurchases(storeTransactions: ArrayList) { + internal fun getRestorePurchases(context: Context, storeTransactions: ArrayList) { if (storeTransactions.isEmpty()) { if (restorePurchases.isEmpty()) { completionCallback.onError( @@ -99,38 +101,57 @@ class CBRestorePurchaseManager { } if (CBPurchase.includeInActivePurchases) { completionCallback.onSuccess(allPurchases) - syncPurchaseWithChargebee(allTransactions) + syncPurchaseWithChargebee(allTransactions, context) } else { completionCallback.onSuccess(activePurchases) - syncPurchaseWithChargebee(activeTransactions) + syncPurchaseWithChargebee(activeTransactions, context) } } restorePurchases.clear() } else { - fetchStoreSubscriptionStatus(storeTransactions, completionCallback) + fetchStoreSubscriptionStatus(context, storeTransactions, completionCallback) } } - internal fun syncPurchaseWithChargebee(storeTransactions: ArrayList) { + internal fun syncPurchaseWithChargebee(storeTransactions: ArrayList, context: Context) { storeTransactions.forEach { productIdList -> - validateReceipt(productIdList.purchaseToken, productIdList.productId.first()) + validateReceipt(productIdList.purchaseToken, productIdList.productId, context) } } - internal fun validateReceipt(purchaseToken: String, productId: String) { - CBPurchase.validateReceipt(purchaseToken, productId) { - when (it) { - is ChargebeeResult.Success -> { - Log.i(javaClass.simpleName, "result : ${it.data}") + internal fun validateReceipt( + purchaseToken: String, + productId: List, + context: Context + ) { + CBPurchase.retrieveProducts( + context, + productId as ArrayList, + object : CBCallback.ListProductsCallback> { + override fun onSuccess(productIDs: ArrayList) { + if (productIDs.size == 0) { + Log.i(javaClass.simpleName, "Product not available") + return + } + CBPurchase.validateReceipt(purchaseToken, productIDs.first()) { + when (it) { + is ChargebeeResult.Success -> { + Log.i(javaClass.simpleName, "result : ${it.data}") + } + is ChargebeeResult.Error -> { + Log.e( + javaClass.simpleName, + "Exception from Server - validateReceipt() : ${it.exp.message}" + ) + } + } + } } - is ChargebeeResult.Error -> { - Log.e( - javaClass.simpleName, - "Exception from Server - validateReceipt() : ${it.exp.message}" - ) + + override fun onError(error: CBException) { + Log.e(javaClass.simpleName, "Error: ${error.message}") } - } - } + }) } } } \ No newline at end of file From e445a1e12f8b58695176594d52ed981dfef626f1 Mon Sep 17 00:00:00 2001 From: cb-amutha Date: Mon, 12 Jun 2023 18:01:40 +0530 Subject: [PATCH 2/5] Updated test case and clear the list on restore purchases --- .../android/restore/CBRestorePurchaseManager.kt | 2 ++ .../billingservice/BillingClientManagerTest.kt | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt b/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt index 28f7e80..83e3703 100644 --- a/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt +++ b/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt @@ -108,6 +108,8 @@ class CBRestorePurchaseManager { } } restorePurchases.clear() + allTransactions.clear() + activeTransactions.clear() } else { fetchStoreSubscriptionStatus(context, storeTransactions, completionCallback) } diff --git a/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt b/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt index 427191d..c47955b 100644 --- a/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt +++ b/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt @@ -14,6 +14,7 @@ import com.chargebee.android.exceptions.CBException import com.chargebee.android.exceptions.CBProductIDResult import com.chargebee.android.exceptions.ChargebeeResult import com.chargebee.android.models.CBProduct +import com.chargebee.android.models.OfferDetail import com.chargebee.android.network.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -51,6 +52,10 @@ class BillingClientManagerTest { private val productIdList = arrayListOf("merchant.pro.android", "merchant.premium.android") private var customer = CBCustomer("test","android","test","test@gmail.com") private var customerId: String = "test" + private val offerDetail = OfferDetail( introductoryPrice = "", + introductoryPriceAmountMicros = 0, + introductoryPricePeriod = 0, + introductoryOfferType = "") @Before @@ -316,7 +321,8 @@ class BillingClientManagerTest { purchaseToken, products.productId, customer, - Chargebee.channel + Chargebee.channel, + offerDetail ) val receiptDetail = ReceiptDetail("subscriptionId","customerId","planId") val response = CBReceiptResponse(receiptDetail) @@ -328,7 +334,7 @@ class BillingClientManagerTest { ) ) verify(ReceiptResource(), times(1)).validateReceipt(params) - verify(CBReceiptRequestBody("receipt","",null,""), times(1)).toCBReceiptReqBody() + verify(CBReceiptRequestBody("receipt","",null,"", offerDetail), times(1)).toCBReceiptReqBody() } } @Test @@ -355,7 +361,8 @@ class BillingClientManagerTest { purchaseToken, products.productId, customer, - Chargebee.channel + Chargebee.channel, + offerDetail ) val exception = CBException(ErrorDetail("Error")) CoroutineScope(Dispatchers.IO).launch { @@ -365,7 +372,7 @@ class BillingClientManagerTest { ) ) verify(ReceiptResource(), times(1)).validateReceipt(params) - verify(CBReceiptRequestBody("receipt","",null,""), times(1)).toCBReceiptReqBody() + verify(CBReceiptRequestBody("receipt","",null,"", offerDetail), times(1)).toCBReceiptReqBody() } } @@ -427,6 +434,7 @@ class BillingClientManagerTest { } lock.await() } + @Test fun test_purchaseProductWithCBCustomer_error(){ val products = CBProduct("","","", skuDetails,true) From efb523e1fd19dfeb84dff5881bdf760b17f01904 Mon Sep 17 00:00:00 2001 From: cb-amutha Date: Tue, 13 Jun 2023 21:16:15 +0530 Subject: [PATCH 3/5] Refactored restore purchase validate receipt functionality --- .../billingservice/BillingClientManager.kt | 66 +++++++- .../restore/CBRestorePurchaseManager.kt | 81 ++-------- .../BillingClientManagerTest.kt | 30 ++-- .../android/restore/RestorePurchaseTest.kt | 141 +++--------------- 4 files changed, 108 insertions(+), 210 deletions(-) diff --git a/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt b/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt index 1245938..d3cac41 100644 --- a/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt +++ b/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt @@ -13,6 +13,7 @@ import com.chargebee.android.models.PurchaseTransaction import com.chargebee.android.exceptions.CBException import com.chargebee.android.exceptions.ChargebeeResult import com.chargebee.android.models.CBProduct +import com.chargebee.android.models.StoreStatus import com.chargebee.android.network.CBReceiptResponse import com.chargebee.android.restore.CBRestorePurchaseManager import kotlin.collections.ArrayList @@ -309,13 +310,7 @@ class BillingClientManager(context: Context) : PurchasesUpdatedListener { queryPurchaseHistory { purchaseHistoryList -> val storeTransactions = arrayListOf() storeTransactions.addAll(purchaseHistoryList) - mContext?.let { - CBRestorePurchaseManager.fetchStoreSubscriptionStatus( - it, - storeTransactions, - restorePurchaseCallBack - ) - } + fetchStoreSubscriptionStatus(storeTransactions) } } else { restorePurchaseCallBack.onError( @@ -324,6 +319,63 @@ class BillingClientManager(context: Context) : PurchasesUpdatedListener { } } + internal fun fetchStoreSubscriptionStatus(storeTransactions: ArrayList){ + CBRestorePurchaseManager.fetchStoreSubscriptionStatus(storeTransactions, result = { restorePurchases -> + val activePurchases = restorePurchases.filter { subscription -> + subscription.storeStatus == StoreStatus.Active.value + } + val allPurchases = restorePurchases.filter { subscription -> + subscription.storeStatus == StoreStatus.Active.value || subscription.storeStatus == StoreStatus.InTrial.value + || subscription.storeStatus == StoreStatus.Cancelled.value || subscription.storeStatus == StoreStatus.Paused.value + } + if (CBPurchase.includeInActivePurchases) { + restorePurchaseCallBack.onSuccess(allPurchases) + syncPurchaseWithChargebee(CBRestorePurchaseManager.allTransactions) + } else { + restorePurchaseCallBack.onSuccess(activePurchases) + syncPurchaseWithChargebee(CBRestorePurchaseManager.activeTransactions) + } + }, error = { + restorePurchaseCallBack.onError(error = it) + }) + } + + internal fun syncPurchaseWithChargebee(storeTransactions: ArrayList) { + storeTransactions.forEach { productIdList -> + retrieveProducts( + CBPurchase.ProductType.SUBS.value, + ArrayList(productIdList.productId), + object : CBCallback.ListProductsCallback> { + override fun onSuccess(productIDs: ArrayList) { + if (productIDs.size == 0) { + Log.i(javaClass.simpleName, "Product not available") + return + } + CBPurchase.validateReceipt( + productIdList.purchaseToken, + productIDs.first() + ) { + when (it) { + is ChargebeeResult.Success -> { + Log.i(javaClass.simpleName, "result : ${it.data}") + } + is ChargebeeResult.Error -> { + Log.e( + javaClass.simpleName, + "Exception from Server - validateReceipt() : ${it.exp.message}" + ) + } + } + } + } + + override fun onError(error: CBException) { + Log.e(javaClass.simpleName, "Error: ${error.message}") + } + }) + } + } + private fun queryPurchaseHistory( storeTransactions: (List) -> Unit ) { diff --git a/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt b/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt index 83e3703..41cef4c 100644 --- a/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt +++ b/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt @@ -16,9 +16,9 @@ import com.chargebee.android.resources.RestorePurchaseResource class CBRestorePurchaseManager { companion object { - private var allTransactions = ArrayList() + internal var allTransactions = ArrayList() private var restorePurchases = ArrayList() - private var activeTransactions = ArrayList() + internal var activeTransactions = ArrayList() private lateinit var completionCallback: CBCallback.RestorePurchaseCallback private fun retrieveStoreSubscription( @@ -55,11 +55,10 @@ class CBRestorePurchaseManager { } internal fun fetchStoreSubscriptionStatus( - context: Context, storeTransactions: ArrayList, - completionCallback: CBCallback.RestorePurchaseCallback + result: (List) -> Unit, + error: (CBException) -> Unit ) { - this.completionCallback = completionCallback if (storeTransactions.isNotEmpty()) { val storeTransaction = storeTransactions.firstOrNull()?.also { storeTransactions.remove(it) } @@ -70,20 +69,24 @@ class CBRestorePurchaseManager { StoreStatus.Active.value -> activeTransactions.add(storeTransaction) else -> allTransactions.add(storeTransaction) } - getRestorePurchases(context, storeTransactions) + getRestorePurchases(storeTransactions, result, error) }, { _ -> - getRestorePurchases(context, storeTransactions) + getRestorePurchases(storeTransactions, result, error) }) } } else { - completionCallback.onSuccess(emptyList()) + result(emptyList()) } } - internal fun getRestorePurchases(context: Context, storeTransactions: ArrayList) { + internal fun getRestorePurchases( + storeTransactions: ArrayList, + result: (List) -> Unit, + error: (CBException) -> Unit + ) { if (storeTransactions.isEmpty()) { if (restorePurchases.isEmpty()) { - completionCallback.onError( + error( CBException( ErrorDetail( message = GPErrorCode.InvalidPurchaseToken.errorMsg, @@ -92,68 +95,14 @@ class CBRestorePurchaseManager { ) ) } else { - val activePurchases = restorePurchases.filter { subscription -> - subscription.storeStatus == StoreStatus.Active.value - } - val allPurchases = restorePurchases.filter { subscription -> - subscription.storeStatus == StoreStatus.Active.value || subscription.storeStatus == StoreStatus.InTrial.value - || subscription.storeStatus == StoreStatus.Cancelled.value || subscription.storeStatus == StoreStatus.Paused.value - } - if (CBPurchase.includeInActivePurchases) { - completionCallback.onSuccess(allPurchases) - syncPurchaseWithChargebee(allTransactions, context) - } else { - completionCallback.onSuccess(activePurchases) - syncPurchaseWithChargebee(activeTransactions, context) - } + result(restorePurchases) } restorePurchases.clear() allTransactions.clear() activeTransactions.clear() } else { - fetchStoreSubscriptionStatus(context, storeTransactions, completionCallback) - } - } - - internal fun syncPurchaseWithChargebee(storeTransactions: ArrayList, context: Context) { - storeTransactions.forEach { productIdList -> - validateReceipt(productIdList.purchaseToken, productIdList.productId, context) + fetchStoreSubscriptionStatus(storeTransactions, result, error) } } - - internal fun validateReceipt( - purchaseToken: String, - productId: List, - context: Context - ) { - CBPurchase.retrieveProducts( - context, - productId as ArrayList, - object : CBCallback.ListProductsCallback> { - override fun onSuccess(productIDs: ArrayList) { - if (productIDs.size == 0) { - Log.i(javaClass.simpleName, "Product not available") - return - } - CBPurchase.validateReceipt(purchaseToken, productIDs.first()) { - when (it) { - is ChargebeeResult.Success -> { - Log.i(javaClass.simpleName, "result : ${it.data}") - } - is ChargebeeResult.Error -> { - Log.e( - javaClass.simpleName, - "Exception from Server - validateReceipt() : ${it.exp.message}" - ) - } - } - } - } - - override fun onError(error: CBException) { - Log.e(javaClass.simpleName, "Error: ${error.message}") - } - }) - } } } \ No newline at end of file diff --git a/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt b/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt index 58d0d44..548efec 100644 --- a/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt +++ b/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt @@ -50,17 +50,19 @@ class BillingClientManagerTest { private var customer = CBCustomer("test", "android", "test", "test@gmail.com") private var customerId: String = "test" private val purchaseToken = "56sadmnagdjsd" + private val offerDetail = OfferDetail( introductoryPrice = "", + introductoryPriceAmountMicros = 0, + introductoryPricePeriod = 0, + introductoryOfferType = "") private val params = Params( "purchaseToken", "product.productId", customer, - Chargebee.channel + Chargebee.channel, + offerDetail ) private val receiptDetail = ReceiptDetail("subscriptionId", "customerId", "planId") - private val offerDetail = OfferDetail( introductoryPrice = "", - introductoryPriceAmountMicros = 0, - introductoryPricePeriod = 0, - introductoryOfferType = "") + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -304,7 +306,7 @@ class BillingClientManagerTest { val purchaseToken = "56sadmnagdjsd" val lock = CountDownLatch(1) CoroutineScope(Dispatchers.IO).launch { - CBPurchase.validateReceipt(purchaseToken, "productId") { + CBPurchase.validateReceipt(purchaseToken, productId = "productId", offerDetail = offerDetail) { when (it) { is ChargebeeResult.Success -> { lock.countDown() @@ -333,7 +335,7 @@ class BillingClientManagerTest { fun test_validateReceipt_error(){ val purchaseToken = "56sadmnagdjsd" CoroutineScope(Dispatchers.IO).launch { - CBPurchase.validateReceipt(purchaseToken, "products") { + CBPurchase.validateReceipt(purchaseToken, productId = "productId", offerDetail = offerDetail){ when (it) { is ChargebeeResult.Success -> { assertThat(it, instanceOf(CBReceiptResponse::class.java)) @@ -447,10 +449,7 @@ class BillingClientManagerTest { fun test_validateReceiptWithChargebee_success() { val response = CBReceiptResponse(receiptDetail) CoroutineScope(Dispatchers.IO).launch { - CBPurchase.validateReceipt( - purchaseToken = "purchaseToken", - productId = "productId" - ) { + CBPurchase.validateReceipt(purchaseToken, productId = "productId", offerDetail = offerDetail) { when (it) { is ChargebeeResult.Success -> { assertThat(it, instanceOf(ReceiptDetail::class.java)) @@ -468,7 +467,7 @@ class BillingClientManagerTest { ) ) verify(ReceiptResource(), times(1)).validateReceipt(params) - verify(CBReceiptRequestBody("receipt", "", null, ""), times(1)).toCBReceiptReqBody() + verify(CBReceiptRequestBody("receipt", "", null, "", offerDetail), times(1)).toCBReceiptReqBody() } } @@ -477,10 +476,7 @@ class BillingClientManagerTest { fun test_validateReceiptWithChargebee_error() { val exception = CBException(ErrorDetail("Error")) CoroutineScope(Dispatchers.IO).launch { - CBPurchase.validateReceipt( - purchaseToken = "purchaseToken", - productId = "productId" - ) { + CBPurchase.validateReceipt(purchaseToken, productId = "productId", offerDetail = offerDetail) { when (it) { is ChargebeeResult.Success -> { assertThat(it, instanceOf(ReceiptDetail::class.java)) @@ -498,7 +494,7 @@ class BillingClientManagerTest { ) ) verify(ReceiptResource(), times(1)).validateReceipt(params) - verify(CBReceiptRequestBody("receipt", "", null, ""), times(1)).toCBReceiptReqBody() + verify(CBReceiptRequestBody("receipt", "", null, "", offerDetail), times(1)).toCBReceiptReqBody() } } } \ No newline at end of file diff --git a/chargebee/src/test/java/com/chargebee/android/restore/RestorePurchaseTest.kt b/chargebee/src/test/java/com/chargebee/android/restore/RestorePurchaseTest.kt index 2111dca..9d66ff1 100644 --- a/chargebee/src/test/java/com/chargebee/android/restore/RestorePurchaseTest.kt +++ b/chargebee/src/test/java/com/chargebee/android/restore/RestorePurchaseTest.kt @@ -67,48 +67,39 @@ class RestorePurchaseTest { fun test_fetchStoreSubscriptionStatus_success() { val lock = CountDownLatch(1) val purchaseTransaction = getTransaction(true) - CBRestorePurchaseManager.fetchStoreSubscriptionStatus( purchaseTransaction, - completionCallback = object : RestorePurchaseCallback { - override fun onSuccess(result: List) { - lock.countDown() - result.forEach { - MatcherAssert.assertThat( - (it), - Matchers.instanceOf(CBRestoreSubscription::class.java) - ) - } - - } - - override fun onError(error: CBException) { - lock.countDown() + result = { restorePurchases -> + lock.countDown() + restorePurchases.forEach { MatcherAssert.assertThat( - error, - Matchers.instanceOf(CBException::class.java) + (it), + Matchers.instanceOf(CBRestoreSubscription::class.java) ) } + }, error = { + lock.countDown() }) + CoroutineScope(Dispatchers.IO).launch { + Mockito.verify(RestorePurchaseResource(), Mockito.times(1)) + .retrieveStoreSubscription(purchaseTransaction.first().purchaseToken) + } lock.await() } @Test fun test_fetchStoreSubscriptionStatus_failure() { val purchaseTransaction = getTransaction(false) - - val storeTransaction = - purchaseTransaction.firstOrNull()?.also { purchaseTransaction.remove(it) } - storeTransaction?.purchaseToken?.let { purchaseToken -> - CBRestorePurchaseManager.retrieveRestoreSubscription(purchaseToken, {}, { error -> - lock.countDown() - MatcherAssert.assertThat( - (error), - Matchers.instanceOf(CBException::class.java) - ) - Mockito.verify(CBRestorePurchaseManager, Mockito.times(1)) - .getRestorePurchases(purchaseTransaction) - }) + CBRestorePurchaseManager.fetchStoreSubscriptionStatus(purchaseTransaction, {}, { error -> + lock.countDown() + MatcherAssert.assertThat( + (error), + Matchers.instanceOf(CBException::class.java) + ) + }) + CoroutineScope(Dispatchers.IO).launch { + Mockito.verify(RestorePurchaseResource(), Mockito.times(1)) + .retrieveStoreSubscription(purchaseTransaction.first().purchaseToken) } lock.await() } @@ -148,96 +139,6 @@ class RestorePurchaseTest { } } - @Test - fun test_validateReceipt_success() { - val purchaseTransaction = getTransaction(true) - val params = Params( - purchaseTransaction.first().purchaseToken, - purchaseTransaction.first().productId.first(), - customer, - Chargebee.channel - ) - CBRestorePurchaseManager.validateReceipt( - params.receipt, - purchaseTransaction.first().productType - ) - CoroutineScope(Dispatchers.IO).launch { - Mockito.`when`(params.let { ReceiptResource().validateReceipt(it) }).thenReturn( - ChargebeeResult.Success( - response - ) - ) - Mockito.verify(ReceiptResource(), Mockito.times(1)).validateReceipt(params) - Mockito.verify(CBReceiptRequestBody("receipt", "", null, ""), Mockito.times(1)) - .toCBReceiptReqBody() - } - } - - @Test - fun test_validateReceipt_failure() { - val purchaseTransaction = getTransaction(false) - val params = Params( - purchaseTransaction.first().purchaseToken, - purchaseTransaction.first().productId.first(), - customer, - Chargebee.channel - ) - CoroutineScope(Dispatchers.IO).launch { - Mockito.`when`(params.let { ReceiptResource().validateReceipt(it) }).thenReturn( - ChargebeeResult.Error( - error - ) - ) - Mockito.verify(ReceiptResource(), Mockito.times(1)).validateReceipt(params) - Mockito.verify(CBReceiptRequestBody("receipt", "", null, ""), Mockito.times(1)) - .toCBReceiptReqBody() - } - } - - @Test - fun test_syncPurchaseWithChargebee_success() { - val purchaseTransaction = getTransaction(false) - val params = Params( - purchaseTransaction.first().purchaseToken, - purchaseTransaction.first().productId.first(), - customer, - Chargebee.channel - ) - CBRestorePurchaseManager.syncPurchaseWithChargebee(purchaseTransaction) - CoroutineScope(Dispatchers.IO).launch { - Mockito.`when`(params.let { ReceiptResource().validateReceipt(it) }).thenReturn( - ChargebeeResult.Success( - response - ) - ) - Mockito.verify(ReceiptResource(), Mockito.times(1)).validateReceipt(params) - Mockito.verify(CBReceiptRequestBody("receipt", "", null, ""), Mockito.times(1)) - .toCBReceiptReqBody() - } - } - - @Test - fun test_syncPurchaseWithChargebee_failure() { - val purchaseTransaction = getTransaction(false) - val params = Params( - purchaseTransaction.first().purchaseToken, - purchaseTransaction.first().productId.first(), - customer, - Chargebee.channel - ) - CBRestorePurchaseManager.syncPurchaseWithChargebee(purchaseTransaction) - CoroutineScope(Dispatchers.IO).launch { - Mockito.`when`(params.let { ReceiptResource().validateReceipt(it) }).thenReturn( - ChargebeeResult.Error( - error - ) - ) - Mockito.verify(ReceiptResource(), Mockito.times(1)).validateReceipt(params) - Mockito.verify(CBReceiptRequestBody("receipt", "", null, ""), Mockito.times(1)) - .toCBReceiptReqBody() - } - } - private fun getTransaction(isTestingSuccess: Boolean): ArrayList { storeTransactions.clear() val result = if (isTestingSuccess) From cc2b0ad0674b1acfb151cfc17af4cf1cffbde983 Mon Sep 17 00:00:00 2001 From: cb-amutha Date: Tue, 13 Jun 2023 21:37:14 +0530 Subject: [PATCH 4/5] Updated unit test case for restore purchase sync with Chargebee --- .../BillingClientManagerTest.kt | 47 +++++++++++++++++++ .../android/billingservice/TestData.kt | 41 ++++++++++++++++ .../android/restore/RestorePurchaseTest.kt | 15 ++---- 3 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 chargebee/src/test/java/com/chargebee/android/billingservice/TestData.kt diff --git a/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt b/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt index 548efec..be19de9 100644 --- a/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt +++ b/chargebee/src/test/java/com/chargebee/android/billingservice/BillingClientManagerTest.kt @@ -497,4 +497,51 @@ class BillingClientManagerTest { verify(CBReceiptRequestBody("receipt", "", null, "", offerDetail), times(1)).toCBReceiptReqBody() } } + + @Test + fun test_syncPurchaseWithChargebee_success() { + val purchaseTransaction = TestData.getTransaction(true) + val params = Params( + purchaseTransaction.first().purchaseToken, + purchaseTransaction.first().productId.first(), + customer, + Chargebee.channel, + offerDetail + ) + billingClientManager?.syncPurchaseWithChargebee(purchaseTransaction) + CoroutineScope(Dispatchers.IO).launch { + Mockito.`when`(params.let { ReceiptResource().validateReceipt(it) }).thenReturn( + ChargebeeResult.Success( + TestData.response + ) + ) + Mockito.verify(ReceiptResource(), Mockito.times(1)).validateReceipt(params) + Mockito.verify(CBReceiptRequestBody("receipt", "", null, "", offerDetail), Mockito.times(1)) + .toCBReceiptReqBody() + } + } + + @Test + fun test_syncPurchaseWithChargebee_failure() { + val purchaseTransaction = TestData.getTransaction(false) + val params = Params( + purchaseTransaction.first().purchaseToken, + purchaseTransaction.first().productId.first(), + customer, + Chargebee.channel, + offerDetail + ) + billingClientManager?.syncPurchaseWithChargebee(purchaseTransaction) + CoroutineScope(Dispatchers.IO).launch { + Mockito.`when`(params.let { ReceiptResource().validateReceipt(it) }).thenReturn( + ChargebeeResult.Error( + TestData.error + ) + ) + Mockito.verify(ReceiptResource(), Mockito.times(1)).validateReceipt(params) + Mockito.verify(CBReceiptRequestBody("receipt", "", null, "", offerDetail), Mockito.times(1)) + .toCBReceiptReqBody() + } + } + } \ No newline at end of file diff --git a/chargebee/src/test/java/com/chargebee/android/billingservice/TestData.kt b/chargebee/src/test/java/com/chargebee/android/billingservice/TestData.kt new file mode 100644 index 0000000..3b5feb7 --- /dev/null +++ b/chargebee/src/test/java/com/chargebee/android/billingservice/TestData.kt @@ -0,0 +1,41 @@ +package com.chargebee.android.billingservice + +import com.chargebee.android.ErrorDetail +import com.chargebee.android.exceptions.CBException +import com.chargebee.android.models.PurchaseTransaction +import com.chargebee.android.network.CBReceiptResponse +import com.chargebee.android.network.ReceiptDetail + +object TestData { + private val list = ArrayList() + private val storeTransactions = arrayListOf() + internal val response = + CBReceiptResponse(ReceiptDetail("subscriptionId", "customerId", "planId")) + internal val error = CBException( + ErrorDetail( + message = "The Token data sent is not correct or Google service is temporarily down", + httpStatusCode = 400 + ) + ) + + fun getTransaction(isTestingSuccess: Boolean): ArrayList { + list.add("chargebee.pro.android") + storeTransactions.clear() + val result = if (isTestingSuccess) + PurchaseTransaction( + productId = list.toList(), + purchaseTime = 1682666112774, + purchaseToken = "fajeooclbamgohgapjeehghm.AO-J1OzxVvoEx7y53c9DsypEKwgcfGw2OrisyQsQ-MG6KiXfJ97nT33Yd5VpbQYxd225QnTAEVdPuLP4YSvZE6LBhsv1rzSlizuBxBTjBWghWguSBBtgp2g", + productType = "subs" + ) + else + PurchaseTransaction( + productId = list.toList(), + purchaseTime = 1682666112774, + purchaseToken = "test data", + productType = "subs" + ) + storeTransactions.add(result) + return storeTransactions + } +} \ No newline at end of file diff --git a/chargebee/src/test/java/com/chargebee/android/restore/RestorePurchaseTest.kt b/chargebee/src/test/java/com/chargebee/android/restore/RestorePurchaseTest.kt index 9d66ff1..1c21488 100644 --- a/chargebee/src/test/java/com/chargebee/android/restore/RestorePurchaseTest.kt +++ b/chargebee/src/test/java/com/chargebee/android/restore/RestorePurchaseTest.kt @@ -2,13 +2,11 @@ package com.chargebee.android.restore import com.android.billingclient.api.* import com.chargebee.android.Chargebee -import com.chargebee.android.ErrorDetail -import com.chargebee.android.billingservice.CBCallback.RestorePurchaseCallback +import com.chargebee.android.billingservice.TestData import com.chargebee.android.exceptions.CBException import com.chargebee.android.exceptions.ChargebeeResult import com.chargebee.android.models.* import com.chargebee.android.network.* -import com.chargebee.android.resources.ReceiptResource import com.chargebee.android.resources.RestorePurchaseResource import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -33,14 +31,7 @@ class RestorePurchaseTest { private val list = ArrayList() private val storeTransactions = arrayListOf() private val lock = CountDownLatch(1) - private val response = - CBReceiptResponse(ReceiptDetail("subscriptionId", "customerId", "planId")) - private val error = CBException( - ErrorDetail( - message = "The Token data sent is not correct or Google service is temporarily down", - httpStatusCode = 400 - ) - ) + @Before fun setUp() { @@ -131,7 +122,7 @@ class RestorePurchaseTest { Mockito.`when`(RestorePurchaseResource().retrieveStoreSubscription(purchaseToken)) .thenReturn( ChargebeeResult.Error( - error + TestData.error ) ) Mockito.verify(RestorePurchaseResource(), Mockito.times(1)) From 7a8718279a528ad74cc5fac1c08f8c32e0c72f52 Mon Sep 17 00:00:00 2001 From: cb-amutha Date: Wed, 21 Jun 2023 19:52:22 +0530 Subject: [PATCH 5/5] Review comments addressed --- .../com/chargebee/example/MainActivity.kt | 39 +++++-------------- .../example/billing/BillingViewModel.kt | 18 +++++++++ .../billingservice/BillingClientManager.kt | 8 +--- .../android/billingservice/CBPurchase.kt | 22 ++++------- .../chargebee/android/models/OfferDetail.kt | 13 +++++-- .../android/network/CBReceiptRequestBody.kt | 30 +++++++------- .../restore/CBRestorePurchaseManager.kt | 5 ++- 7 files changed, 68 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/com/chargebee/example/MainActivity.kt b/app/src/main/java/com/chargebee/example/MainActivity.kt index d98d565..1987cfd 100644 --- a/app/src/main/java/com/chargebee/example/MainActivity.kt +++ b/app/src/main/java/com/chargebee/example/MainActivity.kt @@ -73,6 +73,15 @@ class MainActivity : BaseActivity(), ListItemsAdapter.ItemClickListener { Log.i(javaClass.simpleName, "Google play product identifiers: $it") alertListProductId(it) } + + this.mBillingViewModel!!.restorePurchaseResult.observeForever { + hideProgressDialog() + if (it.isNotEmpty()) { + alertSuccess("${it.size} purchases restored successfully") + } else { + alertSuccess("Purchases not found to restore") + } + } } private fun setListAdapter() { @@ -133,7 +142,7 @@ class MainActivity : BaseActivity(), ListItemsAdapter.ItemClickListener { getSubscriptionId() } CBMenu.RestorePurchase.value -> { - restorePurchases() + mBillingViewModel?.restorePurchases(this) } else -> { Log.i(javaClass.simpleName, " Not implemented") @@ -208,34 +217,6 @@ class MainActivity : BaseActivity(), ListItemsAdapter.ItemClickListener { }) } - private fun restorePurchases() { - showProgressDialog() - CBPurchase.restorePurchases( - context = this, includeInActivePurchases = true, - completionCallback = object : CBCallback.RestorePurchaseCallback { - override fun onSuccess(result: List) { - hideProgressDialog() - result.forEach { - Log.i(javaClass.simpleName, "status : ${it.storeStatus}") - } - CoroutineScope(Dispatchers.Main).launch { - if (result.isNotEmpty()) - alertSuccess("${result.size} purchases restored successfully") - else - alertSuccess("Purchases not found to restore") - } - } - - override fun onError(error: CBException) { - hideProgressDialog() - Log.e(javaClass.simpleName, "error message: ${error.message}") - CoroutineScope(Dispatchers.Main).launch { - showDialog("${error.message}, ${error.httpStatusCode}") - } - } - }) - } - private fun alertListProductId(list: Array) { val builder = AlertDialog.Builder(this) builder.setTitle("Chargebee Product IDs") diff --git a/app/src/main/java/com/chargebee/example/billing/BillingViewModel.kt b/app/src/main/java/com/chargebee/example/billing/BillingViewModel.kt index 932d568..a5678b8 100644 --- a/app/src/main/java/com/chargebee/example/billing/BillingViewModel.kt +++ b/app/src/main/java/com/chargebee/example/billing/BillingViewModel.kt @@ -28,6 +28,7 @@ class BillingViewModel : ViewModel() { var entitlementsResult: MutableLiveData = MutableLiveData() private var subscriptionId: String = "" private lateinit var sharedPreference : SharedPreferences + var restorePurchaseResult: MutableLiveData> = MutableLiveData() fun purchaseProduct(context: Context,product: CBProduct, customer: CBCustomer) { // Cache the product id in sharedPreferences and retry validating the receipt if in case server is not responding or no internet connection. @@ -181,4 +182,21 @@ class BillingViewModel : ViewModel() { editor.putString("productId", productId) editor.apply() } + + fun restorePurchases(context: Context, includeInActivePurchases: Boolean = false) { + CBPurchase.restorePurchases( + context = context, includeInActivePurchases = includeInActivePurchases, + completionCallback = object : CBCallback.RestorePurchaseCallback { + override fun onSuccess(result: List) { + result.forEach { + Log.i(javaClass.simpleName, "status : ${it.storeStatus}") + } + restorePurchaseResult.postValue(result) + } + + override fun onError(error: CBException) { + cbException.postValue(error) + } + }) + } } \ No newline at end of file diff --git a/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt b/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt index d3cac41..919f23e 100644 --- a/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt +++ b/chargebee/src/main/java/com/chargebee/android/billingservice/BillingClientManager.kt @@ -324,16 +324,12 @@ class BillingClientManager(context: Context) : PurchasesUpdatedListener { val activePurchases = restorePurchases.filter { subscription -> subscription.storeStatus == StoreStatus.Active.value } - val allPurchases = restorePurchases.filter { subscription -> - subscription.storeStatus == StoreStatus.Active.value || subscription.storeStatus == StoreStatus.InTrial.value - || subscription.storeStatus == StoreStatus.Cancelled.value || subscription.storeStatus == StoreStatus.Paused.value - } if (CBPurchase.includeInActivePurchases) { - restorePurchaseCallBack.onSuccess(allPurchases) + restorePurchaseCallBack.onSuccess(restorePurchases) syncPurchaseWithChargebee(CBRestorePurchaseManager.allTransactions) } else { restorePurchaseCallBack.onSuccess(activePurchases) - syncPurchaseWithChargebee(CBRestorePurchaseManager.activeTransactions) + syncPurchaseWithChargebee(CBRestorePurchaseManager.activeTransactions) } }, error = { restorePurchaseCallBack.onError(error = it) diff --git a/chargebee/src/main/java/com/chargebee/android/billingservice/CBPurchase.kt b/chargebee/src/main/java/com/chargebee/android/billingservice/CBPurchase.kt index 71d1784..ff1401e 100644 --- a/chargebee/src/main/java/com/chargebee/android/billingservice/CBPurchase.kt +++ b/chargebee/src/main/java/com/chargebee/android/billingservice/CBPurchase.kt @@ -19,6 +19,9 @@ object CBPurchase { val productIdList = arrayListOf() private var customer: CBCustomer? = null internal var includeInActivePurchases = false + private var offerDetail: OfferDetail? = null + private lateinit var introductoryOfferType: OfferType + private var numberOfUnits: Int = 0 internal enum class ProductType(val value: String) { SUBS("subs"), @@ -185,7 +188,7 @@ object CBPurchase { internal fun validateReceipt( purchaseToken: String, productId: String, - offerDetail: OfferDetail, + offerDetail: OfferDetail?, completion: (ChargebeeResult) -> Unit ) { val logger = CBLogger(name = "buy", action = "process_purchase_command") @@ -309,34 +312,25 @@ object CBPurchase { return product.skuDetails.introductoryPriceAmountMicros / 1_000_0 } - private fun getOfferDetail(product: CBProduct): OfferDetail { - var offerDetail = OfferDetail( introductoryPrice = "", - introductoryPriceAmountMicros = 0, - introductoryPricePeriod = 0, - introductoryOfferType = "") - val introductoryOfferType: String - val numberOfUnits: Int + private fun getOfferDetail(product: CBProduct): OfferDetail? { if (product.skuDetails.introductoryPrice.isNotEmpty()) { val subscriptionPeriod = product.skuDetails.introductoryPricePeriod + val introductoryPriceAmountMicros = convertIntroductoryPriceAmountInMicros(product) if (product.skuDetails.introductoryPriceCycles == 1) { - introductoryOfferType = "pay_up_front" + introductoryOfferType = OfferType.PAY_UP_FRONT numberOfUnits = subscriptionPeriod.substring(1, subscriptionPeriod.length - 1).toInt() } else { - introductoryOfferType = "pay_as_you_go" + introductoryOfferType = OfferType.PAY_AS_YOU_GO numberOfUnits = product.skuDetails.introductoryPriceCycles } - val introductoryPriceAmountMicros = convertIntroductoryPriceAmountInMicros(product) offerDetail = OfferDetail( introductoryPrice = product.skuDetails.introductoryPrice, introductoryPriceAmountMicros = introductoryPriceAmountMicros, introductoryPricePeriod = numberOfUnits, introductoryOfferType = introductoryOfferType ) - return offerDetail } return offerDetail } - - } \ No newline at end of file diff --git a/chargebee/src/main/java/com/chargebee/android/models/OfferDetail.kt b/chargebee/src/main/java/com/chargebee/android/models/OfferDetail.kt index 1eefab5..12a1d8c 100644 --- a/chargebee/src/main/java/com/chargebee/android/models/OfferDetail.kt +++ b/chargebee/src/main/java/com/chargebee/android/models/OfferDetail.kt @@ -1,8 +1,13 @@ package com.chargebee.android.models data class OfferDetail( - val introductoryPrice: String?, - val introductoryPriceAmountMicros: Long?, - val introductoryPricePeriod: Int?, - val introductoryOfferType: String? + val introductoryPrice: String, + val introductoryPriceAmountMicros: Long, + val introductoryPricePeriod: Int, + val introductoryOfferType: OfferType ) + +enum class OfferType(val value: String) { + PAY_UP_FRONT("pay_up_front"), + PAY_AS_YOU_GO("pay_as_you_go"); +} diff --git a/chargebee/src/main/java/com/chargebee/android/network/CBReceiptRequestBody.kt b/chargebee/src/main/java/com/chargebee/android/network/CBReceiptRequestBody.kt index de15581..b5f57bd 100644 --- a/chargebee/src/main/java/com/chargebee/android/network/CBReceiptRequestBody.kt +++ b/chargebee/src/main/java/com/chargebee/android/network/CBReceiptRequestBody.kt @@ -6,7 +6,7 @@ internal class CBReceiptRequestBody( val receipt: String, val productId: String, val customer: CBCustomer?, val channel: String, - val offerDetail: OfferDetail) { + val offerDetail: OfferDetail?) { companion object { fun fromCBReceiptReqBody(params: Params): CBReceiptRequestBody { return CBReceiptRequestBody( @@ -28,30 +28,34 @@ internal class CBReceiptRequestBody( val receipt: String, ) } - fun toCBReceiptReqCustomerBody(): Map { - return mapOf( + val queryMap = mutableMapOf( "receipt" to this.receipt, "product[id]" to this.productId, "customer[id]" to this.customer?.id, "customer[first_name]" to this.customer?.firstName, "customer[last_name]" to this.customer?.lastName, "customer[email]" to this.customer?.email, - "channel" to this.channel, - "introductory_offer[price]" to this.offerDetail.introductoryPriceAmountMicros.toString(), - "introductory_offer[type]" to this.offerDetail.introductoryOfferType, - "introductory_offer[period]" to this.offerDetail.introductoryPricePeriod.toString() + "channel" to this.channel ) + return getOfferParams(queryMap) } fun toMap(): Map { - return mapOf( + val queryMap = mutableMapOf( "receipt" to this.receipt, "product[id]" to this.productId, - "channel" to this.channel, - "introductory_offer[price]" to this.offerDetail.introductoryPriceAmountMicros.toString(), - "introductory_offer[type]" to this.offerDetail.introductoryOfferType, - "introductory_offer[period]" to this.offerDetail.introductoryPricePeriod.toString() + "channel" to this.channel ) + return getOfferParams(queryMap) + } + + private fun getOfferParams(queryMap: MutableMap) : Map{ + if (offerDetail != null) { + queryMap["introductory_offer[price]"] = this.offerDetail.introductoryPriceAmountMicros.toString() + queryMap["introductory_offer[type]"] = this.offerDetail.introductoryOfferType.value + queryMap["introductory_offer[period]"] = this.offerDetail.introductoryPricePeriod.toString() + } + return queryMap } } @@ -60,7 +64,7 @@ data class Params( val productId: String, val customer: CBCustomer?, val channel: String, - val offerDetail: OfferDetail + val offerDetail: OfferDetail? ) data class CBCustomer( val id: String?, diff --git a/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt b/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt index 41cef4c..a392249 100644 --- a/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt +++ b/chargebee/src/main/java/com/chargebee/android/restore/CBRestorePurchaseManager.kt @@ -66,7 +66,10 @@ class CBRestorePurchaseManager { retrieveRestoreSubscription(purchaseToken, { restorePurchases.add(it) when (it.storeStatus) { - StoreStatus.Active.value -> activeTransactions.add(storeTransaction) + StoreStatus.Active.value -> { + activeTransactions.add(storeTransaction) + allTransactions.add(storeTransaction) + } else -> allTransactions.add(storeTransaction) } getRestorePurchases(storeTransactions, result, error)