Home Work Founder Contact
Back to Portfolio /SMS Salary Portal
Payroll · DLT Messaging & Document Delivery

SMS Salary Portal

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.

Infrastructure contractor · name withheld
6 weeks
Live, monthly runs
SMS Salary Portal sign-in
288
staff and operators on the roll
14
DLT variables per message
30
character cap on every one
1
tap from SMS to payslip

Project Overview

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.

The Interface

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.

Dashboard
Dashboard. Where the month stands: how many staff are on the roll, how many salary rows are in for the current period, what the last run cost and whether anything failed.
CSV import with preview
Import. Payroll arrives as the spreadsheet the accounts team already keeps. Every row is parsed, matched to an employee and shown back before a single record is written — an import that half-succeeded would be worse than one that refused.
SMS compose with per-variable character counts
Compose. The heart of it. Each recipient’s fourteen template variables are rendered and measured against the 30-character cap before anything is sent, so a name one character too long is caught here rather than halfway through a paid run.
Gateway call log
Reconcile. Every gateway call with its request, response and cost. A question about whether someone was paid and told is answered from the record. The API key is masked in the stored URL, not just on screen.
Employee register
Employees. The register, with bulk import and de-activation. Staff leave; their history does not.
Salary records
Salary. One record per person per month, imported or keyed, each one the source for a payslip.
User management with roles
Users. Administrators and operators. Accounts are disabled, never deleted, because the logs name the account that acted.
Settings with masked credentials
Settings. Gateway credentials, per-SMS cost and payslip branding. The key is write-only from here — it is never shown back.

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.

Key Features

Employee register with bulk CSV import and de-activation
One salary record per employee per month, imported or keyed
Per-recipient SMS preview with a live character count per variable
Over-length rows blocked before a single message is sent
Branded PDF payslip generated on demand from a private link
Full audit of every gateway call — request, response, cost
Automatic retry with backoff on gateway or network failure
Settings for gateway credentials, per-SMS cost and slip branding

Technical Implementation

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.

Problems Worth Naming

A link that would not fit in a variable

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.

Failing before sending, not during

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.

One call per person, on purpose

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.

A public URL holding private pay

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.

Security

Bcrypt cost 12, forced rotation on first sign-in
Account lockout and per-IP throttling on sign-in
CSRF token on every form, checked before dispatch
Prepared statements throughout; output escaped at render
Gateway key stored server-side, masked in the UI and in every log line
Application code, database scripts and storage denied at the web server

Project Details

Client
Withheld at the client's request
Industry
Infrastructure & Civil Construction
Technology
PHP 8.1 MySQL DLT SMS Gateway PDF Generation Bootstrap 5 CSV Import
Scale
288 employees · monthly send
Duration
6 weeks

How A Payslip Travels

1Payroll imported from the accounts spreadsheet
2Every row measured against the 30-character cap
3A one-time token minted per payslip
4SMS sent, throttled and retried, each call logged
5Employee taps the link; the PDF is built on the spot