Subscription Management
Managing subscriptions and recurring payments in DeesseJS
Subscription Management
Complete guide to managing subscriptions and recurring billing with DeesseJS.
Subscription Models
Fixed Price Subscriptions
// deesse.config.ts
export const config = defineConfig({
payments: {
subscriptions: {
plans: [
{
id: 'basic',
name: 'Basic',
price: 9.99,
interval: 'month',
currency: 'usd',
features: [
'5 projects',
'Basic support',
'1GB storage',
],
},
{
id: 'pro',
name: 'Pro',
price: 29.99,
interval: 'month',
currency: 'usd',
features: [
'Unlimited projects',
'Priority support',
'100GB storage',
'Advanced analytics',
],
},
],
},
},
})Tiered Pricing
plans: [
{
id: 'starter',
name: 'Starter',
price: 0,
interval: 'month',
features: ['1 project', 'Community support'],
},
{
id: 'growth',
name: 'Growth',
price: 29,
interval: 'month',
features: ['Unlimited projects', 'Email support'],
},
{
id: 'enterprise',
name: 'Enterprise',
price: 99,
interval: 'month',
features: ['Custom solutions', 'Dedicated support'],
},
]Usage-Based Pricing
// Charge based on usage
export async function createUsageBasedSubscription() {
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [
{
price: 'price_usage_based', // Created in Stripe Dashboard
},
],
add_invoice_items: [
{
price: 'price_base_fee',
quantity: 1,
},
],
})
}Creating Subscriptions
With Free Trial
// app/api/create-subscription/route.ts
import { createSubscription } from '@deessejs/payments'
export async function POST(request: Request) {
const { priceId, customerId, trialDays = 14 } = await request.json()
const subscription = await createSubscription({
customer: customerId,
items: [{ price: priceId }],
trial_period_days: trialDays,
payment_behavior: 'default_incomplete',
expand: ['latest_invoice.payment_intent'],
})
return NextResponse.json({
subscriptionId: subscription.id,
status: subscription.status,
trialEnd: subscription.trial_end,
})
}With Setup Fee
export async function createSubscriptionWithSetupFee() {
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{ price: priceId }],
add_invoice_items: [
{
price: 'price_setup_fee',
quantity: 1,
},
],
})
}With Coupon
export async function createSubscriptionWithCoupon() {
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{ price: priceId }],
coupon: 'coupon_50_percent_off', // Created in Stripe Dashboard
})
}Managing Subscriptions
Update Subscription
// Change plan
export async function changePlan(subscriptionId: string, newPriceId: string) {
const subscription = await stripe.subscriptions.update(subscriptionId, {
items: [{ price: newPriceId }],
proration_behavior: 'create_prorations',
})
return subscription
}Cancel Subscription
// Cancel at period end
export async function cancelSubscription(subscriptionId: string) {
const subscription = await stripe.subscriptions.update(subscriptionId, {
cancel_at_period_end: true,
})
// Notify user of cancellation
await sendCancellationNotice(subscription.customer)
}Pause Subscription
export async function pauseSubscription(subscriptionId: string) {
const subscription = await stripe.subscriptions.update(subscriptionId, {
pause_collection: {
behavior: 'keep_as_draft',
},
})
}Resume Subscription
export async function resumeSubscription(subscriptionId: string) {
const subscription = await stripe.subscriptions.update(subscriptionId, {
pause_collection: null as any,
})
}Subscription Status
Check Subscription Status
export async function getSubscriptionStatus(subscriptionId: string) {
const subscription = await stripe.subscriptions.retrieve(subscriptionId)
return {
status: subscription.status,
currentPeriodStart: subscription.current_period_start,
currentPeriodEnd: subscription.current_period_end,
cancelAtPeriodEnd: subscription.cancel_at_period_end,
}
}Status Values
active- Subscription is active and billingtrialing- In free trial periodpast_due- Payment failed, retryingcanceled- Canceled but still active until period endunpaid- Payment failed, not retryingincomplete- Initial payment failed
Sync with Database
// Webhook handler for subscription updates
async function handleSubscriptionUpdated(event: Stripe.Subscription) {
await db.subscriptions.update({
where: { stripeSubscriptionId: event.id },
data: {
status: event.status,
currentPeriodStart: new Date(event.current_period_start * 1000),
currentPeriodEnd: new Date(event.current_period_end * 1000),
cancelAtPeriodEnd: event.cancel_at_period_end,
},
})
}Payment Methods
Add Payment Method
export async function addPaymentMethod(
customerId: string,
paymentMethodId: string
) {
await stripe.paymentMethods.attach(paymentMethodId, {
customer: customerId,
})
// Set as default
await stripe.customers.update(customerId, {
invoice_settings: {
default_payment_method: paymentMethodId,
},
})
}Update Payment Method
export async function updateSubscriptionPaymentMethod(
subscriptionId: string,
paymentMethodId: string
) {
await stripe.subscriptions.update(subscriptionId, {
default_payment_method: paymentMethodId,
})
}Invoices
List Invoices
export async function getCustomerInvoices(customerId: string) {
const invoices = await stripe.invoices.list({
customer: customerId,
limit: 20,
})
return invoices.data.map((invoice) => ({
id: invoice.id,
amount: invoice.total,
currency: invoice.currency,
status: invoice.status,
created: new Date(invoice.created * 1000),
pdf: invoice.invoice_pdf,
}))
}Upcoming Invoice
export async function getUpcomingInvoice(subscriptionId: string) {
const invoice = await stripe.invoices.retrieveUpcoming({
subscription: subscriptionId,
})
return {
amount: invoice.total,
currency: invoice.currency,
date: new Date(invoice.period_end * 1000),
}
}Usage-Based Billing
Report Usage
// For metered billing
export async function reportUsage(subscriptionItemId: string, quantity: number) {
await stripe.subscriptionItems.createUsageRecord(subscriptionItemId, {
quantity,
timestamp: Math.floor(Date.now() / 1000),
action: 'increment',
})
}Check Usage
export async function getUsage(subscriptionItemId: string) {
const usage = await stripe.subscriptionItems.listUsageRecords(
subscriptionItemId,
{ limit: 1 }
)
return usage.data[0]?.total_usage || 0
}Trial Management
Start Trial
export async function startTrial(
customerId: string,
priceId: string,
trialDays: number
) {
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{ price: priceId }],
trial_period_days: trialDays,
trial_settings: {
end_behavior: {
missing_payment_method: 'cancel',
},
},
})
return subscription
}Extend Trial
export async function extendTrial(subscriptionId: string, additionalDays: number) {
const subscription = await stripe.subscriptions.retrieve(subscriptionId)
const newTrialEnd = Math.floor(Date.now() / 1000) + additionalDays * 24 * 60 * 60
await stripe.subscriptions.update(subscriptionId, {
trial_end: newTrialEnd,
})
}Analytics
Subscription Metrics
export async function getSubscriptionMetrics() {
const subscriptions = await stripe.subscriptions.list({
status: 'active',
limit: 100,
})
const mrr = subscriptions.data.reduce((total, sub) => {
return total + sub.items.data[0].price.unit_amount
}, 0)
return {
activeSubscriptions: subscriptions.data.length,
monthlyRecurringRevenue: mrr,
churnRate: await calculateChurnRate(),
trialCount: await getTrialCount(),
}
}Next Steps
- Learn about Stripe Integration
- Explore Payment Components
- Return to Payments Overview