Key takeaways: GST tax math is a pure function of four inputs — taxable amount, rate, seller state, buyer state. expo-print beats HTML-to-PDF libraries for offline invoice generation. Never do money math in floating point: work in paise. Invoice numbers must reset every Indian financial year (April 1).
The GST Logic Problem
Wageasy is a mobile invoicing app we built for freelancers — focused on the Indian market where GST invoicing is mandatory above a certain turnover threshold. The technical challenge: generating correct, compliant GST invoices on a mobile device and exporting them as PDFs. (Product story and screenshots are in the Wageasy case study.)
India's GST system has two scenarios:
| Scenario | Condition | Tax applied |
|---|---|---|
| Intra-state | Seller and buyer in the same state | CGST + SGST (rate split equally) |
| Inter-state | Seller and buyer in different states | IGST (full rate) |
The rate is always the same (18% for most services) — only how it's split changes. But getting this wrong invalidates the invoice for the client's input tax credit claim.
Our tax calculation is a pure function:
function calculateGst(taxableAmountPaise: number, ratePercent: number, sellerState: string, buyerState: string): GstBreakdown {
const totalTax = Math.round((taxableAmountPaise * ratePercent) / 100);
if (sellerState === buyerState) {
const half = Math.round(totalTax / 2);
return { cgst: half, sgst: totalTax - half, igst: 0 };
}
return { cgst: 0, sgst: 0, igst: totalTax };
}The seller's state comes from their profile (set once in settings). The buyer's state comes from the client record. Note the amounts are integers in paise — more on that below.
PDF Generation on Mobile
This is where most React Native invoicing implementations struggle. Options we evaluated:
| Option | Verdict | Why |
|---|---|---|
| react-native-html-to-pdf | Rejected | Limited styling control, inconsistent output across platforms |
| Server-side generation | Rejected | Adds latency, requires connectivity — we wanted offline support |
| expo-print | Chosen | Renders HTML to PDF via the platform's native print APIs, works offline |
The approach: an HTML template string with the invoice data interpolated, then rendered to PDF. The share sheet handles delivery — WhatsApp, email, AirDrop, Drive — the user picks.
Key learnings:
- Use @page CSS to control PDF page size and margins
- Inline all styles — no external stylesheets
- Test the output on both iOS and Android — rendering differences exist
Invoice Number Management
GST law requires sequential invoice numbers that reset each financial year (April 1 in India). We store the sequence per financial year, generating numbers like "2025-26/001" that auto-increment and reset when the fiscal year changes. The financial-year key is derived from the invoice date, not the current date — backdated invoices land in the right sequence.
State Management: Zustand
We used Zustand for global state — invoices, clients, and user profile (including GSTIN and registered state). Simple and effective for this use case. No Redux boilerplate, no Context prop-drilling.
The Tricky Parts
Decimal precision. Financial calculations should never use floating point arithmetic directly. We ran all monetary calculations through a helper that works in paise (the smallest currency unit) to avoid rounding errors. 18% of ₹999.99 must come out the same on every device, every time — floats don't guarantee that, integers do.
HSN/SAC code validation. We store a reference table of valid SAC codes for common freelance services. Invalid codes on a GST invoice can trigger compliance notices.
Cross-state detection. India's 29 states and 8 union territories need a complete lookup table. We include both the full name and the GST state code (01 = Jammu & Kashmir, 27 = Maharashtra, etc.).
What's Live
Wageasy is on the App Store and Google Play. It handles GST invoice creation, PDF export, client management, and payment tracking. See the Wageasy case study for the full build breakdown.
Built by NOTchip](/). If you're building a fintech or invoicing product and need a [React Native team with this experience, reach out.