Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom entitlements: add README and other improvements #1167

Merged
merged 26 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6002092
remove amazon, actually use custom entitlements mode
aboedo Jul 21, 2023
339223c
removed trusted entitlements from this mode
aboedo Jul 21, 2023
0e843f1
more updates to readme
aboedo Jul 21, 2023
6825799
made things internal instead of removing them
aboedo Jul 21, 2023
3573b01
removed trusted entitlements from API tests
aboedo Jul 21, 2023
ce6f417
remove unused import
aboedo Jul 21, 2023
38e2ec1
fix adding dependency README section
vegaro Jul 24, 2023
f40ebfa
update to Purchases.configureInCustomEntitlementsComputationMode
vegaro Jul 24, 2023
6b777fe
comment out includeBuild
vegaro Jul 24, 2023
226c82c
better error handling
vegaro Jul 24, 2023
883202b
update artifact id
vegaro Jul 24, 2023
f99c646
renames in readme
vegaro Jul 24, 2023
83c5a09
comment out includebuild
vegaro Jul 24, 2023
3865342
update screenshot
vegaro Jul 24, 2023
ff0ae38
update PurchasesErrorCode.ProductAlreadyPurchasedError message
vegaro Jul 24, 2023
2c231e2
updates padding so column doesn't change with
vegaro Jul 24, 2023
e50c7dc
Update examples/CustomEntitlementComputationSample/README.md
vegaro Jul 24, 2023
bd54564
Update examples/CustomEntitlementComputationSample/README.md
vegaro Jul 24, 2023
86b7ab6
Update examples/CustomEntitlementComputationSample/README.md
vegaro Jul 24, 2023
d2fe571
Update examples/CustomEntitlementComputationSample/README.md
vegaro Jul 24, 2023
36ea52a
rename screenshot
vegaro Jul 24, 2023
2d2a10e
update description of Purchases.sharedInstance.updatedCustomerInfoLis…
vegaro Jul 24, 2023
cb2e4c4
Update examples/CustomEntitlementComputationSample/README.md
vegaro Jul 24, 2023
240bb4b
Update examples/CustomEntitlementComputationSample/README.md
vegaro Jul 24, 2023
5468bc2
Update examples/CustomEntitlementComputationSample/README.md
vegaro Jul 24, 2023
205bd91
Update examples/CustomEntitlementComputationSample/README.md
vegaro Jul 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
remove amazon, actually use custom entitlements mode
  • Loading branch information
aboedo committed Jul 24, 2023
commit 60020928061ec95907da9406bd5de7d7646d34e0
154 changes: 154 additions & 0 deletions examples/CustomEntitlementComputationSample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Custom Entitlements Computation Example App

This app is useful for testing RevenueCat under Custom Entitlements Computation mode and understanding how it works.

## What is Custom Entitlements Computation mode?

This is a special behavior mode for RevenueCat SDK, is intended for apps that will do their own entitlement computation separate from RevenueCat.

Apps using this mode rely on webhooks to signal their backends to refresh entitlements with RevenueCat.

In this mode, RevenueCat will not generate anonymous user IDs, it will not refresh customerInfo cache automatically only when a purchase goes through
and it will disallow all methods other than those for configuration, switching users, getting offerings and making purchases.

When in this mode, the app should use switchUser() to switch to a different App User ID if needed.
The SDK should only be configured once the initial appUserID is known.

## Using the app

To use the app, you should do the following:
- Configure your app in the [RevenueCat dashboard](https://app.revenuecat.com/). No special configuration is needed, but you should contact RevenueCat support
before enabling this mode to ensure that it's the right one for your app. It's highly recommended to set Transfer Behavior to "Keep with original App User ID" in the RevenueCat Dashboard.
- Update the API key in Constants.kt. You can update the default `appUserID` there too, although apps in this mode should
always be calling configure only when the appUserID is already known.
- Update the applicationId in defaultConfig of the app-level build.gradle to match your RevenueCat app configuration.
- Have at least one Offering with at least one Package configured for Android, since this is the one that the purchase button will use.

Once configured correctly, the app will allow you to log in with different users, and will show a list of all the times CustomerInfoListener fired, as well as
the values for each one.

To use this mode, ensure that you install the

Happy testing!

![sample screenshot](./Sample%20screenshot.png)

## Using Custom Entitlements mode

### Installation:

Install the SDK through Swift Package Manager.

Select File » Add Packages... and enter the repository URL of the https://github.com/RevenueCat/purchases-ios.git into the search bar (top right). Set the Dependency Rule to Up to next major, and the version number to 4.18.0 < 5.0.0.

**Check `RevenueCat_CustomEntitlementComputation` when a prompt for "Choose Package Products for purchases-ios" appears**. Finally, choose the target where you want to use it.

The library should have been added to the Swift Package Dependencies section and you should be able to import it now.

### Configuration:

The SDK should be configured once the user has already logged in. To configure, call:

```swift
Purchases.configureInCustomEntitlementsComputationMode(apiKey: "your_api_key", appUserID: appUserID)
```

### Getting Offerings:

Call getOfferings through either the Async / Await or completion blocks alternatives:

```swift

let offerings = try await Purchases.shared.offerings()

```

```swift
Purchases.shared.getOfferings { (offerings, error) in
// code to handle here
}
```

### Switching users:

To switch to a different user, call:

```swift
Purchases.shared.switchUser(to: appUserID)
```

This will ensure that all purchases made from this point on are posted for the new appUserID.
After calling this method, you might need to call your backend to refresh entitlements for the new appUserID if they haven't been refreshed already.

### Making purchases:

Call `purchase(package:)` through either the Async / Await or completion blocks alternatives:

```swift
do {
let (transaction, customerInfo, _) = try await Purchases.shared.purchase(package: package)
print(
"""
Purchase finished:
Transaction: \(transaction.debugDescription)
CustomerInfo: \(customerInfo.debugDescription)
"""
)
} catch ErrorCode.receiptAlreadyInUseError {
print("The receipt is already in use by another subscriber. " +
"Log in with the previous account or contact support to get your purchases transferred to " +
"regain access")
} catch ErrorCode.paymentPendingError {
print("The purchase is pending and may be completed at a later time." +
"This can happen when awaiting parental approval or going through extra authentication flows " +
"for credit cards in some countries.")
} catch ErrorCode.purchaseCancelledError {
print("Purchase was cancelled by the user.")
} catch {
print("FAILED TO PURCHASE: \(error.localizedDescription)")
}
```

```swift
Purchases.shared.purchase(package: package) { (transaction, customerInfo, error, userCancelled) in
if let error = error as? ErrorCode {
switch error {
case .receiptAlreadyInUseError:
print("The receipt is already in use by another subscriber. " +
"Log in with the previous account or contact support to get your purchases transferred to " +
"regain access")
case .paymentPendingError:
print("The purchase is pending and may be completed at a later time." +
"This can happen when awaiting parental approval or going through extra authentication flows " +
"for credit cards in some countries.")
case .purchaseCancelledError:
print("Purchase was cancelled by the user.")
default:
print("FAILED TO PURCHASE: \(error.localizedDescription)")
}
return
} else if let error = error {
print("FAILED TO PURCHASE: \(error.localizedDescription)")
return
}

print(
"""
Purchase finished:
Transaction: \(transaction.debugDescription)
CustomerInfo: \(customerInfo.debugDescription)
"""
)
}
```

### Observing changes to purchases:

To ensure that your app reacts to changes to subscriptions in real time, you can use `customerInfoStream`. This stream will only fire when new `customerInfo` is registered
in RevenueCat, like when a subscription is renewed. If there are no changes from the last value, it will not fire. This means it's not guaranteed to fire on every app open.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like when a subscription is renewed

This is not true for Android. Maybe we should mention this will only update on purchases. cc @aboedo

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, good call, we should update

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, customerInfoStream is not a thing in Android. I will update

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check it out and let me know what you think


```swift
for await customerInfo in Purchases.shared.customerInfoStream {
// code to handle here
}
```
13 changes: 0 additions & 13 deletions examples/CustomEntitlementComputationSample/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,6 @@ android {
}
}

flavorDimensions "store"
productFlavors {
google {
dimension "store"
buildConfigField "String", "STORE", "\"google\""
}
amazon {
dimension "store"
buildConfigField "String", "STORE", "\"amazon\""
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
Expand Down Expand Up @@ -76,5 +64,4 @@ dependencies {
debugImplementation libs.compose.ui.tooling
debugImplementation libs.compose.ui.test.manifest
implementation libs.revenuecat
implementation libs.revenuecat.amazon
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import com.revenuecat.purchases.PurchasesTransactionException
import com.revenuecat.purchases.awaitOfferings
import com.revenuecat.purchases.awaitPurchase
import com.revenuecat.purchases.interfaces.UpdatedCustomerInfoListener
import com.revenuecat.purchases.purchaseWith
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
agp = "8.0.2"
androidxNavigation = "2.5.3"
kotlin = "1.7.20"
purchases = "6.5.0"
purchases = "6.8.0"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why this was 6.5.0

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing since it is using the local version (in settings.gradle), it doesn't really matter?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is outdated on MagicWeatherCompose, which is what I used as a base project for the sample. I am not sure why it's outdated there, but I guess it's an issue in the releasing scripts

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both magic weather sample apps don't update their version automatically... We definitely should fix that, even if it's just pointing to the latest snapshot... I created a task here.

lifecycle = "2.5.0"
androidxCore = "1.10.1"

Expand Down Expand Up @@ -33,5 +33,4 @@ navigation-compose = "androidx.navigation:navigation-compose:2.5.3"
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" }

revenuecat = { module = "com.revenuecat.purchases:purchases", version.ref = "purchases" }
revenuecat-amazon = { module = "com.revenuecat.purchases:purchases-store-amazon", version.ref = "purchases" }
revenuecat = { module = "com.revenuecat.purchases:purchases@customEntitlementComputation", version.ref = "purchases" }
3 changes: 1 addition & 2 deletions examples/CustomEntitlementComputationSample/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ include ':app'

includeBuild("../../") {
dependencySubstitution {
substitute module("com.revenuecat.purchases:purchases") using project(":purchases")
substitute module("com.revenuecat.purchases:purchases-store-amazon") using project(":feature:amazon")
substitute module("com.revenuecat.purchases:purchases@customEntitlementComputation") using project(":purchases")
}
}