Skip to content

Find a component

Loading the directory…

↑ ↓ to select · Enter to open · Esc to closeBrowse all →
Contributor login

Revenuecat

Mobile subscription synchronization

Bijection source
git clone https://github.com/bijectionhq/revenuecat.git
cd revenuecat
git checkout 098510f8a09cc1f14015ce6c440a9e95c6a0c05f

Bijection repository access is required. Request access.

Description

Mobile subscription synchronization

Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.

Available to evaluate

Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.

Getting started

  1. Get the source at the revision above and read its package documentation.
  2. Check the required Bijection SDK version, dependencies, and external service credentials.
  3. Follow the package setup instructions and the guide to using components.
  4. Exercise the component in your deployment according to your project’s verification rules.

Open source

Maintained source: bijectionhq/revenuecat. Based on ramonclaudio/convex-revenuecat. License: Apache-2.0. Consult the repository for license terms, notices, and contributor attribution.

From the upstream publisher

Convex component that mirrors RevenueCat subscription state. Webhook + REST sync with lifecycle hooks for entitlement transitions.

Upstream overview

From the upstream package documentation.

A Convex component that processes RevenueCat webhooks to maintain subscription state in your Convex database. It provides real-time entitlement checking by syncing all 18 RevenueCat webhook events and storing customer, subscription, and entitlement data. You can query active subscriptions and check user permissions directly from Convex without polling external APIs.

Use cases

From the upstream package documentation.

  • Check premium feature access - Query hasEntitlement() to gate features based on active subscriptions, with real-time updates when subscriptions change
  • Build subscription dashboards - Display user subscription status, billing grace periods, and entitlement history using Convex's reactive queries
  • Handle subscription lifecycle - Automatically sync cancellations, renewals, refunds, and product changes from RevenueCat without manual API calls
  • Manage user transfers - Track when subscriptions move between users and maintain entitlement continuity during account migrations
  • A/B test subscription flows - Access experiment enrollment data from RevenueCat to personalize pricing and features based on test variants

How it works

From the upstream package documentation.

The component registers a webhook handler that processes all RevenueCat events and stores them in structured Convex tables. You mount the webhook at /webhooks/revenuecat using the provided httpHandler() and secure it with a shared authorization header. RevenueCat sends events like purchases, cancellations, and renewals to your Convex deployment.

The RevenueCat class provides query methods like hasEntitlement(), getActiveSubscriptions(), and getCustomer() that read from the synced database tables. The component handles edge cases correctly - cancellations don't immediately revoke access, grace periods maintain entitlements, and lifetime purchases never expire.

All webhook events are processed idempotently and stored in the webhookEvents table for debugging. The component includes built-in rate limiting and automatic cleanup of old events. You query entitlement state using standard Convex patterns, getting real-time reactivity when subscription status changes.

Upstream package FAQ

Does this component handle RevenueCat webhook retries correctly?

Yes, convex-revenuecat processes all webhook events idempotently, meaning duplicate webhook deliveries won't corrupt your data. It also includes built-in rate limiting at 100 requests per minute per app to protect against webhook abuse.

What subscription data does this store in my Convex database?

The convex-revenuecat component stores entitlements, subscription status, subscriber attributes, and A/B test enrollments. Webhook payloads are stored as-is while providing typed API methods for accessing the subscription state.

How does this handle subscription cancellations and pauses?

convex-revenuecat correctly handles edge cases where cancellation keeps access until the expiration date and paused subscriptions don't immediately revoke access. It processes all 18 RevenueCat webhook event types with proper business logic.

Can I query subscription status reactively with this component?

Yes, since convex-revenuecat stores all subscription data in Convex tables, you can use standard useQuery hooks to reactively check entitlements. Your UI automatically updates when webhook events change subscription state.

Use with agents and CLI

Give your coding agent the component’s source identity and setup guidance.

agent-prompt.md
Help me integrate Revenuecat with Bijection.

Read: https://bijection.com/components/revenuecat/SKILL.md

Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.

Upstream README.md

View repository ↗

Documentation from ramonclaudio/convex-revenuecat, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.

What This Component Does

This component receives RevenueCat webhooks and maintains subscription state in your Convex database. Use it to:

  • Check if users have active entitlements (e.g., "premium" access)
  • Query subscription status with Convex's real-time reactivity
graph LR
    A[RevenueCat] -->|webhooks| B[Component]
    B -->|writes| C[(Convex DB)]
    C -->|queries| D[Your App]

This is not a replacement for the RevenueCat SDK. Use their SDK in your client app for purchases. This component handles the server-side state that webhooks provide.

[!TIP] Webhook timing: After a purchase completes in the SDK, there's a delay before RevenueCat sends the webhook (usually seconds, occasionally longer). During this window, hasEntitlement() returns false. Once the webhook arrives, Convex's real-time sync updates your UI. No polling needed.

Features

  • Webhook Processing: Idempotent handling of all 18 RevenueCat webhook events
  • Convex Integration: Data stored in Convex tables with real-time reactivity
  • Correct Edge Cases: Cancellation keeps access until expiration, pause doesn't revoke, etc.
  • Rate Limiting: Built-in protection against webhook abuse (100 req/min per app)
  • Subscriber Attributes: Stores customer attributes from webhooks
  • Experiment Tracking: Tracks A/B test enrollments
  • TypeScript: Typed API methods (webhook payloads stored as-is)

Prerequisites

Installation

npm install convex-revenuecat

Quick Start

1. Configure the Component

// convex/convex.config.ts
import { defineApp } from "convex/server";
import revenuecat from "convex-revenuecat/convex.config";

const app = defineApp();
app.use(revenuecat);

export default app;

2. Mount the Webhook Handler

// convex/http.ts
import { httpRouter } from "convex/server";
import { RevenueCat } from "convex-revenuecat";
import { components } from "./_generated/api";

const http = httpRouter();

const revenuecat = new RevenueCat(components.revenuecat, {
  REVENUECAT_WEBHOOK_AUTH: process.env.REVENUECAT_WEBHOOK_AUTH,
});

http.route({
  path: "/webhooks/revenuecat",
  method: "POST",
  handler: revenuecat.httpHandler(),
});

export default http;

3. Set Up Environment Variables

Generate a secure random string for webhook authorization:

openssl rand -base64 32

Add it to your Convex deployment:

npx convex env set REVENUECAT_WEBHOOK_AUTH "your-generated-secret"

For local development, add to your .env.local file instead.

4. Configure RevenueCat Webhooks

  1. Open the RevenueCat Dashboard
  2. Select your project
  3. Go to Project Settings → Integrations → Webhooks
  4. Click + New
  5. Configure the webhook:
FieldValue
NameConvex (or any identifier)
Webhook URLhttps://<your-deployment>.convex.site/webhooks/revenuecat
Authorization headerThe secret you generated in step 3
  1. Click Save

Find your Convex deployment URL in the Convex Dashboard under your project's Settings → URL & Deploy Key.

5. Test the Webhook

  1. In RevenueCat, go to your webhook configuration
  2. Click Send Test Event
  3. Verify the event was received:
npx convex logs

You should see a log entry showing the TEST event was processed.

If the test fails, check Troubleshooting below.

Usage

Check Entitlements

import { query } from "./_generated/server";
import { components } from "./_generated/api";
import { RevenueCat } from "convex-revenuecat";
import { v } from "convex/values";

const revenuecat = new RevenueCat(components.revenuecat);

export const checkPremium = query({
  args: { appUserId: v.string() },
  returns: v.boolean(),
  handler: async (ctx, args) => {
    return await revenuecat.hasEntitlement(ctx, {
      appUserId: args.appUserId,
      entitlementId: "premium",
    });
  },
});

Get Active Subscriptions

export const getSubscriptions = query({
  args: { appUserId: v.string() },
  handler: async (ctx, args) => {
    return await revenuecat.getActiveSubscriptions(ctx, {
      appUserId: args.appUserId,
    });
  },
});

Centralizing Access

Create a module to avoid instantiating RevenueCat in every file:

// convex/revenuecat.ts
import { RevenueCat } from "convex-revenuecat";
import { components } from "./_generated/api";

export const revenuecat = new RevenueCat(components.revenuecat, {
  REVENUECAT_WEBHOOK_AUTH: process.env.REVENUECAT_WEBHOOK_AUTH,
});

[!IMPORTANT] ID matching is critical:

  • The app_user_id you pass to Purchases.logIn() must match what you query with hasEntitlement(). Use a consistent identifier (e.g., your Convex user ID).
  • The entitlementId parameter (e.g., "premium") must match exactly what you configured in the RevenueCat dashboard.

API Reference

Query Behavior

  • Missing users: Queries return empty arrays or null (never throw). Use this for loading states.
  • Billing issues: During grace periods, hasEntitlement() returns true and getActiveSubscriptions() includes the subscription.
  • Lifetime purchases: Subscriptions without expirationAtMs are always considered active.

Constructor

const revenuecat = new RevenueCat(components.revenuecat, {
  REVENUECAT_WEBHOOK_AUTH?: string, // Webhook authorization header
});

Query Methods

MethodDescription
hasEntitlement(ctx, { appUserId, entitlementId })Check if user has active entitlement
getActiveEntitlements(ctx, { appUserId })Get all active entitlements
getAllEntitlements(ctx, { appUserId })Get all entitlements (active and inactive)
getActiveSubscriptions(ctx, { appUserId })Get all active subscriptions (includes grace period)
getAllSubscriptions(ctx, { appUserId })Get all subscriptions
getSubscriptionsInGracePeriod(ctx, { appUserId })Get subscriptions currently in billing grace period
isInGracePeriod(ctx, { originalTransactionId })Check grace period status for a subscription
getCustomer(ctx, { appUserId })Get customer record
getExperiment(ctx, { appUserId, experimentId })Get user's variant for a specific experiment
getExperiments(ctx, { appUserId })Get all experiments user is enrolled in
getTransfer(ctx, { eventId })Get transfer event by ID
getTransfers(ctx, { limit? })Get recent transfers (default limit: 100)
getInvoice(ctx, { invoiceId })Get invoice by ID
getInvoices(ctx, { appUserId })Get all invoices for user
getVirtualCurrencyBalance(ctx, { appUserId, currencyCode })Get balance for a specific currency
getVirtualCurrencyBalances(ctx, { appUserId })Get all currency balances for user
getVirtualCurrencyTransactions(ctx, { appUserId, currencyCode? })Get virtual currency transactions

This component is a read-only sync layer. To grant promotional entitlements, use the RevenueCat API directly — the webhook will sync the state automatically.

Webhook Events

EventBehavior
INITIAL_PURCHASECreates subscription, grants entitlements
RENEWALExtends entitlement expiration
CANCELLATIONKeeps entitlements until expiration
EXPIRATIONRevokes entitlements
BILLING_ISSUEKeeps entitlements during grace period
SUBSCRIPTION_PAUSEDDoes not revoke entitlements
SUBSCRIPTION_EXTENDEDExtends expiration (customer support)
TRANSFERMoves entitlements between users
UNCANCELLATIONClears cancellation status
PRODUCT_CHANGEUpdates subscription product
NON_RENEWING_PURCHASEGrants entitlements for one-time purchase
TEMPORARY_ENTITLEMENT_GRANTGrants temp access during store outage
REFUNDRevokes entitlements immediately
REFUND_REVERSEDRestores entitlements after refund undone
TESTDashboard test event (logged only)
INVOICE_ISSUANCEWeb Billing invoice created
VIRTUAL_CURRENCY_TRANSACTIONVirtual currency adjustment
EXPERIMENT_ENROLLMENTA/B test enrollment (tracked)
SUBSCRIBER_ALIASMigrates entitlements/subscriptions from anonymous to real user ID (deprecated)

[!IMPORTANT] CANCELLATION does not revoke entitlements. Users keep access until EXPIRATION.

Database Schema

The component creates ten tables:

TablePurpose
customersUser identity, aliases, and subscriber attributes
subscriptionsPurchase records with product and payment details
entitlementsAccess control state (active/inactive, expiration)
experimentsA/B test enrollments from RevenueCat experiments
transfersEntitlement transfer records between users
invoicesWeb Billing invoice records
virtualCurrencyBalancesVirtual currency balances per user per currency
virtualCurrencyTransactionsIndividual virtual currency adjustments
webhookEventsEvent log for idempotency and debugging (30-day retention)
rateLimitsWebhook endpoint rate limiting (100 req/min per app)

Limitations

  • No initial sync — Existing subscribers before webhook setup won't appear until they trigger a new event (renewal, cancellation, etc.)
  • Webhook-driven only — Data comes exclusively from webhooks; no API polling or backfill mechanism
  • Raw payload storage — Webhook payloads are stored as-is for debugging. These may contain subscriber attributes or other data you've configured in RevenueCat. Events are auto-deleted after 30 days.
  • Production usage — Core entitlement checking (hasEntitlement) is production-tested. Other query methods (transfers, invoices, virtual currency) are unit-tested but not yet battle-tested in production apps.

Testing

Register the component in your tests:

import { convexTest } from "convex-test";
import revenuecatTest from "convex-revenuecat/test";

function initConvexTest() {
  const t = convexTest();
  revenuecatTest.register(t);
  return t;
}

test("check premium access", async () => {
  const t = initConvexTest();
  // Your test code here
});

Example

See the example/ directory for a complete working example with:

  • Component registration
  • Webhook handler setup
  • Query and mutation examples

Troubleshooting

The authorization header doesn't match.

  1. Verify the environment variable is set:

    npx convex env get REVENUECAT_WEBHOOK_AUTH
    
  2. Ensure the value in RevenueC

[README truncated for prompt length]

Keywords