Backend, How to Build a Subscription Billing System in India (2026): Mandates, Proration, Dunning, GST Invoices, and Build vs BuyBackend
Backend

How to Build a Subscription Billing System in India (2026): Mandates, Proration, Dunning, GST Invoices, and Build vs Buy

RRRavi Rai·July 20, 2026·21 min read

A payment gateway moves rupees once. It authorizes a card or a UPI request, settles the money, and fires a success webhook. That is where most integration guides stop, and that is exactly where the real work of a subscription business begins. Recurring revenue is not one payment repeated. It is a relationship you have to manage on a schedule, forever, and no gateway sells you that layer.

In India the gap is wider than the Stripe-world tutorials admit. You cannot just save a card and re-charge it whenever you like. You register a mandate the customer's bank approves, you live inside RBI's e-mandate rules, you respect the UPI Autopay per-debit cap, and you stamp a GST-correct tax invoice on every renewal. Get any one of those wrong and the debit fails or the invoice does not match the money in your account.

This guide is the full build, in the order we actually build it: what a billing system is versus a gateway, the India mandate problem, the data model, the subscription state machine, proration maths, dunning, GST-compliant recurring invoices, the self-serve portal, revenue analytics, and an honest build-vs-buy call. We write it from production, not a vendor deck. Where a number depends on the current rulebook or your own pricing, we say so in the text, and you should verify it against your own setup before you ship.

What a subscription billing system actually is (and what a payment gateway is not)

A payment gateway collects money once. It authorizes a card, a UPI request, or a mandate, moves the rupees, and hands you a success webhook. That is the rail, and we covered how to pick it in our accept payments in India post, so we will not repeat it here.

A billing system manages the relationship after the money moves, on repeat, forever. It is the layer no gateway sells you. Seven jobs live here:

  • Plans and pricing: tiers, trials, coupons, metered usage
  • Subscription lifecycle: activate, upgrade, pause, cancel
  • Mandates: UPI Autopay, e-mandate, card-on-file recurring
  • Proration: mid-cycle plan changes, credits, refunds
  • Dunning: retry failed charges, notify, then suspend
  • Recurring GST invoices: correct place of supply, HSN/SAC, IRN
  • MRR and churn reporting: what is actually recurring, and what is leaking

Who has outgrown a raw gateway integration: Indian SaaS billing monthly, OTT and streaming, edtech, gym and coworking memberships, and D2C subscription boxes. The morning one card fails and nobody retries it, you have outgrown the gateway.

We write this from production, not a vendor doc. We run recurring billing inside PlugEV (metered EV session billing), CloudNX (INR hosting renewals), and MultiVendor CRM (per-tenant plans).

The thesis is simple: you will build this layer whether you design it on purpose or it grows by accident. The accidental version still runs. It just leaks money quietly.

The India mandate problem: UPI Autopay, e-NACH, and RBI e-mandate rules

In India, a recurring charge is not a saved card you re-charge whenever you like. You first register a mandate, a standing instruction the customer's bank approves, and every future debit runs against that mandate's limits and rules. Get the mandate wrong and the debit simply fails, no matter how clean your billing logic is.

UPI Autopay is the easiest to onboard, but it carries an AFA-free threshold of Rs 15,000 per debit. A debit at or below Rs 15,000 auto-debits hands-free; a debit above Rs 15,000 is allowed, but it triggers additional factor authentication (an OTP) on every single debit, which defeats the point of hands-free billing. That single number shapes your architecture. A Rs 24,000 annual plan cannot ride UPI Autopay hands-free, because a single debit above Rs 15,000 triggers an OTP every cycle. You either route high-value plans to another mandate type or split the amount into smaller debits that stay under the threshold. An enhanced Rs 1,00,000 AFA-free limit does exist, but only for specific categories (insurance premiums, mutual funds, and credit-card bills), not general SaaS.

e-NACH / e-mandate covers higher-value collections and bank-account debits, with per-mandate limits you set at registration. These are not capped at Rs 1,00,000: that figure is the enhanced AFA-free category limit, not a universal e-NACH ceiling. General e-NACH per-mandate limits can run much higher, and banks support well above Rs 1,00,000 (the exact ceiling varies by sponsor bank and gateway). Card e-mandates are the third rail for card-first customers. Your system should pick the mandate type per plan, not force one rail on everyone.

RBI e-mandate rules that become code. Two things you must build in: a pre-debit notification sent to the customer at least 24 hours before each charge, and additional factor of authentication on the first registration (and on debits above the AFA threshold). You also cannot store the raw card number. You work with network tokens only, the same tokenisation flow we cover in our accept-payments guide.

Model the mandate as a first-class object. Give it its own row and lifecycle: created, active, paused, revoked, expired. Do not bury the mandate as a hidden field on the subscription, because a paused or revoked mandate can outlive or precede a plan change, and you need that history when a debit bounces.

Every debit and every mandate event flows back through webhooks. Apply the same idempotent, callback-driven discipline we detail in our Razorpay webhooks post: one handler, keyed on event ID, safe to replay.

The data model that makes or breaks it

Get the schema wrong and every feature you bolt on later fights you. We have rebuilt billing tables for clients who baked prices into plans, and the migration is never a weekend job. Here is the shape that holds up.

Split product, plan, and price into three tables. A product is the thing you sell (your Pro tier). A plan is a billing shape (monthly, annual). A price is the actual amount. One plan carries many prices: monthly INR, annual INR, USD for overseas users, a Diwali promo. Never store the amount on the plan row. The day you launch annual billing, you will thank yourself.

Model subscriptions with subscription_items. A subscription links a customer to a plan, but the real charges live in subscription_items. That is how one customer holds a base seat, three extra seats, and an SMS add-on without you inventing a flat combined charge. Proration later reads from these items.

Invoices are append-only. Keep invoices, invoice_line_items, and credit_notes as separate records. Once an invoice is finalized you never edit it. To correct it you issue a credit note. This is not a preference, it is a GST requirement: a finalized tax invoice is an immutable document, and adjustments happen through credit notes with their own numbering series.

Mandates and payment_attempts get their own tables. An e-mandate (UPI Autopay, card, or eNACH under the RBI framework) has a lifecycle. Every debit, retry, and failure needs a row in payment_attempts so dunning has a real audit trail, not a status field you overwrite.

Money and immutability. Store amounts as integer paise, never floating point. Never hard-delete a billing row; soft-delete or void.

Tenant isolation from day one. In a multi-tenant SaaS, scope every billing table by tenant_id. See our per-tenant billing section for the isolation model.

The subscription lifecycle as a state machine

Every billing bug we have cleaned up traces back to a subscription being in a state nobody named. So name them first.

The six states worth modeling: trialing, active, past_due, paused, canceled, and expired. Fix the legal transitions and forbid the rest. trialing goes to active or canceled, never straight to expired. active slips to past_due when a debit fails, then either recovers to active or ends in canceled after dunning gives up. paused only comes from active and only returns to active.

Drive transitions from events, not a cron job guessing. A mandate debit succeeded, a debit failed, a cancel was requested: each is an event that moves the machine. Razorpay tells you this through webhooks (subscription.charged, subscription.halted), so handle them idempotently. Store the event ID, check it before you act, and a retried webhook never double-advances a subscription. See our Razorpay webhooks guide for the dedupe pattern.

Trials and coupons touch the invoice, not the mandate. A 14-day trial or a first-cycle 30% off changes what the first invoice charges. The e-mandate you registered still authorizes the full recurring amount (though on UPI Autopay any debit above ₹15,000 needs an OTP each time, so it is not truly hands-free above that threshold). Discount the line item, keep the mandate intact.

Pause and resume is a real Indian retention lever. Gym, OTT, and coworking members freeze for a month instead of canceling. A paused state that stops billing but keeps the mandate alive saves the relationship. Cancel does not.

The renewal engine is a scheduled job that finds due subscriptions, raises the GST invoice, and triggers the debit. Make it re-runnable: guard on invoice_already_raised_for_period so a re-run never bills twice.

Cancellation: cancel_at_period_end lets access run out the paid cycle; immediate cancel revokes now. Either way, cancel the mandate at the gateway so no orphan debit fires later.

Proration: the maths of mid-cycle upgrades and downgrades

Proration in one line: when a customer changes plans mid-cycle, charge them fairly for the part of the cycle they actually used.

Upgrade path. Credit the unused time on the old plan, charge the new plan immediately, and net the difference. The customer pays the gap now and their renewal date usually stays the same.

Downgrade path. Do not refund cash. Bank a credit and apply it against the next invoice. Refunds trigger reconciliation, RBI mandate mess, and support tickets you do not want.

Compute it without bugs. Derive a per-second or per-day rate from the full cycle, not a rounded monthly figure. Work in integer paise end to end and round once, at the very end. Example: a customer on the ₹999 plan upgrades to ₹2,499 on day 20 of a 30-day cycle. Unused credit is 999 × (10/30) = ₹333. New plan charge for the remaining 10 days is 2,499 × (10/30) = ₹833. They pay ₹500 today.

Proration is invoicing, not just arithmetic. A downgrade credit becomes a GST credit note against the original invoice. An upgrade charge is a new taxable line at 18% GST. Get the document type wrong and your GSTR filing breaks.

The harder cousin: metered billing. On PlugEV the charge depends on kWh consumed, not a flat plan, so we rate usage events into paise continuously and invoice the total at cycle close. Same integer discipline, more moving parts.

We test the proration engine like a payments path. An off-by-one-day error looks tiny in a unit test and bleeds real money at scale, so we run fixed-clock cases across every upgrade, downgrade, and trial-conversion boundary.

Dunning: how to stop involuntary churn eating your MRR

Not all churn is a customer walking away. Voluntary churn is someone who cancels on purpose. Involuntary churn is someone who wanted to stay but whose auto-debit failed. Industry estimates put involuntary churn (failed payments and expired mandates) at roughly 20 to 40 percent of total subscription churn, and that slice is recoverable if you build for it.

The retry ladder. Don't hammer a failed mandate the same day. Space retries out, for example day 1, 3, 5, and 7, and tune the schedule to the actual failure reason. In India the common ones are insufficient balance, a paused mandate, and bank downtime. Insufficient balance clears near salary dates, so a day-5 or day-7 retry often lands when day-1 won't. Razorpay Subscriptions runs a retry schedule but gives you limited control over its timing and messaging; a rules-based cadence like this, with your own retry days and pre-debit copy, needs a dedicated tool like Chargebee or a custom dunning layer.

Pre-debit notifications feed dunning. The RBI-mandated pre-debit heads-up (24 hours before the debit) is also your best churn-reducer, because customers top up their account when they see it coming. Treat it as the first rung of dunning, not a compliance checkbox.

Multi-channel, but respect the rules. Send reminders in-app, over email, and via WhatsApp or SMS. SMS and promotional WhatsApp go through DLT-registered templates, so wire your dunning copy into the same DLT-compliant notification pipeline you already use for transactional messages, or they won't deliver.

Grace period and the cancel path. Decide how long a subscription stays past_due before it expires. A 7 to 14 day grace window is common. Keep the customer's access live during grace so a recovered payment restores service without a break, then downgrade or expire once retries are exhausted.

Measure it. Track recovery rate (failed payments recovered divided by total failed) per cohort and per retry day. Without that number, dunning is hope. With it, you can move retry days and reminder channels and see the MRR you saved.

GST-compliant recurring invoices

Invoice on the settled amount, not the attempt. When Razorpay or a NACH mandate fires a renewal, wait for the webhook that confirms the debit cleared, then generate the invoice against that captured payment. If you invoice on the gross plan price at the attempt stage, a failed or partial debit leaves you with an invoice that never matches the money in your account. Tie every invoice to a real payment_id, not a scheduled charge.

Keep the number series audit-clean. Invoice numbers must run sequentially per financial year and per GSTIN. Reset the counter on 1 April, prefix by GSTIN if you bill from more than one state, and never reuse or skip. For downgrades, mid-cycle proration credits, and refunds, issue a GST credit note linked to the original invoice rather than editing it. That keeps the sequence intact when an auditor pulls it.

Get place of supply right. For SaaS the customer's registered state decides the tax split. Same state as your GSTIN gets CGST + SGST, a different state gets IGST, both at 18%. Your billing engine has to store and read the customer state before it stamps the line, so capture GSTIN and state at signup. Use SAC 9983 / 998314 for software services on every recurring line.

Know the boundary. This is the billing engine cutting the invoice. IRP submission and IRN generation are a separate step once you cross the e-invoicing turnover threshold (see our GST e-invoicing post), and booking these into the ledger belongs in Zoho Books or Tally (see those posts).

Send it automatically. On every successful renewal, auto-email the PDF invoice and expose the full history in the customer self-serve portal so nobody raises a ticket asking for it.

The customer self-serve billing portal

The portal exists so your inbox stops filling with billing tickets. A customer should log in and, without emailing anyone, view past invoices, download the GST invoice (with your GSTIN, HSN/SAC, and tax split), see the next debit date and amount, and upgrade, downgrade, pause, or cancel. If they can't do these six things themselves, support does it for them.

Mandate re-registration is the one to get right. When a card expires or a customer revokes a mandate at their bank, the debit silently fails and that is churn nobody noticed. Detect the dead mandate, email/WhatsApp the customer, and drop them into a one-click re-registration flow before the next cycle.

Make cancel a save flow, not a button. Before the final confirm, offer pause (reuse your lifecycle pause state) or a downgrade to a cheaper tier. Many "cancels" are really "too expensive this month."

Let them change payment method and switch rail: a customer moving to an annual plan whose per-debit amount clears the UPI Autopay AFA-free threshold (INR 15,000) would face an OTP on every debit, so it makes sense to shift them from UPI Autopay to e-NACH, and you should surface that automatically.

Isolation is non-negotiable. Every route is authenticated and tenant-scoped; customer A can never load customer B's invoice by guessing an ID.

Each self-serve action is a support ticket that never gets raised. That is how it pays for itself.

Revenue analytics: MRR, churn, and revenue recognition

The number your investors ask for first is MRR (monthly recurring revenue), and ARR is just MRR times 12. But the headline figure hides the story. Break it into new MRR (fresh signups), expansion MRR (upgrades and add-ons), contraction MRR (downgrades), and churned MRR (cancellations). That breakdown is what tells you whether growth is healthy or you are just outrunning a leak.

Watch gross vs net revenue churn. Gross churn counts only what you lost. Net churn subtracts expansion, so a few big upgrades can mask a base that is quietly bleeding. If net churn looks fine but gross churn is climbing, your base is leaky and the wins are covering for it.

Here is the trap your payment dashboard sets: cash collected is not revenue recognized. An annual plan paid upfront in January is INR 12,000 in the bank but only INR 1,000 of recognized revenue that month. The rest is deferred revenue, released month by month.

Razorpay cannot give you any of this. The gateway sees transactions, not subscription state, so MRR, churn, and deferred schedules only exist if your billing layer computes and stores them.

Then feed the accountant. Reconcile recognized revenue to the books by posting into Zoho Books or Tally (invoices, deferred entries, GST), so finance and product agree on one number.

Finally, run cohort retention: group customers by signup month and track how many still pay at month 3, 6, 12. That single report tells you if the product is actually sticky.

Build vs buy: custom vs Razorpay Subscriptions vs Chargebee vs Zoho

Razorpay Subscriptions sits on the rail you already use. It handles the hard part: creating e-mandates (UPI AutoPay, card, eNACH), charging on schedule, and running the RBI-mandated pre-debit notification flow. Plans and basic trials work out of the box. The ceiling shows up fast. Proration is thin, so mid-cycle upgrades and metered usage need your own math. Dunning runs a retry schedule, but you get limited control over its timing and messaging, not the rules-based cadence you would design yourself. And GST invoices are still yours to shape: Razorpay gives the charge event, you build the compliant tax invoice.

Chargebee and Zoho Subscriptions are mature. You get full lifecycle (pause, resume, downgrade), a hosted customer portal, coupons, and MRR/churn analytics without building any of it. The tradeoff is the cost model and where your data lives. Chargebee moves to percentage-of-revenue or per-tier billing that grows as you grow; Zoho is per-tier by customer/user count. Either way your subscription data now lives outside your app, and every report or automation crosses an API boundary.

Custom genuinely wins when proration is complex or metered, when your pricing is product-specific, when billing is tightly coupled to your app, and when a revenue-share bill would hurt at scale. Our PlugEV metered EV-charging billing is exactly the case bought tools handle badly: per-kWh usage, session-level rating, no clean plan fit.

The hybrid most teams should pick: keep Razorpay as the rail and mandate engine, and build the domain layer yourself (lifecycle, proration, dunning rules, GST invoicing, analytics). You own the data without rebuilding payments.

Honest INR ranges: a full custom billing layer (plans, proration, dunning, recurring GST invoicing, a self-serve portal, and MRR/churn analytics) is roughly Rs 6,00,000 to Rs 18,00,000 to build, then low maintenance. A more focused build, scoped to your specific plan and mandate mix rather than the whole platform, is roughly Rs 3,50,000 to Rs 9,00,000 and usually a 4 to 10 week build. A bought tool typically runs about 0.5 to 0.75 percent of billed revenue, or roughly Rs 15,000 to Rs 60,000 and up per month depending on tier and volume (vendor pricing changes, so check the current pricing page). Compare over three years, not month one. At scale, revenue-share overtakes a one-time build.

Decision checklist:

  • Plan complexity: flat tiers or metered/usage-based?
  • Mandate mix: UPI AutoPay, cards, eNACH?
  • GST needs: simple or multi-state, e-invoicing?
  • Analytics depth: dashboard cards or cohort-level?
  • Is billing core to your product, or a side feature?

How we build subscription billing, and when we tell you not to

Our default shape is a Node.js backend where the lifecycle (plan changes, proration, dunning retries, reconciliation) is covered by tests, because these are money paths and deserve to be treated like it. That is the core of our Node.js development work.

We ship this from lived work, not theory. The same engine runs metered per-session billing, INR renewals with GST invoices, and per-tenant plans across the products we operate, so the edge cases we describe here are ones we have actually hit and fixed.

The honest part: if you run a single flat plan and a few hundred subscribers, Razorpay Subscriptions is enough, and we will say so. Custom earns its cost once metering, mid-cycle proration, or data ownership make it pay: a focused build scoped to your specific plan and mandate mix (not the whole platform) is usually a 4 to 10 week build at roughly INR 3.5 to 9 lakh.

Frequently asked questions

Should I build a custom subscription billing system or just use Razorpay Subscriptions, Chargebee, or Zoho Subscriptions?

If you run a single flat plan with a few hundred subscribers, Razorpay Subscriptions is genuinely enough and we would tell you not to build. Custom starts to earn its cost when you have metered or usage-based pricing, complex mid-cycle proration, or a reason to keep your subscription data inside your own app instead of a vendor's. The middle path most teams should take is hybrid: keep Razorpay as the rail and mandate engine, and build the lifecycle, proration, dunning, and GST invoicing yourself.

What is the UPI Autopay limit for subscriptions in India, and how do I bill high-value annual plans above it?

UPI Autopay carries an AFA-free threshold of Rs 15,000 per debit: a charge at or below that auto-debits hands-free, while a charge above it is allowed but triggers additional factor authentication (an OTP) on every debit, which defeats hands-free billing. For a high-value annual plan you have two clean options: route it to an e-NACH or card e-mandate, whose per-mandate limits are set at registration and can run well above Rs 1,00,000 (that Rs 1,00,000 figure is the enhanced AFA-free category limit, not a universal e-NACH ceiling), or split the amount into smaller scheduled debits that each stay under the threshold. Your system should pick the mandate type per plan rather than forcing every customer onto one rail.

How do recurring GST invoices work when a customer upgrades or downgrades mid-cycle?

An upgrade adds a new taxable line for the prorated difference, invoiced at 18% GST like any normal charge. A downgrade does not edit the original invoice, because a finalized tax invoice is immutable; instead you issue a GST credit note linked to it, banking the credit against the next cycle rather than refunding cash. In both cases the place of supply (the customer's registered state) decides whether the split is CGST plus SGST or IGST, so capture GSTIN and state at signup.

What does it cost to build a custom subscription billing system in India in 2026?

A full custom billing layer, meaning plans, proration, dunning, recurring GST invoicing, a self-serve portal, and MRR/churn analytics, runs roughly Rs 6,00,000 to Rs 18,00,000 to build, then stays low on maintenance. A more focused build, scoped to your specific plan structure and mandate mix rather than the whole platform, is usually a 4 to 10 week engagement at around Rs 3,50,000 to Rs 9,00,000. Compare either against a bought tool, which typically runs about 0.5 to 0.75 percent of billed revenue, or roughly Rs 15,000 to Rs 60,000 and up per month depending on tier and volume (vendor pricing changes, so check the current pricing page), and do the sum over three years, because revenue-share pricing overtakes a one-time build as you scale.

How do I reduce involuntary churn from failed auto-debits and expired mandates?

Build a retry ladder that spaces attempts out (for example day 1, 3, 5, and 7) and tunes to the failure reason, since insufficient-balance failures often clear near salary dates. Treat the RBI pre-debit notification as your first dunning rung, send reminders across in-app, email, and DLT-registered WhatsApp or SMS, and keep access live through a 7 to 14 day grace window. Detect dead mandates and drop customers into a one-click re-registration flow, then track recovery rate per retry day so you can see the MRR you save.

Can I keep Razorpay for payments but build the subscription and billing logic myself?

Yes, and this is the setup we recommend for most growing teams. Razorpay stays as the rail and mandate engine, handling e-mandate creation, scheduled charges, and the pre-debit notification flow, while you build the domain layer on top: subscription lifecycle, proration, dunning rules, GST invoicing, and revenue analytics. You own your subscription data and reporting without taking on the burden of rebuilding payments or PCI-scope card handling.

The honest summary: this layer gets built either way, on purpose or by accident, and the accidental version is the one that leaks money quietly. If you want a straight build-vs-buy call for your specific plan structure, mandate mix, and GST footprint, talk to us and we will give you the real answer, even when it is "just use Razorpay Subscriptions."

RR
Written by
Ravi Rai

Founder of buildbyRaviRai, a freelance web development agency based in Noida, India. 5+ years shipping Next.js, WordPress, Shopify, and Laravel projects for clients in India, USA, Canada, and the UK.

Keep Reading

Backend, Zoho Books Integration in India (2026): Connect Your Website, Store, or CRM to Your Books, What It Costs, and Build vs BuyBackend

Zoho Books Integration in India (2026): Connect Your Website, Store, or CRM to Your Books, What It Costs, and Build vs Buy

Zoho Books integration in India: connect your website, store, or CRM to your books. REST API, webhooks, GST/IRN handling, 2026 costs, and build vs buy.

Backend, Tally Integration With Your Website or Custom Software (India, 2026): How It Actually Works, What It Costs, and When to Build vs BuyBackend

Tally Integration With Your Website or Custom Software (India, 2026): How It Actually Works, What It Costs, and When to Build vs Buy

Tally integration with a website or custom software, explained the way it actually works: the XML API on port 9000, TDL and ODBC, build versus buy, real 2026 India costs, and how to stop duplicate vouchers in TallyPrime.

Backend, DLT-Compliant Notification Service Architecture in India (2026): SMS, WhatsApp, and Email Without Silent OTP FailuresBackend

DLT-Compliant Notification Service Architecture in India (2026): SMS, WhatsApp, and Email Without Silent OTP Failures

A DLT-compliant notification service architecture that stops silent OTP failures: template registry in CI, idempotent queues, SMS fallback, DLR audits.

Backend, How to Actually Accept Payments in India in 2026: UPI, Cards, Autopay, and What Each One Really CostsBackend

How to Actually Accept Payments in India in 2026: UPI, Cards, Autopay, and What Each One Really Costs

Taking money online in India works differently from almost anywhere else: a national rail that is free to accept, an RBI rulebook rewritten in 2025, recurring payments that do not behave like a US SaaS, and card rules that forbid storing a card number at all. This is the honest, practical guide for founders and developers: payment gateway versus aggregator, how UPI and cards and autopay actually work, what each method really costs, how to collect from customers abroad, and a checklist to go live without leaking money on fees or failing renewals.

Backend, REST API Design Best Practices 2026: How to Build APIs That LastBackend

REST API Design Best Practices 2026: How to Build APIs That Last

An API is a promise. Once a mobile app, a frontend, or a partner depends on yours, changing it is painful and breaking it is worse. The good news is that good API design is mostly a set of well-worn conventions, not genius. This is the practical guide to designing REST APIs in 2026: resource naming, using HTTP properly, versioning, auth, pagination, errors, idempotency, and the mistakes that haunt teams later.

bR

buildbyRaviRai Assistant

Replies within 24 hours

Chat on WhatsApp

+91 74289 19927 · Replies within 24 hours

Pick a quick message to start a conversation on WhatsApp, or type your own below. Your message pre-fills, you hit send from WhatsApp.

Or type your own

We'll send your message via WhatsApp Web or the WhatsApp app.