Created
May 3, 2025 21:50
-
-
Save SebastianCastilloDev/b91df73c7dc4b9814525a92e445c7fda to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Quiero que hagas una investigacion a fondo de la implementacion actual | |
Lo que se busca es el siguiente modelo de negocio. Por mi parte es crear un conjunto de talonarios digitales, de diferentes tipos, la idea es llegar a unos 100, pero actualmente tengo 3 (budgets, receipts y report-maquinaria), Para ello he construido todo un conjunto de caracteristicas que son necesarios para poder administrar la emision de estos talonarios, entre ellas, clientes, companies, coupons, productos, upload-image, users, seeding a la base de datos y en este momento me encuenctro creando la plataforma de pagos con mercado pago, la cual ya tiene la implementacion basica. | |
Lo que busco ahora, es poder unificar estos pagos, con un plan de suscripciones basado en el uso, es decir por ejemplo $5000 / 100 talonarios, o lo que sea, pero no es una suscripcion mensual. | |
oara ello he creado otras estructuras como transactions, subscriptions, plans que permitiran administrar este modelo de negocio, pero aun no están listas, e incluso se puede simplificar si es necesario, la idea es tener entidades con nombres apropiados que se encarguen de la tarea. | |
ChatGPT dijo: | |
Gracias por la explicación detallada. Para poder ayudarte a fondo con una investigación y propuesta de diseño del modelo de negocio basado en suscripciones por uso (por ejemplo, $5000 por 100 talonarios), ¿podrías precisar lo siguiente? | |
¿Quieres mantener también el plan "freemium" gratuito o eliminarlo? | |
¿Los talonarios son intercambiables (es decir, un crédito sirve para cualquier tipo) o cada tipo de talonario consume créditos distintos? | |
¿Se espera que los usuarios compren múltiples "packs" de créditos simultáneamente o solo uno a la vez? | |
¿Deseas mantener la posibilidad de planes (como BASIC) además de los packs o se migra completamente a un sistema basado en créditos? | |
¿Tienes alguna lógica de expiración para los créditos (por tiempo o por condiciones)? | |
Con estas respuestas podré ayudarte con una propuesta de modelo de datos, flujos de compra y consumo, y ajustes necesarios al backend. | |
Dijiste: | |
backend-tree.txt | |
Documento | |
backend.txt | |
Documento | |
¿Quieres mantener también el plan "freemium" gratuito o eliminarlo? | |
Si claro, ese es el plan por defecto, pero la idea es que pueda hacer un upgrade y tener un plan de pago. | |
¿Los talonarios son intercambiables (es decir, un crédito sirve para cualquier tipo) o cada tipo de talonario consume créditos distintos? | |
cada talonario consume un credito. | |
¿Se espera que los usuarios compren múltiples "packs" de créditos simultáneamente o solo uno a la vez? | |
1 a la vez, por el momento. | |
¿Deseas mantener la posibilidad de planes (como BASIC) además de los packs o se migra completamente a un sistema basado en créditos? | |
hay que redefinir los planes para que se ajusten al modelo actual, aunque ya estan bastante completos, pero no me interesan los enfoques temporales para cobrar a usuarios. | |
¿Tienes alguna lógica de expiración para los créditos (por tiempo o por condiciones)? | |
No, no hay logica de expiracion, la idea es que puedan usar sus talonarios y emitir hasta que se le acaben sus creditos.... | |
La idea es tener una logica simple pero flexible que permita ejecutar el modelo antes descrito. | |
Revela si existen inconsistencias graves en el diseño ya implementado o si la infraestructura actual es adecuada y solo debemos implementarla bien. | |
ChatGPT dijo: | |
Gracias. Voy a analizar si existen inconsistencias graves en el modelo actual de monetización por créditos para talonarios y si la infraestructura actual basada en NestJS y módulos como plans, subscriptions, y mercado-pago es adecuada o necesita ajustes significativos. | |
Te avisaré cuando tenga un diagnóstico claro del diseño y recomendaciones para su evolución. | |
Research completed in 9m· 2 fuentes | |
Evaluación de Consistencia del Modelo de Créditos y Planes en Backend NestJS | |
Análisis de la Arquitectura para el Modelo de Créditos | |
Introducción | |
El siguiente informe analiza la arquitectura backend actual (basada en NestJS) para determinar su adecuación e identificar posibles inconsistencias al implementar un nuevo modelo de monetización por créditos para la emisión de talonarios (documentos digitales). El modelo propuesto por el usuario se basa en los siguientes aspectos clave: | |
Plan freemium predeterminado: Todos los usuarios comienzan en un plan gratuito (freemium) por defecto. | |
Consumo por talonario: La creación/emisión de cada talonario debe costar 1 crédito de la cuenta del usuario. | |
Compra de paquetes de créditos: Los usuarios pueden comprar paquetes de créditos predefinidos (de a uno por vez, no múltiples paquetes simultáneos). | |
Planes sin periodicidad: Los planes de suscripción mensuales deben eliminarse o redefinirse; no se manejarán cuotas temporales (no habrá renovaciones mensuales, solo el sistema de créditos). | |
Sin expiración de créditos: Los créditos adquiridos no expiran con el tiempo. | |
A continuación se revisan los módulos, entidades y flujos relevantes (planes, suscripciones, Mercado Pago, etc.), evaluando su consistencia con este modelo. Se señalan posibles limitaciones de la implementación actual y se proponen mejoras o refactorizaciones específicas para alinear el sistema con la monetización por créditos. | |
Estructura de Módulos: Plans, Subscriptions y Mercado Pago | |
La aplicación separa la lógica en módulos bien definidos, lo cual en principio es positivo para la mantenibilidad. Los módulos más relevantes son: | |
Módulo plans: Contiene la lógica relacionada con los planes y paquetes. Incluye entidades para Planes y Packs, controladores y servicios para gestionar planes (e.g. consulta de planes disponibles) y packs de créditos. Esta división es apropiada, ya que distingue entre la definición estática de planes/paquetes y el uso por parte de usuarios. Sin embargo, habría que adaptar su funcionalidad: actualmente está enfocada en planes con límites mensuales, pero deberá centrarse en configurar el plan freemium y los paquetes de créditos. | |
Módulo subscriptions: Abarca las suscripciones de usuarios y créditos. Define la entidad de Subscription (suscripción a un plan) y la entidad UserCredits (saldo de créditos del usuario), junto con servicios/controladores para manejarlos. Aquí se nota un posible desajuste con el nuevo modelo: la presencia de suscripciones temporales podría volverse obsoleta. Actualmente, este módulo provee métodos para obtener el plan activo de un usuario y para consumir o añadir créditos a su cuenta. Dado que en el modelo de créditos ya no habrá “suscripciones” pagas en el sentido tradicional, este módulo podría reorientarse únicamente a la gestión de créditos del usuario. Mantener la estructura modular es válido, pero quizás sería conveniente renombrar o depurar este módulo (por ejemplo, separando la parte de créditos de la lógica de suscripción mensual, para evitar confusiones). | |
Módulo mercado-pago: Encapsula la integración con la pasarela de pago Mercado Pago. Gestiona la creación de preferencias de pago, webhooks de notificación y registro de transacciones en la base de datos. Actualmente, su implementación está enfocada en vender un único producto (el Plan Básico mensual) y registrar la transacción correspondiente. Esto requerirá modificaciones para soportar la compra de paquetes de créditos en lugar de una suscripción fija. La estructura modular en sí es adecuada – aísla las preocupaciones de pago – pero el contenido del módulo debe generalizarse para manejar múltiples ítems (diferentes packs de créditos) en vez de un solo plan. | |
En términos generales, la separación de responsabilidades es correcta. No se observan conflictos mayores entre módulos, aunque la nomenclatura “plans” vs “subscriptions” podría inducir a error tras migrar a créditos. Se sugiere evaluar la necesidad de la lógica de subscriptions (planes temporales) una vez implementado el nuevo esquema. Podría simplificarse el sistema eliminando la dependencia de suscripciones periódicas y concentrando todo en “planes” (solo para el plan freemium) y “créditos/packs” para lo demás. | |
Entidades: Plan, Pack, UserCredits y Subscription | |
A continuación se examinan las entidades principales para verificar si reflejan el modelo por créditos o si requieren ajustes: | |
Entidad Plan: Representa los distintos planes del sistema y define sus límites y características. En la implementación actual, existen al menos dos planes configurados: un plan FREEMIUM gratuito y un plan BASIC (básico) de pago | |
file-uqkcvgnfo2kaq8twdq6d5n | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Cada instancia de Plan contiene campos como maxTalonarios, maxCompanies, maxClients, allowsReports, etc., que establecen límites de uso y funcionalidades según el plan, así como un precio asociado (priceInCents). Esta definición está pensada para modelos de suscripción mensual (por ejemplo, el plan Básico permite hasta 100 talonarios y tiene un costo mensual de 4.990 en moneda local | |
file-uqkcvgnfo2kaq8twdq6d5n | |
). Bajo el nuevo esquema de créditos, el plan Freemium seguiría existiendo como el plan por defecto para todos los usuarios. Sin embargo, la idea de múltiples planes pagos desaparece (ya no se ofrecería el plan Básico mensual, puesto que la monetización será por uso de créditos). En consecuencia, varios atributos de Plan podrían perder relevancia o requerir reinterpretación: por ejemplo, priceInCents ya no se usaría (no se venderán planes mensuales directamente) y los límites fijos (maxTalonarios, maxCompanies, etc.) dejarán de ser rígidos una vez que el usuario pueda excederlos mediante créditos. Diagnóstico: La entidad Plan es suficiente para representar el plan gratuito (Freemium) y posiblemente cualquier clasificación de usuario que se quiera mantener (por ejemplo, eventualmente podría haber un plan "Enterprise" sin límite, pero en principio no aplica dado que no habrá planes temporales). No obstante, la presencia del plan Básico en la base de datos y sus campos ligados a cuotas mensuales pueden generar confusión. Se recomienda inactivar o eliminar los planes pagos preexistentes y ajustar la configuración del plan Freemium. Por ejemplo, podría establecerse que el plan Freemium tenga límites base (como 10 talonarios gratuitos históricos) pero permitir sobrepasarlos comprando créditos. Alternativamente, esos campos de límite podrían ignorarse por completo bajo el modelo de créditos (e incluso ponerse en un valor muy alto o ilimitado para que no interfieran). En cualquier caso, habría que asegurarse de que Plan.name = FREEMIUM siga siendo el predeterminado para todos los usuarios nuevos y existentes. | |
Entidad Pack: Modela los paquetes de créditos disponibles para la venta. Sus campos (name, description, credits, priceInCents, etc.) parecen adecuados para representar diferentes ofertas de créditos | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Por ejemplo, un Pack 1 podría tener credits: 10 y priceInCents correspondiente al valor en pesos. Esta entidad encaja perfectamente con el modelo de monetización solicitado, ya que define qué compra el usuario cuando paga. Diagnóstico: La entidad Pack es consistente con el modelo. Permite definir paquetes activos de varios tamaños y precios, y su estructura es sencilla. No se evidencian inconsistencias graves aquí. Solo será necesario asegurar que haya una lista de packs configurados (vía seeders o administración) y que la lógica de compra use esta entidad. Sugerencia: Verificar que el diseño solo permita comprar un pack a la vez. Dado que cada transacción de Mercado Pago está pensada para un único item, esto ya se cumple (un Pack = un item). Para mayor robustez, el sistema podría impedir que un usuario inicie una nueva compra si ya tiene una transacción pendiente sin completar, evitando duplicaciones de pedidos de créditos. | |
Entidad UserCredits: Registra el saldo de créditos de un usuario y métricas relacionadas. Contiene campos availableCredits (créditos disponibles actualmente), totalCreditsPurchased (acumulado histórico comprado) y creditsUsed (total de créditos gastados), entre otros metadatos | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Esta entidad es fundamental para llevar el control granular del modelo de créditos por usuario. En la implementación actual, la lógica asociada (en CreditsService) inicializa un registro UserCredits por usuario si no existe y actualiza estos contadores al agregar o consumir créditos. Diagnóstico: La entidad UserCredits refleja correctamente el modelo de consumo por créditos. Permite saber cuántos créditos tiene el usuario para emitir talonarios y registra el historial agregado de compras/uso. Un posible detalle a revisar es la relación con User: actualmente está mapeada con @ManyToOne(() => User) | |
file-uqkcvgnfo2kaq8twdq6d5n | |
, lo que indica que podrían existir múltiples registros de créditos por usuario (aunque en la práctica seguramente se use solo uno por usuario). Podría considerarse hacer esta relación uno a uno para reforzar la unicidad, o garantizar a nivel de lógica que solo exista un UserCredits activo por usuario. Fuera de esto, la estructura es sólida. Sugerencia: utilizar métodos transaccionales al actualizar los créditos para evitar inconsistencias (por ejemplo, al consumir créditos concurridamente). El servicio actual ya valida y actualiza de forma atómica en la base de datos usando TypeORM, lo cual es aceptable. | |
Entidad Subscription: Representa una suscripción de un usuario a un plan específico. Vincula al usuario con un plan (ManyToOne plan) e incluye fechas de inicio y fin de la suscripción, así como un indicador de actividad | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Esta entidad en la arquitectura actual soporta el modelo de planes temporales (por ejemplo, un registro indicando que el usuario X está suscrito al plan Básico desde el 01-01-2024 hasta 01-02-2024). De hecho, los campos startDate y endDate evidencian un enfoque basado en periodos de vigencia | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Además, métodos en el servicio de suscripciones buscan la suscripción activa de un usuario para determinar su plan vigente | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Diagnóstico: En un modelo puramente por créditos, esta entidad pierde casi toda su relevancia. Si ya no se van a ofrecer planes de pago mensuales, no habrá “suscripciones” activas que gestionar (todos los usuarios permanecerían siempre en el plan Freemium, sin necesidad de registros temporales). Podría argumentarse que Subscription aún tendría uso para marcar que un usuario está en Freemium (pero esto ya se deduce por default) o para conservar histórico de antiguos planes. Sin embargo, mantener lógica de suscripciones activas con fechas podría introducir complejidad innecesaria y posibles conflictos con el nuevo sistema. Por ejemplo, si quedara alguna verificación que espere una suscripción activa para permitir algo, podría bloquear indebidamente a usuarios que ahora solo usan créditos. Se sugiere eliminar o al menos deshabilitar la lógica de Subscription en la implementación: no crear registros de suscripción nuevos (salvo quizás uno por defecto al registro del usuario, asociado al plan Freemium indefinidamente), y limpiar referencias a cambios de plan. La entidad podría quedar para historial o futuras expansiones, pero operativamente, el sistema debería funcionar sin depender de Subscription (salvo para obtener el plan Freemium por defecto, lo cual igualmente puede hacerse consultando el Plan directamente). | |
En resumen, las entidades Pack y UserCredits ya se ajustan bien al esquema de créditos. La entidad Plan puede reutilizarse para mantener la configuración del plan gratuito (y potenciales limitaciones gratuitas), pero debe ajustarse su uso: dejar de lado los planes pagos mensuales. La entidad Subscription parece ser lógica heredada del modelo anterior y convendría retirarla del flujo principal para evitar inconsistencias con el nuevo enfoque. | |
Flujo de Compra con Mercado Pago | |
El flujo actual para compras a través de Mercado Pago está implementado pero orientado a la venta de un plan de suscripción fijo (el Plan Básico). Al analizar el código del módulo mercado-pago, se observa lo siguiente: | |
Creación de preferencia de pago: existe un endpoint POST /mercado-pago/create-preference que invoca a MercadoPagoService.createPreference(). Este método, en su versión actual, construye una preferencia con un ítem fijo correspondiente al Plan Básico | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. En concreto, se define un solo item con id: 'plan-basic', título “Plan Básico – Hasta 100 talonarios”, cantidad 1 y precio unitario $5000 CLP | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Los URLs de retorno (éxito, fallo, pendiente) se configuran desde variables de entorno, y se usa el userId como external_reference. Finalmente, se guarda en la base de datos una transacción (entidad MercadoPagoTransaction) con estado PENDIENTE y el detalle de este pago. Implicación: Actualmente el sistema asume que cada pago es para actualizar al usuario al plan Básico (100 talonarios mensuales). Esto no coincide con el nuevo modelo, donde el pago debe corresponder a la compra de un paquete de créditos. Es necesario refactorizar la creación de preferencias para que los ítems reflejen los packs de créditos seleccionados por el usuario. En la práctica, esto significa: en lugar de armar siempre el item plan-basic de $5000, el backend debería recibir una referencia al pack de créditos que se desea comprar (por ejemplo, un packId o código) y construir dinámicamente la preferencia con los datos de ese pack (nombre del pack como título, cantidad=1, precio unitario según priceInCents y posiblemente el id del item indicando el tipo de pack). La arquitectura actual puede soportar esto añadiendo un método sobrecargado o parametrizado. Sugerencia de implementación: extender createPreference para que acepte un identificador de pack en el cuerpo de la petición; buscar el Pack en la base de datos y usar sus atributos para poblar items. De este modo, se puede reutilizar gran parte de la lógica existente (registro de transacción, etc.) pero orientada a créditos. | |
Webhook de confirmación de pago: El endpoint POST /mercado-pago/webhook recibe notificaciones de Mercado Pago cuando una transacción cambia de estado (aprobada, rechazada, etc.). La función processPaymentNotification() del servicio maneja estos eventos. En la implementación actual, tras validar que el evento corresponde a un pago, busca el pago vía API de Mercado Pago para obtener detalles (método fetchPaymentInfo). Luego intenta ubicar la transacción en la base de datos mediante payment_id, preference_id o external_reference y actualiza su estado y detalles según la información obtenida | |
file-uqkcvgnfo2kaq8twdq6d5n | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Sin embargo, no se observa ninguna lógica que aplique los efectos de una transacción aprobada en la cuenta del usuario. Es decir, actualmente si el pago del Plan Básico es exitoso, se registra la transacción como aprobada, pero no se crea una suscripción ni se modifica el plan del usuario dentro de processPaymentNotification (esto posiblemente quedó pendiente de implementar). Análogamente, para el caso de créditos, haría falta agregar dicha lógica de post-procesamiento. Implicación: Para el modelo de créditos, es crítico que al confirmarse un pago se acrediten los créditos comprados al usuario. La arquitectura tiene las piezas para hacerlo (el servicio CreditsService.addCredits(user, n) ya existe | |
file-uqkcvgnfo2kaq8twdq6d5n | |
), pero hay que insertarlo en el flujo. Se recomienda lo siguiente: en la gestión del webhook, cuando paymentInfo.status indique que el pago fue aprobado, determinar qué se compró y actualizar en consecuencia. Dado que la transacción almacenó un campo items (json) con el detalle, podemos distinguir por el items[0].id o algún identificador si se trata de un pack de créditos. Por ejemplo, si adoptamos id: 'pack-<UUID>' para las preferencias de packs, el backend podría extraer ese ID, buscar el Pack correspondiente y luego invocar CreditsService.addCredits para sumarle pack.credits al usuario comprador. En caso de mantener también la opción de plan (menos probable según el nuevo modelo), habría que manejar ese caso aparte (crear o activar la suscripción). Lo esencial es que el flujo de Mercado Pago quede acoplado a la lógica de créditos: una transacción aprobada debe traducirse en créditos incrementados en UserCredits.availableCredits. | |
Restricción “un pack a la vez”: El usuario menciona que solo se podrá comprar un pack de créditos a la vez. Esto es razonable (cada pago es individual), y la implementación actual ya lo asume implícitamente (una preferencia = un ítem). Si se desea reforzar esto, se podría implementar una verificación antes de crear una nueva preferencia: por ejemplo, comprobar que no exista ya una MercadoPagoTransaction PENDING para ese usuario y, de existir, evitar generar otra hasta que se resuelva (esto previene que un usuario genere dos órdenes de pago simultáneamente, lo cual podría complicar la conciliación de créditos). Actualmente no se realiza tal comprobación, pero sería una mejora opcional para robustez. | |
En síntesis, el flujo de compra requiere ajustes para soportar el nuevo modelo: parametrización de ítems de pago según el pack de créditos, y la aplicación del crédito comprado al saldo del usuario cuando la transacción se completa con éxito. La estructura base (transacciones registradas, webhooks recibidos) es válida, pero falta incorporar la lógica de negocio de créditos en estos puntos. Con estos cambios, el sistema de pago podrá reflejar fielmente la compra de créditos en lugar de la suscripción a un plan mensual. | |
Lógica Heredada de Planes Mensuales vs. Consumo de Créditos | |
Un área crítica a revisar es cómo la lógica existente de control de límites y planes mensuales puede interferir con el nuevo mecanismo por créditos. En la implementación actual, la aplicación impone límites estrictos de talonarios basados en el plan activo del usuario. Esto se realiza mediante guards y servicios comunes: | |
Guardia de límite de talonarios (TalonarioLimitGuard): Este guardián se aplica a las rutas de creación de talonarios (presupuestos, recibos, etc.) para impedir que un usuario supere la cantidad máxima permitida por su plan. Su funcionamiento actual es: consultar el plan activo del usuario, obtener sus estadísticas de talonarios creados y determinar si alcanzó el límite definido (plan.maxTalonarios). Si el límite se alcanzó, se lanza una excepción Forbidden con el mensaje instruyendo actualizar el plan | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Por ejemplo, para un usuario Freemium con límite 10, al intentar crear el talonario número 11 el guard rechazará la operación indicando “Has alcanzado el límite de talonarios para tu plan actual. Actualiza tu plan para crear más talonarios.” | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Conflicto con el modelo de créditos: Esta lógica, tal como está, bloquearía la creación de talonarios adicionales incluso si el usuario tuviera créditos disponibles para pagarlos, ya que solo considera el límite fijo del plan. En el contexto de créditos, queremos que el usuario pueda seguir creando más allá del límite gratuito siempre y cuando consuma créditos para ello. Por tanto, se debe modificar la lógica de este guardián (o eliminarlo en favor de otra estrategia). Posibles soluciones: Se podría redefinir el guardián de modo que contemple los créditos: por ejemplo, en lugar de simplemente checar hasReachedLimit, podría: | |
Obtener el plan (Freemium) y su límite gratuito (digamos 10 talonarios). | |
Obtener la cantidad de talonarios ya creados por el usuario. | |
Si aún está debajo del límite, permitir la creación sin cargos (tal como hoy). | |
Si ya alcanzó el límite pero el usuario tiene créditos disponibles (UserCredits.availableCredits >= 1), entonces permitir la creación descontando un crédito del saldo. | |
Si alcanzó el límite y no tiene créditos, entonces sí denegar y enviar un mensaje apropiado (p. ej. “Créditos insuficientes. Compra créditos para crear más talonarios.”). | |
Implementar lo anterior conlleva algunos cambios: el guard actualmente no realiza modificaciones de estado (solo lectura y decisión). Para debitar un crédito habría que o bien hacerlo dentro del guard (no ideal, ya que los guards se ejecutan antes del manejo de la transacción de creación) o mejor, delegarlo al servicio que crea el talonario. Una estrategia podría ser que el guard solo evalúe permiso de forma pasiva: permitir el paso si el usuario no ha excedido su límite gratuito o tiene créditos; en caso de tener créditos, simplemente deja pasar la solicitud. Luego, en el servicio de creación (por ejemplo, ReceiptsService.create()), al persistir el nuevo talonario se invoca a CreditsService.useCredits(user, 1) para restar el crédito correspondiente. De este modo se asegura que el débito del crédito y la creación del talonario ocurran en la misma acción controlada (posiblemente en una transacción de base de datos común, para consistencia). En cualquier caso, el mensaje de error y la lógica del guard debe actualizarse. Ya no tendría sentido hablar de “actualizar plan” cuando no habrá planes superiores a los cuales cambiarse. En su lugar, la notificación de límite excedido debe guiar al usuario a comprar créditos. Este es un cambio menor pero importante de UX (p. ej., el texto podría ser: “Has alcanzado el límite gratuito de talonarios. Adquiere créditos para crear más.”). | |
Servicio de registro de talonarios (TalonarioRegistryService): Este servicio centraliza la obtención de información de planes y conteos de talonarios. Por ejemplo, provee métodos como getPlanInfo(userId) y hasReachedTalonariosLimit(userId) que utilizan el servicio de suscripciones para obtener el plan activo y los límites correspondientes | |
file-uqkcvgnfo2kaq8twdq6d5n | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. En la implementación actual, si el usuario no tiene una suscripción activa, asume el plan Freemium por defecto | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Esto está bien; bajo el nuevo modelo prácticamente todos los usuarios estarán en esa situación (solo Freemium). Pero la lógica de hasReachedTalonariosLimit hará simplemente una comparación numérica totalTalonarios >= plan.maxTalonarios | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Como discutido, esta comparación por sí sola ya no debería causar un bloqueo permanente si existe la alternativa de usar créditos. Habría que enriquecerla o deprecarla. Recomendación: Dado que la contabilidad más fina será por créditos, quizá no se necesite más un método genérico hasReachedTalonariosLimit (salvo para el caso gratuito puro). En lugar de eso, las decisiones de permitir creación se basarán en (talonariosCreados < limiteGratuito) OR (créditosDisponibles > 0). Se podría implementar un nuevo método utilitario en este servicio para esa verificación compuesta. Otra opción es limitar el uso del plan.maxTalonarios: podríamos fijar maxTalonarios del plan Freemium a un valor muy alto (p.ej. 100000) para efectos prácticos de no bloquear, y manejar la gratuidad inicial de 10 talonarios de otra forma (por ejemplo, acreditando 10 créditos gratuitos iniciales a cada usuario nuevo y haciendo que cada talonario consuma siempre créditos). Esta última vía simplificaría la lógica (si todo talonario consume créditos, simplemente das créditos de regalo en lugar de tener lógica dual gratuita vs pagada). Sin embargo, si se desea seguir distinguiendo un tramo gratuito sin tener que manejar “créditos gratis”, la condición dual en la lógica de creación es necesaria. | |
En conclusión, existe lógica legada de los planes mensuales que entra en conflicto directo con el modelo de créditos. Es imperativo ajustarla para que en vez de detener al usuario por superar un límite fijo, se integre con el sistema de créditos. Los pasos concretos serían: | |
Actualizar o eliminar el guard TalonarioLimitGuard para que no impida creación cuando el usuario dispone de créditos. | |
Modificar los mensajes y referencias a “actualizar/upgrade plan” por indicaciones de compra de créditos. | |
Decidir cómo se gestiona el tramo gratuito: vía un contador de talonarios libres (manteniendo maxTalonarios en Freemium) o vía créditos gratuitos iniciales. En ambos casos, implementar la lógica consistente en todo el backend. | |
Asegurarse de retirar cualquier otro rastro de periodicidad: por ejemplo, si hubiera tareas programadas para expirar suscripciones al cabo de un mes, deben ser removidas. (Por la revisión del código, no se evidenció una tarea de expiración automática; posiblemente la baja de suscripciones mensuales sería manual o simplemente no implementada aún). | |
Al limpiar estas partes, la implementación quedará enfocada únicamente en el control de créditos disponibles como método de autorización para emitir nuevos talonarios, logrando el control granular buscado. | |
Escalabilidad y Control Granular de Consumo de Créditos | |
Un beneficio esperado del nuevo modelo es un control más granular y escalable del uso de la plataforma mediante créditos. Analicemos si la arquitectura lo soporta adecuadamente: | |
Diferentes tipos de talonarios con distinto costo: Actualmente todos los talonarios se considerarán equivalentes (1 talonario = 1 crédito). Sin embargo, se menciona la posibilidad de manejar costos distintos por tipo de documento en el futuro. La arquitectura actual no define explícitamente una tabla de costos por tipo, pero es suficientemente flexible para incorporarlo. Dado que la deducción de créditos se realiza invocando useCredits(user, n) con la cantidad a descontar | |
file-uqkcvgnfo2kaq8twdq6d5n | |
, sería trivial llamar con n=2 o cualquier valor según el tipo de talonario. Por ejemplo, si en el futuro un talonario especial debiera costar 2 créditos, la lógica de creación de ese documento podría simplemente solicitar 2 créditos al servicio de créditos. Para mantener esto organizado, se podría introducir una configuración central (un mapa de tipo de talonario a costo en créditos) o añadir un campo de costo en alguna entidad relacionada a talonarios. Pero incluso sin cambios estructurales, el sistema puede soportar esta variación: las funciones de créditos no asumen un coste fijo, reciben un parámetro numérico. En resumen, hay control granular disponible, solo habría que diseñar dónde se define el costo por tipo. Por ahora, con costo uniforme, no se requiere cambio adicional. En cuanto a escalabilidad, este enfoque distribuido (cada módulo de talonario decide cuándo consumir créditos) es manejable dado el tamaño acotado del sistema; si los tipos crecen, quizás se centralizaría más la decisión, pero no es un problema inmediato. | |
Escalabilidad de la solución de créditos: Usar un saldo de créditos por usuario es un patrón simple y escalable. Operaciones de suma/resta son constantes y se pueden optimizar con índices en la base de datos si hiciera falta consultar saldos masivamente (poco probable, ya que siempre se consultan por usuario específico). La entidad MercadoPagoTransaction registra cada compra; esto también es escalable y útil para auditoría. No se aprecia ningún cuello de botella evidente. Incluso si la base de usuarios crece, la lógica sigue siendo O(1) por evento de compra o consumo de crédito. | |
Mantenibilidad y extensibilidad: Al remover la complejidad de las suscripciones temporales, el modelo de créditos simplifica la adición de nuevas funcionalidades. Por ejemplo, añadir un nuevo tipo de paquete de créditos es tan fácil como crear un nuevo registro Pack. Asimismo, si en el futuro se quisiera ofrecer promociones (p.ej. regalar créditos), simplemente se usaría addCredits al usuario correspondiente. La arquitectura actual, con la separación de Packs y UserCredits, soporta estos escenarios sin refactorizaciones mayores. | |
En cuanto al control de consumo, cada acción de creación de talonario deberá ahora siempre verificar y consumir créditos. Esto implica un mayor control transaccional en cada operación, pero es inherente al modelo escogido. La implementación debe cuidarse de hacerlo de forma consistente en todos los módulos que generan talonarios (presupuestos, recibos, reportes, etc.), para que ninguno escape al sistema de cobro. Centralizar parte de esta lógica (por ejemplo, mediante el guard modificado o un servicio común) ayudaría a mantener la consistencia. En síntesis, la arquitectura es suficientemente flexible y escalable para el modelo de créditos. Sólo requiere completar la integración en los puntos adecuados. Con los cambios propuestos, se obtiene un control muy granular: la empresa puede definir cuántos créditos vale cada acción y los usuarios pagan exactamente por el uso que hacen, permitiendo además extender o modificar esas reglas en el futuro con impacto acotado. | |
Conclusiones y Recomendaciones | |
Diagnóstico general: La base de la arquitectura NestJS existente es sólida y modular, pero arrastra componentes diseñados para planes de suscripción mensual que deben ajustarse o eliminarse para alinearse con la monetización por créditos. No se encontraron impedimentos insalvables; más bien, la necesidad es de refactorización enfocada en reemplazar la lógica de límites por una lógica de créditos. A continuación se resumen las recomendaciones específicas para adecuar el sistema: | |
1. Simplificar la gestión de planes: Mantener solo el plan Freemium como plan vigente. Eliminar del flujo cualquier transición a planes pagos mensuales (el plan Básico) y sus referencias. La entidad Plan puede conservarse para configuración global (p.ej. límites gratuitos), pero asegurarse de que Plan.priceInCents y similares no sean usados indebidamente. Opcionalmente, considerar renombrar PlansController/Service a algo más representativo (p.ej. PlansAndPacks) si ahora manejará básicamente la consulta del plan free y la lista de packs de créditos. | |
2. Retirar la lógica de Subscription temporal: Dejar de crear nuevas Subscription y evaluar si conviene migrar los usuarios existentes directamente al estado “freemium permanente”. El método SubscriptionsService.getActivePlan() puede simplificarse para devolver siempre el plan Freemium (ya lo hace por defecto si no hay suscripción | |
file-uqkcvgnfo2kaq8twdq6d5n | |
). A futuro, podría eliminarse la entidad Subscription o dejarla solo para historial. Esto reduce complejidad y evita estados inconsistentes. | |
3. Integrar el sistema de créditos en la creación de talonarios: Modificar el guard TalonarioLimitGuard o su lógica equivalente para incorporar la verificación de créditos. En concreto, no denegar la creación si el usuario tiene créditos disponibles. Implementar la deducción de 1 crédito por talonario en el momento apropiado (idealmente al crear el registro del talonario, dentro de la misma transacción de base de datos). Actualizar mensajes de error para reflejar el nuevo modelo (p.ej. “compra créditos” en lugar de “actualiza tu plan” | |
file-uqkcvgnfo2kaq8twdq6d5n | |
). Si se desea conservar algunos talonarios gratuitos iniciales, mantener maxTalonarios en el plan Freemium (ej. 10) y aplicar la lógica híbrida descrita; en caso contrario, podría simplemente requerirse crédito desde el primero y otorgar créditos de regalo iniciales a nuevos usuarios (ambas opciones son válidas, pero debe ser consistente globalmente). | |
4. Refactorizar el flujo de Mercado Pago: Hacer que la compra se asocie a packs de créditos: | |
Crear preferencias de pago dinámicamente según el pack que el usuario elija (buscar el Pack en la base de datos y usar sus datos en items en lugar del item fijo del plan Básico). | |
Al recibir confirmación de pago aprobado, actualizar el saldo de créditos del usuario con CreditsService.addCredits usando la cantidad adquirida | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. Este paso puede implementarse dentro de processPaymentNotification una vez verificado el pago, o en un paso subsiguiente si se prefiere (por ejemplo, un endpoint al que redirija la URL de éxito para finalizar la compra). Integrarlo en el webhook automatiza todo el proceso. | |
Registrar en la entidad MercadoPagoTransaction el tipo de pack comprado (se puede inferir del JSON de items o agregar un campo adicional para mayor claridad). Esto será útil para auditoría y para evitar que un usuario abuse de un endpoint manual. | |
Probar escenarios de error: pagos pendientes o rechazados no deben alterar créditos; pagos duplicados (muy poco probable) deberían quizás ser ignorados si ya se procesó uno. La infraestructura actual de transacciones ya permite filtrar por payment_id para no duplicar registros | |
file-uqkcvgnfo2kaq8twdq6d5n | |
. | |
5. Verificar consistencia en todos los módulos de talonarios: Asegurarse de que cada tipo de talonario (presupuestos, recibos, reportes, etc.) aplique la nueva política. Dado que el sistema está diseñado con un servicio común (TalonarioRegistry) y guards globales, es probable que con los cambios en ellos ya se cubran todos los casos. Aun así, conviene revisar cada controlador de creación para confirmar que no haya atajos (por ejemplo, algún administrador pudiendo crear talonarios sin pasar por el guard; aunque el SUPER_ADMIN ya está exento de límites según el código | |
file-uqkcvgnfo2kaq8twdq6d5n | |
, lo cual puede mantenerse). | |
6. Escenarios futuros: Mantener la flexibilidad. La arquitectura propuesta seguirá permitiendo, más adelante, reintroducir planes especiales si se quisiera (p.ej. un plan premium que dé créditos mensuales incluidos), ya que no estamos eliminando las tablas sino modulando su uso. También, si se decide que distintos talonarios cuesten diferente, simplemente documentar y programar esos costos en la capa de negocio. Por ahora, con costo uniforme, la implementación es directa. | |
En conclusión, no existen limitaciones técnicas mayores en la arquitectura para implementar el modelo de monetización por créditos, pero sí es necesario realinear ciertos componentes clave. La eliminación de la lógica de suscripción mensual y la incorporación del manejo de créditos en las validaciones y flujos de pago son los puntos neurálgicos. Con las refactorizaciones propuestas – principalmente en los guards de límite y en la integración con Mercado Pago – el backend podrá soportar de forma coherente el sistema de créditos: los usuarios consumirán un crédito por cada talonario emitido, podrán adquirir más créditos según lo necesiten, y el sistema tendrá control total y en tiempo real sobre estos consumos de forma escalable y mantenible. | |
file-uqkcvgnfo2kaq8twdq6d5n | |
file-uqkcvgnfo2kaq8twdq6d5n | |
(Estas referencias de código ilustran la configuración actual del plan Freemium con límite fijo y la preferencia de pago rígida para un plan, las cuales se modificarán según lo descrito). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment