{
  "name": "Front of House for Event Espresso",
  "slug": "eea-foh",
  "version": "0.23.6",
  "download_url": "https://gitea.mulligan.casa/ee-extensions/foh-updates/raw/branch/main/eea-foh/eea-foh-0.23.6.zip",
  "homepage": "https://gitea.mulligan.casa/ee-extensions/eea-foh",
  "requires": "6.4",
  "tested": "7.0",
  "requires_php": "8.1",
  "last_updated": "2026-06-27 01:02:04",
  "icons": {
    "1x": "https://gitea.mulligan.casa/ee-extensions/foh-updates/raw/branch/main/eea-foh/icon-128x128.png",
    "2x": "https://gitea.mulligan.casa/ee-extensions/foh-updates/raw/branch/main/eea-foh/icon-256x256.png"
  },
  "author": "Mike Mulligan",
  "author_homepage": "https://gitea.mulligan.casa/mmulligan",
  "tags": [
    "event espresso",
    "point of sale",
    "box office",
    "front of house",
    "ticketing",
    "registration"
  ],
  "sections": {
    "description": "<p><strong>Front of House for Event Espresso</strong> runs the box office of a live event from a tablet. Each device is trusted by an administrator, every action is attributed to the clerk who took it, and the whole shift is captured in an append-only audit trail. It is an Event Espresso add-on — it reads and writes through Event Espresso's own models, so a sale, a payment, or a return at the door is the same real transaction it would be in wp-admin.</p>\n<p>The station runs as an installable PWA with no WordPress chrome: a clerk signs in with a badge, the tablet shows a fast registration search or a sales grid, and the door keeps moving.</p>\n<p><strong>What a station can do</strong></p>\n<ul>\n<li><strong>Check-in.</strong> Ranked name/email search, camera or hardware-wedge barcode scanning, cascade check-in across an event's datetimes, check-out, undo, and a manager-authorized force check-in for non-approved registrations. A segmented filter (All / Needs check-in / Needs payment) narrows the line to exactly who needs attention.</li>\n<li><strong>Sell / POS.</strong> A category-tabbed item grid with per-ticket variations and quantity steppers, a persistent running-total cart, registrant questions, then a real Event Espresso transaction with automatic tax and full rollback if anything fails — no orphaned rows.</li>\n<li><strong>In-person payments.</strong> Cash (with amount-tendered entry and live change-due), check, and card-present through a pluggable provider add-on. A shared transaction screen carries a role-governed payment gate so only permitted clerks can take money.</li>\n<li><strong>Returns, exchanges, and customer credit.</strong> Returns and exchanges settle as real Event Espresso transactions — replacement items are genuine ticket purchases, returned goods are a typed return payment, and the server computes the net. Overpayments resolve to a cash refund (role- and event-gated), a forfeit, or store credit parked on the customer's account (via a companion credit plugin).</li>\n<li><strong>Bluetooth thermal receipts.</strong> Print itemized receipts straight from the tablet to a 58mm thermal printer over Web Bluetooth — templated header and footer with token substitution, money-aligned line items, and a configurable tear feed. On-device test buttons prove the printer without ringing up a sale.</li>\n<li><strong>Clerk badges + scan-to-login.</strong> Print a badge per clerk carrying a CODE128 barcode and a QR; a clerk signs in by scanning that badge with the device camera or a hardware wedge scanner, or by typing a code.</li>\n<li><strong>Manager assist.</strong> A permitted manager can log in <em>over</em> an active clerk for a single task — a stacked, temporary session that preserves the base clerk's session and resumes it on exit.</li>\n<li><strong>Per-device themes.</strong> Four CSS themes (a system-following default plus dark, light, and high-contrast variants), chosen per device by a manager.</li>\n</ul>\n<p><strong>Built for the front desk</strong></p>\n<ul>\n<li><strong>Device trust.</strong> A device enrolls from an enrollment link, an admin approves it with a nickname and station, and the device then operates on a one-time-delivered secret — the operating credential, not a shared token.</li>\n<li><strong>Clerks, not WP users.</strong> Clerks are lightweight badge/PIN identities with configurable roles, idle and focus-loss timeouts, multi-login policy, and station scoping — volunteers never touch the WordPress user table or wp-admin.</li>\n<li><strong>Generic and reusable.</strong> Core stays event-agnostic and ticket-native. Filters let companion plugins layer on ticket categories, row enrichment, payment providers, and customer credit without forking the core.</li>\n<li><strong>Multisite-ready.</strong> Activate per site on a multisite network.</li>\n</ul>\n<p>Full documentation and architecture notes live in the bundled <strong>README.md</strong>.</p>",
    "installation": "<ol>\n<li>Install and activate <strong>Event Espresso 5</strong> on the site first — this plugin is an Event Espresso add-on and does nothing without it.</li>\n<li>Upload the plugin to <code>wp-content/plugins/</code> and activate it (activate per site on multisite).</li>\n<li>Open the <strong>Front of House</strong> admin menu and set up your <strong>Access Profiles</strong>, <strong>Clerk Roles</strong>, <strong>Clerks</strong>, and <strong>Settings</strong> (including receipt header/footer).</li>\n<li>Create an <strong>Enrollment Link</strong>, open it on a tablet, install the PWA, and approve the device under <strong>Devices</strong>.</li>\n<li>The site should be served over <strong>HTTPS</strong> — the camera scanner and Bluetooth receipt printing require a secure context.</li>\n</ol>",
    "faq": "<p><strong>Does this require Event Espresso?</strong></p>\n<p>Yes. It is an Event Espresso add-on and is tested against Event Espresso 5. Sales, payments, check-ins, and returns are written through Event Espresso's own models, so they are identical to what wp-admin would produce.</p>\n<p><strong>Why does it need HTTPS?</strong></p>\n<p>The camera-based barcode/QR scanner (camera access) and Bluetooth thermal printing (Web Bluetooth) only work in a secure browser context. Serve the site over HTTPS and the station works on tablets and phones.</p>\n<p><strong>How do clerks log in?</strong></p>\n<p>A clerk signs in to a station with a badge — scanned by the device camera, read by a hardware wedge scanner, or typed in as a code. Clerks are not WordPress users; they are managed under <strong>Front of House → Clerks</strong> and governed by configurable roles. Treat a printed badge as a physical login key.</p>\n<p><strong>What happens when the network drops?</strong></p>\n<p>The station distinguishes a genuine network failure (an offline banner) from a server-side response such as a too-short search or an out-of-scope result (shown inline). When the card-present path charges but recording the payment fails, the station does not swallow it: it retries, prints a manager error slip, records an audit incident, and shows a blocking manager modal.</p>\n<p><strong>Can I add card payments or store credit?</strong></p>\n<p>Yes. Cash and check are built in. Card-present payments come from a pluggable provider add-on, and store credit / pay-with-credit come from a companion customer-credit plugin. When those companions are absent, the station hides the affordances entirely.</p>\n<p><strong>Does it work on multisite?</strong></p>\n<p>Yes. Activate it per site on a multisite network.</p>",
    "changelog": "<p><strong>0.23.6</strong><br />\n* Card return parsing. With 0.23.5 the card finally bumps back, so the return handler ran against a real Collect return for the first time — and reported \"Card payment declined.\" It now reads the provider's params from the URL query OR fragment (an Android intent re-entry can deliver them after <code>#</code> instead of <code>?</code>, leaving the query empty and the status read as null → false decline), and records the amount we launched Collect with (full-remaining balance + surcharge, in cents) instead of the provider's dollar-formatted echo. A temporary diagnostic appends the raw return string to any decline message so the exact return shape is visible on-device; removed once locked.</p>\n<p><strong>0.23.5</strong><br />\n* Card return — the real bump-back blocker. 0.23.4 made the card launch a tapped anchor (correct), but a real card sale still ran standalone while the diagnostic bumped back. The cause was the launch description: the card sent <code>Txn #&lt;id&gt; — &lt;title&gt;</code>, and Collect's deep-link handler decodes the whole query string once before re-parsing, so the <code>#</code> (valid as <code>%23</code> in the link) decodes to a literal <code>#</code>, starts a URL fragment, and swallows the trailing <code>&amp;callback=…</code>. With no callback Collect runs standalone and never returns. The description is now sanitized to plain ASCII (no <code>#</code>, em dash, or punctuation). A second temporary Diagnostics probe (\"# in description\") deliberately reproduces the failure to confirm the cause; both diagnostics are removed once the live card flow is confirmed.</p>\n<p><strong>0.23.4</strong><br />\n* Card bump-back fix: the \"Pay with card\" action now launches the card-present provider (Collect) via a real tapped <code>&lt;a href&gt;</code> instead of a programmatic <code>window.location.assign</code>. From an installed Android WebAPK only a user-tapped anchor hands the launchApp deep link to the provider as an Android intent, so the provider runs integrated and returns to our <code>/foh/payment-result</code> callback (auto-bumps back into the app); a programmatic navigation made it run standalone and return to its own home. The temporary Diagnostics test link stays for one more cycle. Manifest unchanged — no reinstall; reopen the app after updating.</p>\n<p><strong>0.23.3</strong><br />\n* TEMPORARY diagnostic: a \"Test: $1 Collect charge\" link under Settings (hamburger) ▸ Diagnostics. It launches Collect via a real tapped <code>&lt;a href&gt;</code> (the way the proven probe did) instead of the app's programmatic <code>window.location.assign</code>, to isolate why the card return doesn't bump back into the app. Manifest unchanged — no reinstall needed, just reopen the app after updating. Remove once diagnosed.</p>\n<p><strong>0.23.2</strong><br />\n* Revert the 0.23.1 manifest link-capture keys. On Android the installed WebAPK already captures in-scope https returns purely from <code>scope</code> (the proven, working mechanism — Collect's redirect to an in-scope <code>/foh/</code> URL re-enters the app). <code>handle_links</code>/<code>launch_handler</code> are desktop-only no-ops there, and the explicit <code>id</code> risked changing WebAPK identity (treated as a different app without <code>legacy_ids</code>). Manifest is back to the minimal scope-only form. <strong>Reinstall the PWA</strong> (uninstall + Chrome \"Install app\") so the WebAPK re-mints its <code>/foh/</code> intent filters.</p>\n<p><strong>0.23.1</strong><br />\n* PWA link capture: the station manifest now declares <code>handle_links: preferred</code> + <code>launch_handler: navigate-existing</code> (and an explicit <code>id</code>) so an installed station can capture an in-scope return navigation — e.g. a card-present provider redirecting back to <code>/foh/payment-result</code> after a charge — into the existing app window instead of a browser tab. Requires reinstalling the PWA to pick up the new manifest.</p>\n<p><strong>0.23.0</strong><br />\n* Registrations now keep their contact questions on a Purchase sale. A ticket flagged by a companion (via the new <code>eea_foh_ticket_collects_contact</code> filter) — e.g. an \"attendee\"-class registration — keeps its contact/personal questions even on an anonymous Purchase, so the attendee's details are still captured. Anonymous merch/meal tickets are unchanged (contact still suppressed for a one-tap counter sale).<br />\n* Fix: corrected the card-present return fallback to match Collect's actual callback (<code>result=success</code>). Pair with eea-foh-collect 0.3.2.</p>\n<p><strong>0.22.3</strong><br />\n* Screenshots tab now uses the gallery pipeline (captions paired with screenshot-N images); with no images committed yet, the captions-only tab is dropped. No functional change.</p>\n<p><strong>0.22.2</strong><br />\n* Update-details sections now come from readme.txt (WP-native); base/config renamed to .mu-manifest.yaml + .mu-release.yaml. No functional change.</p>\n<p><strong>0.22.1</strong><br />\n* Release now flows through the shared manifest pipeline; the \"View details\" modal carries a description, changelog, and tags. No functional change to the plugin.</p>\n<p><strong>0.22.0</strong><br />\n* Added a color-coded plugin icon (delivered through the update channel) and declared WordPress 7.0 compatibility. No functional change.</p>\n<p>The full history is in <strong>CHANGELOG.md</strong> bundled with the plugin.</p>\n<p><strong>0.21.2</strong><br />\n* Updates are now handled by the Manifest Updater plugin (via the standard \"Update URI\" header) instead of a bundled updater. No change to how Front of House works; this lets it run on a single site instead of network-wide. Requires the Manifest Updater plugin to be network-activated to receive update notifications.</p>\n<p><strong>0.21.1</strong><br />\n* Fixed for real (multisite): the \"payment method Return/Settlement was automatically deactivated\" admin notice that kept coming back. Root cause was that Front of House auto-activated its internal Return/Settlement methods on every site in the network and then kept reactivating them, which Event Espresso re-deactivated in a loop. Those methods are now created only on the site that actually records a return/settlement (the desk site), and the stale notice is cleared in every admin context. No effect on how returns/settlements work.</p>\n<p><strong>0.21.0</strong><br />\n* Per-method payment options. Settings ▸ Payment Methods now lets you set, per method, a custom button label, whether a check number is required, and (for card) a surcharge. A card surcharge is added to the sale as a real fee line item — it raises the total, prints on the receipt, and is recorded in Event Espresso — so the customer is actually charged it. Card payments include the surcharge in the amount charged on the reader.<br />\n* Card is now provided through the Collect for Stripe add-on's registration with Front of House, and Front of House owns enabling/disabling and labelling every method (including card) in one place. Update the Collect add-on to 0.2.0 alongside this release.<br />\n* The old \"Allow cash refunds\" setting is carried forward into the new Payment Methods section automatically; no reconfiguration needed.</p>\n<p><strong>0.20.1</strong><br />\n* Fixed a \"payment method was automatically deactivated\" admin notice that kept reappearing for the Return and Settlement payment methods. The methods themselves were working; the stale warning is now cleared properly (the previous fix was being undone by Event Espresso's own notice save).</p>\n<p><strong>0.20.0</strong><br />\n* Per-device payment hardware. Each device now has a \"Hardware\" block (card reader, cash drawer); unchecking one hides that payment method on that station's point of sale — card needs a card reader, cash needs a cash drawer. Visibility only: it never changes who may take payment, and nothing changes on existing devices (every device is treated as fully equipped until you say otherwise).</p>\n<p><strong>0.19.0</strong><br />\n* A single home for payment methods. A new \"Payment Methods\" settings section turns cash, check, and card on or off and sets cash-refund policy in one place; an access profile can restrict which methods a given station offers. Nothing changes on an existing install — every method stays enabled exactly as before until you adjust it. Under the hood the method set is now data-driven (a registry) so future methods can plug in cleanly.</p>\n<p><strong>0.18.1</strong><br />\n* Check payments at the desk (activates Event Espresso's Check method in the admin scope). Server-side event-scope enforcement on every payment/return endpoint (a clerk can only act on their own event's transactions). Clerk/manager codes widened to 10 digits with a per-device brute-force lockout, plus a remote \"Clear lockout\" admin action.</p>\n<p><strong>0.18.0</strong><br />\n* Card-present payments are verified against Stripe before approval (no more trusting the return URL). Every first-touch money write (sales, exchanges, cash, credit, settlements) is now idempotent against a lost-response retry. Audit-log failures are no longer silent.</p>\n<p><strong>0.17.2</strong><br />\n* The Return and Settlement payment methods now reactivate themselves automatically after a plugin update — no manual step on the Payment Methods screen, ever.</p>\n<p><strong>0.17.1</strong><br />\n* Walk-up Return (card-less return/exchange). Sales and returns now verify the transaction's true status in Event Espresso before completing — no more false \"Sale Complete\" on an unpaid exit.</p>\n<p><strong>0.17.0</strong><br />\n* Admin-configurable branding (receipt logo, app icon, splash, background) and Bluetooth badge printing in wp-admin (one badge at a time, tear between).</p>\n<p><strong>0.16.2</strong><br />\n* Service worker REST namespace fix (eea-checkin/v1 -&gt; eea-foh/v1); no behavior change.</p>\n<p><strong>0.16.1</strong><br />\n* Update channel + repository URLs moved to the ee-extensions org. Removed a hard-coded external domain from the receipt printer's barcode/QR test page.</p>\n<p><strong>0.16.0</strong><br />\n* Taking payment is now a lookup filter (All / Needs check-in / Needs payment), not a separate mode. Sell remains its own mode. No schema or server change.</p>\n<p><strong>0.15.0</strong><br />\n* Printable clerk badges plus camera and hardware-wedge scan-to-login, with per-device camera configuration.</p>\n<p><strong>0.14.x</strong><br />\n* Bluetooth thermal receipt printing with templated header/footer, itemized line items, and configurable tear feed; a dedicated receipt-printer configuration modal with on-device test buttons; loud \"charged but unrecorded\" incident handling for the card-present path.</p>\n<p><strong>0.13.0</strong><br />\n* Returns, exchanges, refunds, and customer credit settled as real Event Espresso transactions, with cash-refund/forfeit/store-credit settlement and exchange tax suppression.</p>\n<p><strong>0.12.0</strong><br />\n* Card-present payments via a pluggable, provider-agnostic descriptor model; cash tender with change; on-demand emailed receipts.</p>\n<p><strong>0.11.0</strong><br />\n* Sell / POS mode — front-desk sales through a category grid and persistent cart, building real Event Espresso transactions with automatic tax and full rollback.</p>\n<p><strong>0.9.0 - 0.10.0</strong><br />\n* Station modes, pluggable in-person payments, the corrected visibility-vs-action check-in model, assistive (stacked) manager login, per-device themes, and a Q&amp;A + payments details modal.</p>\n<p><strong>0.5.0 - 0.7.x</strong><br />\n* Device-secret authentication and nonce-sealed enrollment, first-class enrollment links, configurable clerk roles and stateful sessions, clerk-to-station scoping, linked-registration reverse lookup, and a role-gated registration details modal.</p>\n<p><strong>0.1.0 - 0.4.0</strong><br />\n* Foundation: profiles/devices/clerks/audit data model, REST layer, ranked search, check-in over Event Espresso models, the device approval handshake, and the station PWA.</p>"
  }
}
