Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 6 additions & 4 deletions revenuecat/skills/integrate-revenuecat/platforms/android.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,17 @@ class MyApplication : Application() {

- Use the **Google Play** public SDK key: it starts with `goog_` (Amazon Appstore keys start with `amzn_`).
- The SDK declares `com.android.vending.BILLING` in its own manifest; you do not need to add it.
- Minimum SDK: **21**. Confirm `minSdk` in the app module's `build.gradle(.kts)`.
- Minimum SDK: **23**, or **24** if you use `purchases-ui` for paywalls or Customer Center. Confirm `minSdk` in the app module's `build.gradle(.kts)`.
- Proguard/R8: the SDK ships consumer rules, no extra config needed.

## Verify

Run the app. In **logcat**, filter by tag `Purchases` and look for:
Run the app with `Purchases.logLevel = LogLevel.DEBUG` set before `configure`. The SDK logs under the tag `[Purchases] - <LEVEL>`, so filter on the text rather than an exact tag:

```
Purchases: ℹ️ [Purchases] - INFO: 😻‍👼 Purchases is configured
adb logcat | grep "\[Purchases\]"
```

A wrong API key shows up as an auth error on the first offerings fetch. No logs at all usually means `android:name=".MyApplication"` is missing from the manifest.
At configure time you should see debug lines containing `SDK Version - <version>` and `Package name - <your.package>`.

A wrong API key shows up as an auth error on the first offerings fetch. No `[Purchases]` lines at all, with the log level at DEBUG, usually means `configure` never ran, most often because `android:name=".MyApplication"` is missing from the manifest. A custom `Purchases.logHandler` also keeps these lines out of logcat.
23 changes: 10 additions & 13 deletions revenuecat/skills/integrate-revenuecat/platforms/kmp.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Install

Find the latest stable release at <https://github.com/RevenueCat/purchases-kmp/releases> and substitute that tag for `<latest>` in the snippet below. The KMP tag uses a `<wrapper>+<bundled-deps>` format (e.g. `2.10.2+17.55.1`), where the part before `+` is the KMP wrapper version and the part after is the bundled `purchases-hybrid-common` version. Use the full tag string as the artifact version first; if Gradle interprets the `+` as a wildcard, fall back to the wrapper portion only (e.g. `2.10.2`). If GitHub is unreachable, ask the user for a version to pin.
Find the latest stable release at <https://github.com/RevenueCat/purchases-kmp/releases> and substitute that version for `<latest>` in the snippet below (for example `3.10.1`). Since 3.0.0, versions are plain, without the old `+<purchases-hybrid-common>` suffix. Current releases need Kotlin 2.3.20+ and Android API 23+ (24+ once you add `purchases-kmp-ui`). Use 3.10.0 or later: 3.0 to 3.9 can log iOS users out when they update from a version before 3.0.0, and 3.10.0 recovers them. If GitHub is unreachable, ask the user for a version to pin.

In the shared module's `build.gradle.kts`:

Expand All @@ -15,7 +15,7 @@ kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.revenuecat.purchases:purchases-kmp-core:<latest>")
// implementation("com.revenuecat.purchases:purchases-kmp-ui:<latest>") // Compose Multiplatform paywalls
// implementation("com.revenuecat.purchases:purchases-kmp-ui:<latest>") // Compose Multiplatform paywalls, needs Android minSdk 24
}
}
}
Expand All @@ -25,7 +25,7 @@ Add `mavenCentral()` to your project repositories if it isn't already there.

### iOS linking

`purchases-kmp` bridges to `purchases-ios` on iOS. If you use CocoaPods with the Kotlin CocoaPods plugin, the bridge pod is wired automatically. For pure Swift Package Manager setups, follow the iOS target instructions in the [purchases-kmp README](https://github.com/RevenueCat/purchases-kmp#readme). The exact shape evolves per release.
Since 3.0.0 you add no CocoaPods pod or Swift package for the iOS SDK: `purchases-kmp` brings it along. Coming from 2.x, remove the `PurchasesHybridCommon` and `PurchasesHybridCommonUI` pods or packages ([3.0.0 migration guide](https://github.com/RevenueCat/purchases-kmp/blob/main/migrations/3.0.0-MIGRATION.md)). In the iOS source sets, opt in to `kotlinx.cinterop.ExperimentalForeignApi`, and if the project does not compile, set `isStatic = true` on the iOS framework binaries.

## Configure

Expand All @@ -48,22 +48,17 @@ fun initRevenueCat(apiKey: String) {

### Android: call from `Application.onCreate()`

On Android, `PurchasesConfiguration` needs a `Context`. The KMP SDK provides a platform specific overload that takes the Android `Context` as the first argument. Use it from your `Application` class:
The KMP `PurchasesConfiguration.Builder` takes only the API key. On Android, `purchases-kmp` gets the `Application` context itself through AndroidX App Startup, so keep `androidx.startup.InitializationProvider` in the merged manifest (remove only specific initializers if you must). Call the shared function from your `Application` class:

```kotlin
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// The purchases-kmp Android actual of PurchasesConfiguration takes a Context.
// Pass it here before forwarding to the shared initRevenueCat, or construct
// the PurchasesConfiguration inline on this Android-only path.
initRevenueCat("goog_YOUR_ANDROID_PUBLIC_SDK_KEY")
}
}
```

> If the `Purchases.configure(...)` signature in your installed version of purchases-kmp requires a different shape on Android (e.g. `PurchasesConfiguration(context, apiKey)`), follow what the IDE autocompletes. The KMP SDK's `expect`/`actual` surface has changed across versions. Don't guess.

### iOS: call from `@main App` (SwiftUI)

```swift
Expand All @@ -82,12 +77,14 @@ struct iOSApp: App {
## Notes

- Two public SDK keys: `appl_…` for iOS, `goog_…` for Android. Keep them separate.
- Because the KMP SDK wraps the native SDKs, the verify logs below are the native SDK logs. See `platforms/ios.md` / `platforms/android.md` for the exact log line on each side.
- When in doubt about the exact shape of `PurchasesConfiguration`, check the installed version's source or the [purchases-kmp README](https://github.com/RevenueCat/purchases-kmp). The skill prefers "accurate for your version" over "plausibly correct in general."
- Because the KMP SDK wraps the native SDKs, the verify logs below are the native SDK logs.
- When in doubt about the exact shape of `PurchasesConfiguration`, check the installed version's source or the [configuration docs](https://www.revenuecat.com/docs/getting-started/quickstart#2-initialize-and-configure-the-revenuecat-sdk). The skill prefers "accurate for your version" over "plausibly correct in general."

## Verify

Run each platform target. Expect:

- **iOS**: `[Purchases] - INFO: 😻‍👼 Purchases is configured` in Xcode console.
- **Android**: `Purchases: ℹ️ [Purchases] - INFO: 😻‍👼 Purchases is configured` in logcat.
- **iOS**: with `LogLevel.DEBUG`, the Xcode console shows a line containing `SDK Version - <version>`.
- **Android**: with `LogLevel.DEBUG`, `adb logcat | grep "\[Purchases\]"` shows a line containing `SDK Version - <version>`.

On both targets `<version>` is the native SDK's version, not the purchases-kmp version.
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class SettingsActivity : ComponentActivity() {

## Notes

- `CustomerCenter` and `ShowCustomerCenter` require `purchases-ui` **8.x** or newer. If the installed version is older, fall back to rendering subscription state from `Purchases.sharedInstance.getCustomerInfo(…)` plus a restore button and a link to `https://play.google.com/store/account/subscriptions`.
- `CustomerCenter` requires `purchases-ui` **8.12.0** or newer, and `ShowCustomerCenter` **8.13.0** or newer. If the installed version is older, fall back to rendering subscription state from `Purchases.sharedInstance.getCustomerInfo(…)` plus a restore button and a link to `https://play.google.com/store/account/subscriptions`.
- Refund requests are **not** supported on Android (refunds go through Google Play). Any refund related dashboard action degrades gracefully to either hiding the option or deep linking to the Play subscriptions page.
- When the user taps **Manage subscription** on Android, the Customer Center opens the system subscriptions screen via an `Intent`. Your app returns to the foreground afterwards.
- The composable requires a Material 3 theme in the Compose tree. `CustomerCenterActivity` sets one up for you; if you embed `CustomerCenter` inside your own Compose host, wrap it in `MaterialTheme { … }`.
Expand All @@ -84,6 +84,6 @@ Sign into the app with a Google account that has at least one active sandbox sub
2. Tap **Restore purchases**. The active entitlements reload. If you wired `CustomerCenterOptions.listener`, its `onRestoreCompleted` callback fires.
3. Tap **Manage subscription**. The Google Play subscriptions screen opens in a new task.
4. Close the Customer Center. With the composable, `onDismiss` fires. With `ShowCustomerCenter`, the `ActivityResultCallback` runs.
5. `adb logcat -s Purchases` shows the customer info fetch and any transaction events.
5. `adb logcat | grep "\[Purchases\]"` shows the customer info fetch and any transaction events.

If the view is empty for a user who has purchases, confirm `Purchases.sharedInstance.appUserID` matches the dashboard user who owns those transactions and that the dashboard's Customer Center section is configured.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Install

Find the latest stable release at <https://github.com/RevenueCat/purchases-kmp/releases> and substitute that tag for `<latest>` in the snippet below. The KMP tag uses a `<wrapper>+<bundled-deps>` format (e.g. `2.10.2+17.55.1`), where the part before `+` is the KMP wrapper version and the part after is the bundled `purchases-hybrid-common` version. Use the full tag string as the artifact version first; if Gradle interprets the `+` as a wildcard, fall back to the wrapper portion only (e.g. `2.10.2`). If GitHub is unreachable, ask the user for a version to pin.
Find the latest stable release at <https://github.com/RevenueCat/purchases-kmp/releases> and substitute that version for `<latest>` in the snippet below (for example `3.10.1`). Since 3.0.0, versions are plain, without the old `+<purchases-hybrid-common>` suffix. Current releases need Kotlin 2.3.20+, and `purchases-kmp-ui` needs Android API 24+ (core alone needs 23+). Use 3.10.0 or later: 3.0 to 3.9 can log iOS users out when they update from a version before 3.0.0, and 3.10.0 recovers them. If GitHub is unreachable, ask the user for a version to pin.

In the shared module's `build.gradle.kts`:

Expand All @@ -21,7 +21,7 @@ kotlin {
}
```

Compose Multiplatform must already be set up on the shared module. On iOS, the UI artifact bridges to `RevenueCatUI`; for CocoaPods-based KMP projects, the Kotlin CocoaPods plugin wires the pod automatically. For pure SPM setups, follow the iOS linking section of the [purchases-kmp README](https://github.com/RevenueCat/purchases-kmp#readme).
Compose Multiplatform must already be set up on the shared module. On iOS, the UI artifact wraps the native `RevenueCatUI` Customer Center. Since 3.0.0 it needs no extra CocoaPods pod or Swift package. Coming from 2.x, remove `PurchasesHybridCommonUI` ([3.0.0 migration guide](https://github.com/RevenueCat/purchases-kmp/blob/main/migrations/3.0.0-MIGRATION.md)).

## Implement

Expand Down Expand Up @@ -53,7 +53,7 @@ The common API is minimal: `modifier` and a required `onDismiss` callback. Platf

- Customer Center requires iOS 15+ on the iOS target. On Android, refund flows degrade to deep linking into Google Play (Apple only feature).
- The KMP common API exposes fewer hooks than either native SDK. If your product requires `onRestoreCompleted`, `onManagementOptionSelected`, or other lifecycle callbacks, wire them through platform code directly, or upgrade to a `purchases-kmp-ui` release that exposes them in common.
- If the IDE cannot resolve `com.revenuecat.purchases:purchases-kmp-ui`, confirm artifact coordinates against the [purchases-kmp README](https://github.com/RevenueCat/purchases-kmp) for your installed version; the module has evolved across 1.x.
- If the IDE cannot resolve `com.revenuecat.purchases:purchases-kmp-ui`, check the version against the [purchases-kmp releases](https://github.com/RevenueCat/purchases-kmp/releases) (current releases are 3.x) and the coordinates against the [Customer Center docs for Kotlin Multiplatform](https://www.revenuecat.com/docs/tools/customer-center/customer-center-kmp).
- Ensure `Purchases.logIn(appUserId)` has run for signed in users before opening the Customer Center, or subscriptions will show up under an anonymous ID.

## Verify
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ runCatching {

- `Purchases.sharedInstance.delegate` holds a single reference. If you need to fan out to multiple consumers, funnel through a single repository (as shown) and expose a `StateFlow`.
- Replace `"premium"` with the entitlement identifier configured in the RevenueCat dashboard. It is case sensitive.
- If your installed version of `purchases-kmp` does not expose `awaitCustomerInfo` in `com.revenuecat.purchases.kmp.ktx`, prefer what the IDE autocompletes and see the `purchases-kmp` README. Some versions ship result based variants in a separate module.
- If your installed version of `purchases-kmp` does not expose `awaitCustomerInfo` in `com.revenuecat.purchases.kmp.ktx`, prefer what the IDE autocompletes and see the [ktx package in the purchases-kmp API reference](https://revenuecat.github.io/purchases-kmp/core/com.revenuecat.purchases.kmp.ktx/). Some versions ship result based variants in a separate module.

## Verify

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Purchases.sharedInstance.logOut(

- Use a stable opaque identifier (UUID / hash). Do not pass an email address, phone number, or a raw integer database id.
- `Purchases.sharedInstance.isAnonymous` is available in commonMain and is the correct guard before calling `awaitLogOut`.
- If your installed version of `purchases-kmp` does not expose `awaitLogIn` / `awaitLogOut` in `com.revenuecat.purchases.kmp.ktx`, prefer what the IDE autocompletes and see the `purchases-kmp` README. Some versions ship `Result`-based variants in a separate module.
- If your installed version of `purchases-kmp` does not expose `awaitLogIn` / `awaitLogOut` in `com.revenuecat.purchases.kmp.ktx`, prefer what the IDE autocompletes and see the [ktx package in the purchases-kmp API reference](https://revenuecat.github.io/purchases-kmp/core/com.revenuecat.purchases.kmp.ktx/). Some versions ship `Result`-based variants in a separate module.

## Verify

Expand Down
4 changes: 2 additions & 2 deletions revenuecat/skills/revenuecat-migrate/platforms/android.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Use observer mode. Your existing Play Billing code keeps owning the purchase flo

### Install the SDK

See `integrate-revenuecat/platforms/android.md` for dependency specifics. Target a recent 8.x or newer release.
See `integrate-revenuecat/platforms/android.md` for dependency specifics. Target the current major release (10.x). It depends on Play Billing Library 8.3.0 and minSdk 23, so your existing Play Billing code must compile against Billing Library 8.

### Configure in observer mode

Expand Down Expand Up @@ -74,7 +74,7 @@ Treat the SDK repo's docs as authoritative. Any version-specific diff written he
After migration:

1. App builds at the new SDK version with `Purchases.logLevel = LogLevel.DEBUG`.
2. Logcat at launch shows `Purchases: ℹ️ [Purchases] - INFO: 😻‍👼 Purchases is configured`.
2. `adb logcat | grep "\[Purchases\]"` at launch shows the `SDK Version - <version>` debug line.
3. A sandbox purchase from a license tester on the Internal Testing track shows on the RevenueCat dashboard Sandbox view, attached to the correct appUserID.
4. A user with an existing active subscription still has it after relaunch.
5. Log level is dropped before the next release.
2 changes: 1 addition & 1 deletion revenuecat/skills/revenuecat-migrate/platforms/kmp.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Treat the SDK repo's docs as authoritative. Any version-specific diff written he
After migration:

1. Both iOS and Android targets build at the new version with debug logging on.
2. The native SDK configure banner appears in each target's platform console.
2. Each target's platform console shows a line containing `SDK Version - <version>`, with the version of the native SDK that `purchases-kmp` bundles.
3. A sandbox purchase on each target shows on the RevenueCat dashboard.
4. An existing subscriber still has their entitlement active on each target.
5. Log level dropped before release.
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,4 @@ Run the app on a device or emulator signed into a Google sandbox tester:
1. Trigger the code path that opens the paywall. The template configured in the dashboard renders.
2. Tap a package and complete a test purchase. The paywall closes and either `PaywallListener.onPurchaseCompleted` fires (composable) or the result handler receives `PaywallResult.Purchased` (activity).
3. Dismiss with the close button. `dismissRequest` (composable) or `PaywallResult.Cancelled` (activity) fires.
4. Run `adb logcat -s Purchases` and confirm a successful transaction log line around the purchase.
4. Run `adb logcat | grep "\[Purchases\]"` and confirm a successful transaction log line around the purchase.
Loading
Loading