Billing Cycle
How to choose between monthly and annual billing, and how to price the annual discount to maximize cash flow and retention.
Why
The billing cycle decision is a trade-off between cash flow and conversion. Annual billing gives you 12 months of cash up front (improving runway and reducing dunning overhead by 12x) and locks the user in for a year (reducing churn by 20-40% because they cannot cancel mid-year without asking). The cost is a conversion hit at checkout — $348/year feels like more than $29/month even though it is the same, so you must offer a discount (typically 2 months free, so $348 → $290) to make annual the obvious choice. The right design is to show both, default to annual with the savings highlighted, and make monthly available for users who cannot get annual approved (common in enterprise procurement). The worst design is monthly-only, which leaves cash on the table and gives users an easy off-ramp every 30 days.
When
At launch, and revisit when you have enough data to measure annual-vs-monthly churn differential. If annual churn is not at least 30% lower than monthly, your annual discount is too small.
How
1. Set the monthly price as the anchor ($29/mo), then compute annual as 10x monthly (2 months free) — $290/year, displayed as "$24/mo billed annually"
2. On the pricing page, show a toggle (Monthly | Annual) with Annual selected by default and the savings ("Save $58/year") in green
3. In Stripe, create two Price objects on the same Product: one with interval=month, one with interval=year and a unit_amount 10/12 of monthly
4. When the user picks a cycle, pass the corresponding price_id to checkout — do not compute the discount in your app, let Stripe be the source of truth
5. For mid-cycle switches (monthly → annual), use stripe.subscriptions.update with proration enabled — Stripe credits the unused monthly time and charges the annual prorated
6. Send a renewal reminder 14 days before the annual renewal — annual users who forget to cancel and get renewed are the #1 source of chargebacks
7. Track the annual adoption rate (target > 60% of new subscriptions); if below 40%, increase the discount to 3 months free
8. For enterprise, offer multi-year (2-3 year) commitments at a further 10-15% discount — these lock in revenue and reduce sales cycle pressureExample
# Stripe: two prices on one product
const product = await stripe.products.create({ name: "Pro" });
const monthly = await stripe.prices.create({
product: product.id, unit_amount: 2900, currency: "usd",
recurring: { interval: "month" },
});
const annual = await stripe.prices.create({
product: product.id, unit_amount: 29000, currency: "usd", # 10x monthly = 2 months free
recurring: { interval: "year" },
});
# Pricing page toggle
<label class="toggle">
<input type="checkbox" checked={isAnnual} onChange={toggle} />
Annual — save $58/year
</label>
<p>{isAnnual ? "$24/mo billed annually" : "$29/mo"}</p>
# Mid-cycle upgrade monthly → annual
await stripe.subscriptions.update(sub.id, {
items: [{ id: sub.items.data[0].id, price: annual.id }],
proration_behavior: "create_prorations",
});Common Problems
- ⚠Computing the annual discount in your app instead of in Stripe — you display $290 but Stripe charges $348 because the price_id points to the wrong Price. Always let Stripe's price be the source of truth.
- ⚠Not sending a renewal reminder before annual renewal — the user forgot they subscribed, gets renewed for $290, and issues a chargeback. A 14-day reminder email cuts these chargebacks by 90%.
- ⚠Defaulting to monthly on the pricing page — users pick the default, so defaulting to monthly leaves the annual cash flow on the table. Default to annual with the savings highlighted.
Best Practices
- ✓Offer a 2-month annual discount (10x monthly) — this is the sweet spot where annual feels like a clear win without giving away too much revenue, and it is what most successful SaaS use.
- ✓Make the annual savings visible as both a percentage and an absolute amount — 'Save 17% ($58/year)' beats either alone because some users anchor on percent and some on dollars.
- ✓For users who downgrade from annual to monthly, ask why in a one-question survey — the answer is usually 'price' or 'uncertain about the product', and both are fixable.