Skip to content

Backend - Portal de Clientes

Modulo DDD moderno bajo Modules/Portal/ que extiende Sistema Bautista con funcionalidad de autoservicio para clientes finales. Endpoints publicos del portal montados bajo /backend/portal/*. Endpoints ERP de conciliacion manual montados bajo /backend/portal-erp/*.

Estado: Implementado — módulo DDD completo con 7 sub-módulos

Arquitectura

Modulo DDD: El portal se implementa como un modulo moderno dentro de Modules/Portal/, siguiendo la arquitectura DDD que ya usan los modulos CRM y Membresia.

  • Estructura DDD con sub-modulos independientes
  • Reutiliza infraestructura existente (PostgreSQL, Slim Framework)
  • Comparte servicios del ERP donde corresponda (CuponPagoService, ReciboRelationsService)
  • Autenticacion propia via JWT con password
  • Namespace aislado: Modules\Portal\

Sub-modulos

El modulo Portal se divide en 7 sub-modulos DDD agrupados por capa (Application / Presentation):

Sub-moduloControllerService principalResponsabilidad
AuthPortalAuthControllerAuthServiceRegistro, login, recuperacion de password, refresh, logout, JWT
AccountPortalAccountControllerPortalAccountServiceConsulta de saldo, movimientos y deudas de cuenta corriente
PerfilPortalPerfilControllerPortalPerfilServiceLectura y actualizacion de datos de contacto del usuario autenticado
CuponPortalCuponControllerPortalCuponService + CuponPdfServiceInterfaceListado y descarga PDF de cupones de membresia (delega a servicios existentes)
PaymentPortalPaymentController, PortalWebhookControllerPortalPaymentService, PortalWebhookServicePagos online (gateway), historial, cancelacion/devolucion, webhook publico
ReconciliacionPortalErpController (namespace Erp)PortalReciboCreatorService, PortalErpQueryServiceConciliacion manual desde ERP de pagos aprobados (genera recibo en ordcta)

El sub-modulo Reconciliacion expone su Controller bajo el namespace Erp (Portal\Presentation\Erp\Controllers\PortalErpController) porque sus rutas se montan desde el ERP (/backend/portal-erp) y no desde el namespace publico del portal.

Modules/Portal/
  Application/
    Auth/Services/AuthService.php
    Account/Services/PortalAccountService.php
    Perfil/Services/PortalPerfilService.php
    Cupon/Services/PortalCuponService.php
    Payment/
      Services/PortalPaymentService.php
      Services/PortalWebhookService.php
      Services/PaymentGatewayService.php
      Validators/IniciarPagoValidator.php
      Validators/CancelarPagoValidator.php
      Validators/DevolverPagoValidator.php
    Reconciliacion/
      Services/PortalReciboCreatorService.php
      Services/PortalErpQueryService.php
      DTOs/FacturaRecibo.php
      Exceptions/ (PaymentNotFoundException, PaymentNotApprovedException,
                   AlreadyReconciledException, OrdconNotFoundException)
      Ports/CtaCteReconciliacionPort.php
  Presentation/
    Auth/Controllers/PortalAuthController.php
    Auth/DTOs/ (LoginRequest, RegisterRequest, ForgotPasswordRequest, ResetPasswordRequest)
    Account/Controllers/PortalAccountController.php
    Account/DTOs/ (SaldoResponse, MovimientoResponse, DeudaResponse)
    Perfil/Controllers/PortalPerfilController.php
    Perfil/DTOs/UpdatePerfilRequest.php
    Cupon/Controllers/PortalCuponController.php
    Cupon/DTOs/CuponMembresiaResponse.php
    Payment/Controllers/PortalPaymentController.php
    Payment/Controllers/PortalWebhookController.php
    Erp/Controllers/PortalErpController.php
  Infrastructure/
    Http/
      Middleware/PortalJwtMiddleware.php
      Routes/PortalRoutes.php          # Raiz: /backend/portal
      Routes/AuthRoutes.php            # /auth (publico)
      Routes/AccountRoutes.php         # /account (JWT)
      Routes/PerfilRoutes.php          # /account/perfil (JWT)
      Routes/CuponRoutes.php           # /cupon (JWT)
      Routes/PaymentRoutes.php         # /pagos (webhook publico + resto JWT)
      Routes/PortalErpRoutes.php       # /backend/portal-erp (AuthMiddleware ERP)
  Domain/
    Auth/ (JwtClaims, PortalUserFactory, Exceptions)
    Payment/ (Enums, Exceptions)
    Cupon/Exceptions
  Contracts/ (interfaces de servicios y repositorios)

Autenticacion

JWT con password (no passwordless):

  • El cliente se registra con DNI/CUIT que debe coincidir con un ordcon existente
  • Se almacena password_hash en portal_users
  • Login genera JWT con payload: { portal_user_id, tenant_id, sucursal_id }
  • El frontend envia tenant_id y sucursal_id en el login (valores de .env)
  • Recuperacion de password via codigo enviado por email

Resolucion de Tenant

NO se usa resolucion por dominio. Cada tenant tiene su propio deploy Docker con variables de entorno.

Flujo de resolucion:

  1. Frontend envia tenant_id + sucursal_id en el login
  2. Backend valida que la sucursal pertenece al tenant
  3. JWT incluye tenant_id y sucursal_id
  4. En cada request autenticado:
    • tenant_id del JWT resuelve la base de datos via ini.sistema
    • sucursal_id del JWT resuelve el schema (sucXXXX, o public si ordcon es a nivel empresa)
  5. Backend valida que la sucursal pertenece al tenant

Tablas

TablaUbicacionDescripcion
portal_usersMismo schema que ordcon (dinamico via ini.sistema)Usuarios del portal con password_hash
portal_paymentsMismo schema que ordconPagos online pendientes, aprobados, rechazados

NO existe tabla portal_cupones. El sub-modulo Cupon delega a los servicios existentes CuponPagoService y CuponValidacionService del modulo CtaCte.

Documentacion

API Endpoints

Documentacion completa de los endpoints REST del portal.

Services

Servicios de logica de negocio del portal.

Seguridad

  • Autenticacion JWT con RS256 (par de claves RSA private-key.pem / public-key.pem), expiracion configurable. PortalJwtMiddleware rechaza tokens cuyo claim iss no sea portal (impide reutilizar tokens del backoffice ERP).
  • Refresh token para renovar sesion sin re-login
  • Rate limiting: 5 intentos login/min por IP, 100 requests/min por usuario
  • Bloqueo automatico tras 5 intentos fallidos de login (15 min)
  • Validacion de pertenencia: portal_user vinculado a ordcon del tenant
  • Webhook: validacion de firma especifica por gateway, idempotencia