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.
Source link

