This repository presents a schematic implementation of how RevenueCat subscription management has been integrated with Supabase in Gurwi. The code is made publicly available as part of RevenueCat's Shipaton 2025 initiative to support and assist fellow developers in their implementation journey.
Important Note: The code presented here may become outdated over time and may not accurately represent how this implementation is currently maintained within Gurwi's production environment.
This repository demonstrates how to build a complete subscription system using:
- Flutter for mobile/web applications
- RevenueCat for subscription management and cross-platform purchases
- Supabase for backend database and webhook handling
- Deno Edge Functions for processing RevenueCat webhooks
- π Real-time subscription sync between RevenueCat and Supabase
- π± Cross-platform support (iOS, Android, Web)
- π― Free trial management with automatic detection
- π Secure webhook validation
- πΎ Persistent subscription state in Supabase
- π¨ Clean Architecture implementation
- π Automatic subscription renewal handling
- π Comprehensive subscription analytics
- Architecture Overview
- Prerequisites
- Database Schema
- Backend Setup
- Flutter Integration
- RevenueCat Configuration
- Webhook Implementation
- Testing
- Production Deployment
- Troubleshooting
graph TB
A[Flutter App] -->|Purchase Request| B[RevenueCat SDK]
B -->|Store Communication| C[App Store/Google Play]
C -->|Purchase Confirmation| B
B -->|Webhook| D[Supabase Edge Function]
D -->|Update Subscription| E[Supabase Database]
A -->|Check Subscription| E
E -->|Subscription Status| A
- Flutter App: Handles UI and subscription purchases
- RevenueCat: Manages subscriptions across platforms
- Supabase Edge Function: Processes webhook events
- Supabase Database: Stores subscription data
- App Stores: Process actual payments
- Flutter SDK (3.10+)
- Supabase account and project
- RevenueCat account and app configuration
- iOS/Android developer accounts (for testing)
dependencies:
flutter:
sdk: flutter
purchases_flutter: ^6.21.0
supabase_flutter: ^2.3.4
flutter_bloc: ^8.1.3
equatable: ^2.0.5
fpdart: ^1.1.0This condensed setup replaces the standalone SETUP.md to avoid duplication. Use it as a quick start; deeper explanations remain in sections below.
git clone <your-repo-url>
cd flutter-revenuecat-supabase
flutter pub get
cp env.example .env# Install CLI (if needed)
npm install -g supabase
# Initialize and link
supabase init
supabase link --project-ref <your-project-ref>
# Apply schema and optional seed
supabase db push
supabase db reset --with-seed # optionalsupabase functions deploy revenue-cat-webhook
supabase secrets set REVENUE_CAT_WEBHOOK_SECRET=your_webhook_secret_here- Create your app in the RevenueCat dashboard
- Create products (iOS/Android) and a "premium" entitlement
- Set webhook URL:
https://<your-project>.supabase.co/functions/v1/revenue-cat-webhook - Enable the desired event types
# Supabase
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
# RevenueCat
REVENUE_CAT_IOS_API_KEY=appl_your_ios_key
REVENUE_CAT_ANDROID_API_KEY=goog_your_android_key
REVENUE_CAT_WEBHOOK_SECRET=your_webhook_secret# Webhook logs
supabase functions logs revenue-cat-webhook --follow
# Run app
flutter run --debug
# Verify DB state (example)
# SELECT * FROM commerce.user_subscriptions_active_view; -- in Supabase SQL editor# iOS
flutter build ipa --release --obfuscate --split-debug-info=build/app/outputs/symbols
# Android
flutter build appbundle --release --obfuscate --split-debug-info=build/app/outputs/symbolsThis project follows Clean Architecture principles with a clear separation of concerns:
lib/
βββ core/
β βββ services/
β βββ revenue_cat_service.dart # RevenueCat SDK wrapper
βββ features/
β βββ commerce/ # Subscription feature
β βββ cubits/
β β βββ subscription_cubit.dart # State management
β βββ models/
β β βββ subscription.dart # Data models
β βββ pages/
β β βββ subscription_page.dart # UI pages
β βββ repositories/
β βββ subscription_repository.dart # Data access layer
βββ main.dart # App entry point
supabase/
βββ functions/
β βββ revenue-cat-webhook/
β βββ index.ts # Webhook handler
βββ migrations/
β βββ 001_create_commerce_schema.sql # Database schema
βββ seed.sql # Sample data
- Core Services: Shared services across the app (RevenueCat service)
- Feature-based Structure: Each feature (commerce) has its own domain
- Clean Architecture: Separation between UI, business logic, and data layers
- Cubit State Management: Reactive state management with flutter_bloc
Defines what features users get access to.
CREATE TABLE commerce.entitlements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
description TEXT,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT now()
);Maps RevenueCat products to your internal subscription plans.
CREATE TABLE commerce.gateway_subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entitlement_id UUID REFERENCES commerce.entitlements(id),
name TEXT NOT NULL,
duration_months INTEGER NOT NULL,
payment_gateway TEXT NOT NULL DEFAULT 'revenuecat',
gateway_store TEXT, -- 'app_store', 'play_store'
gateway_product_id TEXT NOT NULL, -- RevenueCat product ID
price DECIMAL NOT NULL,
currency TEXT NOT NULL,
is_test BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT now()
);Stores actual user subscription data.
CREATE TABLE commerce.user_subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
entitlement_id UUID REFERENCES commerce.entitlements(id),
subscription_product_id UUID REFERENCES commerce.gateway_subscriptions(id),
gateway_subscription_id TEXT NOT NULL, -- RevenueCat transaction ID
gateway_store TEXT,
status TEXT NOT NULL, -- 'active', 'trial', 'cancelled', 'expired'
start_date TIMESTAMP NOT NULL,
end_date TIMESTAMP NOT NULL,
renews_at TIMESTAMP,
cancelled_at TIMESTAMP,
is_trial BOOLEAN DEFAULT false,
trial_end_date TIMESTAMP,
customer_portal JSONB DEFAULT '{}',
created_at TIMESTAMP DEFAULT now(),
updated_at TIMESTAMP DEFAULT now()
);Convenient view for checking active subscriptions.
CREATE VIEW commerce.user_subscriptions_active_view AS
SELECT
us.*,
u.email,
gs.name as subscription_name,
gs.duration_months
FROM commerce.user_subscriptions us
JOIN auth.users u ON us.user_id = u.id
JOIN commerce.gateway_subscriptions gs ON us.subscription_product_id = gs.id
WHERE us.end_date > now()
AND us.status IN ('active', 'trial');Create a new edge function to handle RevenueCat webhooks:
supabase functions new revenue-cat-webhookThe webhook handler (supabase/functions/revenue-cat-webhook/index.ts) processes RevenueCat events and updates your Supabase database:
Key Features:
- Webhook signature validation for security
- Handles all major RevenueCat events (purchase, renewal, cancellation, expiration)
- Maps RevenueCat products to database subscriptions
- Supports trial periods and grace periods
- Comprehensive error handling and logging
Event Processing:
INITIAL_PURCHASE: Creates new subscription recordsRENEWAL: Updates subscription end datesCANCELLATION: Marks subscriptions as cancelledEXPIRATION: Updates expired subscriptionsBILLING_ISSUE: Handles payment failures
The webhook ensures your database stays synchronized with RevenueCat's subscription state in real-time.
supabase functions deploy revenue-cat-webhookThe Flutter integration consists of several key components that work together to manage subscriptions:
-
RevenueCat Service (
lib/core/services/revenue_cat_service.dart)- Singleton wrapper around RevenueCat SDK
- Handles initialization, purchases, and customer info
- Includes trial eligibility checking and restoration
-
Subscription Repository (
lib/features/commerce/repositories/subscription_repository.dart)- Data access layer for Supabase subscription data
- Queries active subscriptions and entitlements
- Handles subscription updates and cancellations
-
Subscription Cubit (
lib/features/commerce/cubits/subscription_cubit.dart)- State management with flutter_bloc
- Coordinates between RevenueCat and Supabase data
- Manages subscription states: Initial, Loading, Active, Trial, Inactive, Error
-
Subscription Models (
lib/features/commerce/models/subscription.dart)- Data models for subscriptions, entitlements, and gateway products
- JSON serialization for Supabase integration
-
UI Pages (
lib/features/commerce/pages/subscription_page.dart)- Responsive subscription management interface
- Displays different views based on subscription state
- Handles purchase flows and subscription management
- User opens subscription page β Cubit loads current state
- RevenueCat provides available packages and trial info
- Supabase provides current subscription status
- UI displays appropriate view (active, trial, inactive, etc.)
- Purchase triggers RevenueCat β Webhook β Supabase β UI update
In RevenueCat dashboard:
- Create your app
- Add products (e.g., "monthly_premium", "annual_premium")
- Configure entitlements
- Set up webhook URL to your Supabase edge function
Set your webhook URL in RevenueCat:
https://your-project.supabase.co/functions/v1/revenue-cat-webhook
Add your webhook secret to Supabase environment variables.
Insert sample data into your database:
-- Insert entitlement
INSERT INTO commerce.entitlements (id, name, description)
VALUES ('06f8ae77-2719-45cf-8c33-dc4ed68e1c25', 'premium', 'Premium features access');
-- Insert gateway subscriptions
INSERT INTO commerce.gateway_subscriptions (entitlement_id, name, duration_months, payment_gateway, gateway_product_id, price, currency)
VALUES
('06f8ae77-2719-45cf-8c33-dc4ed68e1c25', 'Monthly Premium', 1, 'revenuecat', 'monthly_premium', 9.99, 'USD'),
('06f8ae77-2719-45cf-8c33-dc4ed68e1c25', 'Annual Premium', 12, 'revenuecat', 'annual_premium', 99.99, 'USD');Use RevenueCat's sandbox environment for testing:
// Enable debug mode
await Purchases.setLogLevel(LogLevel.debug);
// Use sandbox API keys during developmentCheck that webhook events properly update your Supabase database:
SELECT * FROM commerce.user_subscriptions
WHERE user_id = 'your-test-user-id';Set up production environment variables in Supabase:
# RevenueCat webhook secret
REVENUE_CAT_WEBHOOK_SECRET=your_webhook_secret
# Production API keys
REVENUE_CAT_IOS_API_KEY=your_ios_production_key
REVENUE_CAT_ANDROID_API_KEY=your_android_production_keyEnsure Row Level Security is enabled:
-- Users can only see their own subscriptions
CREATE POLICY "Users can view own subscriptions" ON commerce.user_subscriptions
FOR SELECT USING (auth.uid() = user_id);
-- Service role can manage all subscriptions (for webhooks)
CREATE POLICY "Service role can manage subscriptions" ON commerce.user_subscriptions
FOR ALL USING (auth.role() = 'service_role');Set up monitoring for webhook failures and subscription issues.
- Webhook not firing: Check RevenueCat webhook URL and authorization header
- Database not updating: Review Supabase Edge Function logs
- Purchase failures: Verify product IDs match between RevenueCat and database
- State not updating: Ensure user authentication before checking subscriptions
# Check webhook logs
supabase functions logs revenue-cat-webhook --follow
# Test purchases in sandbox
flutter run --debug
# Verify database state
# In Supabase dashboard: SELECT * FROM commerce.user_subscriptions_active_view;This project is licensed under the MIT License - see the LICENSE file for details.