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.propertiesReal 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
subscriptionsmigration. revenuecat-webhookdeployed with--no-verify-jwt.REVENUECAT_WEBHOOK_AUTHstored as a Supabase Edge secret.- Google Play Console app with package name
com.soarstarter.kotlin, or your renamedapplicationId. - 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.kotlinUpload 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 id | Name | Base plan |
|---|---|---|
pro_monthly | Soar Starter Pro Monthly | Monthly, auto-renewing, active |
pro_yearly | Soar Starter Pro Yearly | Yearly, auto-renewing, active |
Set prices and activate the base plans.
Create the lifetime product
In Monetize -> Products -> In-app products, create:
| Product id | Name | Type |
|---|---|---|
pro_lifetime | Soar Starter Lifetime | One-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_lifetimeRevenueCat reads localized prices and product metadata from Play.
Create the entitlement
Create one entitlement named:
proAttach 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_keyMissing 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-webhookSet 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 data | Supabase row |
|---|---|
app_user_id | subscriptions.user_id |
store = PLAY_STORE | provider = play_store |
product_id | product_id |
| Purchase span or non-renewing event | product_type = month, year, or one_time |
| Event type and trial data | status = trialing, active, canceled, or expired |
| Purchase timestamp | period_start and last_paid_at |
| Expiration or grace timestamp | period_end |
| Transaction ids | last_order_no and subscription_id |
| RevenueCat original app user id | customer_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
proentitlement contains the monthly, yearly, and lifetime products. - The current Android offering returns at least one package.
-
REVENUECAT_ANDROID_KEYis present inlocal.propertiesfor purchase builds. - RevenueCat webhook test delivery returns HTTP 200 with
success. - A signed-in sandbox purchase writes
provider = play_storetopublic.subscriptions.
