Flujo de Check-in con QR
Generacion automatica de pases QR, escaneo en tiempo real y validacion con soporte offline para check-in de invitados el dia del evento.
Tabla de Contenidos
- Vision General
- Generacion de Pases QR
- Tipos de Pase
- Pre-carga para Modo Offline
- Escaneo de Codigos QR
- Validacion del Pase
- Flujo Completo: Generar, Escanear, Validar
- Cola Offline y Sincronizacion
- Casos de Error
- Archivos Clave
1. Vision General
El check-in con QR es la funcionalidad central del dia del evento. Permite al anfitrion (o a un colaborador con rol HOST) verificar la entrada de cada invitado escaneando un codigo QR unico desde la app movil o la PWA.
El sistema esta disenado para funcionar incluso sin conexion a internet, un escenario comun en salones de eventos, haciendas o locaciones rurales. Los pases se pre-cargan con anticipacion y las validaciones se ejecutan localmente, encolandose para sincronizacion posterior cuando la conexion se restablezca.
Numeros clave
| Concepto | Detalle |
|---|---|
| Tiempo de validacion online | < 200ms |
| Tiempo de validacion offline | < 50ms (local) |
| Vigencia del pase | Configurable, por defecto hasta fin del evento |
| Formato del codigo QR | String alfanumerico de 24 caracteres |
| Formato de imagen | PNG Base64, 300x300px |
2. Generacion de Pases QR
La generacion de pases QR es automatica y se dispara en dos momentos:
- Al agregar invitados: Cuando se importan invitados via CSV o se crean manualmente desde el admin, el sistema genera automaticamente un pase QR para cada uno
- Bajo demanda: El anfitrion puede regenerar pases desde el admin o la app movil
Proceso de generacion
- El admin o la app llama a
POST /v1/qr-passes/generatecon{ guestId, eventId, type } QRPassesService.generate()ejecuta:- Verifica que el guest existe y pertenece al evento
- Genera un codigo unico de 24 caracteres alfanumericos con
crypto.randomBytes() - Genera la imagen QR en formato PNG Base64 (300x300px) usando la libreria
qrcode - Crea el registro
QRPassen la base de datos con estadoACTIVE
- Retorna el pase con codigo e imagen al cliente
Modelo QRPass
| Campo | Tipo | Descripcion |
|---|---|---|
id | UUID | Identificador unico |
guestId | UUID | Invitado propietario del pase |
eventId | UUID | Evento asociado |
code | String (24) | Codigo alfanumerico unico |
type | Enum | ENTRY, EXIT, VIP |
imageBase64 | Text | Imagen QR en PNG Base64 |
isUsed | Boolean | Si el pase ya fue escaneado |
usedAt | DateTime (nullable) | Momento del escaneo |
scannedBy | UUID (nullable) | ID del HOST que escaneo |
checkInMethod | Enum | QR_SCAN, MANUAL, OFFLINE_SYNC |
expiresAt | DateTime | Momento de expiracion |
createdAt | DateTime | Momento de creacion |
3. Tipos de Pase
El sistema soporta tres tipos de pase QR, cada uno con un proposito diferente:
| Tipo | Proposito | Validacion especial |
|---|---|---|
| ENTRY | Entrada general al evento | Ninguna adicional |
| EXIT | Registro de salida | Solo valido si el guest ya hizo check-in |
| VIP | Acceso a areas restringidas (mesa VIP, backstage) | Verifica atributo VIP del guest |
El tipo mas comun es ENTRY, que se genera automaticamente para todos los invitados. Los tipos EXIT y VIP se configuran manualmente por el anfitrion.
4. Pre-carga para Modo Offline
Para garantizar el funcionamiento sin conexion, los pases QR se pre-cargan en el dispositivo con anticipacion.
En la app nativa (nvito-client)
useQRPasseshook constaleTime: 10 * 60 * 1000(10 minutos) ygcTime: 7 * 24 * 60 * 60 * 1000(7 dias)- Los pases se almacenan en SecureStore con persistencia entre sesiones
- React Query mantiene los datos en cache incluso sin conexion
- El hook se ejecuta al entrar a la pantalla de scanner, precargando todos los pases del evento
En la PWA (nvito-pwa)
- Los pases se almacenan en IndexedDB usando la tabla
qr-passes useOfflineDatahook sincroniza los pases desde el BFF cuando hay conexion- La PWA puede funcionar como Service Worker para interceptar requests offline
Estructura del cache offline
Cada pase en cache contiene:
code: El codigo de 24 caracteres (para busqueda local)guestId: Para actualizar el estado del invitadoguestName: Para mostrar en la UI de confirmaciontype: Tipo de paseisUsed: Estado actualtableNumber: Mesa asignada (si existe)dietaryRestrictions: Restricciones alimentarias (para informar al staff)
5. Escaneo de Codigos QR
El escaneo utiliza tecnologias diferentes segun la plataforma:
App nativa: expo-camera
La app usa expo-camera con el modo de escaneo de codigos de barras:
- Escaneo continuo en tiempo real
- Feedback háptico via
expo-hapticsal detectar un codigo - Overlay visual con guia de encuadre
- Linterna integrada para escaneo en condiciones de poca luz
StatsHeadermuestra conteo en tiempo real: total invitados, checked-in, pendientes
PWA: html5-qrcode
La PWA usa la libreria html5-qrcode que accede a la camara via WebRTC:
- Compatible con todos los navegadores modernos (Chrome, Safari, Firefox)
- Seleccion de camara frontal/trasera
- Misma UX de overlay y feedback que la app nativa
- Fallback a input de archivo si la camara no esta disponible
6. Validacion del Pase
Una vez escaneado el codigo, la validacion sigue un proceso estricto.
Validacion online
- La app envia
POST /v1/qr-passes/validatecon{ code, eventId } QRCheckInService.validate()ejecuta:- Busca el pase por
codeen la tablaqr_passes - Verifica que
eventIdcoincida - Verifica que
isUsed === false - Verifica que
expiresAt > now() - Marca el pase como usado:
isUsed = true,usedAt = now(),scannedBy = hostUserId,checkInMethod = QR_SCAN
- Busca el pase por
- Actualiza el estado del guest:
checkedIn = true,checkedInAt = now() - Emite el evento de dominio
GuestCheckedInEvent - Crea un registro en
AuditLogcon tipoGUEST_CHECK_IN - Retorna la confirmacion con datos del invitado (nombre, mesa, restricciones)
Validacion offline
- La app busca el codigo en el cache local (SecureStore o IndexedDB)
- Verifica
isUsed === falseyexpiresAt > now()localmente - Marca el pase como usado en el cache local
- Muestra la confirmacion al anfitrion
- Encola la operacion en la cola offline para sincronizacion posterior
Respuesta de validacion exitosa
| Campo | Descripcion |
|---|---|
valid | true |
guest.name | Nombre completo del invitado |
guest.tableNumber | Numero de mesa asignada |
guest.companionCount | Numero de acompanantes |
guest.dietaryRestrictions | Restricciones alimentarias |
guest.notes | Notas adicionales |
checkInTime | Timestamp del check-in |
7. Flujo Completo: Generar, Escanear, Validar
8. Cola Offline y Sincronizacion
La cola offline garantiza que ningun check-in se pierda aunque no haya conexion en el momento del escaneo.
Estructura de la cola
Cada operacion encolada contiene:
| Campo | Descripcion |
|---|---|
id | UUID unico de la operacion |
type | QR_CHECK_IN |
payload | { code, eventId, scannedAt, scannedBy } |
createdAt | Momento del escaneo offline |
retryCount | Numero de intentos de sincronizacion |
Proceso de sincronizacion
Manejo de conflictos
El escenario de conflicto mas comun es: dos dispositivos escanean el mismo pase offline. Cuando ambos sincronizan, el segundo recibe un 409 Conflict. En ese caso:
- El sistema mantiene el primer check-in como valido (el que llego primero al servidor)
- El segundo dispositivo recibe una notificacion de conflicto con el timestamp del check-in original
- El conflicto se registra en
AuditLogpara revision posterior
Almacenamiento segun plataforma
| Plataforma | Tecnologia | Clave de almacenamiento |
|---|---|---|
| App nativa | AsyncStorage | nvito_offline_queue |
| PWA | IndexedDB | Tabla offline-queue |
Hook de sincronizacion
En la app nativa, useOfflineSync monitorea el estado de conectividad:
- Escucha cambios de red via
ConnectivityContext - Cuando
isConnectedcambia defalseatrue, inicia sincronizacion - Procesa items en orden FIFO (primero el mas antiguo)
- Muestra un badge con el conteo de items pendientes en la UI del scanner
9. Casos de Error
El sistema maneja multiples escenarios de error con mensajes claros para el anfitrion:
| Escenario | Codigo HTTP | Mensaje al HOST | Detalle |
|---|---|---|---|
| Pase ya utilizado | 409 | "Pase ya escaneado" | Muestra usedAt y nombre de quien lo escaneo |
| Pase expirado | 410 | "Pase expirado" | Muestra expiresAt y opcion de regenerar |
| Pase no encontrado | 404 | "Codigo QR invalido" | Posible QR falso o de otro sistema |
| Evento no activo | 403 | "Evento no activo" | El evento no esta en estado ACTIVE |
| Guest eliminado | 404 | "Invitado no encontrado" | El guest fue removido de la lista |
| Tipo VIP sin permiso | 403 | "Acceso VIP no autorizado" | El guest no tiene atributo VIP |
UI de error
Cada error muestra un indicador visual distinto en la pantalla del scanner:
- Pase valido: Fondo verde, nombre del invitado, numero de mesa, efecto haptico suave
- Pase ya usado: Fondo amarillo, nombre del invitado, hora del primer escaneo
- Pase invalido/expirado: Fondo rojo, mensaje de error, sin datos de invitado
10. Archivos Clave
nvito-api (Backend)
| Archivo | Responsabilidad |
|---|---|
qr-passes.service.ts | Orquestador: generacion, listado, regeneracion de pases |
qr-generation.service.ts | Generacion de codigo unico e imagen QR |
qr-check-in.service.ts | Validacion de pases y registro de check-in |
qr-passes.controller.ts | Endpoints REST para pases QR |
nvito-client (App Nativa)
| Archivo | Responsabilidad |
|---|---|
app/(app)/(host)/scanner.tsx | Pantalla principal del scanner |
scanner/hooks/use-scanner.ts | Logica de escaneo y validacion |
scanner/stats-header.tsx | Conteo en tiempo real de check-ins |
src/hooks/queries/use-qr-passes.ts | Hook de React Query para pases |
src/storage/offline-queue.ts | Cola FIFO en AsyncStorage |
src/hooks/use-offline-sync.ts | Sincronizacion automatica al reconectar |
nvito-pwa (Progressive Web App)
| Archivo | Responsabilidad |
|---|---|
src/app/(app)/(host)/scanner/page.tsx | Pantalla del scanner PWA |
src/hooks/use-qr-scanner.ts | Wrapper de html5-qrcode |
src/lib/offline/indexed-db.ts | Acceso a IndexedDB |
src/lib/offline/offline-queue.ts | Cola offline en IndexedDB |