Skip to content

QR Generator — Complete Reference

Dashboard Location: Sidebar → My Venues → QR Generator
URL Slug: /wifi-qr-generator
Navigation Sort: 30 (4th item in "My Venues" group)
Navigation Group: My Venues
Navigation Icon: heroicon-o-qr-code (in sidebar), heroicon-o-squares-2x2 (page definition)
Access: All customers + all admins (universal feature, no plan restriction)


Overview

The QR Generator page creates scannable WiFi QR codes that guests can use to connect to a venue's WiFi network instantly. When a guest scans the QR code with their phone camera, it automatically connects them to the WiFi network without manually entering the SSID or password.

This is a universal feature — available to all customers regardless of their subscription plan or server type. There are no plan gating restrictions.


Access Control

php
public static function canAccess(): bool
{
    $user = auth()->user();
    if (!$user) return false;
    if ($user->isAdmin()) return true;
    return $user->role === 'customer';
}
  • Admins: Always have access; can see all sites across all users
  • Customers: Always have access; see only their assigned sites
  • Navigation item is shown/hidden based on canAccess() via shouldRegisterNavigation()

Page Layout

The page consists of three main sections:

1. Site Selector Panel

A white card at the top with:

  • Title: "WiFi QR Code Generator"
  • Subtitle: "Generate a scannable QR code for your guest WiFi network"
  • Site Dropdown: Shown only when user has multiple sites

Site Selection Behaviour:

ScenarioBehaviour
No sitesShows empty state with computer icon: "No sites available — You don't have any sites with UniFi controllers configured."
Single siteAuto-selects the only site; no dropdown shown
Multiple sitesShows "Select Location" dropdown; first site auto-selected on page load

Dropdown Field:

PropertyValue
Label"Select Location"
Wire Modelwire:model.live="selectedSiteId"
OptionsAll user's sites, ordered by name
DisplaySite name
ValueSite ID

When the selection changes, updatedSelectedSiteId() fires and calls loadSiteData().

2. QR Code Display (when SSID exists)

Shown when a site is selected AND the site has a wifi_network_name (SSID) configured.

Action Buttons Bar

A top border-separated row (hidden during print) with two buttons:

ButtonColourIconAction
Download PDFGreen (bg-green-600)Document download iconwire:click="downloadPdf" — Generates and downloads a PDF file
PrintGrey (bg-gray-600)Printer icononclick="window.print()" — Opens browser print dialog

QR Code Content Area

Centered layout within a max-width container (max-w-2xl):

  1. Title Section:

    • "Free WiFi Access" — text-lg font-semibold
    • "Scan to Connect Instantly" — text-sm text-gray-500
  2. QR Code Image — Rendered as inline SVG within a white bordered container:

    • Container: bg-white p-6 rounded-xl shadow-lg border-2 border-gray-200
    • QR code display area: 400×400px
    • QR code is generated server-side as SVG and rendered with {!! $qrCodeSvg !!}
  3. SSID Display Box:

    • Grey background pill: bg-gray-100 px-6 py-4 rounded-lg
    • Label: "Network Name" — text-xs uppercase tracking-wider
    • Value: The SSID in monospace bold — text-xl font-bold text-primary-600 font-mono
  4. Footer:

    • "Powered by" — text-xs text-gray-400
    • "CaptiFi.io" — text-sm font-bold text-primary-600

3. No SSID Warning (when SSID missing)

Shown when a site is selected but has no wifi_network_name. Yellow warning box with:

  • Title: "No SSID Configured"
  • Message: "This site doesn't have a WiFi network name (SSID) configured. Try syncing from UniFi or configure it in the site settings."

QR Code Generation

WiFi String Format

The QR code encodes a WiFi connection string in the standard format:

WIFI:T:nopass;S:{ESCAPED_SSID};;
ParameterValueDescription
TnopassNetwork type — open network (no password required). CaptiFi uses captive portal authentication, so the WiFi itself is open.
S{SSID}The network SSID name, with special characters escaped

Special Character Escaping:

The following characters are escaped in the SSID before encoding:

CharacterEscaped As
\\\
;\;
,\,
"\"
:\:

QR Code Parameters

ParameterValueDescription
Size400pxWidth and height of the QR code in pixels
Error CorrectionH (High, 30%)Highest error correction level — recommended for WiFi QR codes as they may be printed and subject to wear
Margin1Minimal quiet zone border for optimal scanning
FormatSVGOutput format for screen display

Service Class: WifiQrCodeService

Dependencies:

Public Methods:

MethodParametersReturnsDescription
generateWifiString()string $ssidstringCreates the WIFI:T:nopass;S:{ssid};; format string with character escaping
generateQrCode()Site $sitestring (SVG)Generates QR code as SVG string. Throws InvalidArgumentException if no SSID.
generateQrCodePng()Site $sitestring (data URL)Generates QR code as base64-encoded SVG data URL for PDF embedding
syncAndGetSsid()Site $sitearrayAttempts to sync SSID from UniFi controller. Returns ['success' => bool, 'ssid' => string, 'message' => string]
generatePdf()Site $sitePDFGenerates a downloadable A4 portrait PDF with QR code

SSID Sync

The QR Generator can automatically fetch the WiFi network SSID from the site's UniFi controller.

Auto-Sync on Page Load

When loadSiteData() runs and the site has no wifi_network_name:

  1. Calls WifiQrCodeService::syncAndGetSsid()
  2. If successful, refreshes the site model to get the updated SSID
  3. Proceeds to generate the QR code

Manual Sync Button

The "Try Syncing from UniFi" button (shown when no SSID): 2. Invokes WifiQrCodeService::syncAndGetSsid() 3. On success: shows green notification "SSID Synced" and reloads site data 4. On failure: shows warning notification with error message

Sync Requirements

RequirementCheck
Site has a server configured$site->server must exist
Server is UniFi typeserver_type must be unifi or unifi_api

If sync fails, the user can still see the QR code if the SSID was previously configured manually in the site settings.


PDF Download

Generation Process

  1. User clicks "Download PDF"
  2. downloadPdf() validates: site selected, site exists, SSID configured
  3. Calls WifiQrCodeService::generatePdf()
  4. PDF is rendered from Blade template with site data
  5. Streamed as download with filename: wifi-qr-{slug(site-name)}.pdf

PDF Template

Paper: A4 Portrait

PDF Layout:

ElementStyling
Content Area170mm wide, centered with 30mm top margin
Venue LogoMax 150×80px, centered (only shown if available)
Title"Free WiFi Access" — 24px, #333
Subtitle"Scan to Connect Instantly" — 14px, #666
QR Code Container330px wide, 15px padding, 2px #e0e0e0 border, 10px border-radius
QR Code Image300×300px
SSID Box330px wide, #f5f5f5 background, 8px border-radius
SSID Label"Network Name" — 11px, uppercase, #666, letter-spacing 1px
SSID Value18px bold, Courier New monospace, #219ebc (CaptiFi brand cyan)
Footer"Powered by" (10px, #999) + "CaptiFi.io" (14px bold, #219ebc) + generation date (9px, #ccc)

Template Variables:

VariableSource
$siteSite model instance
$ssid$site->wifi_network_name
$qrCodePngBase64 SVG data URL from generateQrCodePng()
$venueName$site->name
$generatedDatenow()->format('F j, Y') (e.g., "January 28, 2026")

The page includes comprehensive CSS @media print styles that:

  1. Hide everything except the #qr-display-section div
  2. Hide the action buttons bar within the QR display section
  3. Position the QR section as absolute, full-width, white background
  4. Set A4 page with portrait orientation and zero margins
  5. Match PDF layout dimensions:
    • Content width: 170mm
    • Top margin: 30mm auto
    • Logo: max 80×150px
    • QR container: 330px wide, 15px padding
    • QR code: 300×300px
    • SSID box: 330px wide
  6. Force colour printing with print-color-adjust: exact
  7. Override dark mode styles to white/black for printing

This ensures the browser print output closely matches the PDF download.


Security

Site Access Verification

When loading site data, the page performs a security check:

php
$user = auth()->user();
if (!$user->isAdmin() && !$site->users->contains($user)) {
    // Show "Unauthorized" notification
    // Reset selectedSiteId to null
    return;
}

This prevents users from accessing QR codes for sites they don't own, even if they manipulate the site ID.

Site Query Scoping

  • Customers: Sites filtered through whereHas('users', ...) pivot relationship

Component Properties

PropertyTypeDefaultDescription
selectedSiteId?intnullCurrently selected site ID
sitesCollectionEmptyAll sites available to the user
currentSsid?stringnullThe SSID of the selected site
qrCodeSvg?stringnullGenerated QR code SVG markup
logoUrl?stringnullURL to the venue's logo image

Data Flow

1. Page Mount
   └─ If sites exist → auto-select first site → loadSiteData()

2. loadSiteData()
   ├─ Find site by selectedSiteId
   ├─ Security check (user owns site)
   ├─ If no wifi_network_name → attempt auto-sync from UniFi
   ├─ Set currentSsid from site
   └─ If SSID exists → WifiQrCodeService::generateQrCode() → set qrCodeSvg

3. Site Change (dropdown)
   └─ updatedSelectedSiteId() → loadSiteData()

4. Sync SSID (button click)

5. Download PDF (button click)
   └─ downloadPdf() → WifiQrCodeService::generatePdf() → stream response

6. Print (button click)
   └─ window.print() → browser handles with @media print CSS

FilePurpose

CaptiFi — Guest WiFi Marketing Platform