Referencia de API

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 - Desarrollo: http://localhost:5000/api/api-docs * El esquema OpenAPI (JSON) está disponible en: https://app.unosportclub.com.co/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.

Autenticación

Todas las peticiones requieren autenticación mediante Firebase Auth. El token debe enviarse en el header:

Authorization: Bearer [firebase-id-token]

Endpoints

Reservas

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

Reservas (Cuenta de Usuario)

Los endpoints de reservas de cuenta permiten a los usuarios autenticados gestionar sus propias reservas con información detallada de pagos.

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

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"
}

Pagos (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 ser "huérfanos" (sin reservation_id) cuando se reciben desde webhooks antes de que se cree la reserva * El campo payment_type_id es opcional y puede ser null

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

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

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"
  },
  {
    "id": 2,
    "reservation_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 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,
  "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"
}

POST /admin/payments

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

Request Body:

{
  "reservation_id": 1,
  "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) * 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: * Si se proporciona reservation_id, debe existir en la tabla reservation * El transaction_id debe ser una cadena no vacía

Response 201:

{
  "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 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,
  "status": true,
  "description": "Pago actualizado"
}

Campos actualizables: * reservation_id: Asignar o cambiar la reservación asociada (integer, nullable) * 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 * Al menos un campo debe ser proporcionado para actualizar

Response 200:

{
  "id": 1,
  "reservation_id": 2,
  "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"
}

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)

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"
}

Disponibilidad

GET /availability

Consulta la disponibilidad de canchas en un rango de fechas.

Query Parameters: * court_id (opcional): Filtrar por cancha específica * from (requerido): Fecha desde (ISO 8601) * to (requerido): Fecha hasta (ISO 8601) * court_type_id (opcional): Filtrar por tipo de cancha

Response 200:

{
  "data": [
    {
      "court_id": 1,
      "court_name": "Cancha 1",
      "date": "2024-01-15",
      "available_slots": [
        {
          "start": "10:00:00",
          "end": "12:00:00",
          "available": true
        },
        {
          "start": "14:00:00",
          "end": "16:00:00",
          "available": false
        }
      ]
    }
  ]
}

Clientes

GET /clients

Obtiene la lista de clientes con información complementaria de la tabla user.

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

Response 200:

[
  {
    "id": 1,
    "user_id": "firebase-uid-123",
    "client_type_id": 1,
    "email": "cliente@example.com",
    "display_name": "Juan Pérez",
    "email_verified": true,
    "client_type_name": "Regular"
  }
]

GET /clients/:id

Obtiene un cliente específico por ID.

Response 200:

{
  "id": 1,
  "user_id": "firebase-uid-123",
  "client_type_id": 1,
  "email": "cliente@example.com",
  "display_name": "Juan Pérez",
  "email_verified": true,
  "client_type_name": "Regular"
}

POST /clients

Crea un nuevo cliente.

Request Body:

{
  "user_id": "firebase-uid-123",
  "client_type_id": 1
}

Response 201:

{
  "id": 1,
  "user_id": "firebase-uid-123",
  "client_type_id": 1,
  "email": "cliente@example.com",
  "display_name": "Juan Pérez",
  "email_verified": true,
  "client_type_name": "Regular"
}

PATCH /clients/:id

Actualiza un cliente existente (solo permite actualizar client_type_id).

Request Body:

{
  "client_type_id": 2
}

DELETE /clients/:id

Elimina un cliente.

Canchas

GET /courts

Obtiene la lista de canchas con información del tipo.

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

Response 200:

[
  {
    "id": 1,
    "court_type_id": 1,
    "name": "Cancha 1",
    "cost": 10000.00,
    "price": 50000.00,
    "court_type_name": "Fútbol"
  }
]

GET /courts/:id

Obtiene una cancha específica por ID.

POST /courts

Crea una nueva cancha.

Request Body:

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

PATCH /courts/:id

Actualiza una cancha existente.

DELETE /courts/:id

Elimina una cancha.

Dashboard

GET /dashboard/stats

Obtiene estadísticas generales del dashboard.

Response 200:

{
  "clients": {
    "total": 50,
    "verified": 45,
    "unverified": 5
  },
  "courts": {
    "total": 10
  },
  "reservations": {
    "total": 200,
    "today": 5,
    "this_week": 25,
    "this_month": 80
  },
  "payments": {
    "total": 150,
    "total_amount": 7500000.00,
    "completed_amount": 6500000.00,
    "pending": 10
  },
  "recent_reservations": [
    {
      "id": 1,
      "checking": "2024-01-15T10:00:00Z",
      "checkout": "2024-01-15T12:00:00Z",
      "created_at": "2024-01-10T08:00:00Z",
      "court_name": "Cancha 1",
      "client_email": "cliente@example.com",
      "client_name": "Juan Pérez"
    }
  ],
  "recent_payments": [
    {
      "id": 1,
      "amount": 50000.00,
      "transaction_id": "TXN-123456",
      "status": true,
      "date": "2024-01-10T08:00:00Z",
      "description": "Pago de reserva",
      "reservation_id": 1
    }
  ]
}

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)

  • 401 Unauthorized: No autenticado o token inválido

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

  • 404 Not Found: Recurso no encontrado

  • 500 Internal Server Error: Error del servidor

Manejo de Errores

Todas las respuestas de error siguen el formato:

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Descripción del error",
    "details": {}
  }
}

Ejemplo:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Los campos requeridos no están presentes",
    "details": {
      "fields": ["court_id", "checking"]
    }
  }
}

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) 3. Si existe y tiene reservation_id, actualizará el estado del pago 4. Si existe sin reservation_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)