Saltar a contenido

⚠ Alcance. Este inventario describe la rama de desarrollo hquintero (commit 35af6cd al 2026-08-19), que no está publicada. Nueve de las doce funcionalidades listadas no existen en main / staging / develop. La columna «Estado» de cada tabla indica exactamente cuáles sí. Ver Arquitectura para lo que sí está desplegado.

Inventario del desarrollo custom sobre Medusa

Todo lo que se construyó encima de Medusa v2 para el e-commerce de Compulandia: qué se implementó, dónde vive cada pieza y cuál está realmente en producción.

Archivos 87
Líneas 12.339
Módulos propios 4
Rutas API propias 12
Widgets de admin 6
Versión de Medusa 2.15.5
Período septiembre 2025 – agosto 2026
Commits / autores 93 / 3

Panorama

Medusa v2 trae el núcleo de comercio —catálogo, carrito, órdenes, inventario, precios— pero no sabe nada de Paraguay. Todo el desarrollo custom llena exactamente ese hueco: cobrar por Bancard QR, calcular flete por caja y ciudad, notificar en castellano, y darle al equipo comercial pantallas de administración que Medusa no incluye.

Son 12.339 líneas repartidas en 87 archivos. Cerca de 900 son el script de seed; el resto es funcionalidad.

El trabajo se apoya en seis puntos de extensión de Medusa —módulos, rutas API, suscriptores, jobs, hooks de workflow y widgets de admin— más una capa propia de tiempo real que no corresponde a ninguno de ellos.

Área Archivos Líneas
src/admin 44 4.866
src/modules 13 3.157
src/api 13 1.815
src/scripts 3 961
src/subscribers 9 799
src/lib 2 360
src/workflows 2 213
src/jobs 1 168

Estado de despliegue

El dato más importante del inventario: la mayor parte de lo construido todavía no llegó a producción. Las ramas main y staging llevan deliberadamente una porción chica del desarrollo; el resto vive en la rama hquintero, pendiente de pruebas.

En producción — 3

Presente en main y staging.

Funcionalidad Tipo
Transferencia bancaria payment
Editor de variantes en listas de precios admin
Ajustes de UI del admin (2 widgets CSS) admin

En desarrollo — 9

Solo en hquintero, sin desplegar.

Funcionalidad Tipo
Pagos con Bancard QR payment
Flete calculado MC Group fulfillment
Notificaciones por email notification
Login con Google auth
Estado de pago en tiempo real sse
Descuento por transferencia promo
Gestión de geozonas admin
Configuración de envíos admin
Detalle de pago en la orden (intento de pago) admin
Intentos de pago: página, detalle, widget en el pedido, rutas de admin y resumen diario (RF-010) admin + API + job

Módulos propios

Son la pieza más pesada del desarrollo: 3.157 líneas. Dos proveedores de pago, uno de envío y uno de notificaciones, todos registrados en medusa-config.ts.

Bancard QR · en desarrollo

src/modules/bancard-qr/

Proveedor de pago con QR de Bancard, la red de pagos paraguaya.

Implementa el ciclo completo de un payment provider de Medusa —iniciar, autorizar, capturar, cancelar, reembolsar— más un cliente HTTP propio contra la API de Bancard y el manejo de webhooks de confirmación. El flujo real es asincrónico:

  1. El cliente elige Bancard QR y el backend genera el QR contra Bancard.
  2. La sesión de pago queda pending y se programa su expiración.
  3. El cliente paga desde su app bancaria; Bancard llama al webhook.
  4. El backend marca el pago confirmado y avisa al storefront por SSE, sin que el cliente recargue.

Si nadie paga, el QR se cancela solo. La expiración usa un setTimeout por sesión para ser precisa al segundo, y un job cada 5 minutos actúa de red de seguridad para los QR que quedaron huérfanos por un reinicio del servidor.

4 archivos · 569 líneas · 3 rutas API + 1 webhook · 1 job programado

Ver también: Integración Bancard QR.

MC Group · en desarrollo

src/modules/mc-fulfillment/

Proveedor de envío con precio calculado por caja, ciudad y categoría.

El módulo más elaborado en lógica de negocio. En vez de una tarifa plana, calcula el flete en tiempo real cuando el cliente ingresa su dirección:

  1. Toma la ciudad de la dirección y busca su geozona.
  2. Verifica que MC Group opere en esa ciudad; si no, la opción no se ofrece.
  3. Separa los ítems en especiales (categorías con precio propio) y normales.
  4. Para los normales, busca el producto más grande y le asigna la caja más chica en la que entra.
  5. Suma el precio de esa caja más el de cada ítem especial por cantidad.

Los tipos de caja se configuran por depósito, y los precios por opción de envío, todo desde los widgets de admin. Las geozonas ya contemplan tres transportadoras —MC, CLX y AEX— con disponibilidad y plazo propios, así que sumar otro courier es configuración, no código.

3 archivos · 674 líneas · 11 tipos de dato · 2 widgets de configuración

Ver también: Fulfillment MC Group.

Notificaciones por email · en desarrollo

src/modules/email-notification/

Envío de correos transaccionales por SMTP con plantillas HTML propias.

15 plantillas escritas a mano, en castellano y con la identidad de Compulandia. Diez son para el cliente —bienvenida, recuperación de contraseña, verificación de correo, y el ciclo completo del pedido: confirmado, en preparación, enviado, entregado, cancelado, reembolsado, devolución— y cinco son avisos internos al equipo comercial (pedido nuevo, enviado, cancelado, devolución solicitada).

Es el módulo con más líneas del proyecto, y casi todas son plantilla: el contenido y los estilos viven separados de la lógica de envío, así que cambiar un texto no toca código.

4 archivos · 1.706 líneas · 15 plantillas · 9 suscriptores que lo usan

Transferencia bancaria · en producción

src/modules/bank-transfer/

Proveedor de pago sin cobro inmediato, con captura manual.

El más simple de los cuatro, y el único de esta lista que ya está cobrando. Permite cerrar la orden sin pago confirmado: queda pendiente, el vendedor verifica la transferencia y captura el pago desde el admin. Sin integración externa ni webhooks.

2 archivos · 208 líneas · 1 hook de promoción asociado

Rutas API propias

Endpoints que Medusa no expone y que el storefront o el admin necesitan. 1.815 líneas contando el middleware.

Ruta Método Para qué Estado
/store/bancard-qr/generate POST Genera el QR de pago y programa su expiración desarrollo
/store/bancard-qr/cancel POST Cancela un QR vigente si el cliente cambia de método desarrollo
/webhooks/bancard-qr POST · GET Recibe la confirmación de pago de Bancard desarrollo
/store/payment-status/:session_id GET Consulta puntual del estado de una sesión de pago desarrollo
/payment-events/:session_id GET Canal SSE: empuja el cambio de estado al storefront desarrollo
/store/geozones GET Departamentos y ciudades de Paraguay con cobertura por courier desarrollo
/admin/fulfillment-sets/:id GET · POST Lee y guarda tipos de caja (Medusa no expone este recurso) desarrollo
/admin/shipping-options/:id/metadata POST Actualiza solo la metadata de precios sin tocar el resto desarrollo
/store/custom/products/:id GET Producto con precios calculados por región resueltos producción
/admin/custom/price-lists/current GET Listas de precios vigentes hoy en zona horaria de Asunción producción
/store/custom · /admin/custom GET Endpoints de prueba del scaffold inicial producción

Suscriptores y jobs

Todo lo que reacciona sin que nadie lo pida. Los suscriptores escuchan eventos de Medusa y disparan correos; dos de ellos además avisan al equipo comercial.

Evento Qué dispara Líneas
order.placed Confirmación al cliente + aviso interno de pedido nuevo 162
order.fulfillment_created Aviso de envío al cliente + aviso interno 168
order.completed Correo de pedido entregado 82
order.canceled Correo de cancelación 72
order.return_requested Acuse de devolución solicitada 98
order.return_received Confirmación de devolución recibida 95
customer.created Correo de bienvenida 59
auth.password_reset Correo de recuperación de contraseña 49
product.created · updated · deleted Suscriptor de ejemplo del scaffold: solo escribe en consola 14

El único job programado es bancard-qr-cleanup, que corre cada 5 minutos para revertir QR huérfanos. No es el mecanismo principal de expiración —ese es el temporizador por sesión— sino el que cubre los reinicios del servidor.

Descuento automático por transferencia

Una funcionalidad chica en líneas pero con lógica delicada, repartida entre dos hooks de workflow y un middleware.

La idea comercial es simple: pagar por transferencia bancaria tiene descuento. Lo difícil es que el descuento siga al método de pago mientras el cliente navega el checkout. La implementación cubre los tres momentos:

  1. Al crear la sesión de pago, un middleware aplica o quita la promoción según el método elegido, sin que el cliente escriba ningún código.
  2. Si alguien ingresa el código a mano, un hook valida que efectivamente tenga transferencia seleccionada.
  3. Si el cliente vuelve atrás en el checkout, otro hook remueve la promoción al refrescarse el carrito.

El código de promoción es configurable por variable de entorno (BANK_TRANSFER_PROMO_CODE); si no está definida, toda esta lógica se desactiva sola.

Widgets de administración

4.866 líneas de React —el área con más archivos del proyecto— repartidas en 6 widgets y 36 archivos de componentes y hooks reutilizables.

Widget Dónde aparece Qué permite hacer Estado
Variantes de lista de precios price_list.details Ver, buscar, agregar y quitar variantes con su precio normal, stock y precio especial producción
Configuración de envíos location.details Definir tipos de caja, precios por caja y categorías especiales por depósito desarrollo
Geozonas location.details Administrar ciudades por zona de servicio y cobertura de cada courier desarrollo
Detalle de pago order.details.side Ver datos del pagador, código de autorización y ticket de Bancard en la orden desarrollo
Columna ancha en listas de precios price_list.details Ensancha la columna de título para que entren nombres largos producción
Descripción recortada product.details Limita la descripción a tres líneas en la ficha de producto producción

Los dos últimos son inyecciones de CSS de treinta líneas, no aplicaciones. Los cuatro primeros sí son interfaces completas, con carga de datos, modales de edición y tablas propias.

Ver también: Widgets de administración de envíos.

Estado de pago en tiempo real

Una capa propia que no corresponde a ningún punto de extensión de Medusa: 360 líneas que resuelven cómo avisarle al storefront que el QR ya se pagó.

La decisión de diseño está documentada en el propio código: se eligió Server-Sent Events sobre Socket.IO porque no requiere puerto adicional ni dependencias extra, y reutiliza el servidor HTTP de Medusa. El storefront abre una conexión a /payment-events/:session_id y espera; cuando llega el webhook de Bancard, el backend empuja el evento por ese canal.

La acompaña un gestor de expiraciones que programa un temporizador por cada QR emitido, en lugar de sondear la base periódicamente. Es lo que permite que el QR expire al segundo exacto configurado en BANCARD_QR_TIMEOUT_MS.

Infraestructura y operación

Fuera de src/ hay una capa de infraestructura completa, construida sobre todo entre junio y agosto de 2026.

Pieza Qué resuelve
Dockerfile Build multi-etapa: compila y deja una imagen final solo con dependencias de producción
.github/workflows/build-deploy.yml Build a GHCR, deploy automático a staging, deploy a producción con aprobación manual
deploy.sh Despliegue manual con backup de base, health check y rollback automático a la imagen anterior si falla
docker-compose.yml Compose agnóstico al ambiente, con Postgres, Redis y el backend
admin.nginx.conf Proxy del panel de administración
docs/ Runbook, troubleshooting, CI/CD, ADR, integraciones y análisis

La configuración de Medusa registra además dos cosas que no son código propio pero sí decisiones de arquitectura: Redis para caché, bus de eventos, motor de workflows y locking, y login con Google junto al de email y contraseña.

Ver también: Arquitectura y Runbook.

Pendientes conocidos

Cosas detectadas al levantar el proyecto que no bloquean el desarrollo pero sí conviene tener a la vista.

El seed corre en cada arranque de producción · riesgo

start.sh es el comando de arranque del contenedor y ejecuta migraciones, npm run seed y la creación de un usuario administrador en cada inicio. El seed está protegido con || true, así que un fallo no detiene el arranque —pero tampoco avisa. Además, la contraseña del administrador está escrita en el archivo, que está versionado en el repositorio.

Ver ADR-0002 · Migraciones y bootstrap en start.sh.

El 61% del catálogo no puede completar el checkout · riesgo

1.578 de 2.574 productos publicados no tienen vinculado un shipping profile, y Medusa rechaza cerrar cualquier carrito que los contenga con el error The cart items require shipping profiles that are not satisfied by the current shipping methods.

La causa es que createProductsWorkflow solo crea ese vínculo si el payload trae shipping_profile_id explícito, y el integrador que sincroniza el catálogo no lo envía. En diciembre de 2025 se corrió un backfill puntual (936 vínculos), por eso los productos de ese mes funcionan; todo lo importado antes y después está afectado.

El arreglo son dos piezas: un backfill de los productos existentes y un suscriptor de product.created que asigne el perfil por defecto cuando falte.

Medusa 2.15.5 frente a 2.19.0 · atención

El proyecto está cuatro versiones menores atrás de la última estable. No es urgente —2.15.5 es de mayo de 2026— pero conviene encarar el upgrade después de estabilizar las features pendientes, no en paralelo.

Ver Upgrade Medusa 2.11.3 → 2.15.5 como referencia del esfuerzo del salto anterior.

Métodos de envío que se acumulan en el carrito · atención

Un carrito de prueba llegó a tener 19 métodos de envío asociados, varios duplicados. Sugiere que el storefront los agrega sin limpiar los anteriores. No rompe el checkout, pero ensucia el carrito.

Identidades de Google sin vincular · atención

Un cliente que se registró con email y contraseña y después intenta entrar con Google recibe un 422 Customer with this email already has an account. La identidad de Google queda creada sin customer_id en su app_metadata, el token sale sin actor_id, y el storefront lo interpreta como usuario nuevo. Medusa v2 no vincula identidades entre proveedores por email, por diseño.

Balance

Lo construido cubre las tres piezas que un e-commerce paraguayo necesita y Medusa no trae: cobrar por QR, calcular flete real por caja y ciudad y hablarle al cliente en castellano. Está escrito, compila contra 2.15.5 y arranca.

Lo que falta no es desarrollo, es validación: nueve de las doce funcionalidades nunca pasaron por staging. El próximo tramo de trabajo es probarlas y subirlas de a una, no seguir agregando.


Inventario levantado sobre la rama hquintero, commit 35af6cd, al 19/08/2026.