bitdreamit/laravel-qz-tray

Enterprise silent printing with QZ Tray for Laravel - Zero configuration, automatic certificate, browser caching

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Language:JavaScript

pkg:composer/bitdreamit/laravel-qz-tray

v0.1.1 2026-01-13 16:36 UTC

This package is auto-updated.

Last update: 2026-01-13 16:38:21 UTC


README

Latest Version Total Downloads License PHP Version Laravel Version

🎯 What is This Package?

Laravel QZ Tray is a complete silent printing solution that connects your Laravel application to desktop printers via QZ Tray. It allows you to print directly from the browser without print dialogs, with smart caching, printer memory, and automatic fallback.

Perfect for: POS systems, receipt printing, label printing, invoices, reports, and any application needing silent, automated printing.

📁 FINAL PACKAGE STRUCTURE

laravel-qz-tray/
├── composer.json                    # Package configuration
├── LICENSE                          # MIT License
├── README.md                        # This documentation
├── config/
│   └── qz-tray.php                  # Package configuration
├── database/
│   └── migrations/
│       └── 2024_01_01_000000_create_qz_print_jobs_table.php
├── routes/
│   ├── web.php                      # Web routes for QZ Tray
│   └── api.php                      # API routes (optional)
├── resources/
│   ├── js/
│   │   ├── smart-print.js           # Main printing library (23KB)
│   │   ├── smart-print.min.js       # Minified version (12KB)
│   │   ├── printer-switcher.js      # UI for switching printers
│   │   ├── printer-status.js        # Status indicator component
│   │   └── adapters/
│   │       ├── escpos.js            # ESC/POS thermal printer commands
│   │       ├── zpl.js               # ZPL label printer commands
│   │       └── raw-print.js         # Generic raw printing
│   └── installers/                  # QZ Tray installers
│       ├── qz-tray-windows.exe      # Windows installer
│       ├── qz-tray-linux.deb        # Linux installer
│       └── qz-tray-macos.pkg        # macOS installer
├── src/
│   ├── QzTrayServiceProvider.php    # Laravel service provider
│   ├── Console/
│   │   └── Commands/
│   │       ├── InstallQzTray.php    # Installation command
│   │       └── GenerateCertificate.php
│   └── Http/
│       └── Controllers/
│           └── QzSecurityController.php
└── storage/
    └── qz/
        ├── certificate.pem          # Auto-generated SSL certificate
        └── private-key.pem          # Auto-generated private key

🚀 Step-by-Step Installation Guide

Step 1: Install via Composer

# Install the package
composer require bitdreamit/laravel-qz-tray

Step 2: Run the Installer

# This does everything automatically:
# - Publishes config
# - Generates SSL certificate
# - Publishes JavaScript files
# - Sets up routes
php artisan qz-tray:install

What the installer does:

  1. ✅ Creates config/qz-tray.php
  2. ✅ Generates SSL certificate in storage/qz/
  3. ✅ Publishes JS files to public/vendor/qz-tray/
  4. ✅ Sets up all necessary routes
  5. ✅ Optional: Runs migrations

Step 3: Include JavaScript in Your Layout

Add to resources/views/layouts/app.blade.php:

<!DOCTYPE html>
<html>
<head>
    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">
    
    <!-- QZ Tray Library (REQUIRED) -->
    <script src="https://cdn.jsdelivr.net/npm/qz-tray@2.2.5/qz-tray.min.js"></script>
    
    <!-- Laravel QZ Tray (REQUIRED) -->
    <script src="{{ asset('vendor/qz-tray/smart-print.min.js') }}"></script>
    
    <!-- Optional Components -->
    <script src="{{ asset('vendor/qz-tray/printer-status.min.js') }}"></script>
    <script src="{{ asset('vendor/qz-tray/adapters/escpos.min.js') }}"></script>
    <script src="{{ asset('vendor/qz-tray/adapters/zpl.min.js') }}"></script>
</head>
<body>
    <!-- Your content -->
    
    <!-- Optional: Printer status indicator -->
    <div data-qz-status='{"position": "bottom-right"}'></div>
</body>
</html>

Step 4: Install QZ Tray on Client Computers

Users need QZ Tray installed on their computers. Provide download links:

<!-- In your settings page or help section -->
<div class="card">
    <div class="card-header">Install Printer Software</div>
    <div class="card-body">
        <p>Download and install QZ Tray for silent printing:</p>
        
        <a href="{{ route('qz.installer', 'windows') }}" class="btn btn-primary">
            <i class="fas fa-download"></i> Download for Windows
        </a>
        
        <a href="{{ route('qz.installer', 'linux') }}" class="btn btn-primary">
            <i class="fas fa-download"></i> Download for Linux
        </a>
        
        <a href="{{ route('qz.installer', 'macos') }}" class="btn btn-primary">
            <i class="fas fa-download"></i> Download for macOS
        </a>
        
        <p class="text-muted mt-2">
            <small>After installation, restart your browser and this page.</small>
        </p>
    </div>
</div>

🖨️ COMPLETE USAGE GUIDE

1. Basic Print Buttons

<!-- Simple print button -->
<button class="btn btn-primary" 
        data-qz-print="{{ route('invoice.pdf', $id) }}">
    <i class="fas fa-print"></i> Print Invoice
</button>

<!-- With specific printer -->
<button class="btn btn-success"
        data-qz-print="{{ route('label.pdf', $id) }}"
        data-qz-printer="Label Printer"
        data-qz-copies="2">
    Print 2 Copies
</button>

<!-- With delay (useful for chaining prints) -->
<button data-qz-print="{{ route('report.pdf', $id) }}"
        data-qz-delay="2000">
    Print with 2-second delay
</button>

2. Auto-Print on Page Load

<!-- Auto-print when page loads (great for receipts) -->
@if(session('print_receipt'))
<div data-qz-auto-print="{{ route('receipt.pdf', session('receipt_id')) }}"
     data-qz-printer="Receipt Printer"
     data-qz-delay="1000">
</div>
@endif

Controller example for auto-print:

public function storeOrder(Request $request)
{
    // Process order...
    $order = Order::create($request->all());
    
    return redirect()->route('order.confirmation', $order->id)
        ->with([
            'success' => 'Order placed successfully!',
            'auto_print' => true,
            'receipt_id' => $order->id,
        ]);
}

public function confirmation($id)
{
    $order = Order::find($id);
    
    return view('orders.confirmation', [
        'order' => $order,
        'auto_print' => session('auto_print', false),
    ]);
}

3. Advanced Printing Options

<!-- Multiple data attributes -->
<button class="btn btn-info"
        data-qz-print="{{ route('document.pdf', $id) }}"
        data-qz-printer="Laser Printer"
        data-qz-copies="3"
        data-qz-type="pdf"
        data-qz-delay="500">
    <i class="fas fa-copy"></i> Print 3 Copies
</button>

<!-- Print on click with JavaScript -->
<button class="btn btn-warning" onclick="printMyDocument()">
    Custom Print
</button>

<script>
function printMyDocument() {
    smartPrint('/documents/123.pdf', {
        printer: 'Receipt Printer',
        copies: 2,
        onComplete: function(job) {
            alert('Printed successfully!');
        }
    });
}
</script>

4. Raw Printing (Thermal Printers & Labels)

<!-- ESC/POS Thermal Receipt -->
<button class="btn btn-dark" onclick="printThermalReceipt()">
    <i class="fas fa-receipt"></i> Print Thermal Receipt
</button>

<!-- ZPL Label -->
<button class="btn btn-secondary" onclick="printShippingLabel()">
    <i class="fas fa-tag"></i> Print Shipping Label
</button>

<script>
// Thermal receipt (ESC/POS)
function printThermalReceipt() {
    const receiptData = {
        storeName: "My Store",
        receiptNumber: "12345",
        items: [
            { name: "Item 1", price: "10.00", quantity: 2 },
            { name: "Item 2", price: "15.00", quantity: 1 },
        ],
        subtotal: "35.00",
        tax: "3.50",
        total: "38.50"
    };
    
    const commands = ESCPOS.createReceipt(receiptData);
    smartPrintESC(commands, 'Thermal Printer');
}

// Shipping label (ZPL)
function printShippingLabel() {
    const labelData = {
        fromName: "John Doe",
        fromAddress: "123 Street",
        toName: "Jane Smith",
        toAddress: "456 Avenue",
        trackingNumber: "TRK123456789"
    };
    
    const zpl = ZPL.createShippingLabel(labelData);
    smartPrintZPL(zpl, 'Label Printer');
}
</script>

5. Programmatic API Usage

// Check if QZ Tray is connected
if (SmartPrint.isConnected()) {
    console.log('Ready to print!');
}

// Get current printer
const currentPrinter = await SmartPrint.getCurrentPrinter();
console.log('Current printer:', currentPrinter);

// Get all available printers
const printers = await SmartPrint.getPrinters();
console.log('Available printers:', printers);

// Set printer for current page
await SmartPrint.setPrinter('Label Printer');

// Print with options
const jobId = await SmartPrint.print('/documents/invoice.pdf', {
    printer: 'Office Printer',
    copies: 2,
    type: 'pdf'
});

// Listen for events
SmartPrint.on('connected', () => {
    console.log('Connected to QZ Tray!');
});

SmartPrint.on('job-completed', (event) => {
    console.log('Print job completed:', event.job);
    alert('Document printed successfully!');
});

SmartPrint.on('job-failed', (event) => {
    console.error('Print job failed:', event.error);
    alert('Printing failed. Please try again.');
});

6. Hotkey Feature

Default: Ctrl + Shift + P

When pressed, shows a dialog to switch printers for the current page. The selection is remembered permanently for that URL.

Hotkey Demo

7. Printer Status Indicator

<!-- Add anywhere in your layout -->
<div data-qz-status='{
    "position": "bottom-right",
    "showPrinterName": true,
    "showConnection": true,
    "autoHide": true,
    "clickToSwitch": true
}'></div>

Options:

  • position: top-left, top-right, bottom-left, bottom-right, top-center, bottom-center
  • showPrinterName: Show current printer name
  • showConnection: Show connection status
  • autoHide: Automatically hide after delay
  • clickToSwitch: Click to open printer switcher

🔧 Configuration Reference

After publishing config (php artisan vendor:publish --tag=qz-config):

config/qz-tray.php

<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Certificate Settings
    |--------------------------------------------------------------------------
    */
    'cert_path' => storage_path('qz/certificate.pem'),
    'key_path' => storage_path('qz/private-key.pem'),
    'cert_ttl' => 3600, // Cache certificate for 1 hour
    
    /*
    |--------------------------------------------------------------------------
    | Printer Settings
    |--------------------------------------------------------------------------
    */
    'default_printer' => env('QZ_DEFAULT_PRINTER', 'Receipt Printer'),
    'allow_printer_switch' => true,
    'remember_printer_per_page' => true,
    'printer_cache_duration' => 86400, // Remember for 24 hours
    
    /*
    |--------------------------------------------------------------------------
    | WebSocket Settings
    |--------------------------------------------------------------------------
    */
    'websocket' => [
        'host' => env('QZ_WEBSOCKET_HOST', '127.0.0.1'),
        'port' => env('QZ_WEBSOCKET_PORT', 8181),
        'retries' => 3,
        'timeout' => 30,
    ],
    
    /*
    |--------------------------------------------------------------------------
    | Browser Fallback
    |--------------------------------------------------------------------------
    */
    'fallback' => [
        'enabled' => true, // Fallback to browser printing if QZ fails
        'open_in_new_tab' => true,
        'show_warning' => true,
    ],
    
    /*
    |--------------------------------------------------------------------------
    | Hotkey Settings
    |--------------------------------------------------------------------------
    */
    'hotkey' => [
        'enabled' => true,
        'combination' => 'ctrl+shift+p',
        'require_confirmation' => false,
    ],
    
    /*
    |--------------------------------------------------------------------------
    | Route Settings
    |--------------------------------------------------------------------------
    */
    'routes' => [
        'prefix' => 'qz',
        'middleware' => ['web'],
        'throttle' => '60,1', // 60 requests per minute
    ],
    
    /*
    |--------------------------------------------------------------------------
    | Logging
    |--------------------------------------------------------------------------
    */
    'logging' => [
        'enabled' => env('QZ_LOGGING_ENABLED', false),
        'channel' => env('QZ_LOGGING_CHANNEL', 'stack'),
        'level' => env('QZ_LOGGING_LEVEL', 'info'),
    ],
    
    /*
    |--------------------------------------------------------------------------
    | Installer Settings
    |--------------------------------------------------------------------------
    */
    'installers' => [
        'windows' => 'qz-tray-windows.exe',
        'linux' => 'qz-tray-linux.deb',
        'macos' => 'qz-tray-macos.pkg',
    ],
];

Environment Variables (.env)

# Default printer
QZ_DEFAULT_PRINTER="Receipt Printer"

# WebSocket settings (if QZ Tray is on different machine)
QZ_WEBSOCKET_HOST=192.168.1.100
QZ_WEBSOCKET_PORT=8181

# Logging
QZ_LOGGING_ENABLED=true
QZ_LOGGING_CHANNEL=stack
QZ_LOGGING_LEVEL=info

📊 API Endpoints Reference

Endpoint Method Description Example
GET /qz/certificate GET Get SSL certificate curl /qz/certificate
POST /qz/sign POST Sign data for QZ Tray {"data": "string to sign"}
GET /qz/printers GET Get available printers curl /qz/printers
GET /qz/printer/{path} GET Get printer for path curl /qz/printer/invoices
POST /qz/printer POST Set printer for path {"printer": "HP", "path": "/invoices"}
POST /qz/print POST Log print job {"url": "/invoice.pdf", "printer": "HP"}
GET /qz/status GET System status curl /qz/status
GET /qz/health GET Health check curl /qz/health
GET /qz/installer/{os} GET Download installer /qz/installer/windows

🎨 JavaScript API Reference

Global Functions

// Print PDF/HTML from URL
smartPrint(url, options);

// Print ZPL commands
smartPrintZPL(zplCommands, printer);

// Print ESC/POS commands
smartPrintESC(escposCommands, printer);

SmartPrint Object

// Access the full API
const api = window.SmartPrint;

// Print methods
api.print(url, options);              // Print document
api.printRaw(data, type, printer);    // Print raw data
api.printZPL(zpl, printer);           // Print ZPL
api.printESC(escpos, printer);        // Print ESC/POS

// Printer management
api.getPrinters();                    // Get all printers
api.getCurrentPrinter();              // Get current printer
api.setPrinter(printer, path);        // Set printer for path
api.showPrinterSwitcher();            // Show printer switcher

// Connection
api.connect();                        // Connect to QZ Tray
api.disconnect();                     // Disconnect
api.isConnected();                    // Check connection

// Queue management
api.getQueue();                       // Get print queue
api.clearQueue();                     // Clear queue

// Settings
api.getSettings();                    // Get current settings
api.updateSettings(newSettings);      // Update settings

// Events
api.on(event, callback);              // Listen to event
api.off(event, callback);             // Remove listener

// Utility
api.clearCache();                     // Clear localStorage cache

Options Object

{
    printer: 'Printer Name',          // Specific printer
    type: 'pdf',                      // pdf, zpl, escpos, raw
    copies: 1,                        // Number of copies
    rawData: null,                    // Raw data for type='raw'
    delay: 0,                         // Delay before printing (ms)
    onComplete: function(job) {},     // Callback on success
    onError: function(error) {}       // Callback on error
}

Events

// Connection events
SmartPrint.on('connected', () => {});
SmartPrint.on('disconnected', () => {});
SmartPrint.on('connection-failed', (data) => {});

// Printer events
SmartPrint.on('printers-loaded', (data) => {});
SmartPrint.on('printer-saved', (data) => {});
SmartPrint.on('printers-error', (data) => {});

// Print job events
SmartPrint.on('job-queued', (data) => {});
SmartPrint.on('job-processing', (data) => {});
SmartPrint.on('job-completed', (data) => {});
SmartPrint.on('job-failed', (data) => {});
SmartPrint.on('fallback-print', (data) => {});

// System events
SmartPrint.on('ready', (data) => {});
SmartPrint.on('init-failed', (data) => {});
SmartPrint.on('queue-cleared', () => {});
SmartPrint.on('cache-cleared', () => {});
SmartPrint.on('settings-updated', (data) => {});

🔌 Adapters Reference

ESC/POS Adapter (Thermal Receipt Printers)

// Create receipt
const receipt = ESCPOS.createReceipt({
    storeName: "My Store",
    receiptNumber: "12345",
    items: [...],
    subtotal: "35.00",
    total: "38.50"
});

// Print receipt
ESCPOS.printReceipt(receiptData, 'Receipt Printer');

// Open cash drawer
ESCPOS.openDrawer('Receipt Printer');

// Global shortcuts
printReceipt(data, printer);      // Print receipt
printLabel(data, printer);        // Print label
openCashDrawer(printer);          // Open cash drawer

ZPL Adapter (Zebra Label Printers)

// Create shipping label
const zpl = ZPL.createShippingLabel({
    fromName: "John Doe",
    toName: "Jane Smith",
    trackingNumber: "TRK123456789"
});

// Print label
ZPL.printShippingLabel(labelData, 'Label Printer');

// Create product label
ZPL.printProductLabel(productData, 'Label Printer');

// Create QR code label
ZPL.printQRLabel(qrData, 'Label Printer');

// Global shortcuts
printShippingLabel(data, printer);
printProductLabel(data, printer);
printQRLabel(data, printer);

Raw Print Adapter (Generic Printing)

// Print formatted text
RawPrint.printText("Hello World", {
    printer: 'Printer',
    align: 'center',
    bold: true,
    fontSize: 'large'
});

// Print table
RawPrint.printTable(data, {
    printer: 'Printer',
    columns: [
        { title: 'Name', key: 'name', width: 20 },
        { title: 'Price', key: 'price', width: 10 }
    ]
});

// Print barcode
RawPrint.printBarcode('123456789', 'CODE128', 'Printer');

// Cut paper
RawPrint.cutPaper('Printer');        // Full cut
RawPrint.cutPaper('Printer', true);  // Partial cut

// Test printer
RawPrint.testPrinter('Printer');

// Global shortcuts
printText(text, options);
printTable(data, options);
printBarcode(data, type, printer);
cutPaper(printer, partial);
openCashDrawer(printer);
testPrinter(printer);

🚨 Troubleshooting Guide

Problem: QZ Tray Not Connecting

Symptoms:

  • "Connecting to QZ Tray..." message stays
  • Print buttons don't work
  • Console shows WebSocket errors

Solutions:

  1. Check if QZ Tray is installed and running

    • Look for QZ Tray icon in system tray
    • Right-click → About to check version
    • If not installed: Download from /qz/installer/windows
  2. Check WebSocket connection

    // In browser console
    SmartPrint.connect().then(connected => {
        console.log('Connected:', connected);
    });
  3. Check certificate

    # Regenerate certificate
    php artisan qz-tray:generate-certificate --force
  4. Check firewall/antivirus

    • Allow port 8181
    • Add exception for QZ Tray

Problem: Certificate Errors

Error messages:

  • "Invalid certificate"
  • "Certificate not trusted"
  • Security warnings

Solutions:

  1. Clear browser cache:

    // In console
    SmartPrint.clearCache();
    localStorage.clear();
    sessionStorage.clear();
  2. Regenerate certificate:

    php artisan vendor:publish --tag=qz-certificate --force
  3. Restart everything:

    • Restart QZ Tray
    • Restart browser
    • Clear DNS cache

Problem: Printing Fails

Symptoms:

  • Print job starts but nothing prints
  • Printer not found errors
  • Timeout errors

Solutions:

  1. Check printer status:

    // Check if printer is available
    const printers = await SmartPrint.getPrinters();
    console.log('Available printers:', printers);
  2. Try fallback printing:

    <!-- Add fallback attribute -->
    <button data-qz-print="/document.pdf"
            data-qz-fallback="true">
        Print with Fallback
    </button>
  3. Check printer configuration:

    • Verify printer is online
    • Check paper/ink levels
    • Test print from OS
  4. Enable debug mode:

    <script>
    window.QZ_CONFIG = {
        debug: true,
        endpoint: '/qz'
    };
    </script>

Problem: Auto-Print Not Working

Check:

  1. Is data-qz-auto-print div present in HTML?
  2. Is there any JavaScript error in console?
  3. Is QZ Tray connected? (SmartPrint.isConnected())
  4. Is the delay too short? Try data-qz-delay="2000"

Common Error Messages:

Error Solution
"QZ Tray library not loaded" Add <script src="https://cdn.jsdelivr.net/npm/qz-tray@2.2.5/qz-tray.min.js"></script>
"Cannot connect to QZ Tray" Install QZ Tray, check it's running
"No printer selected" Set default printer in config or use data-qz-printer
"PDF download timeout" Check PDF generation route, increase timeout
"Response is not a PDF" Ensure route returns proper PDF content-type

📋 Best Practices

1. User Experience

<!-- Show loading state -->
<button class="btn btn-primary" 
        data-qz-print="/document.pdf"
        onclick="this.innerHTML='<i class=\'fas fa-spinner fa-spin\'></i> Printing...'">
    <i class="fas fa-print"></i> Print
</button>

<!-- Provide feedback -->
<script>
SmartPrint.on('job-completed', () => {
    showNotification('Document printed successfully!', 'success');
});

SmartPrint.on('job-failed', () => {
    showNotification('Printing failed. Please try again.', 'error');
});
</script>

2. Error Handling

async function safePrint(url, options) {
    try {
        const jobId = await smartPrint(url, options);
        return { success: true, jobId };
    } catch (error) {
        console.error('Print failed:', error);
        
        // Try fallback
        if (options.fallback !== false) {
            window.open(url, '_blank');
            return { success: true, fallback: true };
        }
        
        return { success: false, error };
    }
}

3. Batch Printing

<!-- Print multiple documents -->
<button onclick="printBatch()">Print Batch</button>

<script>
async function printBatch() {
    const documents = [
        '/documents/invoice-1.pdf',
        '/documents/invoice-2.pdf',
        '/documents/receipt-1.pdf'
    ];
    
    for (let i = 0; i < documents.length; i++) {
        await smartPrint(documents[i], {
            printer: 'Laser Printer',
            delay: i * 1000 // 1 second between prints
        });
    }
}
</script>

4. Printer Selection UI

<!-- Custom printer selector -->
<div class="printer-selector">
    <select id="printerSelect" class="form-select">
        <option value="">Default Printer</option>
        <option value="Receipt Printer">Receipt Printer</option>
        <option value="Label Printer">Label Printer</option>
        <option value="Office Printer">Office Printer</option>
    </select>
    
    <button class="btn btn-sm btn-primary"
            onclick="setPrinterFromSelect()">
        Set Printer
    </button>
</div>

<script>
async function setPrinterFromSelect() {
    const select = document.getElementById('printerSelect');
    const printer = select.value;
    
    if (printer) {
        await SmartPrint.setPrinter(printer);
        alert(`Printer "${printer}" set for this page`);
    }
}
</script>

🔄 Migration from Other Printing Solutions

From: Browser Print Dialog

<!-- BEFORE: Browser print -->
<button onclick="window.print()">Print</button>

<!-- AFTER: Silent print -->
<button data-qz-print="{{ route('document.pdf', $id) }}">Print</button>

From: Raw PHP Printing

// BEFORE: Direct printing
$printer = "\\\\server\\printer";
$handle = printer_open($printer);
printer_write($handle, "Receipt content");
printer_close($handle);

// AFTER: Client-side printing
// Controller remains the same, just return PDF
return PDF::loadView('receipt', $data)->stream();

From: JavaScript Print Libraries

// BEFORE: window.print()
window.print();

// AFTER: Smart Print
smartPrint('/document.pdf', { printer: 'Default' });

📈 Performance Optimization

1. Cache Printers

// Printers are cached for 5 minutes
// Clear cache if printers change
SmartPrint.clearCache();

2. Optimize PDF Generation

// Use caching for frequently printed documents
public function pdf($id)
{
    return Cache::remember("document.{$id}.pdf", 300, function() use ($id) {
        $data = Document::find($id);
        return PDF::loadView('document', $data)->stream();
    });
}

3. Batch Processing

// Process print queue efficiently
const queue = [];
let isProcessing = false;

function addToQueue(url, options) {
    queue.push({ url, options });
    processQueue();
}

async function processQueue() {
    if (isProcessing || queue.length === 0) return;
    
    isProcessing = true;
    const job = queue.shift();
    
    try {
        await smartPrint(job.url, job.options);
    } catch (error) {
        console.error('Queue error:', error);
    } finally {
        isProcessing = false;
        processQueue();
    }
}

🔐 Security Considerations

1. Certificate Security

  • Certificates auto-generated with 4096-bit RSA
  • Stored in storage/qz/ directory
  • Regenerate periodically with php artisan qz-tray:generate-certificate

2. Rate Limiting

  • Routes are rate-limited (60 requests/minute)
  • Adjust in config: 'throttle' => '60,1'

3. CSRF Protection

  • All POST routes protected with CSRF tokens
  • Automatically included with Laravel

4. Access Control

// Add middleware to routes
Route::middleware(['auth', 'can:print'])->group(function () {
    Route::post('/qz/print', [QzSecurityController::class, 'print']);
    Route::post('/qz/printer', [QzSecurityController::class, 'setPrinter']);
});

🎯 Real-World Examples

1. POS System Receipt

<!-- POS System -->
<div class="pos-receipt">
    <h4>Order #{{ $order->id }}</h4>
    
    <button class="btn btn-success"
            data-qz-print="{{ route('pos.receipt', $order->id) }}"
            data-qz-printer="Thermal Printer">
        <i class="fas fa-receipt"></i> Print Receipt
    </button>
    
    <button class="btn btn-info"
            data-qz-print="{{ route('pos.kitchen', $order->id) }}"
            data-qz-printer="Kitchen Printer">
        <i class="fas fa-utensils"></i> Print Kitchen
    </button>
    
    <!-- Auto-print on order completion -->
    @if($order->status === 'completed')
    <div data-qz-auto-print="{{ route('pos.receipt', $order->id) }}"
         data-qz-printer="Thermal Printer"
         data-qz-delay="500">
    </div>
    @endif
</div>

2. Shipping Label System

<!-- Shipping System -->
<div class="shipping-label">
    <h5>Shipment #{{ $shipment->tracking_number }}</h5>
    
    <button class="btn btn-primary"
            onclick="printShippingLabel({{ $shipment->id }})">
        <i class="fas fa-tag"></i> Print Label
    </button>
    
    <button class="btn btn-secondary"
            onclick="printPackingSlip({{ $shipment->id }})">
        <i class="fas fa-clipboard-list"></i> Packing Slip
    </button>
</div>

<script>
async function printShippingLabel(shipmentId) {
    const response = await fetch(`/api/shipments/${shipmentId}/label-data`);
    const data = await response.json();
    
    const zpl = ZPL.createShippingLabel(data);
    smartPrintZPL(zpl, 'Zebra Printer');
}

async function printPackingSlip(shipmentId) {
    smartPrint(`/shipments/${shipmentId}/packing-slip.pdf`, {
        printer: 'Laser Printer',
        copies: 2
    });
}
</script>

3. Report Generation System

<!-- Report System -->
<div class="report-actions">
    <button class="btn btn-primary"
            data-qz-print="{{ route('reports.daily.pdf') }}"
            data-qz-printer="Report Printer">
        <i class="fas fa-print"></i> Print Daily Report
    </button>
    
    <button class="btn btn-success"
            onclick="printReportBatch()">
        <i class="fas fa-copy"></i> Print All Reports
    </button>
    
    <!-- Auto-print scheduled reports -->
    @if($autoPrintReports)
    <div id="report-batch">
        @foreach($reports as $index => $report)
        <div data-qz-auto-print="{{ route('reports.view', $report->id) }}"
             data-qz-printer="Report Printer"
             data-qz-delay="{{ $index * 5000 }}">
        </div>
        @endforeach
    </div>
    @endif
</div>

📞 Support & Community

Getting Help

  1. Check Troubleshooting Guide above
  2. Enable Debug Mode:
    <script>
    window.QZ_CONFIG = { debug: true };
    </script>
  3. Check Console: Browser developer tools

Reporting Issues

# Include debug information
SmartPrint.getStatus();
SmartPrint.getSettings();

Feature Requests

Visit GitHub repository issues section

🚀 Quick Start Cheat Sheet

1-Line Installation

composer require bitdreamit/laravel-qz-tray && php artisan qz-tray:install

Minimal Setup

<!-- In layout -->
<script src="https://cdn.jsdelivr.net/npm/qz-tray@2.2.5/qz-tray.min.js"></script>
<script src="{{ asset('vendor/qz-tray/smart-print.min.js') }}"></script>

<!-- Print button -->
<button data-qz-print="/document.pdf">Print</button>

Common Use Cases

<!-- Receipt -->
<button data-qz-print="/receipt/123.pdf" data-qz-printer="Thermal">Receipt</button>

<!-- Label -->
<button onclick="smartPrintZPL('^XA...^XZ')">Label</button>

<!-- Auto-print -->
<div data-qz-auto-print="/invoice/123.pdf" data-qz-delay="1000"></div>

Final Checklist Before Production

  • QZ Tray installed on all client machines
  • SSL certificate properly generated
  • Default printer set in config
  • JavaScript files included in layout
  • Fallback printing enabled
  • Error handling implemented
  • User permissions configured
  • Rate limiting appropriate for usage
  • Logging enabled for debugging
  • Backup/restore procedure for certificates

🎉 Congratulations!

Your Laravel application now has enterprise-grade silent printing capabilities.

Key Benefits:

  • ✅ Zero configuration setup
  • ✅ Automatic certificate management
  • ✅ Per-page printer memory
  • ✅ Smart browser caching
  • ✅ Hotkey printer switching
  • ✅ Fallback to browser printing
  • ✅ ZPL/ESC-POS support
  • ✅ Production-ready with monitoring

Start printing:

<button data-qz-print="{{ route('your.pdf.route') }}">Print Now</button>

Need help? Check the troubleshooting guide or create an issue on GitHub.

Happy Printing! 🖨️✨