Citybox payment portal and dashboard
Web portal for tenants to rent and pay for their unit, and a dashboard to review payments and generate payment links.

What was needed
Let the staff review and manage payments across several locations, give tenants who don’t use the app a web version, and allow charging one-off items (fines, repairs, deposits) without setting up an account in the system.
What I built
A Laravel application with three parts that share the same payments:
- Customer portal (
/portal): the tenant chooses the location (Kennedy, Samborondón or Quito), logs in or creates their account, sees their door code and the balance of each unit, and pays the debt. They can also rent a unit: they choose the type, the floor (only floors with free units appear) and the unit, see the breakdown with the prorated month, registration fee and 15% VAT, and pay by card. It has light and dark modes. - Staff dashboard: the list of payment records with environment, status, card type, amount, batch, authorization, date and location, with search and sorting by column, and a view that adapts to the phone. There are three roles: the super admin sees everything and creates accounts, the admin sees the payments of all locations, and the delegate only those of the locations assigned to them, where they can also cancel payments. New accounts are activated with an invitation email so each person sets their own password.
- Payment orders: the staff generates a payment link with a description, an optional order or invoice number, amount before VAT (the system calculates VAT and the total), and the customer’s name and email, and can send it by email. The customer opens it without logging in and pays. The link expires after 24 hours. A summary shows what was generated, collected, pending and the number of orders. A delegate can only create orders if the administrator gives them that permission, and only sees their own.
The data on tenants and units comes in real time from an external SOAP service. Payments are processed through the Datafast gateway, and the mobile app uses the same backend, with its own endpoints for payments, refunds and saved cards. Five emails are sent: booking confirmation, debt payment, tenant welcome, notice to the administrator and password change.
Security
- No access to other people’s data: in the portal, the tenant and the location always come from the session, never from what the browser sends. Someone else’s payment and one that doesn’t exist get the same response.
- Amounts calculated on the server: the client can’t change what they pay, and every payment attempt is recorded and processed only once.
- Session: it is renewed on login, expires after inactivity and after a maximum of 8 hours, and rejects tampered sessions.
- Attempt control: layered limits on login, registration and password recovery, plus a honeypot field against bots.
- No user enumeration: password recovery and the login error respond the same whether the email exists or not.
- Inputs and outputs: strict validation of every field, output always escaped, a content security policy and security headers.
- Roles per location: each delegate only sees and manages what belongs to their locations.
- Tests: there are automated security tests for the portal.
Result
The app, the web portal and the administrative team use the same accounts and the same payments. It is a system in production that keeps evolving along with the new version of the mobile app.