Referencia de API

Table of Contents

Este documento describe la API REST de UnoSportClub implementada en functions/server.js con Express.js.

Base URL

Producción (API Público)

La API está disponible en producción en los siguientes subdominios:

Desarrollo Local

Para desarrollo local, la API está disponible en:

O usando el proxy de Angular (configurado en proxy.conf.json):

/api

Nota: * El servidor backend en producción se ejecuta con PM2 y está balanceado por Caddy. * La documentación interactiva de la API (Swagger UI) está disponible en: - Producción: https://app.unosportclub.com.co/api/api-docs - QA: https://panel.qa.unosportclub.com.co/api/api-docs - Desarrollo: http://localhost:6100/api/api-docs * El esquema OpenAPI (JSON) está disponible en: - Producción: https://app.unosportclub.com.co/api/api-docs.json - QA: https://panel.qa.unosportclub.com.co/api/api-docs.json - Desarrollo: http://localhost:6100/api/api-docs.json * Los endpoints con localhost son exclusivamente para desarrollo local. * Los endpoints con subdominios de unosportclub.com.co son el API público en producción. * Esta documentación está basada en el esquema OpenAPI oficial (versión 3.0.0) y las interfaces TypeScript definidas en @cortex-ia-com-co/common. * Recomendación: Para la documentación más actualizada y completa, consulta el esquema OpenAPI JSON directamente o usa Swagger UI para explorar la API de forma interactiva.

Autenticación

La API soporta dos métodos de autenticación:

Autenticación con Firebase Auth (Usuarios)

Para usuarios autenticados mediante Firebase Auth, el token debe enviarse en el header:

Authorization: Bearer [firebase-id-token]

Este método se usa para usuarios humanos que se autentican mediante Firebase Authentication.

Autenticación con API Key (Bots)

Para bots y sistemas automatizados, se puede usar autenticación mediante API Key enviando el valor de BOT_API_KEYS en el header:

X-Auth: [BOT_API_KEYS]

Características de la autenticación con API Key: * El header debe ser X-Auth (no Authorization) * El valor debe coincidir exactamente con la variable de entorno BOT_API_KEYS configurada en el servidor * Otorga permisos completos: operator, admin, sudo, trainer, y bot * Disponible en las rutas: /account/, /admin/, /trainer/, y /sudo/ * El usuario autenticado tendrá uid: "bot-api-key" y bot: true

Ejemplo de uso:

curl -H "X-Auth: tu-api-key-aqui" https://app.unosportclub.com.co/api/admin/clients

Nota: Algunos endpoints públicos (/public/*) no requieren autenticación.

Formatos de Datos

Fechas y Horas

La API utiliza diferentes formatos según el contexto:

  • Fechas: Formato ISO 8601 YYYY-MM-DD (ej: 2024-01-15)

  • Horas: Formato HH:MM o HH:MM:SS (ej: 18:00 o 18:00:00)

  • Fecha y Hora: Formato ISO 8601 completo YYYY-MM-DDTHH:MM:SSZ (ej: 2024-01-15T18:00:00Z)

Campos de fecha/hora en reservas: * checking: Fecha y hora de inicio en formato ISO 8601 completo * checkout: Fecha y hora de fin en formato ISO 8601 completo * checking_date: Fecha de inicio en formato YYYY-MM-DD (cuando se separa) * checking_time: Hora de inicio en formato HH:MM:SS (cuando se separa) * checkout_date: Fecha de fin en formato YYYY-MM-DD (cuando se separa) * checkout_time: Hora de fin en formato HH:MM:SS (cuando se separa)

Nota: Algunos endpoints pueden devolver campos separados (checking_date, checking_time) mientras que otros usan campos combinados (checking). Consulta la respuesta específica de cada endpoint para más detalles.

Estructura de Endpoints

La API está organizada por roles y funcionalidades:

  • /account/*: Endpoints para usuarios autenticados (clientes)

  • /admin/*: Endpoints para administradores y operadores

  • /trainer/*: Endpoints para entrenadores

  • /public/*: Endpoints públicos (sin autenticación)

  • /sudo/*: Endpoints para super administradores

Endpoints por Rol

Account (Usuario Autenticado)

Los endpoints bajo /account/* permiten a los usuarios autenticados gestionar sus propios recursos.

Reservas (Account)

GET /reservations

Obtiene la lista de reservas.

Query Parameters: * client_id (opcional): Filtrar por cliente * operator_id (opcional): Filtrar por operador * court_id (opcional): Filtrar por cancha * status (opcional): Filtrar por estado * from (opcional): Fecha desde (ISO 8601) * to (opcional): Fecha hasta (ISO 8601)

Response 200:

{
  "data": [
    {
      "id": 1,
      "operator_id": 1,
      "client_id": 1,
      "court_id": 1,
      "reservation_type_id": 1,
      "checking": "2024-01-15T10:00:00Z",
      "checkout": "2024-01-15T12:00:00Z",
      "notes": "Cancha 1",
      "created_at": "2024-01-10T08:00:00Z",
      "updated_at": "2024-01-10T08:00:00Z"
    }
  ],
  "total": 1
}

GET /reservations/:id

Obtiene una reserva específica.

Response 200:

{
  "data": {
    "id": 1,
    "operator_id": 1,
    "client_id": 1,
    "court_id": 1,
    "reservation_type_id": 1,
    "checking": "2024-01-15T10:00:00Z",
    "checkout": "2024-01-15T12:00:00Z",
    "notes": "Cancha 1",
    "created_at": "2024-01-10T08:00:00Z",
    "updated_at": "2024-01-10T08:00:00Z"
  }
}

POST /reservations

Crea una nueva reserva.

Request Body:

{
  "operator_id": 1,
  "client_id": 1,
  "court_id": 1,
  "reservation_type_id": 1,
  "checking": "2024-01-15T10:00:00Z",
  "checkout": "2024-01-15T12:00:00Z",
  "notes": "Cancha 1"
}

Response 201:

{
  "data": {
    "id": 1,
    "operator_id": 1,
    "client_id": 1,
    "court_id": 1,
    "reservation_type_id": 1,
    "checking": "2024-01-15T10:00:00Z",
    "checkout": "2024-01-15T12:00:00Z",
    "notes": "Cancha 1",
    "created_at": "2024-01-10T08:00:00Z",
    "updated_at": "2024-01-10T08:00:00Z"
  }
}

PUT /reservations/:id

Actualiza una reserva existente.

Request Body:

{
  "checking": "2024-01-15T10:30:00Z",
  "checkout": "2024-01-15T12:30:00Z",
  "notes": "Cancha 1 - Actualizado"
}

Response 200:

{
  "data": {
    "id": 1,
    "operator_id": 1,
    "client_id": 1,
    "court_id": 1,
    "reservation_type_id": 1,
    "checking": "2024-01-15T10:30:00Z",
    "checkout": "2024-01-15T12:30:00Z",
    "notes": "Cancha 1 - Actualizado",
    "created_at": "2024-01-10T08:00:00Z",
    "updated_at": "2024-01-10T09:00:00Z"
  }
}

DELETE /reservations/:id

Elimina una reserva.

Response 204: No Content

Nota importante: * Estos endpoints solo devuelven reservas del usuario autenticado * Las reservas incluyen información agregada de pagos (total_paid, total_amount, is_payment_complete, can_add_payment) * Los pagos dentro de las reservas incluyen tanto status (string) como statusBoolean (boolean) para compatibilidad * Las reservas incluyen campos checking y checkout para mostrar fecha y hora de inicio y fin * Los horarios están disponibles en slots de 30 minutos * Las interfaces TypeScript correspondientes están en AccountBookingInterface y AccountBookingPaymentInterface del paquete @cortex-ia-com-co/common

GET /account/bookings

Obtiene las reservas del usuario autenticado con información detallada de pagos y paginación.

Query Parameters: * page (opcional, default: 1): Número de página * limit (opcional, default: 10): Cantidad de resultados por página

Response 200:

{
  "bookings": [
    {
      "id": 1,
      "client_id": 1,
      "court_id": 1,
      "reservation_type_id": 1,
      "checking": "2024-01-15T10:00:00Z",
      "checkout": "2024-01-15T12:00:00Z",
      "notes": "Cancha 1",
      "created_at": "2024-01-10T08:00:00Z",
      "updated_at": "2024-01-10T08:00:00Z",
      "court_name": "Cancha 1",
      "reservation_type_name": "Libre",
      "total_paid": 50000.00,
      "total_amount": 50000.00,
      "is_payment_complete": true,
      "can_add_payment": false,
      "payments": [
        {
          "id": 1,
          "reservation_id": 1,
          "payment_type_id": 1,
          "payment_type_name": "Efectivo",
          "amount": 50000.00,
          "transaction_id": "TXN-123456",
          "gateway_response": null,
          "status": "completed",
          "statusBoolean": true,
          "date": "2024-01-10T08:00:00Z",
          "description": "Pago de reserva"
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 15,
    "totalPages": 2
  }
}

Campos de información de pagos: * total_paid: Suma total de pagos completados asociados a la reserva * total_amount: Monto total requerido para la reserva * is_payment_complete: Indica si el pago está completo (total_paid >= total_amount) * can_add_payment: Indica si se pueden agregar más pagos a la reserva * payments: Array de pagos asociados con información detallada

Formato dual de status en pagos: * status: String con valores "pending" o "completed" (para compatibilidad con frontend) * statusBoolean: Boolean equivalente (false = pending, true = completed)

Response 401:

{
  "error": "No autenticado"
}

POST /account/bookings

Crea una nueva reserva para el cliente autenticado.

Request Body:

{
  "court_id": 1,
  "reservation_type_id": 1,
  "checking": "2024-01-15T10:00:00Z",
  "checkout": "2024-01-15T12:00:00Z",
  "notes": "Cancha 1"
}

Campos requeridos: * court_id: ID de la cancha (integer) * reservation_type_id: ID del tipo de reserva (integer) * checking: Fecha y hora de inicio (ISO 8601) * checkout: Fecha y hora de fin (ISO 8601)

Campos opcionales: * notes: Notas adicionales (string, nullable)

Response 201:

{
  "id": 1,
  "client_id": 1,
  "court_id": 1,
  "reservation_type_id": 1,
  "checking": "2024-01-15T10:00:00Z",
  "checkout": "2024-01-15T12:00:00Z",
  "notes": "Cancha 1",
  "created_at": "2024-01-10T08:00:00Z",
  "updated_at": "2024-01-10T08:00:00Z"
}

Response 400: Datos incompletos, fecha inválida o referencia inválida Response 401: No autenticado Response 404: Cliente no encontrado Response 409: Conflicto de reserva (cancha ocupada)

GET /account/bookings/calculate-price

Calcula el precio de una reserva con ajustes de tarifas usando función PostgreSQL.

Query Parameters: * date (requerido): Fecha de la reserva (YYYY-MM-DD) * checking (requerido): Hora de inicio (HH:MM) * checkout (requerido): Hora de fin (HH:MM) * court_id (requerido): ID de la cancha * reservation_type_id (opcional): ID del tipo de reserva * reservation_id (opcional): ID de la reserva (para edición)

Response 200:

{
  "court": {
    "id": 1,
    "name": "Cancha 1",
    "base_price": 50000.00
  },
  "date": "2024-01-15",
  "checking": "10:00",
  "checkout": "12:00",
  "available": true,
  "hours": 2.0,
  "final_price": 50000.00,
  "price_per_hour": 25000.00,
  "applied_adjustments": [
    {
      "tariff_id": 1,
      "description": "Descuento nocturno",
      "adjustment": "-10%",
      "adjusted_price": 45000.00
    }
  ]
}

Campos de respuesta: * court: Información de la cancha (id, name, base_price) * date: Fecha de la reserva (YYYY-MM-DD) * checking: Hora de inicio (HH:MM) * checkout: Hora de fin (HH:MM) * available: Indica si la cancha está disponible en ese horario * hours: Número de horas de la reserva (decimal) * final_price: Precio final calculado después de aplicar tarifas * price_per_hour: Precio por hora calculado * applied_adjustments: Array de ajustes de tarifa aplicados con información detallada

Nota: La función calcula el precio por periodos de 30 minutos aplicando tarifas según rangos de fechas y horas. La interfaz TypeScript correspondiente es CalculatePriceResponseInterface del paquete @cortex-ia-com-co/common.

GET /account/bookings/{bookingId}

Obtiene una reserva específica del usuario autenticado.

Response 200: Similar a GET /account/bookings pero con un solo objeto en lugar de array.

Response 400: ID de reserva inválido Response 401: No autenticado Response 404: Reserva no encontrada o no pertenece al usuario

GET /account/courts

Obtiene canchas disponibles con verificación de disponibilidad.

Query Parameters: * checking (opcional): Fecha y hora de inicio para verificar disponibilidad (ISO 8601) * checkout (opcional): Fecha y hora de fin para verificar disponibilidad (ISO 8601)

Response 200: Array de canchas con información de disponibilidad.

GET /account/reservation-types

Obtiene todos los tipos de reserva disponibles.

Response 200: Array de tipos de reserva (ReservationTypeInterface).

GET /account/payment-types

Obtiene todos los tipos de pago disponibles para el cliente.

Response 200: Array de tipos de pago (PaymentTypeInterface).

GET /account/stats

Obtiene estadísticas del usuario autenticado.

Response 200: Objeto con estadísticas del usuario (reservas, pagos, etc.). Response 401: No autenticado Response 403: Acceso denegado

Perfil (Account)

GET /account/profile

Obtiene el perfil del usuario autenticado.

Response 200: Perfil del usuario obtenido exitosamente (FirebaseUserInterface). Response 401: No autenticado Response 403: Acceso denegado

PUT /account/profile

Actualiza el perfil del usuario autenticado.

Request Body:

{
  "displayName": "Juan Pérez",
  "phone": "+573001234567"
}

Campos opcionales: * displayName: Nombre de visualización (string) * phone: Teléfono de contacto (string)

Response 200: Perfil actualizado exitosamente. Response 401: No autenticado Response 403: Acceso denegado

Cliente (Account)

GET /account/client

Obtiene los datos del cliente del usuario autenticado.

Response 200: Objeto ClientInterface del cliente autenticado.

Response 401: No autenticado Response 404: Cliente no encontrado

PATCH /account/client

Actualiza los datos del cliente del usuario autenticado.

Request Body:

{
  "firstName": "Juan",
  "lastName": "Pérez",
  "document": "1234567890",
  "documentTypeId": 1,
  "address": "Calle 123",
  "phone": "+573001234567"
}

Response 200: Cliente actualizado exitosamente.

POST /account/client

Crea un nuevo cliente para el usuario autenticado.

Request Body:

{
  "email": "cliente@example.com",
  "first_name": "Juan",
  "last_name": "Pérez",
  "document": "1234567890",
  "document_type_id": 1,
  "phone": "+573001234567",
  "address": "Calle 123"
}

Campos requeridos: * email: Email del cliente * first_name: Nombre del cliente * last_name: Apellido del cliente * document: Número de documento * document_type_id: ID del tipo de documento

Response 201: Cliente creado exitosamente. Response 400: Datos incompletos o usuario no encontrado Response 409: Cliente ya existe para este usuario

Clases (Account)

GET /account/classes

Obtiene lista de clases disponibles para inscripción.

Query Parameters: * reservation_type_id (opcional): Filtrar por tipo de reserva. Siempre filtra por gestion=2.

Response 200: Lista de clases disponibles (filtradas por gestion=2).

Nota: Las clases disponibles son aquellas con reservation_type.gestion = 2 (clases de entrenamiento).

GET /account/classes/my-enrollments

Obtiene las inscripciones del cliente actual.

Response 200: Lista de inscripciones del cliente (EnrollmentInterface[]).

GET /account/classes/{id}

Obtiene detalles de una clase específica.

Response 200: Detalles de la clase (TrainingClassInterface). Response 404: Clase no encontrada

POST /account/classes/{id}/enroll

Inscribirse en una clase.

Request Body:

{
  "payments": [
    {
      "paymentTypeId": 1,
      "amount": 50000.00,
      "transaction_id": "TXN-123456"
    }
  ]
}

Response 201: Inscripción exitosa. Response 400: Error de validación Response 404: Clase no encontrada Response 409: Ya está inscrito en esta clase

Admin (Administradores y Operadores)

Los endpoints bajo /admin/* permiten la gestión completa del sistema para administradores y operadores.

Reservas (Admin)

Los endpoints de reservas permiten la gestión completa (CRUD) de reservas desde el panel de administración.

Nota importante: * Las reservas pueden tener un client_id opcional (null para reservas de mantenimiento) * Las reservas incluyen información de estado (reservation_status_id) y tipo (reservation_type_id) * Los campos checking y checkout son fechas y horas en formato ISO 8601 * La interfaz TypeScript correspondiente es BookingInterface y BookingDetailInterface del paquete @cortex-ia-com-co/common

GET /admin/booking

Obtiene todas las reservas con filtros opcionales.

Query Parameters: * client_id (opcional): Filtrar por ID de cliente * court_id (opcional): Filtrar por ID de cancha * limit (opcional, default: 100): Límite de resultados * offset (opcional, default: 0): Offset para paginación

Response 200: Array de reservas (BookingInterface[]).

Nota: Este endpoint es un alias de /admin/bookings.

GET /admin/bookings

Obtiene todas las reservas con filtros opcionales.

Query Parameters: * client_id (opcional): Filtrar por ID de cliente * court_id (opcional): Filtrar por ID de cancha * limit (opcional, default: 100): Límite de resultados * offset (opcional, default: 0): Offset para paginación

Response 200: Array de reservas (BookingInterface[]).

GET /admin/booking/full

Obtiene reservas con información completa de pagos y precios.

Query Parameters: * client_id (opcional): Filtrar por ID de cliente * court_id (opcional): Filtrar por ID de cancha * start_date (opcional): Filtrar por fecha de inicio (YYYY-MM-DD) * end_date (opcional): Filtrar por fecha de fin (YYYY-MM-DD) * active (opcional): Filtrar solo reservas activas (checkout > ahora) * confirmed (opcional): Filtrar por estado confirmado (true = confirmadas, false = pendientes) * limit (opcional, default: 1000): Límite de resultados * offset (opcional, default: 0): Offset para paginación

Response 200: Array de reservas con información completa incluyendo total_amount, total_paid, total_debt.

POST /admin/booking

Crea una nueva reserva.

Request Body:

{
  "client_id": 1,
  "court_id": 1,
  "reservation_type_id": 1,
  "reservation_status_id": 1,
  "checking": "2024-01-15T18:00:00Z",
  "checkout": "2024-01-15T19:00:00Z",
  "notes": "Notas adicionales"
}

Campos requeridos: * court_id: ID de la cancha (integer) * reservation_type_id: ID del tipo de reserva (integer) * checking: Fecha y hora de inicio (ISO 8601) * checkout: Fecha y hora de fin (ISO 8601)

Campos opcionales: * client_id: ID del cliente (integer, nullable, opcional para reservas de mantenimiento) * reservation_status_id: ID del estado de reserva (integer, nullable) * notes: Notas adicionales (string, nullable)

Response 201: Reserva creada exitosamente (BookingInterface). Response 400: Datos inválidos, incompletos o referencia inválida Response 401: No autenticado Response 409: Conflicto de reserva (cancha ocupada)

Nota: Este endpoint es un alias de /admin/bookings.

POST /admin/bookings

Crea una nueva reserva.

Request Body: Similar a POST /admin/booking.

Response 201: Reserva creada exitosamente (BookingInterface).

GET /admin/booking/{id}

Obtiene una reserva por ID con detalles completos.

Response 200: Reserva obtenida exitosamente con detalles completos (BookingDetailInterface). Response 404: Reserva no encontrada

Nota: BookingDetailInterface incluye información adicional del cliente (client_first_name, client_last_name, client_email, client_phone, client_document, client_address) y de la cancha (court_base_price, court_type_name).

GET /admin/bookings/{id}

Obtiene una reserva por ID con detalles completos.

Response 200: Reserva obtenida exitosamente con detalles completos (BookingDetailInterface). Response 404: Reserva no encontrada

PATCH /admin/booking/{id}

Edita una reserva por ID.

Request Body:

{
  "client_id": 1,
  "court_id": 1,
  "reservation_type_id": 1,
  "reservation_status_id": 1,
  "checking": "2024-01-15T18:00:00Z",
  "checkout": "2024-01-15T19:00:00Z",
  "notes": "Notas actualizadas"
}

Response 200: Reserva actualizada exitosamente (BookingInterface). Response 400: Datos inválidos Response 401: No autenticado Response 404: Reserva no encontrada

PATCH /admin/bookings/{id}

Edita una reserva por ID.

Request Body: Similar a PATCH /admin/booking/{id}.

Response 200: Reserva actualizada exitosamente (BookingInterface).

DELETE /admin/booking/{id}

Elimina una reserva por ID.

Response 204: Reserva eliminada exitosamente. Response 400: ID de reserva inválido Response 401: No autenticado Response 404: Reserva no encontrada

DELETE /admin/bookings/{id}

Elimina una reserva por ID.

Response 204: Reserva eliminada exitosamente.

DELETE /admin/booking

Elimina varias reservas.

Request Body:

{
  "ids": ["1", "2", "3"]
}

Response 200: Reservas eliminadas exitosamente.

Nota: Este endpoint es un alias de /admin/bookings DELETE.

DELETE /admin/bookings

Elimina varias reservas.

Request Body: Similar a DELETE /admin/booking.

Response 200: Reservas eliminadas exitosamente.

GET /admin/booking/calculate-price

Calcula el precio de una reserva con ajustes de tarifas.

Query Parameters: * date (requerido): Fecha de la reserva (YYYY-MM-DD) * checking (requerido): Hora de inicio (HH:MM) * checkout (requerido): Hora de fin (HH:MM) * court_id (requerido): ID de la cancha * reservation_type_id (opcional): ID del tipo de reserva

Response 200:

{
  "court": {
    "id": 1,
    "name": "Cancha 1",
    "base_price": 50000.00
  },
  "checking_date": "2024-01-15",
  "checking_time": "18:00:00",
  "checkout_date": "2024-01-15",
  "checkout_time": "19:00:00",
  "available": true,
  "hours": 1.0,
  "final_price": 50000.00,
  "price_per_hour": 50000.00,
  "applied_adjustments": []
}

Campos de respuesta: * court: Información de la cancha (id, name, base_price) * checking_date: Fecha de inicio (YYYY-MM-DD) * checking_time: Hora de inicio (HH:MM:SS) * checkout_date: Fecha de fin (YYYY-MM-DD) * checkout_time: Hora de fin (HH:MM:SS) * available: Indica si la cancha está disponible * hours: Número de horas de la reserva (decimal) * final_price: Precio final calculado después de aplicar tarifas * price_per_hour: Precio por hora calculado * applied_adjustments: Array de ajustes de tarifa aplicados

Response 400: Parámetros inválidos Response 404: Cancha no encontrada Response 500: Error interno del servidor

Nota: Este endpoint es un alias de /admin/bookings/calculate-price.

GET /admin/bookings/calculate-price

Calcula el precio de una reserva con ajustes de tarifas.

Query Parameters: Similar a GET /admin/booking/calculate-price.

Response 200: Ficha detallada con precio calculado. Estructura similar a GET /admin/booking/calculate-price pero con campos date, checking, checkout en lugar de checking_date, checking_time, checkout_date, checkout_time.

Nota: Este endpoint puede tener una estructura de respuesta ligeramente diferente para compatibilidad con versiones anteriores.

GET /admin/booking/{id}/price

Calcula el precio de una reserva existente por su ID.

Response 200: Ficha detallada con precio calculado. Estructura similar a GET /admin/booking/calculate-price con campos checking_date, checking_time, checkout_date, checkout_time. Response 404: Reserva no encontrada Response 500: Error interno del servidor

POST /admin/booking/validate

Valida fecha y disponibilidad de una reserva.

Request Body:

{
  "court_id": 1,
  "checking": "2024-01-15T18:00:00Z",
  "checkout": "2024-01-15T19:00:00Z",
  "reservation_id": null
}

Campos requeridos: * court_id: ID de la cancha (integer) * checking: Fecha y hora de inicio (ISO 8601) * checkout: Fecha y hora de fin (ISO 8601)

Campos opcionales: * reservation_id: ID de reserva existente (integer, nullable, excluir en validación)

Response 204: Validación exitosa (sin contenido). Response 400: Datos inválidos Response 409: Conflicto - fecha inválida o cancha no disponible

Pagos de Reservas (Admin)

Los endpoints de pagos permiten la gestión completa (CRUD) de pagos desde el panel de administración.

Nota importante: * El campo status es un boolean: false = pendiente, true = completado * Los pagos pueden estar asociados a reservas (reservation_id) o inscripciones (enrollment_id), pero no a ambas * Los pagos pueden ser "huérfanos" (sin reservation_id ni enrollment_id) cuando se reciben desde webhooks antes de que se cree el recurso asociado * El campo payment_type_id es opcional y puede ser null * La interfaz TypeScript correspondiente es PaymentInterface del paquete @cortex-ia-com-co/common

GET /admin/payments

Obtiene la lista de pagos con filtros y paginación.

Query Parameters: * search (opcional): Búsqueda por transaction_id o description (búsqueda ILIKE) * status (opcional): Filtrar por estado del pago (boolean: false = pendiente, true = completado) * reservation_id (opcional): Filtrar por ID de reservación * limit (opcional, default: 100): Límite de resultados * offset (opcional, default: 0): Offset para paginación

Nota: Los pagos pueden estar asociados a reservas (reservation_id) o inscripciones (enrollment_id).

Ordenamiento: Los resultados se ordenan primero por estado (pendientes primero) y luego por fecha descendente.

Response 200:

[
  {
    "id": 1,
    "reservation_id": 1,
    "enrollment_id": null,
    "payment_type_id": 1,
    "amount": 50000.00,
    "transaction_id": "TXN-123456",
    "gateway_response": "{\"status\":\"completed\"}",
    "status": true,
    "date": "2024-01-10T08:00:00Z",
    "description": "Pago de reserva"
  },
  {
    "id": 2,
    "reservation_id": null,
    "enrollment_id": null,
    "payment_type_id": null,
    "amount": 30000.00,
    "transaction_id": "TXN-789012",
    "gateway_response": null,
    "status": false,
    "date": "2024-01-11T10:00:00Z",
    "description": "Pago huérfano"
  }
]

GET /admin/payments/stats

Obtiene estadísticas agregadas de pagos.

Response 200:

{
  "total": 150,
  "pending": 10,
  "completed": 130,
  "orphan_payments": 8,
  "total_amount": 7500000.00,
  "completed_amount": 6500000.00,
  "by_status": {
    "completed": 130,
    "pending": 10,
    "failed": 0,
    "refunded": 0
  }
}

Campos de respuesta: * total: Total de pagos en el sistema * pending: Cantidad de pagos pendientes (status = false) * completed: Cantidad de pagos completados (status = true) * orphan_payments: Cantidad de pagos sin reservation_id ni enrollment_id asociado * total_amount: Suma total de todos los pagos * completed_amount: Suma total de pagos completados * by_status: Desglose por estado (nota: failed y refunded no están implementados actualmente)

GET /admin/payments/:id

Obtiene un pago específico por ID.

Response 200:

{
  "id": 1,
  "reservation_id": 1,
  "payment_type_id": 1,
  "amount": 50000.00,
  "transaction_id": "TXN-123456",
  "gateway_response": "{\"status\":\"completed\"}",
  "status": true,
  "date": "2024-01-10T08:00:00Z",
  "description": "Pago de reserva"
}

Response 404:

{
  "error": "Pago no encontrado"
}

GET /admin/payments/transaction/:transaction_id

Obtiene un pago por transaction_id del gateway de pago.

Response 200:

  {
    "id": 1,
    "reservation_id": 1,
    "enrollment_id": null,
    "payment_type_id": 1,
    "amount": 50000.00,
    "transaction_id": "TXN-123456",
    "gateway_response": "{\"status\":\"completed\"}",
    "status": true,
    "date": "2024-01-10T08:00:00Z",
    "description": "Pago de reserva"
  }

Response 404:

{
  "error": "Pago no encontrado"
}

GET /admin/booking/{bookingId}/payment

Lista todos los pagos de una reserva.

Response 200: Array de pagos de la reserva (PaymentInterface[]). Response 404: Reserva no encontrada Response 500: Error interno del servidor

POST /admin/booking/{bookingId}/payment

Crea un nuevo pago para una reserva.

Request Body:

{
  "payment_type_id": 1,
  "amount": 50000.00,
  "transaction_id": "TXN-123456",
  "status": false,
  "description": "Pago de reserva",
  "gateway_response": null
}

Campos requeridos: * amount: Monto del pago (number, decimal) * transaction_id: ID de transacción del gateway de pago (string)

Campos opcionales: * payment_type_id: ID del tipo de pago (integer, nullable) * status: Estado del pago (boolean, default: false) * description: Descripción del pago (string, nullable) * gateway_response: Respuesta completa del gateway de pago (string, nullable)

Response 201: Pago creado exitosamente (PaymentInterface). Response 400: Datos inválidos Response 404: Reserva no encontrada Response 500: Error interno del servidor

Nota: El reservation_id se toma automáticamente del parámetro de ruta bookingId.

GET /admin/booking/{bookingId}/payment/{paymentId}

Obtiene un pago específico de una reserva.

Response 200: Pago obtenido exitosamente (PaymentInterface). Response 404: Reserva o pago no encontrado Response 500: Error interno del servidor

PATCH /admin/booking/{bookingId}/payment/{paymentId}

Actualiza un pago de una reserva.

Request Body:

{
  "payment_type_id": 1,
  "amount": 50000.00,
  "transaction_id": "TXN-123456",
  "status": true,
  "description": "Pago actualizado",
  "gateway_response": "{\"status\":\"completed\"}"
}

Response 200: Pago actualizado exitosamente (PaymentInterface). Response 400: Datos inválidos Response 404: Reserva o pago no encontrado Response 500: Error interno del servidor

DELETE /admin/booking/{bookingId}/payment

Elimina un pago de una reserva.

Query Parameters: * paymentId (requerido): ID del pago a eliminar

Response 200: Pago eliminado exitosamente. Response 404: Reserva o pago no encontrado Response 500: Error interno del servidor

POST /admin/payments

Crea un nuevo pago. Útil para registrar pagos manuales o sincronizar pagos desde sistemas externos.

Request Body:

{
  "reservation_id": 1,
  "enrollment_id": null,
  "payment_type_id": 1,
  "amount": 50000.00,
  "transaction_id": "TXN-123456",
  "status": true,
  "description": "Pago de reserva",
  "gateway_response": "{\"status\":\"completed\"}"
}

Campos requeridos: * amount: Monto del pago (number) * transaction_id: ID de transacción del gateway (string) * status: Estado del pago (boolean)

Campos opcionales: * reservation_id: ID de la reservación asociada (integer, nullable, mutuamente exclusivo con enrollment_id) * enrollment_id: ID de la inscripción asociada (integer, nullable, mutuamente exclusivo con reservation_id) * payment_type_id: ID del tipo de pago (integer, nullable) * description: Descripción del pago (string, nullable) * gateway_response: Respuesta completa del gateway (string, nullable)

Validaciones: * Debe proporcionarse reservation_id O enrollment_id, pero no ambos * Si se proporciona reservation_id, debe existir en la tabla reservation * Si se proporciona enrollment_id, debe existir en la tabla enrollment * El transaction_id debe ser una cadena no vacía

Response 201:

  {
    "id": 1,
    "reservation_id": 1,
    "enrollment_id": null,
    "payment_type_id": 1,
    "amount": 50000.00,
    "transaction_id": "TXN-123456",
    "gateway_response": "{\"status\":\"completed\"}",
    "status": true,
    "date": "2024-01-10T08:00:00Z",
    "description": "Pago de reserva"
  }

Response 400:

{
  "error": "Datos incompletos",
  "message": "amount y transaction_id son requeridos"
}

PATCH /admin/payments/:id

Actualiza un pago existente. Permite actualizar campos específicos sin requerir todos los campos.

Request Body:

{
  "reservation_id": 2,
  "enrollment_id": null,
  "status": true,
  "description": "Pago actualizado"
}

Campos actualizables: * reservation_id: Asignar o cambiar la reservación asociada (integer, nullable, mutuamente exclusivo con enrollment_id) * enrollment_id: Asignar o cambiar la inscripción asociada (integer, nullable, mutuamente exclusivo con reservation_id) * payment_type_id: Cambiar el tipo de pago (integer, nullable) * status: Actualizar el estado del pago (boolean) * description: Actualizar la descripción (string, nullable)

Validaciones: * Si se proporciona reservation_id, debe existir en la tabla reservation y no puede tener enrollment_id simultáneamente * Si se proporciona enrollment_id, debe existir en la tabla enrollment y no puede tener reservation_id simultáneamente * Al menos un campo debe ser proporcionado para actualizar

Response 200:

  {
    "id": 1,
    "reservation_id": 2,
    "enrollment_id": null,
    "payment_type_id": 1,
    "amount": 50000.00,
    "transaction_id": "TXN-123456",
    "gateway_response": "{\"status\":\"completed\"}",
    "status": true,
    "date": "2024-01-10T08:00:00Z",
    "description": "Pago actualizado"
  }

Response 400:

{
  "error": "Datos incompletos",
  "message": "Debe proporcionar al menos un campo para actualizar"
}

Response 404:

{
  "error": "Pago no encontrado"
}

DELETE /admin/payments/:id

Elimina un pago del sistema. Use con precaución, ya que esto elimina permanentemente el registro del pago.

Response 204: No Content (éxito)

Response 404:

{
  "error": "Pago no encontrado"
}

Clases (Admin)

GET /admin/classes

Obtiene lista de clases (admin).

Response 200: Lista de clases (TrainingClassInterface[]).

POST /admin/classes

Crea una nueva clase (admin).

Request Body:

{
  "user_id": 1,
  "title": "Clase de Fútbol",
  "description": "Clase de entrenamiento",
  "start_time": "2024-01-15T10:00:00Z",
  "end_time": "2024-01-15T12:00:00Z",
  "start_date": "2024-01-15",
  "end_date": "2024-03-15",
  "class_start_time": "10:00:00",
  "recurrence_days": ["MO", "WE", "FR"]
}

Campos requeridos: * title: Título de la clase * start_time: Fecha y hora de inicio * end_time: Fecha y hora de fin * start_date: Fecha de inicio del período * end_date: Fecha de fin del período * class_start_time: Hora de inicio de cada clase * recurrence_days: Array de días de recurrencia (MO, TU, WE, TH, FR, SA, SU)

Campos opcionales: * user_id: ID del entrenador (opcional, usa admin actual) * description: Descripción de la clase

Response 201: Clase creada exitosamente.

GET /admin/classes/{id}

Obtiene una clase específica (admin).

Response 200: Datos de la clase (TrainingClassInterface). Response 404: Clase no encontrada

PUT /admin/classes/{id}

Actualiza una clase (admin).

Response 200: Clase actualizada exitosamente.

DELETE /admin/classes/{id}

Elimina una clase (admin).

Response 200: Clase eliminada exitosamente.

PATCH /admin/classes/{id}/cancel

Cancela una clase (admin).

Response 200: Clase cancelada exitosamente.

PATCH /admin/classes/{id}/complete

Marca una clase como completada (admin).

Response 200: Clase marcada como completada exitosamente.

Inscripciones (Admin)

GET /admin/enrollments

Obtiene lista de inscripciones (admin).

Query Parameters: * class_id (opcional): Filtrar inscripciones por clase * status (opcional): Filtrar inscripciones por estado (boolean: true = activo, false = inactivo)

Response 200: Lista de inscripciones (EnrollmentInterface[]).

POST /admin/enrollments

Crea una nueva inscripción (admin).

Request Body:

{
  "class_id": 1,
  "client_id": 1,
  "payment_type_id": 1,
  "payment_amount": 50000.00,
  "payment_reference": "TXN-123456"
}

Campos requeridos: * class_id: ID de la clase * client_id: ID del cliente

Campos opcionales: * payment_type_id: ID del tipo de pago * payment_amount: Monto del pago * payment_reference: Referencia de la transacción

Response 201: Inscripción creada exitosamente. Response 400: Datos inválidos Response 404: Clase no encontrada

GET /admin/enrollments/{id}

Obtiene una inscripción específica (admin).

Response 200: Datos de la inscripción (EnrollmentInterface). Response 404: Inscripción no encontrada

DELETE /admin/enrollments/{id}

Elimina una inscripción (admin).

Response 200: Inscripción eliminada exitosamente.

PATCH /admin/enrollments/{id}/accept

Acepta una inscripción (admin).

Response 200: Inscripción aceptada exitosamente. Response 400: La inscripción no está en estado pendiente

PATCH /admin/enrollments/{id}/reject

Rechaza una inscripción (admin).

Request Body:

{
  "reason": "Cupo lleno"
}

Response 200: Inscripción rechazada exitosamente.

PATCH /admin/enrollments/{id}/attendance

Registra asistencia de un alumno (admin).

Request Body:

{
  "attendance_status": "present"
}

Valores permitidos: "present" o "absent"

Response 200: Asistencia registrada exitosamente. Response 400: La inscripción no está aceptada

Notificaciones (Admin)

POST /admin/notifications/token

Registra o actualiza token FCM para usuario (admin).

Request Body:

{
  "userId": "firebase-uid-123",
  "token": "fcm-token-abc123"
}

Campos requeridos: * userId: Firebase Auth UID del usuario * token: Token FCM del dispositivo

Response 200: Token FCM registrado exitosamente. Response 400: Datos incompletos

DELETE /admin/notifications/token

Elimina token FCM del usuario autenticado (admin).

Response 200: Token FCM eliminado exitosamente. Response 404: Token no encontrado

POST /admin/notifications/send

Envia notificación push a un cliente específico.

Request Body:

{
  "clientId": 1,
  "title": "Título de la notificación",
  "body": "Cuerpo de la notificación",
  "url": "/reservations/1"
}

Campos requeridos: * clientId: ID del cliente al que se enviará la notificación

Campos opcionales: * title: Título de la notificación * body: Cuerpo de la notificación * url: URL a la que navegar al hacer clic

Response 200: Notificación enviada exitosamente. Response 400: Datos incompletos Response 404: Cliente o usuario no encontrado

Nota: La interfaz TypeScript correspondiente es PushNotificationPayload del paquete @cortex-ia-com-co/common.

Trainer (Entrenadores)

Los endpoints bajo /trainer/* permiten a los entrenadores gestionar sus clases y alumnos.

Clases (Trainer)

GET /trainer/classes

Obtiene lista de clases del entrenador.

Response 200: Lista de clases (TrainingClassInterface[]).

POST /trainer/classes

Crea una nueva clase.

Request Body: Similar a POST /admin/classes pero sin user_id (usa el entrenador autenticado).

Response 201: Clase creada exitosamente.

GET /trainer/classes/{id}

Obtiene una clase específica.

Response 200: Datos de la clase (TrainingClassInterface). Response 404: Clase no encontrada

PUT /trainer/classes/{id}

Actualiza una clase.

Response 200: Clase actualizada exitosamente.

DELETE /trainer/classes/{id}

Elimina una clase.

Response 200: Clase eliminada exitosamente.

PATCH /trainer/classes/{id}/cancel

Cancela una clase.

Response 200: Clase cancelada exitosamente.

PATCH /trainer/classes/{id}/complete

Marca una clase como completada.

Response 200: Clase marcada como completada exitosamente.

Reservas (Trainer)

GET /trainer/bookings

Obtiene lista de reservas del entrenador.

Query Parameters: * court_id (opcional): Filtrar por cancha específica * from (opcional): Fecha de inicio para filtrar reservas (ISO 8601) * to (opcional): Fecha de fin para filtrar reservas (ISO 8601)

Response 200: Lista de reservas (excluye canceladas).

POST /trainer/bookings

Crea una reserva para el entrenador.

Request Body: Similar a POST /account/bookings.

Response 201: Reserva creada exitosamente.

GET /trainer/bookings/calculate-price

Calcula el precio de una reserva con ajustes de tarifas.

Query Parameters: * date (requerido): Fecha de la reserva (YYYY-MM-DD) * checking (requerido): Hora de inicio (HH:MM) * checkout (requerido): Hora de fin (HH:MM) * court_id (requerido): ID de la cancha * reservation_type_id (opcional): ID del tipo de reserva

Response 200: Ficha detallada con precio calculado (CalculatePriceResponseInterface). Response 400: Parámetros inválidos Response 404: Cancha no encontrada Response 500: Error interno del servidor

Inscripciones (Trainer)

GET /trainer/enrollments

Obtiene lista de inscripciones del entrenador.

Query Parameters: * class_id (opcional): Filtrar inscripciones por clase * status (opcional): Filtrar inscripciones por estado (boolean)

Response 200: Lista de inscripciones (EnrollmentInterface[]).

POST /trainer/enrollments

Crea una nueva inscripción.

Request Body:

{
  "class_id": 1,
  "client_id": 1
}

Response 201: Inscripción creada exitosamente. Response 400: Datos inválidos Response 404: Clase no encontrada

GET /trainer/enrollments/{id}

Obtiene una inscripción específica.

Response 200: Datos de la inscripción (EnrollmentInterface). Response 404: Inscripción no encontrada

DELETE /trainer/enrollments/{id}

Elimina una inscripción.

Response 200: Inscripción eliminada exitosamente. Response 400: No se puede eliminar (fecha pasada o no creada por el trainer)

PATCH /trainer/enrollments/{id}/accept

Acepta una inscripción.

Response 200: Inscripción aceptada exitosamente.

PATCH /trainer/enrollments/{id}/reject

Rechaza una inscripción.

Request Body:

{
  "reason": "Cupo lleno"
}

Response 200: Inscripción rechazada exitosamente.

PATCH /trainer/enrollments/{id}/attendance

Registra asistencia de un alumno.

Request Body:

{
  "attendance_status": "present"
}

Response 200: Asistencia registrada exitosamente.

Alumnos (Trainer)

GET /trainer/students

Obtiene lista de alumnos del entrenador.

Query Parameters: * class_id (opcional): Filtrar alumnos por clase específica

Response 200: Lista de alumnos (StudentInterface[]).

GET /trainer/students/{id}

Obtiene un alumno específico.

Response 200: Datos del alumno (StudentInterface). Response 404: Alumno no encontrado

POST /trainer/students/{id}/assign

Asigna un alumno a una clase.

Request Body:

{
  "class_id": 1
}

Response 201: Alumno asignado exitosamente. Response 400: Datos inválidos Response 404: Clase o alumno no encontrado

Clientes (Trainer)

GET /trainer/clients

Obtiene lista de clientes con búsqueda.

Query Parameters: * search (opcional): Búsqueda por email, display_name, first_name, last_name, document o phone * limit (opcional, default: 20): Límite de resultados * offset (opcional, default: 0): Offset para paginación

Response 200: Lista de clientes (ClientInterface[]).

POST /trainer/clients

Crea un nuevo cliente.

Request Body: Similar a POST /admin/clients.

Response 201: Cliente creado exitosamente.

GET /trainer/clients/{id}

Obtiene un cliente específico.

Response 200: Datos del cliente (ClientInterface). Response 404: Cliente no encontrado

Canchas (Trainer)

GET /trainer/courts

Obtiene lista de canchas disponibles.

Response 200: Lista de canchas (CourtInterface[]).

Dashboard (Trainer)

GET /trainer/dashboard/stats

Obtiene estadísticas del dashboard del entrenador.

Response 200: Estadísticas del dashboard.

Tipos de Documento (Trainer)

GET /trainer/document-types

Obtiene todos los tipos de documento disponibles.

Response 200: Lista de tipos de documento (DocumentTypeInterface[]).

Tipos de Pago (Trainer)

GET /trainer/payment-types

Obtiene todos los tipos de pago disponibles.

Response 200: Lista de tipos de pago (PaymentTypeInterface[]).

Tipos de Reserva (Trainer)

GET /trainer/reservation-types

Obtiene todos los tipos de reserva disponibles.

Response 200: Lista de tipos de reserva (ReservationTypeInterface[]).

Tarifas de Clase (Trainer)

GET /trainer/tariff-classes

Obtiene lista de tarifas de clase.

Query Parameters: * court_type_id (opcional): Filtrar por tipo de cancha

Response 200: Lista de tarifas de clase (TariffClassInterface[]).

GET /trainer/tariff-classes/{id}

Obtiene una tarifa de clase específica.

Response 200: Tarifa de clase encontrada (TariffClassInterface). Response 404: Tarifa de clase no encontrada

Public (Público)

Los endpoints bajo /public/* no requieren autenticación.

GET /public/document-types

Obtiene todos los tipos de documento (público).

Response 200: Lista de tipos de documento (DocumentTypeInterface[]).

Sudo (Super Administradores)

Los endpoints bajo /sudo/* son exclusivos para super administradores con permisos completos.

Usuarios (Sudo)

GET /sudo/users

Obtiene todos los usuarios de Firebase Auth (solo para administradores).

Query Parameters: * maxResults (opcional, default: 1000): Número máximo de resultados (máximo 1000) * pageToken (opcional): Token para paginación

Response 200: Lista de usuarios con paginación (UsersListResponseInterface). Response 403: No tienes permisos de administrador

POST /sudo/users

Crea un nuevo usuario en Firebase Auth.

Request Body:

{
  "email": "usuario@example.com",
  "password": "password123",
  "displayName": "Usuario",
  "disabled": false,
  "emailVerified": false,
  "role": "admin",
  "trainer": false
}

Campos requeridos: * email: Email del usuario * password: Contraseña del usuario

Campos opcionales: * displayName: Nombre para mostrar * disabled: Si el usuario está deshabilitado * emailVerified: Si el email está verificado * role: Rol principal ("admin", "operator", "sudo", "trainer") * trainer: Indica si el usuario tendrá rol de entrenador (boolean)

Response 201: Usuario creado exitosamente (FirebaseUserInterface). Response 400: Datos inválidos (email duplicado, formato inválido, etc.)

GET /sudo/users/{uid}

Obtiene un usuario específico de Firebase Auth.

Response 200: Usuario obtenido exitosamente (FirebaseUserInterface). Response 404: Usuario no encontrado

PUT /sudo/users/{uid}

Actualiza un usuario de Firebase Auth.

Request Body: Similar a POST pero con campos opcionales.

Response 200: Usuario actualizado exitosamente.

DELETE /sudo/users/{uid}

Elimina un usuario de Firebase Auth.

Response 204: Usuario eliminado exitosamente. Response 404: Usuario no encontrado

POST /sudo/users/{uid}/enable

Habilita un usuario de Firebase Auth.

Response 200: Usuario habilitado exitosamente.

POST /sudo/users/{uid}/disable

Deshabilitar un usuario de Firebase Auth.

Response 200: Usuario deshabilitado exitosamente.

Pagos (Cuenta de Usuario)

Los endpoints de pagos de cuenta permiten a los usuarios autenticados gestionar sus propios pagos asociados a sus reservas.

Nota importante: * Estos endpoints solo devuelven pagos asociados a reservas del usuario autenticado * El campo status es un boolean: false = pendiente, true = completado * Los pagos incluyen información adicional de la reserva asociada (court_name, checking, checkout) * Validación de pagos para confirmar reserva: Para confirmar una reserva, todos los pagos asociados deben tener status: true (completados) y el monto total pagado debe cubrir el precio total de la reserva

GET /account/payments

Obtiene el historial de pagos del usuario autenticado con paginación.

Query Parameters: * page (opcional, default: 1): Número de página * limit (opcional, default: 10): Cantidad de resultados por página

Response 200:

{
  "payments": [
    {
      "id": 1,
      "reservation_id": 1,
      "amount": 50000.00,
      "transaction_id": "TXN-123456",
      "gateway_response": "{\"status\":\"completed\"}",
      "status": true,
      "date": "2024-01-10T08:00:00Z",
      "description": "Pago de reserva",
      "court_name": "Cancha 1",
      "checking": "2024-01-15T10:00:00Z",
      "checkout": "2024-01-15T12:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 25,
    "totalPages": 3
  }
}

Campos adicionales en respuesta: * court_name: Nombre de la cancha de la reserva asociada * checking: Fecha y hora de inicio de la reserva * checkout: Fecha y hora de fin de la reserva

GET /account/payments/:id

Obtiene un pago específico del usuario autenticado.

Response 200:

{
  "id": 1,
  "reservation_id": 1,
  "amount": 50000.00,
  "transaction_id": "TXN-123456",
  "gateway_response": "{\"status\":\"completed\"}",
  "status": true,
  "date": "2024-01-10T08:00:00Z",
  "description": "Pago de reserva",
  "court_name": "Cancha 1",
  "checking": "2024-01-15T10:00:00Z",
  "checkout": "2024-01-15T12:00:00Z"
}

Response 403:

{
  "error": "Acceso denegado",
  "message": "Este pago no pertenece al usuario autenticado"
}

Response 404:

{
  "error": "Pago no encontrado"
}

POST /account/payments

Crea un nuevo pago asociado a una reserva del cliente autenticado. El frontend debe asegurarse de que la reserva ya exista y pertenezca al cliente actual.

Request Body:

{
  "reservation_id": 1,
  "payment_type_id": 1,
  "amount": 50000.00,
  "transaction_id": "TXN-123456",
  "description": "Pago de reserva"
}

Campos requeridos: * reservation_id: ID de la reserva asociada (integer) * payment_type_id: ID del tipo de pago (integer) * amount: Monto del pago (number, debe ser mayor a 0) * transaction_id: ID de transacción del gateway (string, no vacío)

Campos opcionales: * description: Descripción del pago (string, nullable)

Validaciones: * La reserva debe existir y pertenecer al cliente del usuario autenticado * El transaction_id debe ser una cadena no vacía * El amount debe ser un número mayor a 0 * Los IDs deben ser numéricos válidos

Response 201:

{
  "id": 1,
  "reservation_id": 1,
  "payment_type_id": 1,
  "amount": 50000.00,
  "transaction_id": "TXN-123456",
  "gateway_response": null,
  "status": false,
  "date": "2024-01-10T08:00:00Z",
  "description": "Pago de reserva"
}

Response 400:

{
  "error": "Datos incompletos",
  "message": "reservation_id, payment_type_id, amount y transaction_id son requeridos"
}

Response 403:

{
  "error": "Acceso denegado",
  "message": "La reserva no pertenece al usuario autenticado"
}

Clientes (Admin)

GET /admin/clients

Obtiene todos los clientes.

Query Parameters: * search (opcional): Búsqueda por email o display_name * limit (opcional, default: 100): Límite de resultados * offset (opcional, default: 0): Offset para paginación

Response 200: Lista de clientes (ClientInterface[]).

POST /admin/clients

Crea un nuevo cliente.

Request Body:

{
  "user_id": 1,
  "email": "cliente@example.com",
  "display_name": "Juan Pérez",
  "first_name": "Juan",
  "last_name": "Pérez",
  "phone": "+573001234567",
  "document": "1234567890",
  "document_type_id": 1,
  "address": "Calle 123"
}

Campos requeridos: * first_name: Nombre del cliente * last_name: Apellido del cliente * document: Número de documento * document_type_id: ID del tipo de documento

Campos opcionales: * user_id: ID del usuario existente en la base de datos (user.id). Opcional si se proporciona email * email: Email del nuevo usuario (opcional si se proporciona user_id). Se creará el usuario en Firebase Auth con contraseña aleatoria y se enviará un email con link para establecer contraseña * display_name: Nombre para mostrar del usuario (solo si se proporciona email) * phone: Teléfono del cliente (opcional). Se vinculará al usuario en Firebase Auth * address: Dirección del cliente

Response 201: Cliente creado exitosamente (ClientInterface). Response 400: Datos inválidos

GET /admin/clients/{id}

Obtiene un cliente por ID.

Response 200: Cliente encontrado (ClientInterface). Response 404: Cliente no encontrado

PATCH /admin/clients/{id}

Edita un cliente por ID.

Request Body:

{
  "first_name": "Juan",
  "last_name": "Pérez",
  "document": "1234567890",
  "document_type_id": 1,
  "address": "Calle 123"
}

Response 200: Cliente actualizado exitosamente.

DELETE /admin/clients/{id}

Elimina un cliente por ID.

Response 200: Cliente eliminado exitosamente.

Canchas (Admin)

GET /admin/courts

Obtiene todas las canchas.

Query Parameters: * search (opcional): Búsqueda por nombre

Response 200: Lista de canchas (CourtInterface[]).

POST /admin/courts

Crea una nueva cancha.

Request Body:

{
  "name": "Cancha 1",
  "court_type_id": 1,
  "cost": 10000.00,
  "price": 50000.00
}

Campos requeridos: * name: Nombre de la cancha * court_type_id: ID del tipo de cancha

Response 201: Cancha creada exitosamente (CourtInterface). Response 400: Datos inválidos

GET /admin/courts/{id}

Obtiene una cancha por ID.

Response 200: Cancha encontrada (CourtInterface). Response 404: Cancha no encontrada

PATCH /admin/courts/{id}

Actualiza una cancha.

Response 200: Cancha actualizada exitosamente.

DELETE /admin/courts/{id}

Elimina una cancha.

Response 204: Cancha eliminada exitosamente.

Tipos de Cancha (Admin)

GET /admin/court-types

Obtiene todos los tipos de cancha.

Query Parameters: * search (opcional): Búsqueda por nombre o descripción

Response 200: Lista de tipos de cancha (CourtTypeInterface[]).

POST /admin/court-types

Crea un nuevo tipo de cancha.

Request Body:

{
  "name": "Fútbol",
  "description": "Cancha de fútbol"
}

Campos requeridos: * name: Nombre del tipo de cancha

Response 201: Tipo de cancha creado exitosamente (CourtTypeInterface). Response 400: Datos inválidos

GET /admin/court-types/{id}

Obtiene un tipo de cancha por ID.

Response 200: Tipo de cancha encontrado (CourtTypeInterface). Response 404: Tipo de cancha no encontrado

PATCH /admin/court-types/{id}

Actualiza un tipo de cancha.

Response 200: Tipo de cancha actualizado exitosamente.

DELETE /admin/court-types/{id}

Elimina un tipo de cancha.

Response 204: Tipo de cancha eliminado exitosamente.

Tipos de Documento (Admin)

GET /admin/document-types

Obtiene todos los tipos de documento.

Query Parameters: * search (opcional): Búsqueda por nombre o descripción

Response 200: Lista de tipos de documento (DocumentTypeInterface[]).

POST /admin/document-types

Crea un nuevo tipo de documento.

Request Body:

{
  "name": "Cédula de Ciudadanía",
  "description": "Documento de identidad colombiano"
}

Campos requeridos: * name: Nombre del tipo de documento

Response 201: Tipo de documento creado exitosamente (DocumentTypeInterface). Response 400: Datos inválidos

GET /admin/document-types/{id}

Obtiene un tipo de documento por ID.

Response 200: Tipo de documento encontrado (DocumentTypeInterface). Response 404: Tipo de documento no encontrado

PATCH /admin/document-types/{id}

Actualiza un tipo de documento.

Response 200: Tipo de documento actualizado exitosamente.

DELETE /admin/document-types/{id}

Elimina un tipo de documento.

Response 204: Tipo de documento eliminado exitosamente.

Tipos de Pago (Admin)

GET /admin/payment-types

Obtiene todos los tipos de pago.

Query Parameters: * search (opcional): Búsqueda por nombre o descripción

Response 200: Lista de tipos de pago (PaymentTypeInterface[]).

POST /admin/payment-types

Crea un nuevo tipo de pago.

Request Body:

{
  "name": "Efectivo",
  "description": "Pago en efectivo"
}

Campos requeridos: * name: Nombre del tipo de pago

Response 201: Tipo de pago creado exitosamente (PaymentTypeInterface). Response 400: Datos inválidos

GET /admin/payment-types/{id}

Obtiene un tipo de pago por ID.

Response 200: Tipo de pago encontrado (PaymentTypeInterface). Response 404: Tipo de pago no encontrado

PATCH /admin/payment-types/{id}

Actualiza un tipo de pago.

Response 200: Tipo de pago actualizado exitosamente.

DELETE /admin/payment-types/{id}

Elimina un tipo de pago.

Response 204: Tipo de pago eliminado exitosamente.

Estados de Reserva (Admin)

GET /admin/reservation-statuses

Obtiene todos los estados de reserva.

Query Parameters: * search (opcional): Búsqueda por nombre o descripción

Response 200: Lista de estados de reserva (ReservationStatusInterface[]).

POST /admin/reservation-statuses

Crea un nuevo estado de reserva.

Request Body:

{
  "name": "Confirmada",
  "description": "Reserva confirmada",
  "color": "#10b981"
}

Campos requeridos: * name: Nombre del estado * color: Color hexadecimal para representación visual

Response 201: Estado de reserva creado exitosamente (ReservationStatusInterface). Response 400: Datos inválidos

GET /admin/reservation-statuses/{id}

Obtiene un estado de reserva por ID.

Response 200: Estado de reserva encontrado (ReservationStatusInterface). Response 404: Estado de reserva no encontrado

PATCH /admin/reservation-statuses/{id}

Actualiza un estado de reserva.

Response 200: Estado de reserva actualizado exitosamente.

DELETE /admin/reservation-statuses/{id}

Elimina un estado de reserva.

Response 204: Estado de reserva eliminado exitosamente.

Tipos de Reserva (Admin)

GET /admin/reservation-types

Obtiene todos los tipos de reserva.

Query Parameters: * search (opcional): Búsqueda por nombre o descripción

Response 200: Lista de tipos de reserva (ReservationTypeInterface[]).

POST /admin/reservation-types

Crea un nuevo tipo de reserva.

Request Body:

{
  "name": "Libre",
  "description": "Reserva libre"
}

Campos requeridos: * name: Nombre del tipo de reserva

Response 201: Tipo de reserva creado exitosamente (ReservationTypeInterface). Response 400: Datos inválidos

GET /admin/reservation-types/{id}

Obtiene un tipo de reserva por ID.

Response 200: Tipo de reserva encontrado (ReservationTypeInterface). Response 404: Tipo de reserva no encontrado

PATCH /admin/reservation-types/{id}

Actualiza un tipo de reserva.

Response 200: Tipo de reserva actualizado exitosamente.

DELETE /admin/reservation-types/{id}

Elimina un tipo de reserva.

Response 204: Tipo de reserva eliminado exitosamente.

Tarifas (Admin)

GET /admin/tariffs

Obtiene todas las tarifas.

Query Parameters: * search (opcional): Búsqueda por descripción * court_id (opcional): Filtrar por cancha * reservation_type_id (opcional): Filtrar por tipo de reserva

Response 200: Lista de tarifas (TariffInterface[]).

POST /admin/tariffs

Crea una nueva tarifa.

Request Body:

{
  "court_id": 1,
  "reservation_type_id": 1,
  "rrule": null,
  "start_date": "2024-01-01",
  "end_date": "2024-12-31",
  "start_time": "18:00:00",
  "end_time": "22:00:00",
  "adjustment": "-10%",
  "description": "Descuento nocturno",
  "is_active": true
}

Campos requeridos: * adjustment: Ajuste (ej: "-10%", "5000", "-2000", "15%") * is_active: Si la tarifa está activa (boolean)

Campos opcionales: * court_id: ID de la cancha (null = todas las canchas) * reservation_type_id: ID del tipo de reserva (null = todos los tipos) * rrule: Regla recurrente según RFC 5545 (RRULE) * start_date: Fecha de inicio del período (YYYY-MM-DD) * end_date: Fecha de fin del período (YYYY-MM-DD) * start_time: Hora de inicio del rango horario (HH:MM:SS) * end_time: Hora de fin del rango horario (HH:MM:SS) * description: Descripción de la tarifa

Response 201: Tarifa creada exitosamente (TariffInterface). Response 400: Datos inválidos

GET /admin/tariffs/{id}

Obtiene una tarifa por ID.

Response 200: Tarifa encontrada (TariffInterface). Response 404: Tarifa no encontrada

PATCH /admin/tariffs/{id}

Actualiza una tarifa.

Response 200: Tarifa actualizada exitosamente.

DELETE /admin/tariffs/{id}

Elimina una tarifa.

Response 204: Tarifa eliminada exitosamente.

Tarifas de Clase (Admin)

GET /admin/tariff-classes

Obtiene todas las tarifas de clase.

Query Parameters: * court_type_id (opcional): Filtrar por tipo de cancha * reservation_type_id (opcional): Filtrar por tipo de reserva

Response 200: Lista de tarifas de clase (TariffClassInterface[]).

POST /admin/tariff-classes

Crea una nueva tarifa de clase.

Request Body:

{
  "court_type_id": 1,
  "reservation_type_id": 2,
  "single_price": 25000.00,
  "total_price": 50000.00,
  "max_capacity": 20
}

Campos requeridos: * court_type_id: ID del tipo de cancha * reservation_type_id: ID del tipo de reserva * single_price: Precio por persona * total_price: Precio total de la clase * max_capacity: Capacidad máxima de la clase

Response 201: Tarifa de clase creada exitosamente (TariffClassInterface). Response 400: Datos inválidos

GET /admin/tariff-classes/{id}

Obtiene una tarifa de clase por ID.

Response 200: Tarifa de clase encontrada (TariffClassInterface). Response 404: Tarifa de clase no encontrada

PATCH /admin/tariff-classes/{id}

Actualiza una tarifa de clase.

Response 200: Tarifa de clase actualizada exitosamente.

DELETE /admin/tariff-classes/{id}

Elimina una tarifa de clase.

Response 204: Tarifa de clase eliminada exitosamente.

Dashboard (Admin)

GET /admin/dashboard/stats

Obtiene estadísticas del dashboard.

Response 200: Estadísticas del dashboard (DashboardStatsWithRecentInterface).

Campos de respuesta: * clients: Estadísticas de clientes (total, verified, unverified) * courts: Estadísticas de canchas (total) * reservations: Estadísticas de reservas (total, today, this_week, this_month) * payments: Estadísticas de pagos (total, total_amount, completed_amount, pending) * recent_reservations: Array de reservas recientes * recent_payments: Array de pagos recientes

Usuarios (Admin)

GET /admin/users

Obtiene usuarios de Firebase Auth (para operadores y administradores).

Query Parameters: * maxResults (opcional, default: 50): Número máximo de resultados (máximo 1000) * pageToken (opcional): Token para paginación

Response 200: Lista de usuarios con paginación (UsersListResponseInterface).

GET /admin/users/{uid}

Obtiene un usuario específico de Firebase Auth.

Response 200: Usuario obtenido exitosamente (FirebaseUserInterface). Response 404: Usuario no encontrado

Interfaces TypeScript

Todas las interfaces TypeScript están definidas en el paquete @cortex-ia-com-co/common y están disponibles para importar:

import {
  BookingInterface,
  BookingDetailInterface,
  BookingFullInterface,
  AccountBookingInterface,
  AccountBookingPaymentInterface,
  AccountPaymentInterface,
  PaymentInterface,
  ClientInterface,
  TrainingClassInterface,
  EnrollmentInterface,
  TariffClassInterface,
  ReservationTypeInterface,
  ReservationStatusInterface,
  PaymentTypeInterface,
  DocumentTypeInterface,
  CourtInterface,
  CourtTypeInterface,
  TariffInterface,
  CalculatePriceResponseInterface,
  PaginationInterface,
  PaginatedResponseInterface,
  StudentInterface,
  FirebaseUserInterface,
  UserInterface,
  UsersListResponseInterface,
  DashboardStatsWithRecentInterface,
  PushNotificationPayload,
  ErrorResponseInterface
} from '@cortex-ia-com-co/common';

Interfaces principales:

Reservas

  • BookingInterface: Estructura base de reservas con campos básicos (id, client_id, court_id, reservation_type_id, checking, checkout, notes, etc.)

  • BookingDetailInterface: Extiende BookingInterface con información completa del cliente (client_first_name, client_last_name, client_email, client_phone, client_document, client_address) y de la cancha (court_base_price, court_type_name), además de pagos asociados

  • BookingFullInterface: Reservas con información completa de pagos y precios (total_amount, total_paid, total_debt)

  • AccountBookingInterface: Reservas con información de pagos para usuarios autenticados, incluye total_paid, total_amount, is_payment_complete, can_add_payment y array de payments

  • AccountBookingPaymentInterface: Estructura de pagos dentro de reservas de cuenta, incluye status (string: "pending" | "completed") y statusBoolean (boolean) para compatibilidad

Pagos

  • PaymentInterface: Estructura de pagos (puede tener reservation_id o enrollment_id, pero no ambos). El campo status es boolean (false = pendiente, true = completado)

  • AccountPaymentInterface: Extiende PaymentInterface con información adicional de la reserva asociada (court_name, checking, checkout)

Clientes y Usuarios

  • ClientInterface: Estructura de clientes con información completa (id, user_id, email, display_name, first_name, last_name, phone, document, document_type_id, address, email_verified, is_available)

  • StudentInterface: Estructura de alumnos del entrenador (id, user_id, email, display_name, first_name, last_name, phone, email_verified, created_at)

  • FirebaseUserInterface: Estructura de usuarios de Firebase Auth con claims de roles (uid, email, emailVerified, displayName, disabled, role, trainer, operator, admin, sudo, metadata)

  • UserInterface: Estructura de usuarios en la base de datos (id, firebase_id, email, email_verified, display_name, phone, disabled, creation_time, last_sign_in_time, etc.)

  • UsersListResponseInterface: Respuesta de lista de usuarios con paginación (users, pageToken, total)

Clases y Inscripciones

  • TrainingClassInterface: Estructura de clases de entrenamiento (id, user_id, reservation_id, title, description, start_time, end_time, start_date, end_date, class_start_time, recurrence_days, status, completed_at, trainer_name, enrolled_count, etc.)

  • EnrollmentInterface: Estructura de inscripciones (id, class_id, client_id, user_id, status, enrolled_at, accepted_at, rejected_at, attendance_status, attendance_marked_at, payment_type_id, payment_amount, payment_reference, student, training_class)

Canchas y Tipos

  • CourtInterface: Estructura de canchas (id, court_type_id, name, cost, price, court_type_name)

  • CourtTypeInterface: Estructura de tipos de cancha (id, name, description, created_at)

Tarifas

  • TariffInterface: Estructura de tarifas con ajustes dinámicos (id, court_id, reservation_type_id, rrule, start_date, end_date, start_time, end_time, adjustment, description, is_active, created_at, updated_at)

  • TariffClassInterface: Estructura de tarifas de clase (id, court_type_id, reservation_type_id, single_price, total_price, max_capacity, created_at, updated_at, court_type_name, reservation_type_name)

Tipos y Estados

  • ReservationTypeInterface: Estructura de tipos de reserva (id, name, description)

  • ReservationStatusInterface: Estructura de estados de reserva (id, name, color, description). El campo color es un color hexadecimal para representación visual en calendarios

  • PaymentTypeInterface: Estructura de tipos de pago (id, name, description)

  • DocumentTypeInterface: Estructura de tipos de documento (id, name, description)

Utilidades

  • CalculatePriceResponseInterface: Respuesta del cálculo de precio con información detallada (court, date, checking, checkout, available, hours, price_per_hour, final_price, applied_adjustments)

  • PaginationInterface: Información de paginación (page, limit, total, totalPages)

  • PaginatedResponseInterface<T>: Respuesta paginada genérica con datos y paginación

  • DashboardStatsWithRecentInterface: Estadísticas del dashboard con datos recientes (clients, courts, reservations, payments, recent_reservations, recent_payments)

  • PushNotificationPayload: Payload para notificaciones push (clientId, title, body, url)

  • ErrorResponseInterface: Estructura de respuestas de error (error, message, stack)

Nota: Todas las interfaces están completamente tipadas y documentadas en el paquete common. Consulta la documentación del paquete para más detalles sobre cada interfaz. Las interfaces están alineadas con el esquema OpenAPI disponible en /api/api-docs.json.

Códigos de Estado HTTP

  • 200 OK: Petición exitosa

  • 201 Created: Recurso creado exitosamente

  • 204 No Content: Recurso eliminado exitosamente

  • 400 Bad Request: Error en la petición (validación fallida, datos mal formados)

  • Ejemplos: user_id debe ser un número válido, email inválido, se requiere user_id o email

  • 401 Unauthorized: No autenticado, token inválido, o API key inválida

    • Para Firebase Auth: Token de Firebase inválido o expirado

    • Para API Key: Header X-Auth faltante o valor de BOT_API_KEYS incorrecto

  • 403 Forbidden: No autorizado para realizar la acción

  • 404 Not Found: Recurso no encontrado

  • 409 Conflict: Conflicto de recursos (recurso ya existe o está en conflicto)

  • Ejemplos: Email ya en uso, teléfono ya en uso, usuario ya es cliente, usuario no puede convertirse en cliente

  • 500 Internal Server Error: Error del servidor

Nota sobre códigos de estado: * Los códigos 400 se usan para solicitudes mal formadas o datos inválidos * Los códigos 409 se usan específicamente para conflictos de recursos (email/teléfono en uso, usuario ya es cliente, etc.) * Esta separación permite a los clientes distinguir entre errores de validación y conflictos de recursos

Manejo de Errores

Todas las respuestas de error siguen el formato:

{
  "error": "Mensaje de error",
  "message": "Descripción detallada del error (opcional)"
}

Ejemplo:

{
  "error": "Datos incompletos",
  "message": "Los campos requeridos no están presentes: court_id, checking"
}

Nota: La interfaz TypeScript correspondiente es ErrorResponseInterface del paquete @cortex-ia-com-co/common.

Webhooks

El sistema expone webhooks para notificaciones de eventos:

POST /webhooks/payment

Webhook para recibir notificaciones de pagos del gateway.

Request Body:

{
  "transaction_id": "TXN-123456",
  "amount": 50000.00,
  "status": "completed",
  "gateway_response": "{\"status\":\"completed\"}",
  "date": "2024-01-10T08:00:00Z"
}

Nota: El webhook puede recibir status como string desde el gateway (ej: "completed", "pending", "failed"). El sistema convertirá internamente estos valores a boolean según corresponda.

El sistema procesará el webhook y: 1. Buscará si existe un pago con ese transaction_id 2. Si no existe, creará un pago huérfano (sin reservation_id ni enrollment_id) 3. Si existe y tiene reservation_id o enrollment_id, actualizará el estado del pago 4. Si existe sin reservation_id ni enrollment_id, lo mantendrá como huérfano hasta que se alinee manualmente

Response 200:

{
  "status": "processed",
  "payment_id": 1,
  "aligned": false
}

Campos de respuesta: * status: Estado del procesamiento del webhook ("processed", "error", etc.) * payment_id: ID del pago creado o actualizado * aligned: Indica si el pago está alineado con una reserva (true) o es huérfano (false)