Founder & Lead Engineer, RAITHub
A school fee management system turns a fee structure (tuition, transport, exams) into invoices and instalments per student, collects payments, sends reminders, applies concessions and late fees, and reconciles each payment in a ledger. Off the shelf, openSIS includes billing and fees from its Essential plan at $8 per staff member a month billed annually (openSIS pricing). Build when your fee rules or payment rails fit no product.
If you would rather have it built for you, see how RAITHub would build this below.
This guide is for school bursars, school groups and EdTech founders who sell fee collection to schools. It goes deeper on fees than the school management system guide, which covers timetables, attendance and roles. RAITHub has not shipped a school fee product. The payment experience below is real: bKash, Nagad and SSLCommerz shipped in TheSkinProof, the founder's own venture (not a client), and Stripe rent collection shipped in PropDesk. The school-specific parts are engineering guidance, labelled that way. Where tax or receipt rules come up, it is general information; confirm with your adviser.
What should a school fee management system do?
Eight jobs, in roughly the order a bursar meets them in a term. A product that does the first four well and the last four badly is the usual reason schools fall back to spreadsheets.
| Job | What it means in practice | Where it goes wrong |
|---|---|---|
| Fee structure | Fee heads (tuition, transport, exam, lab, admission) priced per class, term and sometimes per route or subject | Prices hard-coded, so next year's increase means a developer |
| Instalments and due dates | A term fee split into 1, 3 or 10 instalments, each with a due date | Rounding: three instalments of 333.33 never add up to 1,000.00 |
| Concessions | Sibling discounts, staff-child waivers, scholarships, one-off hardship reductions | Discounts applied by editing the invoice, leaving no record of why |
| Late fees | A flat or daily charge after a grace period, often with a cap | Charged twice by a job that ran twice |
| Collection | Card, mobile wallet, bank transfer, cash at the office | A payment marked paid because the browser said so |
| Receipts | A numbered receipt per payment, downloadable by the parent | Gaps or duplicates in the receipt sequence |
| Reminders | Messages before and after each due date, stopped once paid | A reminder sent to a parent who paid an hour ago |
| Reconciliation | Every gateway settlement matched to ledger entries, daily | Done by eye at month end, if at all |
Should a school buy fee software, use Excel, or build its own?
Most single schools should buy a school system with a fees module, or start with a hosted invoicing tool. Build when you sell fee collection to many schools, when parents pay through local rails a product does not support, or when your concession and instalment rules fit nothing on the market.
| Option | Example and cited price | Choose this when | Watch out for |
|---|---|---|---|
| Spreadsheet | Excel or Google Sheets, usually already paid for | Under a few hundred students, one bursar, payments by bank transfer | No audit trail, manual reminders, reconciliation by eye |
| Off-the-shelf school system with fees | openSIS Essential, $8 per staff member a month billed annually, lists "Billing and fees management"; Advanced at $12 adds a QuickBooks integration (openSIS pricing) | One school or a small group with standard fee heads and card or bank payments | Check which local gateways it supports before you sign |
| Hosted invoicing (no-code) | Stripe Invoicing Starter at 0.4% per invoice, capped at $2.00, on top of card fees of 2.9% + 30 cents for US domestic cards (Stripe pricing) | Card-paying parents, a modest number of invoices, no complex concessions | Fee heads, siblings and late-fee rules live in your head, not the tool |
| Custom build | A fee module on your own database, quoted per scope | You sell to many schools, need bKash or SSLCommerz alongside cards, or have rules no product models | Money code needs the most testing of anything a school runs |
Prices were checked on 2 October 2026 and illustrate pricing models; this is not a ranking. Trial any product with your most complicated family: three children, one on a scholarship, two bus routes, and a parent who pays half by card and half in cash.
Can I manage school fees in Excel, and when does it stop working?
Yes, for a while. Size is rarely the problem: an Excel worksheet holds 1,048,576 rows by 16,384 columns (Microsoft: Excel specifications and limits). What breaks is everything around the rows:
- No history. When a balance changes, the sheet shows the new number, not who changed it or why. Fee disputes with parents need the why.
- Concurrent edits. Two staff taking cash at two counters, or the bursar and an accountant editing at once, overwrite each other.
- Reminders are manual. Someone filters for overdue rows and sends messages by hand, and the filter is out of date the moment a payment lands.
- Online payments do not write back. A gateway can confirm a payment in seconds; the sheet learns about it when someone types it in.
- Formulas drift. A sibling discount formula copied down a column, then edited in one row, silently changes one family's fees.
A practical trigger to move: when you take payments online, or when more than one person edits the fee sheet. Either one turns the spreadsheet from a record into a guess.
How should a fee ledger store money so balances are always right?
Store amounts as integers in the currency's minor unit, with the currency beside every amount, and record money movements as an append-only ledger rather than editing a balance column. Minor units are not always cents: Stripe expects 1000 to charge 10 USD but 10 to charge 10 JPY, because the yen has no decimals (Stripe: supported currencies). A minimal PostgreSQL sketch written for this post:
CREATE TABLE fee_head (
id uuid PRIMARY KEY,
school_id uuid NOT NULL REFERENCES school(id),
code text NOT NULL, -- 'TUITION', 'TRANSPORT', 'EXAM'
name text NOT NULL,
UNIQUE (school_id, code)
);
CREATE TABLE invoice (
id uuid PRIMARY KEY,
student_id uuid NOT NULL REFERENCES student(id),
term_id uuid NOT NULL REFERENCES term(id),
currency char(3) NOT NULL, -- 'BDT', 'USD'
issued_on date NOT NULL
);
CREATE TABLE instalment (
invoice_id uuid NOT NULL REFERENCES invoice(id),
seq smallint NOT NULL,
due_on date NOT NULL,
amount_minor bigint NOT NULL CHECK (amount_minor > 0),
PRIMARY KEY (invoice_id, seq)
);
-- Positive = the family owes more; negative = the balance goes down.
CREATE TABLE ledger_entry (
id bigserial PRIMARY KEY,
student_id uuid NOT NULL REFERENCES student(id),
invoice_id uuid REFERENCES invoice(id),
fee_head_id uuid REFERENCES fee_head(id),
kind text NOT NULL,
amount_minor bigint NOT NULL,
currency char(3) NOT NULL,
reason text, -- 'Sibling concession, 2nd child'
gateway text, -- 'bkash', 'sslcommerz', 'stripe', 'cash'
gateway_ref text, -- the gateway's transaction id
created_by uuid NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
CHECK (
(kind IN ('charge', 'late_fee', 'refund') AND amount_minor > 0) OR
(kind IN ('concession', 'payment', 'write_off') AND amount_minor < 0)
),
UNIQUE (gateway, gateway_ref) -- a replayed confirmation cannot pay twice
);
-- A family's balance is a sum, never a stored number.
SELECT student_id, currency, SUM(amount_minor) AS balance_minor
FROM ledger_entry
GROUP BY student_id, currency;
Four decisions matter more than the SQL:
- Nothing is edited. A wrong charge is reversed by a new entry, not deleted. Grant the application role INSERT and SELECT on the ledger, not UPDATE or DELETE. The audit log design guide covers the same pattern for every other change.
- Concessions are entries, not edited prices. A sibling discount is a negative line with a reason, so the invoice still shows the full fee and what was taken off.
- The unique gateway reference is the idempotency guard. If a gateway confirms the same payment twice, the second insert fails and the family is not credited twice. (PostgreSQL treats NULLs as distinct, so cash entries without a reference are unaffected.)
- Late fees come from a job with a key. Generate late fees from a scheduled job that records which instalment and which day it charged, so a job that runs twice charges once.
How do you split a fee into instalments without losing a cent?
Divide in integer minor units and hand the remainder out one unit at a time, so the instalments always add up to the invoice. A TypeScript sketch written for this post:
type Instalment = { seq: number; dueOn: string; amountMinor: number }
export function splitInstalments(totalMinor: number, dueDates: string[]): Instalment[] {
if (!Number.isSafeInteger(totalMinor) || totalMinor < 0) {
throw new Error('totalMinor must be a non-negative integer')
}
if (dueDates.length === 0) throw new Error('need at least one due date')
const n = dueDates.length
const base = Math.floor(totalMinor / n)
const remainder = totalMinor - base * n
return dueDates.map((dueOn, i) => ({
seq: i + 1,
dueOn,
// the first "remainder" instalments carry one extra minor unit
amountMinor: base + (i < remainder ? 1 : 0),
}))
}
// 1,000.00 over three dates: 333.34 + 333.33 + 333.33 = 1,000.00
splitInstalments(100000, ['2027-01-10', '2027-02-10', '2027-03-10'])
Schools that front-load fees (40% now, 30% and 30% later) need weighted splits. The same rule applies: compute each share with integer division, then give the leftover units to the earliest instalments, and write a test asserting the sum equals the total for thousands of random inputs.
How do sibling concessions, scholarships and late fees work as rules?
Model them as data a bursar can change, applied when the invoice is generated, and never retroactively to paid invoices.
| Rule | Typical shape | Decision to make up front |
|---|---|---|
| Sibling concession | 10% off tuition for the second child, 15% for the third | Which child counts as "first": oldest, earliest enrolled, or highest fee? |
| Scholarship or waiver | A percentage or fixed amount off named fee heads | Does it cover transport and exam fees, or tuition only? |
| Staff child | Full or partial tuition waiver | What happens when the parent leaves mid-term? |
| Late fee | Flat charge after a grace period, or a daily amount with a cap | Is it waivable, by whom, and is the waiver logged? |
| Mid-term joiner or leaver | Pro-rated fees by days or months | Refund the unused part, or credit it to next term? |
Whether late fees, discounts or receipts have tax or consumer-law consequences depends on your country and school type. That is general information; confirm with your adviser before you configure the rules.
Which payment gateways should a school fee system support: bKash, SSLCommerz or Stripe?
The ones your parents actually use. In Bangladesh that usually means a mobile wallet such as bKash and a local aggregator such as SSLCommerz for cards and other wallets; in the US, UK or Gulf it usually means cards and bank debits through a processor such as Stripe. Many schools need two rails plus cash at the office, all writing to the same ledger.
| Rail | How confirmation works | RAITHub experience |
|---|---|---|
| bKash | Tokenized checkout, with Query Payment and Search Transaction APIs to confirm status and a refund API (bKash developer portal) | Shipped in TheSkinProof, the founder's own venture |
| SSLCommerz | An IPN (instant payment notification) to your server, then a call to the validation API with the val_id; the docs say to check amount, currency and transaction id against your database (SSLCommerz developer docs) | Shipped in TheSkinProof |
| Stripe | Webhooks confirm payment; Invoicing can send reminders before, on or after the due date (Stripe: automatic collection) | Stripe rent collection shipped in PropDesk, a property management build with 1,024 tests |
| Cash and bank transfer | Staff record the payment with a receipt number | Same ledger, same receipt sequence, with the staff member recorded |
The rule across all of them: a payment is recorded when the server confirms it with the gateway, never when the parent's browser lands on a success page. The bKash and SSLCommerz integration guide walks through both flows, and the payments and webhooks testing guide shows how to test replays and timeouts. Rent collection is the same problem with tenants instead of parents; the online rent collection guide covers it from the PropDesk side.
How should receipts, reminders and reconciliation work?
- Receipts. Issue one per payment from a database sequence per school, so numbers never repeat. Generate the PDF from the ledger entry, not from form input. Many schools also need the fee heads itemised on the receipt.
- Reminders. Send a schedule relative to each due date, for example 7 days before, on the day and 5 days after, and check the ledger at send time so a parent who just paid is skipped. Email is cheap; SMS and WhatsApp cost per message, so cap them.
- Reconciliation. Each day, import the gateway's settlement report and match every line to a ledger entry by gateway reference. Three buckets come out: matched, in the gateway but not the ledger (a missed confirmation), and in the ledger but not settled (pending or reversed). The second bucket is the one that loses money quietly.
- Reports. Collections by fee head and class, overdue by age (0 to 30, 31 to 60, 60+ days), and concessions granted by reason. These are queries on the ledger, which is why the ledger has to be right.
How long does it take to build a fee module yourself, and what is the main risk?
As an estimate, a developer comfortable with PostgreSQL and one payment gateway needs roughly 3 to 5 weeks for fee heads, invoices, instalments, one online rail, receipts and email reminders, and longer for a second gateway, concessions and reconciliation. The main risk is not the screens; it is payments recorded twice or never, from webhook retries, timeouts and parents pressing "pay" twice. Budget as much time for tests around payment confirmation as for the user interface.
Why RAITHub for this
- Local and international payment rails, shipped. bKash, Nagad, SSLCommerz and cash on delivery in TheSkinProof, the founder's own venture (217 API endpoints, 750+ tests). Stripe rent collection in PropDesk (1,024 tests, 4 roles).
- Education platforms with many gateways. PadhAI, the AI tutoring platform RAITHub built, integrates 9 payment gateways across 11 services. See the EdTech industry page and the EdTech software development guide.
- Permissions for money. Sundor Skin, a B2B wholesale build, runs 88 permission codes, 12 staff roles, credit and tier pricing over 146 PostgreSQL tables with row-level security: the same discipline as "the cashier can record, only the bursar can waive".
- Honest scope. RAITHub has not built a school fee product. Fee heads, concessions and school receipts would be new work, quoted as such.
- Clear terms. Fixed scope or a dedicated monthly team. We sign NDAs and DPAs and work inside your controls; production and financial data stay in your own cloud account, and development uses synthetic data.
When you don't need us
- One school, card or bank payments, standard fees. Buy a school system with a fees module, or use hosted invoicing.
- A few hundred students and one bursar. A well-kept spreadsheet with a separate receipt book can be enough until you take payments online.
- You need accounting, payroll or tax filing. Use accounting software and an accountant; a fee system should export to it, not replace it.
- You need a native iOS or Android app. RAITHub builds web apps and PWAs only; a PWA installs on parents' phones without an app store.
- Your existing fee system just double-charges sometimes. That is a bug, not a rebuild; start with a fix.
How RAITHub would build this
- Scope: fee heads and fee plans per class and term; invoices and instalments in integer minor units; concessions and late fees as rules with reasons; an append-only ledger with numbered receipts.
- Payments: your rails, for example bKash and SSLCommerz, or Stripe, plus cash at the office, all confirmed server-side and idempotent.
- Operations: scheduled reminders that check the ledger before sending, daily reconciliation against settlement reports, and overdue and collections reports.
- Parent view: a parent portal or PWA showing each child's balance, due dates and receipts.
Timeline: a standalone fee module is typically 4 to 6 weeks at fixed scope, on the SaaS development range. Integrating into an existing school system through its API, or building fee collection as a backend for several schools, is backend and API work at 6 to 12 weeks.
You receive: automated tests and CI, with payment confirmation, rounding and replay cases covered; handover docs and runbooks, including how to reconcile and how to reverse a wrong entry; and full IP, under an NDA.
Next step: book the free 15-minute technical audit, then you get a written fixed quote. Bring your fee structure for one class, your concession rules and the gateways your parents use.
Frequently asked questions
What is a school fee management system?
It is software that turns a school's fee structure into invoices and instalments per student, collects payments by card, wallet, bank or cash, applies concessions and late fees, issues receipts, sends reminders and reconciles payments against a ledger.
Can I use Excel for school fee management?
Yes, for a small school with one person editing and payments by bank transfer. Excel holds over a million rows, so size is not the limit; the limits are no audit trail, conflicting edits, manual reminders and online payments that never write back.
How should school fees be stored in a database?
As integers in the currency's minor unit, such as paisa or cents, with the currency code beside each amount, in an append-only ledger. A balance is the sum of entries, and corrections are new reversing entries rather than edits.
How do you split a fee into equal instalments?
Divide the total in minor units with integer division, then add one unit to the first few instalments until the remainder is used up. That way 1,000.00 over three becomes 333.34, 333.33 and 333.33, which always adds back to the total.
Which payment gateways work for school fees in Bangladesh?
Mobile wallets such as bKash and aggregators such as SSLCommerz, which also cover cards. Whichever you use, confirm each payment server-side with the gateway's query or validation API before marking an instalment paid.
How long does it take to build a school fee management system?
A focused fee module with one or two gateways, receipts and reminders typically takes 4 to 6 weeks at fixed scope with RAITHub. Integration into an existing school system, or a multi-school backend, is usually 6 to 12 weeks.
Has RAITHub built a school fee system?
No. RAITHub has shipped bKash and SSLCommerz in TheSkinProof, the founder's own venture, Stripe rent collection in PropDesk and 9 payment gateways in PadhAI. A school fee system would be new work, quoted after a free technical audit.
Related posts
Ready to discuss your project?
Book a free 15-minute technical audit with our engineering team.