Dev Log: 2026-07-04
TL;DR Two Laravel backends started serving Flutter apps on the same day — an events platform (auth, orders, offline check-in) and a helpdesk product (ops mode for agents). gatherhub-web moved to plans-only pricing wi
TL;DR
- Two Laravel backends started serving Flutter apps on the same day — an events platform (auth, orders, offline check-in) and a helpdesk product (ops mode for agents).
- gatherhub-web moved to plans-only pricing with a comparison matrix driven by one data file.
- A hardening pass: payment-safe queues, gateway reconciliation, one heavyweight dependency dropped.
Two mobile APIs in one day
Coincidence, but a useful one: two products I'm building both needed their Laravel backends to serve mobile apps this week.
The events platform got the full foundation — token auth (login/refresh/logout/me), participant orders, mobile payment with status polling, push-device registration, and an offline-first staff check-in flow. That last one is the interesting bit; I wrote it up as its own post.
The helpdesk product went the other way: its API was client-only, and today it became role-aware. The same endpoints now serve ops agents working tickets from their phones, with abilities deciding what each role sees. One API surface, two personas, no duplicated /admin routes.
The lesson that repeated in both: API Resources are the contract. The moment a mobile dev consumes your endpoint, every field you accidentally leak becomes a field you can't remove.
Plans-only pricing (public)
gatherhub-web, the Next.js marketing site, dropped à-la-carte feature pricing for three plans and gained a plan comparison matrix. Everything renders from a single plans.ts — the matrix, the pricing cards, the enterprise page — so the marketing site can't drift from what's actually sold. A pricing page is a contract too; it deserves a single source of truth as much as your API does.
Hardening pass
| Change | Why |
|---|---|
| Bulk email blasts isolated to their own queue | one big send must never delay a payment webhook |
| Reconciliation command for stuck pending orders | webhooks fail silently; polling the gateway is the safety net |
maatwebsite/excel → spatie/simple-excel for exports |
streams rows instead of building sheets in memory, smaller dependency surface |
| Publish readiness enforced on every transition path | one invariant in one place, not per-controller checks |
Takeaway
Everything today was the same disease in different organs: drift from a single source of truth. plans.ts on the site, Resources in the API, one readiness gate in the backend. Same medicine everywhere.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.