All articles
Guides

Razorpay or Stripe for an Indian SaaS? What Building Both Taught Us

We built subscriptions on Razorpay twice and card payments on Stripe once, chosen by where the customers were. Rupee customers: start with Razorpay. Mostly abroad: Stripe. Nearly every billing bug we fixed lived in our code around the gateway.

Surya Kumar Muni5 November 20268 min read

Quick answer

If your company is Indian and customers pay in rupees, start with Razorpay: subscriptions on cards and UPI, with the RBI e-mandate journey built in. If your company and customers are mostly abroad, start with Stripe. An Indian SaaS with some customers abroad can run rupee and dollar plans on one gateway, chosen by billing address. Most billing bugs live in renewals, upgrades, webhooks and cancellations, not checkout.

We have built subscription billing on Razorpay twice and card payments on Stripe once, and the reason for each was the same: where the customers were. The paid tool tiers on countrydevs.com and Storafleet, our multi-cloud storage product, sell to people who mostly pay in rupees, so they run on Razorpay. Trainr, a fitness marketplace we built for a company in Dubai, charges clients in dirhams, so it runs on Stripe.

That is also our answer for most founders. If your company is Indian and your customers pay in rupees, start with Razorpay. If your company and customers are mostly outside India, start with Stripe. The more useful part of this post is what came after the choice, because nearly every billing bug we fixed lived in code we wrote around the gateway, not in the gateway.

The comparison, as we would fill it in today

Question Razorpay Stripe
Who it is built for Indian businesses selling to Indian customers Businesses selling internationally
Signing up as an Indian company Routine Has been by invitation. Check before you design around it
UPI for subscriptions Yes, through UPI mandates Not the centre of its India offer
RBI e-mandate journey Part of its subscription product Depends on how you are set up in India
International cards and currencies Supported, once enabled on the account Its main strength
Where we used it countrydevs.com tool tiers, Storafleet Trainr, card payments in dirhams

We have left fees out on purpose. Both companies publish them, both change them, and what matters is your own mix of payment methods, ticket sizes and international share on the day you decide.

The row that decides most Indian SaaS cases is the e-mandate one. Recurring card and UPI payments in India run on mandates under the RBI's rules: debits up to ₹15,000 can run without an OTP once the customer has registered the mandate, the customer must be told at least 24 hours before each debit (merchant, amount and date), and plans above the limit need the customer to approve each charge. A gateway that handles that journey inside its subscription product saves you from building it, and from explaining to customers why their card was charged without the notice they were owed.

Razorpay: what went wrong after checkout

Checkout worked early on both of our Razorpay builds. Then we built renewals, upgrades and cancellations, and the interesting problems arrived. In rough order of how much they cost us:

Webhooks that were never read. Razorpay signs each webhook with an HMAC-SHA256 over the exact bytes it sent, so the check has to run on the raw request body. On Storafleet the raw-body hook was mounted on the wrong path. The JSON parser had already eaten the body, every signature check failed, and every event was answered 400. Nothing looked broken from the outside. The fix had two parts: verify on the raw bytes, and save every signed event before doing any work, reply inside Razorpay's five-second window, then process it, replaying failures on a schedule.

Two handlers writing the same money. Several events arrive for one payment, some before the subscription event, some again on every renewal. At one point the browser's verify call and the webhook both fulfilled the same order: two invoices, and the customer's credit drained twice. The rule now is that one event, the subscription charge, writes the invoice and the new period end. Everything else only updates status.

An upgrade that could not happen. Razorpay will not update a subscription that was authorised by UPI or e-mandate. Our first upgrade code had been written for card mandates and had never met a UPI one. A tester paid the prorated difference, Razorpay captured it, our handler tried to update the subscription, got a 400, retried five times and gave up. The stored error message read "[object Object]", because the SDK puts the detail somewhere other than where we were looking. Now an upgrade collects the difference as a one-off payment and then moves the customer: in place for card mandates, or by cancelling and creating a new subscription that starts when the paid period ends for UPI.

A cancellation that only cancelled on our side. On Storafleet, cancelling a plan once changed our own database and nothing else, so Razorpay would have kept charging the card. Our end-to-end tests passed because they only read local state. Every path that ends a paid subscription now calls Razorpay's cancel API and retries if it fails.

Paid, and shown as free. On countrydevs.com a tester's UPI subscription was active at Razorpay while our side showed the free tier for at least 19 minutes, because the events that would have changed the tier had not been processed. After four rounds of fixes we stopped treating our database as the truth. Razorpay is the source, our records are a cache of it, a billing page read pulls the subscription first, and every subscribe, upgrade, downgrade, cancel or resume is followed by reconcile passes over the next minute and a half.

Two smaller ones are worth knowing before you write a line. A Razorpay plan's amount and period cannot be edited once it exists, so a price change means a new plan, and Storafleet's sync job creates one per price version, billing cycle and currency. And the subscription signature uses a different field order from one-off orders, so copying the order formula rejects every genuine payment. The full list, with each fix, is on our Razorpay subscriptions integration page.

Stripe: one webhook, one consumer

Trainr was a different shape of problem. Clients in the UAE buy trainer packages, classes, upgrades and add-ons by card, and get refunds for downgrades and cancelled classes. We switched it to live Stripe payments in August 2026, and version 1.0.3 of the Android app was the first production build to take them.

The decisions that mattered were the same ones Razorpay taught us, applied from the start:

  • prices come from the server, never the app. A package carries a monthly price and sessions per month, and the server works out the sessions, the total and the price per session, so a modified request cannot pick its own number;
  • Stripe sends three events (payment succeeded, payment failed, charge refunded) to a single webhook on one backend, verified against the raw body, so no event is processed twice by two services that each think they own it;
  • a refund already recorded under its Stripe refund ID is never booked again, however many times the event arrives.

None of that is clever. It is the boring version of payments, and boring is what you want from money code. Our Stripe payments integration page describes how we wire it into new builds.

Storafleet's two attempts at charging in dollars

Storafleet is sold by an Indian LLP through Razorpay, but storage buyers think in dollars. It took two designs.

In June 2026 we priced plans in dollars and collected the rupee equivalent at a daily exchange rate, locked per invoice, with a 3% buffer. For one-off payments it worked. Then we built renewals and hit the plan rule above: a Razorpay subscription charges a fixed amount on a plan you cannot edit, so a converted rupee price either drifts away from the dollar price or needs a new plan every day. Neither is a product.

Once international cards were enabled on the account in July 2026, we switched. Each plan version now has a rupee price and a dollar price, both set by a person. Checkout picks the currency from the billing address and locks the subscription to the currency actually charged, because invoices, credits and refunds all have to be worked out in that currency. The exchange rate survives only for reporting, and revenue dashboards show rupees and dollars side by side, never added together.

That route, one gateway with two currencies, is what we would try first for an Indian SaaS with some customers abroad. Two gateways chosen by billing country is the other option, at the cost of a second integration, two sets of webhooks and a reconciliation that spans both. It starts paying for itself only when the international share is large.

GST invoices are your job

The gateway moves money. It does not, by itself, give you a correct Indian tax invoice. Your billing code has to number invoices in a financial-year series with no gaps, give credit notes their own series, and get the tax split right.

Storafleet's payment path was audited before payments were switched on, and the audit logged 35 findings, of which 33 were closed by 16 July 2026, each with its own acceptance test. One of them: the first invoice of every financial year was numbered 00000. Another class of bug we designed out: a number reserved before the invoice was validated, so a failure burned it and left a gap. Invoices are now validated first and numbered second. Our glossary has the detail on an invoice number series, and the guide to GST e-invoicing for software covers when e-invoicing applies. If you ever start holding customers' money rather than passing it through, read the RBI payment aggregator rules before you build.

How we would decide for you

  • Indian company, customers paying in rupees, many wanting UPI renewals: Razorpay, with UPI mandates designed in from the first sprint.
  • Company and customers mostly outside India: Stripe.
  • Indian company, a minority of customers abroad: Razorpay with rupee and dollar plans by billing address, as Storafleet does.
  • A large international share as well as Indian customers: both gateways, behind one billing layer of your own.

Whichever you pick, keep the gateway behind your own plans, invoices and entitlement checks, so a second gateway or a switch is a contained project instead of a rewrite. That is most of what our SaaS platform work looks like on the billing side, and the SaaS development page covers the decisions around it. Before launch, run the billing items on our SaaS launch checklist: a full cycle in test mode, each payment method separately, a lost webhook, a duplicated one, a failed renewal and an invoice across the April boundary.

And write the reconcile job before launch. The customer who paid and was shown the free plan will not wait 19 minutes to tell you about it.

What we could not confirm

  • Current gateway fees. Both publish them and they change often, so compare them for your own payment mix on the day you decide.
  • Stripe's current India onboarding position. It has been by invitation; check with Stripe for your business.
  • Production renewal volumes on Storafleet. Its Razorpay subscriptions were built and tested, but as of 16 September 2026 no production customer had been charged on them.

Which header identifies a duplicate Razorpay webhook event?

Razorpay documents the x-razorpay-event-id header as unique per event, which makes it the right key for spotting repeats. Keying on the payment or subscription ID instead makes later, different events for the same payment look like duplicates, so real charges or refunds get skipped.

What is the smallest prorated upgrade Razorpay will process on a subscription?

Razorpay's immediate update works out a prorated difference of at least ₹0.50, and it refuses updates on UPI and e-mandate subscriptions altogether. On countrydevs.com we work out the unused part of the cycle in paise ourselves, collect it as a one-off payment, and change the plan once that payment is captured.

Should a SaaS choose a customer's currency from their IP address?

We would not. Use the billing address, and lock the subscription to the currency it was first charged in. An IP address changes when the customer travels or uses a VPN, and invoices, credits and refunds all have to be worked out in the currency actually charged.

Why store subscription prices in paise and cents rather than rupees and dollars?

Because decimal amounts drift when you add, split and convert them, and a paisa out on an invoice is still wrong. Store money as whole numbers in minor units, convert only for display, and never sum amounts across currencies.

Your project

Building subscription billing for customers in India and abroad?

Tell us what you are building and the one journey that must work. We reply with the questions that shape the plan, and every project starts with a two-week Discovery Sprint.

Start a project →

Part ofPayments in apps: every page on the topic, in one place