Malik SohaibEmail me
← All work

GymPaid

A renewals tracker for Pakistani gyms that replaces the paper register and answers one question: who owes me money today?

Role
Solo: requirements, design, frontend, backend
When
Sep 2026
Status
In hand-over

A gym in Punjab kept memberships in a paper register. A member who joined on the 17th pays on the 17th, every month, and with a few hundred members on a few hundred different days the owner could not tell who was due.

GymPaid gives the front desk a due list, WhatsApp reminders and revenue figures that stay correct without anyone maintaining them.

Decisions

  1. 01

    Renewals anchored to the join dayDecision D-3

    Chose
    Each member keeps a pay day separate from their next due date.
    Instead of
    Computing each due date from the previous one.
    Why
    A member anchored on the 31st is due on 28 February; computing March from the 28th would move them to the 28th for good. Keeping the anchor restores the 31st.
  2. 02

    Status is computed, never storedDecision D-14

    Chose
    Active, due soon, overdue or lapsed is worked out on every request from the due date and the gym’s grace period.
    Instead of
    A status column.
    Why
    A status column goes stale at midnight because nothing runs to flip it, and every screen, and later the door, would believe it.
  3. 03

    No cron, no queue: reminders are a queryDecision D-11

    Chose
    The due list is a query, and a reminder opens WhatsApp with the message already typed.
    Instead of
    A background worker, a queue of reminder records and the WhatsApp Business API.
    Why
    A query is always correct and has nothing to retry. It shipped on day one with no verification process and no per-message cost.
  4. 04

    Wrong payments are voided, not deletedDecision D-24

    Chose
    An owner can void the latest payment, which restores the previous due date and keeps the receipt.
    Instead of
    Deleting the payment, or fixing it in SQL.
    Why
    Staff will tap Paid on the wrong row. Deleting would free a receipt number a member already holds and erase the record that it happened.
  5. 05

    A door that holds no fingerprintsDecision D-46

    Chose
    Each minute the door terminal is sent only the difference between who it lets in and who should get in now; fingerprint templates never leave the device.
    Instead of
    A command queue with retries, and a copy of members’ biometrics.
    Why
    A member who lapses at midnight is shut out on the next poll with nothing scheduled, and “we hold no fingerprints” is a promise the design keeps.

GymPaid, in use: GymPaid’s due list: who owes money today, with WhatsApp and Paid buttons. The members list: plan, status and next due date for each member. Membership plans: monthly, three months and annual. GymPaid: who owes you money today?

  • gympaid.ezvenda.com/queueThe one question it answers: who owes money today, with a reminder a tap away.
  • gympaid.ezvenda.com/membersEvery member’s plan and when they are next due.
  • gympaid.ezvenda.com/plansPlans with the price per month worked out.
  • gympaid.ezvenda.comThe question it answers, on its card.

What shipped

  • Milestone 1 built against a two-day hand-over deadline.
  • 154 automated tests green, 48 of them on the renewal rule alone.
  • A pre-deploy security review that found and closed a way around the sign-in rate limit.
  • Door polling at about 12 ms per poll, with no fingerprints stored.

Built with

TypeScript, Fastify, Drizzle, Postgres, React, TanStack, ZKTeco ADMS