Skip to content

Commit

Permalink
Merge pull request #93 from cb-haripriyan/billing-library-5
Browse files Browse the repository at this point in the history
Changes for Billing library 5
  • Loading branch information
cb-haripriyan authored Nov 27, 2023
2 parents 0e325ef + 015435b commit b21555e
Show file tree
Hide file tree
Showing 29 changed files with 501 additions and 266 deletions.
48 changes: 34 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# Chargebee Android

> #### Updates for Billing Library 5
> ***Note**:
> - SDK Version 2.0: This version uses Google Billing Library 5.2.1 APIs to fetch product information from the Google Play Console and make purchases. If you’re integrating Chargebee’s SDK for the first time, then use this version, and if you’re migrating from the older version of SDK to this version, follow the migration steps in this [document](https://www.chargebee.com/docs/2.0/mobile-playstore-billing-library-5.html).
> - SDK Version 1.2.0: This [version](https://github.com/chargebee/chargebee-android/tree/1.x.x) includes Billing Library 5.2.1 but still uses Billing Library 4.0 APIs to fetch product information from the Google Play Console and make purchases. This will enable you to list or update your Android app on the store without any warnings from Google and give you enough time to migrate to version 2.0.
> - SDK Version 1.1.0: This and less than this version of SDKs use billing library 4.0 APIs that are deprecated by Google. Therefore, it is highly recommended that you upgrade your app and integrate it with SDK version 1.2.0 and above.

This is Chargebee’s Android Software Development Kit (SDK). This SDK makes it efficient and comfortable to build a seamless subscription experience in your Android app.

Post-installation, initialization, and authentication with the Chargebee site, this SDK will support the following process.
Expand All @@ -21,7 +29,7 @@ The following requirements must be set up before installing Chargebee’s Androi
The `Chargebee-Android` SDK can be installed by adding below dependency to the `build.gradle` file:

```kotlin
implementation 'com.chargebee:chargebee-android:1.2.0'
implementation 'com.chargebee:chargebee-android:2.0.0-beta-1'
```

## Example project
Expand Down Expand Up @@ -55,9 +63,21 @@ To configure the Chargebee Android SDK for completing and managing In-App Purcha
```kotlin
import com.chargebee.android.Chargebee

Chargebee.configure(site= "your-site",
publishableApiKey= "api_key",
sdkKey= "sdk_key",packageName = "packageName")
Chargebee.configure(
site = "your-site",
publishableApiKey = "api-key",
sdkKey = "sdk-key",
packageName = "your-package"
) {
when (it) {
is ChargebeeResult.Success -> {
// Success
}
is ChargebeeResult.Error -> {
// Error
}
}
}
```
### Configuration for credit card using tokenization
To configure SDK only for tokenizing credit card details, follow these steps.
Expand All @@ -69,7 +89,7 @@ To configure SDK only for tokenizing credit card details, follow these steps.
```kotlin
import com.chargebee.android.Chargebee

Chargebee.configure(site = "your-site", publishableApiKey = "api_key")
Chargebee.configure(site = "your-site", publishableApiKey = "api-key")

```
## Integration
Expand All @@ -86,7 +106,7 @@ The following section describes how to use the SDK to integrate In-App Purchase
Every In-App Purchase subscription product that you configure in your Play Store account, can be configured in Chargebee as a Plan. Start by retrieving the Google IAP Product IDs from your Chargebee account.

```kotlin
CBPurchase.retrieveProductIdentifers(queryParam) {
CBPurchase.retrieveProductIdentifiers(queryParam) {
when (it) {
is CBProductIDResult.ProductIds -> {
Log.i(TAG, "List of Product Identifiers: $it")
Expand All @@ -106,29 +126,29 @@ The above function will determine your product catalog version in Chargebee and
Retrieve the Google IAP Product using the following function.

```kotlin
CBPurchase.retrieveProducts(this, productIdList= "[Product ID's from Google Play Console]",
object : CBCallback.ListProductsCallback<ArrayList<Products>> {
override fun onSuccess(productDetails: ArrayList<Products>) {
CBPurchase.retrieveProducts(activity, productIdList= ["Product ID's from Google Play Console"],
object : CBCallback.ListProductsCallback<ArrayList<CBProduct>> {
override fun onSuccess(productDetails: ArrayList<CBProduct>) {
Log.i(TAG, "List of Products: $productDetails")
}
override fun onError(error: CBException) {
Log.e(TAG, "Error: ${error.message}")
// Handle error here
}
})

```
You can present any of the above products to your users for them to purchase.

### Buy or Subscribe Product
Pass the `CBProduct` and `CBCustomer` objects to the following function when the user chooses the product to purchase.
Pass the `PurchaseProductParams`, `CBCustomer` and `OfferToken` to the following 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` if it is available before the user subscribes to your App. 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
CBPurchase.purchaseProduct(product=CBProduct, customer=CBCustomer, object : CBCallback.PurchaseCallback<PurchaseModel>{
val purchaseParams = PurchaseProductParams(selectedCBProduct, "selectedOfferToken")
val cbCustomer = CBCustomer("customerId","firstName","lastName","email")
CBPurchase.purchaseProduct(purchaseProductParams = purchaseProductParams, customer = cbCustomer, object : CBCallback.PurchaseCallback<String>{
override fun onSuccess(result: ReceiptDetail, status:Boolean) {
Log.i(TAG, "$status")
Log.i(TAG, "${result.subscription_id}")
Expand Down Expand Up @@ -162,7 +182,7 @@ CBPurchase.purchaseNonSubscriptionProduct(product = CBProduct, customer = CBCust

The given code defines a function named `purchaseNonSubscriptionProduct` in the CBPurchase class, which takes four input parameters:

- `product`: An instance of `CBProduct` class, initialized with a `SkuDetails` instance representing the product to be purchased from the Google Play Store.
- `product`: An instance of `CBProduct` class, representing the product to be purchased from the Google Play Store.
- `customer`: Optional. An instance of `CBCustomer` class, initialized with the customer's details such as `customerId`, `firstName`, `lastName`, and `email`.
- `productType`: An enum instance of `productType` type, indicating the type of product to be purchased. It can be either .`consumable`, or `non_consumable`.
- `callback`: The `OneTimePurchaseCallback` listener will be invoked when product purchase completes.
Expand Down
10 changes: 5 additions & 5 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 30
compileSdkVersion 33
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId "com.chargebee.example"
minSdkVersion 21
targetSdkVersion 30
versionCode 3
minSdkVersion 24
targetSdkVersion 33
versionCode 6
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down Expand Up @@ -40,7 +40,7 @@ dependencies {
implementation 'com.google.android.material:material:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

implementation 'com.google.code.gson:gson:2.8.8'

Expand Down
13 changes: 7 additions & 6 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
<activity android:name="com.chargebee.example.token.TokenizeActivity" />
<activity android:name="com.chargebee.example.plan.PlanInJavaActivity">
</activity>
<activity android:name=".billing.BillingActivity"/>
<activity android:name=".items.ItemsActivity"/>
<activity android:name=".items.ItemActivity"/>
<activity android:name=".plan.PlansActivity"/>
<activity android:name=".subscription.SubscriptionActivity"/>
<activity android:name="com.chargebee.example.MainActivity">
<activity android:name="com.chargebee.example.billing.BillingActivity"/>
<activity android:name="com.chargebee.example.items.ItemsActivity"/>
<activity android:name="com.chargebee.example.items.ItemActivity"/>
<activity android:name="com.chargebee.example.plan.PlansActivity"/>
<activity android:name="com.chargebee.example.subscription.SubscriptionActivity"/>
<activity android:name="com.chargebee.example.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
16 changes: 7 additions & 9 deletions app/src/main/java/com/chargebee/example/ExampleApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.chargebee.example

import android.app.Application
import android.content.Context
import com.chargebee.android.Chargebee
import android.content.SharedPreferences
import android.util.Log
import com.chargebee.android.billingservice.CBCallback
Expand All @@ -18,7 +17,7 @@ import com.chargebee.example.util.NetworkUtil

class ExampleApplication : Application(), NetworkUtil.NetworkListener {
private lateinit var networkUtil: NetworkUtil
private lateinit var sharedPreference: SharedPreferences
private var sharedPreference: SharedPreferences? = null
lateinit var mContext: Context
private val customer = CBCustomer(
id = "sync_receipt_android",
Expand All @@ -36,8 +35,7 @@ class ExampleApplication : Application(), NetworkUtil.NetworkListener {
}

override fun onNetworkConnectionAvailable() {
Chargebee.configure(site = "", publishableApiKey= "",sdkKey= "", packageName = this.packageName)
val productId = sharedPreference.getString("productId", "")
val productId = sharedPreference?.getString("productId", "")
if (productId?.isNotEmpty() == true) {
val productList = ArrayList<String>()
productList.add(productId)
Expand All @@ -55,7 +53,7 @@ class ExampleApplication : Application(), NetworkUtil.NetworkListener {
productIdList,
object : CBCallback.ListProductsCallback<ArrayList<CBProduct>> {
override fun onSuccess(productIDs: ArrayList<CBProduct>) {
if (productIDs.first().productType == ProductType.SUBS)
if (productIDs.first().type == ProductType.SUBS)
validateReceipt(mContext, productIDs.first())
else
validateNonSubscriptionReceipt(mContext, productIDs.first())
Expand All @@ -76,8 +74,8 @@ class ExampleApplication : Application(), NetworkUtil.NetworkListener {
completionCallback = object : CBCallback.PurchaseCallback<String> {
override fun onSuccess(result: ReceiptDetail, status: Boolean) {
// Clear the local cache once receipt validation success
val editor = sharedPreference.edit()
editor.clear().apply()
val editor = sharedPreference?.edit()
editor?.clear()?.apply()
Log.i(javaClass.simpleName, "Subscription ID: ${result.subscription_id}")
Log.i(javaClass.simpleName, "Plan ID: ${result.plan_id}")
Log.i(javaClass.simpleName, "Customer ID: ${result.customer_id}")
Expand All @@ -99,8 +97,8 @@ class ExampleApplication : Application(), NetworkUtil.NetworkListener {
completionCallback = object : CBCallback.OneTimePurchaseCallback {
override fun onSuccess(result: NonSubscription, status: Boolean) {
// Clear the local cache once receipt validation success
val editor = sharedPreference.edit()
editor.clear().apply()
val editor = sharedPreference?.edit()
editor?.clear()?.apply()
Log.i(javaClass.simpleName, "Subscription ID: ${result.invoiceId}")
Log.i(javaClass.simpleName, "Plan ID: ${result.chargeId}")
Log.i(javaClass.simpleName, "Customer ID: ${result.customerId}")
Expand Down
21 changes: 15 additions & 6 deletions app/src/main/java/com/chargebee/example/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.chargebee.android.Chargebee
import com.chargebee.android.billingservice.CBCallback
import com.chargebee.android.billingservice.CBPurchase
import com.chargebee.android.models.CBRestoreSubscription
import com.chargebee.android.exceptions.CBException
import com.chargebee.android.exceptions.ChargebeeResult
import com.chargebee.android.models.CBProduct
import com.chargebee.example.adapter.ListItemsAdapter
import com.chargebee.example.addon.AddonActivity
Expand Down Expand Up @@ -173,11 +173,20 @@ class MainActivity : BaseActivity(), ListItemsAdapter.ItemClickListener {
) && !TextUtils.isEmpty(sdkKeyEditText.text.toString())
)
Chargebee.configure(
siteNameEditText.text.toString(),
apiKeyEditText.text.toString(),
true,
sdkKeyEditText.text.toString(), this.packageName
)
site = siteNameEditText.text.toString(),
publishableApiKey = apiKeyEditText.text.toString(),
sdkKey = sdkKeyEditText.text.toString(),
packageName = this.packageName
) {
when (it) {
is ChargebeeResult.Success -> {
Log.i(javaClass.simpleName, "Configured")
}
is ChargebeeResult.Error -> {
Log.e(javaClass.simpleName, " Failed")
}
}
}
}
builder.show()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,26 @@
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;

import com.chargebee.android.billingservice.ProductType;
import com.chargebee.android.models.CBProduct;
import com.chargebee.android.models.PricingPhase;
import com.chargebee.android.models.SubscriptionOffer;
import com.chargebee.example.R;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class ProductListAdapter extends RecyclerView.Adapter<ProductListAdapter.ViewHolder> {

private List<CBProduct> mProductsList;
private List<PurchaseProduct> purchaseProducts;
private ProductListAdapter.ProductClickListener mClickListener;
private Context mContext = null;
private PurchaseProduct selectedProduct = null;

public ProductListAdapter(Context context, List<CBProduct> mProductsList, ProductClickListener mClickListener) {
public ProductListAdapter(Context context, List<PurchaseProduct> purchaseProducts, ProductClickListener mClickListener) {
mContext = context;
this.mProductsList = mProductsList;
this.purchaseProducts = purchaseProducts;
this.mClickListener = mClickListener;
}

Expand All @@ -31,10 +38,12 @@ public ProductListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int vi

@Override
public void onBindViewHolder(ProductListAdapter.ViewHolder holder, int position) {
CBProduct products = mProductsList.get(position);
holder.mTextViewTitle.setText(products.getProductId());
holder.mTextViewPrice.setText(products.getProductPrice());
if (products.getSubStatus()) {
PurchaseProduct purchaseProduct = purchaseProducts.get(position);
holder.mTextViewTitle.setText(purchaseProduct.getProductId() + " "+ purchaseProduct.getBasePlanId());
holder.mTextViewPrice.setText(purchaseProduct.getPrice());
boolean isSubscriptionProductSelected = selectedProduct != null && selectedProduct.getCbProduct().getType().equals(ProductType.SUBS) && selectedProduct.getOfferToken().equals(purchaseProduct.getOfferToken());
boolean isOtpProductSelected = selectedProduct != null && selectedProduct.getCbProduct().getType().equals(ProductType.INAPP) && selectedProduct.getProductId().equals(purchaseProduct.getProductId());
if (isSubscriptionProductSelected || isOtpProductSelected) {
holder.mTextViewSubscribe.setText(R.string.status_subscribed);
holder.mTextViewSubscribe.setTextColor(mContext.getResources().getColor(R.color.success_green));
}else {
Expand All @@ -46,7 +55,7 @@ public void onBindViewHolder(ProductListAdapter.ViewHolder holder, int position)

@Override
public int getItemCount() {
return mProductsList.size();
return purchaseProducts.size();
}

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
Expand All @@ -63,6 +72,7 @@ public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickL
@Override
public void onClick(View view) {
if (mClickListener != null) {
selectedProduct = purchaseProducts.get(getAdapterPosition());
mClickListener.onProductClick(view, getAdapterPosition());
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.chargebee.example.adapter;

import com.chargebee.android.models.CBProduct;
import com.chargebee.android.models.PricingPhase;
import com.chargebee.android.models.SubscriptionOffer;

public class PurchaseProduct {
private final String productId;
private final CBProduct cbProduct;
private final String basePlanId;
private final String offerId;
private final String offerToken;
private final String price;

public PurchaseProduct(CBProduct cbProduct, SubscriptionOffer subscriptionOffer) {
this(cbProduct.getId(), cbProduct, subscriptionOffer.getBasePlanId(), subscriptionOffer.getOfferId(),
subscriptionOffer.getOfferToken(), subscriptionOffer.getPricingPhases().get(0).getFormattedPrice());
}

public PurchaseProduct(CBProduct cbProduct, PricingPhase oneTimePurchaseOffer) {
this(cbProduct.getId(), cbProduct,
null, null,
null, oneTimePurchaseOffer.getFormattedPrice());
}

public PurchaseProduct(String id, CBProduct cbProduct, String basePlanId, String offerId, String offerToken, String formattedPrice) {
this.productId = id;
this.cbProduct = cbProduct;
this.basePlanId = basePlanId;
this.offerId = offerId;
this.offerToken = offerToken;
this.price = formattedPrice;
}

public String getProductId() {
return productId;
}

public String getBasePlanId() {
return basePlanId;
}

public String getOfferId() {
return offerId;
}

public String getOfferToken() {
return offerToken;
}

public String getPrice() {
return price;
}

public CBProduct getCbProduct() {
return cbProduct;
}
}
Loading

0 comments on commit b21555e

Please sign in to comment.