TheShipStack Docs
Features

Billing

Stripe multi-plan billing with upgrade, downgrade, and cancellation flows.

TheShipStack includes full Stripe billing wired end-to-end — checkout, webhooks, plan enforcement, and in-app subscription management.

Plans

Three paid plans on top of a free tier:

PlanMonthlyAnnual
Starterconfigurableconfigurable
Proconfigurableconfigurable
Businessconfigurableconfigurable

Plan definitions (names, prices, limits) live in lib/plans.ts. Update that file to match your Stripe Price objects.

How it works

  1. Checkout — users click a plan on /dashboard/pricing, a Stripe Checkout session is created, and they're redirected to Stripe to complete payment
  2. Webhook sync — Stripe sends events to /api/webhooks/stripe; the handler updates the subscription table with status, plan, and period dates
  3. Plan enforcement — server actions read the subscription from the database and compare usage against plan limits before allowing operations

Subscription status

The subscription table stores:

  • statusactive, trialing, past_due, canceled, inactive
  • planIdstarter, pro, business, or null
  • cancelAtPeriodEnd — whether cancellation is scheduled
  • currentPeriodEnd — when the current period ends

Checking the subscription in your code

import { getOrgSubscription } from '@/lib/subscription'

const sub = await getOrgSubscription(orgId)
const isPro = sub?.status === 'active' || sub?.status === 'trialing'

Enforcing plan limits

import { getPlanLimits } from '@/lib/plans'
import { getOrgSubscription } from '@/lib/subscription'

const sub = await getOrgSubscription(orgId)
const limits = getPlanLimits(sub?.status === 'active', sub?.planId)

if (limits.members !== null && currentMemberCount >= limits.members) {
  throw new Error('UPGRADE_REQUIRED')
}

Upgrade and downgrade

Users manage their plan from the workspace billing tab (/dashboard/workspace/settings?tab=billing). Upgrades and downgrades update the Stripe subscription immediately with proration_behavior: 'always_invoice' — Stripe generates an invoice for the difference right away.

Downgrades check current usage against the new plan's limits and block the action with a clear message if the workspace would exceed them.

Cancellation

Cancelling sets cancel_at_period_end: true on the Stripe subscription. Access continues until the end of the billing period. The billing settings page shows the access-until date.

Adding a new plan

  1. Create the product and price in the Stripe dashboard
  2. Add the plan to lib/plans.ts with its name, price, limits, and Price ID env vars
  3. Add the corresponding env vars to .env and Vercel

Webhook events handled

EventAction
customer.subscription.createdActivates subscription
customer.subscription.updatedSyncs status, plan, and dates
customer.subscription.deletedMarks subscription as canceled
invoice.payment_failedSets status to past_due, notifies owner

On this page