Stripe Integration & Subscriptions
Relevant source files
The following files were used as context for generating this wiki page:
Stripe Integration & Subscriptions manages the monetization and feature gating of the Oddity 1 platform. The system facilitates the transition of users from the Free tier to the Standard (Pro) tier, unlocking advanced features like custom PDF export subtitles and higher annotation limits.
The integration spans the backend API for session management and webhooks, the dashboard for plan selection, and the shared package for unified type definitions.
Sources: README.md:145-147, CHANGELOG.md:46-50, packages/shared/src/types.ts:140-149
Subscription Tiers and Features
Oddity 1 utilizes two primary subscription tiers: Free and Standard. Subscription status determines the available personality styles, annotation rate limits, and export capabilities.
| Feature | Free Tier | Standard Tier |
|---|---|---|
| Annotation Styles | Terry, Jerry | Terry, Jerry, Sally |
| Annotation Limits | Limited (e.g., 50/day) | 20x higher (e.g., 500/day) |
| PDF Export | Fixed subtitle | Custom subtitles |
| Advanced AI | Basic | Advanced annotations & structured arguments |
| PDF Annotation | No | Yes |
Sources: Oddity1Dashboard/src/pages/PlansPage.jsx:7-21, README.md:145-147, CHANGELOG.md:120-125
Architecture and Data Flow
The subscription system follows a request-response cycle initiated by the user in the dashboard, followed by asynchronous updates via Stripe webhooks to the database.
Checkout Flow
The PlansPage component handles the selection of billing intervals (monthly, quarterly, yearly) and initiates the checkout process. If a user is not authenticated, the system stores the intent and redirects to an auth modal before proceeding.
The sequence diagram shows the interaction between the user interface, the backend service, and Stripe's external checkout platform.
Sources: Oddity1Dashboard/src/pages/PlansPage.jsx:92-167, backend/routes/webhook-stripe.ts:31-75
Webhook Processing
The backend listens for Stripe events to synchronize local user profiles with subscription changes. The webhook-stripe.ts router processes these events and updates the profiles table in Supabase.
The flowchart describes the logic used by the backend to handle different Stripe event types and ensure database consistency.
Sources: backend/routes/webhook-stripe.ts:16-160
Data Structures
Subscription Types
The system defines specific types for tracking the lifecycle of a subscription.
export type UserTier = "free" | "standard";
export type SubscriptionStatus =
| "active"
| "past_due"
| "canceled"
| "incomplete"
| "trialing"
| "unpaid"
| null;
export type BillingInterval = "month" | "quarter" | "year" | null;
Sources: packages/shared/src/types.ts:140-154
Database Profile Mapping
Stripe events update the following fields in the user profiles table:
stripe_customer_id: Unique Stripe identifier for the customer.stripe_subscription_id: The current active subscription ID.tier: The current service level (freeorstandard).subscription_status: The status string from Stripe (e.g.,active,past_due).billing_interval: The frequency of billing (month,quarter,year).current_period_end: Timestamp for the end of the current billing cycle.cancel_at_period_end: Boolean indicating if the subscription will terminate at the cycle end.
Sources: backend/routes/webhook-stripe.ts:50-60, backend/routes/webhook-stripe.ts:98-104
API Endpoints
The subscription system relies on three primary API routes:
| Method | Endpoint | Description |
|---|---|---|
GET | /api/prices | Fetches available subscription prices and intervals from Stripe. |
POST | /api/checkout | Creates a Stripe Checkout session for a logged-in user. |
POST | /api/webhook/stripe | Processes raw Stripe event bodies for status synchronization. |
Sources: Oddity1Dashboard/src/pages/PlansPage.jsx:92-95, Oddity1Dashboard/src/pages/PlansPage.jsx:153-157, backend/routes/webhook-stripe.ts:16-18
Implementation Details
Billing Interval Mapping
The backend includes logic to map Stripe's recurring intervals to the application's supported intervals. A month interval with an interval_count of 3 is treated as a quarterly plan.
Sources: backend/routes/webhook-stripe.ts:10-14
Concurrency and Limits
The extension's background script monitors user tier status to enforce rate limits. The system prevents bursts of simultaneous API requests using a per-tab concurrency limiter, while the backend enforces strict daily request counts based on the user's tier.
Sources: extension/src/background/index.ts:25-44, README.md:244
Feature Gating in the Dashboard
The dashboard dynamically adjusts the UI based on the user's tier. For example, the standard tier badge is applied to the profile bar, and the PDF export dialog enables custom subtitles only for Standard users.
Sources: extension/src/popup/index.html:433-437, Oddity1Dashboard/src/pages/PlansPage.jsx:218-220