Billing by Nepali Month: FLITO's Invoices and Fees
How FLITO records payments it never holds, bills truck owners by Nepali month, and numbers PDF invoices per Nepali fiscal year, with dates in Bikram Sambat.
Matching a load to a truck is only half of a freight marketplace. The other half is money: the shipper pays the truck owner for the trip, and the owner pays FLITO for the work it brought them. In Nepal both happen in Bikram Sambat (BS), the official calendar, where months don't line up with AD months and the fiscal year starts on 1 Shrawan, in mid-July. This post covers the parts of FLITO that deal with money: recording payments it never holds, billing owners once a Nepali month, and numbering invoices the way a Nepali business expects to read them.
FLITO records payments; it never holds them
FLITO doesn't move the money for a trip. Each truck owner lists their bank account and eSewa or Khalti wallets, with QR codes, and the booking shows them to the shipper. The shipper pays with their own bank app or wallet, then reports it on the booking: the amount, which of the owner's accounts it went to, the transaction ID, and a screenshot if they have one. It stays 'reported' until the owner confirms the money arrived, or marks it disputed if it didn't. Only confirmed payments count towards what's paid. An owner can also record a payment themselves, like cash handed to the driver, and that counts straight away, because the person receiving the money is the one saying so.
Without a payment gateway there's no merchant account to set up before launch, and no trip money sitting with FLITO that it would have to account for. The cost is that confirmation is manual, so the API has two guards against double counting. A reported payment that's still waiting counts against what the shipper can report next, so the same transfer can't be entered twice. And a confirmation adds to the booking's paid total in a single update the database computes, so two confirmations landing at the same moment both count. The account a payment went to is copied onto the record at the time, so an old payment still reads right after the owner changes their details.
A fee that only truck owners pay
Shippers pay FLITO nothing. Truck owners pay a fee on each completed trip, starting with trips booked on or after 1 Magh 2083 (15 January 2027). Trips booked before that are free, each owner's first five trips under fees are free too, and a cancelled trip never has a fee.
The fee depends on the trip's fare, and the rule behind it lives only on the server. Owners see the amount for a given fare before they send an offer, on the booking and on their monthly bill, but never the formula. What an owner needs to decide on an offer is the number for that trip, and keeping the rule server-side means it can change later (the Terms promise notice first) without any app update.
A charge is created once, when the driver marks the delivery done. It's an upsert keyed on the booking, with a unique index behind it, so a retry or two requests racing can't charge a trip twice. The charge keeps the fare and rate as they were at that moment, so a later change to the rates never rewrites an old bill. And if creating it fails, the error is logged, not thrown: a billing problem must never undo a delivery.
Billing in Nepali months
Every charge is billed in the BS month the trip was completed in, stored as a key like '2083-06' for Ashwin 2083. A month's fees are due by the 15th of the next Nepali month, so Ashwin's are due by 15 Kartik. BS is the calendar Nepal runs on, and a bill should be in the months its reader actually uses.
BS can't be calculated with a formula. Month lengths run from 29 to 32 days and change from year to year, so the conversion is a lookup table of month lengths for BS 2000 to 2090, counted from 1 Baisakh 2000, which fell on 14 April 1943. The backend and the app carry the same table, with a comment in each saying to change both together. Underneath, every day is still stored as a plain AD key like '2026-10-06'. Only what people see changes.
Which day something happened on is worked out in Nepal time, not UTC. Nepal is UTC+5:45 all year with no daylight saving, so the day is the moment shifted by that offset. It matters at the edges: a trip delivered at 1 AM in Kathmandu on 1 Kartik is still the last day of Ashwin in UTC, and using the server's clock would put it on the wrong month's bill.
Oldest month first, and what happens when fees are late
Owners pay into one of FLITO's own accounts, which admins add with the official QR codes, then tap 'I have paid FLITO'. An admin checks FLITO's account and confirms the payment or rejects it with a reason. The owner doesn't pick which month a payment is for. The account summary walks the months oldest first and pays each one off in turn, and every month comes out as open (the current month), due, overdue or paid.
Nothing in that summary is a stored balance. It's worked out each time from two lists, the charges grouped by month and the payments, so there's no running total that can drift away from the records it's supposed to add up.
If fees are still unpaid after their due date, the owner's new offers are paused, both making one and accepting a shipper's, until they pay. Trips already booked carry on, because stopping a truck mid-trip would hurt a shipper who did nothing wrong. A payment the owner has recorded counts while an admin checks it, so an owner who has paid isn't stuck waiting on the admin.
Invoice numbers that run per Nepali fiscal year
Every completed trip gets an invoice number like FL/2083-84/00042: the 42nd invoice of fiscal year 2083/84. The fiscal year starts on 1 Shrawan, so a trip delivered in Asar 2083 belongs to 2082-83, and one delivered a few weeks later in Shrawan starts the new year's count. The invoice is the owner's to the shipper, not FLITO's, because FLITO doesn't take the fare. It shows what the shipper has paid so far and the balance due, and lists the owner's accounts only while something is left to pay.
The running number is a counter document per fiscal year, like 'invoice-2083-84', taken with one atomic increment, so two trips completed at the same moment never get the same number. The number is then written to the booking only if it doesn't have one yet, which makes numbering safe to repeat: the booking keeps whichever number reached it first. The one cost is that the call that loses a race has already taken a number, so the sequence can skip one. A gap can be explained; two trips sharing an invoice number can't. Trips completed before invoices existed get their number the first time someone asks for the invoice.
A one-page PDF in Bikram Sambat
The invoice is a one-page A4 PDF drawn with pdfkit: the route, the truck, the charges, the amount in words, the payments, the balance due and the receiver's signature captured at delivery. Every date is in BS, like '20 Ashwin 2083', and the issue and completion dates also show the AD date underneath.
The amount in words counts the way Nepal does, in lakh and crore rather than millions: Rs. 12,34,567.50 becomes 'Nepalese Rupees Twelve Lakh Thirty-Four Thousand Five Hundred Sixty-Seven and Fifty Paisa Only'. All text is set in Noto Sans Devanagari, which has Latin letters as well as Devanagari, so a name or address typed in Nepali prints properly instead of as empty boxes. One test renders the most crowded invoice it can (a long company name, a Nepali shipper name, eight payments and a 500-character description) and checks that it still fits on one page.
The delivery signature is fetched from storage on a best-effort basis: a four-second timeout, a 2 MB cap, and a check of the file's first bytes so only a real PNG or JPEG gets embedded. If any of that fails, the invoice goes out without the signature instead of not going out at all.
Download links that can't log anyone in
The app doesn't handle files. It asks the API for a link, and the phone's browser or the web page downloads the PDF itself. The link carries a token that lasts ten minutes and names one user and one booking. It's signed with a separate secret from login tokens, so an invoice link can't be used to log in, and a login token can't be used as an invoice link.
Only the shipper and the owner can get the invoice; the driver isn't part of the payment. When the link is opened, the API checks again that the account is still active, so a link stops working the moment an account is suspended, and the PDF is sent with caching turned off.
None of this needed a payment gateway. FLITO records money instead of moving it, works out every balance from charges and confirmed payments instead of storing one that can drift, and prints the calendar its users live in on every bill. The payment records already have statuses set aside for a gateway (initiated, failed, refunded), so when Khalti or eSewa integration comes, it changes how money arrives, not the books underneath.
