JavisTab Booking Pro | Changelog

JavisTab Booking Pro · Release Notes

What shipped, line by line.

The JavisTab Booking Pro changelog documents every public release of the JavisTab WordPress restaurant booking plugin — from the first v3.6.0 baseline through today’s v5.9.15 shipping build. Each entry pairs the customer-facing benefit with the engineering detail that made it possible: root cause, fix, files touched, and where applicable, before/after benchmarks at production scale (1 million bookings, 500 k customers, 13 languages, 6 payment gateways). Use it to track upgrades, audit security patches, or just to see what improved since you last updated.

📦 Current build: v5.9.15 📅 Released:  ⚙️ Minimum: WordPress 5.8 · PHP 7.4 🌐 Languages: 13 (full 169-key parity) 💳 Gateways: 6 (Stripe, PayPal, Square, Razorpay, OnePay, VietQR)
45+ Documented releases
13 Supported languages
1M+ Booking scalability
0% Added gateway fees

Release categories

  • 🆕 Feature
  • 🛠 Bugfix
  • ✨ Improvement
  • 🔐 Security
  • ⚡ Performance
  • 🌐 Internationalisation
  • 🔴 Critical
v5.9.15 ★ Current Release
✨ Stability 🆕 Feature 🛠 Bugfix

Production build — Invoice → Voucher quick-link + 1-admin-email guarantee

v5.9.15 is the shipping build that consolidates every fix and feature from the v5.9.14 development cycle into the stable channel. Two material changes carry forward: a one-click View Voucher shortcut on the printable Invoice view (industry-standard PDF workflow — no PDF library required), and a Stripe-style idempotency guard that finally collapses bank-transfer admin notifications to exactly one email per booking across all four payment paths.

🎫 Invoice → Voucher quick-link (Airbnb / Booking.com / Resy pattern)

Feature

Admins can now jump from the printable Invoice to the matching Voucher with a single tap. The new 🎫 View Voucher button sits beside the existing 🖨️ Print / Save as PDF button and opens the voucher in a new tab so the host can browser-print both as PDFs in one workflow.

Why this design Mirrors the Airbnb / Booking.com / Resy pattern of link to printable view → browser-native PDF save. No TCPDF, dompdf or mPDF dependency. Avoids the spam-filter fall-out from binary attachments that prompted the v5.9.12 removal of HTML voucher attachments. Server-enforced by maybe_print_voucher() (nonce +  rbp_view_bookings/manage_options). Hidden when the invoice has no booking_ref (orphan invoices) and wrapped in no-print so it never appears in the printed output.

BUG-ADMIN-DUP — duplicate admin notification on Bank Transfer flow

Bugfix Critical

Symptom: Admins received two “🔔 New Booking” emails for the same Bank-Transfer booking — the first when the customer reserved, the second when they clicked “I confirmed my transfer”.

Root cause: RBP_Email::notify_staff_new_booking() was invoked from two code paths (RBP_Booking::create() and RBP_API::bank_transfer_confirm()) with no deduplication. The other three payment types sent exactly one email only by coincidence.

Fix: Added an idempotency guard via a 30-day WordPress transient (rbp_admin_notified_{booking_id}). Pattern mirrors Stripe webhook, AWS SES and Mailgun. A $force=true third argument is reserved for a future “resend admin notification” admin action.

Payment typeBefore v5.9.15After v5.9.15
at_restaurant1 email1 email ✓
deposit_required (online)1 email1 email ✓
prepaid (online full)1 email1 email ✓
Bank transfer (manual)2 emails1 email ✓
Why a transient, not a DB column No schema migration → safe upgrade on million-row installs. Auto-expires → no DB cruft. Reverting is trivial. Same trade-off Stripe’s idempotency keys make.

Voucher email toggle description updated

Improvement

The Attach booking voucher to confirmation email toggle in Settings → Notifications still described the legacy HTML-attachment behaviour. Renamed to “Show voucher link in confirmation email” with copy that accurately reflects the v5.9.12 inline-button behaviour and explains why HTML attachments were removed (global spam-filter issues with Gmail and Outlook).

Files javis-bookingpro.php includes/invoice/class-rbp-invoice.php includes/notifications/class-rbp-email.php admin/views/settings.php CHANGELOG.md

v5.9 series

Voucher workflow · email reliability · global i18n parity

The 5.9 line hardened the customer-facing email pipeline, shipped a printable booking voucher with QR check-in, and closed the long-tail of missing translation keys across Chinese, Korean, Japanese, French and German.

v5.9.14
🆕 Feature 🛠 Bugfix

Invoice voucher quick-link & bank-transfer deduplication

Development build that introduced the Invoice → Voucher button and the admin-email idempotency guard. Promoted to the stable channel as v5.9.15 — see the current release card above for the full breakdown.

Files includes/invoice/class-rbp-invoice.php includes/notifications/class-rbp-email.php admin/views/settings.php
v5.9.13
🌐 i18n 🛠 Bugfix 🔴 Critical

13-language key parity, per-transaction payment email, tax for venue bookings

A triple-fix release that finishes the translation work started in v5.9.5, aligns payment-confirmation emails with Stripe/PayPal/Square conventions, and applies service charge plus tax to at_restaurant bookings for the first time.

BUG-LANG-5 — 76 keys missing in Chinese & Korean (plus 4 other locales)

i18n

Customers using the booking form in 简体中文 (zh), 한국어 (ko), 繁體中文 (zh_TW), 日本語 (ja), Français (fr) or Deutsch (de) saw mixed-language UI: labels were translated but most validation errors, status badges, payment-widget strings, placeholders and the Step 2/3/4 captions fell back to English.

LanguageKeys beforeMissing vs en
zh — Simplified Chinese9476
ko — Korean9476
zh_TW — Traditional Chinese11755
ja — Japanese13043
fr — French15712
de — German1684

All 13 supported languages now ship the full 169-key set. Translations follow the same native-translation policy used in v5.9.5 — no machine-translated artifacts. Static key parity verified across en, vi, fr, zh, ko, zh_TW, ja, de, it, es, id, pt_BR, pt.

BUG-EMAIL-AMT — payment-received email showed cumulative paid_amount

Bugfix Critical

On multi-payment bookings (deposit online, top-up later), the second Payment received email displayed the running total paid instead of the amount of the new transaction. Stripe, PayPal and Square all email per-transaction amounts — this plugin was the outlier.

Fix: Three-part change. RBP_Payment::confirm() and the rbp_record_manual_payment AJAX handler now pass payment_amount (the per-transaction value) into RBP_Booking::update_status(), which reads it as the primary source for the email amount. A four-step fallback chain is preserved for legacy callers. The email also no longer fires on seated → confirmed transitions (undo check-in) — only on a legitimate first confirmation or an explicit admin-recorded payment.

Service Charge & Tax now apply to at_restaurant bookings

Improvement

Pay-at-venue bookings were the only payment type that previously skipped SC + tax. Hospitality-standard behaviour applies them at the same rates as deposit and prepaid flows. Existing prepaid/deposit math is untouched.

Files includes/class-rbp-i18n.php includes/booking/class-rbp-booking.php includes/payment/class-rbp-payment.php admin/ajax-handlers.php
v5.9.12
🛠 Bugfix ✨ Improvement 🔴 Critical

Voucher total reconciliation · email link replaces HTML attachment · QR frame centring

Three concurrent fixes: the printable Voucher Grand Total now matches the Invoice and Payment Summary; the confirmation email switches from a .html attachment (flagged as suspicious by Gmail/Outlook globally) to an inline View & Print Voucher button; and the QR scanner viewfinder is finally pixel-centred in Safari.

Voucher Grand Total mismatch with discounts

Bugfix

The Voucher always displayed total_amount (pre-discount), ignoring the disc_pre_tax logic that bookings.php and class-rbp-invoice.php already implemented. The Voucher now uses identical Amount Due logic across all four payment modes (disc_pre_tax+prepaid, standard discount, deposit_required, no discount). Layout now mirrors the Payment Summary and Invoice exactly.

Email voucher: inline link replaces .html attachment

Improvement Security

HTML file attachments are flagged as potentially malicious by Gmail, Outlook and corporate filters worldwide — confirmation emails were landing in spam or showing security warnings. The attachment is replaced by an inline 🎫 View & Print Voucher button linking to a new token-authenticated public route (?rbp_voucher=REF&t=TOKEN) using the existing 32-character checkin_token with hash_equals() for timing-attack safety. Two new template variables ({{voucher_url}}, {{voucher_link_block}}) are available in the editable email templates.

QR scanner viewfinder off-centre (Safari)

Bugfix

The scan border and corner marks (::before + ::after) were left-aligned in WebKit because the flexbox centring of box-shadow-based overlays is unreliable. Switched both pseudo-elements to absolute positioning with top:50%;left:50%;transform:translate(-50%,-50%) — pixel-perfect on every browser.

Files class-rbp-voucher.php class-rbp-email.php class-rbp-helpers.php admin/views/qr-scanner.php
v5.9.11
🛠 Bugfix 🆕 Feature ⚡ Performance 🔴 Critical

Locale-safe language switcher · dashboard period tabs · 1M-booking scalability

Critical fix for Traditional Chinese (zh_TW) and Brazilian Portuguese (pt_BR) customers whose locale code was silently lower-cased to the empty fallback. Dashboard gains Yesterday, Last Week and Last Month tabs. Top Services and Busiest Staff queries are now sargable on the idx_rest_status_date composite index — full table scans eliminated on the hot path.

  • Locale fix: WordPress’s sanitize_key() lower-cases locale codes (zh_TW → zh_tw), failing the case-sensitive whitelist in RBP_I18n::languages(). New utility RBP_Helpers::sanitize_locale() strips unsafe characters without lower-casing — drop-in replacement at three call sites.
  • Performance: Replaced MONTH(booking_date) = MONTH(CURDATE()) patterns (index-killers) with sargable BETWEEN over the stats date range. Confirmed all dashboard, list, count and stats queries use bounded ranges + composite indexes — no unbounded scans remain.
Files class-rbp-helpers.php class-rbp-api.php booking-form.php admin/views/dashboard.php
v5.9.10
🆕 Feature

Voucher / Print Settings tab

New Settings tab governing the printable booking voucher: configurable accent colour (with fall-back to brand colour), terms & conditions rendered as a bullet list, and layout options. The “Reseed email templates” action now forces a fresh reseed on installs that already ran the seed once, clearing legacy icon-format rows.

v5.9.9
🆕 Feature

Printable booking confirmation voucher

New Print Voucher button on the booking detail screen renders a clean, print-optimised confirmation page with booking details, QR check-in code and restaurant branding (handler at ?rbp_print_voucher=1&booking_id=…). Optionally attaches to confirmation emails — replaced by an inline link in v5.9.12 for spam-filter compatibility. New class RBP_Voucher generates the HTML with configurable layout, colours and terms.

v5.9.8
✨ Improvement 🛠 Bugfix

Refund UX polish & QR scanner layout fix

Payment history in the booking detail now includes refund records (timeline view) with a refund-summary banner above the payment table. The refund button correctly hides when paid_amount - total_refunded ≤ 0. QR scanner CSS handles the intermediate wrapper <div> that html5-qrcode renders on some themes. Refund-notification email is restyled to match the v5.9.7 design system.

v5.9.7
🆕 Feature ✨ Improvement

Modern card-based Settings UI · full & partial refund flow

The Settings area gets a card-based redesign with a refined tab order (General → Bookings → Design → Guest Groups → Tax & Pricing → Payments → Notifications → Email Templates → Form Labels → Form Messages → Integrations → Form Integrations → POS Sync → Loyalty → Tracking → Performance → Diagnostics). New CSS tokens (--rbp-accent, --rbp-surface, --rbp-shadow-*, --rbp-radius) underpin pill-style tab navigation and four banner variants (info/success/warning/danger).

Refund logic (full & partial)

Feature

A new ↩ Refund button appears in Booking Details when paid_amount > 0 and payment_status ≠ refunded. Opens a professional modal with quick-select (Full / 50% / Custom), real-time validation, a preset reason dropdown (6 common reasons + custom text) and a customer-notification toggle (defaults ON).

AJAX handler rbp_record_refund — capability gate (rbp_manage_bookings), CSRF nonce, amount validation (refund_amount ≤ booking.paid_amount), inserts a gateway='refund' payment row, recalculates paid_amount, flips invoice payment state, refreshes the customer total_spent cache, fires the rbp_booking_refunded action hook for add-ons, and sends the localised refund email when the toggle allows.

New refund email template (VI / EN / FR / DE) with 💸 red gradient hero, prominent refund amount block, details table (booking ref, date, time, reason) and a tip block with bank-processing timeline guidance. All four language variants are auto-generated via the $keys array.

Files admin/views/bookings.php admin/ajax-handlers.php includes/notifications/class-rbp-email.php includes/email-templates/templates-data.php assets/css/admin.css
v5.9.6
🛠 Bugfix ✨ Mobile

BUG-QR-1 — QR Check-in Station fully responsive on phones

Camera card, search field and Start Camera button no longer slip off-screen in portrait mode. Landscape grid stacks correctly. Touch targets meet Apple HIG’s 44×44px minimum. iOS Safari search input no longer auto-zooms. Flash/confirm modals get proper padding on narrow screens. End-to-end usability fix for high-volume host stands.

v5.9.5
🛠 Bugfix 🌐 i18n 🔴 Critical

Email hero icon · 133 missing translation keys for 5 locales · Vietnamese label fixes

First half of the 13-language parity work (completed in v5.9.13): Italian, Spanish, Indonesian, Brazilian and European Portuguese all stubbed with only ~49 keys since v5.6.7. Now ship the full 176-key set with native translations. Vietnamese tables_left and guests_left finally translated. admin_strings() and presets() extended to cover all 13 languages (German, Italian, Spanish, Indonesian, Brazilian and European Portuguese previously fell through to English).

BUG-ICON — email hero icon rendered full-width in Gmail / Apple Mail

Bugfix

The 48px icon circle in every customer email rendered as a giant full-width block. Root cause: RBP_Email::wrap() declared a global table{width:100%} CSS rule. The v5.9.2 attempt to constrain the icon via <table width="48"> failed because tag-level rules beat HTML attributes and inline styles in Gmail’s CSS sanitiser.

Fix: rbp_tpl_hero() now uses <div> + <span>. The span isn’t matched by table{width:100%}, so the circle always renders at exactly 48px across all email clients.

Files includes/class-rbp-i18n.php includes/email-templates/templates-data.php
v5.9.4
🛠 Bugfix 🔴 Critical

Five critical fixes — PayPal undercharge, settings fatal, invoice line-items, Stripe webhook persistence

A consolidated audit pass on the payment subsystem closed five distinct bugs that could have caused financial inconsistency.

BUG#1 — Settings → Bookings save threw “critical error”

Bugfix

Clicking Save Changes with Auto-Complete After Arrival Date ON triggered WordPress’s fatal-error screen. Root cause: ( new RBP_Reminder() )->auto_complete_arrival() instantiated the singleton with a private constructor → PHP fatal. Fix: route through RBP_Reminder::instance().

BUG#2 — PayPal severe undercharge when base currency is non-USD

Bugfix Critical

A VND-base customer seeing $211.62 on the PayPal panel was being charged ~$0.0086 — off by a factor of the FX rate. The non-PayPal-supported base branch converted $amount to USD but never set $currency = 'USD', so PHP forwarded NULL (not the default 'USD', because defaults only apply to omitted arguments, not undefined ones). resolve_currency() then took the unsupported-currency path and multiplied by the USD rate a second time. Fix: assign $currency = 'USD' explicitly inside both code branches.

BUG#3 — PayPal panel displayed wildly wrong amount

Bugfix

Display showed $6,127,450,980 instead of $10.20. Root cause: booking-form.js still divided by paypalRate using the pre-v5.6.11 convention. The v5.6.11 inversion (1 base = N foreign) was applied to convertCurr and fmtCurr but the PayPal block was missed.

BUG#4 — Invoice line items broke ratio reconcile

Bugfix

Adult $20 × 2 + Child $10 × 1 = $50 became Adult $15 + Child $7.50 = $37.50 when reconciled against a wrong-low price_breakdown.subtotal. The v5.9.2 scale-by-ratio block trusted the breakdown value and scaled unit prices down. Fix: new reconcile_line_items() helper does ratio-preserving distribution — reads guest-line ratios (adults : children : infants), distributes the authoritative subtotal across them, derives unit price from group_share / group_qty. Legacy invoices auto-correct on next page-load via the same reconcile in render_html().

BUG#5 — Stripe webhook didn’t persist event payload

Bugfix

Stripe-confirmed payments had gateway_response = NULL in rbp_payments, while PayPal / Square / Razorpay / OnePay all persisted the full payload. Forensic-audit gap. Fix: handle_webhook() now passes the full Stripe event to RBP_Payment::confirm( $pay->id, $txn_id, $event ).

Audit pass: End-to-end review of Stripe (Payment Intents + webhook + zero-decimal mapping), PayPal (create + capture + IPN with pull-verify fallback), Square (capture + HMAC webhook + idempotency_key retry-safety), Razorpay (HMAC key_secret + INR paise mapping) and OnePay Vietnam (HMAC-SHA256 with hex-decoded hash_key, path-based vpc_CallbackURL, IPN idempotency). No further fixes required.

Files admin/views/settings.php includes/class-rbp-api.php assets/js/booking-form.js includes/invoice/class-rbp-invoice.php includes/payment/class-rbp-stripe.php

v5.8 series

Elegant payment cards · price summary modes · check-in mobile

The 5.8 line introduced the Luxury Heritage payment card design, codified the Mode A / Mode B price summary spec, and made the Check-in Station genuinely usable on laptops, tablets and phones.

v5.8.6
🆕 Feature ✨ Design

“Elegant / Luxury” payment card style

A complete redesign of the Elegant payment card aesthetic for premium hospitality brands. 3-column desktop grid (collapsing to 2 / 1 on tablet / mobile) with three distinct dark-fill selected states — deep forest green for pay-at-venue, charcoal for deposit, deep navy for full prepayment.

Design system Amex / Marriott / Four Seasons reference. Typography-only, no icons. Selected = full dark background + white text (high contrast). Title 10px, letter-spacing 1.6px, uppercase. Amount 15px, font-weight 500, tabular-nums. CTA outlined on idle, solid on hover/selected, 1px corner radius.
v5.8.5
🆕 Feature

Template List grouped by language · Payment Card Style selector

Language filter tabs above the Email Templates list — All (N), English (N), Tiếng Việt (N), Français (N), etc. Active tab in burgundy; inactive grey. Edit URL preserves ?tpl_lang= so the active tab stays after saving. Settings → Design → Style Variants gets a Payment Card Style dropdown with Standard (default) and Elegant options + a live side-by-side preview.

v5.8.4
🛠 Bugfix

Check-in Station — camera button visible on 1366×768 laptops

Square aspect-ratio:1/1 gave the camera 340px+ height, pushing Start Camera below the viewport on 1366×768 laptops and split-screen windows. Now scales proportionally with max-height: min(340px, 38vh). Camera also auto-restarts 4 seconds after a successful scan — essential at high-volume check-in desks where staff would otherwise tap Start Camera for every guest.

v5.8.3
🛠 Bugfix

Mobile camera card · auto-complete arrival · marketing subject vars

Camera card stays visible on iOS Safari via explicit grid-row sizing. Enabling Auto-Complete After Arrival Date now runs immediately instead of waiting for the next page-visit (WP-Cron is pseudo-cron). Email template icon restored at the standardised 48px. {{variables}} in marketing campaign subject lines are now substituted per-recipient on both sync and async paths.

v5.8.2
🛠 Bugfix

Price Summary — Prepaid % shows immediately after Subtotal (Mode A)

Step 1–2 live sidebar now matches the Mode A spec: Subtotal → Prepaid % → After discount → +SC → +Tax. Mode B unchanged.

v5.8.1
✨ Cleanup

Removed prepaid_full_amount from the Tax & Pricing UI

The “💰 Pay in Full — Base Price (fallback)” field was confusing admins about its relationship to service / package pricing. UI removed; stored values continue to work for backward compatibility.

v5.8.0
🆕 Feature

Model A & Model B price summary layouts

Codified the two price-summary modes for discount handling. Mode A (disc_pre_tax=TRUE): discount applies to subtotal, tax recalculates on the reduced base, Grand Total is the final pay amount — no separate “Pay in Full” split. Mode B (disc_pre_tax=FALSE): discount applies to Grand Total, with a dashed separator splitting if you choose Pay in Full below. Three new CSS classes (.rbp-pbd-subtotal, .rbp-pbd-green, .rbp-pbd-grand-row) drive the visual hierarchy.

v5.7 series

Tax presets · 1M-booking scaling · Mailchimp-lite editor

The most architecturally significant series to date: an async job queue replaced synchronous reminder dispatch, six denormalised cache columns made 1M-row queries sub-second, and the Marketing area gained a wp_editor-based campaign composer with variable insertion and live preview.

v5.7.9
🛠 Bugfix 🔴 Critical

Prepaid discount applied twice in disc_pre_tax mode

Customers received ~10.25% off instead of the intended 5%. taxAmt / scAmt were overwritten with discounted values before grandBeforeDisc was computed; the partially-discounted hint then went to PHP which applied the discount a second time. Fix: snapshot originals in _origTaxAmt / _origScAmt before the disc_pre_tax branch. Plus: prepaid_discount is now synced between Tax & Pricing tab and the booking engine’s restaurant JSON (previously only wp_options was written, causing stale-value reads).

v5.7.8
🆕 Feature

🌐 Tax Region Preset — one-click tax configuration

Three independent toggles (tax_inclusive, sc_cascade_tax, disc_pre_tax) created 12 combinations that confused non-technical owners. New Region Preset dropdown collapses common configurations to one click.

PresetTypical countriestax_inclusivesc_cascade_taxdisc_pre_tax
🇺🇸 US / StandardUS, Canada, AU B2BOFFOFFON
🇪🇺 EU / UK InclusiveEU-27, UK, AU B2C, NZONOFFON
🇹🇭 SE Asia “++”Thailand, Malaysia, IndonesiaOFFONON
🇻🇳 VietnamVietnam domesticOFFOFFON
🇯🇵 JapanJapan (消費税 10%)ONOFFON
⚙️ CustomAdvanced usersManual control
Architecture Preset is a convenience layer only — the PHP and JS calculation engines still read the three underlying flags. The save handler enforces preset definitions server-side as a safety net. Existing installs default to tax_region=custom, preserving their manual configuration.
v5.7.7
🛠 Bugfix 🔴 Critical

Locale-safe decimals · mobile camera button · Pay-Full display consistency

Three concurrent fixes. _fmtAmt() in the post-redirect confirmed banner previously used Intl.NumberFormat() without an explicit locale — Vietnamese (vi-VN) browsers got 57,5 instead of 57.50. Now reads locale_separators from data-rbp-config. Camera preview compactly placeholders at aspect-ratio:4/3; max-height:200px on mobile pre-scan, expanding to aspect-ratio:3/4; max-height:55vh on activation. Pay-Full display now uses effectiveDiscAmt = grandBeforeDisc − grandTotal so the visible arithmetic adds up: $57.50 − $2.87 = $54.63 ✅.

v5.7.6
🛠 Bugfix ✨ Improvement

Exact-percentage discount display · multi-CDN QR scanner

Discount display now shows $2.875 (3-decimal exact) instead of $2.88 or $2.87 rounded variants for standard prepaid percentages, with a smart fallback to the DB value for complex stacked discounts. Amount Received clearly labelled “paid — gateway charged” to distinguish from Amount Due when currency rounding causes ≤1¢ deltas. New helper RBP_Helpers::format_money_precise() handles arbitrary decimal precision.

QR scanner reliability: Multi-CDN fallback (jsDelivr primary, unpkg fallback) for html5-qrcode; up-to-4-second retry on library load; explicit getUserMedia() pre-warm to surface the OS camera-permission dialog; platform-specific permission-denied messages (iPhone Settings → Safari → Camera; Android tap-camera-icon-in-address-bar).

v5.7.5
🛠 Bugfix 🔴 Critical

Six audit-pass fixes — payment summary math · i18n keys · diagnostics nonce

Closed six issues from a deep architecture review including: payment summary arithmetic inconsistency (Grand Total − Discount ≠ Amount Due) now derived from a single source of truth; Pay-in-Full card’s missing i18n keys (pay_now, discount_pct) replaced with the correct existing keys for all 8 languages; Diagnostics buttons that died on “The link you followed has expired” now use wp_verify_nonce() (silent false) instead of check_admin_referer() (wp_die()); 84px circular email icon replaced; auto-complete-arrival now fires for at_restaurant bookings.

v5.7.2
🆕 Feature ⚡ Performance ✨ Major

1M-booking scalability · async job queue · Mailchimp-lite editor · 5 new segments

The largest single release in JavisTab’s history. Four revisions over two days delivered a 1M-row scalable data layer, an async job queue for reminders / SMS / WhatsApp / marketing, a TinyMCE-powered campaign composer with variable insertion and live preview, five new HubSpot-style customer segments, and search + filter + pagination on the Invoices admin page.

Performance — synthetic 1M booking + 500k customer benchmark

Performance
Operationv5.7.1v5.7.2
Admin Customers page open~22s (timeout)<500ms
Marketing send (10k recipients)timeout<200ms (enqueued)
Dashboard stats~3.5s<80ms
Reminder cron (busy day)timeout<800ms enqueue, +50/min delivery
Booking creation (concurrent)race conditionatomic via UNIQUE
RBP_Availability::get_available_tables (40 tables)80 queries3 queries

Database — 14 indexes, 1 UNIQUE, 3 denormalised columns, 1 new table

Schema
  • 14 indexes across rbp_bookings, rbp_customers, rbp_payments, rbp_activity_log and rbp_email_log.
  • UNIQUE on rbp_customers(restaurant_id, identity) for atomic get_or_create().
  • 3 denormalised columns on rbp_customers: bookings_count, no_show_count, last_booking_at — backfilled once via 3-step UPDATE..JOIN, refreshed via RBP_Customer::refresh_cache().
  • New wp_rbp_jobs table for the async queue.

Async job queue — RBP_Jobs

Feature

New module includes/queue/ with a generic job queue supporting priority, delay, dedupe_key, max_attempts; atomic UPDATE-based claim worker; exponential-backoff retry (60s × 2attempts, capped at 1h); single-instance lock via MySQL GET_LOCK; daily 7-day retention prune. Worker fires every minute (new rbp_minute WP-Cron schedule); configurable batch size defaults to 50/minute = ~3,000/hour — safe for Mailgun, SendGrid and Twilio rate limits.

5 new HubSpot-style customer segments

Feature
SegmentThresholdUse case
💰 High Spenderstotal_spent ≥ NPremium event invites, top-tier perks
🔁 Frequent Visitorsvisit_count ≥ NLoyalty rewards, frequent-diner programs
⚠️ At Risklast_booking_at < N days AND visit_count ≥ MWin-back campaigns before losing high-value customers
✨ New Customerscreated_at within N daysWelcome series, intro offers
👨‍👩‍👧‍👦 Big Group BookersAt least one booking with guests ≥ NPrivate dining marketing, party promos

All 10 new methods (get_* + count_*) plus the 8 existing segments live in RBP_CRM. The dropdown has 3 optgroups: 🎯 Tier-based, 📊 Behaviour-based, 🕐 Recency-based. Live count preview via wp_ajax_rbp_segment_count debounced 300ms with 30-second wp_cache.

Mailchimp-lite custom-content editor

Feature

Full wp_editor (TinyMCE) with restricted toolbar (bold, italic, lists, link, colour, code — no media uploads, since campaigns are text-first). 18 variable pill buttons insert {{full_name}}, {{tier}}, {{booking_url}} etc. at the cursor in both Visual and Text modes. 5 starter templates: Blank, Simple Note, Event Invite, Special Offer, Friendly Reminder — load subject + body with a confirm-replace guard. Live preview iframe split-view 1fr : 1fr (stacks below 1100px) substitutes {{var}} placeholders with realistic sample values. 100% client-side preview — no new server endpoints, no new dependencies.

Invoices admin — search, filter, pagination

Feature

Multi-field search across invoice_number, booking_ref, customer name and email (prefix-LIKE on indexed columns, contains-LIKE on name/email — bounded so the query planner uses indexes even at 1M+ invoices). Status filter (All / ✅ Paid / ⏳ Partial / ❌ Unpaid). Date range with server-side YYYY-MM-DD validation. 30-per-page pagination with filter chips. Two new methods on RBP_Invoice: get_list() extended with search/pagination + new get_list_count(). Sub-50ms at 1M rows.

Migration safety: idempotent; gated by rbp_db_version + per-feature option flags; ALTER TABLE wrapped in hide_errors() to prevent abort on user-modified columns. Public API breaking changes: 0 — old methods retained as @deprecated for add-on compatibility.

v5.7.1
✨ Maintenance

Maintenance release

Internal hardening; no public API changes from v5.7.0.

v5.7.0
✨ Baseline

Pre-1M scaling baseline

Snapshot of plugin state before the v5.7.2 scalability work began. See v5.7.2 for the full set of improvements that followed.

v5.6 series

Global commercialisation · EU VAT compliance · 6 gateways IPN parity

The 5.6 line laid the legal and structural groundwork for global launch: EU VAT reverse-charge, locale-aware number formatting, complete IPN webhook coverage for all six payment gateways, and ten new languages.

v5.6.23
🆕 Feature 🔐 Reliability 🔴 Critical

PayPal IPN webhook · OnePay IPN restored — full 6-gateway parity

Closed the two remaining IPN gaps. PayPal: new handle_webhook() processing PAYMENT.CAPTURE.COMPLETED with pull-verify fallback if no Webhook ID is configured (fetches the capture directly from PayPal API to confirm COMPLETED) — secure without extra setup, upgradeable to cryptographic verify-webhook-signature when the admin provides a Webhook ID. OnePay: vpc_CallbackURL restored with a path-based URL (/payments/onepay/ipn/{booking_id}) so the booking ID lives in the URL path, not as a ?booking_id= query string that would collide with OnePay’s HMAC separator parsing.

GatewayPrimary pathIPN / Webhook backup
StripeJS payment intent✅ HMAC-verified webhook
PayPalJS capture✅ Webhook (pull-verify or HMAC-verify) ← NEW
SquareJS capture✅ HMAC-verified webhook
RazorpayJS HMAC verify✅ HMAC-verified webhook
OnePayBrowser redirect returnvpc_CallbackURL IPN ← RESTORED
Bank TransferCustomer self-declareManual (by design)
v5.6.22
🔐 Security 🌐 i18n 🔴 Critical

Global commercialisation hardening — 7 audit issues · 10 new languages

Closed every blocking and medium-severity finding from the v5.6.21 pre-release security audit. Highlights: a transient-based rate limiter on the public /discounts/validate endpoint (default 30 attempts per IP per 10 minutes, configurable in Settings → Booking) to prevent brute-force enumeration; an extra token on bank_transfer_confirm to block sequential booking_id enumeration; ten new languages bringing the supported total to 13.

v5.6.21
🔐 Audit

Pre-release security audit baseline

Baseline snapshot for the v5.6.22 commercialisation hardening pass.

v5.6.20
🛠 Patch

Polish & patch release

Incremental fixes en route to the v5.6.22 commercialisation pass.

v5.6.19 — 5.6.16
🛠 Patch

Iterative patch releases

A burst of incremental hotfixes between v5.6.15 and v5.6.20, each individually scoped: small UI tweaks, cron-schedule corrections, fence-post checks on date ranges, and admin-form field defaults.

v5.6.14
🛠 Bugfix 🔐 Security

Industry-agnostic role labels · Cashier role · Days Off save · QR contrast

Five distinct bug-fixes ahead of the cross-vertical relaunch.

  • Cashier role registered: admin/views/staff.php referenced an rbp_cashier role that class-rbp-roles.php never actually registered. Assigning a user to Cashier silently no-op’d. Now properly registered.
  • Industry-agnostic labels: “Restaurant Manager” and “Restaurant Staff” renamed to neutral “Manager”, “Staff”, “Cashier”. New refresh_role_label() helper handles label migration on existing installs.
  • Remove user / Change Role UI: Per-row inline buttons with WP nonces, self-row protection, RBP-role-only allow-list. Removing strips the RBP role only — the WordPress account survives so customer-side bookings/loyalty data persists.
  • QR scanner auto-contrast: PHP computes WCAG relative luminance (0.2126R + 0.7152G + 0.0722B) of admin’s gradient stops. If the brighter stop is ≥0.55, text/surface/border tokens flip to dark equivalents — fixes invisible scanner UI on white-background themes.
  • Days Off field saves: RBP_Staff::update() had off_days missing from its $allowed whitelist, silently dropping the field. create() had the same gap. Both now persist correctly.
v5.6.13
🛠 Hotfix 🔴 Critical

OnePay hotfix — revert vpc_CallbackURL + vpc_SecureHashType

Single-file hotfix on top of v5.6.12. OnePay sandbox returned a blank page (HMAC failure) when redirecting from the booking form, despite valid credentials. Byte-level diff against the v4.4.11 production zip isolated the regression to two fields added in v5.6.11: vpc_CallbackURL contained a literal ? and & in its value, which OnePay’s hash parser treated as field separators — splitting one parameter into many and breaking the HMAC digest. Reverted to v4.4.11’s exact $params shape. IPN trade-off accepted until v5.6.23 restored it via path-based URL.

v5.6.12
🆕 Feature 🔐 Compliance 🛠 Bugfix

EU VAT compliance · locale-aware currency formatting · zero-decimal fix · 6 audit issues

The legal foundation for EU launch — plus six confirmed issues from the global-commerce readiness audit.

EU VAT compliance helpers (4 components)

Compliance
  • Q7a — EU country / VAT rates table: New RBP_EU_VAT class. Pure-data + pure-function. EU-27 + EEA extras (IS / LI / NO), official standard VAT rates per member state, is_eu(), is_eea(), standard_rate(), normalize_country(). Standard rates are auto-suggest defaults only, never a silent fallback for charging customers.
  • Q7b — Booking-level B2B fields: Nullable customer_vat_id (32), customer_company (200), is_b2b (tinyint) columns on rbp_bookings. Attached to the booking (not the customer) because the same individual can place B2C one day and B2B the next.
  • Q7d — EU reverse-charge logic: When a verified B2B EU buyer books from a different EU member state, tax is zeroed and the invoice notes “Reverse charge — VAT to be accounted for by the recipient”. Service charge unchanged.

ISSUE #9 — hardcoded FX fallback silently mis-charged customers

Bugfix Critical

When a site’s base currency wasn’t directly supported by a gateway (VND on Stripe / PayPal / Square, or anything-but-INR on Razorpay), conversion fell back to a hardcoded constant — 1.0/24500.0 for USD/VND or 83.0 for INR/USD. Every percent of FX drift = every customer mis-charged by that percent. Silent — no admin warning, no log entry.

Fix: All four gateways now return WP_Error('no_fx_rate', …) with an admin-targeted message when the relevant rate isn’t configured. The REST layer surfaces a 422 to the client, so the customer sees “rate not configured” instead of being silently mis-charged. Pattern matches the v5.6.11 OnePay precedent.

ISSUE #10 — locale-aware decimal & thousand separators

i18n

EUR shown to a German reader rendered as €1,234.56 instead of the locally-correct 1.234,56 €. New helper RBP_Helpers::locale_format_separators() keys separators on the reader’s language (matching EU custom):

  • Anglo / East Asian / VI: . decimal, , thousands — en, ja, ko, zh, zh_TW, vi
  • Continental EU dot-thousand: , decimal, . thousands — de, it, es, pt, pt_BR, id, nl
  • French space-thousand: , decimal, NBSP thousands — fr (per French typography)

ISSUE #13 — zero-decimal currency tax / SC rounded to integer

Bugfix

Server-side round( $amount * $rate / 100 ) with default precision 0 collapsed cents on every USD/EUR/GBP tax & service-charge calculation. $25.00 × 10% = $2.50 → round(2.50) = $3.00: customer over-charged by 50¢. Two new helpers form the single source of truth: currency_decimals() (returns 0 or 2 based on full ISO 4217 zero-decimal list) and currency_round() (wraps round() with correct precision). VND-base sites were unaffected because VND is genuinely zero-decimal.

v5.6.11
🆕 Feature ✨ Major

Multi-currency convention overhaul · ICS calendar attachment · invoice line items

Three structural changes in one release. The currency rate convention was unified to “1 base = N foreign” across PHP and JS, with a one-shot idempotent migration (migrate_currency_rates_inversion_v5611()) for existing installs. Confirmation emails now carry an RFC 5545 ICS attachment so guests can add the booking to Apple Calendar, Google Calendar or Outlook with one tap. Invoices switched from a single line to per-group line items (Adults / Children / Infants) with proper ratio handling.

v5.6.2
🔐 Security 🛠 Bugfix

License system hardening — three independent fixes

Closed three license-system holes. (1) Expired or banned licences could previously re-activate by falling through to local HMAC validation; now any success:false from the server hard-blocks, with stored-status check on offline fallback. (2) Activation/deactivation are now broadcast to all three sync servers (fire-and-forget, non-blocking), so HTTP 404s from sync gaps no longer masquerade as revoked. (3) License status card finally renders the customer’s Name and Website alongside Email and Key.

v5.6.1
🛠 Bugfix ✨ Email templates

Guest Groups save isolation · email templates VI/FR/DE upgraded to EN-style

Saving the Guest Groups tab no longer reset Deposit Amount to its default (200,000 ₫). Saving the Booking tab no longer flipped Guest Group 2 to OFF. Both fixed by read-merge-write pattern + clean tab_bool separation. Every Vietnamese, French and German email template (confirmation, reminders, cancellation, payment_confirmation) was upgraded to the icon-hero card layout used by English. Seven new French and German templates added (waitlist, abandon, win-back, birthday, loyalty upgrade, admin notification, full slot). Auto-migration via migrate_email_templates_v5512() runs once per install.

Earlier releases

v5.5 · v5.4 · v4.x · v3.x archive

A condensed view of the older release history — the engineering and product decisions that built the foundation everything since has been built on.

v5.5.7
🛠 Bugfix

Prepaid status · service-price total · payment-method label

Prepaid bookings stopped showing “Partially Paid” after full payment (compared against pay_amount rather than pre-discount total_amount). Service price now respects the service_price_in_total setting server-side. Emails & admin panels gained {{payment_method}} and {{payment_status}} variables, plus a get_payment_method_label() helper that returns combined labels like “Prepaid (Full) – PayPal”.

v5.4.27 → v5.4.33
🛠 Bugfix ✨ Design

Service cards · Globe Trigger Design · Luxury skin polish

Service card price + Select row layout fixed for 3-4 column grids on desktop and mobile. “Starting from” badge implemented for the from price-type. Globe Trigger Design settings (bg/color/check) finally apply on the Luxury skin via CSS variable scoping. Three globe trigger styles shipped: pill, elegant, compact. Square / Razorpay added to the payment-recovery widget.

v4.3.30 → v4.3.40
🆕 Feature 🛠 Bugfix

Luxury Heritage skin launch · multi-currency · gateway audit

The Luxury Heritage skin (rbp-lux) shipped in v4.3.36 as a second visual template option — parchment + burgundy + bronze with Playfair Display, Cormorant Garamond and Jost. All existing booking logic, payment gateways, status transitions and email flows were preserved unchanged — only the CSS shell swapped. v4.3.35 closed the same-page double-form fatal (_rbp_gg_resolve() wrapped in if (! function_exists())), made Stripe-only sites finally show their Deposit and Prepaid cards, released the missing MySQL advisory lock on the waitlist path, and corrected Stripe’s 100× over-charge for zero-decimal currencies. v4.3.30 introduced the multi-currency system and Settings → Diagnostics tab.

v4.1.0 → v4.1.12
✨ Milestone

All-Seasons Flow · final v4.1 release

The 4.1 line consolidated the booking flow into a single “all-seasons” engine handling lunch/dinner, à la carte / buffet, walk-in vs reservation, and deposit / prepaid / pay-at-venue from one form. The handover release before the 5.x architectural rewrite began.

v3.16.0 → v3.17.12
🆕 Feature 🛠 Bugfix

v3 mature line — twelve incremental releases

Twelve iterative releases that refined the floor plan editor, multi-room support, loyalty point redemption rules, and the customer self-cancellation flow. Each release is individually documented in the in-plugin CHANGELOG.md.

v3.7.0 → v3.15.5
🆕 Foundation

v3 foundation — CRM, waitlist, loyalty

The CRM, waitlist promotion engine and loyalty points module all entered the codebase between v3.7 and v3.15. The booking-form template gained shortcode-level customisation, WooCommerce currency bridge (WOOCS) integration, and the first iteration of QR check-in.

v3.6.0
✨ Baseline

Baseline release — start of recorded changelog

First entry in the public changelog. Earlier internal builds existed but are not documented here.

About this changelog

Versioning policy

JavisTab Booking Pro follows Semantic Versioning 2.0 (MAJOR.MINOR.PATCH). Patch releases (e.g. v5.9.14 → v5.9.15) carry only backward-compatible fixes and small features. Minor releases (v5.8 → v5.9) add backward-compatible features. Major releases (v4 → v5) may include schema migrations and deprecate public APIs, with a one-version warning window for add-on authors.

Update path

Updates are delivered through the standard WordPress → Plugins → Updates screen once your licence is active. Database migrations are idempotent and gated by rbp_db_version so they only run once per install. ALTER TABLE statements are wrapped in hide_errors() to prevent fatal aborts on user-modified columns.

Support & reporting bugs

Email with: (1) the plugin version from Settings → Diagnostics, (2) reproduction steps, (3) a screenshot, and (4) the relevant lines from wp-content/uploads/rbp-logs/error.log. Confirmed bugs are typically fixed within 24–72 hours and shipped on the next patch release.

Always-on. Always shipping.

Every release in this changelog is shipped to live restaurants, spas, gyms and salons across 13 languages and 6 payment gateways. Start your free 15-day trial — no credit card, full feature access, all data carries over when you upgrade.