real-scanner
v1.1.2
Published
A secure QR/Barcode scanner package with modal interface and API key verification
Maintainers
Readme
Real Scanner Package
A powerful, secure QR/Barcode scanner package with modal interface and API key verification. Built with TypeScript and powered by ZXing library for reliable barcode detection across multiple formats.
Features
- 🔐 Secure Authentication - API key verification with backend validation
- 📱 Modal Interface - Beautiful, responsive modal scanner with auto-sizing
- 🎯 Multi-Format Support - QR codes, Code 128, EAN-13, UPC, Data Matrix, and more
- 📊 Auto Logging - Automatic scan logging with metadata tracking
- 🔧 TypeScript Ready - Full type definitions and IntelliSense support
- 📷 Optimized Camera - Enhanced camera settings with autofocus and HD resolution
- 🎨 Responsive Design - Mobile-friendly modal with adaptive sizing
- ⚡ Performance Optimized - Non-blocking scan operations and efficient processing
- 🛠️ Easy Integration - Simple API with automatic initialization
Installation
npm install real-scannerQuick Start
1. Basic Setup & Initialization
import RealScanner from 'real-scanner';
// Initialize scanner with auto-verification
const scanner = new RealScanner({
accessKey: 'ak_your_access_key_here',
secret: 'your_secret_here',
open_model: true, // Enable modal interface (default: true)
autoLogScans: true, // Auto-log all scans (default: true)
onScanSuccess: (result) => {
console.log('✅ Scan successful!');
console.log('Code:', result.getText());
console.log('Format:', result.getBarcodeFormat().toString());
// Scanner will auto-close the modal after successful scan
},
onScanError: (error) => {
console.error('❌ Scan error:', error);
},
onAuthError: (error) => {
console.error('🔒 Authentication failed:', error);
// Handle auth errors (show login form, etc.)
}
});
// Scanner automatically initializes and verifies credentials2. Open Scanner Modal
// Simple modal opening
const openScannerButton = document.getElementById('scan-btn');
openScannerButton?.addEventListener('click', async () => {
try {
await scanner.openScanner();
} catch (error) {
console.error('Failed to open scanner:', error);
}
});
// Advanced modal with metadata tracking
await scanner.openScanner({
metadata: [
{ key: 'location', value: 'warehouse-A' },
{ key: 'operator', value: 'john-doe' },
{ key: 'shift', value: 'morning' }
],
onClose: () => {
console.log('📱 Scanner modal closed');
}
});API Reference
ScannerConfig Interface
interface ScannerConfig {
accessKey: string; // Required: Your API access key (format: ak_xxxxx)
secret: string; // Required: Your API secret key
serverUrl?: string; // Optional: Backend server URL (default: https://scanner-backend-iy9y.onrender.com)
open_model?: boolean; // Optional: Enable modal interface (default: true)
onScanSuccess?: (result: Result) => void; // Optional: Called when scan succeeds
onScanError?: (error: Error) => void; // Optional: Called on scan errors
onAuthError?: (error: string) => void; // Optional: Called on auth failures
hints?: Map<DecodeHintType, any>; // Optional: ZXing decoder hints for custom formats
autoLogScans?: boolean; // Optional: Auto-log all scans to backend (default: true)
}Additional Interfaces
interface ScanModalOptions {
metadata?: MetadataField[]; // Optional: Custom metadata to attach to scans
onClose?: () => void; // Optional: Callback when modal closes
width?: string; // Optional: Modal width (auto-responsive)
height?: string; // Optional: Modal height (auto-responsive)
}
interface MetadataField {
key: string; // Metadata key (e.g., 'location', 'operator')
value: string; // Metadata value
}
interface ScanLogData {
scannedCode: string;
scanType: 'QR' | 'BARCODE' | 'OTHER';
scanResult?: any;
deviceInfo?: any;
timestamp?: string;
metadata?: Record<string, string>;
}Modal Options
interface ScanModalOptions {
metadata?: MetadataField[]; // Optional: Additional data to log with scans
onClose?: () => void; // Optional: Callback when modal is closed
width?: string; // Optional: Modal width (default: '400px')
height?: string; // Optional: Modal height (default: '500px')
}Core Methods
openScanner(options?: ScanModalOptions): Promise<void>
Opens the modal scanner interface. This is the primary method for scanning.
// Simple usage
await scanner.openScanner();
// With metadata and callbacks
await scanner.openScanner({
metadata: [
{ key: 'department', value: 'inventory' },
{ key: 'session_id', value: 'abc123' }
],
onClose: () => {
console.log('Scanning session ended');
}
});setCredentialsAndVerify(accessKey: string, secret: string): Promise<boolean>
Manually set and verify API credentials (alternative to constructor approach).
try {
const isValid = await scanner.setCredentialsAndVerify('ak_newkey', 'newsecret');
if (isValid) {
console.log('✅ Credentials updated and verified!');
}
} catch (error) {
console.error('❌ Verification failed:', error);
}initialize(): Promise<boolean>
Verifies the API credentials with the backend. Called automatically in constructor.
// Manual initialization (if needed)
try {
const isValid = await scanner.initialize();
console.log('Scanner ready:', isValid);
} catch (error) {
console.error('Initialization failed:', error);
}setMetadata(metadata: MetadataField[]): void
Set metadata that will be attached to all future scans.
scanner.setMetadata([
{ key: 'store_id', value: 'store_001' },
{ key: 'cashier', value: 'jane_doe' }
]);getMetadata(): Record<string, string>
Get the currently set metadata.
const currentMetadata = scanner.getMetadata();
console.log('Current metadata:', currentMetadata);stopScanning(): void
Stops scanning and releases camera resources.
scanner.stopScanning();closeScanner(): void
Closes the scanner modal and stops scanning.
scanner.closeScanner();isReady(): boolean
Checks if scanner is initialized and ready to use.
if (scanner.isReady()) {
console.log('✅ Scanner ready - can open modal');
} else {
console.log('⏳ Scanner not ready - check credentials');
}isScannerActive(): boolean
Checks if scanner is currently active/scanning.
if (scanner.isScannerActive()) {
console.log('📷 Scanner is currently active');
}getUserId(): string | null
Gets the current user ID from the verified API key.
const userId = scanner.getUserId();
if (userId) {
console.log('User ID:', userId);
}destroy(): void
Cleans up all resources.
scanner.destroy();Complete Usage Examples
1. Simple Modal Scanner
import RealScanner from 'real-scanner';
// Initialize scanner
const scanner = new RealScanner({
accessKey: 'ak_your_key_here',
secret: 'your_secret_here',
onScanSuccess: (result) => {
console.log(`✅ Scanned: ${result.getText()}`);
console.log(`📊 Format: ${result.getBarcodeFormat().toString()}`);
// Modal automatically closes after successful scan
// Scan is automatically logged to backend
},
onAuthError: (error) => {
alert(`Authentication failed: ${error}`);
}
});
// Add scan button to your page
const scanButton = document.getElementById('scan-btn');
scanButton.addEventListener('click', async () => {
if (scanner.isReady()) {
await scanner.openScanner();
} else {
alert('Scanner not ready. Check your credentials.');
}
});2. E-commerce Integration Example
import RealScanner from 'real-scanner';
class ProductScanner {
private scanner: RealScanner;
constructor() {
this.scanner = new RealScanner({
accessKey: process.env.SCANNER_ACCESS_KEY!,
secret: process.env.SCANNER_SECRET!,
onScanSuccess: (result) => {
this.handleProductScan(result.getText());
},
onAuthError: (error) => {
console.error('Scanner auth failed:', error);
this.showAuthError();
}
});
}
async scanProduct(storeId: string, employeeId: string) {
// Set metadata for tracking
this.scanner.setMetadata([
{ key: 'store_id', value: storeId },
{ key: 'employee_id', value: employeeId },
{ key: 'action', value: 'product_scan' }
]);
// Open scanner with custom close handler
await this.scanner.openScanner({
onClose: () => {
console.log('Product scan session ended');
}
});
}
private async handleProductScan(barcode: string) {
try {
// Look up product in your database
const product = await fetch(`/api/products/${barcode}`).then(r => r.json());
if (product) {
this.displayProduct(product);
} else {
alert('Product not found in inventory');
}
} catch (error) {
console.error('Product lookup failed:', error);
}
}
private displayProduct(product: any) {
// Update UI with product info
document.getElementById('product-name')!.textContent = product.name;
document.getElementById('product-price')!.textContent = `$${product.price}`;
document.getElementById('product-stock')!.textContent = `${product.stock} in stock`;
}
private showAuthError() {
// Handle authentication error in UI
const errorDiv = document.getElementById('error-message');
errorDiv!.textContent = 'Scanner authentication failed. Please contact support.';
errorDiv!.style.display = 'block';
}
}
// Usage
const productScanner = new ProductScanner();
const scanBtn = document.getElementById('scan-product-btn');
scanBtn?.addEventListener('click', () => {
productScanner.scanProduct('store_001', 'emp_123');
});Scan from Image File
const scanner = new RealScanner({
accessKey: 'your-access-key',
secret: 'your-secret-key'
});
await scanner.initialize();
const fileInput = document.getElementById('file-input');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
const imageUrl = URL.createObjectURL(file);
try {
const result = await scanner.scanImage(imageUrl);
console.log('Scanned:', result.getText());
// Automatically logged to backend
} catch (error) {
console.error('No barcode found');
}
});Disable Auto-Logging and Log Manually
const scanner = new RealScanner({
accessKey: 'your-access-key',
secret: 'your-secret-key',
autoLogScans: false, // Disable automatic logging
onScanSuccess: async (result) => {
// Custom logic before logging
const shouldLog = confirm('Log this scan?');
if (shouldLog) {
await scanner.manualLogScan({
scannedCode: result.getText(),
scanType: 'QR',
scanResult: {
format: result.getBarcodeFormat().toString(),
timestamp: new Date().toISOString()
}
});
}
}
});
await scanner.initialize();
await scanner.startScanning();View Scan History
const scanner = new RealScanner({
accessKey: 'your-access-key',
secret: 'your-secret-key'
});
await scanner.initialize();
// Get recent scans
const logsData = await scanner.getScanLogs(1, 10);
console.log(`Total scans: ${logsData.pagination.total}`);
logsData.logs.forEach(log => {
console.log(`${log.scan_type}: ${log.scanned_code} at ${log.created_at}`);
});Display Statistics Dashboard
const scanner = new RealScanner({
accessKey: 'your-access-key',
secret: 'your-secret-key'
});
await scanner.initialize();
const stats = await scanner.getStats();
console.log('API Key:', stats.apiKey.name);
console.log('Total Scans:', stats.statistics.totalScans);
console.log('Recent Scans (24h):', stats.statistics.recentScans24h);
console.log('QR Codes:', stats.statistics.scansByType.QR || 0);
console.log('Barcodes:', stats.statistics.scansByType.BARCODE || 0);Select Camera Device
const scanner = new RealScanner({
accessKey: 'your-access-key',
secret: 'your-secret-key'
});
await scanner.initialize();
// Get available devices
const devices = await scanner.getVideoDevices();
console.log('Available cameras:', devices);
// Use specific camera
await scanner.startScanning({
deviceId: devices[1].deviceId // Use second camera
});Backend Integration
Server Requirements
Your backend server must implement the following endpoints:
1. Verify API Key
POST /api/v1/verify-key
Request:
{
"accessKey": "your-access-key",
"secret": "your-secret-key"
}Response (Success):
{
"success": true,
"valid": true,
"message": "API key verified successfully",
"data": {
"id": "key-id",
"user_id": "user-id",
"name": "My API Key",
"access_key": "your-access-key",
"status": "active"
}
}2. Log Scan
POST /api/v1/log-scan
Headers:
Authorization: Bearer your-access-key
Content-Type: application/jsonRequest:
{
"scannedCode": "ABC123",
"scanType": "QR",
"scanResult": {
"format": "QR_CODE",
"text": "ABC123"
},
"deviceInfo": {
"userAgent": "...",
"platform": "..."
},
"ipAddress": "192.168.1.1",
"timestamp": "2025-01-01T00:00:00.000Z"
}Response:
{
"success": true,
"message": "Scan logged successfully",
"data": {
"scanLogId": "log-id",
"timestamp": "2025-01-01T00:00:00.000Z"
}
}3. Get Statistics (Optional)
GET /api/v1/stats
Headers:
Authorization: Bearer your-access-key4. Get Scan Logs (Optional)
GET /api/v1/scan-logs?page=1&limit=50
Headers:
Authorization: Bearer your-access-keyScan Types
The scanner automatically categorizes scans into three types:
- QR: QR_CODE, DATA_MATRIX, AZTEC, MAXICODE
- BARCODE: EAN_13, EAN_8, UPC_A, UPC_E, CODE_128, CODE_39, CODE_93, CODABAR, ITF
- OTHER: All other formats
Automatic Scan Logging
By default, every successful scan is automatically logged to your backend with comprehensive data:
Logged Data Includes:
- 📝 Scanned code text
- 🔖 Scan type (QR/BARCODE/OTHER)
- 📊 Barcode format (CODE_128, QR_CODE, etc.)
- 🖥️ Device information (user agent, platform, screen resolution)
- ⏰ Timestamp (ISO format)
- 🏷️ Custom metadata (if provided)
- 📍 IP address and session info
Example logged data:
{
"scannedCode": "ABC123456789",
"scanType": "BARCODE",
"scanResult": {
"format": "CODE_128",
"text": "ABC123456789",
"rawBytes": 12
},
"deviceInfo": {
"userAgent": "Mozilla/5.0...",
"platform": "MacIntel",
"language": "en-US",
"screenResolution": "1920x1080"
},
"metadata": {
"location": "warehouse-A",
"operator": "john-doe",
"shift": "morning"
},
"timestamp": "2025-11-30T10:15:30.000Z"
}Disable automatic logging:
const scanner = new RealScanner({
accessKey: 'ak_your_key',
secret: 'your_secret',
autoLogScans: false // Disable auto-logging
});Error Handling
const scanner = new RealScanner({
accessKey: 'your-key',
secret: 'your-secret'
});
try {
await scanner.initialize();
await scanner.startScanning();
} catch (error) {
if (error.message === 'Invalid API credentials') {
console.error('Please check your access key and secret');
} else {
console.error('Scanner error:', error);
}
}Security Notes
- Access key and secret are transmitted securely via HTTPS
- Invalid credentials trigger alerts and prevent scanner usage
- All scanning operations are blocked until credential verification succeeds
- Scans are logged with authentication headers
Performance Features
- 🚀 Non-blocking Operations - Scan logging doesn't block the scanning loop
- 📱 Responsive Modal - Auto-adapts to mobile/desktop (100vh mobile, 80vh desktop)
- 🎯 Optimized Camera Settings - 1920x1080 resolution with autofocus
- ⚡ Fast Detection - Enhanced ZXing hints for quick barcode recognition
- 🔄 Efficient Scanning - Suppresses "not found" errors to reduce noise
- 📦 Lightweight - Minimal dependencies, TypeScript compiled
Browser Compatibility
- ✅ Chrome/Edge 56+ (Recommended)
- ✅ Firefox 52+
- ✅ Safari 11+
- ✅ Mobile browsers (iOS Safari, Chrome Mobile)
- 🔒 Requires HTTPS for camera access (except localhost development)
- 📱 Mobile responsive - Optimized for touch devices
Troubleshooting
Common Issues
Scanner not opening:
// Check if scanner is ready
if (!scanner.isReady()) {
console.log('Scanner not ready - check credentials');
}
// Check browser console for auth errorsCamera permission denied:
- Ensure you're using HTTPS (required for camera access)
- Check browser camera permissions in settings
- Try refreshing the page and allowing camera access
Performance warnings in console:
- These are normal ZXing library warnings
- The scanner is optimized to prevent blocking operations
- No action needed from your side
Scans not being logged:
// Check if auto-logging is enabled
const scanner = new RealScanner({
accessKey: 'your_key',
secret: 'your_secret',
autoLogScans: true // Make sure this is true
});
// Check network requests in browser dev toolsModal not responsive on mobile:
- The modal automatically adapts to screen size
- On mobile: 100vw x 100vh (fullscreen)
- On desktop: 80vh x 80vh (windowed)
License
MIT
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
Support
For issues and questions, please open an issue on GitHub or contact support.
