Every payday, each employee receives a text message with their full salary break-up and a private link to a PDF payslip. No app to install, no email address required, no printed slips to hand out on site — which matters when most of the workforce is spread across construction sites.
A civil infrastructure contractor employs several hundred people across sites — engineers, supervisors, machine operators and site staff. Most have a mobile phone and no company email address, and many are not at a desk on payday. Distributing salary slips meant printing, carrying and chasing signatures.
This portal replaced that. Payroll is imported from the spreadsheet the accounts team already keeps, reviewed on screen, and sent as a DLT-approved SMS containing the month's earnings, deductions and net pay — plus a private one-tap link to a full PDF payslip.
Every send is logged with its request, response and cost, so a month's distribution can be reconciled line by line — and a question about whether someone was paid, and told, is answered from the record rather than from memory.
Built for one accounts clerk doing a monthly run, not for a room of users. The whole job is four steps — import, review, send, reconcile — and the interface is arranged in that order.
Screenshots are of the live system with the client’s name, sender ID, domains, employee names, mobile numbers and salary figures replaced by invented equivalents. Layout, counts and behaviour are untouched.
PHP 8.1 on MySQL, with a small hand-written core — router, session, CSRF, validator, PDO wrapper — and no Composer, because the hosting offers no shell and no build step. The PDF engine is vendored into the tree for the same reason. The front end is Bootstrap 5 and DataTables from a CDN: no bundler, no compile, deployable by upload.
Indian regulation does not allow arbitrary commercial SMS. Every message must match a template registered in advance on the DLT registry, with the variable parts declared and each one capped at 30 characters. The portal is built around that constraint rather than against it.
The payslip link needs a token, and no DLT variable may exceed 30 characters — a URL plus token does not fit. The template therefore carries the fixed URL as static text and the token as three separate two-character variables, reassembled by the recipient's phone into one tappable link. Six base62 characters give 56 billion possibilities, which is what makes an unguessable link safe to send in clear text.
A name or a figure one character over the cap is rejected by the gateway — but only for that recipient, halfway through a run, after the earlier messages have been sent and paid for. So every row is measured before the run starts and over-length ones are blocked with the offending field named. A month's send either goes cleanly or does not begin.
The gateway supports multi-recipient batches, which would be faster and cheaper to call — but every recipient's variables differ, so a batch is not available here. Instead each send is its own call, throttled to four a second and retried twice with backoff on a gateway error, and each one is logged separately so a failure is one person to re-send rather than a batch to re-run.
The payslip link has to open without a login — the recipient has a phone, not an account. It is therefore rate-limited to thirty attempts per IP per minute, every invalid token returns a plain 404 and is recorded, and the slip is generated at download rather than stored, so there is no folder of everyone's pay sitting on the server to be found.