.do

Monetization Strategies

Pricing models and revenue strategies for services

Learn how to price, bill, and generate revenue from your services.

Pricing Models

Per-Use Pricing

Charge based on actual usage:

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

// Per-unit pricing
const translationService = await $.Service.create({
  name: 'Document Translator',
  pricing: {
    model: 'per-unit',
    unit: 'word',
    rate: 0.02, // $0.02 per word
    minimumCharge: 5.0,
  },
})

on($.ServiceResult.delivered, async (result) => {
  if (result.serviceId !== translationService.id) return

  const wordCount = result.metadata.wordCount
  const cost = Math.max(wordCount * translationService.pricing.rate, translationService.pricing.minimumCharge)

  await send($.Payment.charge, {
    customerId: result.customerId,
    amount: cost,
    description: `Translation: ${wordCount} words`,
    breakdown: {
      words: wordCount,
      rate: translationService.pricing.rate,
      subtotal: wordCount * translationService.pricing.rate,
      minimum: translationService.pricing.minimumCharge,
      total: cost,
    },
  })
})

Tiered Pricing

Different rates for different volume levels:

// Volume-based tiers
const imageService = await $.Service.create({
  name: 'Image Generator',
  pricing: {
    model: 'tiered',
    tiers: [
      { min: 0, max: 10, rate: 2.0 }, // First 10 images: $2 each
      { min: 11, max: 50, rate: 1.5 }, // Next 40 images: $1.50 each
      { min: 51, max: 100, rate: 1.0 }, // Next 50 images: $1 each
      { min: 101, max: Infinity, rate: 0.75 }, // 100+: $0.75 each
    ],
  },
})

function calculateTieredPrice(quantity: number, tiers: any[]): number {
  let cost = 0
  let remaining = quantity

  for (const tier of tiers) {
    if (remaining <= 0) break

    const tierSize = tier.max - tier.min + 1
    const unitsInTier = Math.min(remaining, tierSize)

    cost += unitsInTier * tier.rate
    remaining -= unitsInTier
  }

  return cost
}

on($.ServiceResult.delivered, async (result) => {
  if (result.serviceId !== imageService.id) return

  const quantity = result.metadata.imageCount
  const cost = calculateTieredPrice(quantity, imageService.pricing.tiers)

  await send($.Payment.charge, {
    customerId: result.customerId,
    amount: cost,
    description: `${quantity} images generated`,
  })
})

Subscription Pricing

Recurring charges with usage limits:

// Monthly subscription
const contentService = await $.Service.create({
  name: 'Content Generator Pro',
  pricing: {
    model: 'subscription',
    plans: [
      {
        id: 'starter',
        name: 'Starter',
        price: 49.0,
        interval: 'month',
        limits: {
          articles: 10,
          words: 15000,
          images: 20,
        },
        features: ['basic-templates', 'seo-optimization'],
      },
      {
        id: 'professional',
        name: 'Professional',
        price: 149.0,
        interval: 'month',
        limits: {
          articles: 50,
          words: 100000,
          images: 100,
        },
        features: ['all-templates', 'seo-optimization', 'plagiarism-check', 'priority-support'],
      },
      {
        id: 'enterprise',
        name: 'Enterprise',
        price: 499.0,
        interval: 'month',
        limits: {
          articles: Infinity,
          words: Infinity,
          images: Infinity,
        },
        features: ['all-features', 'dedicated-account-manager', 'custom-templates', 'api-access'],
      },
    ],
    overage: {
      articles: 5.0,
      words: 0.01,
      images: 1.0,
    },
  },
})

// Check subscription limits
on($.ServiceRequest.created, async (request) => {
  if (request.serviceId !== contentService.id) return

  // Get customer subscription
  const subscription = await db.findOne($.Subscription, {
    customerId: request.customerId,
    serviceId: contentService.id,
    status: 'active',
  })

  if (!subscription) {
    return await send($.ServiceRequest.reject, {
      requestId: request.id,
      reason: 'No active subscription',
    })
  }

  // Check usage limits
  const usage = await db.findOne($.Usage, {
    subscriptionId: subscription.id,
    period: getCurrentBillingPeriod(subscription),
  })

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

  if (usage.articles >= plan.limits.articles) {
    // Calculate overage charge
    const overageCost = contentService.pricing.overage.articles

    await send($.Payment.charge, {
      customerId: request.customerId,
      amount: overageCost,
      description: 'Overage: Additional article',
      type: 'overage',
    })
  }

  // Execute service...
})

Value-Based Pricing

Price based on value delivered:

// Price optimization service
const optimizationService = await $.Service.create({
  name: 'Price Optimizer',
  pricing: {
    model: 'value-based',
    valueShare: 0.1, // 10% of value created
    minimumFee: 100.0,
    maximumFee: 10000.0,
    calculation: 'projected-revenue-increase',
  },
})

on($.ServiceResult.delivered, async (result) => {
  if (result.serviceId !== optimizationService.id) return

  // Calculate value created
  const currentRevenue = result.inputs.currentRevenue
  const projectedIncrease = result.outputs.projectedIncrease

  const valueCreated = projectedIncrease
  let fee = valueCreated * optimizationService.pricing.valueShare

  // Apply bounds
  fee = Math.max(fee, optimizationService.pricing.minimumFee)
  fee = Math.min(fee, optimizationService.pricing.maximumFee)

  await send($.Payment.charge, {
    customerId: result.customerId,
    amount: fee,
    description: 'Price Optimization (10% of projected value)',
    breakdown: {
      currentRevenue,
      projectedIncrease,
      valueShare: optimizationService.pricing.valueShare,
      calculatedFee: valueCreated * optimizationService.pricing.valueShare,
      finalFee: fee,
    },
  })
})

Dynamic Pricing

Adjust prices based on demand, customer, or context:

// Dynamic pricing engine
const aiService = await $.Service.create({
  name: 'AI Content Generator',
  pricing: {
    model: 'dynamic',
    baseRate: 50.0,
    factors: {
      demand: { weight: 0.3, min: 0.8, max: 1.5 },
      customerTier: { weight: 0.2, discount: true },
      timeOfDay: { weight: 0.1, offPeak: 0.9 },
      quality: { weight: 0.2, premium: 1.3 },
      urgency: { weight: 0.2, rush: 1.5 },
    },
  },
})

async function calculateDynamicPrice(service, request, context) {
  let price = service.pricing.baseRate
  const factors = []

  // Demand factor
  const demandLevel = await getCurrentDemandLevel(service.id)
  const demandMultiplier = mapRange(demandLevel, 0, 100, service.pricing.factors.demand.min, service.pricing.factors.demand.max)
  price *= demandMultiplier
  factors.push({ name: 'demand', level: demandLevel, multiplier: demandMultiplier })

  // Customer tier discount
  const customer = await db.findOne($.Customer, { id: request.customerId })
  if (customer.tier) {
    const discount = customer.tier.discount || 0
    price *= 1 - discount
    factors.push({ name: 'customerTier', tier: customer.tier.name, discount })
  }

  // Time of day
  const hour = new Date().getHours()
  if (hour >= 0 && hour < 8) {
    // Off-peak
    price *= service.pricing.factors.timeOfDay.offPeak
    factors.push({ name: 'timeOfDay', period: 'off-peak', multiplier: 0.9 })
  }

  // Quality level
  if (request.inputs.quality === 'premium') {
    price *= service.pricing.factors.quality.premium
    factors.push({ name: 'quality', level: 'premium', multiplier: 1.3 })
  }

  // Urgency
  if (request.inputs.urgent) {
    price *= service.pricing.factors.urgency.rush
    factors.push({ name: 'urgency', rush: true, multiplier: 1.5 })
  }

  return {
    price: Math.round(price * 100) / 100,
    baseRate: service.pricing.baseRate,
    factors,
  }
}

on($.ServiceResult.delivered, async (result) => {
  if (result.serviceId !== aiService.id) return

  const pricing = await calculateDynamicPrice(aiService, result.request, result.context)

  await send($.Payment.charge, {
    customerId: result.customerId,
    amount: pricing.price,
    description: 'AI Content Generation',
    breakdown: {
      baseRate: pricing.baseRate,
      factors: pricing.factors,
      finalPrice: pricing.price,
    },
  })
})

Freemium Model

Free tier with paid upgrades:

const summarizationService = await $.Service.create({
  name: 'Document Summarizer',
  pricing: {
    model: 'freemium',
    free: {
      limits: {
        daily: 5,
        monthlyWords: 10000,
      },
      features: ['basic-summaries'],
    },
    paid: [
      {
        id: 'pro',
        price: 19.0,
        interval: 'month',
        limits: {
          daily: 100,
          monthlyWords: 500000,
        },
        features: ['basic-summaries', 'detailed-summaries', 'multi-language'],
      },
      {
        id: 'business',
        price: 99.0,
        interval: 'month',
        limits: {
          daily: Infinity,
          monthlyWords: Infinity,
        },
        features: ['all-features', 'api-access', 'priority-processing'],
      },
    ],
  },
})

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

  const customer = await db.findOne($.Customer, { id: request.customerId })

  // Check if customer has paid plan
  const subscription = await db.findOne($.Subscription, {
    customerId: customer.id,
    serviceId: summarizationService.id,
    status: 'active',
  })

  let limits, features
  if (subscription) {
    const plan = summarizationService.pricing.paid.find((p) => p.id === subscription.planId)
    limits = plan.limits
    features = plan.features
  } else {
    limits = summarizationService.pricing.free.limits
    features = summarizationService.pricing.free.features
  }

  // Check daily limit
  const usage = await getUsageToday(customer.id, summarizationService.id)
  if (usage.count >= limits.daily) {
    return await send($.ServiceRequest.reject, {
      requestId: request.id,
      reason: 'Daily limit reached',
      upgrade: {
        message: 'Upgrade to Pro for unlimited daily summaries',
        plans: summarizationService.pricing.paid,
      },
    })
  }

  // Check word limit
  if (usage.words + request.inputs.wordCount > limits.monthlyWords) {
    return await send($.ServiceRequest.reject, {
      requestId: request.id,
      reason: 'Monthly word limit reached',
      upgrade: {
        message: 'Upgrade for higher limits',
        plans: summarizationService.pricing.paid,
      },
    })
  }

  // Execute service (free for subscribers, would be charged for pay-per-use)
  // ...
})

Billing Implementation

Invoice Generation

// Monthly invoice generation
on($.BillingPeriod.ended, async (period) => {
  // Get all active subscriptions
  const subscriptions = await db.query($.Subscription, {
    where: { status: 'active' },
  })

  for (const subscription of subscriptions) {
    // Get usage for period
    const usage = await db.findOne($.Usage, {
      subscriptionId: subscription.id,
      periodId: period.id,
    })

    // Calculate charges
    const service = await db.findOne($.Service, { id: subscription.serviceId })
    const plan = service.pricing.plans.find((p) => p.id === subscription.planId)

    let total = plan.price

    // Add overage charges
    const overages = []
    for (const [metric, limit] of Object.entries(plan.limits)) {
      if (usage[metric] > limit) {
        const overage = usage[metric] - limit
        const overageCost = overage * service.pricing.overage[metric]
        total += overageCost

        overages.push({
          metric,
          limit,
          actual: usage[metric],
          overage,
          cost: overageCost,
        })
      }
    }

    // Create invoice
    const invoice = await db.create($.Invoice, {
      customerId: subscription.customerId,
      subscriptionId: subscription.id,
      periodId: period.id,
      lineItems: [
        {
          description: `${plan.name} - ${period.start} to ${period.end}`,
          amount: plan.price,
        },
        ...overages.map((o) => ({
          description: `Overage: ${o.metric} (${o.overage} over limit)`,
          amount: o.cost,
        })),
      ],
      subtotal: total,
      tax: total * 0.08, // 8% tax
      total: total * 1.08,
      dueDate: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000), // 14 days
    })

    // Send invoice to customer
    await send($.Invoice.send, {
      invoiceId: invoice.id,
      customerId: subscription.customerId,
    })
  }
})

Payment Processing

// Process payment
on($.Payment.charge, async (charge) => {
  try {
    // Get customer payment method
    const customer = await db.findOne($.Customer, { id: charge.customerId })
    const paymentMethod = await db.findOne($.PaymentMethod, {
      customerId: customer.id,
      default: true,
    })

    if (!paymentMethod) {
      throw new Error('No payment method on file')
    }

    // Process with payment provider
    const result = await processPayment({
      amount: charge.amount,
      currency: charge.currency || 'USD',
      paymentMethod: paymentMethod.token,
      description: charge.description,
      metadata: {
        serviceId: charge.serviceId,
        requestId: charge.requestId,
      },
    })

    // Record successful payment
    await db.create($.Payment, {
      customerId: charge.customerId,
      amount: charge.amount,
      status: 'succeeded',
      providerId: result.id,
      providerData: result,
      createdAt: new Date(),
    })

    // Update service request
    if (charge.requestId) {
      await db.update($.ServiceRequest, {
        where: { id: charge.requestId },
        data: { paid: true, paymentId: result.id },
      })
    }

    await send($.Payment.succeeded, {
      paymentId: result.id,
      customerId: charge.customerId,
      amount: charge.amount,
    })
  } catch (error) {
    // Record failed payment
    await db.create($.Payment, {
      customerId: charge.customerId,
      amount: charge.amount,
      status: 'failed',
      error: error.message,
      createdAt: new Date(),
    })

    await send($.Payment.failed, {
      customerId: charge.customerId,
      amount: charge.amount,
      error: error.message,
    })

    // Retry logic
    if (charge.retry < 3) {
      setTimeout(
        () => {
          send($.Payment.charge, {
            ...charge,
            retry: (charge.retry || 0) + 1,
          })
        },
        1000 * 60 * 60
      ) // Retry in 1 hour
    }
  }
})

Usage Tracking

// Track service usage
on($.ServiceResult.delivered, async (result) => {
  // Get or create usage record
  const subscription = await db.findOne($.Subscription, {
    customerId: result.customerId,
    serviceId: result.serviceId,
    status: 'active',
  })

  if (!subscription) return

  const period = getCurrentBillingPeriod(subscription)

  const usage =
    (await db.findOne($.Usage, {
      subscriptionId: subscription.id,
      periodId: period.id,
    })) ||
    (await db.create($.Usage, {
      subscriptionId: subscription.id,
      periodId: period.id,
      metrics: {},
    }))

  // Update usage metrics
  const updates = {}
  for (const [metric, value] of Object.entries(result.usage)) {
    updates[`metrics.${metric}`] = { increment: value }
  }

  await db.update(usage, updates)

  // Check if approaching limits
  const plan = await getPlan(subscription)
  for (const [metric, limit] of Object.entries(plan.limits)) {
    const current = usage.metrics[metric] || 0
    const percentage = current / limit

    if (percentage >= 0.8 && percentage < 0.9) {
      await send($.Customer.notify, {
        customerId: subscription.customerId,
        type: 'usage-warning',
        message: `You've used 80% of your ${metric} limit`,
        data: { metric, current, limit },
      })
    } else if (percentage >= 0.9) {
      await send($.Customer.notify, {
        customerId: subscription.customerId,
        type: 'usage-critical',
        message: `You've used 90% of your ${metric} limit`,
        data: { metric, current, limit },
      })
    }
  }
})

Revenue Optimization

A/B Testing Prices

// Price experimentation
const pricingExperiment = await $.Experiment.create({
  name: 'Content Service Pricing Test',
  serviceId: contentService.id,
  variants: [
    {
      id: 'control',
      pricing: { rate: 50.0 },
      traffic: 0.4,
    },
    {
      id: 'lower',
      pricing: { rate: 39.0 },
      traffic: 0.3,
    },
    {
      id: 'higher',
      pricing: { rate: 69.0 },
      traffic: 0.3,
    },
  ],
  metrics: ['conversion-rate', 'revenue', 'customer-lifetime-value'],
  duration: 30, // days
})

// Assign variant to customer
on($.ServiceRequest.created, async (request) => {
  if (request.serviceId !== contentService.id) return

  // Get or assign variant
  let assignment = await db.findOne($.ExperimentAssignment, {
    experimentId: pricingExperiment.id,
    customerId: request.customerId,
  })

  if (!assignment) {
    assignment = await assignVariant(pricingExperiment, request.customerId)
  }

  // Use variant pricing
  const variant = pricingExperiment.variants.find((v) => v.id === assignment.variantId)
  const pricing = variant.pricing

  // Track conversion
  await db.create($.ExperimentEvent, {
    experimentId: pricingExperiment.id,
    variantId: variant.id,
    customerId: request.customerId,
    event: 'service-requested',
    pricing: pricing.rate,
  })

  // Process with variant pricing...
})

// Analyze results
on($.Experiment.complete, async (experiment) => {
  const results = await analyzeExperiment(experiment)

  // Winner: highest revenue per customer
  const winner = results.variants.reduce((best, current) => (current.revenuePerCustomer > best.revenuePerCustomer ? current : best))

  // Update service pricing
  await db.update(experiment.service, {
    pricing: winner.pricing,
  })

  await send($.Team.notify, {
    message: `Pricing experiment complete. Winner: ${winner.id} with ${winner.revenuePerCustomer}/customer`,
    data: results,
  })
})

Pricing Analytics

// Revenue analytics dashboard
async function getRevenueAnalytics(serviceId: string, period: string) {
  const service = await db.findOne($.Service, { id: serviceId })

  // Total revenue
  const totalRevenue = await db.aggregate($.Payment, {
    where: {
      serviceId,
      status: 'succeeded',
      createdAt: { gte: getStartOfPeriod(period) },
    },
    sum: 'amount',
  })

  // Revenue by pricing model
  const byModel = await db.aggregate($.Payment, {
    where: { serviceId },
    groupBy: 'pricingModel',
    sum: 'amount',
    count: true,
  })

  // Customer segments
  const byTier = await db.query(
    `
    SELECT c.tier, SUM(p.amount) as revenue, COUNT(DISTINCT p.customerId) as customers
    FROM payments p
    JOIN customers c ON p.customerId = c.id
    WHERE p.serviceId = ? AND p.status = 'succeeded'
    GROUP BY c.tier
  `,
    [serviceId]
  )

  // Cohort analysis
  const cohorts = await calculateCohortRevenue(serviceId)

  // Churn analysis
  const churn = await calculateChurnRate(serviceId, period)

  return {
    totalRevenue,
    byModel,
    byTier,
    cohorts,
    churn,
    averageTransactionValue: totalRevenue / byModel.reduce((sum, m) => sum + m.count, 0),
    customerLifetimeValue: await calculateCLV(serviceId),
  }
}

Best Practices

1. Transparent Pricing

Always show customers what they'll pay:

// Price preview before execution
on($.ServiceRequest.created, async (request) => {
  // Calculate price
  const estimate = await estimatePrice(request)

  // Show to customer
  await send($.Customer.priceEstimate, {
    customerId: request.customerId,
    requestId: request.id,
    estimate: {
      base: estimate.base,
      additions: estimate.additions,
      discounts: estimate.discounts,
      total: estimate.total,
      breakdown: estimate.breakdown,
    },
    requireApproval: estimate.total > 100, // Require approval for large charges
  })
})

2. Fair Billing

Only charge for successful deliveries:

on($.ServiceRequest.fail, async (request) => {
  // Refund if already charged
  if (request.paymentId) {
    await send($.Payment.refund, {
      paymentId: request.paymentId,
      reason: 'Service execution failed',
    })
  }
})

3. Clear Communication

Notify customers about charges:

on($.Payment.succeeded, async (payment) => {
  await send($.Email.send, {
    to: payment.customer.email,
    template: 'payment-receipt',
    data: {
      amount: payment.amount,
      service: payment.service.name,
      description: payment.description,
      date: payment.createdAt,
    },
  })
})

Next Steps