✅ FASE 4 COMPLETADA: Pagos y Monetización con MercadoPago
Implementados 4 módulos con agent swarm: 1. MERCADOPAGO INTEGRADO - SDK oficial de MercadoPago - Crear preferencias de pago - Webhooks para notificaciones - Reembolsos y cancelaciones - Estados: PENDING, PROCESSING, COMPLETED, REFUNDED 2. SISTEMA DE BONOS Y PACKS - Pack 5, Pack 10, Pack Mensual - Compra online con MP - Uso FIFO automático - Control de expiración - Aplicación en reservas 3. SUSCRIPCIONES/MEMBRESÍAS - Planes: Básico, Premium, Anual VIP - Beneficios: descuentos, reservas gratis, prioridad - Cobro recurrente vía MP - Estados: ACTIVE, PAUSED, CANCELLED - Aplicación automática en reservas 4. CLASES CON PROFESORES - Registro de coaches con verificación - Tipos: Individual, Grupal, Clínica - Horarios y disponibilidad - Reservas con pago integrado - Sistema de reseñas Endpoints nuevos: - /payments/* - Pagos MercadoPago - /bonus-packs/*, /bonuses/* - Bonos - /subscription-plans/*, /subscriptions/* - Suscripciones - /coaches/* - Profesores - /classes/*, /class-enrollments/* - Clases Variables de entorno: - MERCADOPAGO_ACCESS_TOKEN - MERCADOPAGO_PUBLIC_KEY - MERCADOPAGO_WEBHOOK_SECRET Datos de prueba: - 3 Bonus Packs - 3 Planes de suscripción - 1 Coach verificado (admin) - 3 Clases disponibles
This commit is contained in:
@@ -76,6 +76,20 @@ model User {
|
||||
tournamentsCreated Tournament[] @relation("TournamentsCreated")
|
||||
tournamentParticipations TournamentParticipant[]
|
||||
|
||||
// Bonos (Fase 4.2)
|
||||
userBonuses UserBonus[]
|
||||
|
||||
// Pagos (Fase 4.1)
|
||||
payments Payment[]
|
||||
|
||||
// Suscripciones (Fase 4.3)
|
||||
subscriptions UserSubscription[]
|
||||
|
||||
// Clases con profesores (Fase 4.4)
|
||||
coach Coach?
|
||||
studentEnrollments StudentEnrollment[]
|
||||
coachReviews CoachReview[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@ -132,6 +146,7 @@ model Court {
|
||||
recurringBookings RecurringBooking[]
|
||||
leagueMatches LeagueMatch[]
|
||||
tournamentMatches TournamentMatch[]
|
||||
classBookings ClassBooking[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -193,6 +208,9 @@ model Booking {
|
||||
recurringBooking RecurringBooking? @relation(fields: [recurringBookingId], references: [id])
|
||||
recurringBookingId String?
|
||||
|
||||
// Uso de bonos
|
||||
bonusUsages BonusUsage[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -757,3 +775,449 @@ model LeagueStanding {
|
||||
@@index([points])
|
||||
@@map("league_standings")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Modelo de Pagos (Fase 4.1)
|
||||
// ============================================
|
||||
|
||||
model Payment {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Usuario que realiza el pago
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
|
||||
// Tipo de pago: BOOKING, TOURNAMENT, BONUS, SUBSCRIPTION, CLASS
|
||||
type String
|
||||
|
||||
// ID de la entidad relacionada (booking, tournament, etc.)
|
||||
referenceId String
|
||||
|
||||
// Monto en centavos (para evitar decimales)
|
||||
amount Int
|
||||
|
||||
// Moneda (ARS, MXN, etc.)
|
||||
currency String @default("ARS")
|
||||
|
||||
// Proveedor de pago
|
||||
provider String @default("MERCADOPAGO")
|
||||
|
||||
// IDs de MercadoPago
|
||||
providerPaymentId String? // ID del pago en MP (cuando se confirma)
|
||||
providerPreferenceId String @unique // ID de la preferencia MP
|
||||
|
||||
// Estado del pago
|
||||
status String @default("PENDING") // PENDING, PROCESSING, COMPLETED, FAILED, REFUNDED, CANCELLED
|
||||
|
||||
// Información del método de pago
|
||||
paymentMethod String?
|
||||
installments Int? // Cantidad de cuotas
|
||||
|
||||
// Metadata adicional (JSON)
|
||||
metadata String?
|
||||
|
||||
// Fechas
|
||||
paidAt DateTime?
|
||||
refundedAt DateTime?
|
||||
refundAmount Int? // Monto reembolsado en centavos
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([userId])
|
||||
@@index([status])
|
||||
@@index([type, referenceId])
|
||||
@@index([providerPaymentId])
|
||||
@@index([providerPreferenceId])
|
||||
@@index([createdAt])
|
||||
@@map("payments")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Modelos de Sistema de Bonos (Fase 4.2)
|
||||
// ============================================
|
||||
|
||||
// Modelo de Pack de Bonos (tipos de bonos disponibles)
|
||||
model BonusPack {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
description String?
|
||||
|
||||
// Configuración del bono
|
||||
numberOfBookings Int // Cantidad de reservas incluidas
|
||||
price Int // Precio del bono en centavos
|
||||
validityDays Int // Días de validez desde la compra
|
||||
|
||||
// Estado
|
||||
isActive Boolean @default(true)
|
||||
|
||||
// Relaciones
|
||||
userBonuses UserBonus[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([isActive])
|
||||
@@map("bonus_packs")
|
||||
}
|
||||
|
||||
// Modelo de Bono de Usuario (bonos comprados)
|
||||
model UserBonus {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Relaciones
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
bonusPack BonusPack @relation(fields: [bonusPackId], references: [id])
|
||||
bonusPackId String
|
||||
|
||||
// Uso del bono
|
||||
totalBookings Int
|
||||
usedBookings Int @default(0)
|
||||
remainingBookings Int
|
||||
|
||||
// Fechas
|
||||
purchaseDate DateTime
|
||||
expirationDate DateTime
|
||||
|
||||
// Estado: ACTIVE, EXPIRED, DEPLETED
|
||||
status String @default("ACTIVE")
|
||||
|
||||
// Referencia al pago
|
||||
paymentId String?
|
||||
|
||||
// Relaciones
|
||||
usages BonusUsage[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([userId])
|
||||
@@index([status])
|
||||
@@index([expirationDate])
|
||||
@@index([userId, status])
|
||||
@@map("user_bonuses")
|
||||
}
|
||||
|
||||
// Modelo de Uso de Bono (registro de usos)
|
||||
model BonusUsage {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Relaciones
|
||||
userBonus UserBonus @relation(fields: [userBonusId], references: [id], onDelete: Cascade)
|
||||
userBonusId String
|
||||
|
||||
// Reserva asociada
|
||||
booking Booking @relation(fields: [bookingId], references: [id])
|
||||
bookingId String
|
||||
|
||||
// Fecha de uso
|
||||
usedAt DateTime @default(now())
|
||||
|
||||
@@unique([bookingId])
|
||||
@@index([userBonusId])
|
||||
@@index([usedAt])
|
||||
@@map("bonus_usages")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Modelos de Sistema de Suscripciones (Fase 4.3)
|
||||
// ============================================
|
||||
|
||||
// Modelo de Plan de Suscripción (planes disponibles)
|
||||
model SubscriptionPlan {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
description String?
|
||||
|
||||
// Tipo de plan
|
||||
type String // MONTHLY, QUARTERLY, YEARLY
|
||||
|
||||
// Precio en centavos
|
||||
price Int
|
||||
|
||||
// Características (JSON array de strings)
|
||||
features String? // Ej: ["Reservas ilimitadas", "Prioridad en reservas"]
|
||||
|
||||
// Beneficios del plan (almacenados como JSON)
|
||||
// discountPercentage: porcentaje de descuento en reservas
|
||||
// freeBookingsPerMonth: cantidad de reservas gratis por mes
|
||||
// priorityBooking: prioridad en reservas
|
||||
// tournamentDiscount: descuento en torneos
|
||||
benefits String // JSON: { discountPercentage, freeBookingsPerMonth, priorityBooking, tournamentDiscount }
|
||||
|
||||
// ID del plan en MercadoPago
|
||||
mercadoPagoPlanId String?
|
||||
|
||||
// Estado
|
||||
isActive Boolean @default(true)
|
||||
|
||||
// Relaciones
|
||||
subscriptions UserSubscription[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([type])
|
||||
@@index([isActive])
|
||||
@@map("subscription_plans")
|
||||
}
|
||||
|
||||
// Modelo de Suscripción de Usuario
|
||||
model UserSubscription {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Usuario
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
|
||||
// Plan
|
||||
plan SubscriptionPlan @relation(fields: [planId], references: [id])
|
||||
planId String
|
||||
|
||||
// Estado: PENDING, ACTIVE, PAUSED, CANCELLED, EXPIRED
|
||||
status String @default("PENDING")
|
||||
|
||||
// Fechas de suscripción
|
||||
startDate DateTime?
|
||||
endDate DateTime?
|
||||
|
||||
// Período actual
|
||||
currentPeriodStart DateTime?
|
||||
currentPeriodEnd DateTime?
|
||||
|
||||
// Cancelar al final del período
|
||||
cancelAtPeriodEnd Boolean @default(false)
|
||||
|
||||
// Referencia a MercadoPago
|
||||
mercadoPagoSubscriptionId String?
|
||||
|
||||
// Método de pago vinculado
|
||||
paymentMethodId String?
|
||||
|
||||
// Fechas de pagos
|
||||
lastPaymentDate DateTime?
|
||||
nextPaymentDate DateTime?
|
||||
|
||||
// Contador de reservas gratis usadas en el período actual
|
||||
freeBookingsUsed Int @default(0)
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([userId, status])
|
||||
@@index([userId])
|
||||
@@index([planId])
|
||||
@@index([status])
|
||||
@@index([mercadoPagoSubscriptionId])
|
||||
@@map("user_subscriptions")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Modelos de Clases con Profesores (Fase 4.4)
|
||||
// ============================================
|
||||
|
||||
// Modelo de Profesor (Coach)
|
||||
model Coach {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Relación con usuario
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String @unique
|
||||
|
||||
// Perfil profesional
|
||||
bio String?
|
||||
specialties String? // JSON array de especialidades
|
||||
certifications String? // JSON array de certificaciones
|
||||
yearsExperience Int @default(0)
|
||||
hourlyRate Int @default(0) // en centavos
|
||||
photoUrl String?
|
||||
|
||||
// Estado
|
||||
isActive Boolean @default(true)
|
||||
isVerified Boolean @default(false)
|
||||
|
||||
// Calificaciones
|
||||
rating Float?
|
||||
reviewCount Int @default(0)
|
||||
|
||||
// Relaciones
|
||||
availabilities CoachAvailability[]
|
||||
classes Class[]
|
||||
classBookings ClassBooking[]
|
||||
coachReviews CoachReview[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([isActive])
|
||||
@@index([isVerified])
|
||||
@@index([userId])
|
||||
@@map("coaches")
|
||||
}
|
||||
|
||||
// Modelo de Disponibilidad del Coach
|
||||
model CoachAvailability {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Relación con coach
|
||||
coach Coach @relation(fields: [coachId], references: [id], onDelete: Cascade)
|
||||
coachId String
|
||||
|
||||
// Día de la semana (0=Domingo, 1=Lunes, ..., 6=Sábado)
|
||||
dayOfWeek Int
|
||||
|
||||
// Horario
|
||||
startTime String
|
||||
endTime String
|
||||
|
||||
// Estado
|
||||
isAvailable Boolean @default(true)
|
||||
|
||||
@@index([coachId])
|
||||
@@index([coachId, dayOfWeek])
|
||||
@@index([dayOfWeek])
|
||||
@@map("coach_availabilities")
|
||||
}
|
||||
|
||||
// Modelo de Clase (programa/tipo de clase)
|
||||
model Class {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Relación con coach
|
||||
coach Coach @relation(fields: [coachId], references: [id], onDelete: Cascade)
|
||||
coachId String
|
||||
|
||||
// Información de la clase
|
||||
title String
|
||||
description String?
|
||||
|
||||
// Tipo: INDIVIDUAL, GROUP, CLINIC
|
||||
type String @default("INDIVIDUAL")
|
||||
|
||||
// Configuración
|
||||
maxStudents Int @default(1) // Máximo de alumnos
|
||||
price Int @default(0) // Precio por persona en centavos
|
||||
duration Int @default(60) // Duración en minutos
|
||||
|
||||
// Nivel mínimo requerido
|
||||
levelRequired String?
|
||||
|
||||
// Estado
|
||||
isActive Boolean @default(true)
|
||||
|
||||
// Relaciones
|
||||
sessions ClassBooking[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([coachId])
|
||||
@@index([type])
|
||||
@@index([isActive])
|
||||
@@map("classes")
|
||||
}
|
||||
|
||||
// Modelo de Sesión de Clase (instancia específica de una clase)
|
||||
model ClassBooking {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Relaciones
|
||||
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
||||
classId String
|
||||
|
||||
coach Coach @relation(fields: [coachId], references: [id])
|
||||
coachId String
|
||||
|
||||
court Court? @relation(fields: [courtId], references: [id], onDelete: SetNull)
|
||||
courtId String?
|
||||
|
||||
// Fecha y hora
|
||||
date DateTime
|
||||
startTime String
|
||||
|
||||
// Estudiantes (JSON array de userIds)
|
||||
students String @default("[]")
|
||||
|
||||
// Cupo
|
||||
maxStudents Int @default(1)
|
||||
enrolledStudents Int @default(0)
|
||||
|
||||
// Estado: AVAILABLE, FULL, COMPLETED, CANCELLED
|
||||
status String @default("AVAILABLE")
|
||||
|
||||
// Precio
|
||||
price Int @default(0)
|
||||
|
||||
// Pago
|
||||
paymentId String?
|
||||
|
||||
// Relaciones
|
||||
enrollments StudentEnrollment[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([classId])
|
||||
@@index([coachId])
|
||||
@@index([courtId])
|
||||
@@index([date])
|
||||
@@index([status])
|
||||
@@map("class_bookings")
|
||||
}
|
||||
|
||||
// Modelo de Inscripción de Estudiante
|
||||
model StudentEnrollment {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Relaciones
|
||||
classBooking ClassBooking @relation(fields: [classBookingId], references: [id], onDelete: Cascade)
|
||||
classBookingId String
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
|
||||
// Referencia al pago
|
||||
paymentId String?
|
||||
|
||||
// Estado: PENDING, CONFIRMED, CANCELLED, ATTENDED
|
||||
status String @default("PENDING")
|
||||
|
||||
// Timestamps
|
||||
enrolledAt DateTime @default(now())
|
||||
cancelledAt DateTime?
|
||||
|
||||
@@unique([classBookingId, userId])
|
||||
@@index([userId])
|
||||
@@index([status])
|
||||
@@index([classBookingId])
|
||||
@@map("student_enrollments")
|
||||
}
|
||||
|
||||
// Modelo de Reseña de Coach
|
||||
model CoachReview {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Relaciones
|
||||
coach Coach @relation(fields: [coachId], references: [id], onDelete: Cascade)
|
||||
coachId String
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
|
||||
// Calificación (1-5)
|
||||
rating Int
|
||||
comment String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([coachId, userId])
|
||||
@@index([coachId])
|
||||
@@index([userId])
|
||||
@@index([rating])
|
||||
@@map("coach_reviews")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user