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:
-
Portal Principal:
-
Portal para usuarios finales
-
Panel de Administración:
-
Panel operativo para administradores y operadores
-
Super Administrador:
-
Consola de control para super usuarios (sudo)
Desarrollo Local
Para desarrollo local, la API está disponible en:
-
http://localhost:5000/api- Emulador de desarrollo -
http://localhost:5005/api- Emulador de desarrollo alternativo -
http://localhost:5006/api- Emulador de desarrollo alternativo
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"
}
}
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"
}
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"
}
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"
}
]
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)