Paywall & Subscriptions

The kit sells subscriptions (and one-time purchases) through RevenueCat. You wire it with /kit-setup-paywall — the agent makes every code edit; you just create a few things in the RevenueCat and Google Play dashboards. This page is the full reference for that dashboard side, especially the release-time pieces you do once, right before you start charging real users.

TL;DR. Run /kit-setup-paywall to turn the paywall on and paste your API key. The paywall screen works in development immediately (it shows an "empty offerings" state). Before you ship to Play and charge real money, complete the release-time steps below.

How the paywall works in the kit

  • The kit's PaywallScreen wraps RevenueCat's prebuilt Paywall composable — the design, copy, prices, and call-to-action all live in the RevenueCat dashboard, not in your code. Tweak the paywall there and the app picks it up; no rebuild needed.
  • KitConfig.PAYWALL_MODESOFT (dismissible; user can skip and use the free app) or HARD (blocks the app until purchase or restore).
  • KitConfig.ENTITLEMENT_ID — the RevenueCat entitlement (default premium) that marks a user as paid. The nav flow shows the paywall only when the user doesn't hold it.
  • KitConfig.PAYWALL_ENABLED — the master switch. /kit-setup-paywall sets it; a free app turns it off and the paywall is dropped from navigation.

These are the only paywall settings in your code, and the setup command writes all of them.

What /kit-setup-paywall sets up (quick)

Running the command walks you through, in the RevenueCat dashboard:

  1. Create a project (Native Android).
  2. Add your Google Play app (your applicationId).
  3. Create the premium entitlement.
  4. Copy your Android API key (goog_…) → the agent writes it to local.properties.
  5. Pick SOFT or HARD enforcement.

When you enable the paywall, the command also adds the billing permission in app/src/main/AndroidManifest.xml:

<uses-permission android:name="com.android.vending.BILLING" />

RevenueCat / Google Play Billing needs it for purchases, and Google Play Console won't let you create in-app products until an uploaded build declares it. It's a normal permission — no runtime prompt. A free app don't need this permission.

After this the paywall screen renders on device. Real purchases need the steps below.


Before you charge real users

These pieces have to be wired before real billing works. The agent reminds you in /kit-publish-to-play.

⏳ Start these early. Two of them are slow and run in the background: the Google Play ↔ RevenueCat connection takes ~36 hours to propagate, and a build has to sit on a testing track (first review can take a few days) before products load. /kit-start-setup offers to kick both off right after your first run — say yes and keep building while the clocks tick.

Order matters. Google Play blocks product creation until a build that declares the BILLING permission is live on a track — so the build comes before products, not after. Doing it the other way round (creating products first) is the single most common thing developers get stuck on. The steps below are in the order that actually works.

1. Create your Play Console app

Create the app entry in Play Console (Create app) if you haven't. Two fields trip people up:

  • Package name must exactly match your applicationId (in app/build.gradle.kts) — it's permanent once the app is created. The agent reads it for you so you don't have to remember.
  • Free or paid: choose Free. A subscription / in-app-purchase app is a free download that charges through billing. "Paid" means an upfront price to install — and you can't switch Free → Paid after publishing.

2. Get a signed build onto a testing track (this unblocks everything)

This is what lets you create products and makes RevenueCat able to verify the Play connection.

  • Run /kit-sign-release — it builds a signed AAB (the BILLING permission is already in the manifest) and walks the internal testing upload.
  • Wait for the release to be processed, then open the tester opt-in URL (Play Console → Internal testing → Testers → Copy link) on your test device. Skipping the opt-in is the most common "offerings empty" cause.

3. Create your in-app products in Play Console

Now unblocked by step 2. Play nests subscriptions as Subscription → Base plan — and you must activate both:

  • Subscriptions: Monetize → Products → Subscriptions → Create subscription → set a permanent Product ID (e.g. premium_monthly) → add a base plan (Base plan ID, Type = Auto-renewing, billing period, price). Activate the base plan AND the subscription — an inactive one silently won't load.
  • One-time products: Monetize → Products → One-time products → create + Activate (just an ID + price).
  • Note every Product ID — you import them into RevenueCat next.

4. Connect Play to RevenueCat (service-account JSON)

RevenueCat verifies purchases by talking to Google Play on your behalf. It needs a Google service account with Play Console permissions. /kit-setup-paywall offers two ways to do the Google Cloud half:

  • Automatic (recommended) — the kit installs/uses the gcloud CLI on your machine, you sign in once (with the same Google account that owns your Play Console + Cloud project), and it creates the service account, enables the required APIs, grants the roles, and downloads the key file for you. This is RevenueCat's own automation, run locally by your AI agent. You then do just two clicks: grant the service account access in Play Console, and upload the key to RevenueCat. If you already set up Firebase, it reuses that Google Cloud project.

  • Manual — do every step yourself in the Google Cloud Console, Play Console, and RevenueCat, following RevenueCat's official guide: creating-play-service-credentials.

  • Wait ~36 hours for Google to propagate the permission before testing real purchases. It will show the Verified label once approved (often sooner — ~24 h is typical).

5. Import products, attach the entitlement, publish the paywall

The chain that actually makes a purchase unlock the app — the entitlement attach is the step everyone forgets:

  • Product catalog → Products → + New → pick Google Play → paste each Product ID. For a subscription, RevenueCat pulls its base plans in automatically.
  • Attach each product to your premium entitlement (Product catalog → Entitlements → premium → Attach products). Skip this and purchases succeed but nothing unlocks in the app.
  • Offerings → use the default offering (mark it current) → Add package for each product you want shown.
  • Paywalls → + New → select the offering → design in the visual editor → Save + Publish (a saved-but-unpublished paywall won't appear).

Testing real purchases

  • Real billing only works in a signed build on a Play Console test track (internal testing is fine), after step 4's JSON has propagated and step 2's build is live + you've opted in.
  • Add license testers in Play Console (Settings → License testing) so you can buy without being charged.
  • Before all this, the paywall showing an "offerings empty" state is normal — not a bug.

Changing things later

  • Re-run /kit-setup-paywall any time to change the mode (SOFT/HARD), API key, or entitlement — it detects what's already set and only touches what you pick.
  • Paywall design / prices / copy change in the RevenueCat dashboard, never in code.
  • Turning the paywall off later: re-run the command and choose the free-app path (sets PAYWALL_ENABLED = false).

Troubleshooting

ProblemFix
Paywall shows "offerings empty"Usually: no build on a Play track yet, or you haven't opened the tester opt-in URL (step 4). Also check the offering is published (step 3). Normal in pure local development.
Purchases don't unlock the appService-account JSON missing/just uploaded (wait ~36 h), or the product isn't attached to your offering.
Paywall design looks default/plainYou haven't created + published a paywall in RevenueCat → Paywalls.
goog_… key error on launchAPI key missing or wrong in local.properties → re-run /kit-setup-paywall.

Related: Commands Reference · Features · The Flow — 0 to Google Play