Skip to content

Subscriptions

Subscriptions

Subscriptions represent recurring billing relationships between customers and plans.

Subscription Lifecycle

┌─────────┐ ┌──────────┐ ┌────────┐ ┌───────────┐
│ Created │───▶│ Trialing │───▶│ Active │───▶│ Cancelled │
└─────────┘ └──────────┘ └────────┘ └───────────┘
┌──────────┐
│ Past Due │
└──────────┘
┌──────────┐
│ Unpaid │
└──────────┘

Creating Subscriptions

Basic Subscription

const subscription = await billing.subscriptions.create({
customerId: 'cus_123',
planId: 'price_pro_monthly'
});

With Trial Period

const subscription = await billing.subscriptions.create({
customerId: 'cus_123',
planId: 'price_pro_monthly',
trialDays: 14
});

With Promo Code

const subscription = await billing.subscriptions.create({
customerId: 'cus_123',
planId: 'price_pro_monthly',
promoCodeId: 'promo_launch50'
});

Subscription Status

StatusDescription
trialingIn trial period, no payment yet
activePaid and active
past_duePayment failed, grace period
unpaidMultiple payment failures
pausedTemporarily paused
cancelledCancelled, may have access until period end

Managing Subscriptions

Upgrading/Downgrading

const subscription = await billing.subscriptions.update('sub_123', {
planId: 'price_enterprise_monthly',
proration: 'create_prorations' // or 'none'
});

Pausing

const subscription = await billing.subscriptions.pause('sub_123');

Resuming

const subscription = await billing.subscriptions.resume('sub_123');

Cancelling

// Cancel at period end
const subscription = await billing.subscriptions.cancel('sub_123', {
atPeriodEnd: true
});
// Cancel immediately
const subscription = await billing.subscriptions.cancel('sub_123', {
atPeriodEnd: false
});

Retrieving Subscriptions

Single Subscription

const subscription = await billing.subscriptions.get('sub_123');

Customer’s Subscriptions

const subscriptions = await billing.subscriptions.getByCustomerId('cus_123');

All Subscriptions

const subscriptions = await billing.subscriptions.list({
status: 'active',
planId: 'price_pro_monthly',
limit: 10
});

Subscription Object

interface Subscription {
id: string;
customerId: string;
planId: string;
status: SubscriptionStatus;
currentPeriodStart: Date;
currentPeriodEnd: Date;
trialStart?: Date;
trialEnd?: Date;
cancelAt?: Date; // Scheduled cancellation date
canceledAt?: Date; // When cancellation occurred
cancelAtPeriodEnd: boolean;
metadata: Record<string, string>;
externalIds: Record<string, string>;
createdAt: Date;
updatedAt: Date;
}

Events

EventDescription
subscription.createdNew subscription created
subscription.updatedSubscription was modified
subscription.canceledSubscription was canceled
subscription.pausedSubscription was paused
subscription.resumedSubscription was resumed
subscription.trial_endingTrial ends in 3 days
billing.on('subscription.created', async (event) => {
const { customerId, planId } = event.data;
await provisionFeatures(customerId, planId);
});
billing.on('subscription.canceled', async (event) => {
await sendCancellationSurvey(event.data.customerId);
});

Best Practices

  1. Always handle status changes via webhooks
  2. Implement grace periods for failed payments
  3. Send trial ending reminders before conversion
  4. Track cancellation reasons for analytics
  5. Use cancel at period end for better UX