Skip to content

Commit

Permalink
Merge pull request #139 from cb-haripriyan/retrieve-entitlements
Browse files Browse the repository at this point in the history
Adds Retrieve entitlements API
  • Loading branch information
cb-amutha authored Jul 18, 2023
2 parents ef61325 + c34db6d commit d0b5ee5
Show file tree
Hide file tree
Showing 20 changed files with 345 additions and 37 deletions.
2 changes: 1 addition & 1 deletion ChargebeeReactNative.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Pod::Spec.new do |s|

s.source_files = "ios/**/*.{h,m,mm,swift}"

s.dependency "Chargebee", '1.0.23'
s.dependency "Chargebee", '1.0.25'

s.dependency "React-Core"
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,24 @@ try {

```

#### Retrieve Entitlements of a Subscription

Use the Subscription ID for fetching the list of [entitlements](https://www.chargebee.com/docs/2.0/entitlements.html) associated with the subscription.

```ts
const request: EntitlementsRequest = {
subscriptionId: 'subscription-id',
};
try {
const entitlements: Array<Entitlement> = await Chargebee.retrieveEntitlements(request);
} catch (error) {
console.log(error);
}
```

**Note**: Entitlements feature is available only if your Chargebee site is on [Product Catalog 2.0](https://www.chargebee.com/docs/2.0/product-catalog.html).


#### Restore Purchases

The `restorePurchases()` function helps to recover your app user's previous purchases without making them pay again. Sometimes, your app user may want to restore their previous purchases after switching to a new device or reinstalling your app. You can use the `restorePurchases()` function to allow your app user to easily restore their previous purchases.
Expand Down Expand Up @@ -221,6 +239,8 @@ These are the possible error codes and their descriptions:
| 1000 | INVALID_SDK_CONFIGURATION |
| 1001 | INVALID_CATALOG_VERSION |
| 1002 | CANNOT_MAKE_PAYMENTS |
| 1003 | NO_PRODUCT_TO_RESTORE |
| 1004 | INVALID_RESOURCE |
| 2001 | INVALID_OFFER |
| 2002 | INVALID_PURCHASE |
| 2003 | INVALID_SANDBOX |
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'com.chargebee:chargebee-android:1.0.18'
implementation 'com.chargebee:chargebee-android:1.0.20'
implementation 'com.android.billingclient:billing-ktx:5.1.0'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.chargebee.android.billingservice.GPErrorCode
import com.chargebee.android.exceptions.CBException
import com.chargebee.android.exceptions.CBProductIDResult
import com.chargebee.android.exceptions.ChargebeeResult
import com.chargebee.android.models.CBEntitlements
import com.chargebee.android.models.CBProduct
import com.chargebee.android.models.CBRestoreSubscription
import com.chargebee.android.models.CBSubscription
Expand All @@ -19,6 +20,7 @@ import com.chargebee.android.reactnative.models.messageUserInfo
import com.chargebee.android.reactnative.models.toMap
import com.chargebee.android.reactnative.utils.convertArrayToWritableArray
import com.chargebee.android.reactnative.utils.convertAuthenticationDetailToDictionary
import com.chargebee.android.reactnative.utils.convertEntitlementsToDictionary
import com.chargebee.android.reactnative.utils.convertListToWritableArray
import com.chargebee.android.reactnative.utils.convertPurchaseResultToDictionary
import com.chargebee.android.reactnative.utils.convertQueryParamsToArray
Expand All @@ -31,6 +33,7 @@ import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import java.lang.RuntimeException

class ChargebeeReactNativeModule internal constructor(context: ReactApplicationContext) :
ChargebeeReactNativeSpec(context) {
Expand All @@ -53,6 +56,7 @@ class ChargebeeReactNativeModule internal constructor(context: ReactApplicationC
is ChargebeeResult.Success -> {
promise.resolve(convertAuthenticationDetailToDictionary(it.data))
}

is ChargebeeResult.Error -> {
val messageUserInfo = it.exp.messageUserInfo()
promise.reject(
Expand All @@ -75,9 +79,15 @@ class ChargebeeReactNativeModule internal constructor(context: ReactApplicationC
val identifiers = it.IDs.toArray(arrayOf<String>())
promise.resolve(convertArrayToWritableArray(identifiers))
}

is CBProductIDResult.Error -> {
val messageUserInfo = it.exp.messageUserInfo()
promise.reject("${it.exp.errorCode().code}", messageUserInfo.getString("message"), it.exp, messageUserInfo)
promise.reject(
"${it.exp.errorCode().code}",
messageUserInfo.getString("message"),
it.exp,
messageUserInfo
)
}
}
}
Expand All @@ -90,19 +100,36 @@ class ChargebeeReactNativeModule internal constructor(context: ReactApplicationC
CBPurchase.retrieveProducts(it, convertReadableArray(productIds),
object : CBCallback.ListProductsCallback<ArrayList<CBProduct>> {
override fun onSuccess(productDetails: ArrayList<CBProduct>) {
if(productDetails.isEmpty()) {
val productNotAvailableError = CBException(ErrorDetail(message = GPErrorCode.ProductUnavailable.errorMsg, httpStatusCode = CBReactNativeError.PRODUCT_NOT_AVAILABLE.code))
if (productDetails.isEmpty()) {
val productNotAvailableError = CBException(
ErrorDetail(
message = GPErrorCode.ProductUnavailable.errorMsg,
httpStatusCode = CBReactNativeError.PRODUCT_NOT_AVAILABLE.code
)
)
val messageUserInfo = productNotAvailableError.messageUserInfo()
promise.reject("${productNotAvailableError.httpStatusCode}", messageUserInfo.getString("message"), productNotAvailableError, productNotAvailableError.messageUserInfo())
promise.reject(
"${productNotAvailableError.httpStatusCode}",
messageUserInfo.getString("message"),
productNotAvailableError,
productNotAvailableError.messageUserInfo()
)
} else {
promise.resolve(convertListToWritableArray(productDetails))
}
}

override fun onError(error: CBException) {
val cbReactNativeError = error.httpStatusCode?.let { it -> CBReactNativeError.fromBillingCode(it) } ?: CBReactNativeError.UNKNOWN
val cbReactNativeError =
error.httpStatusCode?.let { it -> CBReactNativeError.fromBillingCode(it) }
?: CBReactNativeError.UNKNOWN
val messageUserInfo = error.messageUserInfo()
promise.reject("${cbReactNativeError.code}", messageUserInfo.getString("message"), error, messageUserInfo)
promise.reject(
"${cbReactNativeError.code}",
messageUserInfo.getString("message"),
error,
messageUserInfo
)
}
})
}
Expand All @@ -128,21 +155,44 @@ class ChargebeeReactNativeModule internal constructor(context: ReactApplicationC
}

override fun onError(error: CBException) {
val cbReactNativeError = error.httpStatusCode?.let { it -> CBReactNativeError.fromBillingCode(it) } ?: CBReactNativeError.UNKNOWN
val cbReactNativeError =
error.httpStatusCode?.let { it -> CBReactNativeError.fromBillingCode(it) }
?: CBReactNativeError.UNKNOWN
val messageUserInfo = error.messageUserInfo()
promise.reject("${cbReactNativeError.code}", messageUserInfo.getString("message"), error, messageUserInfo)
promise.reject(
"${cbReactNativeError.code}",
messageUserInfo.getString("message"),
error,
messageUserInfo
)
}
})
} else {
val productNotAvailableError = CBException(ErrorDetail(message = GPErrorCode.ProductUnavailable.errorMsg, httpStatusCode = CBReactNativeError.PRODUCT_NOT_AVAILABLE.code))
val productNotAvailableError = CBException(
ErrorDetail(
message = GPErrorCode.ProductUnavailable.errorMsg,
httpStatusCode = CBReactNativeError.PRODUCT_NOT_AVAILABLE.code
)
)
val messageUserInfo = productNotAvailableError.messageUserInfo()
promise.reject("${productNotAvailableError.httpStatusCode}", messageUserInfo.getString("message"), productNotAvailableError, productNotAvailableError.messageUserInfo())
promise.reject(
"${productNotAvailableError.httpStatusCode}",
messageUserInfo.getString("message"),
productNotAvailableError,
productNotAvailableError.messageUserInfo()
)
}

}

override fun onError(error: CBException) {
val messageUserInfo = error.messageUserInfo()
promise.reject("${CBReactNativeError.SYSTEM_ERROR.code}", messageUserInfo.getString("message"), error, messageUserInfo)
promise.reject(
"${CBReactNativeError.SYSTEM_ERROR.code}",
messageUserInfo.getString("message"),
error,
messageUserInfo
)
}
})
}
Expand All @@ -156,6 +206,7 @@ class ChargebeeReactNativeModule internal constructor(context: ReactApplicationC
val subscriptions = (it.data as CBSubscription).list
promise.resolve(convertSubscriptionsToDictionary(subscriptions))
}

is ChargebeeResult.Error -> {
val messageUserInfo = it.exp.messageUserInfo()
promise.reject(
Expand All @@ -178,9 +229,15 @@ class ChargebeeReactNativeModule internal constructor(context: ReactApplicationC
override fun onSuccess(result: List<CBRestoreSubscription>) {
promise.resolve(convertRestoredSubscriptionsToDictionary(result))
}

override fun onError(error: CBException) {
val messageUserInfo = error.messageUserInfo()
promise.reject("${CBReactNativeError.RESTORE_FAILED.code}", messageUserInfo.getString("message"), error, messageUserInfo)
promise.reject(
"${CBReactNativeError.RESTORE_FAILED.code}",
messageUserInfo.getString("message"),
error,
messageUserInfo
)
}
})
}
Expand All @@ -206,26 +263,81 @@ class ChargebeeReactNativeModule internal constructor(context: ReactApplicationC
}

override fun onError(error: CBException) {
val cbReactNativeError = error.httpStatusCode?.let { it -> CBReactNativeError.fromBillingCode(it) } ?: CBReactNativeError.UNKNOWN
val cbReactNativeError =
error.httpStatusCode?.let { it -> CBReactNativeError.fromBillingCode(it) }
?: CBReactNativeError.UNKNOWN
val messageUserInfo = error.messageUserInfo()
promise.reject("${cbReactNativeError.code}", messageUserInfo.getString("message"), error, messageUserInfo)
promise.reject(
"${cbReactNativeError.code}",
messageUserInfo.getString("message"),
error,
messageUserInfo
)
}
})
} else {
val productNotAvailableError = CBException(ErrorDetail(message = GPErrorCode.ProductUnavailable.errorMsg, httpStatusCode = CBReactNativeError.PRODUCT_NOT_AVAILABLE.code))
val productNotAvailableError = CBException(
ErrorDetail(
message = GPErrorCode.ProductUnavailable.errorMsg,
httpStatusCode = CBReactNativeError.PRODUCT_NOT_AVAILABLE.code
)
)
val messageUserInfo = productNotAvailableError.messageUserInfo()
promise.reject("${productNotAvailableError.httpStatusCode}", messageUserInfo.getString("message"), productNotAvailableError, productNotAvailableError.messageUserInfo())
promise.reject(
"${productNotAvailableError.httpStatusCode}",
messageUserInfo.getString("message"),
productNotAvailableError,
productNotAvailableError.messageUserInfo()
)
}

}

override fun onError(error: CBException) {
val messageUserInfo = error.messageUserInfo()
promise.reject("${CBReactNativeError.SYSTEM_ERROR.code}", messageUserInfo.getString("message"), error, messageUserInfo)
promise.reject(
"${CBReactNativeError.SYSTEM_ERROR.code}",
messageUserInfo.getString("message"),
error,
messageUserInfo
)
}
})
}
}

@ReactMethod
override fun retrieveEntitlements(entitlementsRequest: ReadableMap, promise: Promise) {
val subscriptionId = entitlementsRequest.getString("subscriptionId") ?: run {
val runtimeException = RuntimeException("Subscription ID is invalid")
promise.reject(
"${CBReactNativeError.INVALID_RESOURCE.code}",
runtimeException.message,
runtimeException,
null
)
return
}
Chargebee.retrieveEntitlements(subscriptionId) {
when (it) {
is ChargebeeResult.Success -> {
val entitlements = (it.data as CBEntitlements).list
promise.resolve(convertEntitlementsToDictionary(entitlements))
}

is ChargebeeResult.Error -> {
val messageUserInfo = it.exp.messageUserInfo()
promise.reject(
"${CBReactNativeError.INVALID_SDK_CONFIGURATION.code}",
messageUserInfo.getString("message"),
it.exp,
messageUserInfo
)
}
}
}
}

companion object {
const val NAME = "ChargebeeReactNative"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum class CBReactNativeError(val code: Int) {
INVALID_CATALOG_VERSION(1001),
CANNOT_MAKE_PAYMENTS(1002),
NO_PRODUCT_TO_RESTORE(1003),
INVALID_RESOURCE(1004),

// Store Errors
INVALID_OFFER(2001),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.chargebee.android.reactnative.utils

import com.android.billingclient.api.SkuDetails
import com.chargebee.android.models.CBEntitlementsWrapper
import com.chargebee.android.models.CBProduct
import com.chargebee.android.models.CBRestoreSubscription
import com.chargebee.android.reactnative.models.PurchaseResult
Expand Down Expand Up @@ -118,3 +119,26 @@ internal fun convertRestoredSubscriptionToDictionary(restoredSubscription: CBRes
writableMap.putString("storeStatus", restoredSubscription.storeStatus)
return writableMap
}

internal fun convertEntitlementsToDictionary(entitlements: List<CBEntitlementsWrapper>): WritableArray {
val writableArray: WritableArray = WritableNativeArray()
for (item in entitlements) {
writableArray.pushMap(convertEntitlementToDictionary(item))
}
return writableArray
}

fun convertEntitlementToDictionary(entitlementWrapper: CBEntitlementsWrapper): ReadableMap {
val writableMap: WritableMap = WritableNativeMap()
val entitlement = entitlementWrapper.subscription_entitlement
writableMap.putString("subscriptionId", entitlement.subscription_id)
writableMap.putString("featureId", entitlement.feature_id)
writableMap.putString("featureName", entitlement.feature_name)
writableMap.putString("featureDescription", entitlement.feature_description)
writableMap.putString("featureType", entitlement.feature_type)
writableMap.putString("value", entitlement.value)
writableMap.putString("name", entitlement.name)
writableMap.putBoolean("isOverridden", entitlement.is_overridden)
writableMap.putBoolean("isEnabled", entitlement.is_enabled)
return writableMap
}
1 change: 1 addition & 0 deletions android/src/oldarch/ChargebeeReactNativeSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ abstract class ChargebeeReactNativeSpec internal constructor(context: ReactAppli
abstract fun retrieveSubscriptions(queryParams: ReadableMap, promise: Promise)
abstract fun restorePurchases(includeInactivePurchases: Boolean, promise: Promise)
abstract fun validateReceipt(productId: String, customer: ReadableMap, promise: Promise)
abstract fun retrieveEntitlements(entitlementsRequest: ReadableMap, promise: Promise)
}
10 changes: 5 additions & 5 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
PODS:
- boost (1.76.0)
- Chargebee (1.0.23)
- ChargebeeReactNative (2.2.0):
- Chargebee (= 1.0.23)
- Chargebee (1.0.25)
- ChargebeeReactNative (2.2.1):
- Chargebee (= 1.0.25)
- React-Core
- CocoaAsyncSocket (7.6.5)
- DoubleConversion (1.1.6)
Expand Down Expand Up @@ -572,8 +572,8 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
boost: 57d2868c099736d80fcd648bf211b4431e51a558
Chargebee: 25ca17c26930aaa82b464d7578a786d09e29c9ab
ChargebeeReactNative: a202ad20eebd74b434ca75249645b4f575b4d008
Chargebee: 4ae46565eea81a367b210381a86b723dfda14017
ChargebeeReactNative: 519f027606991f6b55f30d521ea3d62ab2c10e61
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
FBLazyVector: 61839cba7a48c570b7ac3e1cd8a4d0948382202f
Expand Down
Loading

0 comments on commit d0b5ee5

Please sign in to comment.