The first bug every rewards platform hits is a balance that is off by a hundredth. It is almost always a floating point number doing exactly what floating point numbers do.
Store integers in the smallest unit
Paise, cents, whatever the currency's minor unit is. A balance is an integer. Formatting for display happens once, at the edge, and never in the maths.
Round once, deliberately
Every division - a split, a percentage, a fee - creates a fraction. Decide where the remainder goes and write it down. Rounding scattered through the code will not agree with itself.
Currency is part of the amount
An integer alone is not money. Carry the currency with it, and refuse to add two amounts that do not match. This catches an entire class of bug at the type level.
Never mutate a balance
- Every change is an entry with a reason
- The balance is derived from the entries
- Reconcile derived against stored on a schedule
- Alert on any drift, however small