Skip to content

Membresías

Endpoints para gestionar membresías de usuarios.

Los siguientes modelos de Prisma pertenecen al schema users:

model Membership {
id String @id @default(cuid())
userProfileId String @unique @map("user_profile_id")
type MembershipType
status MembershipStatus @default(ACTIVE)
startDate DateTime @map("start_date")
endDate DateTime @map("end_date")
price Float
autoRenew Boolean @default(false) @map("auto_renew")
suspendedAt DateTime? @map("suspended_at")
cancelledAt DateTime? @map("cancelled_at")
cancelReason String? @map("cancel_reason")
notes String?
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("memberships")
@@schema("users")
}
enum MembershipType {
BASIC
PLUS
PRO
DAY_PASS
@@schema("users")
}
enum MembershipStatus {
ACTIVE
INACTIVE
SUSPENDED
EXPIRED
@@schema("users")
}

Obtener todas las membresías. Admin Staff

  • URL: /api/v1/memberships
  • Método: GET
  • Autenticación Requerida:Admin Staff

Este endpoint obtiene membresías con filtrado avanzado:

  1. Filtros disponibles:

    • userProfileId: Filtrar por usuario
    • type: Tipo de membresía (MONTHLY, QUARTERLY, ANNUAL)
    • status: Estado (ACTIVE, EXPIRED, SUSPENDED, INACTIVE)
    • autoRenew: Filtrar por auto-renovación
    • expiresSoon: Solo membresías que expiran en 7 días
  2. Lógica de expiresSoon:

    • Calcula fecha 7 días adelante
    • Filtra membresías ACTIVE con endDate entre hoy y 7 días
  3. Incluye userProfile con nombre y email

  4. Paginación y ordenamiento dinámico por sortBy/sortOrder

  • status: Filtrar por status
  • type: Filtrar por tipo
  • userProfileId: Filtrar por usuario
  • expiresSoon: Solo membresías próximas a expirar
  • page: Número de página
  • limit: Resultados por página
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.memberships.$get({
query: {
status: 'ACTIVE'
}
})

Obtener la membresía activa del usuario actual.

  • URL: /api/v1/memberships/me/active
  • Método: GET
  • Autenticación Requerida:

Este endpoint obtiene la membresía activa del usuario:

  1. Busca membresía con:

    • userProfileId del usuario autenticado
    • status: ACTIVE
  2. Ordena por endDate descendente (más reciente primero)

  3. Lanza error si no tiene membresía activa (findFirstOrThrow)

{
"data": {
"id": "...",
"type": "MONTHLY",
"status": "ACTIVE",
"startDate": "2023-10-01T00:00:00Z",
"endDate": "2023-11-01T00:00:00Z",
"price": 50,
"autoRenew": true
}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.memberships.me.active.$get()

Obtener el historial de membresías para el usuario actual.

  • URL: /api/v1/memberships/me/history
  • Método: GET
  • Autenticación Requerida:

Este endpoint obtiene el historial de membresías:

  1. Busca todas las membresías del usuario
  2. Ordena por createdAt descendente
  3. Retorna lista completa sin paginación

Crear una nueva membresía para un usuario.

  • URL: /api/v1/memberships
  • Método: POST
  • Autenticación Requerida:Admin Staff

Este endpoint crea una nueva membresía:

  1. Verifica membresía activa existente:

    • Busca si el usuario ya tiene membresía ACTIVE
    • Si existe, lanza error 400
  2. Valida fechas:

    • endDate debe ser posterior a startDate
  3. Crea la membresía con:

    • status: ACTIVE por defecto
    • Todos los campos proporcionados
  4. Retorna membresía con userProfile incluido

{
"userId": "user_id",
"type": "MONTHLY",
"startDate": "2023-10-01T00:00:00Z",
"endDate": "2023-11-01T00:00:00Z",
"price": 50
}
  • 400 Bad Request: El usuario ya tiene una membresía activa
  • 400 Bad Request: La fecha de fin debe ser posterior a la fecha de inicio
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.memberships.$post({
json: {
userId: 'user_id',
type: 'MONTHLY',
startDate: '2023-10-01T00:00:00Z',
endDate: '2023-11-01T00:00:00Z',
price: 50
}
})

Renovar una membresía existente.

  • URL: /api/v1/memberships/:id/renew
  • Método: POST
  • Autenticación Requerida: Sí (Admin/Staff)

Este endpoint renueva una membresía existente:

  1. Obtiene membresía actual

  2. Calcula nuevas fechas:

    • newStartDate: Mayor entre endDate actual y hoy
    • newEndDate: newStartDate + duration (en días)
  3. Actualiza membresía con:

    • Nuevas fechas
    • Precio (proporcionado o existente)
    • autoRenew (proporcionado o existente)
    • status: ACTIVE
  4. Retorna membresía actualizada

{
"duration": 30,
"price": 50,
"autoRenew": true
}

Cancelar una membresía.

  • URL: /api/v1/memberships/:id/cancel
  • Método: POST
  • Autenticación Requerida:

Este endpoint cancela una membresía:

  1. Verifica estado: Solo se pueden cancelar membresías ACTIVE

  2. Registra cancelación:

    • cancelledAt: Fecha actual
    • autoRenew: false
    • cancelReason: Motivo proporcionado
  3. Si immediate es true:

    • status: INACTIVE
    • endDate: Fecha actual
  4. Si immediate es false:

    • Mantiene status ACTIVE
    • Expira naturalmente en endDate
{
"reason": "Moving away",
"immediate": false
}
  • 400 Bad Request: Solo se pueden cancelar membresías activas
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.memberships[':id'].cancel.$post({
param: { id: 'membership_id' },
json: {
reason: 'Moving away',
immediate: false
}
})

Suspender temporalmente una membresía.

  • URL: /api/v1/memberships/:id/suspend
  • Método: POST
  • Autenticación Requerida: Sí (Admin/Staff)

Este endpoint suspende temporalmente una membresía:

  1. Verifica estado: Solo membresías ACTIVE pueden suspenderse
  2. Actualiza membresía:
    • status: SUSPENDED
    • suspendedAt: Fecha actual
    • notes: Motivo de suspensión
{
"reason": "Payment issue"
}

Reactivar una membresía suspendida.

  • URL: /api/v1/memberships/:id/reactivate
  • Método: POST
  • Autenticación Requerida: Sí (Admin/Staff)

Este endpoint reactiva una membresía suspendida:

  1. Verifica estado: Solo membresías SUSPENDED pueden reactivarse
  2. Verifica expiración: Si endDate < hoy, no puede reactivarse (debe renovarse)
  3. Actualiza: status: ACTIVE
  • 400 Bad Request: Solo se pueden reactivar membresías suspendidas
  • 400 Bad Request: La membresía ha expirado, debe renovarla

Obtener estadísticas de membresías (Admin).

  • URL: /api/v1/memberships/stats
  • Método: GET
  • Autenticación Requerida:Admin

Este endpoint calcula estadísticas de membresías:

  1. Estadísticas generales (transacción):

    • Total de membresías
    • Conteo por estado: ACTIVE, EXPIRED, SUSPENDED, INACTIVE
  2. Por tipo: Agrupa y cuenta por tipo de membresía

  3. Ingresos: Suma total de precios

  4. Auto-renovación:

    • Conteo de membresías con autoRenew activo
    • Porcentaje sobre el total
  • startDate: Filtrar estadísticas desde fecha
  • endDate: Filtrar estadísticas hasta fecha
{
"data": {
"total": 500,
"byStatus": {
"active": 350,
"expired": 100,
"suspended": 30,
"inactive": 20
},
"byType": [
{ "type": "MONTHLY", "count": 200 },
{ "type": "ANNUAL", "count": 150 }
],
"revenue": { "total": 25000 },
"autoRenew": { "count": 280, "percentage": 56 }
}
}

Obtener membresías próximas a expirar (Admin/Staff).

  • URL: /api/v1/memberships/expiring
  • Método: GET
  • Autenticación Requerida: Sí (Admin/Staff)

Este endpoint obtiene membresías próximas a expirar:

  1. Calcula fecha límite: Hoy + días especificados (default: 7)
  2. Filtra membresías:
    • status: ACTIVE
    • endDate entre hoy y fecha límite
  3. Incluye userProfile con nombre y email
  4. Ordena por endDate ascendente (más urgentes primero)
  • days: Días hasta expiración (por defecto: 7)