How I Built Finwage — Payroll & HRMS with React and Django
AlgoBiz · 2026
June 25, 2026 · 15 min read
How I Built Finwage — Payroll & HRMS with React and Django
A deep dive into building Finwage, a payroll management system and HRMS using React, Django, PostgreSQL & AWS. Architecture decisions, salary calculations, attendance tracking, and lessons learned at AlgoBiz.
Finwage started as a simple payroll calculator and grew into a full HRMS handling attendance, leave management, salary processing, tax deductions, and employee self-service for multiple organizations. Here's how I built it at AlgoBiz using React, Django, PostgreSQL, and AWS.
The Problem
Most businesses in Kerala still manage payroll on spreadsheets. They track attendance manually, calculate salaries by hand, and forget about tax compliance until the deadline. We needed a system that handles the entire lifecycle: attendance in → salary calculation → tax deduction → payslip generation → bank disbursement file.
Architecture Overview
Finwage is a multi-tenant SaaS — each company gets isolated data with shared infrastructure. The frontend is React with TypeScript and Tailwind CSS for the dashboard. The backend is Django REST Framework with PostgreSQL for transactional data. AWS handles hosting (EC2), file storage (S3 for payslips), and email notifications (SES).
Frontend: React + TypeScript + Tailwind CSS\nBackend: Django REST Framework + Python 3.12\nDatabase: PostgreSQL 16 (multi-tenant with schema isolation)\nQueue: Celery + Redis (for payroll batch processing)\nStorage: AWS S3 (payslips, documents)\nHosting: AWS EC2 + RDS + CloudFront\nCI/CD: GitHub Actions → Docker → ECR → ECSAttendance Management
Attendance is the foundation of payroll. Finwage supports multiple clock-in methods: biometric device integration (via API), mobile GPS check-in, and manual entry with approval workflows. The tricky part is handling shift patterns — rotational shifts, split shifts, overtime calculation, and half-day logic. I modeled shifts as PostgreSQL JSONB columns with validation at the Django serializer level.
Salary Calculation Engine
Salary calculation looks simple until you account for Indian payroll complexity: PF (Provident Fund), ESI (Employee State Insurance), Professional Tax (state-specific), TDS (Tax Deducted at Source), LOP (Loss of Pay), overtime, bonuses, and reimbursements. I built a rule engine where each component is a Python class with a calculate() method. Components can depend on other components (PF depends on basic salary), so I use topological sorting to determine calculation order.
class SalaryComponent(ABC):\n @abstractmethod\n def calculate(self, context: PayrollContext) -> Decimal:\n pass\n\nclass BasicSalary(SalaryComponent):\n def calculate(self, ctx: PayrollContext) -> Decimal:\n working_days = ctx.attendance.working_days\n total_days = ctx.period.total_days\n return (ctx.employee.ctc_basic * working_days) / total_days\n\nclass PFContribution(SalaryComponent):\n depends_on = ['basic_salary']\n \n def calculate(self, ctx: PayrollContext) -> Decimal:\n basic = ctx.computed['basic_salary']\n return min(basic * Decimal('0.12'), Decimal('1800'))Payslip Generation
Payslips are generated as PDFs using WeasyPrint (HTML/CSS to PDF) with Jinja2 templates. Each company can customize their payslip template — logo, layout, which components to show. The generation runs as a Celery batch job because processing 500+ employees' payslips synchronously would timeout. We generate them, upload to S3, and email links to employees.
Multi-Tenant Architecture
Multi-tenancy in Django is tricky. I chose PostgreSQL schema-based isolation using django-tenants. Each company gets its own schema with identical table structures. Shared data (plans, billing) lives in the public schema. This gives us true data isolation (a bug in one tenant's query can never leak another tenant's data) while keeping infrastructure costs manageable.
Frontend Dashboard
The React dashboard is where HR managers spend their time. It shows attendance summaries, pending approvals, salary status, and compliance alerts. I used Recharts for salary trend visualizations and TanStack Table for the employee data grid (sorting, filtering, pagination on 1000+ rows). The calendar view for attendance uses a custom component built with date-fns.
Lessons Learned
- Indian payroll has edge cases that no documentation covers — you learn by getting it wrong
- Multi-tenant schema isolation is worth the complexity for financial data
- Batch processing with Celery prevents timeout issues on payroll runs
- Always store monetary values as Decimal, never float
- Generate audit logs for every salary calculation — compliance requires it
- PDF generation is slow — always do it async and cache the result
- Test with real payroll data (anonymized) — synthetic data misses edge cases
What's Next
We're adding AI-powered anomaly detection for attendance (flagging unusual patterns), automated tax filing integration, and a mobile app for employee self-service. Finwage started as a side project and now handles real payroll for multiple companies — the most rewarding kind of software to build.