ProductApril 14, 20265 min read

Pro-rata refunds without the spreadsheet.

A walkthrough of how Brightgoal calculates partial refunds, mid-cycle plan swaps and slot changes.

H
Harvindar Singh
Founder · Brightgoal · Uttar Pradesh, India
Isometric receipt and coins with a floating billing-breakdown panel resolving to a single total.

Every library owner has had this conversation — a student cancels two weeks into the month and asks what they are owed. Before Brightgoal, the answer involved a phone calculator, a moment of mental arithmetic, and a nagging feeling that someone was going to feel cheated. Now the number is already on the screen when you open the app.

Why refund disputes happen at all

Study libraries in Uttar Pradesh run on thin margins and high trust. A student in Lucknow who pays ₹1,200 for a seat in January is trusting you to treat them fairly when they leave on the 12th. You are trusting that they understand they cannot get back everything they paid. The gap between those two reasonable expectations — and the absence of a written, verifiable calculation — is exactly where disputes are born.

The classic hand-calculation method has three failure modes.

Rounding error. "It is roughly half the month" is not the same as 18 out of 31 days. At ₹1,200 a month the difference is nearly ₹50 — small to you, not small to a student managing a JEE preparation budget.

Inconsistency. When two students cancel on the same day and you quote them different numbers because one asked on a busy morning and one asked on a quiet evening, word gets around. Trust evaporates fast in a study library because students talk constantly.

No paper trail. If a student disputes the figure two days later — or tells their friend a different number — you have no record of what was calculated or when. Your only defense is memory against theirs.

Brightgoal solves all three failure modes with a single consistent formula baked into the platform and a billing snapshot attached to every transaction.

The formula

Every payment in Brightgoal stores the total paid and the calendar window it covers. When a cancellation or mid-cycle change is triggered, the refund calculation is:

lib/billing/pro-rata.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{{muted:/**
* Pro-rata refund for any partial period.
* Returns zero if the student has already used more
* than they paid for (no negative refunds).
*/}}
export function calculateProRataRefund({
totalPaid,
totalDays,
remainingDays,
}: {
totalPaid: number;
totalDays: number;
remainingDays: number;
}): number {
const dailyRate = totalPaid / totalDays;
return Math.max(
0,
Math.round(dailyRate * remainingDays),
);
}

The daily rate is totalPaid ÷ totalDays. The refund is that rate multiplied by the unused days, floored at zero and rounded to the nearest rupee. No student ever owes you money on a cancellation — the minimum refund is always ₹0.

The inputs come entirely from the database. totalPaid is the amount on the payment record. totalDays is the span between start_date and end_date as stored in the billing snapshot. remainingDays is today's date subtracted from end_date. The system never trusts client-provided numbers for billing — every figure is sourced and computed server-side.

Worked examples

The formula is simple. Seeing it applied to real scenarios makes it concrete.

Cancellation scenarios
ScenarioFee PaidPeriodDay of CancelRefund
Cancel 12 days in (30-day month)₹1,20030 daysDay 12₹720
Cancel 12 days in (31-day month)₹1,20031 daysDay 12₹736
Cancel on the last day₹1,20030 daysDay 30₹0
Cancel on Day 1 (same day)₹1,20030 daysDay 1₹1,160
Cancel 3-month block, 45 days in₹3,00090 daysDay 45₹1,500

The Day 1 cancellation returning ₹1,160 (not the full ₹1,200) is intentional and defensible: the student did occupy the seat for one day, so one day's worth of the fee is consumed. This is the kind of detail that causes arguments when it is not written down. Because it is computed from the same formula every time, you can show the student the exact arithmetic.

Slot upgrade mid-cycle

Suppose a student in Kanpur is on the Morning Slot (6 AM–12 PM) at ₹800 per month and wants to switch to the Full Day Slot (6 AM–10 PM) at ₹1,500 per month. They are 10 days into a 30-day cycle.

The settlement calculation has two parts:

Credit from the old slot. They paid ₹800 and have used 10 days of a 30-day period. Remaining credit = (800 / 30) × 20 = ₹533.

Cost of the new slot for the remaining period. The new slot costs ₹1,500 per month. Pro-rata for 20 remaining days = (1500 / 30) × 20 = ₹1,000.

Net charge = ₹1,000 − ₹533 = ₹467. The student pays ₹467 to upgrade. If the new slot were cheaper (a downgrade), the arithmetic would produce a negative number — meaning a refund of the difference.

Brightgoal shows this breakdown on screen before the student confirms, and generates a GST invoice the moment the transaction is complete.

Slot downgrade mid-cycle

Same student, reverse direction. Moving from Full Day (₹1,500) to Morning Slot (₹800) on Day 10 of 30.

Credit from old slot = (1500 / 30) × 20 = ₹1,000. Cost of new slot for remaining days = (800 / 30) × 20 = ₹533. Net refund = ₹467. The system applies ₹467 as a credit or issues it back to the original payment method — your choice in settings.

Releasing a locker mid-month

Locker fees in Brightgoal are calculated per day (not per month) when assigned or released mid-cycle. A locker priced at ₹300 per month — with 18 days remaining in the subscription — costs (300 / 30) × 18 = ₹180 if assigned today, or refunds ₹180 if released today.

This per-day treatment is important because lockers are independent of seat slots. A student can keep their seat and release only the locker. The refund for the locker does not affect the seat calculation.

How a refund flows in the app

  1. 1

    Open the student's profile

    Navigate to the student record in the Students tab. The active subscription panel shows the current slot, seat, locker, and end date at a glance.

  2. 2

    Trigger the action

    Click Cancel Subscription, Change Slot, or Release Locker. A confirmation popup opens immediately showing the computed refund amount — before you click anything irreversible.

  3. 3

    Review the breakdown

    The popup shows the billing snapshot: total paid, days used, days remaining, daily rate, and the refund. Nothing is hidden. You can show this screen directly to the student if they are standing in front of you.

  4. 4

    Confirm

    One tap confirms the action. The system creates a refund payment record, updates the student's status, and releases the seat (and locker, if applicable) back to the available pool.

  5. 5

    Invoice is generated instantly

    A GST-compliant refund document is generated and can be downloaded as a PDF immediately. The original payment record and the new refund record are linked in the ledger. The audit trail is complete.

No calculator needed

The refund amount you see in the confirmation popup is the exact amount that will be recorded. The formula runs on the server against database values — no rounding differences between what the popup shows and what lands in the ledger.

The billing snapshot: your audit trail

Every payment record in Brightgoal stores a billing_snapshot — a JSON object that captures every figure that went into the original calculation at the moment of payment. It includes the monthly rates, duration, discount percentages (if an offer was applied), before-discount subtotals, after-discount subtotals, and the final total.

This matters for three reasons.

Refunds reconstruct exactly. When a refund is triggered, the system reads totalPaid and the date window from the snapshot — not from current slot prices, which may have changed since the student enrolled. If you raised your seat fee from ₹1,200 to ₹1,400 in February, a student who enrolled in January is refunded against what they actually paid (₹1,200), not the new rate.

Bills can be regenerated any time. The PDF bill for any payment can be regenerated on demand from the snapshot. The numbers will match the original bill to the rupee, regardless of when you regenerate it.

Disputes become unambiguous. If a student or their parent questions a refund amount three weeks later, you pull up the payment record, show the snapshot, and the arithmetic is right there. The conversation is short.

What a billing snapshot contains

A typical snapshot for a one-month slot enrollment looks like this:

FieldExample value
seat_fee (monthly rate)₹1,200
locker_fee (monthly rate)₹300
duration1 month
start_date / end_date2026-01-05 / 2026-02-04
seat_cost_before_discount₹1,200
discount_on_seat10%
seat_discount_amount₹120
seat_cost (after discount)₹1,080
locker_cost (after discount)₹270
total_amount₹1,350

The snapshot is immutable. Once a payment is recorded, its snapshot is never edited.

Why this matters more than it looks

The operational benefit is obvious: faster resolutions, fewer errors. But the deeper benefit is psychological.

When you quote a refund from your head, the student's brain automatically performs a competing calculation. Even if your number is correct, their mental estimate is slightly different, and the difference feels like you are shortchanging them. This is not dishonesty — it is cognitive bias. Both of you are rounding, but in different directions.

When the number comes from a system with a visible formula and a saved receipt, the dynamic changes entirely. The calculation is not yours or theirs — it belongs to the math. Students who might have pushed back on your spoken estimate accept a screen-shown breakdown because screens feel neutral.

This is why library owners using Brightgoal report that refund conversations went from five-minute negotiations to thirty-second acknowledgements. The argument is not won by convincing the student — it is pre-empted by having nothing to argue about.

The student asked what they would get back. I showed them the screen. They said okay. That was the whole conversation.

S
Siddharth Yadav · Library owner, Prayagraj

Frequently asked questions

What if a student enrolled for only a partial first month?
For Custom Slot students, duration can be any number of days — 7, 13, 45, whatever was agreed. The billing snapshot stores the exact start and end date, so the daily rate is calculated against the actual agreed period, not an assumed 30-day month. If a student enrolled for 13 days at ₹600 and cancels after 5 days, the refund is (600 / 13) × 8 = ₹369. Pre-made slot students (regular monthly plans) require a minimum of one full month, so partial first months do not arise for that subscription type.
How does an upgrade differ from a downgrade in the ledger?
Both produce a settlement payment record. An upgrade produces a charge — a new payment where the student pays the net difference. A downgrade produces a credit — a new payment with a negative amount that represents money owed back to the student. Both records link to the original enrollment and are visible in the payment history. GST calculations apply to charges; refund documents are generated for credits.
Can a locker refund be processed independently of a seat cancellation?
Yes. Locker assignment and release are separate operations. A student can release their locker and keep their seat for the remainder of the subscription. The locker refund is calculated on the locker fee alone (daily rate × remaining days). The seat subscription is unaffected. The same works in reverse — a student can add a locker mid-month and be charged only for the remaining days of their current period.
What fees are non-refundable?
Brightgoal does not enforce a platform-level non-refundable fee — that is your call as the library owner. However, the formula already reflects Day 1 consumption: even if a student cancels on the first day, one day's fee is consumed. If you want to add a registration or processing fee on top of the seat fee, that should be handled as a separate payment line. Do not fold it into the seat fee, or the pro-rata formula will partially refund it, which is probably not what you intend.
Does an offer (discount) affect the refund amount?
The refund is always calculated against the amount the student actually paid — which is already the discounted amount. The snapshot stores both the full rate and the discounted rate, but the refund formula uses totalPaid (the after-discount total). A student who got a 10% loyalty discount pays ₹1,080 instead of ₹1,200 on a ₹1,200 seat. If they cancel on Day 12 of 30, the refund is (1080 / 30) × 18 = ₹648 — calculated against what they paid, not the list price.
Key takeaway

The formula is simple. The discipline is running it every time.

Daily rate × unused days, floored at zero, rounded to the nearest rupee. Brightgoal runs this calculation automatically on every cancellation, slot change, and locker release — against database values, not client-provided numbers — and stores the complete arithmetic in a billing snapshot that cannot be edited after the fact. The result is that refund conversations in Lucknow, Kanpur, and Prayagraj now take thirty seconds instead of five minutes, and nobody walks away feeling cheated.

Fair math, every time.

Brightgoal handles the calculation — you handle the library.

Written by
H
Harvindar Singh
Founder · Brightgoal · Uttar Pradesh, India

Started Brightgoal after running two paid study libraries in Uttar Pradesh for three years. Writes about the unglamorous parts of running a small business — operations, pricing, and the spreadsheets you wish you'd built earlier.

12 articles
Writing since 2024
Uttar Pradesh, IN
The Brightgoal Dispatch · Monthly

The short, useful emailfor library owners.

One email a month. New Brightgoal features, real lessons from owners growing their libraries, and the occasional template you can steal.

No spam. Unsubscribe with one click. We never share your email.

Product updates
What shipped this month
Owner lessons
From real libraries
Templates
Free, yours to steal
First Tuesday of every month
Read by library owners across 24+ countries
Read past issues
Pro-rata refunds without the spreadsheet. — Brightgoal