Out of the box, Zoho Subscriptions handles the happy path. Monthly plan, fixed seats, credit card on file, auto-renew. Reality is messier. Here is how to configure for the messy parts without writing your own billing engine.
Mid-cycle seat changes: prorate or batch
Decide org-wide policy. Prorate-on-change is fair to the customer but creates invoice clutter. Batch-on-renewal is clean but feels punitive. Most B2B SaaS goes with: prorate up immediately, prorate down at next renewal. Configure under Plan, Proration Settings.
Upgrade: Prorate immediately, charge difference
Downgrade: Apply at next renewal cycle
Add-on: Prorate immediately
Remove add-on: Apply at next renewal cycle
Custom billing terms (net-30, net-60)
Default is “charge on issue date.” Enterprise customers want net-30. Create Payment Terms under Settings, Preferences. Assign per customer or per subscription. Subscription auto-generates the invoice on the issue date but the due date respects the term. Dunning rules then trigger at due-date+7, +14, +21 instead of from issue.
Mid-cycle plan changes that respect contract value
If a customer on Plan A ($1k/mo) switches to Plan B ($2k/mo) on day 10 of a 30-day cycle, Subscriptions will prorate the difference. But if they signed an annual commit, Plan B should ladder onto the existing commit, not re-start the term. Use the API:
// Change plan but preserve the original term end
PUT /subscriptions/{id}/changeplan
{
"plan": {"plan_code":"PLAN_B"},
"preserve_billing_cycles": true,
"end_of_term": "2026-12-31"
}
Discount codes that expire correctly
Apply a 20% discount for the first 3 months. Config: Coupon, Duration = Limited, Cycles = 3. After cycle 3, the discount falls off automatically. Tag the discount with a reason code so revenue reports can show net-of-discount and discount-cost separately.
Failed payment recovery
Auto-retry 3 times over 10 days, then notify CSM via Cliq. Do not silently downgrade or cancel. Most failures are expired cards, recoverable in one outbound message.
if(invoice.status == "payment_failed" && invoice.retry_count >= 3)
{
zoho.cliq.postToUser(account.CSM_Email, "Payment failed 3x for " + account.Name + " - $" + invoice.amount);
}
Push every event to CRM
Subscriptions emits webhooks for subscription_created, plan_changed, payment_received, renewal_upcoming. Pipe each to a CRM custom module Billing_Events. Now AE pulling up the account sees the billing timeline next to the deal history.
Revenue recognition basics
Subscriptions records cash. Books does the deferred revenue accounting. If you sell annual upfront, Subscriptions records the cash on day 1, Books amortizes 1/12th per month. Make sure the journal mapping is right or the CFO will have a quarter-end surprise.
What to do this week: pick your hardest customer, model their next 12 months in Subscriptions, and confirm proration and renewal behavior matches the contract. Fixing one customer reveals the policy gaps for all of them.