Billing — Subscriptions & Payments
reauth handles Stripe subscriptions for your users. Plans, checkout, and subscription status are all managed through the SDK.
Setup
- Connect your Stripe account in the reauth dashboard
- Create subscription plans (code, name, price, interval, features)
- Choose test or live mode
Plans
Plans are configured in the dashboard. Each plan has:
| Field | Description |
|---|---|
code | Machine-readable identifier (e.g., "pro") |
name | Display name (e.g., "Pro Plan") |
priceCents | Price in cents (e.g., 1999 for $19.99) |
currency | ISO currency code (e.g., "usd") |
interval | "monthly" | "yearly" | "custom" |
intervalCount | Billing cycles per interval |
trialDays | Free trial period |
features | Array of plan features (boolean or numeric) |
planType | "self_service" (checkout) or "sales" (contact URL) |
creditsAmount | Credits included with subscription |
contactUrl | URL for sales-type plans |
Fetching Plans
const plans = await reauth.getPlans();
// Returns public plans sorted by displayOrder
plans.forEach(plan => {
console.log(`${plan.name}: $${plan.priceCents / 100}/${plan.interval}`);
plan.features.forEach(f => {
if (f.featureType === 'boolean') {
console.log(` ✓ ${f.name}`);
} else {
console.log(` ${f.numericValue} ${f.unitLabel ?? f.name}`);
}
});
});
Checkout
Self-Service Plans
// Option 1: Create checkout and redirect manually
const { checkoutUrl } = await reauth.createCheckout(
'pro', // plan code
'https://yourdomain.com/billing/success', // success URL
'https://yourdomain.com/pricing', // cancel URL
);
window.location.href = checkoutUrl;
// Option 2: Convenience method (browser-only)
// Uses current URL as both success and cancel URL
await reauth.subscribe('pro');
Sales Plans
For planType: "sales" plans, redirect to the contact URL instead:
if (plan.planType === 'sales') {
window.location.href = plan.contactUrl!;
} else {
await reauth.subscribe(plan.code);
}
Subscription Status
From Session (No API Call)
Subscription data is embedded in the JWT and available in the session:
const session = await reauth.getSession();
if (session.subscription) {
session.subscription.status; // "active", "trialing", "none", etc.
session.subscription.plan_code; // "pro"
session.subscription.plan_name; // "Pro Plan"
session.subscription.current_period_end; // Unix timestamp
session.subscription.cancel_at_period_end;
session.subscription.trial_ends_at;
}
From Server SDK (No API Call)
const result = await reauth.authenticate(request);
if (result.valid && result.user) {
const { subscription } = result.user;
subscription.status; // "active"
subscription.planCode; // "pro" (camelCase in server SDK)
}
Full Subscription Details
const sub = await reauth.getSubscription();
// {
// id: string | null,
// planCode: string | null,
// planName: string | null,
// status: SubscriptionStatus,
// currentPeriodEnd: number | null,
// trialEnd: number | null,
// cancelAtPeriodEnd: boolean | null,
// }
Subscription Lifecycle
(no subscription) → trialing → active → canceled
↓ ↓
active past_due → paused → canceled
| Status | Meaning |
|---|---|
none | No subscription |
trialing | In free trial period |
active | Paid and current |
past_due | Payment failed, in grace period |
paused | Grace period expired, access suspended |
canceled | Subscription ended |
incomplete | First payment pending |
incomplete_expired | First payment failed |
unpaid | Unpaid after retries |
Cancellation
Cancel at the end of the current billing period:
await reauth.cancelSubscription();
// Subscription remains active until currentPeriodEnd
// cancelAtPeriodEnd becomes true
Billing Portal
Open the Stripe customer portal for self-service subscription management:
await reauth.openBillingPortal('https://yourdomain.com/settings');
// Redirects to Stripe portal (browser-only)
The portal lets users:
- Update payment method
- View invoices
- Cancel or resume subscription
Org-Scoped Billing
Subscriptions are scoped to the active organization. When a user switches orgs, switchOrg() returns the subscription for that org:
const result = await reauth.switchOrg('team-org-uuid');
result.subscription.status; // May differ from personal org
result.subscription.planCode;
See organizations.md for more on org context.
Feature Gating
Use plan features for access control:
const plans = await reauth.getPlans();
const session = await reauth.getSession();
const currentPlan = plans.find(p => p.code === session.subscription?.plan_code);
const apiCalls = currentPlan?.features.find(f => f.code === 'api_calls');
if (apiCalls?.featureType === 'numeric') {
console.log(`Allowed API calls: ${apiCalls.numericValue}`);
}