RevenueCat Setup

Configure products, offerings, webhook secrets, and entitlement sync.

This page wires Google Play products to RevenueCat, then wires RevenueCat events to the Supabase entitlement read model used by the Android app.

Setup chain

Play Console app and products
  -> RevenueCat Android app, products, entitlement, offering
  -> Supabase revenuecat-webhook
  -> REVENUECAT_ANDROID_KEY in local.properties

Real Google Play Billing purchase flows require an app uploaded to a Play testing track. A debug build installed directly from Android Studio can render the paywall, but Play products usually do not become purchasable until the package is known to Play and the tester installs through an opt-in track.

Prerequisites

  • Supabase project configured with the subscriptions migration.
  • revenuecat-webhook deployed with --no-verify-jwt.
  • REVENUECAT_WEBHOOK_AUTH stored as a Supabase Edge secret.
  • Google Play Console app with package name com.soarstarter.kotlin, or your renamed applicationId.
  • A Google Play developer account for real product setup and license testers.

Create Play products

Create the app in Play Console

Create the Play Console app with the same package name used by app/build.gradle.kts. The template default is:

com.soarstarter.kotlin

Upload an .aab to Internal testing before expecting purchase flows to work end to end.

Create subscription products

In Play Console, open Monetize -> Products -> Subscriptions and create:

Product idNameBase plan
pro_monthlySoar Starter Pro MonthlyMonthly, auto-renewing, active
pro_yearlySoar Starter Pro YearlyYearly, auto-renewing, active

Set prices and activate the base plans.

Create the lifetime product

In Monetize -> Products -> In-app products, create:

Product idNameType
pro_lifetimeSoar Starter LifetimeOne-time in-app product

Set prices for the markets you support and activate the product.

Add license testers

In Play Console, add tester Google accounts for purchase testing. Those testers also need access to the Internal testing track and should install the app from the opt-in link.

Configure RevenueCat

Create the RevenueCat project and Android app

Create a RevenueCat project, add an Android app, and set the package name to the same applicationId that Play Console sees.

Connect Google Play credentials

In RevenueCat, connect the Play service-account credentials JSON with the permissions RevenueCat requires to read products and validate purchases. Keep this JSON in RevenueCat/Google Cloud, not in the Android repo.

Add products

Add these Play products to RevenueCat:

pro_monthly
pro_yearly
pro_lifetime

RevenueCat reads localized prices and product metadata from Play.

Create the entitlement

Create one entitlement named:

pro

Attach the monthly, yearly, and lifetime products to that entitlement. This matches PurchasesController.PRO_ENTITLEMENT_ID.

Create the Android offering

Create or update the current Android offering and add the packages as monthly, annual, and lifetime. The app renders whichever subset the active offering returns.

Copy the Android SDK key

Copy the public Android SDK key into the template's local.properties:

REVENUECAT_ANDROID_KEY=goog_your_public_android_sdk_key

Missing values become an empty BuildConfig string. In that case PurchasesController.isConfigured is false and the paywall shows an unavailable info state instead of starting RevenueCat.

Wire the webhook

RevenueCat should call the deployed Supabase Edge Function:

https://<ref>.supabase.co/functions/v1/revenuecat-webhook

Set the RevenueCat webhook Authorization header to the same value stored in:

supabase secrets set REVENUECAT_WEBHOOK_AUTH=<long-random-string> --project-ref <ref>

Deploy the function from soar-kotlin/supabase:

supabase functions deploy revenuecat-webhook --no-verify-jwt --project-ref <ref>

The function runs without Supabase JWT verification because RevenueCat is not a signed-in app user. It verifies the static Authorization header, then writes with the service-role key.

The current Kotlin function returns the text body success on accepted deliveries. Some older notes may mention {"ok":true}; the deployed code is authoritative.

Event mapping

RevenueCat event dataSupabase row
app_user_idsubscriptions.user_id
store = PLAY_STOREprovider = play_store
product_idproduct_id
Purchase span or non-renewing eventproduct_type = month, year, or one_time
Event type and trial datastatus = trialing, active, canceled, or expired
Purchase timestampperiod_start and last_paid_at
Expiration or grace timestampperiod_end
Transaction idslast_order_no and subscription_id
RevenueCat original app user idcustomer_id

Unsupported stores, unmodeled event types, and non-UUID app_user_id values are ignored by design. Accepted events are idempotent because the table is unique on user_id.

Verify

  • RevenueCat can read the three Play products.
  • The pro entitlement contains the monthly, yearly, and lifetime products.
  • The current Android offering returns at least one package.
  • REVENUECAT_ANDROID_KEY is present in local.properties for purchase builds.
  • RevenueCat webhook test delivery returns HTTP 200 with success.
  • A signed-in sandbox purchase writes provider = play_store to public.subscriptions.

On this page