/** * QR System Demonstration Script * Shows QR validation, backup code generation, and manual entry flow */ import { QRValidator, createQRValidator, getQRErrorMessage, formatBackupCode } from '../src/lib/qr-validator'; import { QRGenerator, createQRGenerator, generateTicketQR, generateQRForFormat, validateQRData } from '../src/lib/qr-generator'; console.log('🎫 Black Canyon Tickets - QR System Demonstration\n'); // Sample ticket data const sampleTicketData = { ticketId: '550e8400-e29b-41d4-a716-446655440000', eventId: 'evt_789012345', zone: 'GA', seat: 'A12', expiresInDays: 30 }; console.log('1. Ticket Data Validation'); console.log('========================='); const validation = validateQRData(sampleTicketData); console.log(`✅ Valid: ${validation.valid}`); if (!validation.valid) { console.log(`❌ Errors: ${validation.errors.join(', ')}`); } console.log(); console.log('2. QR Code Generation'); console.log('===================='); const generator = createQRGenerator(); // Generate QR for different formats const formats = ['email', 'print', 'thermal', 'wallet'] as const; for (const format of formats) { const { qr, svg } = generateQRForFormat(sampleTicketData, format); console.log(`📱 ${format.toUpperCase()} Format:`); console.log(` QR Data: ${qr.qrData.substring(0, 50)}...`); console.log(` Backup Code: ${qr.backupCode}`); console.log(` Format: ${qr.metadata.format}`); console.log(` Generated: ${new Date(qr.metadata.generatedAt).toLocaleString()}`); if (qr.metadata.expiresAt) { console.log(` Expires: ${new Date(qr.metadata.expiresAt).toLocaleString()}`); } console.log(); } console.log('3. QR Code Validation'); console.log('===================='); const validator = createQRValidator(); // Test different QR formats const testQRs = [ // Simple format `TICKET_${sampleTicketData.ticketId}`, // Signed token format (generated) generateTicketQR(sampleTicketData).qrData, // Invalid formats 'INVALID_QR_CODE', 'BCT.v2.invalid.signature', 'TICKET_not-a-uuid' ]; testQRs.forEach((qr, index) => { console.log(`Test ${index + 1}: ${qr.substring(0, 40)}${qr.length > 40 ? '...' : ''}`); const result = validator.validateQR(qr); console.log(` ✅ Valid: ${result.valid}`); console.log(` 📱 Format: ${result.format}`); if (result.ticketId) { console.log(` 🎫 Ticket ID: ${result.ticketId}`); } if (result.eventId) { console.log(` 🎪 Event ID: ${result.eventId}`); } if (!result.valid) { console.log(` ❌ Error: ${getQRErrorMessage(result)}`); } if (result.metadata) { if (result.metadata.expiresAt) { const expires = new Date(result.metadata.expiresAt * 1000); console.log(` ⏰ Expires: ${expires.toLocaleString()}`); } if (result.metadata.zone) { console.log(` 🏟️ Zone: ${result.metadata.zone}`); } if (result.metadata.seat) { console.log(` 💺 Seat: ${result.metadata.seat}`); } } console.log(); }); console.log('4. Backup Code Validation'); console.log('========================='); const testBackupCodes = [ '55440000', // Valid (last 8 chars of ticket ID) '1234ABCD', // Valid hex format '12345', // Too short '123456789', // Too long '1234GHIJ', // Invalid characters '' // Empty ]; testBackupCodes.forEach((code, index) => { console.log(`Test ${index + 1}: "${code}"`); const result = validator.validateBackupCode(code); console.log(` ✅ Valid: ${result.valid}`); if (result.valid && result.normalizedCode) { console.log(` 🔢 Normalized: ${formatBackupCode(result.normalizedCode)}`); } console.log(); }); console.log('5. Backup Code Generation'); console.log('========================'); const testTicketIds = [ sampleTicketData.ticketId, '123e4567-e89b-12d3-a456-426614174000', 'invalid-uuid' ]; testTicketIds.forEach((ticketId, index) => { console.log(`Test ${index + 1}: ${ticketId}`); try { const backupCode = generator.generateBackupCode(ticketId); console.log(` 🔢 Backup Code: ${formatBackupCode(backupCode)}`); } catch (error) { console.log(` ❌ Error: ${error instanceof Error ? error.message : 'Unknown error'}`); } console.log(); }); console.log('6. QR Format Detection'); console.log('======================'); const mixedQRs = [ 'TICKET_550e8400-e29b-41d4-a716-446655440000', 'BCT.v2.eyJ0aWQiOiIxMjMiLCJlaWQiOiI0NTYifQ.signature', '550e8400-e29b-41d4-a716-446655440000', // Legacy UUID only 'MANUAL_55440000' ]; mixedQRs.forEach((qr, index) => { console.log(`QR ${index + 1}: ${qr}`); const ticketId = validator.extractTicketId(qr); if (ticketId) { console.log(` 🎫 Extracted Ticket ID: ${ticketId}`); const backupCode = generator.generateBackupCode(ticketId); console.log(` 🔢 Backup Code: ${formatBackupCode(backupCode)}`); } else { console.log(` ❌ Could not extract ticket ID`); } console.log(); }); console.log('7. Production Recommendations'); console.log('============================='); console.log('✅ Use signed tokens (BCT.v2.{payload}.{signature}) for security'); console.log('✅ Implement proper HMAC-SHA256 signatures in production'); console.log('✅ Rotate signing keys quarterly'); console.log('✅ Use Error Correction Level M (15%) for most use cases'); console.log('✅ Use Error Correction Level H (30%) for thermal printers'); console.log('✅ Include backup codes on all printed tickets'); console.log('✅ Train gate staff on manual entry procedures'); console.log('✅ Test QR codes across different devices and lighting conditions'); console.log('✅ Monitor QR scan success rates and manual entry frequency'); console.log(); console.log('🚀 QR System Demo Complete!'); console.log('Visit /scanner?eventId=test-event-123 to test the scanner interface'); console.log(`Server running at: http://localhost:5174`); console.log(); export {};