Skip to content

Implement subscriptions in Flutter with RevenueCat and Supabase: webhooks/Edge Functions, trials, renewals and cancellations, active view in the database, and sample UI under Clean Architecture.

Notifications You must be signed in to change notification settings

camilopenalver/flutter-revenuecat-supabase

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Flutter RevenueCat + Supabase Integration

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.

πŸš€ Overview

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

✨ Features

  • πŸ”„ 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

πŸ“‹ Table of Contents

  1. Architecture Overview
  2. Prerequisites
  3. Database Schema
  4. Backend Setup
  5. Flutter Integration
  6. RevenueCat Configuration
  7. Webhook Implementation
  8. Testing
  9. Production Deployment
  10. Troubleshooting

πŸ—οΈ Architecture Overview

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
Loading

Key Components

  1. Flutter App: Handles UI and subscription purchases
  2. RevenueCat: Manages subscriptions across platforms
  3. Supabase Edge Function: Processes webhook events
  4. Supabase Database: Stores subscription data
  5. App Stores: Process actual payments

πŸ“š Prerequisites

  • Flutter SDK (3.10+)
  • Supabase account and project
  • RevenueCat account and app configuration
  • iOS/Android developer accounts (for testing)

Required Dependencies

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.0

🧰 Setup Guide (condensed)

This condensed setup replaces the standalone SETUP.md to avoid duplication. Use it as a quick start; deeper explanations remain in sections below.

1) Clone and install

git clone <your-repo-url>
cd flutter-revenuecat-supabase
flutter pub get
cp env.example .env

2) Supabase project

# 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   # optional

3) Deploy Edge Function (webhook)

supabase functions deploy revenue-cat-webhook
supabase secrets set REVENUE_CAT_WEBHOOK_SECRET=your_webhook_secret_here

4) RevenueCat configuration

  1. Create your app in the RevenueCat dashboard
  2. Create products (iOS/Android) and a "premium" entitlement
  3. Set webhook URL: https://<your-project>.supabase.co/functions/v1/revenue-cat-webhook
  4. Enable the desired event types

5) Environment variables (.env)

# 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

6) Test

# 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

7) Production

# 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/symbols

πŸ“‚ Project Structure

This 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

Architecture Layers

  • 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

πŸ—„οΈ Database Schema

Entitlements Table

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()
);

Gateway Subscriptions Table

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()
);

User Subscriptions Table

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()
);

Active Subscriptions View

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');

πŸ”§ Backend Setup

1. Create Supabase Edge Function

Create a new edge function to handle RevenueCat webhooks:

supabase functions new revenue-cat-webhook

2. Webhook Implementation

The 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 records
  • RENEWAL: Updates subscription end dates
  • CANCELLATION: Marks subscriptions as cancelled
  • EXPIRATION: Updates expired subscriptions
  • BILLING_ISSUE: Handles payment failures

The webhook ensures your database stays synchronized with RevenueCat's subscription state in real-time.

3. Deploy Edge Function

supabase functions deploy revenue-cat-webhook

πŸ“± Flutter Integration

The Flutter integration consists of several key components that work together to manage subscriptions:

Key Components

  1. 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
  2. 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
  3. 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
  4. Subscription Models (lib/features/commerce/models/subscription.dart)

    • Data models for subscriptions, entitlements, and gateway products
    • JSON serialization for Supabase integration
  5. 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

State Flow

  1. User opens subscription page β†’ Cubit loads current state
  2. RevenueCat provides available packages and trial info
  3. Supabase provides current subscription status
  4. UI displays appropriate view (active, trial, inactive, etc.)
  5. Purchase triggers RevenueCat β†’ Webhook β†’ Supabase β†’ UI update

βš™οΈ RevenueCat Configuration

1. Create Products

In RevenueCat dashboard:

  1. Create your app
  2. Add products (e.g., "monthly_premium", "annual_premium")
  3. Configure entitlements
  4. Set up webhook URL to your Supabase edge function

2. Configure Webhooks

Set your webhook URL in RevenueCat:

https://your-project.supabase.co/functions/v1/revenue-cat-webhook

Add your webhook secret to Supabase environment variables.

3. Sample Data

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');

πŸ§ͺ Testing

1. Test Purchases

Use RevenueCat's sandbox environment for testing:

// Enable debug mode
await Purchases.setLogLevel(LogLevel.debug);

// Use sandbox API keys during development

2. Verify Database Updates

Check that webhook events properly update your Supabase database:

SELECT * FROM commerce.user_subscriptions 
WHERE user_id = 'your-test-user-id';

πŸš€ Production Deployment

1. Environment Variables

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_key

2. Enable RLS Policies

Ensure 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');

3. Monitor Webhooks

Set up monitoring for webhook failures and subscription issues.

πŸ”§ Troubleshooting

Common Issues

  1. Webhook not firing: Check RevenueCat webhook URL and authorization header
  2. Database not updating: Review Supabase Edge Function logs
  3. Purchase failures: Verify product IDs match between RevenueCat and database
  4. State not updating: Ensure user authentication before checking subscriptions

Quick Debug

# 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;

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Implement subscriptions in Flutter with RevenueCat and Supabase: webhooks/Edge Functions, trials, renewals and cancellations, active view in the database, and sample UI under Clean Architecture.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published