DEV Community

Atlas Whoff
Atlas Whoff

Posted on

Stripe Revenue Optimization: Trials, Annual Plans, Usage Billing, and Dunning

Most solo developers and indie hackers are leaving money on the table with their Stripe setup. The default integration handles payments — but the optimized setup maximizes revenue through trials, annual plans, usage-based billing, and smart recovery flows.

Free Trial Architecture

// Create a subscription with a 14-day trial
const subscription = await stripe.subscriptions.create({
  customer: customerId,
  items: [{ price: priceId }],
  trial_period_days: 14,
  payment_settings: {
    save_default_payment_method: 'on_subscription',
  },
  trial_settings: {
    end_behavior: { missing_payment_method: 'pause' }, // pause if no card added
  },
})

// In your webhook handler
case 'customer.subscription.trial_will_end':
  // Fires 3 days before trial ends
  await sendTrialEndingEmail(subscription.customer, subscription.trial_end)
  break
Enter fullscreen mode Exit fullscreen mode

Annual Plans with Savings

Annual plans improve cash flow and reduce churn. Display the monthly equivalent with savings badge:

const PLANS = {
  pro_monthly: { priceId: 'price_...', amount: 2900, interval: 'month' },
  pro_annual: { priceId: 'price_...', amount: 24900, interval: 'year' },
}

// Display: $99/year = $8.25/mo — save 17%
function getMonthlyCost(plan) {
  if (plan.interval === 'year') return plan.amount / 12
  return plan.amount
}

function getSavings(monthly, annual) {
  const annualMonthly = annual.amount / 12
  return Math.round((1 - annualMonthly / monthly.amount) * 100)
}
Enter fullscreen mode Exit fullscreen mode

Usage-Based Billing

Charge per API call, per seat, or per token:

// Create a metered price in Stripe
// aggregate_usage: 'sum' | 'max' | 'last_during_period'

// Report usage at the end of each billing period
async function reportUsage(subscriptionItemId: string, quantity: number) {
  await stripe.subscriptionItems.createUsageRecord(
    subscriptionItemId,
    {
      quantity,
      timestamp: Math.floor(Date.now() / 1000),
      action: 'increment', // or 'set'
    }
  )
}

// Track in your own DB, report to Stripe daily or on each API call
await redis.incr(`usage:${userId}:${month}`)
Enter fullscreen mode Exit fullscreen mode

Smart Dunning (Failed Payment Recovery)

Configure Stripe's Smart Retries in the dashboard. Also send proactive emails:

case 'invoice.payment_failed':
  const invoice = event.data.object
  const attemptCount = invoice.attempt_count

  if (attemptCount === 1) {
    // First failure -- send polite reminder
    await sendPaymentFailedEmail(invoice.customer_email, {
      updateUrl: stripe.billingPortal.sessions.create({ customer: invoice.customer }),
      nextAttempt: invoice.next_payment_attempt,
    })
  } else if (attemptCount >= 3) {
    // Final warning before cancellation
    await sendFinalWarningEmail(invoice.customer_email)
  }
  break

case 'customer.subscription.deleted':
  // Subscription cancelled after failed payments
  await downgradeUser(event.data.object.customer)
  await sendCancellationEmail(event.data.object.customer)
  break
Enter fullscreen mode Exit fullscreen mode

Customer Portal

Let customers manage their own subscription — no custom UI needed:

// app/api/billing/portal/route.ts
export async function POST(req: Request) {
  const session = await getServerSession()
  const user = await db.user.findUnique({ where: { id: session.user.id } })

  const portalSession = await stripe.billingPortal.sessions.create({
    customer: user.stripeCustomerId,
    return_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard/billing`,
  })

  return Response.json({ url: portalSession.url })
}
Enter fullscreen mode Exit fullscreen mode

The portal handles: upgrade/downgrade, cancel, payment method update, invoice history. Zero custom UI.

Proration on Plan Changes

// Upgrade mid-cycle -- Stripe handles proration automatically
await stripe.subscriptions.update(subscriptionId, {
  items: [{ id: subscriptionItemId, price: newPriceId }],
  proration_behavior: 'create_prorations', // default
  billing_cycle_anchor: 'unchanged', // keep existing renewal date
})
Enter fullscreen mode Exit fullscreen mode

The AI SaaS Starter at whoffagents.com ships with Stripe fully pre-wired: checkout, webhooks, customer portal, trial support, and subscription lifecycle handlers. $99 one-time.

Top comments (0)