If you searched "gst invoice razorpay" hoping to automate billing, here's the detail that changes the design: Razorpay Invoices can produce GST-compliant invoices, but only from the Dashboard. Razorpay's API reference says it directly: "You can only create non-GST Invoices via APIs." So if you were planning to call the Invoices API after every app or subscription payment and get a GST invoice back, that plan won't work. For anything automated, a GST invoice with Razorpay as the payment source means building a small invoice service of your own.
This post covers what Razorpay gives you, why automated flows need their own service, and the webhook-to-invoice pattern we'd use. Before going further: this is engineering guidance, not tax or legal advice. Your chartered accountant decides what your invoices must contain.
What Razorpay Invoices does for GST
On the Dashboard, Razorpay Invoices handles GST properly for manually created invoices. You add your GSTIN first (without it, the option to show tax rates per HSN/SAC code isn't available), set a place of supply, and give each item a tax rate, optional cess, tax-inclusive or exclusive pricing, and an HSN code (for goods) or SAC code (for services). Razorpay then splits tax into CGST and SGST, or IGST, based on place of supply.
That's a good fit for a services business that raises a few invoices a month by hand and wants a payment link on each one.
It doesn't fit:
- payments taken in a Flutter, React Native or web checkout through Orders;
- subscription renewals that happen at 3 AM;
- marketplaces or ed-tech portals with thousands of transactions;
- anything where invoice creation has to happen without a person clicking.
The API's invoice entity does have fields like hsn_code, sac_code, tax_rate and customer gstin, which makes it look supported. Go by the documented rule, not the schema: API-created invoices are non-GST. If that changes, Razorpay's API docs will say so, so check them when you start.
The GST invoice Razorpay flow we'd build: webhook to invoice service
The design that works: Razorpay confirms the money, your service issues the invoice.
- Checkout creates an order on your server with everything the invoice will need: line items, HSN/SAC codes, the rate in force, the customer's billing state and (for B2B) their GSTIN. Capture GSTIN at checkout, not afterwards by email.
- Razorpay sends
payment.capturedororder.paid(orsubscription.chargedfor renewals). Your webhook handler verifies the signature, deduplicates onx-razorpay-event-id, stores the event and returns 200. - A worker issues the invoice: allocates the next invoice number, computes tax, writes an immutable invoice record, then renders a PDF.
- Delivery happens last: email the PDF, show it in the app, push it to your accounting system.
Each step should be safe to repeat. Put a unique constraint on the Razorpay payment ID in the invoices table so a replayed webhook can't create a second invoice for one payment.
The details that cause trouble later
Invoice numbers
GST rules require a consecutive serial number, unique for the financial year, of no more than 16 characters. Two traps:
- Database sequences leave gaps. A Postgres sequence value consumed by a rolled-back transaction is gone. Use a counter row per series and financial year, incremented inside the same transaction that writes the invoice, so a rollback gives the number back.
- Financial year resets. Your series restarts in April. Put the year in the series key, for example
OL/26-27/000123, which also stays under 16 characters.
Place of supply and tax split
Intra-state supply gets CGST plus SGST; inter-state gets IGST. Which applies depends on your registered state and the place of supply, and for services the rules on place of supply have their own cases. Store the place of supply on the order, compute the split in one well-tested function, and have your accountant sign off on its test cases.
Snapshot, don't look up
Copy the rate, HSN/SAC code, your GSTIN, your address and the customer's details into the invoice record when you issue it. If a rate or a product's code changes next quarter, old invoices must not change with it.
Refunds
Never edit or delete an issued invoice. A refund (Razorpay sends refund.processed) should create a credit note that references the original invoice, with its own number series.
Rounding
Razorpay amounts are in paise, as integers. Do tax maths in integer paise too, decide whether you round per line or per invoice, and make the PDF total match the amount Razorpay captured exactly. Mismatches of one paisa produce surprisingly long reconciliation threads.
E-invoicing
Businesses above the GST e-invoicing turnover threshold must report B2B invoices to the Invoice Registration Portal and print the IRN and QR code on them. If you're over it, or close, add an IRP call to step 3 (usually through a GST Suvidha Provider) and treat it like another external dependency that can fail and retry. Ask your CA where you stand.
Where this lives in your stack
The invoice service sits on your backend. Your Flutter app, React Native app and web dashboard all just fetch the PDF from it. That's also why web and mobile teams benefit from doing it once: invoices come from one numbering series and one tax function, regardless of where the customer paid.
For small volumes, there's a middle option: take payment through Razorpay, then raise the GST invoice in accounting software that has an API for it. You still need the webhook handler and the idempotency, but not the PDF rendering and numbering.
We built GST-compliant invoicing, refunds and reconciliation for a production ed-tech payment portal that takes payments through multiple Indian gateways. If you need invoicing wired into a Razorpay checkout, orithLabs can build it as part of our e-commerce development work, and the first call is free. One last reminder: none of this is tax or legal advice. Get your invoice format and tax logic reviewed by your chartered accountant.