=== Event Tickets with Ticket Scanner ===
Contributors: sasonikolov
Tags: event tickets, ticket scanner, congress, woocommerce tickets, seating plan
Requires at least: 6.0
Requires PHP: 8.1
Stable tag: 3.1.1
Tested up to: 7.0
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html

Sell event tickets with WooCommerce. Design seating plans, generate PDF tickets with QR codes, and scan them at the door. No per-ticket fees.

== Description ==

**Stop paying per-ticket fees. Own your entire ticketing workflow inside WordPress.**

Event Tickets with Ticket Scanner turns any WooCommerce product into a scannable event ticket — complete with QR code, downloadable PDF, and a built-in mobile ticket scanner for your team at the door.

Unlike platforms like Eventbrite or Ticketmaster, you keep 100% of your revenue. Unlike other WordPress plugins, you get a **visual seating plan designer** and a **ticket PDF designer** included — not as expensive add-ons.

https://youtu.be/uWSdKdOyn70

**Used by 1,000+ event organizers worldwide** — from small community events to large concert venues. Rated ⭐ 4.9/5 on WordPress.org.

= Who is this for? =

* **Concert & festival organizers** — sell general admission or assigned seats with interactive seat selection
* **Theaters & venues** — design your seating layout with drag & drop, let customers pick their seats
* **Sports events & arenas** — handle high volumes with offline fallback and team scanner access
* **Clubs, spas, gyms & theme parks** — sell multi-entry passes, family tickets, or memberships with expiration
* **Community events & fundraisers** — get started in minutes with the free version

= How it works — 3 steps =

1. Install the plugin and create a ticket list under "Event Tickets"
2. Enable "Ticket Sales" on any WooCommerce product
3. Customers receive a unique QR code ticket — scan and redeem at the entrance

That's it. No coding required. [Watch the quickstart video](https://youtu.be/KKLp1Lwqj_U)

= 🎨 Visual Seating Plan Designer =

Create professional venue layouts without any design tools:

* Drag & drop seats, shapes, labels, and text onto your canvas
* Upload venue floor plans as background images
* Rotate, duplicate, and bulk-edit elements
* Color-code seat categories and pricing tiers
* Customers see a **real-time interactive seat map** during checkout
* Seats are automatically blocked during checkout and released on cancel/refund

= 🎟️ Ticket PDF Designer =

Design tickets that match your brand — not generic templates:

* Add your logo, custom colors, header and background images
* Position QR codes exactly where you want them
* Create event badges for staff and VIP passes
* Multi-page PDF support with attached documents
* Full bleed mode for edge-to-edge designs

= 📱 Built-in Ticket Scanner =

No extra app needed. The scanner runs in any mobile browser — or install it as a PWA:

* Scan QR codes with your phone camera — works on iOS and Android
* **Install as PWA** — add to home screen for instant launch without browser chrome
* **Fullscreen mode** — immersive scanning with a single tap
* **Haptic feedback** — vibration confirms valid/invalid tickets instantly
* Grant team members scanner access via Auth Tokens (no WordPress login needed)
* See ticket details, seat position, and venue map instantly after scan
* Voice output confirms valid/invalid tickets for fast processing
* Protection against fake tickets, double redemption, and brute-force attempts
* Supports hardware barcode scanners for high-traffic entrances
* Customizable theme color to match your brand

= 📲 Vollstart Wallet — All Tickets in One App =

Your customers can collect tickets from multiple shops in one free app:

* **Vollstart Wallet** at [wallet.vollstart.com](https://wallet.vollstart.com) — a Progressive Web App that works on iOS, Android, and desktop
* Tickets are added with one click from the ticket page or order email
* QR code always available — even offline
* **Privacy first** — ticket data flows directly from your shop to the customer's browser. Vollstart never receives or stores any data
* [Learn more about security & privacy](https://vollstart.com/vollstart-wallet/)

Enable it in Settings → Digital Wallets. Free for all users.

= 🎓 Congress & Attendee Portal =

Give ticket holders a private event portal — no extra login, no separate app. Assign a **congress** to any ticket product and buyers reach it straight from their ticket ID.

* **Organize content into pages** — a start page plus as many pages as you need (Speakers, Programme, Downloads, Sponsors …), with a sidebar on desktop and a hamburger menu on mobile
* **Rich section types** — formatted text, a day-by-day programme schedule, downloadable files, link lists, image galleries, single images, and embedded videos (paste a YouTube/Vimeo link or upload your own)
* **Password-protect** individual sections for VIP or speaker-only content
* **Fast by design** — the start page loads instantly and other pages are fetched on demand, so even congresses with dozens of speakers stay snappy
* **Installable & offline-friendly** — the portal ships as a mini Progressive Web App
* **Time-controlled access** — open the portal a set number of hours before the event and keep it available for a chosen number of days afterwards
* **Recurring events** — duplicate a congress as a "new edition" in one click

Build it under the Congresses menu, assign it in the product's Event Tickets tab, and optionally add a portal link to the order email. Included free.

= Powerful Ticket Types =

* **Single entry** — classic one-time event ticket
* **Multi-entry passes** — allow multiple scans (configurable limit)
* **Family tickets** — generate multiple tickets per order item
* **Memberships & season passes** — set expiration dates
* **Day chooser tickets** — let customers pick their event date at checkout
* **Purchase allowance codes** — restrict who can buy specific products

= Built for WooCommerce =

* Works with product variants (e.g., VIP vs. General Admission)
* Tickets auto-generate on order completion
* Refunded orders automatically recover and recycle ticket numbers
* Compatible with WooCommerce Subscriptions
* Works with WooCommerce PDF Invoices & Packing Slips
* WPML compatible for multilingual stores

= Free vs. Premium =

The free version covers everything you need to start selling tickets:

* ✅ Ticket generation with QR codes
* ✅ PDF ticket download (via link in email)
* ✅ Built-in ticket scanner
* ✅ Interactive seating plan designer
* ✅ Seat selection at checkout
* ✅ Multi-entry and family tickets
* ✅ Day chooser for date-based events
* ✅ Congress / attendee portal (pages, programme, downloads, media, password-protected sections)
* ✅ Webhooks for third-party integrations

**Premium adds professional features for larger events:**

* 🔓 PDF ticket as email attachment (not just a link)
* 🔓 Team scanner access via Auth Tokens
* 🔓 Calendar invites (ICS files) in emails
* 🔓 Custom flyers and multi-page PDFs
* 🔓 CVV security check on tickets
* 🔓 Brute-force IP blocking
* 🔓 No ticket limits
* 🔓 Advanced shortcodes for ticket display and validation

[Get Premium](https://vollstart.com/event-tickets-with-ticket-scanner/)

= Links =

* [Documentation](https://vollstart.com/event-tickets-with-ticket-scanner/docs/)
* [Quickstart Video](https://youtu.be/KKLp1Lwqj_U)
* [All Features](https://vollstart.com/event-tickets-with-ticket-scanner/docs/event-tickets-with-ticket-scanner-feature-list/)
* [Premium Plugin](https://vollstart.com/event-tickets-with-ticket-scanner/)
* [Support](mailto:support@vollstart.com)

== Installation ==

= Requirements =
* WordPress 5.0 or greater
* WooCommerce 6.0 or greater
* PHP 8.1 or greater (PHP 8.4 compatible)
* PHP extensions: php-curl, php-imagick

= Installation =

1. Go to **Plugins → Add New** in your WordPress admin
2. Search for "Event Tickets with Ticket Scanner"
3. Click **Install Now**, then **Activate**
4. Go to **Event Tickets** in your admin menu and create your first ticket list
5. Edit any WooCommerce product → open the **Event Tickets** tab → enable ticket sales

= Quick Setup =

The plugin extends WooCommerce so any product can become a ticket. Enable the checkbox on your product, assign a ticket list, and you're ready to sell.

**Tip:** Set the product as "Virtual" so WooCommerce auto-completes the order and triggers ticket generation immediately after payment.

To test: Create a manual order in WooCommerce, set it to "Completed", and check the order email for the ticket link.

**For a step-by-step walkthrough, [watch the quickstart video](https://vollstart.com/event-tickets-with-ticket-scanner/docs/#quickstart).**

== Frequently Asked Questions ==

= Do I need any other plugins? =
You need WooCommerce (free) to handle payments and orders. Everything else is included — no additional ticketing add-ons required.

= Does this work without WooCommerce? =
WooCommerce is required for selling tickets. However, you can use the plugin to manage and validate ticket lists manually without WooCommerce sales.

= Can my team scan tickets without a WordPress account? =
Yes! Premium includes Auth Tokens that give your door staff scanner access via a simple URL — no login required.

= Does the scanner work offline? =
The scanner is browser-based and requires an internet connection. For large events, the plugin includes offline fallback options to prevent interruptions.

= Can customers choose their own seats? =
Yes. Design your venue layout with the drag & drop seating designer, and customers will see an interactive seat map during checkout where they can pick available seats.

= What ticket types can I create? =
Single entry, multi-entry passes, family tickets (multiple tickets per purchase), memberships with expiration dates, and day-chooser tickets where customers pick their event date.

= How are tickets delivered to customers? =
In the free version, the order confirmation email includes a link to download the ticket PDF and view the QR code. Premium allows attaching the PDF directly to the email and adding calendar invites (ICS).

= What happens when an order is refunded? =
The ticket is automatically deactivated, the assigned seat is released, and the ticket number is recovered for reuse.

= Is the plugin compatible with WPML? =
Yes. WPML is supported for multilingual ticket sales. The plugin also ships translations for German, Spanish, French, Italian, Hungarian, Japanese, Dutch, Portuguese, and Chinese.

= What if I exceed the free ticket limit? =
If you reach the limit, the plugin will display a message asking the customer to contact support. Your sales are never interrupted. Premium has no ticket limits.

= Can I use my own QR code or barcode scanner hardware? =
Yes. The built-in scanner page accepts input from hardware barcode scanners in addition to camera-based QR scanning.

= How do I prevent ticket fraud? =
Every ticket number is unique. The scanner detects duplicate redemption attempts. Premium adds CVV verification and brute-force IP blocking for additional security.

= What is the congress / attendee portal? =
It's a private event portal you can attach to a ticket product — organized into pages (start page, speakers, programme, downloads, sponsors, etc.) with text, schedules, files, image galleries and embedded videos. Ticket holders open it directly via their ticket, no separate account needed. You can password-protect individual sections, control when the portal opens and how long it stays available, and the page works offline as a mini PWA. It's included in the free version; build it under the Congresses menu and assign it in the product's Event Tickets tab.

== Screenshots ==

1. **Ticket on Mobile** — Customers see their ticket details optimized for any device, with QR code and PDF download.
2. **Ticket Details Desktop** — Configure exactly which information appears on the ticket detail page.
3. **PDF Ticket** — Professionally designed PDF ticket with QR code, ready for print or mobile display.
4. **Ticket Scanner** — Built-in mobile scanner for your team at the entrance. No app required.
5. **Event Badge** — Print name badges for conferences, VIP events, or staff credentials.
6. **Admin Options** — Comprehensive settings to configure ticket behavior, formats, and delivery.
7. **Ticket Management** — Overview of all generated tickets with status, redemption info, and filters.
8. **Ticket Number Formats** — Pre-generate ticket numbers or let the plugin create them automatically.
9. **Event Flyer** — Generate promotional flyers for your event directly from the plugin.
10. **PDF Designer** — Customize every area of your ticket PDF: logo, images, QR position, colors.
11. **Product Settings** — Enable ticket sales on any WooCommerce product with one checkbox.
12. **Flyer Settings** — Configure flyer content, images, and event details.
13. **Order Ticket View** — Quick-scan tickets directly from the WooCommerce order detail page.

== Upgrade Notice ==

= 2.8.0 =
Major release: Old premium version will no longer work with this version. Interactive seating plan designer with drag & drop editor. Default ticket template updated to display seat numbers. Please backup your system before upgrading.

= 2.8.10 = 
Old premium version will no longer work with this version. You need to downgrade the basic plugin or get a new license for premium to update your premium plugin too.

== Changelog ==

= 3.1.1 - 2026-06-15 =
* New: Event portals — the Congress tool now works for any event (opera, zoo, festival, …).
* New: Optional card-grid landing showing each page as a card with an icon or image and a short description.
* New: Customizable portal label per portal (with a global default, "Infos") — used on the page, in the wallet action and the order email.
* New: Back-to-home control in the portal view.
* New: Insert ticket/order/product/customer variables into info and custom section text; they are filled with the visitor's data when the page is opened.
* Improvement: Internal data and links are unchanged — existing congresses keep working exactly as before.
* Improved: Cleaner admin dashboard — moved the "List of tickets" description into the list card next to its buttons and removed the redundant heading and divider.
* New: Speaker sections — add one or more speakers (photo, name, title, short bio). A single speaker is shown in full; multiple speakers appear as a grid of cards with a detail view and a back control.
* New: "My ticket" entry in the portal always shows the ticket's QR code, so attendees without a printed badge can present it at the entrance.
* New: Portal entries (such as "My ticket") also appear as cards on the card-grid landing.
* Improvement: The portal title is now clickable and returns to the start page; the "Add to Vollstart Wallet" link moved from the header into the "My ticket" view.
* New: View access toggles (security) — each output reachable through the ticket link (ticket detail view, PDF, ICS calendar, badge PDF, all-tickets-in-one-PDF, event portal/congress) can now be switched off individually. When off, the view is no longer rendered and a short "deactivated" notice (or 403 for downloads) is shown. All default to on, so existing installs are unaffected. Unlike the older "hide button" options, this actually blocks the URL.
* New: Optional "Allow redemption by plain ticket number" (off by default). When enabled, a ticket can additionally be redeemed using only the plain ticket number printed on the card; the full ticket id keeps working as before. Note: for the plain number the copy protection does not apply, and it is unsafe together with ticket reuse (a warning is shown on both options).
* Fix: Settings checkboxes whose default is "on" (e.g. the new view-access toggles, the QR scanner on the validation form, the seating-plan/venue buttons) could appear unchecked until the settings page was saved once. The admin UI compared a boolean default too strictly; it now renders the correct checked state. The actual behaviour was always correct (the option was on) — only the checkbox display was wrong.

= 3.1.0 - 2026-06-04 =
* New: Congress mode — organizers create congress objects (programme, downloads, texts, media) and assign them to WooCommerce products.
* New: A dismissible "What's New" banner in the admin introduces the Congress feature (shown once per version).
* New: Ticket holders open the congress page via their ticket ID. Like the ticket detail and scanner pages it is served from the plugin path (`…/ticket/{ticket-id}?congress`, query fallback `…/?code={ticket-id}&congress`), so it is compatibility-mode aware and never collides with shop page slugs.
* New: Offline-capable congress page with a Web App Manifest (installable as a mini PWA).
* New: Sections with optional password protection (server-side transient, 1h TTL).
* New: Programme sections with a day/slot structure, plus download and media sections (WordPress Media Library).
* New: "New edition" button for recurring congresses — duplicates the congress and sets an automatic expiry date on the original.
* New: Option to activate/deactivate the public congress page (default: active); the admin area stays available either way.
* New: Event-relative access window — optional event start/end time per congress; global options control how many hours before the start access opens and how many days after the end the data stays available.
* Improvement: Congress assignment now lives directly in the Event Tickets product tab (instead of a separate tab); the dropdown shows the event/expiry date for easier selection.
* New: `congress_url` in the Wallet API response — the Wallet app shows an "Open congress" button when the product is assigned to an active congress.
* New: Congress URL pattern listed on the Support/System Info page (adapts to the site location); the admin ticket detail shows a direct "Open congress page" link for tickets whose product is assigned to a congress.
* Fix: Congress access now accepts the public ticket ID (as used by the Wallet/QR), not only the internal code — so the Wallet/customer link works reliably.
* New: Congress section editor with full input masks per type — Info/Custom use a visual editor, Download picks files from the media library, URL with internal/external link option, Program with day & time-slot structure, Media gallery.
* New: Congress sections now track and display created/updated date and the editing user.
* New: Visual text editor (TinyMCE) for the "Additional text on PDF ticket" and "Ticket Badge HTML" options instead of a plain textarea.
* New: Tag picker dropdown inside the Ticket Badge editor — insert replacement tags ({TICKET.*}, {ORDER.*}, {PRODUCT.*} …) at the cursor instead of copy-pasting from the list below.
* Improvement: Modernized admin UI — compact buttons, lightweight ghost-style table action buttons grouped together, navigation menu with active highlight, modern DataTables styling.
* Improvement: Congress editor redesigned as a clean card layout; all native browser confirm/prompt dialogs replaced with the plugin's own styled dialogs and loading spinner.
* New: Congress pages — sections are now grouped into pages (the first page is the start page). Visitors get a sidebar (desktop) / hamburger drawer (mobile) navigation; the start page loads instantly and other pages are fetched on demand, so large congresses stay fast.
* New: Congress section types Image (from the media library, with caption) and Video (paste a YouTube/Vimeo link for automatic embedding, or upload a video file).
* New: Image sections have a display-size option — full width (default), original size, or a custom width (px or %).
* New: Image sections can optionally open full size in a lightbox when clicked (default off).
* New: Download files can be set to "open inline" (PDF/images open in the browser) instead of forcing a download.
* New: Admin can create, rename, delete and reorder congress pages (drag & drop) and move a section to another page; sections are drag-sortable within a page.
* Security: All HTML content is sanitized with `wp_kses_post()` — no JavaScript allowed in congress content; video embeds are generated server-side via WordPress oEmbed.
* i18n: All new congress strings (admin editor + visitor page) translated into 11 locales (de_DE, de_CH, es_ES, fr_FR, hu_HU, it_IT, ja_JP, nl_NL, pt_BR, pt_PT, zh_CN), including the JavaScript UI (script-translation JSON).
* Performance: Transient cache (5 min) for access checks; ETag/Last-Modified for browser cache invalidation.
* Fix: Same-day cutoff time was not working when the product also had a fixed start date configured. The absolute start date was applied after the cutoff check and silently overrode it, leaving today selectable past the cutoff time. Cutoff check now runs after all date overrides and correctly disables today in every configuration.
* Fix: Same-day cutoff time is now validated server-side at checkout. A customer who selected today's date before the cutoff and held the cart past it is now blocked at checkout.
* New: Congress can be switched active/inactive per congress (on top of the global on/off) — hide it everywhere without unassigning, e.g. to publish the info only later after purchase.
* New: The Vollstart Wallet shows a congress button on the ticket; it enables/disables on the next refresh based on the global switch, the per-congress active flag, the order status, and the access window (which now derives the event date from the ticket's product). Outside the window it shows a disabled "available from …" hint.
* Changed: Wallet API now returns a generic, extensible `actions` array (future content types reuse it) instead of the single `congress_url` field.
* Changed: Removed the redundant per-congress "Event start/end" fields from the congress editor — the event date comes from the WooCommerce product.

= 3.0.10 - 2026-05-18 =
* Fix: Same-day cutoff time was not working when the product also had a fixed start date configured. The absolute start date was applied after the cutoff check and silently overrode it, leaving today selectable past the cutoff time. Cutoff check now runs after all date overrides and correctly disables today in every configuration.
* Fix: Same-day cutoff time is now validated server-side at checkout. A customer who selected today's date before the cutoff and held the cart past it is now blocked at checkout.

= 3.0.9 - 2026-05-18 =
* New: Day-chooser products can now define a "Same-day cutoff time" (HH:MM) in the product's ticket settings. When the customer's local time is past that value, today's date is automatically disabled in the date picker — only tomorrow and future dates remain selectable. The field has no effect when "Offset days for start date" is already ≥ 1 (today would be excluded anyway).
* Fix: Same-day cutoff time was ineffective on products that also have a fixed start date (ticket_start_date) configured. The absolute start date was applied after the cutoff check and silently overrode it, leaving today selectable past the cutoff. The cutoff check now runs after all min-date overrides and correctly shifts the minimum to tomorrow whenever today would still be reachable.
* New: CVV-at-Scanner — a per-product opt-in second-factor for ticket redemption. Enable "Require security code at scanner" (`saso_eventtickets_require_cvv_at_scanner`) on any ticket product; a unique 4-character alphanumeric code (uppercase A–Z plus 2–9, no visually ambiguous O/0/I/1) is generated automatically at WC order-completion time and stored alongside the ticket. At the scanner, the ticket's public ID is shown immediately on QR scan but all identifying information (name, seat, billing details, product title) is withheld until the bearer reads the code from their ticket and enters it. An incorrect code counts as one strike; after 5 consecutive wrong attempts the ticket locks and the scanner shows a prominent locked-screen warning — the attendant must contact the organiser to reset via the "Reset CVV attempts" button in the admin code-edit modal. The default UI is a two-stage hand-over flow: first stage confirms the public ticket ID and prompts the organiser to hand the device to the attendee; second stage shows the masked code entry field with a show/hide toggle. An optional one-stage flow (global option `wcTicketScannerCVVOneStageFlow`, default off) skips the hand-over prompt for high-trust environments. Input masking is on by default and controlled globally via `wcTicketScannerCVVMaskInput` (default on). The anti-information-leak short-circuit response from REST endpoints exposes only `requires_cvv`, `attempts_remaining`, `locked`, and `public_ticket_id` — no product name, seat, billing, or order metadata is returned before a correct CVV is entered.
* New: Order-ticket QR codes (one QR for an entire order) now honor the per-product CVV requirement on a per-ticket-row basis. When an order QR is scanned and one or more of its tickets is from a CVV-protected product, the scanner UI renders a compact CVV input next to each protected row instead of revealing name/seat/order details for it, while non-protected rows in the same order continue to show full information. A "Confirm codes" button at the bottom submits all entered codes in one round-trip; rows with a correct CVV unlock and become fully redeemable. Wrong-CVV rows decrement the per-ticket attempt counter (same 5-strike lockout as single-ticket scans); attempts on one row do not affect other rows. Anti-information-leak: blocked rows expose only `requires_cvv`, `attempts_remaining`, `locked`, and `public_ticket_id`.
* New: CSV ticket export gains two new columns — `meta_cvv_attempts` (number of failed attempts so far) and `meta_cvv_locked` (boolean, 1 if the 5-strike limit was reached) — so organisers can audit CVV activity across all tickets without opening individual admin screens.

= 3.0.8 - 2026-05-11 =
* Fix: On variable products, the parent's "Request a value per ticket", "...mandatory" and the matching name-per-ticket checkboxes silently turned themselves back ON every time anything on the product was saved (e.g. flipping a variation's stock status). Root cause: each variation row submits an override `<select name="<key>[<i>]">` under the same root name as the parent's checkbox/input, so on a variable-product save `$_POST[<key>]` arrived as an array. The save handler treated "key is present in POST" as "checkbox is checked" and overwrote the parent meta with `yes` — which is also why the cart kept asking customers to fill in a value field even when, in the admin, the boxes looked unchecked. The handler now ignores array values for parent-level fields (`request_name/value_per_ticket(_mandatory|_label|_def)`, `ticket_start/end_date/time`, `ticket_amount_per_item`, and Premium's `expiration_days`). After updating, simply uncheck the affected boxes once and they will stay unchecked. No data migration is required.
* Fix: Fatal TypeError when adding products to cart from a wishlist (or any 3rd-party plugin that calls `WC_Cart::add_to_cart()` without supplying `$variation` as an array). The internal handler used a strict `array` type-hint and aborted the whole cart operation — it now accepts any input and normalizes defensively. Affects sites running WooCommerce Wishlists.
* Fix: Ticket configuration fields (date, dropdown, name-per-ticket, etc.) no longer appear on every variation of every variable product in the admin. They are now only rendered when the parent product is explicitly marked as a ticket — same UX rule that already applies to simple products. Catalogs with thousands of non-ticket variable products no longer have to opt out per variation.
* Fix: Cart and checkout no longer render ticket-specific input fields (name-per-ticket, value-per-ticket, daychooser, seat info) for products whose parent is not (or no longer) a ticket. The `is_ticket` flag on the parent product is now the single source of truth — stale variation/product meta from a previous configuration is ignored. Validation at checkout follows the same rule and no longer blocks orders for non-ticket products with leftover meta. Purchase-restriction codes are unaffected and continue to work on any product type.
* Fix: Cart and checkout input fields for the name-per-ticket, value-per-ticket and purchase-restriction features now use standard WooCommerce form-row markup (`<p class="form-row form-row-wide"><label>…</label><input class="input-text" />`) instead of a custom `<small>`-label wrapper. This lets the active theme's CSS apply normally so the fields look consistent with the rest of the cart/checkout form.

= 3.0.7 - 2026-05-05 =
* Fix: License-key modal no longer nags on every admin page load. Clicking "Later" now silences the prompt for 24 hours (per browser, via localStorage) instead of re-popping on the next click. Customers can still enter the key any time on the plugin's Options page — the field is rendered there.
* New: Eventado public-calendar teaser added to the "What's New" version-notices system on the plugin's settings page (the same release-announcement surface that already carries the Wallet card). Active for all users until "Dismiss" is clicked; dismissing pins it to the current plugin version, so it re-appears on the next plugin update.
* New: Authtoken audit on ticket redemption — when an external scanner team uses an authtoken (no WP login), the token's database id is now stored in the redeem record (`redeemed_via_authtoken_id`). Backend ticket detail view shows "Redeemed via authtoken: NAME (#ID)" alongside any WP-user redeem info; CSV export gains `meta_wc_ticket_redeemed_via_authtoken_id` + `_name` columns. The token name is resolved live with per-request caching, so a renamed token shows the current name everywhere — only the id is persisted.
* i18n: 13 Premium-side strings now ship with translations in all 11 languages (de_DE, de_CH, en, es_ES, fr_FR, hu_HU, it_IT, ja_JP, nl_NL, pt_BR, pt_PT, zh_CN). Strings cover the new Eventado options (publish toggle, per-product exclude, 18+ flag, sync notice, license-expiry banner) and a few Premium options that previously only existed as English source strings.
* i18n: Context-Wizard suggestion-card system gains a new card for the Eventado publishing option (Premium only). Replaces the temporary admin_notices banner from 1.6.8 — uses the existing dismiss-per-user flow, renders inline on the options page next to the Wallet suggestion.
* i18n: Premium plugin now uses the basic plugin's textdomain consistently — strings that lived under `event-tickets-with-woocommmerce-premium` (license-expiry messages) are migrated to `event-tickets-with-ticket-scanner`, so a single .mo file per language covers both plugins.

= 3.0.6 - 2026-05-04 =
* New: "First Activated" date in Support Info — shows when the plugin was first activated, included in the copyable support text. Marked as "(estimate)" for installs predating this version.
* Internal: First-activation timestamp is sent to the license server with the next license check (Premium plugin) for free-to-paid analytics.
* Improvement: Day chooser auto-jumps to the first month with available dates. If the current month has no free slots (all days excluded by weekday rules, blackout dates, or min-date), the picker now opens directly on the next month that has at least one selectable day — customers no longer see a frustrating all-grey calendar. Scans up to 24 months forward.

= 3.0.5 - 2026-04-24 =
* Fix: "Update available" badge stayed visible after auto-upgrade of the premium plugin — the `update_plugins` site transient was only cleared BEFORE the upgrade, not after. WordPress then kept the stale "update available" entry until the next WP-Cron (12h later). The transient is now cleared + re-checked after the upgrade completes, so the red badge disappears on the next page load.
* Improvement: License activation modal now locks the UI while the license check runs — "Activate" button and input field are disabled, a clear spinner with "Validating license — please wait…" replaces the small status text. Prevents multiple clicks and confusion during the 2–5s background check.

= 3.0.4 - 2026-04-23 =
* Fix: License activation flow — after entering a license key, the "Starter plugin installed" and "subscription expired" banners now disappear immediately (via JS fade + 60s server-side suppression) instead of staying visible until the next page load. Page now always reloads after a serial is saved to reflect the new plugin state.
* Fix: "Subscription expired" banner no longer shown when no license key is configured (was confusing new customers who had just installed the starter plugin).
* Fix: PHP warning "Undefined array key SCRIPT_NAME" when plugin runs in CLI or WP-Cron context — index.php:92 and :578 now use null-safe access. Same class of bug as the REQUEST_METHOD fix in 3.0.3 but for SCRIPT_NAME.
* Fix: Hardened `SASO_EVENTTICKETS::issetRPara()` against missing REQUEST_METHOD in CLI/cron context.

= 3.0.3 - 2026-04-20 =
* New: Auto-upgrade premium plugin after valid license key is saved — no more manual "Update" click needed. Customers who install the starter plugin and enter their license now get the real premium installed automatically in the background.
* Fix: PHP 8.2+ fatal error "Call to undefined function trigger_deprecation()" — Twig Composer autoloader conflict when another plugin bundles Twig. Added polyfill before autoload.
* Fix: PHP warning "Undefined array key REQUEST_METHOD" when plugin runs in CLI or WP-Cron context
* Fix: Day chooser label not visible on product and shop pages — empty string prevented DB lookup and rendering
* Fix: Day chooser showed wrong date in cart after re-adding same product — stale session data was not cleaned up on cart item removal. Added session_unset_value() cleanup.
* Fix: Day chooser dates grew unbounded on WC cart merge — old dates were appended instead of correctly filling only new ticket positions
* Fix: Day chooser on shop/archive page now works with WooCommerce Blocks — click interceptor appends date as GET parameter since WC Blocks no longer fires jQuery adding_to_cart event
* Fix: Day chooser input now blocks keyboard typing and paste via JS — users must pick from datepicker. Server-side validation also checks excluded weekdays and specific dates (Premium).
* Improvement: Datepicker wrapper has CSS class `saso-eventtickets-datepicker` and `data-product-id` for custom styling
* New: Variable fields (name input + value dropdown) now configurable per WooCommerce variation — override enable, label, mandatory, and dropdown values per variation with automatic fallback to parent product settings
* New: Attendance tab — "Sold Tickets" calendar view showing booked dates across all date picker products with product filter, month navigation, drill-down to individual tickets, CSV export, and auto-refresh (10s/30s/1min/5min) for live dashboard use
* New: Error logs CSV export in Support Info
* Improvement: Support Info layout — two-column grid for cards, URLs and Libraries side by side
* Improvement: Sold Tickets Calendar button visible on all ticket products (disabled with hint for non-datepicker products)
* Improvement: Print List table column widths fixed (# 1cm, Order/Status 2cm)
* New: "More Plugins by Vollstart" submenu page — discover other free plugins from the same team

= 3.0.2 - 2026-04-12 =
* Fix: Old premium (< 1.5.0) compatibility — ReflectionClass-based method check BEFORE instantiation prevents "ghost hook" crashes. The old premium constructor registers 25+ WordPress hooks on $this; if the class was instantiated and then discarded, WordPress kept the callbacks alive in memory and fired them later, crashing the site on methods removed in the WC manager refactor. Now the compatibility check happens without ever running the constructor.
* Fix: DB migration for redeemed codes no longer loads all rows into PHP memory — replaced SELECT+loop with a single SQL UPDATE, preventing memory exhaustion on large databases (1M+ tickets)
* New: Vollstart Wallet integration — customers can add tickets to the Vollstart Wallet app (wallet.vollstart.com) to collect tickets from multiple shops in one place. Enable in plugin settings under "Digital Wallets". Adds "Add to Vollstart Wallet" button on ticket detail page and in order emails. Includes REST API endpoint for the wallet app.
* New: Wallet option in Setup Wizard — all 4 presets (Event, Day Pass, Membership, Voucher) now include the Vollstart Wallet toggle
* New: Context suggestion for existing installations — prompts to enable Vollstart Wallet when tickets exist
* New: REST API warning in wallet option when WordPress REST API is disabled
* New: Version notice system — shows "What's New" messages after plugin updates, dismissible per version
* New: Wallet test link in admin ticket detail view — quickly import a ticket to Vollstart Wallet for testing
* Fix: Hard-throttled periodic license check via site transient — prevents runaway license requests when admin pages are hit rapidly by bots, monitoring tools, or other plugins
* Improvement: Updated all translations — 11 new wallet strings added to all 11 languages

= 3.0.1 - 2026-04-07 =
* Improvement: Updated all translations — regenerated POT from source, added 188 new strings, all 11 languages at 100% (de, es, fr, hu, it, ja, nl, pt_BR, pt_PT, zh_CN)
* Improvement: Premium plugin labels (Ticket Template, Excluded Days, License Key etc.) now included in translations

= 3.0.0 - 2026-03-31 =
* Fix: Admin CSS was cached by Cloudflare/browsers after plugin updates — added version-based cache buster to dynamically loaded stylesheet
* Fix: Scoped global CSS button selectors to plugin container — no longer affects WP admin sidebar
* Fix: Backend CSS now loaded via wp_enqueue_style instead of JS injection (prevents layout shift)
* Fix: Removed duplicate Settings submenu entry (top-level menu only)
* New: QR code scanner on the validator form — users can scan QR codes with their device camera instead of typing codes manually (enable in plugin settings)
* New: License key prompt dialog on settings page instead of browser alert — with direct link to options page
* Fix: Ticket table button column no longer forces minimum width (cleaner layout on small screens)

= 2.9.9 - 2026-03-23 =
* Improvement: License check resilience — brief server outages no longer disable premium features; raised failure threshold from 5 to 10, extended staleness window from 10 to 21 days, and server errors (5xx, empty body) now count as transient failures instead of genuine rejections
* Fix: "Check License" button now resets failure counter before server check — previously, accumulated failures from a server outage could prevent recovery even after the server was back online
* Improvement: Modern admin UI redesign — card-based layout with consistent spacing, branded purple accent color, professional footer grid, improved DataTable styling, smooth loading animations, and responsive design
* New: Plugin header now includes Requires at least, Tested up to, and Requires PHP — displayed in support info area for easier troubleshooting
* Fix: Corrected 10 English typos in source strings that caused translation mismatches (e.g. successfull, duplicat, choosen)
* Fix: No more flash of unstyled content when loading admin page
* Improvement: Regenerated all German backend translations — from 162 to 451 strings (full coverage)
* New: FOMO banner for expired premium subscriptions — shows new features released since expiration with dismissible 30-day reminder
* Fix: Added missing License header to plugin file to satisfy WordPress.org plugin check

= 2.9.8 - 2026-03-11 =
* Fix: TypeError in WooCommerce email preview and order emails — removed strict type hints from all WooCommerce hook callbacks to prevent crashes with different WC versions

= 2.9.7 - 2026-03-11 =
* Fix: Options migration safety net — premium options (e.g. PDF ticket attachment) are now preserved even if the premium plugin was inactive during the database migration from wp_options to the custom options table

= 2.9.6 - 2026-03-10 =
* New: Daily Redemption Summary — view redeemed tickets across all products for any date range (Premium)
* New: No-Show count in Attendance view showing unredeemed tickets per event (Premium)
* New: Drill-down from Attendance rows to individual ticket codes with order links (Premium)
* New: Export Attendance data as CSV (Premium)
* Improvement: Seating CSV export/import now includes visual designer data — position, rotation, shape, and color survive roundtrip (Premium) (#209)
* Fix: Seating CSV export appended JSON artifact at end of file — missing exit after CSV output (#209)
* New: Calendar view and printable ticket list on product edit page for daychooser products (#191)
* Performance: Lazy-load single options via getOption() instead of bulk-loading all options
* New: License Server Connectivity Check — diagnostic button in Support area tests if vollstart.com license/update server is reachable. Shows connection status, response time, and detailed error messages. Helps diagnose Premium update issues caused by firewalls, DNS problems, or network restrictions.
* Improvement: Subscription expiration warnings now distinguish between license types — lifetime/onetime licenses show no warning (continue working with Basic < 2.8.0), monthly subscriptions warn about payment processing, yearly subscriptions warn about expiration.
* Improvement: Renamed "Serial Key" to "License Key" in all user-facing strings for clarity
* Improvement: Complete translations for all 11 languages (DE, ES, FR, HU, IT, JA, NL, PT_BR, PT_PT, ZH_CN, DE_CH) — 100% coverage

= 2.9.5 - 2026-03-03 =
* Fix: Premium serial key displayed as 0 after options migration — premium features appeared lost. Options added late during initialization (e.g. by the premium plugin) are now loaded correctly from the custom table.

= 2.9.4 - 2026-03-03 =
* Performance: Plugin options migrated from individual wp_options rows to a dedicated custom table — all settings now load in a single query instead of 150+ separate queries (#73)
* New: Options change history — tracks who changed which setting, when, with old and new values. Revert button to restore previous values. Keeps last 10 entries per option, older entries cleaned up automatically. Available in Options > Change History tab (#73)
* New: Context-Wizards — smart suggestions on the options page based on your current configuration. Detects related settings and offers to enable them with one click. Covers email attachments, scanner presets, ticket display, and security settings. Dismissals are per-user (#232)
* New: Export and import seating plan seats via CSV for backup, external editing, or transfer between plans (Premium) (#209)
* Fix: Old premium plugin (< 1.6.0 or without version constant) caused crash instead of being gracefully blocked
* Fix: Options migration is now abort-safe — if interrupted, it resumes on next page load instead of silently losing settings. Existing values are updated correctly on re-run.
* Fix: Database upgrade jobs now run before saving the new DB version, preventing incomplete upgrades from being marked as finished
* Fix: Premium or third-party upgrade hooks that throw exceptions are now caught and logged instead of causing an infinite crash loop
* Security: Activate global ticket counter brake to prevent free limit bypass via delete-and-recreate

= 2.9.3 - 2026-03-02 =
* New: "Check License" button — recheck premium license status on demand from Options page (next to serial field) and Support Info page. Shows status, last check, expiration, failure count.
* Improvement: Saving a serial key now immediately checks the license and shows the result inline. On success, the page reloads automatically so premium features are available without manual refresh.
* Improvement: "Check License Now" button bypasses the 7-day server cache to always get a fresh response
* Fix: Dismiss button for ticket format warnings did not work — two bugs: nonce parameter name mismatch, and wp_redirect() called during admin_notices (after output started). Dismiss handler now runs on admin_init.
* Fix: JavaScript error "cannot call methods on dialog prior to initialization" — closeDialog() tried to destroy all dialog elements on page instead of only the target dialog
* Fix: backend.js could be cached by browser across updates — jQuery.getScript() bypassed WordPress cache busting. Now uses file modification time as additional cache-buster parameter.
* New: Setup Wizard — multi-step dialog guides new users through use-case selection (Event tickets, Day passes, Memberships, Vouchers) and configures optimal settings automatically. Covers redemption rules, scanner behavior, email delivery (ICS, date, order view), and order processing. Premium users also get PDF attachment settings. Re-launchable via "Start Wizard" button.
* New: Premium Wizard — one-time dialog when premium is activated, offers to enable recommended premium defaults (PDF email attachment, merge into one PDF). Re-launchable via "Premium Wizard" button (only visible with premium). (#233)
* New: Export/Import options — backup and restore all plugin settings as JSON file
* New: "Max redeems per day" option per product — limit how many times a multi-redeem ticket can be redeemed on a single day (0 = unlimited, only total max applies)
* Improvement: "First Steps" onboarding box upgraded to interactive card with progress bar, auto-detection of completed steps, and action buttons
* Improvement: Code generation now uses date-based prefix (encoded as 5 letters) to partition address space — virtually eliminates collisions even with short code formats
* Improvement: Increased code generation retry limit from 100 to 500 attempts
* Improvement: Ticket detail view now shows WooCommerce order status, billing email, product name, and variation attributes
* Improvement: Support Info — options list is now hidden behind a button click to reduce page load clutter
* Improvement: Bulk action dropdown now shows a message when no tickets are selected
* Improvement: AJAX calls now properly handle network-level errors (timeout, connection lost)
* Improvement: Cart input fields (name per ticket, restriction code) now auto-save on Enter key (#234)
* New: Auto-update dialog — after entering a serial key with an outdated premium plugin, automatically checks for updates and offers to install them. Shows release notes for users without an active subscription.
* Fix: Value-per-ticket dropdown in cart never saved — selector targeted wrong element type and attribute (#234)
* Fix: Options in-memory cache corruption in _setOptionValuesByKey() — variable name collision caused stale/incorrect values within the same request
* Fix: Restriction code input in cart crashed on PHP 8.x due to wrong constant name case (META_KEY_CODELIST_RESTRICTION_order_item → _ORDER_ITEM)
* Fix: False "Ticket format exhausted" warning when free version ticket limit (32) was reached — #208 exception was incorrectly triggering format warning on first attempt
* Fix: clearFormatWarning() never actually cleared warnings — editList() was called with wrong signature, exception silently caught
* Fix: "Edit list" link in format warning notice and email pointed to non-existent admin page
* Fix: Import options failed silently due to WordPress wp_magic_quotes escaping JSON — now handled via stripslashes
* Fix: Product meta save could trigger PHP warning for undefined array key due to operator precedence bug
* Fix: Admin ping timeout (e.g. when browser tab is frozen) no longer shows repeated error dialogs
* Fix: Typo in displayFirstStepsHelp option description ("activet" → "activated")

= 2.9.2 - 2026-02-23 =
* Fix: Format warning data (attempts, last_email) was never persisted — editList() called with wrong signature and would auto-clear warnings

= 2.9.1 - 2026-02-23 =
* Fix: Crash during checkout when format warning check runs (undefined method _json_encode_with_error_handling)

= 2.9.0 - 2026-02-23 =
* Fix: Crash on PHP 8.x when plugin loading order puts basic before premium (e.g. after auto-update) — now defers premium loading via plugins_loaded hook
* Fix: Crash in get_expiration() on PHP 8.x when license data contains invalid JSON
* New: Old premium versions (< 1.6.0) are now gracefully blocked instead of causing fatal errors — site runs as free with admin warning
* New: Serial key field shown in basic plugin settings when old premium is detected, so users can still manage their license
* Improvement: License check now properly throttles retries when serial key is invalid, preventing excessive server calls
* Improvement: Changing the serial key now immediately triggers a fresh license check instead of waiting for the next cycle
* Fix: Invalid serial key no longer causes license check on every page load
* Fix: All non-German translations (NL, ES, IT, PT_BR, PT_PT, HU, JA, ZH_CN) replaced with proper localized translations — previously contained German text
* Fix: Renewal and reactivation links in admin notices pointed to wrong URL
* Fix: Ticket redemption blocking ("deny redeem before event start") was off by the GMT offset for non-UTC timezones

= 2.8.10 - 2026-02-18 =
* Fix: Premium license recovery — license revalidation now runs even when local license data is stale, preventing permanent premium lockout
* Fix: Stale "notvalid" flag is now cleared when the license server confirms a valid subscription

= 2.8.8 - 2026-02-18 =
* Fix: Option "No login required to access scanner" (wcTicketScannerAllowedRoles) now works correctly again for non-authenticated users
* Fix: PHP 8.4 compatibility — explicit nullable type hints for deprecated implicit nullable parameters
* Improved: Updated translations for all 11 languages (DE, ES, FR, HU, IT, JA, NL, PT-BR, PT-PT, ZH) with 16 new strings from v2.8.6 and v2.8.7 (PWA, scanner options, format warnings)
* New: Ticket scanner shows timestamp of last scan for better tracking
* New: Admin notice when PHP version is below the required 8.1
* Improved: Corrected library version display in system info (FPDF 1.85, Twig PHP requirement note)

= 2.8.7 - 2026-02-16 =
* New: PWA (Progressive Web App) support — install the ticket scanner as a home screen app on mobile devices (optional, enable in settings)
* New: Fullscreen mode button for the ticket scanner — immersive scanning without browser chrome
* New: Haptic feedback (vibration) on scan result — different patterns for success and failure
* New: Customizable scanner theme color — affects PWA status bar, loading spinner, and scanner buttons
* New: Expanded FAQ with 7 categories and 21 questions covering scanner setup, PDF design, WooCommerce, webhooks, and more
* Improved: Scanner HTML now uses proper DOCTYPE, charset, and viewport meta tags
* Improved: Scanner assets (JS libraries, CSS, images) cached by service worker for faster loading
* Security: Hardened premium license revalidation with independent subscription verification
* Security: Added HMAC checksum protection against license data tampering in database
* Fix: Resolved infinite recursion during plugin initialization when premium plugin is active
* Fix: Translation loading no longer triggers WordPress 6.7+ "too early" notice
* Fix: License checksum verification now stable across CLI and web contexts
* Fix: License server response now correctly overrides local data (fixed array_merge order)
* Fix: API connection failures are now tracked; premium deactivates after 5 consecutive failures
* Fix: Admin page loads now trigger license check if last check was >24h ago (cron fallback)
* Fix: License key input field now always visible when premium plugin is installed

= 2.8.6 - 2026-02-11 =
* New: Full bleed mode option for ticket designer — removes ALL margins for edge-to-edge background images.
* New: Separate full bleed option for test ticket designer.
* New: Ticket background color option — set a custom background color for PDF tickets, badges, and flyers.
* New: Ticket number format exhaustion warning — alerts admin when ticket format is running out of combinations (counter-based detection).
* New: Automatic email notifications when ticket format reaches 50% capacity or is exhausted (once per day).
* New: Admin notice with dismiss button for format warnings, includes direct link to edit ticket list.
* New: Format warning auto-clears when ticket list is manually saved (user likely adjusted formatter).
* Security: Fixed unauthenticated REST endpoint that exposed admin dispatcher (CVSS 9 — reported by Patchstack).
* Security: REST permission callback now requires authentication (login or authtoken) by default.
* Security: Added capability guards for sensitive admin actions (defense in depth).
* Security: Removed wp_rest nonce fallback from admin action handler.

= 2.8.5 - 2026-01-26 =
* Fix: Fatal error on non-premium installations when SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION constant is undefined.
* Improved: VollstartValidatorDebug URL parameter now passed through to AJAX requests for easier debugging.

= 2.8.4 - 2026-01-26 =
* New: Seating plan visualization in ticket scanner — view venue map with highlighted seat position.
* New: Option to enable/disable seating plan button in scanner.
* New: Option to enable/disable venue image button in scanner.
* Improved: Seating plan data loaded on demand (lazy loading) for better scanner performance.
* New: Shortcode parameter `order_id` for [sasoEventTicketsValidator_code] to display tickets from a specific order.
* New: Shortcode [sasoEventTicketsValidator_ticket_detail] to display ticket detail view on any page.
* Fix: Date localization now correctly displays translated month/day names while maintaining correct timezone handling.
* Fix: Fatal error in removeUsedInformationFromCode() when changing order status.

= 2.8.3 - 2026-01-22 =
* New: Seating Designer — Element rotation (0-359°) with preset buttons.
* New: Seating Designer — Group rotation (rotate multiple selected elements around their common center).
* New: Seating Designer — Bulk property editing when multiple elements are selected.
* New: Seating Designer — Duplicate selection for groups (copy multiple elements at once).
* New: Seating Designer — Enhanced visual feedback for multi-selected elements.
* Improved: Seating API now uses unified bulk operations for better performance.
* Fix: PDF QR code compatibility with third-party FPDI libraries.
* Fix: Event times displayed incorrectly due to double timezone conversion in ticket templates.

= 2.8.2 - 2026-01-21 =
* New: Clone/duplicate seating plans with all seats and layout.
* New: Batch operations for seats (activate, deactivate, delete multiple seats at once).
* New: Premium subscription expiration check with admin warnings and grace period.
* New: Lifetime license support.
* New: Delete All Tickets button on ticket list with double confirmation.
* New: Shortcode parameter download_all_pdf for downloading all user tickets as one PDF.
* New: Safety check when deleting ticket lists — warns if list is still assigned to products.
* Fix: Restored deprecated methods for backward compatibility with older premium plugin versions.

= 2.8.1 - 2026-01-20 =
* Fix: PHP 8.4 compatibility — explicit nullable type for REST API parameter.

= 2.8.0 - 2026-01-20 =
* New: Interactive seating plan designer with drag & drop editor for creating venue layouts.
* New: Let customers choose their seats during checkout with visual seat selection.
* New: Seat information displayed on PDF tickets, in emails, and in the ticket scanner.
* New: Automatic seat blocking during checkout with configurable timeout.
* New: Seat release on order cancel or refund.
* New: Seating plan support on shop/archive pages.
* New: Voice output for the ticket scanner.
* Improved: Security checks for admin area access.
* Improved: Public ticket number now displayed in emails instead of internal ticket number.
* Fix: Ticket list description slashes.
* Fix: Customer search in admin backend.
