← RevenueDotCONTENT HISTORY

Update to RevenueDot

Snapshot Oct 29, 2026 · 12:06 UTC · version 0.1.9

Collection source: downloaded plugin package.

WHAT CHANGED

First saved snapshot

No earlier snapshot is available to establish a change.

Save RevenueDot and check future changes in Following.

Up to 10 plugins free · no card required.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Use this skill when the user wants to add in-app subscriptions, a paywall, a premium plan, a free trial or a lifetime purchase to an iOS, Android, React Native, Flutter or Expo app, with RevenueDot as the subscription backend. It covers creating the catalog, installing and configuring the SDK, showing offerings, gating features on an entitlement, testing with the Test Store and connecting App Store and Google Play credentials, including when the user pastes a store key into the chat.",
  "included_files": [],
  "name": "add-subscriptions",
  "skill_md_contents": "---\nname: add-subscriptions\ndescription: Use this skill when the user wants to add in-app subscriptions, a paywall, a premium plan, a free trial or a lifetime purchase to an iOS, Android, React Native, Flutter or Expo app, with RevenueDot as the subscription backend. It covers creating the catalog, installing and configuring the SDK, showing offerings, gating features on an entitlement, testing with the Test Store and connecting App Store and Google Play credentials, including when the user pastes a store key into the chat.\nlicense: MIT\n---\n\n# Add subscriptions to an app with RevenueDot\n\nRevenueDot is an open-source backend for in-app purchases that speaks RevenueCat's API. Apps use the RevenueCat SDK, pointed at a RevenueDot server with a proxy URL. The steps below set up one `pro` entitlement sold as monthly and yearly subscriptions, using the RevenueDot MCP tools.\n\nRevenueDot is not affiliated with RevenueCat, Inc.\n\n## Rules for the agent\n\n- **Do every RevenueDot step with the RevenueDot MCP tools.** They sign in with OAuth, so you never need a secret key. Do not ask the user for a secret key (`sk_...`), and do not call the REST API with one.\n- **Store credentials are entered by the user in the dashboard.** Never ask the user to paste an App Store key or a Google service account into the chat.\n- **If the user pastes a key into the chat anyway,** do not repeat any part of it (not even the key ID or issuer ID), store it or pass it to a tool, and do not tell them to upload that key. Tell them it is now exposed, in this order: revoke it (an App Store key in App Store Connect > Users and Access > Integrations, a Google service account key in the Google Cloud console, a RevenueDot or RevenueCat secret key on that dashboard's API keys page), create a new one, and enter only the new one in the RevenueDot dashboard (Apps, then the app, for store keys).\n- Only the public SDK key (`test_`, `appl_`, `goog_`) goes into the app. It is public and ships inside the app.\n- Ask the user for the product ids they created (or will create) in App Store Connect and Play Console. Store product ids must match exactly.\n- Run the check at the end of each phase before moving on.\n\n## What you need\n\n1. **The RevenueDot connector connected.** In Claude, that is the RevenueDot connector or this plugin's MCP server, `https://mcp.revenuedot.app/claude/mcp`. Other clients use `https://mcp.revenuedot.app/mcp`. Connecting opens a RevenueDot sign-in (OAuth): the user picks the project and **read and change** access. There is nothing to paste.\n2. A RevenueDot account. For RevenueDot Cloud, sign up at https://app.revenuedot.app (free plan); the SDK's proxy URL is `https://api.revenuedot.app`. To run your own server, use the `self-host` skill first.\n3. The app's bundle id (iOS) and package name (Android).\n\nEvery tool takes an optional `project_id`. Leave it out: the connection has one project.\n\n## Phase 1: Check the connection\n\nCall `list-projects`. If the tools are missing, ask the user to connect the RevenueDot connector (Claude: Settings > Connectors, or `/mcp` in Claude Code) and sign in.\n\n**Check:** `list-projects` returns the project.\n\n## Phase 2: Create the apps\n\nOne app per store, with `create-app`:\n\n1. `type: \"test_store\"`, `name: \"Test Store\"`. It lets you buy without any App Store or Google Play account. Its key only works in debug builds.\n2. `type: \"app_store\"`, `name: \"MyApp (iOS)\"`, `bundle_id: \"com.example.myapp\"`.\n3. `type: \"play_store\"`, `name: \"MyApp (Android)\"`, `package_name: \"com.example.myapp\"`.\n\nIf the connection has no `create-app` tool (some assistants offer fewer tools), ask the user to add the apps in the dashboard under **Apps**.\n\nReal store purchases also need, per store app:\n- **Credentials.** The user enters them in the dashboard (Apps > the app): the App Store In-App Purchase key (.p8 file, key ID, issuer ID), or a Google Play service account JSON with the \"View financial data\" permission. Then call `verify-store-credentials` with the `app_id`; it answers `\"status\":\"valid\"` when Apple or Google accept them.\n- **Store notifications.** `get-app-store-settings` with the `app_id` returns `notification_url`. The user pastes it into App Store Connect > App Information > App Store Server Notifications (Production and Sandbox), or adds it as a **push** subscription to the Pub/Sub topic set in Play Console > Monetization setup.\n\n**Check:** `list-apps` lists the apps. Note each app `id`.\n\n## Phase 3: Create the catalog\n\nCreate each product once per app, with the same store identifier the store uses:\n\n1. **Products:** `create-product` per app, with `app_id`, `store_identifier: \"pro_monthly\"`, `type: \"subscription\"`, `display_name: \"Pro monthly\"` and `subscription_duration: \"P1M\"`. Repeat for `pro_annual` with `\"P1Y\"`. `type` is one of `subscription`, `one_time`, `consumable`, `non_consumable`, `non_renewing_subscription`. For Test Store products the duration sets the subscription period.\n2. **Entitlement:** `create-entitlement` with `lookup_key: \"pro\"` and `display_name: \"Pro access\"`. Then `attach-products-to-entitlement` with `entitlement_id: \"pro\"` and every product id from step 1.\n3. **Offering:** `create-offering` with `lookup_key: \"default\"`, `display_name: \"Standard plans\"` and `is_current: true`.\n4. **Packages:** `create-packages` with the offering id, `lookup_key: \"$rc_monthly\"`, `display_name: \"Monthly\"` and `position: 0`; again with `$rc_annual`, `\"Annual\"` and `position: 1`. Then `attach-products-to-package` for each package, with one product per app (the Test Store, iOS and Android monthly products in `$rc_monthly`).\n\n**Check:** `list-offerings` shows `default` with `is_current: true`, and each package lists one product per app.\n\n## Phase 4: Get the public SDK keys\n\nCall `list-public-api-keys` with each `app_id`. `items[0].key` is the key: `test_...` for the Test Store app, `appl_...` for iOS, `goog_...` for Android. Without that tool, the user copies the key from the app's page in the dashboard.\n\n`get-app-store-settings` returns `api_origin`: the server's public URL, which is the SDK's proxy URL. For RevenueDot Cloud it is `https://api.revenuedot.app`.\n\n**Check:** you have one key per app.\n\n## Phase 5: Install and configure the SDK\n\nRevenueDot's examples use RevenueCat's published SDKs. Install the SDK the usual way:\n\n| Platform | Package (version the examples use) |\n|---|---|\n| iOS (Swift Package Manager) | `https://github.com/RevenueCat/purchases-ios-spm.git`, from `5.91.0`, product `RevenueCat` |\n| Android (Gradle) | `implementation(\"com.revenuecat.purchases:purchases:10.24.0\")` |\n| React Native, Expo | `npm install react-native-purchases` (examples: `^10.10.2`) |\n| Flutter | `purchases_flutter: ^10.13.2` in `pubspec.yaml` |\n\nConfigure once at app start. Set the proxy URL **before** `configure`:\n\n```swift\n// iOS: App init\nimport RevenueCat\nPurchases.proxyURL = URL(string: \"https://revenuedot.example.com\")!\nPurchases.configure(with: Configuration.Builder(withAPIKey: \"appl_...\")\n    .with(entitlementVerificationMode: .disabled)   // RevenueDot does not sign with RevenueCat's key\n    .build())\n```\n\n```kotlin\n// Android: Application.onCreate\nPurchases.proxyURL = URL(\"https://revenuedot.example.com\")\nPurchases.configure(\n    PurchasesConfiguration.Builder(this, \"goog_...\")\n        .entitlementVerificationMode(EntitlementVerificationMode.DISABLED)\n        .build(),\n)\n```\n\n```ts\n// React Native / Expo: verification is disabled by default\nimport Purchases from \"react-native-purchases\";\nawait Purchases.setProxyURL(\"https://revenuedot.example.com\");\nPurchases.configure({ apiKey: Platform.OS === \"ios\" ? \"appl_...\" : \"goog_...\" });\n```\n\n```dart\n// Flutter (iOS and Android; Flutter web cannot use a proxy URL yet). Verification is disabled by default.\nawait Purchases.setProxyURL('https://revenuedot.example.com');\nawait Purchases.configure(PurchasesConfiguration(Platform.isIOS ? 'appl_...' : 'goog_...'));\n```\n\nLocal development: the iOS simulator reaches the computer as `http://localhost:8787`, the Android emulator as `http://10.0.2.2:8787`. A real phone needs an `https://` URL.\n\n**Check:** the app starts and the SDK debug log shows requests to the RevenueDot host, not `api.revenuecat.com`.\n\n## Phase 6: Show the paywall and gate on the entitlement\n\nLoad the current offering, list its packages, buy one, and check the `pro` entitlement:\n\n| Step | iOS (async) | Android (coroutines) | React Native | Flutter |\n|---|---|---|---|---|\n| Offerings | `try await Purchases.shared.offerings()` then `.current?.availablePackages` | `Purchases.sharedInstance.awaitOfferings()` | `await Purchases.getOfferings()` | `await Purchases.getOfferings()` |\n| Buy | `try await Purchases.shared.purchase(package: pkg)` | `Purchases.sharedInstance.awaitPurchase(PurchaseParams.Builder(activity, pkg).build())` | `await Purchases.purchasePackage(pkg)` | `await Purchases.purchase(PurchaseParams.package(pkg))` |\n| Restore | `try await Purchases.shared.restorePurchases()` | `Purchases.sharedInstance.awaitRestore()` | `await Purchases.restorePurchases()` | `await Purchases.restorePurchases()` |\n| Is pro | `customerInfo.entitlements[\"pro\"]?.isActive == true` | `customerInfo.entitlements.active.containsKey(\"pro\")` | `customerInfo.entitlements.active[\"pro\"] !== undefined` | `customerInfo.entitlements.active[\"pro\"] != null` |\n| Updates | `for await info in Purchases.shared.customerInfoStream` | `UpdatedCustomerInfoListener` | `Purchases.addCustomerInfoUpdateListener(fn)` | `Purchases.addCustomerInfoUpdateListener(fn)` |\n\n- Treat a cancelled purchase as a normal outcome, not an error: iOS `result.userCancelled` or `ErrorCode.purchaseCancelledError`; Android `PurchasesTransactionException` with `userCancelled == true`; React Native `PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR`; Flutter `PurchasesErrorCode.purchaseCancelledError`.\n- Always offer **Restore purchases** on the paywall; App Review requires it.\n- Call `logIn(<your user id>)` when the user signs in, so purchases follow the account.\n\nComplete paywall screens per platform: https://github.com/revenuedot/examples/tree/main/mobile\n\n**Check:** the paywall lists the Monthly and Yearly packages.\n\n## Phase 7: Test with the Test Store\n\n1. Configure the app with the `test_` key in a **debug** build. Release builds with a `test_` key stop on purpose.\n2. Tap a package, then **Test valid purchase** in the Test Store dialog.\n3. Confirm on the server: `get-customer` with the app user id shows `pro` in `active_entitlements`.\n4. To test lifecycles without an app, call `create-test-purchase` with `app_user_id`, `product_id` (the product id or the Test Store store identifier, such as `pro_monthly`) and `scenario`. `scenario` is one of `purchase`, `trial`, `trial_conversion`, `renewal`, `cancel`, `billing_issue`, `refund`, `expire`. `offset_days` starts it in the past.\n5. To unlock `pro` for a tester without a purchase, use `grant-customer-entitlement` (and `revoke-customer-entitlement` to undo).\n\n**Check:** the app shows `pro` active, and `get-customer` lists the `pro` entitlement with an `expires_at` about one period ahead.\n\nKnown limits today:\n- Test Store products show $0.00 unless the product has a price. The user sets it in the dashboard: **Products**, the product, **Test Store price** (for example 9.99 USD). Each Test Store product has one price.\n- In Expo Go and on the web, `react-native-purchases` only accepts `test_` and `rcb_` keys. Real store purchases need a development build, made with the project's own Expo CLI (`expo run:ios` / `expo run:android`).\n\n## Next\n\n- Send purchase events to the user's backend: `create-webhook-integration` with a `name` and the backend's `url`. The signing secret is returned once: tell the user to store it in their backend's secret settings right away.\n- Moving an app that already uses RevenueCat: use the `migrate-from-revenuecat` skill.\n"
}

SHA-256 of public snapshot: ab1128526a0ee2bd91041bdb9c09aedd9c4972461536f97db597505e30b09e64