diff --git a/README.md b/README.md index 355cd85..f35fd86 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,29 @@ CBPurchase.purchaseProduct(purchaseProductParams = purchaseProductParams, custom ``` The above function will handle the purchase against Google Play Store and send the IAP token for server-side token verification to your Chargebee account. Use the Subscription ID returned by the above function, to check for Subscription status on Chargebee and confirm the access - granted or denied. +### Upgrade or Downgrade Product +Pass the `ChangeProductParams` and `CBCustomer` to the following `ChangeProduct` function when the user chooses the product to purchase. + +`CBCustomer` - **Optional object**. Although this is an optional object, we recommend passing the necessary customer details, such as `customerId`, `firstName`, `lastName`, and `email`. This ensures that the customer details in your database match the customer details in Chargebee. If the `customerId` is not passed in the customer's details, then the value of `customerId` will be the same as the `SubscriptionId` created in Chargebee. + +**Note**: The `customer` parameter in the below code snippet is an instance of `CBCustomer` class that contains the details of the customer who wants to subscribe or buy the product. + +```kotlin +val changeProductParams = ChangeProductParams(newProductParams, "currentProductId") +val cbCustomer = CBCustomer("customerId","firstName","lastName","email") +CBPurchase.changeProduct(changeProductParams = changeProductParams, customer = cbCustomer, object : CBCallback.PurchaseCallback{ + override fun onSuccess(result: ReceiptDetail, status:Boolean) { + Log.i(TAG, "$status") + Log.i(TAG, "${result.subscription_id}") + Log.i(TAG, "${result.plan_id}") + } + override fun onError(error: CBException) { + Log.e(TAG, "Error: ${error.message}") + } +}) + ``` +The above function is designed to manage subscription upgrades or downgrades between base plans within a single subscription or across different subscriptions. + ### Invoke Manage Subscriptions in your App The `showManageSubscriptionsSettings()` function is designed to invoke the Manage Subscriptions in your app using Chargebee's Android SDKs. `Chargebee.showManageSubscriptionsSettings()`, opens the Play Store App subscriptions settings page. diff --git a/app/src/main/java/com/chargebee/example/MainActivity.kt b/app/src/main/java/com/chargebee/example/MainActivity.kt index a16cc99..5b08607 100644 --- a/app/src/main/java/com/chargebee/example/MainActivity.kt +++ b/app/src/main/java/com/chargebee/example/MainActivity.kt @@ -30,6 +30,7 @@ import com.chargebee.example.plan.PlansActivity import com.chargebee.example.subscription.SubscriptionActivity import com.chargebee.example.token.TokenizeActivity import com.chargebee.example.util.CBMenu +import com.chargebee.example.util.Constants.OLD_PRODUCT_ID import com.chargebee.example.util.Constants.PRODUCTS_LIST_KEY import com.google.gson.Gson import kotlinx.coroutines.CoroutineScope @@ -133,6 +134,9 @@ class MainActivity : BaseActivity(), ListItemsAdapter.ItemClickListener { CBMenu.GetProducts.value -> { getProductIdFromCustomer() } + CBMenu.ChangeProducts.value -> { + getCurrentAndNewProductIdFromCustomer() + } CBMenu.SubsStatus.value, CBMenu.SubsList.value -> { val intent = Intent(this, SubscriptionActivity::class.java) @@ -152,9 +156,10 @@ class MainActivity : BaseActivity(), ListItemsAdapter.ItemClickListener { } } - private fun launchProductDetailsScreen(productDetails: String) { + private fun launchProductDetailsScreen(productDetails: String, currentProductId: String? = null) { val intent = Intent(this, BillingActivity::class.java) intent.putExtra(PRODUCTS_LIST_KEY, productDetails) + intent.putExtra(OLD_PRODUCT_ID, currentProductId) this.startActivity(intent) } @@ -206,7 +211,25 @@ class MainActivity : BaseActivity(), ListItemsAdapter.ItemClickListener { dialog.show() } - private fun getProductIdList(productIdList: ArrayList) { + private fun getCurrentAndNewProductIdFromCustomer() { + val dialog = Dialog(this) + dialog.setContentView(R.layout.dialog_input_update_layout) + val productIds = dialog.findViewById(R.id.productIdInput) as EditText + productIds.hint = "Please enter Product IDs(Comma separated)" + val currentProductId = dialog.findViewById(R.id.currentProductIdInput) as EditText + currentProductId.hint = "Please enter Current Product ID" + val dialogButton = dialog.findViewById(R.id.btn_ok) as Button + dialogButton.text = "Submit" + dialogButton.setOnClickListener { + val productIdList = productIds.text.toString().trim().split(",") + val currentProductId = currentProductId.text.toString().trim() + getProductIdList(productIdList.toCollection(ArrayList()), currentProductId) + dialog.dismiss() + } + dialog.show() + } + + private fun getProductIdList(productIdList: ArrayList, currentProductId: String? = null) { CBPurchase.retrieveProducts( this, productIdList, @@ -214,7 +237,7 @@ class MainActivity : BaseActivity(), ListItemsAdapter.ItemClickListener { override fun onSuccess(productIDs: ArrayList) { CoroutineScope(Dispatchers.Main).launch { if (productIDs.size > 0) { - launchProductDetailsScreen(gson.toJson(productIDs)) + launchProductDetailsScreen(gson.toJson(productIDs), currentProductId) } else { alertSuccess("Items not available to buy") } diff --git a/app/src/main/java/com/chargebee/example/billing/BillingActivity.java b/app/src/main/java/com/chargebee/example/billing/BillingActivity.java index 231ffa4..1879723 100644 --- a/app/src/main/java/com/chargebee/example/billing/BillingActivity.java +++ b/app/src/main/java/com/chargebee/example/billing/BillingActivity.java @@ -1,5 +1,6 @@ package com.chargebee.example.billing; +import static com.chargebee.example.util.Constants.OLD_PRODUCT_ID; import static com.chargebee.example.util.Constants.PRODUCTS_LIST_KEY; import android.app.Dialog; @@ -17,6 +18,7 @@ import com.chargebee.android.billingservice.OneTimeProductType; import com.chargebee.android.billingservice.ProductType; import com.chargebee.android.models.CBProduct; +import com.chargebee.android.models.ChangeProductParams; import com.chargebee.android.models.PurchaseProductParams; import com.chargebee.android.network.CBCustomer; import com.chargebee.example.BaseActivity; @@ -35,6 +37,7 @@ public class BillingActivity extends BaseActivity implements ProductListAdapter.ProductClickListener, ProgressBarListener { private List purchaseProducts = null; + private String currentProductId = null; private ProductListAdapter productListAdapter = null; private LinearLayoutManager linearLayoutManager; private RecyclerView mItemsRecyclerView = null; @@ -53,6 +56,7 @@ protected void onCreate(Bundle savedInstanceState) { mItemsRecyclerView = findViewById(R.id.rv_product_list); String productDetails = getIntent().getStringExtra(PRODUCTS_LIST_KEY); + this.currentProductId = getIntent().getStringExtra(OLD_PRODUCT_ID); if(productDetails != null) { Gson gson = new Gson(); @@ -163,7 +167,11 @@ private void getCustomerID() { dialog.dismiss(); } } else { - purchaseProduct(); + if (this.currentProductId != null){ + changeProduct(); + }else { + purchaseProduct(); + } dialog.dismiss(); } }); @@ -189,6 +197,14 @@ private void purchaseProduct() { this.billingViewModel.purchaseProduct(this, purchaseParams, cbCustomer); } + private void changeProduct() { + showProgressDialog(); + PurchaseProduct selectedPurchaseProduct = purchaseProducts.get(position); + PurchaseProductParams newProductParams = new PurchaseProductParams(selectedPurchaseProduct.getCbProduct(), selectedPurchaseProduct.getOfferToken()); + ChangeProductParams changeProductParams = new ChangeProductParams(newProductParams, currentProductId); + this.billingViewModel.changeProduct(this, changeProductParams, cbCustomer); + } + private void purchaseNonSubscriptionProduct(OneTimeProductType productType) { showProgressDialog(); CBProduct selectedProduct = purchaseProducts.get(position).getCbProduct(); 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 1fdf2ee..2e60c84 100644 --- a/app/src/main/java/com/chargebee/example/billing/BillingViewModel.kt +++ b/app/src/main/java/com/chargebee/example/billing/BillingViewModel.kt @@ -53,6 +53,31 @@ class BillingViewModel : ViewModel() { }) } + fun changeProduct(context: Context, changeProductParams: ChangeProductParams, customer: CBCustomer) { + // Cache the product id in sharedPreferences and retry validating the receipt if in case server is not responding or no internet connection. + sharedPreference = context.getSharedPreferences("PREFERENCE_NAME",Context.MODE_PRIVATE) + CBPurchase.changeProduct(changeProductParams = changeProductParams, customer = customer, object : CBCallback.PurchaseCallback{ + override fun onSuccess(result: ReceiptDetail, status:Boolean) { + Log.i(TAG, "Subscription ID: ${result.subscription_id}") + Log.i(TAG, "Plan ID: ${result.plan_id}") + productPurchaseResult.postValue(status) + } + override fun onError(error: CBException) { + try { + // Handled server not responding and offline + if (error.httpStatusCode!! in 500..599) { + storeInLocal(changeProductParams.newProductParams.product.id) + validateReceipt(context = context, product = changeProductParams.newProductParams.product) + } else { + cbException.postValue(error) + } + } catch (exp: Exception) { + Log.i(TAG, "Exception :${exp.message}") + } + } + }) + } + private fun validateReceipt(context: Context, product: CBProduct) { val customer = CBCustomer( id = "sync_receipt_android", diff --git a/app/src/main/java/com/chargebee/example/util/CBMenu.kt b/app/src/main/java/com/chargebee/example/util/CBMenu.kt index 777dc73..d372e3d 100644 --- a/app/src/main/java/com/chargebee/example/util/CBMenu.kt +++ b/app/src/main/java/com/chargebee/example/util/CBMenu.kt @@ -10,6 +10,7 @@ enum class CBMenu(val value: String) { Tokenize("Tokenize"), ProductIDs("Get Google Play Product Identifiers"), GetProducts("Get Products"), + ChangeProducts("Change Product"), SubsStatus("Get Subscription Status"), SubsList("Get Subscriptions List"), GetEntitlements("Get Entitlements"), diff --git a/app/src/main/java/com/chargebee/example/util/Constants.kt b/app/src/main/java/com/chargebee/example/util/Constants.kt index 700be5d..a813203 100644 --- a/app/src/main/java/com/chargebee/example/util/Constants.kt +++ b/app/src/main/java/com/chargebee/example/util/Constants.kt @@ -2,4 +2,5 @@ package com.chargebee.example.util object Constants { const val PRODUCTS_LIST_KEY = "products" + const val OLD_PRODUCT_ID = "currentProductId" } \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_input_update_layout.xml b/app/src/main/res/layout/dialog_input_update_layout.xml new file mode 100644 index 0000000..21b4a73 --- /dev/null +++ b/app/src/main/res/layout/dialog_input_update_layout.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + +