Per-Seat Billing

Complete guide to implementing and managing per-seat billing in Dopamine Starter Kit

Per-seat billing is a common SaaS pricing model that charges customers based on the number of active users in their organization. Dopamine Starter Kit provides automated per-seat billing that dynamically adjusts subscription costs based on workspace membership.

Overview

What is Per-Seat Billing?

Per-seat billing, also known as per-user pricing, charges customers for each active user in their workspace. This model provides:

  • Scalable pricing that grows with customer usage
  • Transparent costs directly tied to value delivered
  • Automatic billing adjustments when teams grow or shrink
  • Fair pricing for teams of all sizes

Key Benefits

  • Automatic seat calculation: No manual intervention required
  • Real-time updates: Billing adjusts immediately when membership changes
  • Prorated charges: Fair billing for mid-cycle changes
  • Usage transparency: Clear visibility into seat usage and costs

Architecture

Per-Seat Billing Flow

graph LR
    A[User Joins<br/>Workspace] --> B[Count Paid<br/>Members]
    B --> C[Update Subscription<br/>Quantity]
    C --> D[Stripe Processes<br/>Proration]
    D --> E[Webhook Updates<br/>Database]
    E --> F[Billing Reflects<br/>New Seat Count]

Core Components

  1. Workspace Membership: Source of truth for active users
  2. Seat Calculation: Automatic counting of billable members
  3. Stripe Integration: Quantity-based subscription management
  4. Webhook Handling: Real-time subscription updates
  5. Database Synchronization: Consistent state across systems

Seat Calculation

Counting Paid Members

The system automatically calculates seats based on workspace membership.

Seat Calculation Rules

  1. Minimum Seat Count: Every workspace has at least 1 seat (owner)
  2. Real-time Counting: Seat count updates immediately on membership changes
  3. Owner Inclusion: Workspace owner is always counted as 1 seat

Subscription Management

Checkout with Per-Seat Pricing

When creating a subscription, the system automatically calculates the required seats:

// apps/api/src/billing/stripe.service.ts
async createCheckoutSession({
  user,
  workspace,
  plan,
  interval = 'monthly',
}) {
  const priceId = this.plans[plan][interval];
 
  // Automatic seat calculation
  const seats = await this.workspaceService.countPaidMembers(workspace.id);
 
  return await this.stripe.checkout.sessions.create({
    line_items: [{
      price: priceId,
      quantity: seats, // Per-seat quantity
    }],
    mode: 'subscription',
    // ... other options
  });
}

Pricing Configuration

Stripe Price Setup

Configure per-seat prices in Stripe Dashboard:

  1. Create Product: Define your SaaS product
  2. Set Pricing Model: Choose "Per unit" pricing
  3. Configure Billing: Set monthly/yearly intervals
  4. Enable Prorations: Allow mid-cycle adjustments

Price IDs Configuration

# apps/api/.env
STRIPE_PRO_MONTHLY_PRICE_ID=price_1234567890_pro_monthly
STRIPE_PRO_YEARLY_PRICE_ID=price_1234567890_pro_yearly
STRIPE_BUSINESS_MONTHLY_PRICE_ID=price_1234567890_business_monthly
STRIPE_BUSINESS_YEARLY_PRICE_ID=price_1234567890_business_yearly

Webhook Handling

Subscription Updates

Handle quantity changes through Stripe webhooks:

private async handleSubscriptionUpdated(subscription: Stripe.Subscription) {
  const item = subscription.items.data[0];
  const newQuantity = item.quantity;
 
  await this.subscriptionRepository.updateMany({
    where: { stripeSubscriptionId: subscription.id },
    data: {
      quantity: newQuantity,
      currentPeriodStart: new Date(item.current_period_start * 1000),
      currentPeriodEnd: new Date(item.current_period_end * 1000),
    },
  });
}

Prorated Billing Events

Stripe automatically handles prorations for mid-cycle changes:

  • Seat additions: Immediate charge for remaining period
  • Seat removals: Credit applied to next invoice
  • Plan changes: Prorated upgrade/downgrade handling

Billing Scenarios

New Workspace Creation

  1. User creates workspace → Counted as 1 seat
  2. Checkout session created with quantity: 1
  3. Subscription activated with 1 seat billing

Adding Team Members

  1. Invitation sent to new member
  2. Member accepts and joins workspace
  3. Seat count increases: countPaidMembers() → 2
  4. Subscription quantity updated in Stripe
  5. Prorated charge applied immediately

Removing Team Members

  1. Member removed from workspace
  2. Seat count decreases: countPaidMembers() → 1
  3. Subscription quantity updated in Stripe
  4. Credit applied to next billing cycle

Plan Upgrades

  1. User selects higher tier plan
  2. New checkout session with current seat count
  3. Existing subscription cancelled
  4. New subscription created with same quantity
  5. Prorated billing for plan difference

Conclusion

Per-seat billing in Dopamine provides a scalable, transparent, and automated billing solution that grows with your customers' teams while maintaining accuracy and fairness in pricing.