.do

Hybrid Pricing Models

Combine multiple pricing strategies for flexible monetization

Hybrid pricing combines multiple monetization strategies to balance predictable revenue with usage-based scaling. This approach captures value from different customer segments while providing flexibility for varying usage patterns.

When to Use Hybrid Pricing

Best for:

  • Variable usage patterns across customers
  • Services with fixed + variable costs
  • Market with diverse customer segments
  • Complex value propositions
  • Transitioning between pricing models

Not ideal for:

  • Simple, single-dimension services
  • Customers preferring straightforward pricing
  • Early-stage MVPs
  • Highly commoditized offerings

Core Patterns

Pattern 1: Base Fee + Usage

Combine recurring subscription with usage charges:

import $, { db, on, send } from 'sdk.do'

const messagingService = await $.Service.create({
  name: 'Business Messaging Platform',
  type: $.ServiceType.Messaging,

  pricing: {
    model: 'base-plus-usage',
    plans: [
      {
        id: 'starter',
        name: 'Starter',
        baseFee: 49.0, // $49/month base
        included: {
          messages: 10000, // 10k messages included
          users: 5,
          channels: 10,
        },
        usage: {
          messages: 0.005, // $0.005 per message over limit
          users: 10.0, // $10 per additional user
          channels: 5.0, // $5 per additional channel
        },
        features: ['basic-support', 'standard-api'],
      },
      {
        id: 'professional',
        name: 'Professional',
        baseFee: 149.0, // $149/month base
        included: {
          messages: 50000, // 50k messages included
          users: 25,
          channels: 50,
        },
        usage: {
          messages: 0.003, // $0.003 per message (better rate)
          users: 8.0, // $8 per additional user
          channels: 3.0, // $3 per additional channel
        },
        features: ['priority-support', 'advanced-api', 'analytics'],
      },
      {
        id: 'enterprise',
        name: 'Enterprise',
        baseFee: 499.0, // $499/month base
        included: {
          messages: 250000, // 250k messages included
          users: 100,
          channels: 200,
        },
        usage: {
          messages: 0.002, // $0.002 per message (best rate)
          users: 6.0, // $6 per additional user
          channels: 2.0, // $2 per additional channel
        },
        features: ['dedicated-support', 'enterprise-api', 'custom-integrations', 'sla'],
      },
    ],
  },
})

on($.Subscription.create, async (subscriptionRequest) => {
  const plan = messagingService.pricing.plans.find((p) => p.id === subscriptionRequest.planId)

  // Create subscription
  const subscription = await db.create($.Subscription, {
    customerId: subscriptionRequest.customerId,
    serviceId: messagingService.id,
    planId: plan.id,
    status: 'active',
    baseFee: plan.baseFee,
    included: plan.included,
    usageRates: plan.usage,
    currentPeriodStart: new Date(),
    currentPeriodEnd: addMonths(new Date(), 1),
  })

  // Charge base fee
  await send($.Payment.charge, {
    customerId: subscriptionRequest.customerId,
    amount: plan.baseFee,
    description: `${plan.name} - Monthly Base Fee`,
    subscriptionId: subscription.id,
  })

  return subscription
})

// Track usage during the month
on($.Message.sent, async (message) => {
  const subscription = await db.findOne($.Subscription, {
    customerId: message.customerId,
    serviceId: messagingService.id,
    status: 'active',
  })

  if (!subscription) return

  // Record usage
  await db.create($.UsageRecord, {
    subscriptionId: subscription.id,
    customerId: message.customerId,
    type: 'message',
    units: 1,
    timestamp: new Date(),
    metadata: {
      messageId: message.id,
      channel: message.channel,
    },
  })
})

// End-of-month billing for overage
on($.BillingCycle.ended, async (cycle) => {
  const subscriptions = await db.query($.Subscription, {
    where: {
      serviceId: messagingService.id,
      status: 'active',
    },
  })

  for (const subscription of subscriptions) {
    const plan = messagingService.pricing.plans.find((p) => p.id === subscription.planId)

    // Get usage for the period
    const usage = await db.query($.UsageRecord, {
      where: {
        subscriptionId: subscription.id,
        timestamp: { gte: cycle.start, lte: cycle.end },
      },
    })

    // Calculate overage charges
    const usageSummary = {
      messages: usage.filter((u) => u.type === 'message').length,
      users: await getCurrentUserCount(subscription.id),
      channels: await getCurrentChannelCount(subscription.id),
    }

    let overageTotal = 0
    const overageItems = []

    // Message overage
    if (usageSummary.messages > plan.included.messages) {
      const overage = usageSummary.messages - plan.included.messages
      const cost = overage * plan.usage.messages
      overageTotal += cost

      overageItems.push({
        description: `Message Overage - ${overage.toLocaleString()} messages`,
        quantity: overage,
        rate: plan.usage.messages,
        amount: cost,
      })
    }

    // User overage
    if (usageSummary.users > plan.included.users) {
      const overage = usageSummary.users - plan.included.users
      const cost = overage * plan.usage.users
      overageTotal += cost

      overageItems.push({
        description: `Additional Users - ${overage} users`,
        quantity: overage,
        rate: plan.usage.users,
        amount: cost,
      })
    }

    // Channel overage
    if (usageSummary.channels > plan.included.channels) {
      const overage = usageSummary.channels - plan.included.channels
      const cost = overage * plan.usage.channels
      overageTotal += cost

      overageItems.push({
        description: `Additional Channels - ${overage} channels`,
        quantity: overage,
        rate: plan.usage.channels,
        amount: cost,
      })
    }

    // Create invoice if there are overages
    if (overageTotal > 0) {
      await send($.Invoice.create, {
        customerId: subscription.customerId,
        subscriptionId: subscription.id,
        period: { start: cycle.start, end: cycle.end },
        lineItems: overageItems,
        total: overageTotal,
      })
    }

    // Renew subscription base fee for next month
    await send($.Payment.charge, {
      customerId: subscription.customerId,
      amount: plan.baseFee,
      description: `${plan.name} - Monthly Base Fee`,
      subscriptionId: subscription.id,
    })

    // Update period
    await db.update(subscription, {
      currentPeriodStart: cycle.end,
      currentPeriodEnd: addMonths(cycle.end, 1),
    })
  }
})

async function getCurrentUserCount(subscriptionId: string): Promise<number> {
  const users = await db.query($.User, {
    where: { subscriptionId, status: 'active' },
    count: true,
  })
  return users.count
}

async function getCurrentChannelCount(subscriptionId: string): Promise<number> {
  const channels = await db.query($.Channel, {
    where: { subscriptionId, status: 'active' },
    count: true,
  })
  return channels.count
}

function addMonths(date: Date, months: number): Date {
  const result = new Date(date)
  result.setMonth(result.getMonth() + months)
  return result
}

Example Billing:

  • Starter ($49) + 15k messages (5k overage @ $0.005) = $74/month
  • Professional ($149) + 75k messages (25k overage @ $0.003) = $224/month
  • Enterprise ($499) + 300k messages (50k overage @ $0.002) = $599/month

Pattern 2: Credit System (Prepaid + Overage)

Customers buy credits upfront, with flexible overage options:

const aiProcessingService = await $.Service.create({
  name: 'AI Processing Platform',
  type: $.ServiceType.AIProcessing,

  pricing: {
    model: 'credit-system',
    creditPackages: [
      {
        id: 'starter',
        name: 'Starter Credits',
        credits: 100,
        price: 20.0, // $0.20 per credit
        bonus: 0,
        expiry: 90, // 90 days
      },
      {
        id: 'professional',
        name: 'Professional Credits',
        credits: 500,
        price: 85.0, // $0.17 per credit (15% discount)
        bonus: 50, // 50 bonus credits
        expiry: 180,
      },
      {
        id: 'enterprise',
        name: 'Enterprise Credits',
        credits: 2000,
        price: 300.0, // $0.15 per credit (25% discount)
        bonus: 300, // 300 bonus credits
        expiry: 365,
      },
    ],
    usage: {
      'image-generation': {
        cost: 2, // 2 credits per image
        quality: {
          standard: 1.0,
          hd: 2.0,
          ultra: 4.0,
        },
      },
      'text-generation': {
        cost: 0.1, // 0.1 credits per 100 words
      },
      'audio-transcription': {
        cost: 1, // 1 credit per minute
      },
      'video-analysis': {
        cost: 10, // 10 credits per minute
      },
    },
    overage: {
      enabled: true,
      rate: 0.25, // $0.25 per credit (25% premium over base)
      autoRecharge: true,
      minimumRecharge: 50, // Minimum 50 credits
    },
  },
})

on($.CreditPackage.purchase, async (purchase) => {
  const pkg = aiProcessingService.pricing.creditPackages.find((p) => p.id === purchase.packageId)

  // Charge for credit package
  await send($.Payment.charge, {
    customerId: purchase.customerId,
    amount: pkg.price,
    description: `${pkg.name} - ${pkg.credits} credits`,
  })

  // Add credits to customer balance
  const expiryDate = new Date()
  expiryDate.setDate(expiryDate.getDate() + pkg.expiry)

  await db.create($.CreditBalance, {
    customerId: purchase.customerId,
    credits: pkg.credits + pkg.bonus,
    purchased: pkg.credits,
    bonus: pkg.bonus,
    pricePerCredit: pkg.price / pkg.credits,
    expiresAt: expiryDate,
    status: 'active',
  })

  return {
    credits: pkg.credits + pkg.bonus,
    expiresAt: expiryDate,
  }
})

on($.ServiceRequest.created, async (request) => {
  if (request.serviceId !== aiProcessingService.id) return

  const operation = request.inputs.operation
  const usageConfig = aiProcessingService.pricing.usage[operation]

  // Calculate credit cost
  let creditCost = usageConfig.cost

  if (operation === 'image-generation' && request.inputs.quality) {
    creditCost *= usageConfig.quality[request.inputs.quality]
  } else if (operation === 'text-generation' && request.inputs.wordCount) {
    creditCost *= Math.ceil(request.inputs.wordCount / 100)
  }

  try {
    // Check and deduct credits
    const balance = await getCustomerCreditBalance(request.customerId)

    if (balance.available < creditCost) {
      // Check if overage is allowed
      if (aiProcessingService.pricing.overage.enabled) {
        // Auto-recharge if enabled
        if (aiProcessingService.pricing.overage.autoRecharge) {
          const rechargeAmount = Math.max(aiProcessingService.pricing.overage.minimumRecharge, creditCost - balance.available)

          await send($.Payment.charge, {
            customerId: request.customerId,
            amount: rechargeAmount * aiProcessingService.pricing.overage.rate,
            description: `Credit Auto-Recharge - ${rechargeAmount} credits`,
          })

          await db.create($.CreditBalance, {
            customerId: request.customerId,
            credits: rechargeAmount,
            purchased: rechargeAmount,
            bonus: 0,
            pricePerCredit: aiProcessingService.pricing.overage.rate,
            status: 'active',
          })
        } else {
          return await send($.ServiceRequest.reject, {
            requestId: request.id,
            reason: 'Insufficient credits',
            balance: balance.available,
            required: creditCost,
            rechargeUrl: 'https://platform.do/credits/purchase',
          })
        }
      } else {
        return await send($.ServiceRequest.reject, {
          requestId: request.id,
          reason: 'Insufficient credits',
          balance: balance.available,
          required: creditCost,
        })
      }
    }

    // Deduct credits
    await deductCredits(request.customerId, creditCost)

    // Process request
    const result = await processAIRequest(request.inputs)

    // Deliver result
    await send($.ServiceResult.deliver, {
      requestId: request.id,
      outputs: result,
      creditsCost: creditCost,
      creditsRemaining: balance.available - creditCost,
    })
  } catch (error) {
    await send($.ServiceRequest.fail, {
      requestId: request.id,
      error: error.message,
    })
  }
})

async function getCustomerCreditBalance(customerId: string) {
  const balances = await db.query($.CreditBalance, {
    where: {
      customerId,
      status: 'active',
      expiresAt: { gte: new Date() },
    },
    orderBy: { expiresAt: 'asc' }, // Use credits closest to expiry first
  })

  const available = balances.reduce((sum, b) => sum + b.credits, 0)

  return { available, balances }
}

async function deductCredits(customerId: string, amount: number) {
  const { balances } = await getCustomerCreditBalance(customerId)
  let remaining = amount

  for (const balance of balances) {
    if (remaining <= 0) break

    const deduction = Math.min(remaining, balance.credits)

    await db.update(balance, {
      credits: { decrement: deduction },
    })

    if (balance.credits - deduction === 0) {
      await db.update(balance, { status: 'depleted' })
    }

    remaining -= deduction
  }

  // Record usage
  await db.create($.CreditUsage, {
    customerId,
    credits: amount,
    timestamp: new Date(),
  })
}

async function processAIRequest(inputs: any) {
  // Simulate AI processing
  return {
    result: 'processed',
    outputs: {},
  }
}

Credit System Benefits:

  • Flexible usage across different services
  • Prepaid cash flow
  • Volume discounts through packages
  • Bonus credits incentivize larger purchases

Example Usage:

  • Buy 500 credits for $85 (+ 50 bonus) = 550 total credits
  • Generate 100 HD images (200 credits)
  • Transcribe 300 minutes audio (300 credits)
  • Generate 5,000 words text (50 credits)
  • Total used: 550 credits, $0 additional charges

Pattern 3: Freemium + Paid Add-ons

Free base tier with paid feature upgrades:

const projectManagementService = await $.Service.create({
  name: 'Project Management Suite',
  type: $.ServiceType.ProjectManagement,

  pricing: {
    model: 'freemium-addons',
    free: {
      limits: {
        projects: 3,
        users: 5,
        storage: 1, // 1 GB
        tasks: 100,
      },
      features: ['basic-kanban', 'task-management', 'comments'],
    },
    addons: [
      {
        id: 'unlimited-projects',
        name: 'Unlimited Projects',
        price: 9.0, // $9/month
        interval: 'month',
        unlocks: { projects: Infinity },
      },
      {
        id: 'advanced-users',
        name: 'Advanced User Pack (25 users)',
        price: 19.0, // $19/month
        interval: 'month',
        unlocks: { users: 25 },
      },
      {
        id: 'extra-storage',
        name: 'Extra Storage (50 GB)',
        price: 14.0, // $14/month
        interval: 'month',
        unlocks: { storage: 50 },
      },
      {
        id: 'time-tracking',
        name: 'Time Tracking',
        price: 12.0, // $12/month
        interval: 'month',
        features: ['time-tracking', 'timesheets', 'billing-integration'],
      },
      {
        id: 'advanced-reporting',
        name: 'Advanced Reporting',
        price: 15.0, // $15/month
        interval: 'month',
        features: ['custom-reports', 'dashboards', 'export-pdf'],
      },
      {
        id: 'automation',
        name: 'Automation Engine',
        price: 29.0, // $29/month
        interval: 'month',
        features: ['workflow-automation', 'custom-triggers', 'integrations'],
      },
      {
        id: 'priority-support',
        name: 'Priority Support',
        price: 49.0, // $49/month
        interval: 'month',
        features: ['24-7-support', 'dedicated-account-manager', 'phone-support'],
      },
    ],
  },
})

on($.Addon.subscribe, async (addonRequest) => {
  const addon = projectManagementService.pricing.addons.find((a) => a.id === addonRequest.addonId)

  // Create addon subscription
  const subscription = await db.create($.AddonSubscription, {
    customerId: addonRequest.customerId,
    serviceId: projectManagementService.id,
    addonId: addon.id,
    price: addon.price,
    status: 'active',
    startDate: new Date(),
    nextBillingDate: addMonths(new Date(), 1),
  })

  // Charge for addon
  await send($.Payment.charge, {
    customerId: addonRequest.customerId,
    amount: addon.price,
    description: `${addon.name} - First month`,
    subscriptionId: subscription.id,
  })

  // Update customer limits/features
  if (addon.unlocks) {
    await updateCustomerLimits(addonRequest.customerId, addon.unlocks)
  }

  if (addon.features) {
    await enableCustomerFeatures(addonRequest.customerId, addon.features)
  }

  return subscription
})

// Check limits before operations
on($.Project.create, async (projectRequest) => {
  const limits = await getCustomerLimits(projectRequest.customerId)
  const currentProjects = await db.query($.Project, {
    where: { customerId: projectRequest.customerId },
    count: true,
  })

  if (currentProjects.count >= limits.projects) {
    return await send($.Project.createRejected, {
      projectId: projectRequest.id,
      reason: 'Project limit reached',
      current: currentProjects.count,
      limit: limits.projects,
      upgrade: {
        addon: 'unlimited-projects',
        price: 9.0,
      },
    })
  }

  // Create project...
})

async function getCustomerLimits(customerId: string) {
  const addons = await db.query($.AddonSubscription, {
    where: {
      customerId,
      serviceId: projectManagementService.id,
      status: 'active',
    },
  })

  // Start with free tier limits
  const limits = { ...projectManagementService.pricing.free.limits }

  // Apply addon unlocks
  for (const addon of addons) {
    const addonConfig = projectManagementService.pricing.addons.find((a) => a.id === addon.addonId)

    if (addonConfig?.unlocks) {
      for (const [key, value] of Object.entries(addonConfig.unlocks)) {
        if (value === Infinity) {
          limits[key] = Infinity
        } else {
          limits[key] = (limits[key] || 0) + value
        }
      }
    }
  }

  return limits
}

async function updateCustomerLimits(customerId: string, unlocks: any) {
  // Implementation to update limits
}

async function enableCustomerFeatures(customerId: string, features: string[]) {
  // Implementation to enable features
}

Freemium + Add-ons Examples:

  • Free: 3 projects, 5 users, 1 GB storage = $0/month
  • Free + Unlimited Projects = $9/month
  • Free + Unlimited Projects + Time Tracking + Reporting = $36/month
  • Free + All Add-ons = $147/month (complete suite)

Benefits:

  • Low barrier to entry (free tier)
  • Customers pay only for what they need
  • Gradual revenue expansion
  • Clear upgrade path

Pattern 4: Platform Fee + Transaction Percentage

Combine flat platform fee with transaction-based revenue:

const marketplaceService = await $.Service.create({
  name: 'E-Commerce Marketplace',
  type: $.ServiceType.Marketplace,

  pricing: {
    model: 'platform-plus-transaction',
    plans: [
      {
        id: 'basic',
        name: 'Basic',
        platformFee: 29.0, // $29/month platform access
        transactionFee: 0.05, // 5% of transaction value
        fixedFee: 0.3, // $0.30 per transaction
        limits: {
          products: 100,
          orders: Infinity,
        },
        features: ['basic-storefront', 'payment-processing', 'email-support'],
      },
      {
        id: 'professional',
        name: 'Professional',
        platformFee: 99.0, // $99/month platform access
        transactionFee: 0.03, // 3% of transaction value (better rate)
        fixedFee: 0.3,
        limits: {
          products: 1000,
          orders: Infinity,
        },
        features: ['advanced-storefront', 'payment-processing', 'analytics', 'custom-domain', 'priority-support'],
      },
      {
        id: 'enterprise',
        name: 'Enterprise',
        platformFee: 299.0, // $299/month platform access
        transactionFee: 0.02, // 2% of transaction value (best rate)
        fixedFee: 0.2, // Lower fixed fee
        limits: {
          products: Infinity,
          orders: Infinity,
        },
        features: ['custom-storefront', 'payment-processing', 'advanced-analytics', 'multi-store', 'api-access', 'dedicated-support', 'custom-integrations'],
      },
    ],
  },
})

on($.Subscription.create, async (subscriptionRequest) => {
  const plan = marketplaceService.pricing.plans.find((p) => p.id === subscriptionRequest.planId)

  const subscription = await db.create($.Subscription, {
    customerId: subscriptionRequest.customerId,
    serviceId: marketplaceService.id,
    planId: plan.id,
    status: 'active',
    platformFee: plan.platformFee,
    transactionFee: plan.transactionFee,
    fixedFee: plan.fixedFee,
    currentPeriodStart: new Date(),
    currentPeriodEnd: addMonths(new Date(), 1),
  })

  // Charge platform fee
  await send($.Payment.charge, {
    customerId: subscriptionRequest.customerId,
    amount: plan.platformFee,
    description: `${plan.name} - Monthly Platform Fee`,
    subscriptionId: subscription.id,
  })

  return subscription
})

// Charge transaction fees on each sale
on($.Order.completed, async (order) => {
  const subscription = await db.findOne($.Subscription, {
    customerId: order.sellerId,
    serviceId: marketplaceService.id,
    status: 'active',
  })

  if (!subscription) return

  // Calculate transaction fee
  const percentageFee = order.total * subscription.transactionFee
  const totalFee = percentageFee + subscription.fixedFee

  // Deduct from seller payout
  await send($.Payment.charge, {
    customerId: order.sellerId,
    amount: totalFee,
    description: `Transaction Fee - Order #${order.number}`,
    breakdown: {
      orderTotal: order.total,
      percentageFee: subscription.transactionFee,
      percentageAmount: percentageFee,
      fixedFee: subscription.fixedFee,
      total: totalFee,
    },
    metadata: {
      orderId: order.id,
      orderNumber: order.number,
    },
  })

  // Record transaction fee
  await db.create($.TransactionFee, {
    subscriptionId: subscription.id,
    orderId: order.id,
    orderTotal: order.total,
    fee: totalFee,
    timestamp: new Date(),
  })
})

// Monthly summary report
on($.BillingCycle.ended, async (cycle) => {
  const subscriptions = await db.query($.Subscription, {
    where: {
      serviceId: marketplaceService.id,
      status: 'active',
    },
  })

  for (const subscription of subscriptions) {
    const plan = marketplaceService.pricing.plans.find((p) => p.id === subscription.planId)

    // Get transaction fees for period
    const transactions = await db.query($.TransactionFee, {
      where: {
        subscriptionId: subscription.id,
        timestamp: { gte: cycle.start, lte: cycle.end },
      },
    })

    const totalSales = transactions.reduce((sum, t) => sum + t.orderTotal, 0)
    const totalFees = transactions.reduce((sum, t) => sum + t.fee, 0)

    // Send monthly statement
    await send($.Email.send, {
      to: subscription.customer.email,
      template: 'monthly-statement',
      data: {
        plan: plan.name,
        period: { start: cycle.start, end: cycle.end },
        platformFee: plan.platformFee,
        transactionCount: transactions.length,
        totalSales,
        transactionFees: totalFees,
        totalCharges: plan.platformFee + totalFees,
        effectiveRate: totalSales > 0 ? (totalFees / totalSales) * 100 : 0,
      },
    })

    // Renew platform fee
    await send($.Payment.charge, {
      customerId: subscription.customerId,
      amount: plan.platformFee,
      description: `${plan.name} - Monthly Platform Fee`,
      subscriptionId: subscription.id,
    })

    // Update period
    await db.update(subscription, {
      currentPeriodStart: cycle.end,
      currentPeriodEnd: addMonths(cycle.end, 1),
    })
  }
})

Platform + Transaction Examples:

  • Basic ($29/mo) + $10k sales = $29 + (10k × 5% + 100 × $0.30) = $559
  • Professional ($99/mo) + $50k sales = $99 + (50k × 3% + 300 × $0.30) = $1,689
  • Enterprise ($299/mo) + $200k sales = $299 + (200k × 2% + 1000 × $0.20) = $4,499

Pattern 5: Seat-Based + Usage

Per-user licensing combined with usage charges:

const collaborationService = await $.Service.create({
  name: 'Team Collaboration Platform',
  type: $.ServiceType.Collaboration,

  pricing: {
    model: 'seat-plus-usage',
    plans: [
      {
        id: 'team',
        name: 'Team',
        pricePerSeat: 15.0, // $15/user/month
        minimumSeats: 3,
        included: {
          storage: 10, // 10 GB per user
          meetings: 100, // 100 meeting minutes per user
          recordings: 10, // 10 hours per user
        },
        usage: {
          extraStorage: 0.5, // $0.50 per GB over limit
          extraMeetings: 0.05, // $0.05 per minute over limit
          extraRecordings: 2.0, // $2 per hour over limit
        },
        features: ['unlimited-channels', 'screen-sharing', 'file-sharing'],
      },
      {
        id: 'business',
        name: 'Business',
        pricePerSeat: 25.0, // $25/user/month
        minimumSeats: 10,
        included: {
          storage: 25, // 25 GB per user
          meetings: 500, // 500 meeting minutes per user
          recordings: 50, // 50 hours per user
        },
        usage: {
          extraStorage: 0.4, // Better overage rates
          extraMeetings: 0.03,
          extraRecordings: 1.5,
        },
        features: ['all-team-features', 'advanced-admin', 'sso', 'audit-logs', 'priority-support'],
      },
    ],
  },
})

on($.TeamSubscription.create, async (request) => {
  const plan = collaborationService.pricing.plans.find((p) => p.id === request.planId)

  if (request.seats < plan.minimumSeats) {
    throw new Error(`Minimum ${plan.minimumSeats} seats required for ${plan.name} plan`)
  }

  const subscription = await db.create($.TeamSubscription, {
    teamId: request.teamId,
    serviceId: collaborationService.id,
    planId: plan.id,
    seats: request.seats,
    pricePerSeat: plan.pricePerSeat,
    included: plan.included,
    usageRates: plan.usage,
    status: 'active',
    currentPeriodStart: new Date(),
    currentPeriodEnd: addMonths(new Date(), 1),
  })

  // Charge for seats
  const seatCharge = request.seats * plan.pricePerSeat

  await send($.Payment.charge, {
    teamId: request.teamId,
    amount: seatCharge,
    description: `${plan.name} - ${request.seats} seats`,
    subscriptionId: subscription.id,
  })

  return subscription
})

// Add/remove seats mid-month
on($.Subscription.updateSeats, async (updateRequest) => {
  const subscription = await db.findOne($.TeamSubscription, {
    id: updateRequest.subscriptionId,
  })

  const plan = collaborationService.pricing.plans.find((p) => p.id === subscription.planId)

  const seatDiff = updateRequest.newSeats - subscription.seats

  // Prorate the charge/credit
  const now = new Date()
  const periodLength = subscription.currentPeriodEnd.getTime() - subscription.currentPeriodStart.getTime()
  const timeRemaining = subscription.currentPeriodEnd.getTime() - now.getTime()
  const prorationFactor = timeRemaining / periodLength

  const proratedCharge = seatDiff * plan.pricePerSeat * prorationFactor

  if (proratedCharge > 0) {
    // Adding seats
    await send($.Payment.charge, {
      teamId: subscription.teamId,
      amount: proratedCharge,
      description: `Added ${seatDiff} seats (prorated)`,
      subscriptionId: subscription.id,
    })
  } else if (proratedCharge < 0) {
    // Removing seats - credit for next billing
    await db.create($.Credit, {
      teamId: subscription.teamId,
      amount: Math.abs(proratedCharge),
      reason: `Removed ${Math.abs(seatDiff)} seats`,
      expiresAt: subscription.currentPeriodEnd,
    })
  }

  await db.update(subscription, {
    seats: updateRequest.newSeats,
  })
})

// Track usage during month
on($.Meeting.ended, async (meeting) => {
  await db.create($.UsageRecord, {
    teamId: meeting.teamId,
    type: 'meeting',
    units: Math.ceil(meeting.durationSeconds / 60), // minutes
    timestamp: new Date(),
    metadata: {
      meetingId: meeting.id,
      participants: meeting.participants.length,
    },
  })
})

// End-of-month usage billing
on($.BillingCycle.ended, async (cycle) => {
  const subscriptions = await db.query($.TeamSubscription, {
    where: {
      serviceId: collaborationService.id,
      status: 'active',
    },
  })

  for (const subscription of subscriptions) {
    const plan = collaborationService.pricing.plans.find((p) => p.id === subscription.planId)

    // Calculate included limits for the team
    const totalLimits = {
      storage: subscription.seats * plan.included.storage,
      meetings: subscription.seats * plan.included.meetings,
      recordings: subscription.seats * plan.included.recordings,
    }

    // Get actual usage
    const usage = await getTeamUsage(subscription.teamId, cycle.start, cycle.end)

    // Calculate overages
    let overageTotal = 0
    const overageItems = []

    if (usage.storage > totalLimits.storage) {
      const overage = usage.storage - totalLimits.storage
      const cost = overage * plan.usage.extraStorage
      overageTotal += cost

      overageItems.push({
        description: `Extra Storage - ${overage.toFixed(2)} GB`,
        quantity: overage,
        rate: plan.usage.extraStorage,
        amount: cost,
      })
    }

    if (usage.meetings > totalLimits.meetings) {
      const overage = usage.meetings - totalLimits.meetings
      const cost = overage * plan.usage.extraMeetings
      overageTotal += cost

      overageItems.push({
        description: `Extra Meeting Minutes - ${overage} minutes`,
        quantity: overage,
        rate: plan.usage.extraMeetings,
        amount: cost,
      })
    }

    if (usage.recordings > totalLimits.recordings) {
      const overage = usage.recordings - totalLimits.recordings
      const cost = overage * plan.usage.extraRecordings
      overageTotal += cost

      overageItems.push({
        description: `Extra Recording Storage - ${overage.toFixed(2)} hours`,
        quantity: overage,
        rate: plan.usage.extraRecordings,
        amount: cost,
      })
    }

    // Create invoice
    const lineItems = [
      {
        description: `${plan.name} - ${subscription.seats} seats`,
        quantity: subscription.seats,
        rate: plan.pricePerSeat,
        amount: subscription.seats * plan.pricePerSeat,
      },
      ...overageItems,
    ]

    const total = subscription.seats * plan.pricePerSeat + overageTotal

    await send($.Invoice.create, {
      teamId: subscription.teamId,
      subscriptionId: subscription.id,
      period: { start: cycle.start, end: cycle.end },
      lineItems,
      total,
    })

    // Update period
    await db.update(subscription, {
      currentPeriodStart: cycle.end,
      currentPeriodEnd: addMonths(cycle.end, 1),
    })
  }
})

async function getTeamUsage(teamId: string, start: Date, end: Date) {
  const records = await db.query($.UsageRecord, {
    where: {
      teamId,
      timestamp: { gte: start, lte: end },
    },
  })

  return {
    storage: await getCurrentStorageGB(teamId),
    meetings: records.filter((r) => r.type === 'meeting').reduce((sum, r) => sum + r.units, 0),
    recordings: records.filter((r) => r.type === 'recording').reduce((sum, r) => sum + r.units, 0),
  }
}

async function getCurrentStorageGB(teamId: string): Promise<number> {
  const files = await db.query($.File, {
    where: { teamId },
  })

  const totalBytes = files.reduce((sum, f) => sum + f.sizeBytes, 0)
  return totalBytes / 1024 ** 3
}

Seat + Usage Examples:

  • Team: 5 seats @ $15 = $75/mo + overages
    • If 60 GB used (50 GB included) = $75 + (10 GB × $0.50) = $80
  • Business: 20 seats @ $25 = $500/mo + overages
    • If 600 GB used (500 GB included) = $500 + (100 GB × $0.40) = $540

Pattern 6: Tiered Subscription + Credits

Monthly subscription with included credits plus top-up options:

const apiGatewayService = await $.Service.create({
  name: 'API Gateway Service',
  type: $.ServiceType.APIGateway,

  pricing: {
    model: 'subscription-credits',
    plans: [
      {
        id: 'starter',
        name: 'Starter',
        monthlyFee: 29.0,
        includedCredits: 100000, // 100k API calls included
        features: ['basic-analytics', 'rate-limiting', 'caching'],
      },
      {
        id: 'growth',
        name: 'Growth',
        monthlyFee: 99.0,
        includedCredits: 500000, // 500k API calls included
        features: ['advanced-analytics', 'custom-domains', 'webhooks', 'transformations'],
      },
      {
        id: 'scale',
        name: 'Scale',
        monthlyFee: 299.0,
        includedCredits: 2000000, // 2M API calls included
        features: ['all-features', 'dedicated-infrastructure', 'sla-guarantee', 'priority-support'],
      },
    ],
    creditTopup: {
      packages: [
        { credits: 100000, price: 25.0 }, // $0.00025 per call
        { credits: 500000, price: 100.0 }, // $0.0002 per call
        { credits: 1000000, price: 180.0 }, // $0.00018 per call
      ],
      autoTopup: {
        enabled: true,
        threshold: 0.1, // Auto-topup at 10% remaining
        defaultPackage: 0, // Default to smallest package
      },
    },
  },
})

on($.Subscription.create, async (subscriptionRequest) => {
  const plan = apiGatewayService.pricing.plans.find((p) => p.id === subscriptionRequest.planId)

  const subscription = await db.create($.Subscription, {
    customerId: subscriptionRequest.customerId,
    serviceId: apiGatewayService.id,
    planId: plan.id,
    monthlyFee: plan.monthlyFee,
    status: 'active',
    currentPeriodStart: new Date(),
    currentPeriodEnd: addMonths(new Date(), 1),
  })

  // Charge monthly fee
  await send($.Payment.charge, {
    customerId: subscriptionRequest.customerId,
    amount: plan.monthlyFee,
    description: `${plan.name} - Monthly subscription`,
    subscriptionId: subscription.id,
  })

  // Add included credits
  await db.create($.CreditBalance, {
    subscriptionId: subscription.id,
    customerId: subscriptionRequest.customerId,
    credits: plan.includedCredits,
    type: 'included',
    periodStart: new Date(),
    periodEnd: addMonths(new Date(), 1),
  })

  return subscription
})

// Track API usage
on($.APIRequest.completed, async (apiRequest) => {
  const subscription = await db.findOne($.Subscription, {
    customerId: apiRequest.customerId,
    serviceId: apiGatewayService.id,
    status: 'active',
  })

  if (!subscription) return

  // Deduct 1 credit per API call
  const balance = await db.findOne($.CreditBalance, {
    subscriptionId: subscription.id,
    periodEnd: { gte: new Date() },
  })

  await db.update(balance, {
    credits: { decrement: 1 },
  })

  // Check for auto-topup
  const plan = apiGatewayService.pricing.plans.find((p) => p.id === subscription.planId)
  const threshold = plan.includedCredits * apiGatewayService.pricing.creditTopup.autoTopup.threshold

  if (balance.credits - 1 <= threshold && apiGatewayService.pricing.creditTopup.autoTopup.enabled) {
    // Trigger auto-topup
    const packageIndex = apiGatewayService.pricing.creditTopup.autoTopup.defaultPackage
    const topupPackage = apiGatewayService.pricing.creditTopup.packages[packageIndex]

    await send($.Payment.charge, {
      customerId: subscription.customerId,
      amount: topupPackage.price,
      description: `Credit Top-up - ${topupPackage.credits.toLocaleString()} API calls`,
      subscriptionId: subscription.id,
    })

    await db.create($.CreditBalance, {
      subscriptionId: subscription.id,
      customerId: subscription.customerId,
      credits: topupPackage.credits,
      type: 'topup',
      periodStart: new Date(),
      periodEnd: subscription.currentPeriodEnd,
    })

    await send($.Customer.notify, {
      customerId: subscription.customerId,
      type: 'auto-topup',
      message: `Added ${topupPackage.credits.toLocaleString()} credits for $${topupPackage.price}`,
    })
  }
})

// Monthly renewal
on($.BillingCycle.ended, async (cycle) => {
  const subscriptions = await db.query($.Subscription, {
    where: {
      serviceId: apiGatewayService.id,
      status: 'active',
    },
  })

  for (const subscription of subscriptions) {
    const plan = apiGatewayService.pricing.plans.find((p) => p.id === subscription.planId)

    // Get total topups this period
    const topups = await db.query($.CreditBalance, {
      where: {
        subscriptionId: subscription.id,
        type: 'topup',
        periodStart: { gte: cycle.start, lte: cycle.end },
      },
    })

    const totalTopupCost = topups.reduce((sum, t) => {
      const pkg = apiGatewayService.pricing.creditTopup.packages.find((p) => p.credits === t.credits)
      return sum + (pkg?.price || 0)
    }, 0)

    // Send usage summary
    const includedBalance = await db.findOne($.CreditBalance, {
      subscriptionId: subscription.id,
      type: 'included',
    })

    const includedUsed = plan.includedCredits - (includedBalance?.credits || 0)
    const totalTopupCredits = topups.reduce((sum, t) => sum + t.credits, 0)

    await send($.Email.send, {
      to: subscription.customer.email,
      template: 'usage-summary',
      data: {
        plan: plan.name,
        period: { start: cycle.start, end: cycle.end },
        includedCredits: plan.includedCredits,
        includedUsed,
        topupCredits: totalTopupCredits,
        totalUsed: includedUsed + totalTopupCredits,
        monthlyFee: plan.monthlyFee,
        topupCosts: totalTopupCost,
        totalCost: plan.monthlyFee + totalTopupCost,
      },
    })

    // Renew subscription and reset credits
    await send($.Payment.charge, {
      customerId: subscription.customerId,
      amount: plan.monthlyFee,
      description: `${plan.name} - Monthly subscription`,
      subscriptionId: subscription.id,
    })

    // Expire old balances
    await db.update($.CreditBalance, {
      where: {
        subscriptionId: subscription.id,
        periodEnd: { lte: cycle.end },
      },
      data: { status: 'expired' },
    })

    // Add new included credits
    await db.create($.CreditBalance, {
      subscriptionId: subscription.id,
      customerId: subscription.customerId,
      credits: plan.includedCredits,
      type: 'included',
      periodStart: cycle.end,
      periodEnd: addMonths(cycle.end, 1),
    })

    // Update subscription period
    await db.update(subscription, {
      currentPeriodStart: cycle.end,
      currentPeriodEnd: addMonths(cycle.end, 1),
    })
  }
})

Subscription + Credits Examples:

  • Starter ($29/mo) + 100k calls included
    • Use 150k calls → $29 + $25 topup = $54
  • Growth ($99/mo) + 500k calls included
    • Use 800k calls → $99 + $100 topup = $199
  • Scale ($299/mo) + 2M calls included
    • Use 2M calls → $299 (no topup needed)

Best Practices

1. Transparent Pricing Breakdown

Always show customers exactly what they're paying for:

async function generatePricingBreakdown(customerId: string, period: { start: Date; end: Date }) {
  const subscriptions = await db.query($.Subscription, {
    where: { customerId, status: 'active' },
  })

  const breakdown = {
    recurring: [],
    usage: [],
    oneTime: [],
    total: 0,
  }

  for (const subscription of subscriptions) {
    // Recurring fees
    breakdown.recurring.push({
      service: subscription.service.name,
      plan: subscription.plan.name,
      amount: subscription.baseFee || subscription.monthlyFee,
    })

    // Usage charges
    const usage = await getUsageCharges(subscription.id, period)
    if (usage.length > 0) {
      breakdown.usage.push({
        service: subscription.service.name,
        charges: usage,
      })
    }
  }

  // Calculate totals
  breakdown.total = [...breakdown.recurring, ...breakdown.usage.flatMap((u) => u.charges), ...breakdown.oneTime].reduce((sum, item) => sum + item.amount, 0)

  return breakdown
}

2. Usage Forecasting

Help customers predict costs:

async function forecastMonthlyBill(customerId: string) {
  const currentUsage = await getCurrentMonthUsage(customerId)
  const daysInMonth = 30
  const daysPassed = new Date().getDate()
  const projectionFactor = daysInMonth / daysPassed

  const forecast = {
    current: currentUsage.total,
    projected: currentUsage.total * projectionFactor,
    breakdown: currentUsage.breakdown.map((item) => ({
      ...item,
      projected: item.amount * projectionFactor,
    })),
    confidence: daysPassed >= 7 ? 'high' : 'low',
  }

  return forecast
}

3. Flexible Upgrades

Make it easy to switch between models:

async function suggestOptimalPricing(customerId: string) {
  const usage = await getHistoricalUsage(customerId, 6) // 6 months
  const averageMonthly = usage.reduce((sum, m) => sum + m.total, 0) / 6

  // Compare different pricing models
  const models = [
    { type: 'pay-as-you-go', cost: calculatePayAsYouGo(averageMonthly) },
    { type: 'subscription', cost: findBestSubscription(averageMonthly) },
    { type: 'hybrid', cost: findBestHybrid(averageMonthly) },
  ]

  const optimal = models.reduce((best, current) => (current.cost < best.cost ? current : best))

  return {
    current: { type: 'current', cost: averageMonthly },
    optimal,
    savings: averageMonthly - optimal.cost,
    savingsPercentage: ((averageMonthly - optimal.cost) / averageMonthly) * 100,
  }
}

Real-World Examples

Twilio (Communication Platform)

const pricing = {
  model: 'pay-as-you-go',
  services: {
    sms: { rate: 0.0079 }, // $0.0079 per SMS
    voice: { rate: 0.014 }, // $0.014 per minute
    video: { rate: 0.004 }, // $0.004 per participant-minute
  },
}

// No base fee, pure usage-based
// Example: 10k SMS + 1k voice minutes = $93

Stripe (Payment Processing)

const pricing = {
  model: 'transaction-percentage',
  standard: {
    percentage: 0.029, // 2.9%
    fixed: 0.3, // + $0.30 per transaction
  },
  volume: {
    threshold: 1000000, // $1M/month
    percentage: 0.027, // Custom pricing available
  },
}

// Example: $100k in payments = $3,200 in fees

AWS (Cloud Infrastructure)

const pricing = {
  model: 'multi-service-tiered',
  services: {
    compute: { tiered: true, rates: [...] },
    storage: { tiered: true, rates: [...] },
    transfer: { tiered: true, rates: [...] },
  },
  commitments: {
    reserved: { discount: 0.40 }, // 40% off with 1-3 year commit
    savings: { discount: 0.72 }, // Up to 72% with flexible commitment
  },
}

// Highly flexible, multiple dimensions

Next Steps

Resources