Docs

Flujo de Check-in con QR

Generación, escaneo y validación de pases QR para check-in en eventos

PublicadoMarzo 2026Equipo de desarrollo, arquitectos, stakeholders

1. Vision General

El check-in con QR es la funcionalidad central del día del evento. Permite al anfitrion (o a un colaborador con rol HOST) verificar la entrada de cada invitado escaneando un código QR único desde la app movil o la PWA.

El sistema está disenado para funcionar incluso sin conexión a internet, un escenario común en salones de eventos, haciendas o locaciones rurales. Los pases se pre-cargan con anticipacion y las validaciones se ejecutan localmente, encolandose para sincronización posterior cuando la conexión se restablezca.

Numeros clave

ConceptoDetalle
Tiempo de validación online< 200ms
Tiempo de validación offline< 50ms (local)
Vigencia del paseConfigurable, por defecto hasta fin del evento
Formato del código QRString alfanumerico de 24 caracteres
Formato de imagenPNG Base64, 300x300px

2. Generación de Pases QR

La generación de pases QR es automática y se dispara en dos momentos:

  1. Al agregar invitados: Cuando se importan invitados via Excel o se crean manualmente desde el admin, el sistema genera automáticamente un pase QR para cada uno
  2. Bajo demanda: El anfitrion puede regenerar pases desde el admin o la app movil

Proceso de generación

  1. El admin o la app llama a POST /v1/qr-passes/generate con { guestId, eventId, type }
  2. QRPassesService.generate() ejecuta:
    • Verifica que el guest existe y pertenece al evento
    • Genera un código único de 24 caracteres alfanumericos con crypto.randomBytes()
    • Genera la imagen QR en formato PNG Base64 (300x300px) usando la libreria qrcode
    • Crea el registro QRPass en la base de datos con estado ACTIVE
  3. Retorna el pase con código e imagen al cliente

Modelo QRPass

CampoTipoDescripción
idUUIDIdentificador único
guestIdUUIDInvitado propietario del pase
eventIdUUIDEvento asociado
codeString (24)Código alfanumerico único
typeEnumENTRY, EXIT, VIP
imageBase64TextImagen QR en PNG Base64
isUsedBooleanSi el pase ya fue escaneado
usedAtDateTime (nullable)Momento del escaneo
scannedByUUID (nullable)ID del HOST que escaneo
checkInMethodEnumQR_SCAN, MANUAL, OFFLINE_SYNC
expiresAtDateTimeMomento de expiración
createdAtDateTimeMomento de creación

3. Tipos de Pase

El sistema soporta tres tipos de pase QR, cada uno con un proposito diferente:

TipoPropositoValidación especial
ENTRYEntrada general al eventoNinguna adicional
EXITRegistro de salidaSolo válido si el guest ya hizo check-in
VIPAcceso a areas restringidas (mesa VIP, backstage)Verifica atributo VIP del guest

El tipo más común es ENTRY, que se genera automáticamente 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 conexión, los pases QR se pre-cargan en el dispositivo con anticipacion.

En la app nativa (nvito-client)

  • useQRPasses hook con staleTime: 10 * 60 * 1000 (10 minutos) y gcTime: 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 conexión
  • 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
  • useOfflineData hook sincroniza los pases desde el BFF cuando hay conexión
  • La PWA puede funcionar como Service Worker para interceptar requests offline

Estructura del cache offline

Cada pase en cache contiene:

  • code: El código de 24 caracteres (para búsqueda local)
  • guestId: Para actualizar el estado del invitado
  • guestName: Para mostrar en la UI de confirmación
  • type: Tipo de pase
  • isUsed: Estado actual
  • tableNumber: Mesa asignada (si existe)
  • dietaryRestrictions: Restricciones alimentarias (para informar al staff)

5. Escaneo de Códigos QR

El escaneo utiliza tecnologias diferentes según la plataforma:

App nativa: expo-camera

La app usa expo-camera con el modo de escaneo de códigos de barras:

  • Escaneo continuo en tiempo real
  • Feedback háptico via expo-haptics al detectar un código
  • Overlay visual con guia de encuadre
  • Linterna integrada para escaneo en condiciones de poca luz
  • StatsHeader muestra 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 está disponible

6. Validación del Pase

Una vez escaneado el código, la validación sigue un proceso estricto.

Validación online

  1. La app envia POST /v1/qr-passes/validate con { code, eventId }
  2. QRCheckInService.validate() ejecuta:
    • Busca el pase por code en la tabla qr_passes
    • Verifica que eventId coincida
    • Verifica que isUsed === false
    • Verifica que expiresAt > now()
    • Marca el pase como usado: isUsed = true, usedAt = now(), scannedBy = hostUserId, checkInMethod = QR_SCAN
  3. Actualiza el estado del guest: checkedIn = true, checkedInAt = now()
  4. Emite el evento de dominio GuestCheckedInEvent
  5. Crea un registro en AuditLog con tipo GUEST_CHECK_IN
  6. Retorna la confirmación con datos del invitado (nombre, mesa, restricciones)

Validación offline

  1. La app busca el código en el cache local (SecureStore o IndexedDB)
  2. Verifica isUsed === false y expiresAt > now() localmente
  3. Marca el pase como usado en el cache local
  4. Muestra la confirmación al anfitrion
  5. Encola la operación en la cola offline para sincronización posterior

Respuestá de validación exitosa

CampoDescripción
validtrue
guest.nameNombre completo del invitado
guest.tableNumberNumero de mesa asignada
guest.companionCountNumero de acompanantes
guest.dietaryRestrictionsRestricciones alimentarias
guest.notesNotas adicionales
checkInTimeTimestamp del check-in

7. Flujo Completo: Generar, Escanear, Validar

8. Cola Offline y Sincronización

La cola offline garantiza que ningún check-in se pierda aunque no haya conexión en el momento del escaneo.

Estructura de la cola

Cada operación encolada contiene:

CampoDescripción
idUUID único de la operación
typeQR_CHECK_IN
payload{ code, eventId, scannedAt, scannedBy }
createdAtMomento del escaneo offline
retryCountNumero de intentos de sincronización

Proceso de sincronización

Manejo de conflictos

El escenario de conflicto más común es: dos dispositivos escanean el mismo pase offline. Cuando ambos sincronizan, el segundo recibe un 409 Conflict. En ese caso:

  1. El sistema mantiene el primer check-in como válido (el que llego primero al servidor)
  2. El segundo dispositivo recibe una notificación de conflicto con el timestamp del check-in original
  3. El conflicto se registra en AuditLog para revision posterior

Almacenamiento según plataforma

PlataformaTecnologiaClave de almacenamiento
App nativaAsyncStoragenvito_offline_queue
PWAIndexedDBTabla offline-queue

Hook de sincronización

En la app nativa, useOfflineSync monitorea el estado de conectividad:

  • Escucha cambios de red via ConnectivityContext
  • Cuando isConnected cambia de false a true, inicia sincronización
  • Procesa items en orden FIFO (primero el más antiguo)
  • Muestra un badge con el conteo de items pendientes en la UI del scanner

9. Casos de Error

El sistema maneja múltiples escenarios de error con mensajes claros para el anfitrion:

EscenarioCódigo HTTPMensaje al HOSTDetalle
Pase ya utilizado409"Pase ya escaneado"Muestra usedAt y nombre de quien lo escaneo
Pase expirado410"Pase expirado"Muestra expiresAt y opción de regenerar
Pase no encontrado404"Código QR invalido"Posible QR falso o de otro sistema
Evento no activo403"Evento no activo"El evento no está en estado ACTIVE
Guest eliminado404"Invitado no encontrado"El guest fue removido de la lista
Tipo VIP sin permiso403"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, número 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)

ArchivoResponsabilidad
qr-passes.service.tsOrquestador: generación, listado, regeneración de pases
qr-generation.service.tsGeneración de código único e imagen QR
qr-check-in.service.tsValidación de pases y registro de check-in
qr-passes.controller.tsEndpoints REST para pases QR

nvito-client (App Nativa)

ArchivoResponsabilidad
app/(app)/(host)/scanner.tsxPantalla principal del scanner
scanner/hooks/use-scanner.tsLógica de escaneo y validación
scanner/stats-header.tsxConteo en tiempo real de check-ins
src/hooks/queries/use-qr-passes.tsHook de React Query para pases
src/storage/offline-queue.tsCola FIFO en AsyncStorage
src/hooks/use-offline-sync.tsSincronización automática al reconectar

nvito-pwa (Progressive Web App)

ArchivoResponsabilidad
src/app/(app)/(host)/scanner/page.tsxPantalla del scanner PWA
src/hooks/use-qr-scanner.tsWrapper de html5-qrcode
src/lib/offline/indexed-db.tsAcceso a IndexedDB
src/lib/offline/offline-queue.tsCola offline en IndexedDB
Esta pagina fue util?