Subscriptions

Stripe

Manage user subscriptions including status checks, upgrades, downgrades, and cancellations.

Status Tracking

Real-time subscription status

Plan Changes

Upgrade & downgrade handling

Billing Portal

Self-service management

Proration

Automatic billing adjustments

Get Subscription

Retrieve a user's current subscription:

Server-side
import { platform } from '@/lib/platform'

const subscription = await platform.billing.getSubscription(userId)

if (subscription) {
  console.log('Plan:', subscription.plan.name)
  console.log('Status:', subscription.status)
  console.log('Current Period End:', subscription.currentPeriodEnd)
} else {
  console.log('No active subscription (free tier)')
}
Client-side (React)
'use client'

import { useBilling } from '@sylphx/platform-sdk/react'

export function SubscriptionStatus() {
  const { subscription, isLoading } = useBilling()

  if (isLoading) return <div>Loading...</div>

  if (!subscription) {
    return <div>You're on the free plan</div>
  }

  return (
    <div>
      <p>Plan: {subscription.plan.name}</p>
      <p>Status: {subscription.status}</p>
      <p>Renews: {new Date(subscription.currentPeriodEnd).toLocaleDateString()}</p>
    </div>
  )
}

Subscription Status

Subscription status values and what they mean:

activeSubscription is active and paid
trialingUser is in free trial period
past_duePayment failed, retrying
canceledCanceled, active until period end
unpaidPayment failed, subscription paused
// Check if user has active access
function hasActiveSubscription(subscription: Subscription | null): boolean {
  if (!subscription) return false
  return ['active', 'trialing', 'canceled'].includes(subscription.status)
  // 'canceled' still has access until period end
}

Billing Portal

Let users manage their subscription through Stripe's billing portal:

app/api/billing/portal/route.ts
import { platform } from '@/lib/platform'
import { currentUser } from '@sylphx/platform-sdk/nextjs'

export async function POST() {
  const user = await currentUser()
  if (!user) {
    return new Response('Unauthorized', { status: 401 })
  }

  const { url } = await platform.billing.createPortalSession({
    userId: user.id,
    returnUrl: `${process.env.NEXT_PUBLIC_APP_URL}/settings/billing`,
  })

  return Response.json({ url })
}
Manage Subscription Button
'use client'

// Option 1: Custom fetch
export function ManageSubscriptionButton() {
  const handleManage = async () => {
    const res = await fetch('/api/billing/portal', { method: 'POST' })
    const { url } = await res.json()
    window.location.href = url
  }

  return <button onClick={handleManage}>Manage Subscription</button>
}

// Option 2: SDK hook
import { useBilling } from '@sylphx/platform-sdk/react'

export function ManageBillingButton() {
  const { openPortal } = useBilling()

  return (
    <button onClick={() => openPortal({ returnUrl: '/settings' })}>
      Manage Billing
    </button>
  )
}

Portal Features

The Stripe billing portal lets users update payment methods, view invoices, change plans, and cancel subscriptions.

Cancel & Resume

Cancel a subscription (effective at period end) or resume a canceled one:

// Cancel subscription (user keeps access until period end)
const subscription = await platform.billing.cancelSubscription(userId)
// subscription.status = 'canceled'
// subscription.cancelAtPeriodEnd = true

// Resume a canceled subscription before it expires
const resumed = await platform.billing.resumeSubscription(userId)
// resumed.status = 'active'
// resumed.cancelAtPeriodEnd = false

// Client-side
const { cancelSubscription, resumeSubscription } = useBilling()
await cancelSubscription()
await resumeSubscription()

Change Plan

Upgrade or downgrade to a different plan:

// Using the billing portal (recommended)
const { url } = await platform.billing.createPortalSession({
  userId: user.id,
  returnUrl: '/settings/billing',
})

// Or programmatically (instant change)
const subscription = await platform.billing.changePlan({
  userId: user.id,
  newPlanSlug: 'enterprise',
  prorate: true, // Adjust billing for partial periods
})

Proration

When changing plans, Stripe prorates the charges. If upgrading mid-cycle, the user is charged the difference. If downgrading, they receive credit.

Subscription Object

Full subscription object structure:

PropertyTypeDescription
idstringUnique subscription ID
userIdstringUser who owns the subscription
statusenumactive | trialing | past_due | canceled | unpaid
planobjectPlan details (id, slug, name, features)
intervalstringmonthly | annual
currentPeriodStartDateStart of current billing period
currentPeriodEndDateEnd of current billing period
cancelAtPeriodEndbooleanWill cancel at period end
trialEndDate?When trial ends (if applicable)