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.
Release categories
- 🆕 Feature
- 🛠 Bugfix
- ✨ Improvement
- 🔐 Security
- ⚡ Performance
- 🌐 Internationalisation
- 🔴 Critical
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.
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 CriticalSymptom: 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 type | Before v5.9.15 | After v5.9.15 |
|---|---|---|
at_restaurant | 1 email | 1 email ✓ |
deposit_required (online) | 1 email | 1 email ✓ |
prepaid (online full) | 1 email | 1 email ✓ |
| Bank transfer (manual) | 2 emails | 1 email ✓ |
Voucher email toggle description updated
ImprovementThe 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).
v5.9 series
Voucher workflow · email reliability · global i18n parityThe 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.
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.
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.
| Language | Keys before | Missing vs en |
|---|---|---|
zh — Simplified Chinese | 94 | 76 |
ko — Korean | 94 | 76 |
zh_TW — Traditional Chinese | 117 | 55 |
ja — Japanese | 130 | 43 |
fr — French | 157 | 12 |
de — German | 168 | 4 |
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 CriticalOn 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.
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.
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 inRBP_I18n::languages(). New utilityRBP_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 sargableBETWEENover the stats date range. Confirmed all dashboard, list, count and stats queries use bounded ranges + composite indexes — no unbounded scans remain.
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.
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.
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.
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).
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.
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.
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.
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.
v5.8 series
Elegant payment cards · price summary modes · check-in mobileThe 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.
“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.
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.
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.
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.
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.
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.
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 editorThe 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.
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).
🌐 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.
| Preset | Typical countries | tax_inclusive | sc_cascade_tax | disc_pre_tax |
|---|---|---|---|---|
| 🇺🇸 US / Standard | US, Canada, AU B2B | OFF | OFF | ON |
| 🇪🇺 EU / UK Inclusive | EU-27, UK, AU B2C, NZ | ON | OFF | ON |
| 🇹🇭 SE Asia “++” | Thailand, Malaysia, Indonesia | OFF | ON | ON |
| 🇻🇳 Vietnam | Vietnam domestic | OFF | OFF | ON |
| 🇯🇵 Japan | Japan (消費税 10%) | ON | OFF | ON |
| ⚙️ Custom | Advanced users | Manual control | ||
tax_region=custom,
preserving their manual configuration.
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 ✅.
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).
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.
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| Operation | v5.7.1 | v5.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 condition | atomic via UNIQUE |
RBP_Availability::get_available_tables (40 tables) | 80 queries | 3 queries |
Database — 14 indexes, 1 UNIQUE, 3 denormalised columns, 1 new table
Schema- 14 indexes across
rbp_bookings,rbp_customers,rbp_payments,rbp_activity_logandrbp_email_log. - UNIQUE on
rbp_customers(restaurant_id, identity)for atomicget_or_create(). - 3 denormalised columns on
rbp_customers:bookings_count,no_show_count,last_booking_at— backfilled once via 3-step UPDATE..JOIN, refreshed viaRBP_Customer::refresh_cache(). - New
wp_rbp_jobstable 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| Segment | Threshold | Use case |
|---|---|---|
| 💰 High Spenders | total_spent ≥ N | Premium event invites, top-tier perks |
| 🔁 Frequent Visitors | visit_count ≥ N | Loyalty rewards, frequent-diner programs |
| ⚠️ At Risk | last_booking_at < N days AND visit_count ≥ M | Win-back campaigns before losing high-value customers |
| ✨ New Customers | created_at within N days | Welcome series, intro offers |
| 👨👩👧👦 Big Group Bookers | At least one booking with guests ≥ N | Private 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.
Maintenance release
Internal hardening; no public API changes from v5.7.0.
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 parityThe 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.
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.
| Gateway | Primary path | IPN / Webhook backup |
|---|---|---|
| Stripe | JS payment intent | ✅ HMAC-verified webhook |
| PayPal | JS capture | ✅ Webhook (pull-verify or HMAC-verify) ← NEW |
| Square | JS capture | ✅ HMAC-verified webhook |
| Razorpay | JS HMAC verify | ✅ HMAC-verified webhook |
| OnePay | Browser redirect return | ✅ vpc_CallbackURL IPN ← RESTORED |
| Bank Transfer | Customer self-declare | Manual (by design) |
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.
Pre-release security audit baseline
Baseline snapshot for the v5.6.22 commercialisation hardening pass.
Polish & patch release
Incremental fixes en route to the v5.6.22 commercialisation pass.
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.
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.phpreferenced anrbp_cashierrole thatclass-rbp-roles.phpnever 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()hadoff_daysmissing from its$allowedwhitelist, silently dropping the field.create()had the same gap. Both now persist correctly.
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.
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_VATclass. 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 onrbp_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.
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.
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.
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 archiveA condensed view of the older release history — the engineering and product decisions that built the foundation everything since has been built on.
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”.
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.
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.
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 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 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.
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 JavisTab support 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.
Related documentation
Get started
- 5-minute install guide
- System requirements (WP 5.8+, PHP 7.4+)
- License & 15-day free trial
- Try the live demo
Configure
- All 7 shortcodes
- Payments (VietQR, Stripe, PayPal, Square, Razorpay, OnePay)
- Email templates & SMS
- Reminders & ICS calendar attachments
- QR check-in station
- CRM & loyalty points
- 13 supported languages
Developers
- REST API reference —
/wp-json/rbp/v1/ - Troubleshooting
- Help Center · FAQ · Blog
Plans & pricing
- All plans & pricing — starting at $19.90 / 30 days
- Start free 15-day trial — no credit card
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.