Skip to content

Commit

Permalink
Merge pull request #68 from cb-amutha/feature/add_customer
Browse files Browse the repository at this point in the history
Pass customer object to purchaseProduct method
  • Loading branch information
cb-amutha authored Aug 23, 2023
2 parents 586d470 + c9bc1fb commit 5d6fc67
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 31 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 0.2.0
New Feature
* Introduced new API `purchaseStoreProduct` to purchase product with customer information. (#68)
## 0.1.0
New Feature
* Adds show manage subscriptions settings in app (#67)
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ To use Chargebee SDK in your Flutter app, follow these steps:

``` dart
dependencies:
chargebee_flutter: ^0.1.0
chargebee_flutter: ^0.2.0
```

2. Install dependency.
Expand Down Expand Up @@ -91,15 +91,16 @@ try {
You can present any of the above products to your users for them to purchase.

#### Buy or Subscribe Product
Pass the `Product` and `CBCustomer` objects to the following function when the user chooses the product to purchase.

Pass the product and customer identifier to the following function when your customer 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.

`customerId` - **Optional parameter**. Although this is an optional parameter, we recommend passing customerId if it is available before user subscribes on your App. Passing this parameter ensures that customerId in your database matches with the customerId in Chargebee.
In case this parameter is not passed, then the **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.

``` dart
try {
final result = await Chargebee.purchaseProduct(product, customerId);
final customer = CBCustomer('customerId','firstName','lastName','emailId');
final result = await Chargebee.purchaseStoreProduct(product, customer: customer);
print("subscription id : ${result.subscriptionId}");
print("subscription status : ${result.status}");
} on PlatformException catch (e) {
Expand Down Expand Up @@ -184,14 +185,15 @@ Receipt validation is crucial to ensure that the purchases made by your users ar

* Add a network listener, as shown in the example project.
* Save the product identifier in the cache once the purchase is initiated and clear the cache once the purchase is successful.
* When the network connectivity is lost after the purchase is completed at Apple App Store/Google Play Store but not synced with Chargebee, retrieve the product from the cache once the network connection is back and initiate validateReceipt() by passing `productId` and `CBCustomer(optional)` as input. This will validate the receipt and sync the purchase in Chargebee as a subscription. For subscriptions, use the function to validateReceipt().
* When the network connectivity is lost after the purchase is completed at Apple App Store/Google Play Store but not synced with Chargebee, retrieve the product from the cache once the network connection is back and initiate `validateReceipt() / validateReceiptForNonSubscriptions()` by passing `productId` and `CBCustomer(optional)` as input. This will validate the receipt and sync the purchase in Chargebee as a subscription or one-time purchase. For subscriptions, use the function to `validateReceipt()`;for one-time purchases, use the function `validateReceiptForNonSubscriptions()`.

Use the function available for the retry mechanism.
##### Function for validating the Subscriptions receipt

``` dart
try {
final result = await Chargebee.validateReceipt(productId);
final customer = CBCustomer('customerId','firstName','lastName','emailId');
final result = await Chargebee.validateReceipt(productId, customer);
print("subscription id : ${result.subscriptionId}");
print("subscription status : ${result.status}");
} on PlatformException catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group 'com.chargebee.flutter.sdk'
version '0.1.0'
version '0.2.0'

buildscript {
ext.kotlin_version = '1.6.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,12 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar
}

private fun purchaseProduct(args: Map<String, Any>, result: Result) {
val customerID = args["customerId"] as String
val customer = CBCustomer(
args["customerId"] as String,
args["firstName"] as String,
args["lastName"] as String,
args["email"] as String
)
val arrayList: ArrayList<String> = ArrayList<String>()
arrayList.add(args["product"] as String)
CBPurchase.retrieveProducts(
Expand All @@ -198,7 +203,7 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar
}
CBPurchase.purchaseProduct(
productIDs.first(),
customerID,
customer,
object : CBCallback.PurchaseCallback<String> {
override fun onSuccess(
receiptDetail: ReceiptDetail,
Expand Down
32 changes: 28 additions & 4 deletions example/integration_test/chargebee_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ void main() {
await chargebeeTest.retrieveProductIdentifiersWithoutParam();
await chargebeeTest.retrieveProductIdentifiersWithParam();
await chargebeeTest.retrieveProducts();
await chargebeeTest.purchaseProducts_withCustomerInfo();
await chargebeeTest.purchaseProducts_withCustomerId();
await chargebeeTest.purchaseProduct_withCustomerInfo();
await chargebeeTest.purchaseProducts_withoutCustomerInfo();
await chargebeeTest.retrieveSubscriptions();
await chargebeeTest.retrieveEntitlements();
Expand Down Expand Up @@ -111,8 +112,14 @@ class ChargebeeTest {
'currencyCode',
SubscriptionPeriod.fromMap(
{'periodUnit': 'month', 'numberOfUnits': 3},),);

Future<void> purchaseProducts_withCustomerInfo() async {
final customer = CBCustomer(
'abc_flutter_test',
'fn',
'ln',
'[email protected]',
);

Future<void> purchaseProducts_withCustomerId() async {
tester.printToConsole('Starting to subscribe the product');

if (platformName == 'ios') {
Expand Down Expand Up @@ -141,7 +148,24 @@ class ChargebeeTest {
}

try {
final result = await Chargebee.purchaseProduct(product, 'abc');
final result = await Chargebee.purchaseProduct(product);
debugPrint('purchase result: $result');
expect(result.status, 'true');
tester.printToConsole('Product subscribed successfully!');
} on PlatformException catch (e) {
fail('Error: ${e.message}');
}
}

Future<void> purchaseProduct_withCustomerInfo() async {
tester.printToConsole('Starting to subscribe the product');
if (platformName == 'ios') {
_getProduct(productIdForiOS);
} else {
_getProduct(productIdForAndroid);
}
try {
final result = await Chargebee.purchaseStoreProduct(product, customer: customer);
debugPrint('purchase result: $result');
expect(result.status, 'true');
tester.printToConsole('Product subscribed successfully!');
Expand Down
44 changes: 42 additions & 2 deletions example/lib/product_listview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ class ProductListViewState extends State<ProductListView> {
if (product.subscriptionPeriod.unit.isNotEmpty &&
product.subscriptionPeriod.numberOfUnits != 0) {
mProgressBarUtil.showProgressDialog();
purchaseProduct(product);
// purchaseProduct(product);
purchaseStoreProduct(product);
} else {
_showDialog(context, product);
}
Expand All @@ -97,7 +98,46 @@ class ProductListViewState extends State<ProductListView> {

Future<void> purchaseProduct(Product product) async {
try {
final result = await Chargebee.purchaseProduct(product, 'customerId');
final result = await Chargebee.purchaseProduct(product, 'abc');
debugPrint('subscription result : $result');
debugPrint('subscription id : ${result.subscriptionId}');
debugPrint('plan id : ${result.planId}');
debugPrint('subscription status : ${result.status}');

mProgressBarUtil.hideProgressDialog();

if (result.status == 'true') {
_showSuccessDialog(context, 'Success');
} else {
_showSuccessDialog(context, result.subscriptionId);
}
} on PlatformException catch (e) {
debugPrint(
'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}');
mProgressBarUtil.hideProgressDialog();
if (e.code.isNotEmpty) {
final responseCode = int.parse(e.code);
if (responseCode >= 500 && responseCode <= 599) {
/// Cache the productId in SharedPreferences if failed synching with Chargebee.
final prefs = await SharedPreferences.getInstance();
prefs.setString('productId', product.id);

/// validate the receipt
validateReceipt(product.id);
}
}
}
}

Future<void> purchaseStoreProduct(Product product) async {
try {
final customer = CBCustomer(
'abc_flutter_test',
'fn',
'ln',
'[email protected]',
);
final result = await Chargebee.purchaseStoreProduct(product, customer: customer);
debugPrint('subscription result : $result');
debugPrint('subscription id : ${result.subscriptionId}');
debugPrint('plan id : ${result.planId}');
Expand Down
4 changes: 2 additions & 2 deletions ios/Classes/SwiftChargebeeFlutterSdkPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ public class SwiftChargebeeFlutterSdkPlugin: NSObject, FlutterPlugin {
return _result(FlutterError.noArgsError)
}
let productId = params["product"]
let customerId = params["customerId"]
let customer = CBCustomer(customerID: params["customerId"], firstName:params["firstName"], lastName: params["lastName"], email:params["email"])
var dict = [String:String]()
CBPurchase.shared.retrieveProducts(withProductID: [productId!], completion: { result in
DispatchQueue.main.async {
switch result {
case let .success(products):
debugPrint("products: \(products)");
let product: CBProduct = products.self.first!;
CBPurchase.shared.purchaseProduct(product: product, customerId: customerId) { result in
CBPurchase.shared.purchaseProduct(product: product, customer: customer) { result in
switch result {
case .success(let result):
if let subscriptionId = result.subscriptionId, let planId = result.planId{
Expand Down
2 changes: 1 addition & 1 deletion ios/chargebee_flutter.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Pod::Spec.new do |s|
s.name = 'chargebee_flutter'
s.version = '0.1.0'
s.version = '0.2.0'
s.summary = 'This is the official Software Development Kit (SDK) for Chargebee Flutter.'
s.description = <<-DESC
A new Flutter plugin.
Expand Down
49 changes: 47 additions & 2 deletions lib/src/chargebee.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,39 @@ class Chargebee {
///
/// If purchase success [PurchaseResult] object be returned.
/// Throws an [PlatformException] in case of failure.
@Deprecated(
'This method will be removed in upcoming release, Use purchaseStoreProduct API instead',
)
static Future<PurchaseResult> purchaseProduct(
Product product, [
String? customerId = '',
]) async {
customerId ??= '';
final map = _convertToMap(product, customerId: customerId);
return _purchaseResult(map);
}

/// Buy the product with/without customer data.
///
/// [product] product object to be passed.
///
/// [customer] it can be optional.
/// if passed, the subscription will be created by using customerId in chargebee.
/// if not passed, the value of customerId is same as SubscriptionId.
///
/// If purchase success [PurchaseResult] object be returned.
/// Throws an [PlatformException] in case of failure.
static Future<PurchaseResult> purchaseStoreProduct(
Product product, {
CBCustomer? customer,
}) async {
final map = _convertToMap(product, customer: customer);
return _purchaseResult(map);
}

static Future<PurchaseResult> _purchaseResult(Map params) async {
final String purchaseResult = await platform.invokeMethod(
Constants.mPurchaseProduct,
{Constants.product: product.id, Constants.customerId: customerId},
params,
);
if (purchaseResult.isNotEmpty) {
return PurchaseResult.fromJson(jsonDecode(purchaseResult.toString()));
Expand Down Expand Up @@ -387,4 +412,24 @@ class Chargebee {
}
return NonSubscriptionPurchaseResult.fromJson(jsonDecode(purchaseResult));
}

static Map _convertToMap(
Product product, {
String? customerId = '',
CBCustomer? customer,
}) {
String? id = '';
if (customerId?.isNotEmpty ?? false) {
id = customerId;
} else if (customer?.id?.isNotEmpty ?? false) {
id = customer?.id;
}
return {
Constants.product: product.id,
Constants.customerId: id,
Constants.firstName: customer?.firstName ?? '',
Constants.lastName: customer?.lastName ?? '',
Constants.email: customer?.email ?? '',
};
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: chargebee_flutter
description: This is the official Software Development Kit (SDK) for Chargebee Flutter.
version: 0.1.0
version: 0.2.0
homepage: 'https://chargebee.com'
repository: 'https://github.com/chargebee/chargebee-flutter'

Expand Down
Loading

0 comments on commit 5d6fc67

Please sign in to comment.