More
Сhoose

Ingeniería

Aplicada

Resultados

samuel-torres.com

Caravana — Plataforma de Puntos y Canje en Piso de Venta

Leer el caso de estudio
Terminal punto de venta en retail, representando el flujo de canje en tienda
MySQL · PHP · Concurrencia · Multi-zona horaria
2026
Rol

Ingeniería de backend, diseño de concurrencia y desarrollo asistido por IA para un sistema de canje con dinero real, multi-tienda y multi-día.

Stack principal

PHP · MySQL · JavaScript · React (app de piso) · Claude Code (programación asistida) · Zonas horarias IANA

Contexto

Caravana necesitaba correr una campaña de puntos y canje de premios en piso de venta, en tiendas de varias cadenas, durante varios días consecutivos. Había dinero real de por medio: un doble cobro no es un bug en un tablero, es un cliente enojado en el mostrador con gente detrás esperando. El sistema tenía que sostener seis restricciones a la vez — dinero real, WiFi de tienda poco confiable, operadores no técnicos con prisa, servidor compartido, días consecutivos donde cada uno depende del cierre correcto del anterior, y varias zonas horarias con corte a las 5 PM hora local.

El proceso — seis fases, no una línea recta

Requisitos, diseño visual, programación asistida por IA, presentación, capacitación y producción. La verificación humana en capacitación y producción regresó trabajo a programación más de una vez — el diagrama de abajo muestra el ciclo, no una línea.

Diagrama del ciclo de seis fases: requisitos, diseño, programación, presentación, capacitación, producción

1. Requisitos

+
-

Lo que importa aquí no es que hubo juntas — es qué se decidió y qué se descartó. El bono de puntos quedó como captura manual a propósito, para no inventar una regla que el cliente no había definido. La hora de corte pasó por tres valores antes de fijarse en las 5 PM hora local. Encontrar ambigüedades temprano es el trabajo real de esta fase.

2. Diseño visual

+
-

Hecho en Claude Design y navegable antes de escribir código de producción. Eso hizo que programación nunca discutiera lo visual — una regla escrita (el diseño ya está aprobado, solo se corrige lo roto) evitó rediseñar bajo presión. Limitación honesta: el mockup usaba datos falsos; era referencia visual, no especificación de comportamiento.

3. Programación asistida por IA

+
-

Un commit por paso, prompts como archivo completo, protocolo "reporta y continúa", lista explícita de comandos que requieren autorización puntual, respaldo obligatorio antes de ediciones destructivas, evidencia cruda para cada afirmación, pruebas de concurrencia corridas en paralelo (nunca secuenciales), y arnés de pruebas en verde actualizado en el mismo commit que agrega una pantalla.

4. Presentación

+
-

Los cambios que pidió el cliente fueron de reportes y visibilidad — no de lógica de negocio. Es señal de que la fase de requisitos funcionó.

5. Capacitación

+
-

El material salió de los modos de falla del sistema, no de una lista de funciones — capacitar sobre lo que más se puede malinterpretar.

6. Producción

+
-

Primera activación real sin fallos, uso desde tablet confirmado directamente en los registros del servidor.

Las decisiones técnicas

1. Los candados de concurrencia viven en MySQL, no en PHP

+
-

Teléfono único, clave de idempotencia, y una restricción de base de datos que garantiza una sola activación en curso en todo el sistema. Una validación en código la brincan dos peticiones casi simultáneas; un UNIQUE de base de datos, no. Los bloqueos de fila siempre se adquieren en el mismo orden (inventario antes que saldo), con reintento ante bloqueo cruzado — invertir ese orden es la receta clásica del deadlock.

2. La clave de idempotencia nace con la operación lógica

+
-

No con el intento HTTP. Generarla al tocar el botón hace que un reintento por timeout de WiFi de tienda produzca una clave nueva — y cobre doble, justo en el escenario para el que se construyó esta defensa.

3. Un choque de claves regresa el éxito original, nunca un error

+
-

El registro del canje se escribe como el primer statement de la transacción, antes de tocar saldo o inventario. Cuando la clave única choca, la respuesta es 200 con el canje original — un error ahí haría que la pantalla reintentara con clave nueva, y ahí ocurre el doble cobro.

4. Fecha operativa calculada en la aplicación con zonas IANA

+
-

Nunca con las funciones de fecha del motor de base de datos — el servidor de BD no tenía cargado el catálogo de zonas con nombre y fallaba en silencio. El bug real que esto evitó: con el sitio en UTC, el día se cortaba a las 6 PM hora de México en vez de las 5 PM.

5. Se guarda el hecho, se calcula lo derivado

+
-

El estatus de una activación se deriva de los hechos guardados, nunca se escribe como texto suelto. La columna vieja que sí guardaba el estatus como texto hoy muestra valores que ya no coinciden con la realidad.

6. Dos fotografías congeladas: el stock inicial y el reporte de cierre

+
-

El stock inicial se congela en el primer movimiento del día, no al activar — mercancía que se sigue cargando es preparación, no operación. El reporte del día se congela al cerrar y no se recalcula — una corrección posterior no puede cambiar en silencio un número que el cliente ya recibió como definitivo.

7. Lo reversible dentro de la transacción, lo irreversible fuera

+
-

Una fila de auditoría o un correo encolado desaparecen limpiamente con un rollback; el envío real del correo y la escritura de archivos ocurren fuera. Un fallo en la bitácora nunca aborta la operación de negocio.

8. Fotos de comprobante detrás de un endpoint con permisos y expiración

+
-

Fuera de la carpeta pública, con token HMAC y expiración, para que los enlaces del reporte abran con un clic desde una hoja de cálculo sin una URL adivinable. El redirect es reducción de superficie, no la frontera de seguridad — la frontera real es el rechazo del servidor a nivel de endpoint, escrito así como comentario en el código.

9. Apertura y cierre de jornada perezosos, sin tareas cron

+
-

Se ejecutan cuando la app de piso consulta el estado de la jornada — el único punto del sistema donde una petición está realmente garantizada.

10. Concurrencia optimista en la edición de inventario

+
-

Para que un administrador con la pantalla de catálogo abierta no sobrescriba, con un valor absoluto, los canjes hechos en piso mientras tanto.

11. Correo transaccional con cola propia

+
-

Interruptor que no requiere despliegue, tope por hora bajo el límite del servidor compartido, y SPF/DKIM/DMARC alineados.

12. Un arnés de pruebas que falla ante cualquier advertencia de Hooks

+
-

Monta los archivos reales de producción sobre React y un DOM real, hace login simulando eventos reales de usuario, y falla la corrida ante cualquier advertencia de Hooks de React — no solo ante errores.

Lo que se rompió trabajando así

No vendo esta metodología como infalible. Tres incidentes, una sola causa raíz de fondo: el agente corre con privilegios elevados, y todo lo que crea nace con esos mismos privilegios.

  • Un archivo de configuración quedó con el dueño equivocado y tiró el sitio completo.
  • Un directorio creado con privilegios elevados bloqueó guardado de archivos durante horas, sin ningún error visible que apuntara a la causa.
  • Una limpieza automatizada borró trabajo que el cliente ya había capturado.

El hilo que atraviesa las seis fases

Que los mensajes no mintieran. "Sin configurar" se mantuvo distinto de "agotado" en requisitos; se diseñó un estado vacío para que no se leyera como pantalla rota; cada texto que describía mal una condición tenía un bug real detrás en programación; capacitación enseñó exactamente eso. Mismo criterio, cuatro momentos distintos — la idea más transferible de todo el proyecto.

Resultados, y lo que quedó sin verificar

54

activaciones programadas

139

tiendas

19

productos (subió de 14, dos días antes del evento)

1,026

filas de inventario

10

zonas horarias soportadas, 2 en uso real

50+

archivos en tres capas, partidos de un archivo de 1,858 líneas

4

días consecutivos encadenados por la restricción de una sola activación activa

10 / 0

peticiones simultáneas en la prueba de concurrencia — una gana, 0 errores de servidor

No cito: clientes atendidos, ingresos, tasa de conversión — no están verificados.

Y con honestidad: nunca se probó sosteniendo una tablet con luz real de tienda y dedos reales con prisa; nunca se verificó la entrega de correo a un proveedor de bandeja específico; y el correo del cliente es un campo opcional — si el operador no lo pide, ese cliente no recibe nada.

Evidencia de la app de piso y el panel admin en producción

Fotos de stock ilustrativas mientras tanto — cada captura real de este sistema muestra el logo del cliente o datos reales de un cliente, y este caso de estudio corre sin ninguno de los dos.

Foto ilustrativa de un empleado operando una terminal punto de venta, representando el login del operador

Login del operador en la app de piso (foto ilustrativa).

Foto ilustrativa de un mostrador de atención a clientes, representando el alta de cliente

Alta de cliente — flujo completo, punto C (foto ilustrativa).

Foto ilustrativa de una transacción exitosa en punto de venta, representando un canje completado

Canje exitoso — idempotencia en la práctica, punto 3 (foto ilustrativa).

Foto ilustrativa de un dashboard de analítica, representando la pantalla de bitácora y reportes

Reporte congelado y bitácora completa, punto 6 (foto ilustrativa).

Foto ilustrativa de una agenda abierta, representando el calendario de activaciones multi-tienda

Agenda de activaciones multi-tienda y multi-día (foto ilustrativa).

Foto ilustrativa de anaqueles de tienda, representando el catálogo editable de productos

Catálogo con edición de concurrencia optimista, punto 10 (foto ilustrativa).

Estas son fotos de stock que sustituyen capturas reales, no son capturas del sistema real — cada captura real revisada mostraba el logo del cliente incrustado en la interfaz, el nombre y teléfono real de un cliente, o la IP real de un servidor, así que ninguna se pudo publicar tal cual.

Versión corta (card del portafolio)

Sistema de canje de puntos con dinero real para 139 tiendas en 54 activaciones programadas: candados de concurrencia a nivel de base de datos, clave de idempotencia atada a la operación lógica, y zonas horarias IANA calculadas en la aplicación.

Bullets para CV / LinkedIn

  • Diseñé el modelo de concurrencia de un sistema de canje de puntos con dinero real usando restricciones UNIQUE a nivel de base de datos, verificado con una prueba de 10 peticiones simultáneas: una gana, 0 errores de servidor.
  • Corregí un riesgo real de doble cobro al mover la generación de la clave de idempotencia del intento HTTP a la operación lógica.
  • Escalé un catálogo de 14 a 19 productos y 139 tiendas en 54 activaciones a lo largo de 4 días consecutivos sin romper la restricción de una sola activación activa.
Trabajo con clientes nacionales e internacionales. Tarifas en USD y MXN disponibles.

¿Tienes un proyecto que necesita un ingeniero que entregue?

Contacto profesional:

Ciudad de México

Ciudad de México, México Disponible inmediatamente

Disponibilidad

Proyectos remotos y presenciales MXN · USD

© 2026 Ing. Samuel Torres. Ingeniería aplicada.

English version