supabase-rb-rb

Supabase Billing (supabase-billing)

supabase-billing is a provider-agnostic subscription and entitlement layer for Rails apps on Supabase — Stripe (web) and Adapty (mobile) ingestion adapters write into one canonical schema and a single `user.entitled?` API.

supabase-billing is the Ruby on Rails subscription and entitlement layer for Supabase. It ships a canonical billing schema (customers, subscriptions, plans, entitlements, usage limits), ingestion adapters for Stripe and Adapty, and a single entitled? API for gating features — so your app code never reaches for a provider SDK to ask "can this user use this feature?".

The gem builds on supabase-rails and assumes Supabase Auth is your identity layer: Current.user is set per request, RLS policies key off auth.uid(), and provider customer records are linked to auth.users.id automatically.

Starting from a starter kit?

supabase-billing is most commonly paired with the Hotwire monolith starter kit — it ships supabase-rails in :web mode with a full server-rendered app shell where entitled? gating drops naturally into controllers and views. The Inertia + React kit is also :web mode and works the same way on the Rails side; expose entitled? results through inertia_share to gate React routes.

Why supabase-billing

  • Provider-agnostic by design. Your app calls user.entitled?(:pro_export) whether the subscription was opened on the web via Stripe or in your iOS / Android app via Adapty.
  • One canonical schema, two adapters. Webhooks from each provider land in the same customers / subscriptions / subscription_items tables. Reporting, RLS, and feature gates see one shape.
  • RLS-aware out of the box. Tables ship with policies scoped to auth.uid(), so a misconfigured controller can't leak another user's subscription.
  • No reimplementation of Supabase plumbing. Session storage, JWT verification, and per-request auth come from supabase-rails. This gem only adds billing.

Providers

The entitled? API

Once a webhook has landed, gating a feature is a single call:

class ExportsController < ApplicationController
  before_action :authenticate

  def create
    return head :payment_required unless Current.user.entitled?(:pro_export)

    Export.create!(user: Current.user, format: params[:format])
  end
end

entitled? resolves the current user's active subscription, looks up the plan's entitlements, and answers true/false. Companion methods cover the common shapes — Current.user.subscribed?, Current.user.plan, and Current.user.limit(:monthly_exports) for metered features. See Entitlements for the full surface.

Dependency on supabase-rails

supabase-billing is a Rails engine that depends on supabase-rails. Before installing this gem, your app must already be wired up via rails g supabase:install and have a User model generated by rails g supabase:user_model (so users.id matches auth.users.id). The Stripe and Adapty webhook controllers reuse the Supabase request context that supabase-rails sets, and entitlement queries trust that auth.uid() is populated. The Getting Started guide walks through the required supabase-rails setup first.

Explore the reference

Project

On this page