**Version 2.4.4** | Documentation (2026 Edition)

Welcome to **Modern Hotel Booking**, the complete reservation system for WordPress. This guide covers everything from initial setup to advanced Pro features including payment processing, AI assistance, and VAT/TAX management.

---

## Table of Contents

### Part 1: Getting Started

1.1 [Installation & Activation](#11-installation--activation)  
1.2 [Quick Start Guide](#12-quick-start-guide)  
1.3 [System Requirements](#13-system-requirements)

### Part 2: Free Version User Guide

2.1 [Setting Up Your Hotel](#21-setting-up-your-hotel)  
2.2 [Configuration](#22-configuration)  
2.3 [Displaying the Booking System](#23-displaying-the-booking-system)  
2.4 [Managing Bookings](#24-managing-bookings)  
2.5 [Business Information & Communication](#25-business-information--communication)  
2.6 [Granular Permissions (Abilities API)](#26-granular-permissions)

### Part 3: Pro Version User Guide

3.1 [Activating Your License](#31-activating-your-license)  
3.2 [Payment Gateways](#32-payment-gateways)  
3.3 [VAT/TAX System](#33-vattax-system)  
3.4 [Advanced Pricing Rules](#34-advanced-pricing-rules)

- [Global Stay Limits](#343-global-stay-limits)
- [Custom Hotel Timezone](#344-custom-hotel-timezone)
- [Financial Precision (BCMath)](#345-financial-precision-bcmath)  
  3.5 [Booking Extras & Add-ons](#35-booking-extras--add-ons)  
  3.6 [iCal Sync Manager](#36-ical-sync-manager)

- [Open a Room's iCal Page](#361-step-1--open-a-rooms-ical-page)
- [Export: Share Availability With OTAs](#362-step-2--export-share-your-availability-with-otas)
- [Import: Pull OTA Bookings into MHBO](#363-step-3--import-pull-ota-bookings-into-mhbo)
- [Reading the Connections Table](#364-reading-the-connections-table)
- [Syncing Manually](#365-syncing-manually)
- [Automatic Background Sync](#366-automatic-background-sync)
- [Choosing the Right Sync Interval](#choosing-the-right-sync-interval)
- [Security Architecture](#367-security-architecture)
- [Troubleshooting iCal Sync](#368-troubleshooting-ical-sync)
- [Booking.com Limitation & Workarounds](#369-bookingcom-limitation--workarounds)  
  3.7 [Theme Customizer](#37-theme-customizer)  
  3.8 [GDPR Compliance Suite](#38-gdpr-compliance-suite)  
  3.9 [Analytics Dashboard](#39-analytics-dashboard)  
  3.10 [Deposit & Partial Payments](#310-deposit--partial-payments)  
  3.11 [Admin Calendar Pricing & Availability Editor](#311-admin-calendar-pricing--availability-editor)  
  3.12 [Professional Invoicing System](#312-professional-invoicing-system)  
  3.13 [Performance & Cache Management](#313-performance--cache-management)
- [Advanced Availability Resilience](#3134-advanced-availability-resilience-race-protection)
- [High-Performance AJAX Flow](#3135-high-performance-ajax-flow)  
  3.14 [AI Concierge System](#314-ai-concierge-system)
- [Tiered Reliability & WP 7.0 Native AI Cascade](#3143-tiered-reliability--wp-70-native-ai-cascade)
- [Zero-Trust Identity Handshake (OTP)](#3146-zero-trust-identity-handshake-otp)
- [WP 7.0 Native Abilities API Integration](#31412-wp-70-native-abilities-api-integration)
- [AI Concierge Analytics](#31410-ai-concierge-analytics)  
  3.15 [Coupon System](#315-coupon-system)
- [Creating & Managing Coupons](#3152-creating--managing-coupons)
- [Coupon Fields Reference](#3153-coupon-fields-reference)
- [Guest Checkout Experience](#3154-guest-checkout-experience)
- [AI Concierge & Coupons](#3155-ai-concierge--coupons)
- [Taxes, Deposits & Payment Gateways](#3156-taxes-deposits--payment-gateways)
- [Troubleshooting Coupons](#3157-troubleshooting-coupons)

### Part 4: Multilingual Support

4.1 [Supported Translation Plugins](#41-supported-translation-plugins)  
4.2 [Setting Up Multilingual Content](#42-setting-up-multilingual-content)  
4.3 [Multilingual Email Templates](#43-multilingual-email-templates)

### Part 5: Developer Reference

5.1 [REST API](#51-rest-api)  
5.2 [Webhooks & HMAC Security](#52-webhooks--hmac-security)  
5.3 [Action Hooks](#53-action-hooks)  
5.4 [Filter Hooks](#54-filter-hooks)

### Part 6: Troubleshooting

6.1 [Common Issues](#61-common-issues)  
6.2 [Payment Problems](#62-payment-problems)  
6.3 [Tax Calculation Issues](#63-tax-calculation-issues)  
6.4 [Email Delivery Problems](#64-email-delivery-problems)  
6.5 [Performance Optimization](#65-performance-optimization)  
6.6 [iCal Sync Issues](#66-ical-sync-issues)  
6.7 [Getting Support](#67-getting-support)

---

# Part 1: Getting Started

## 1.1 Installation & Activation

### Method 1: WordPress Admin (Recommended)

1. Go to **Plugins > Add New** in your WordPress dashboard
2. Search for "Modern Hotel Booking"
3. Click **Install Now** and then **Activate**

### Method 2: Manual Upload

1. Download the plugin zip file
2. Go to **Plugins > Add New > Upload Plugin**
3. Select the zip file and click **Install Now**
4. Click **Activate Plugin**

### Method 3: FTP Upload

1. Extract the `modern-hotel-booking.zip` file on your computer
2. Upload the extracted folder to `/wp-content/plugins/` via FTP
3. Go to **Plugins** in your WordPress dashboard
4. Find "Modern Hotel Booking" and click **Activate**

### Post-Activation

After activation, a new menu item **Hotel Booking** will appear in your WordPress admin sidebar. The plugin automatically creates the necessary database tables.

## 1.2 Quick Start Guide

Get your hotel booking system running in 5 steps:

| Step | Action               | Location                         |
| ---- | -------------------- | -------------------------------- |
| 1    | Create Room Types    | Hotel Booking > Room Types       |
| 2    | Add Individual Rooms | Hotel Booking > Rooms            |
| 3    | Configure Settings   | Hotel Booking > Settings         |
| 4    | Display the Form     | Add block or shortcode to a page |
| 5    | Test a Booking       | Make a test reservation          |

## 1.3 System Requirements

| Requirement  | Minimum | Recommended |
| ------------ | ------- | ----------- |
| WordPress    | 6.6+    | 7.0+        |
| PHP          | 8.0     | 8.2+        |
| MySQL        | 5.7     | 8.0+        |
| Memory Limit | 128MB   | 256MB       |

**Required PHP Extensions:**

- OpenSSL (for payment encryption)
- JSON
- cURL (for payment APIs and iCal sync)

**Recommended Extensions:**

- BCMath (for higher financial precision in invoicing)

---

# Part 2: Free Version User Guide

The free version of Modern Hotel Booking provides everything you need to take reservations, manage rooms, and send email notifications.

## 2.1 Setting Up Your Hotel

Before accepting bookings, you must define your room inventory.

### 2.1.1 Room Types

Room Types are categories (e.g., "Standard Double", "Deluxe Suite", "Family Room") that define base pricing and capacity.

**To create a Room Type:**

1. Go to **Hotel Booking > Room Types**
2. Click **Add New**
3. Configure the following fields:

| Field          | Description             | Example                             |
| -------------- | ----------------------- | ----------------------------------- |
| Name           | Display name            | "Deluxe Suite"                      |
| Description    | Details shown to guests | "Spacious suite with ocean view..." |
| Base Price     | Per-night rate          | 150.00                              |
| Max Adults     | Maximum adult occupancy | 2                                   |
| Max Children   | Maximum child occupancy | 2                                   |
| Amenities      | Feature checkboxes      | Wi-Fi, AC, TV, Minibar              |
| Featured Image | Representative photo    | Upload image                        |

4. Click **Publish** to save

### 2.1.2 Individual Rooms

Individual Rooms are the actual physical rooms guests will stay in.

**To create an Individual Room:**

1. Go to **Hotel Booking > Rooms**
2. Click **Add New**
3. Configure the following:

| Field            | Description         | Example                          |
| ---------------- | ------------------- | -------------------------------- |
| Room Number/Name | Unique identifier   | "Room 101" or "The Blue Cottage" |
| Assign Type      | Select Room Type    | Deluxe Suite                     |
| Status           | Availability        | Available                        |
| Custom Price     | Override base price | (optional)                       |

4. Click **Publish** to save

## 2.2 Configuration

Go to **Hotel Booking > Settings** to configure global options.

### 2.2.1 General Settings

| Setting                     | Description                                                                                                                                                                                                                                                                                                                                                                         | Default            |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| Check-in Time               | Standard arrival time                                                                                                                                                                                                                                                                                                                                                               | 14:00              |
| Check-out Time              | Standard departure time                                                                                                                                                                                                                                                                                                                                                             | 11:00              |
| Hotel Timezone              | **(Pro Only)** The local timezone of your property. This is critical for properties in regions where the server clock differs from local time. It ensures that "Same-day Turnover" and availability windows are calculated against your local clock. **Version 2.4.4+** uses high-precision parsing to ensure iCal dates never shift, even in positive UTC offsets (e.g., UTC+2/3). | WordPress Default  |
| Notification Email          | Admin email for alerts                                                                                                                                                                                                                                                                                                                                                              | admin@yoursite.com |
| Same-day Turnover           | Prevent same-day check-in/checkout                                                                                                                                                                                                                                                                                                                                                  | Enabled            |
| Booking Page                | The page where the booking form block/shortcode is placed                                                                                                                                                                                                                                                                                                                           | -                  |
| Booking Page URL (Override) | Custom URL to bypass the selected Booking Page                                                                                                                                                                                                                                                                                                                                      | -                  |
| Enable Children Management  | Allow selecting children on the booking form                                                                                                                                                                                                                                                                                                                                        | Disabled           |
| Custom Guest Fields         | Define custom fields to collect additional guest information                                                                                                                                                                                                                                                                                                                        | -                  |
| Save Data on Uninstall      | Persist booking data and settings upon plugin deletion (useful during upgrades)                                                                                                                                                                                                                                                                                                     | Enabled            |
| Show Powered By Link        | Display a discretly branded link at the bottom of the booking form                                                                                                                                                                                                                                                                                                                  | Enabled            |

### 2.2.2 Currency Settings

| Setting         | Description     | Options                  |
| --------------- | --------------- | ------------------------ |
| Currency Code   | ISO 4217 code   | USD, EUR, GBP, RON, etc. |
| Currency Symbol | Display symbol  | $, €, £, lei             |
| Symbol Position | Before or after | Before / After           |

### 2.2.3 Email Templates

Customize emails sent to guests for each booking status.

**Available Statuses:**

- **Pending** - Booking received, awaiting confirmation
- **Confirmed** - Booking confirmed
- **Cancelled** - Booking cancelled

**Available Placeholders:**

| Placeholder          | Replaced With                |
| -------------------- | ---------------------------- |
| `{customer_name}`    | Guest's full name            |
| `{booking_id}`       | Unique booking ID            |
| `{status}`           | Current booking status       |
| `{check_in}`         | Check-in date                |
| `{check_out}`        | Check-out date               |
| `{total_price}`      | Total booking amount         |
| `{room_name}`        | Name of the booked room      |
| `{guests}`           | Number of adult guests       |
| `{children}`         | Number of child guests       |
| `{children_ages}`    | Ages of all child guests     |
| `{nights}`           | Number of nights             |
| `{phone}`            | Guest phone number           |
| `{email}`            | Guest email address          |
| `{special_requests}` | Guest special requests       |
| `{custom_fields}`    | Custom field values          |
| `{payment_details}`  | Payment confirmation section |
| `{tax_breakdown}`    | Tax breakdown section        |

#### 2.2.3.1 Premium HTML Template Example (Pro Tip)

For a professional, modern look that matches your property's branding, you can use the following HTML template. This is particularly useful for the **Pending Booking** status at **Hotel Booking > Settings > Emails**.

**Recommended Subject:**
`Your booking at Beach House - {check_in} - {check_out}`

**Template HTML:**

````html
<div
  style="background-color: #f4f7f9;padding: 20px 10px;font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif"
>
  <div
    style="max-width: 650px;margin: 0 auto;background-color: #ffffff;border: 1px solid #e2e8f0;border-radius: 16px;overflow: visible"
  >
    <div style="padding: 20px;border-bottom: 1px solid #f1f5f9">
      <table width="100%" border="0" cellspacing="0" cellpadding="0">
        <tr>
          <td style="padding-bottom: 10px">
            <img
              src="https://beachhouse.ro/wp-content/uploads/cropped-beach-house-guest-house-1.jpg"
              alt="Beach House Logo"
              style="width: 100%;max-width: 160px;height: auto"
            />
          </td>
          <td
            align="right"
            style="font-size: 12px;color: #64748b;line-height: 1.4"
          >
            <strong style="color: #0f172a;font-size: 14px">Beach House</strong
            ><br />
            Strada D4, Nr. 57, Năvodari<br />
            CT 905701, România<br />
            +40737501765<br />
            <a
              href="https://beachhouse.ro/en/"
              style="color: #0077b6;text-decoration: none"
              >https://beachhouse.ro/</a
            ><br />
          </td>
        </tr>
      </table>
    </div>

    <div style="padding: 30px">
      <p style="font-size: 16px;margin-top: 0">
        Hi <strong>{customer_name}</strong>,
      </p>
      <p>Thank you for booking at <strong>Beach House</strong>!</p>

      <div
        style="background-color: #fff9db;border-left: 4px solid #fcc419;padding: 15px;margin: 25px 0;border-radius: 4px"
      >
        <strong style="color: #856404"
          >Your booking has not yet been confirmed.</strong
        ><br />
        <span style="font-size: 14px;color: #665c33">
          Please wait for an additional confirmation email or phone call from
          us.</span
        >
      </div>

      <h3
        style="color: #0077b6;border-bottom: 2px solid #f0f4f8;padding-bottom: 10px;margin-bottom: 20px;font-size: 18px"
      >
        Your details:
      </h3>

      <table
        width="100%"
        border="0"
        cellspacing="0"
        cellpadding="0"
        style="font-size: 14px;line-height: 1.8;color: #333"
      >
        <tr>
          <td style="color: #64748b"><strong>Booking ID:</strong></td>
          <td style="text-align: right">
            #{booking_id} <small style="color: #94a3b8">({status})</small>
          </td>
        </tr>
        <tr>
          <td style="color: #64748b">Name:</td>
          <td style="text-align: right">{customer_name}</td>
        </tr>
        <tr>
          <td style="color: #64748b">Room:</td>
          <td style="text-align: right">{room_name}</td>
        </tr>
        <tr>
          <td style="color: #64748b">Check-in:</td>
          <td style="text-align: right">{check_in} ({check_in_time})</td>
        </tr>
        <tr>
          <td style="color: #64748b">Check-out:</td>
          <td style="text-align: right">{check_out} ({check_out_time})</td>
        </tr>
        <tr>
          <td style="color: #64748b">Nights:</td>
          <td style="text-align: right">{nights}</td>
        </tr>
        <tr>
          <td style="color: #64748b">Guests:</td>
          <td style="text-align: right">
            {guests} Adults, {children} Children
          </td>
        </tr>
        <tr>
          <td style="color: #64748b">Age of kids:</td>
          <td style="text-align: right">{children_ages}</td>
        </tr>
        <tr>
          <td style="color: #64748b">E-mail:</td>
          <td style="text-align: right">{customer_email}</td>
        </tr>
        <tr>
          <td style="color: #64748b">Tel:</td>
          <td style="text-align: right">{customer_phone}</td>
        </tr>
        <tr>
          <td style="color: #64748b"></td>
          <td style="text-align: right">{custom_fields}</td>
        </tr>
      </table>

      <table
        width="100%"
        border="0"
        cellspacing="0"
        cellpadding="0"
        style="margin-top: 25px"
      >
        <tr>
          <td style="font-size: 13px">
            <p style="margin: 0;color: #475569">
              Looking forward to your arrival!
            </p>
            <p style="margin: 3px 0 0 0;font-weight: bold">Beach House</p>
          </td>
          <td align="right" style="vertical-align: bottom">
            <a
              href="https://wa.me/40737501765"
              style="background-color: #25D366;color: #ffffff;padding: 8px 14px;text-decoration: none;border-radius: 6px;font-weight: bold;font-size: 12px"
              >WhatsApp</a
            >
          </td>
        </tr>
      </table>

      #### 2.2.3.1 Premium HTML Email Templates (Pro Examples) For a
      professional, modern look that matches your property's branding, you can
      use these HTML templates at **Hotel Booking > Settings > Emails**. #####
      A. Pending Booking **Recommended Subject:** `Your booking at Beach House -
      {check_in} - {check_out}` **Message Body:** ```html
      <div
        style="background-color: #f4f7f9;padding: 20px 10px;font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif"
      >
        <div
          style="max-width: 650px;margin: 0 auto;background-color: #ffffff;border: 1px solid #e2e8f0;border-radius: 16px;overflow: visible"
        >
          <div style="padding: 20px;border-bottom: 1px solid #f1f5f9">
            <table width="100%" border="0" cellspacing="0" cellpadding="0">
              <tr>
                <td style="padding-bottom: 10px">
                  <img
                    src="https://beachhouse.ro/wp-content/uploads/cropped-beach-house-guest-house-1.jpg"
                    alt="Beach House Logo"
                    style="width: 100%;max-width: 160px;height: auto"
                  />
                </td>
                <td
                  align="right"
                  style="font-size: 12px;color: #64748b;line-height: 1.4"
                >
                  <strong style="color: #0f172a;font-size: 14px"
                    >Beach House</strong
                  ><br />
                  Strada D4, Nr. 57, Năvodari<br />
                  CT 905701, România<br />
                  +40737501765<br />
                  <a
                    href="mailto:booking@beachhouse.ro"
                    style="color: #0077b6;text-decoration: none"
                    >booking@beachhouse.ro</a
                  ><br />
                  <a
                    href="https://beachhouse.ro/en/"
                    style="color: #0077b6;text-decoration: none"
                    >https://beachhouse.ro/</a
                  ><br />
                </td>
              </tr>
            </table>
          </div>

          <div style="padding: 30px">
            <p style="font-size: 16px;margin-top: 0">
              Hi <strong>{customer_name}</strong>,
            </p>
            <p>Thank you for booking at <strong>Beach House</strong>!</p>

            <div
              style="background-color: #fff9db;border-left: 4px solid #fcc419;padding: 15px;margin: 25px 0;border-radius: 4px"
            >
              <strong style="color: #856404"
                >Rezervarea nu este confirmată automat.</strong
              ><br />
              <span style="font-size: 14px;color: #665c33"
                >Your booking has not yet been confirmed. Please wait for an
                additional confirmation email or phone call from us.</span
              >
            </div>

            <h3
              style="color: #0077b6;border-bottom: 2px solid #f0f4f8;padding-bottom: 10px;margin-bottom: 20px;font-size: 18px"
            >
              Your details:
            </h3>

            <table
              width="100%"
              border="0"
              cellspacing="0"
              cellpadding="0"
              style="font-size: 14px;line-height: 1.8;color: #333"
            >
              <tr>
                <td style="color: #64748b"><strong>Booking ID:</strong></td>
                <td style="text-align: right">
                  #{booking_id} <small style="color: #94a3b8">({status})</small>
                </td>
              </tr>
              <tr>
                <td style="color: #64748b">Name:</td>
                <td style="text-align: right">{customer_name}</td>
              </tr>
              <tr>
                <td style="color: #64748b">Room:</td>
                <td style="text-align: right">{room_name}</td>
              </tr>
              <tr>
                <td style="color: #64748b">Check-in:</td>
                <td style="text-align: right">{check_in} ({check_in_time})</td>
              </tr>
              <tr>
                <td style="color: #64748b">Check-out:</td>
                <td style="text-align: right">
                  {check_out} ({check_out_time})
                </td>
              </tr>
              <tr>
                <td style="color: #64748b">Nights:</td>
                <td style="text-align: right">{nights}</td>
              </tr>
              <tr>
                <td style="color: #64748b">Guests:</td>
                <td style="text-align: right">
                  {guests} Adults, {children} Children
                </td>
              </tr>
              <tr>
                <td style="color: #64748b">Age of kids:</td>
                <td style="text-align: right">{children_ages}</td>
              </tr>
              <tr>
                <td style="color: #64748b">E-mail:</td>
                <td style="text-align: right">{customer_email}</td>
              </tr>
              <tr>
                <td style="color: #64748b">Tel:</td>
                <td style="text-align: right">{customer_phone}</td>
              </tr>
              <tr>
                <td style="color: #64748b"></td>
                <td style="text-align: right">{custom_fields}</td>
              </tr>
            </table>

            <table
              width="100%"
              border="0"
              cellspacing="0"
              cellpadding="0"
              style="margin-top: 25px"
            >
              <tr>
                <td style="font-size: 13px">
                  <p style="margin: 0;color: #475569">
                    Looking forward to your arrival!
                  </p>
                  <p style="margin: 3px 0 0 0;font-weight: bold">Beach House</p>
                </td>
                <td align="right" style="vertical-align: bottom">
                  <a
                    href="https://wa.me/40737501765"
                    style="background-color: #25D366;color: #ffffff;padding: 8px 14px;text-decoration: none;border-radius: 6px;font-weight: bold;font-size: 12px"
                    >WhatsApp</a
                  >
                </td>
              </tr>
            </table>

            <div
              style="margin-top: 30px;border-top: 1px solid #f1f5f9;padding-top: 20px"
            ></div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
````

##### B. Confirmed Booking

**Recommended Subject:** `Your booking at Beach House is CONFIRMED - {check_in} - {check_out}`
**Message Body:**

```html
<div
  style="background-color: #f4f7f9;padding: 20px 10px;font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif"
>
  <div
    style="max-width: 650px;margin: 0 auto;background-color: #ffffff;border: 1px solid #e2e8f0;border-radius: 16px;overflow: visible"
  >
    <div style="padding: 20px;border-bottom: 1px solid #f1f5f9">
      <table width="100%" border="0" cellspacing="0" cellpadding="0">
        <tr>
          <td style="padding-bottom: 10px">
            <img
              src="https://beachhouse.ro/wp-content/uploads/cropped-beach-house-guest-house-1.jpg"
              alt="Beach House Logo"
              style="width: 100%;max-width: 160px;height: auto"
            />
          </td>
          <td
            align="right"
            style="font-size: 12px;color: #64748b;line-height: 1.4"
          >
            <strong style="color: #0f172a;font-size: 14px">Beach House</strong
            ><br />
            Strada D4, Nr. 57, Năvodari<br />
            CT 905701, România<br />
            +40737501765<br />
            <a
              href="mailto:booking@beachhouse.ro"
              style="color: #0077b6;text-decoration: none"
              >booking@beachhouse.ro</a
            ><br />
            <a
              href="https://beachhouse.ro/en/"
              style="color: #0077b6;text-decoration: none"
              >https://beachhouse.ro/</a
            ><br />
          </td>
        </tr>
      </table>
    </div>

    <div style="padding: 30px">
      <p style="font-size: 16px;margin-top: 0">
        Hi <strong>{customer_name}</strong>,
      </p>
      <p>Thanks for making a reservation at <strong>Beach House</strong>!</p>

      <div
        style="background-color: #ccffcc;border-left: 4px solid #009900;padding: 15px;margin: 25px 0;border-radius: 4px"
      >
        <strong style="color: #00cc00">Your booking is confirmed.</strong><br />
        <span style="font-size: 14px;color: #003300"
          >We are happy to confirm your booking!</span
        >
      </div>

      <h3
        style="color: #0077b6;border-bottom: 2px solid #f0f4f8;padding-bottom: 10px;margin-bottom: 20px;font-size: 18px"
      >
        Your details:
      </h3>

      <table
        width="100%"
        border="0"
        cellspacing="0"
        cellpadding="0"
        style="font-size: 14px;line-height: 1.8;color: #333"
      >
        <tr>
          <td style="color: #64748b"><strong>Booking ID:</strong></td>
          <td style="text-align: right">
            #{booking_id} <small style="color: #94a3b8">({status})</small>
          </td>
        </tr>
        <tr>
          <td style="color: #64748b">Name:</td>
          <td style="text-align: right">{customer_name}</td>
        </tr>
        <tr>
          <td style="color: #64748b">Room:</td>
          <td style="text-align: right">{room_name}</td>
        </tr>
        <tr>
          <td style="color: #64748b">Check-in:</td>
          <td style="text-align: right">{check_in} ({check_in_time})</td>
        </tr>
        <tr>
          <td style="color: #64748b">Check-out:</td>
          <td style="text-align: right">{check_out} ({check_out_time})</td>
        </tr>
        <tr>
          <td style="color: #64748b">Nights:</td>
          <td style="text-align: right">{nights}</td>
        </tr>
        <tr>
          <td style="color: #64748b">Guests:</td>
          <td style="text-align: right">
            {guests} Adults, {children} Children
          </td>
        </tr>
        <tr>
          <td style="color: #64748b">Age of kids:</td>
          <td style="text-align: right">{children_ages}</td>
        </tr>
        <tr>
          <td style="color: #64748b">E-mail:</td>
          <td style="text-align: right">{customer_email}</td>
        </tr>
        <tr>
          <td style="color: #64748b">Tel:</td>
          <td style="text-align: right">{customer_phone}</td>
        </tr>
        <tr>
          <td style="color: #64748b"></td>
          <td style="text-align: right">{custom_fields}</td>
        </tr>
      </table>

      <table
        width="100%"
        border="0"
        cellspacing="0"
        cellpadding="0"
        style="margin-top: 30px"
      >
        <tr>
          <td>
            <p style="margin: 0;font-size: 14px;color: #475569">
              Many Thanks, see you soon!
            </p>
            <p style="margin: 5px 0 0 0;font-weight: bold">Beach House</p>
          </td>
          <td style="text-align: right;vertical-align: bottom">
            <a
              href="https://wa.me/40737501765"
              style="background-color: #25D366;color: #ffffff;padding: 8px 15px;text-decoration: none;border-radius: 6px;font-weight: bold;font-size: 13px"
              >WhatsApp</a
            >
          </td>
        </tr>
      </table>

      <div
        style="margin-top: 30px;border-top: 1px solid #f1f5f9;padding-top: 20px"
      ></div>
    </div>
  </div>
</div>
```

##### C. Cancelled Booking

**Recommended Subject:** `Canceling your booking at Beach House - {check_in} - {check_out}`
**Message Body:**

```html
<div
  style="background-color: #f4f7f9;padding: 20px 10px;font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif"
>
  <div
    style="max-width: 650px;margin: 0 auto;background-color: #ffffff;border: 1px solid #e2e8f0;border-radius: 16px;overflow: visible"
  >
    <div style="padding: 20px;border-bottom: 1px solid #f1f5f9">
      <table width="100%" border="0" cellspacing="0" cellpadding="0">
        <tr>
          <td style="padding-bottom: 10px">
            <img
              src="https://beachhouse.ro/wp-content/uploads/cropped-beach-house-guest-house-1.jpg"
              alt="Beach House Logo"
              style="width: 100%;max-width: 160px;height: auto"
            />
          </td>
          <td
            align="right"
            style="font-size: 12px;color: #64748b;line-height: 1.4"
          >
            <strong style="color: #0f172a;font-size: 14px">Beach House</strong
            ><br />
            Strada D4, Nr. 57, Năvodari<br />
            CT 905701, România<br />
            +40737501765<br />
            <a
              href="mailto:booking@beachhouse.ro"
              style="color: #0077b6;text-decoration: none"
              >booking@beachhouse.ro</a
            ><br />
            <a
              href="https://beachhouse.ro/en/"
              style="color: #0077b6;text-decoration: none"
              >https://beachhouse.ro/</a
            ><br />
          </td>
        </tr>
      </table>
    </div>

    <div style="padding: 30px">
      <p style="font-size: 16px;margin-top: 0">
        Hi <strong>{customer_name}</strong>,
      </p>
      <p>Thanks for making a booking at <strong>Beach House</strong>!</p>

      <div
        style="background-color: #ff9999;border-left: 4px solid #cc0000;padding: 15px;margin: 25px 0;border-radius: 4px"
      >
        <strong style="color: #ff0000"
          >We are very sorry to reject your booking! {check_in} -
          {check_out}</strong
        ><br />
        <span style="font-size: 14px;color: #800000"
          >We already have another reservation or another issue for your
          requested travel period.</span
        >
      </div>

      <table
        width="100%"
        border="0"
        cellspacing="0"
        cellpadding="0"
        style="margin-top: 30px"
      >
        <tr>
          <td>
            <p style="margin: 0;font-size: 14px;color: #475569">
              We would be very happy to welcome you any time soon.
            </p>
            <p style="margin: 5px 0 0 0;font-weight: bold">Beach House</p>
          </td>
          <td style="text-align: right;vertical-align: bottom">
            <a
              href="https://wa.me/40737501765"
              style="background-color: #25D366;color: #ffffff;padding: 8px 15px;text-decoration: none;border-radius: 6px;font-weight: bold;font-size: 13px"
              >WhatsApp</a
            >
          </td>
        </tr>
      </table>

      <div
        style="margin-top: 30px;border-top: 1px solid #f1f5f9;padding-top: 20px"
      ></div>
    </div>
  </div>
</div>
```

> [!TIP] > **Dynamic Payment Summary Integration:** Note the unclosed `<div>` at the very end of every template. This is a deliberate technique—it allows the plugin to automatically "join" the generated **Booking Payment Summary** (totals, taxes, and deposit info) into the styled email container. This ensures that the system-generated pricing table inherits your brand's layout and padding seamlessly.

### 2.2.4 Frontend Labels

Translate or customize every label on the booking form.

**Multilingual Format (qTranslate-X):**

```
[:en]Check In[:ro]Check-in[:de]Anreise[:fr]Arrivée[:]
```

---

## 2.6 Granular Permissions (Abilities API)

As of Version 2.4.4, Modern Hotel Booking integrates with the modern **WordPress Abilities API** (native to WordPress 7.0+) to provide granular control over administrative access and conversational AI capabilities. This allows you to delegate specific tasks to different staff members and regulate AI assistant behaviors without granting full Administrator privileges.

### 2.6.1 Administrative Abilities

These abilities govern access to the WordPress admin dashboard:

| Ability             | Internal Name          | Description                                           |
| :------------------ | :--------------------- | :---------------------------------------------------- |
| **Manage Bookings** | `mhbo_manage_bookings` | View, edit, confirm, or cancel reservations.          |
| **View Analytics**  | `mhbo_view_analytics`  | Access the Analytics dashboard and financial reports. |
| **Manage Settings** | `mhbo_manage_settings` | Configure plugin global options, payments, and AI.    |

By default, the **Administrator** role is granted all administrative abilities. You can use a role management plugin (like User Role Editor) to grant these specific `mhbo_` capabilities to other roles (e.g., "Editor" or "Shop Manager").

### 2.6.2 AI Concierge Capabilities (WP 7.0+)

In WordPress 7.0+, guest actions performed via the AI Concierge are mapped to specific custom Abilities registered on the `wp_abilities_api_init` hook. The system checks against these permissions prior to executing any agentic action:

**Free Version Abilities:**

- `mhbo_ai_hotel_info`: Query general hotel details and business information.
- `mhbo_ai_check_availability`: Search room availability and fetch base night rates.
- `mhbo_ai_room_details`: Retrieve specific room features, amenities, and image listings.
- `mhbo_ai_policies`: Access check-in, checkout, and cancellation policy information.
- `mhbo_ai_get_knowledge_base`: Read custom cached knowledge base structures.
- `mhbo_ai_local_tips`: Return local guide recommendations (restaurants, transport).
- `mhbo_ai_get_business_card`: Retrieve payment instructions and professional contact cards.
- `mhbo_ai_create_booking_link`: Generate pre-filled checkout redirection links.

**Pro Version Abilities:**

- `mhbo_ai_create_booking`: Draft direct booking reservations.
- `mhbo_ai_modify_booking`: Update dates, guests, or room parameters on existing active bookings.
- `mhbo_ai_cancel_booking`: Process guest-initiated cancellations.
- `mhbo_ai_apply_promo`: Evaluate and apply discount coupons to active bookings.
- `mhbo_ai_guest_history`: Retrieve booking log histories for returning verified guests.

### 2.6.3 WordPress Abilities Explorer

WordPress 7.0+ introduces a built-in **Abilities Explorer** to help administrators inspect, test, and manage registered capabilities and AI tools. Since Modern Hotel Booking registers all its AI Concierge capabilities as native WordPress Abilities, they will automatically appear here.

#### How to Access the Explorer

To view and manage registered abilities:

1. Navigate to your WordPress Admin dashboard.
2. Go to **Tools > Abilities Explorer** (`wp-admin/tools.php?page=ai-abilities-explorer`).

#### Using the Explorer with Modern Hotel Booking

Within the Abilities Explorer, you can perform the following actions:

- **Browse Categories**: Inspect abilities grouped under the custom categories registered by our plugin: **Hotel Information**, **Booking Management**, and **Guest Services**.
- **Inspect Schema definitions**: Click on any ability (e.g., `mhbo/check-availability` or `mhbo/get-room-details`) to view its structured JSON Schema. You can verify input schemas (parameters the AI client can send) and output schemas (structure of returned data).
- **Test Capabilities**: Use the interactive "Test Ability" tab inside the Explorer to manually trigger an ability. For example, you can query a room's details or test a check-availability payload without having to start a chat session.
- **Verify Permissions & Visibility**: View the active `permission_callback` for each ability, as well as metadata like Model Context Protocol (MCP) public visibility status.

## 2.3 Displaying the Booking System

The plugin provides both Gutenberg blocks and shortcodes for displaying the booking system.

### 2.3.1 Hotel: Booking Form

**Block Name:** `modern-hotel-booking/booking-form`

A full-featured booking form that allows guests to:

- Select check-in and check-out dates
- Choose number of adults and children
- Select a room from available options
- See pricing in real-time
- Complete the booking request

> [!NOTE] > **Future Booking Window Limit:** Guests can select check-in dates up to **13 months (395 days)** in advance. This is hardcoded to ensure database stability and optimal scheduling.

**Using Block Editor (Recommended):**

1. Edit the page where you want the booking form
2. Click the **+** button to add a block
3. Search for **"Hotel: Booking Form"**
4. Drag the block onto the page
5. Configure block settings in the sidebar (optional)
6. Publish the page

**Block Attributes:**

| Attribute | Type   | Default | Description                                                  |
| --------- | ------ | ------- | ------------------------------------------------------------ |
| `roomId`  | number | `0`     | Pre-select a specific room. Set to `0` for manual selection. |
| `anchor`  | string | -       | Unique HTML ID for the block (used for anchor links).        |

> [!TIP] > **Live Preview:** The Booking Form now features a live Gutenberg preview that renders the actual form interface directly in the editor, allowing you to see exactly how your guests will experience the reservation flow.

### 2.3.2 Hotel: Room Calendar

**Block Name:** `modern-hotel-booking/room-calendar`

Displays a read-only calendar showing room availability dates. Perfect for:

- Showing a specific room's availability
- Displaying booked vs. available dates
- Helping guests plan their stay

**Using Block Editor:**

1. Edit the page
2. Click the **+** button to add a block
3. Search for **"Hotel: Room Calendar"**
4. Drag the block onto the page
5. Select the room from the dropdown in block settings
6. Publish the page

**Block Attributes:**

| Attribute | Type   | Default | Description                                             |
| --------- | ------ | ------- | ------------------------------------------------------- |
| `roomId`  | number | `0`     | Select which room's availability to display (required). |
| `anchor`  | string | -       | Unique HTML ID for the block.                           |

### 2.3.3 Booking Form Shortcode

**Shortcode:** `[mhbo_booking_form]` or `[modern_hotel_booking]` (legacy alias)

Alternative to using blocks. Accepts same parameters as block settings.

**Shortcode Parameters:**

| `room_id` | Pre-select a specific room | `[mhbo_booking_form room_id="123"]` |
| `show_calendar` | Show availability calendar | `[mhbo_booking_form show_calendar="yes"]` |

### 2.3.4 AI Concierge Shortcode

**Shortcode:** `[mhbo_ai_concierge]`

Embed the AI virtual assistant directly on any page. This is particularly useful for dedicated support or "Ask Marina" pages.

**Shortcode Parameters:**

| Parameter         | Description                     | Example                                           |
| ----------------- | ------------------------------- | ------------------------------------------------- |
| `variant`         | `floating` or `inline`          | `[mhbo_ai_concierge variant="inline"]`            |
| `position`        | `bottom-right` or `bottom-left` | `[mhbo_ai_concierge position="bottom-left"]`      |
| `welcome_message` | Override the default greeting   | `[mhbo_ai_concierge welcome_message="Hi there!"]` |
| `theme`           | (Pro) Custom UI theme name      | `[mhbo_ai_concierge theme="glass"]`               |

### 2.3.5 Room Calendar Shortcode

**Shortcode:** `[mhbo_room_calendar room_id="123"]`

Display availability for a specific room. Replace `123` with the Room ID (found in the Rooms list).

### 2.3.5 Booking Search Widget

A compact search box for sidebars or footers.

1. Go to **Appearance > Widgets**
2. Find **"MHBO: Booking Search"** widget
3. Drag it to your desired widget area
4. Set the **Title** (e.g., "Book Your Stay")

> [!TIP] > **Widget Performance:** The widget operates exceptionally fast as a "read-only dispatcher." It simply captures dates and dynamically redirects users to your primary booking page to process the calculation, preventing fragmented booking logic across your website.

### 2.3.7 Business Information Blocks & Shortcodes

The plugin provides several specialized blocks to display your property's professional identity and offline payment methods efficiently.

**Available Blocks:**

- **Hotel: Company Profile** (`modern-hotel-booking/company-info`)
- **Hotel: Chat on WhatsApp** (`modern-hotel-booking/whatsapp-button`)
- **Hotel: Bank Transfer Details** (`modern-hotel-booking/banking-details`)
- **Hotel: Pay via Revolut** (`modern-hotel-booking/revolut-details`)
- **Hotel: Business Contact Card** (`modern-hotel-booking/business-card`)

**Block Attributes (Gutenberg):**

| Block                | Attribute          | Type    | Default      | Description                        |
| -------------------- | ------------------ | ------- | ------------ | ---------------------------------- |
| **Company Profile**  | `layout`           | string  | `"vertical"` | `vertical` or `horizontal` layout. |
|                      | `showLogo`         | boolean | `true`       | Show/hide property logo.           |
|                      | `showAddress`      | boolean | `true`       | Show/hide physical address.        |
| **Chat on WhatsApp** | `style`            | string  | `"button"`   | `button`, `link`, or `floating`.   |
|                      | `text`             | string  | -            | Custom button text.                |
|                      | `message`          | string  | -            | Pre-filled guest message.          |
| **Bank Details**     | `layout`           | string  | `"card"`     | `card` or `list` view.             |
|                      | `showInstructions` | boolean | `true`       | Show/hide transfer labels.         |
| **Pay via Revolut**  | `layout`           | string  | `"card"`     | `card` or `list` view.             |
|                      | `showQR`           | boolean | `true`       | Show/hide Payment QR code.         |
|                      | `showLink`         | boolean | `true`       | Show/hide Revolut.me link.         |
| **Business Card**    | `sections`         | string  | `"all"`      | Comma-separated list of items.     |

**Additional Shortcodes:**

| Shortcode                | Description               | Example                                            |
| ------------------------ | ------------------------- | -------------------------------------------------- |
| `[mhbo_company_info]`    | Display company details   | `[mhbo_company_info layout="horizontal"]`          |
| `[mhbo_whatsapp]`        | WhatsApp chat link/button | `[mhbo_whatsapp text="Contact Us"]`                |
| `[mhbo_banking_details]` | Bank transfer information | `[mhbo_banking_details layout="card"]`             |
| `[mhbo_revolut_details]` | Revolut payment info      | `[mhbo_revolut_details show_qr="yes"]`             |
| `[mhbo_business_card]`   | Combined business card    | `[mhbo_business_card sections="company,whatsapp"]` |
| `[mhbo_payment_methods]` | Bank + Revolut grid       | `[mhbo_payment_methods]`                           |

## 2.4 Managing Bookings

1. Go to **Hotel Booking > Bookings**
2. View all bookings with status indicators

**Booking Actions:**

| Action       | Description                                    |
| ------------ | ---------------------------------------------- |
| View         | See complete guest details, pricing, and dates |
| Edit         | Modify booking details                         |
| Confirm      | Change status from Pending to Confirmed        |
| Cancel       | Cancel the booking                             |
| Add Notes    | Private admin notes (not visible to guests)    |
| Resend Email | Resend confirmation email to guest             |

---

## 2.5 Business Information & Communication

Configuring your property's professional identity and offline payment methods is managed in **Hotel Booking > Settings > Business**.

### 2.5.1 Company Profile

Fill in your legal company name, physical address, registration numbers, and upload your high-resolution logo. This data can be displayed on your website using the `[mhbo_company_info]` shortcode or the **Company Info** Gutenberg block.

### 2.5.2 WhatsApp Integration

Enable direct communication with your guests.

- **Phone Number**: Enter in international format (e.g., `+34600000000`).
- **Display Styles**:
  - **Inline Button**: Fits anywhere in your content.
  - **Floating Button**: Sticky button in the corner of the screen.
  - **Text Link**: Simple hyperlink with a WhatsApp icon.
- **Default Message**: Pre-fill the guest's message (e.g., "Hello, I have a question about my booking...").

### 2.5.3 Pay on Arrival & Offline Payments

If you accept offline payments, use these sections to provide your secure details.

- **Bank Transfer**: Supports IBAN, SWIFT/BIC, and custom reference prefixes (e.g., `BOOKING-{id}`).
- **Revolut**: Provide your Revolut.me link and upload a custom **Payment QR Code** for instant mobile transfers.
- **Instructions**: Use the rich-text editor to provide detailed payment or verification steps for your guests.

---

# Part 3: Pro Version User Guide

Unlock advanced automation, payments, and analytics with a Pro license.

## 3.1 Activating Your License

1. Go to **Hotel Booking > Settings > License**
2. Enter your Pro License Key
3. Click **Activate License**
4. New tabs appear: **Payments**, **Tax**, **API**, **GDPR**, **Extras**, **Themes**

### 3.1.1 License Verification & Resilience

To prevent sudden deactivation of Pro features during network outages, temporary server downtime, or external API failures, the plugin implements an **Adaptive License Verification** system:

- **Adaptive Checking Intervals**: The verification engine dynamically scales check frequencies. A healthy license with a distant expiration date is checked every **45 days**. As expiration approaches or if the license health status changes, the interval scales down to **14 days**, **7 days**, and finally **daily** checks.
- **Fail-Safe Network Retries**: If a scheduled license check fails due to network dropouts or API errors, the system does not immediately revoke Pro privileges. Instead, it schedules **3 retries spaced 6 hours apart**.
- **Extended Grace Periods**: If all retries fail, a grace period begins. This grace period scales between **3 and 21 days** based on license health and client loyalty, ensuring ample time to resolve billing or network issues before any Pro functionality is restricted.

## 3.2 Payment Gateways

Accept credit cards and automate confirmations with integrated payment processing.

### 3.2.1 Stripe Integration

Stripe provides secure credit card processing with Apple Pay and Google Pay support. The MHBO engine uses a **Server-Side Intent** model to ensure transaction integrity and seamless recovery for 3D Secure (3DS) authentication redirects.

**Setup Instructions:**

1. Go to **Hotel Booking > Settings > Payments**
2. Enable **Stripe** by checking the box
3. Select **Mode**:
   - **Test** - For development (use test cards)
   - **Live** - For production (real transactions)
4. Enter your API keys from [Stripe Dashboard](https://dashboard.stripe.com/apikeys):
   - **Publishable Key** - Safe for frontend
   - **Secret Key** - Encrypted in database
5. Click **Test Credentials** to verify

**Stripe Test Card Numbers:**

| Card Number           | Result             |
| --------------------- | ------------------ |
| `4242 4242 4242 4242` | Success            |
| `4000 0000 0000 0002` | Decline            |
| `4000 0000 0000 9995` | Insufficient funds |

**Apple Pay & Google Pay:**

When Stripe is enabled, the booking form automatically displays Apple Pay and Google Pay buttons for compatible devices. Requirements:

- HTTPS (SSL certificate)
- Domain verification in Stripe Dashboard
- Compatible browser/device

### 3.2.2 PayPal Integration

**Setup Instructions:**

1. Go to **Hotel Booking > Settings > Payments**
2. Enable **PayPal** by checking the box
3. Select **Mode** (Sandbox or Live)
4. Enter credentials from [PayPal Developer](https://developer.paypal.com/):
   - **Client ID** - Public identifier
   - **Secret** - Encrypted in database
5. Click **Test Credentials** to verify

### 3.2.3 Pay on Arrival

Allow guests to book now and pay at arrival.

**Setup:**

1. Go to **Hotel Booking > Settings > Payments**
2. Enable **Pay on Arrival**
3. Enter instructions shown to guests

**Behavior:**

- Payment status set to "pending" until collected

> [!IMPORTANT] > **Enterprise-Grade Security:** All sensitive API keys (Stripe Secret, PayPal Secret) are stored in the database using high-security **AES-256-CBC encryption**. This ensures that even if the database is compromised, your financial credentials remain protected.

### 3.2.4 Real-time Availability & Fraud Prevention

Modern Hotel Booking incorporates advanced server-side protections:

- **Atomic Availability Locking:** The system performs a final real-time availability check milliseconds before processing payment, preventing race conditions. It utilizes native MySQL advisory locks (`GET_LOCK`) to guarantee that only one guest can secure the final room of a specific type at any given millisecond. This bypasses all caching layers for maximum accuracy.
- **Financial Integrity (Anti-Tamper):** Every booking total is recalculated server-side using the `Pricing` engine immediately during the checkout process. This ensures that any malicious modification of pricing data via the browser console or URL parameters is automatically detected and corrected before payment.
- **3DS Recovery & Persistence:** In rare cases where guests are redirected for 3D Secure verification, the plugin uses a state-aware recovery mechanism to safely persist guest data (including children's ages and extras) until the payment is fully confirmed by the Stripe webhook.
- **Server-to-Server Verification:** PayPal and Stripe orders are verified directly with their respective APIs to ensure the transaction amount matches the booking total.
- **Rate Limiting (Brute Force Protection):** Form submissions are strictly rate-limited to 5 requests per minute per IP address, preventing spam automated bots from filling your database.

**Payment Statuses:**

| Status     | Description            | Color  |
| ---------- | ---------------------- | ------ |
| Pending    | Awaiting payment       | Yellow |
| Processing | Payment in progress    | Blue   |
| Completed  | Payment received       | Green  |
| Failed     | Payment attempt failed | Red    |
| Refunded   | Payment returned       | Gray   |

## 3.3 VAT/TAX System

Comprehensive tax management for hotels worldwide.

### 3.3.1 Tax Modes Explained

| Mode                  | Description                     | Example                                  |
| --------------------- | ------------------------------- | ---------------------------------------- |
| Disabled              | No tax calculation              | Price = $100                             |
| VAT (Inclusive)       | Tax included in displayed price | Display $100, includes $16.67 VAT at 20% |
| Sales Tax (Exclusive) | Tax added on top of price       | Display $100 + $20 tax = $120 total      |

### 3.3.2 Configuring Tax Rates

1. Go to **Hotel Booking > Settings > Tax**
2. Select **Tax Mode** (Disabled/VAT/Sales Tax)
3. Enter **Accommodation Tax Rate** (for room charges)
4. Enter **Extras Tax Rate** (for add-ons, may differ)
5. Add **Tax Registration Number** (displayed on invoices)

**Country-Specific Reference Rates:**

| Country | Standard VAT | Reduced (Hotels) |
| ------- | ------------ | ---------------- |
| Romania | 21%          | 11%              |
| Germany | 19%          | 7%               |
| France  | 20%          | 10%              |
| Italy   | 22%          | 10%              |
| Spain   | 21%          | 10%              |
| UK      | 20%          | 5%               |

_Note: Rates may change. Verify current rates with local authorities._

### 3.3.3 Multilingual Tax Labels

Translate tax labels for multilingual sites:

```
[:en]VAT[:ro]TVA[:de]MwSt[:fr]TVA[:]
```

## 3.4 Advanced Pricing Rules

### 3.4.1 Seasonal Pricing

Create date-based pricing adjustments.

**To Create a Seasonal Rule:**

1. Go to **Hotel Booking > Pricing Rules**
2. Click **Add New Rule**
3. Configure:

| Setting         | Description                       |
| --------------- | --------------------------------- |
| Name            | Rule name (e.g., "Summer Season") |
| Start Date      | First day of the season           |
| End Date        | Last day of the season            |
| Adjustment Type | Fixed or Percentage               |
| Amount          | Price change amount               |

**Adjustment Types:**

| Type                | Example | Result            |
| ------------------- | ------- | ----------------- |
| Fixed Increase      | +$50    | $100 becomes $150 |
| Fixed Decrease      | -$20    | $100 becomes $80  |
| Percentage Increase | +20%    | $100 becomes $120 |
| Percentage Decrease | -15%    | $100 becomes $85  |

### 3.4.2 Weekend & Holiday Multipliers

**Weekend Pricing:**

1. Go to **Hotel Booking > Settings > Advanced Pricing**
2. Select **Weekend Days** (Fri/Sat/Sun)
3. Select **Weekend Modifier Type** (`multiplier`, `percent`, or `fixed`)
4. Set **Weekend Modifier Amount** (e.g., 1.2 for a 20% multiplier increase, 20 for a 20% percent increase, or 50 for a $50 fixed increase)

**Holiday Pricing:**

1. Go to **Hotel Booking > Pricing Rules**
2. Add a new rule for your holiday dates
3. Select **Adjustment Type** (`Fixed` or `Percentage`)
4. Set the **Amount**

### 3.4.3 Global Stay Limits

New in v2.4.0, you can set property-wide stay constraints that apply to all bookings unless overridden by a specific Room Type rule.

1. Go to **Hotel Booking > Settings > General**
2. Locate the **Global Stay Limits** section:
   - **Global Minimum Stay**: Minimum consecutive nights required for any booking.
   - **Global Maximum Stay**: Maximum consecutive nights allowed (use `0` for no limit).

> [!TIP] > **Priority Logic**: If a Room Type has a 3-night minimum and the Global limit is 1-night, the **Room Type** (3-night) will prevail. Specificity always wins over global rules.

### 3.4.4 Custom Hotel Timezone

To ensure your calendar, availability rules, and iCal sync are perfectly aligned with your physical property (even if your server is in a different region):

1. Go to **Hotel Booking > Settings > General**
2. Find **Hotel Timezone**.
3. Select your local timezone from the dropdown.

All check-in/out times and date-based rules will now be computed using this physical location's clock.

#### 3.4.5 Financial Precision (BCMath)

To ensure accounting-grade accuracy, the pricing engine utilizes the **PHP BCMath** extension for all currency calculations. This prevents common floating-point rounding errors often found in simpler booking systems. If your server does not have BCMath enabled, a high-precision fallback is automatically active.

> [!NOTE]
> Pricing rules have priorities. Weekend modifiers are applied to the base rate first, and then Seasonal/Holiday rules are applied on top of that result.

> [!TIP]
> For per-day precision — setting a specific price, blocking a date, or applying min/max stay restrictions on a single night — use the **Admin Calendar Editor** (see [section 3.11](#311-admin-calendar-pricing--availability-editor)). Calendar overrides always win over seasonal rules.

### 3.4.6 Multi-Select Pricing Override Example

To set a specific price for all room types on every Saturday in July:

1. Go to **Hotel Booking > Inventory** (Calendar View).
2. Click **Bulk Update**.
3. Select **Date Range**: July 1st to July 31st.
4. Check **Days of Week**: Saturday.
5. Enter **Custom Price**: 250.
6. Click **Apply & Save**.

## 3.5 Booking Extras & Add-ons

Upsell services during checkout. From **Hotel Booking > Extras**, you can manage optional or mandatory services.

### 3.5.1 Mandatory Fees (Cleaning, Resort, etc.)

You can mark any extra as **Compulsory**. These items are automatically added to every booking and cannot be removed by the guest.

- **Example**: A $50 "Cleaning Fee" per booking.
- **Example**: A $10 "City Tax" per person.

### 3.5.2 Compulsory Service Fee (Percentage)

For a general service charge applied to the entire reservation:

1. Go to **Hotel Booking > Settings > Payments**.
2. Enable **Compulsory Service Fee**.
3. Set **Fee Type** to `Percentage`.
4. Set **Value** to `5.00` (for 5%).
5. This fee is calculated on the _Gross Total_ before taxes are applied.

**Pricing Models:**

| Model              | Calculation       | Example              |
| ------------------ | ----------------- | -------------------- |
| Per Booking        | One-time fee      | $50 transfer         |
| Per Person         | × Total Guests    | $15 × 4 guests = $60 |
| Per Day            | × Nights          | $10 × 3 nights = $30 |
| Per Person Per Day | × Guests × Nights | $5 × 4 × 3 = $60     |

## 3.6 iCal Sync Manager

Prevent double bookings by syncing with external OTA platforms in both directions. The system is **per-room** — each physical room has its own export URL and its own set of inbound connections, giving you independent control and key rotation without affecting other rooms.

> [!IMPORTANT] > **Pro Feature Only:** The iCal Synchronization interface is physically absent from the Free distribution of the plugin. This functionality is exclusive to the Pro version.

**Supported Platforms (auto-detected from the URL — no manual selection needed):**

| Platform        | Badge Color | Detected From                            |
| --------------- | ----------- | ---------------------------------------- |
| Airbnb          | Red         | `airbnb.com` in URL                      |
| Booking.com     | Navy        | `booking.com` or `admin.booking` in URL  |
| Google Calendar | Blue        | `google.com` or `calendar.google` in URL |
| Custom / Other  | Grey        | Any other HTTPS URL                      |

---

### Complete Setup Walkthrough (Do This for Each Room)

This is the full process from zero to two-way sync for one room. Repeat for every room you list on external platforms.

**What two-way sync means:**

- **Your site → OTA:** When a guest books directly on your site, those dates become blocked on the OTA platform (Airbnb, etc.).
- **OTA → Your site:** When a guest books on the OTA, those dates become blocked on your site.

Both directions must be configured. The steps below cover both.

> [!IMPORTANT] > **Booking.com restriction (March 2025):** Booking.com no longer accepts iCal feeds from personal or custom-domain websites. Pasting your MHBO Export URL into Booking.com's extranet will return "This iCal URL isn't valid." This is a Booking.com policy decision — the feed itself is valid. See [Section 3.6.9 — Booking.com Limitation & Workarounds](#369-bookingcom-limitation--workarounds) for your options. The **import direction (Booking.com → MHBO) still works** and should always be configured.

---

**Step 1 — Open the room's iCal page**

1. Go to **Hotel Booking > Rooms** in your WordPress dashboard
2. Find the room (e.g., "Room 101") and click the **iCal** icon in its Actions column
3. The iCal Sync page for that room opens

---

**Step 2 — Copy your Export URL and give it to Airbnb**

This tells Airbnb about your direct bookings so it blocks those dates on their calendar.

1. At the top of the iCal page, find the **Deployment Export URL** field
2. Click **Copy URL** — the URL is now in your clipboard
3. Log in to Airbnb → **Calendar** for your listing → **Availability Settings** → **Connect Calendars** → **Import Calendar** → paste URL → Save

Airbnb re-polls this URL approximately every **2–3 hours**. After saving, wait up to 3 hours before assuming something is wrong.

> [!WARNING] > **Booking.com cannot import this URL.** Since March 2025, Booking.com rejects iCal feeds from any non-OTA website. Skip this step for Booking.com — see [Section 3.6.9](#369-bookingcom-limitation--workarounds) for the available workarounds.

---

**Step 3 — Get the OTA's iCal URL and add it to MHBO**

This tells your site about OTA bookings.

1. Go to your OTA platform and find its iCal export URL:

   **Airbnb:** Calendar → Availability Settings → Export Calendar → copy the `.ics` URL

   **Booking.com:** Extranet → Rates & Availability → Synchronise Calendars → Export Calendar (iCal) → copy the `.ics` URL

2. Back on the MHBO iCal page, scroll down to **Import External Calendars**
3. In the form below the connections table:
   - **Connection Label:** type a descriptive name, e.g. `Airbnb Room 101`
   - **iCal Feed URL:** paste the `.ics` URL you just copied
4. Click **Connect Calendar**
5. The platform badge (Airbnb / Booking.com) is set automatically — no dropdown needed
6. The connection appears in the table with status **Pending**

---

**Step 4 — Trigger the first sync**

1. In the connections table, click the **refresh (↻) icon** next to the connection you just added
2. Wait a few seconds, then reload the page
3. The status should change to **Synced** (green check) and show the number of events imported
4. Any existing OTA bookings now appear in your **Hotel Booking > Bookings** list as external reservations

---

**Step 5 — Confirm it's working**

- Open the Export URL in your browser — you should see valid iCal text starting with `BEGIN:VCALENDAR`
- Validate the feed at [icalendar.org/validator.html](https://icalendar.org/validator.html) if you see a rejection error from any OTA
- The automatic background sync (MHBO pulling OTA feeds) runs every **1 hour by default**

**Realistic sync time expectations:**

| Direction                   | Platform            | How long to expect                                          |
| --------------------------- | ------------------- | ----------------------------------------------------------- |
| OTA → MHBO (import)         | Airbnb, Booking.com | Up to 1 hour (your MHBO cron frequency)                     |
| MHBO → Airbnb (export)      | Airbnb              | Airbnb re-polls every **2–3 hours**; plan for up to 3 hours |
| MHBO → Booking.com (export) | Booking.com         | **Blocked** since March 2025 — see Section 3.6.9            |

> [!IMPORTANT]
> iCal sync is **never instant**. There is always a window — up to 3 hours for Airbnb, up to 24 hours for Booking.com's own calendar refresh — where a double booking is possible. For high-volume properties, a real-time channel manager API connection eliminates this risk.

> [!TIP] > **Repeat for each room and each platform.** If Room 101 is listed on both Airbnb and Booking.com, you will have two connections in the table — one for each platform — both sharing the same single Export URL.

---

### 3.6.1 Step 1 — Open a Room's iCal Page

1. Go to **Hotel Booking > Rooms**
2. Locate the room you want to connect and click its **iCal** action icon in the row Actions column
3. The **iCal Sync** page opens (heading: _"iCal Sync — Room [number]"_)

> [!NOTE]
> Every room is configured independently. Repeat these steps for each physical room you list on Airbnb or Booking.com.

---

### 3.6.2 Step 2 — Export: Share Your Availability With OTAs

The **Deployment Export URL** at the top of the iCal page is the HTTPS feed your OTA platforms will poll to learn which dates are already booked on your site.

**About the export URL:**

- Format: `https://yoursite.com/mhbo-ical/room-{id}.ics?key={24-char-token}`
- Each room has its **own unique key** — rotating one room's key does not affect any other room
- The key is auto-generated on first access and stored permanently
- The ICS feed is object-cached for 1 hour to reduce server load; the cache clears automatically when a booking changes

**To copy the export URL:** Click the **Copy URL** button to the right of the URL field. The button flashes "Copied!" for 2 seconds.

**Giving your MHBO calendar to Airbnb:**

1. Log in to Airbnb → **Calendar** for your listing
2. Click **Availability** → **Connect Calendars** (or "Import Calendar")
3. Paste your MHBO Export URL and save

Airbnb will begin polling the feed automatically. Re-polling happens approximately every **2–3 hours**; you can also trigger a manual refresh from Airbnb's calendar settings.

**Giving your MHBO calendar to Booking.com:**

> [!CAUTION] > **This direction is blocked by Booking.com (since March 2025).** Booking.com validates iCal import URLs by domain and rejects any URL not from a recognized OTA platform. You will receive the error "This iCal URL isn't valid — please try again" regardless of whether your feed is technically correct. This is not a bug in MHBO — the feed is RFC 5545 compliant. See [Section 3.6.9 — Booking.com Limitation & Workarounds](#369-bookingcom-limitation--workarounds) for alternatives.

---

### 3.6.3 Step 3 — Import: Pull OTA Bookings into MHBO

Connections you add here instruct the plugin to fetch the OTA's calendar on a schedule and create external bookings for any occupied dates, blocking them from new reservations.

**To add a new connection:**

1. In the **Import External Calendars** section, find the form below the connections table
2. Fill in:
   - **Connection Label** — a descriptive name, e.g. `Airbnb Room 101` or `Travelminit Suite`
   - **iCal Feed URL (HTTPS)** — the `.ics` URL provided by the OTA (see below)
3. Click **Connect Calendar**
4. The platform is **auto-detected** (Airbnb, Booking.com, Travelminit, Szallas.hu, etc.) — no dropdown needed
5. The new connection appears in the table with status **Pending** until the first sync completes

> [!IMPORTANT]
> Only HTTPS URLs pointing to public OTA calendars are accepted. Private IP ranges, `localhost`, and non-HTTPS URLs are rejected by the SSRF protection layer.

**Getting your iCal URL from Airbnb:**

1. Log in to Airbnb → **Calendar** for your listing
2. Click **Availability Settings** → **Export Calendar**
3. Copy the `.ics` URL shown

**Getting your iCal URL from Booking.com:**

1. Log in to Booking.com Extranet → **Rates & Availability**
2. Click **Synchronise Calendars** → **Export Calendar (iCal)**
3. Copy the `.ics` URL shown

---

### 3.6.4 Reading the Connections Table

| Column              | What you see                                                                                      |
| ------------------- | ------------------------------------------------------------------------------------------------- |
| **Platform**        | Color-coded badge: Airbnb (red), Booking.com (navy), Google Cal (blue), Custom (grey)             |
| **Connection Name** | The label you entered. If the last sync failed, the error message appears in red beneath the name |
| **Status**          | Green check = **Synced** · Red warning = **Failed** · Amber clock = **Pending**                   |
| **Last Sync**       | Relative time ("3 hours ago") plus the number of calendar events imported                         |
| **Actions**         | Refresh icon (↻) = **Sync Now** · Trash icon = **Disconnect**                                     |

---

### 3.6.5 Syncing Manually

**Sync a single connection:**  
Click the **refresh (↻) icon** in the Actions column for that row. The page reloads with the result shown in the status column.

**Force sync all connections for this room:**  
Click the **Force Global Sync** button at the bottom of the iCal page. This triggers an immediate pass over every connection belonging to the current room.

---

### 3.6.6 Automatic Background Sync

The plugin uses WordPress Cron to sync all connections automatically once configured. No ongoing manual action is required. The **Automatic Sync Settings** panel provides granular control over sync frequency, failure handling, and notifications.

**Configuring the sync schedule:**

1. Go to **Hotel Booking > PRO Features > iCal Sync**
2. The **Automatic Sync Settings** card is displayed at the top of the page. Configure:

| Setting                     | Options                                                   | Default     | Description                                                                                                                             |
| --------------------------- | --------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Enable Auto-Sync**        | On / Off                                                  | On          | When enabled, all iCal connections across all rooms are synced automatically on the chosen schedule.                                    |
| **Sync Interval**           | 5 min · 15 min · **Hourly ⭐** · 1 hour · 6 hours · Daily | Hourly      | How frequently the plugin fetches external calendars. See guidance below.                                                               |
| **Next Scheduled Sync**     | _(read-only)_                                             | —           | Displays the exact date and time of the next scheduled cron run so you can verify the schedule is active.                               |
| **Auto-Retry on Failure**   | On / Off                                                  | On          | When enabled, failed syncs are automatically retried with exponential backoff (1h → 2h → 4h) to avoid hammering unresponsive endpoints. |
| **Email Notifications**     | On / Off                                                  | Off         | Send email alerts when sync failures exceed the threshold. Enter the notification email address in the field below the checkbox.        |
| **Notification Email**      | Email address                                             | Admin email | The email address that receives sync failure and success notifications.                                                                 |
| **Failure Email Threshold** | 1–10                                                      | 3           | Number of consecutive failures before an alert email is sent.                                                                           |
| **Conflict Resolution**     | Local Wins · External Wins                                | Local Wins  | Determines which booking takes priority when the same dates exist in both the local database and the OTA feed.                          |
| **Notify on Success**       | On / Off                                                  | Off         | Send an email after every successful sync cycle (useful for monitoring during initial setup).                                           |

3. Click **Save Settings** — a green **✓ Saved** indicator appears next to the button

---

#### Choosing the Right Sync Interval

> [!TIP] > **💡 Recommended: Hourly** — This is the optimal setting for most properties. It balances real-time accuracy with server performance. Airbnb and Booking.com typically update their own calendars every 1–2 hours, so syncing more frequently than hourly provides diminishing returns.

| Interval          | Best For                                                         | Server Impact | Notes                                                                                  |
| ----------------- | ---------------------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------- |
| **5 minutes**     | High-traffic properties with 10+ rooms and frequent OTA bookings | 🔴 High       | Only use if double-bookings are a critical business risk. Requires a real system cron. |
| **15 minutes**    | Properties with 5–10 rooms and moderate OTA traffic              | 🟡 Medium     | Good compromise for busy listings.                                                     |
| **Hourly ⭐**     | Most properties (1–10 rooms)                                     | 🟢 Low        | **Recommended default.** Matches the refresh cadence of major OTAs.                    |
| **Every 1 hour**  | Same as Hourly                                                   | 🟢 Low        | Alias for the Hourly schedule.                                                         |
| **Every 6 hours** | Low-volume properties or seasonal listings                       | 🟢 Very low   | Suitable for properties with infrequent OTA bookings.                                  |
| **Daily**         | Archive/test properties                                          | 🟢 Minimal    | Only for properties where real-time sync is not important.                             |

> [!IMPORTANT] > **Conflict Resolution:** "Local Wins" preserves your MHBO booking when the same date appears in both the local database and the OTA feed. Use "External Wins" only when the OTA is the single source of truth and you never create bookings directly in MHBO. Under "External Wins", if an external booking overlaps with multiple smaller local bookings, **all** conflicting local bookings will be automatically cancelled. This conflict resolution strictly adheres to your global **Same-day Turnover** settings to prevent back-to-back overlaps.

> [!NOTE]
> WordPress Cron is triggered by site traffic. On low-traffic sites, add a true system cron entry that calls `curl https://yoursite.com/wp-cron.php?doing_wp_cron` every 5–15 minutes to ensure the sync fires reliably.

---

### 3.6.7 Security Architecture

- **SSRF Protection:** Every import URL is validated through `Security::is_safe_url()` before any HTTP request is made. Private IP ranges (127.x, 10.x, 192.168.x, etc.) are unconditionally blocked.
- **SSL Verification:** All outbound requests enforce `sslverify: true`. Feeds served over HTTP or with invalid certificates are rejected.
- **Per-Room Key Isolation:** Each room's export URL uses a distinct 24-character alphanumeric token. Rotating one room's key leaves all other rooms unaffected.
- **SEQUENCE Tracking:** The plugin tracks RFC 5545 `SEQUENCE` numbers to detect when an OTA has modified an existing event, preventing stale data from overwriting a newer local booking.
- **Orphan Cleanup:** If an external booking is removed from the OTA's calendar (e.g., a cancellation on their side), MHBO automatically cancels the corresponding internal shadow booking on the next sync.
- **Zero-Cache Feed Architecture:** The export feed (ICS) utilizes `DONOTCACHEPAGE` and `nocache_headers()` to bypass all caching layers (Nginx, Varnish, Cloudflare, and WP plugins). This ensures OTAs always receive real-time availability.
- **Input Sanitization:** All data arriving from external ICS feeds is sanitized and processed through `BookingProcessor::process()` — raw database inserts from feed data are never performed.

---

### 3.6.8 Troubleshooting iCal Sync

See also [Section 6.6 — iCal Sync Issues](#66-ical-sync-issues) for a full diagnostics table.

| Symptom                                      | Likely Cause                                                 | Fix                                                                                                                                                       |
| -------------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Status stuck on **Pending**                  | No sync has run yet                                          | Click the Sync Now (↻) button or wait for the next cron cycle                                                                                             |
| Status **Failed** with error message         | Network error, invalid URL, or OTA changed the feed format   | Read the inline error; verify the URL still opens in a browser                                                                                            |
| Export URL returns an empty calendar         | No bookings exist for that room                              | Expected — add a test booking to confirm the feed works                                                                                                   |
| Export URL returns 404 / Not Found           | Rewrite rule not registered                                  | The plugin now **self-heals** this automatically on the next page load. If the issue persists, go to **Settings > Permalinks** and click **Save Changes** |
| Airbnb still shows dates as available        | Airbnb has not re-polled yet                                 | Wait up to **3 hours**; Airbnb polls every 2–3 hours                                                                                                      |
| Booking.com says "This iCal URL isn't valid" | March 2025 policy — Booking.com blocks custom-site URLs      | See Section 3.6.9 — no fix exists on the MHBO side                                                                                                        |
| Duplicate bookings appearing                 | Conflict set to "External Wins" with existing local bookings | Switch Conflict Resolution to "Local Wins"                                                                                                                |
| Booking.com bookings not appearing in MHBO   | Booking.com export URL not added as a connection             | Add Booking.com's `.ics` export URL in Import External Calendars                                                                                          |
| Dates shifted by one day                     | UTC vs. Local offset mismatch                                | Ensure **Hotel Timezone** is set correctly in General Settings. Version 2.4.4 fixes this via substring date parsing.                                      |

> [!IMPORTANT] > **After updating the plugin files on production**, follow these steps to ensure the iCal export URLs work correctly:
>
> 1. **Deploy** the updated plugin files to your production server (via FTP, Git, or your deployment tool).
> 2. **Visit Settings → Permalinks** in WP Admin and click **Save Changes** to ensure a clean rewrite flush.
> 3. **Verify the export URL** by opening `https://yoursite.com/mhbo-ical/room-{id}.ics?key={token}` in a browser — you should see valid iCal text starting with `BEGIN:VCALENDAR`.
> 4. **Check the Automatic Sync Settings** panel at **Hotel Booking > PRO Features > iCal Sync** and confirm the "Next Scheduled Sync" field shows a future date/time.
>
> The plugin now includes a **self-healing mechanism**: if the iCal rewrite rule is missing from the WordPress rewrite rules database, it will be automatically restored on the next page load without manual intervention.

---

### 3.6.9 Booking.com Limitation & Workarounds

#### The Problem

Since **March 2025**, Booking.com validates iCal import URLs by checking the source domain, not the feed content. Only URLs from a recognised OTA whitelist are accepted. Custom-domain URLs — including your MHBO Export URL — are rejected with **"This iCal URL isn't valid — please try again"** regardless of how well-formed the feed is.

This is a **Booking.com policy decision**. The feed itself is RFC 5545 compliant and passes third-party validators like [icalendar.org/validator.html](https://icalendar.org/validator.html).

#### Confirmed Accepted Domains (Booking.com whitelist, April 2026)

These are the OTA platforms whose iCal export URLs Booking.com will accept for import. All others are rejected.

| Platform                   | Domain                     | Free to use                  |
| -------------------------- | -------------------------- | ---------------------------- |
| **Airbnb**                 | `airbnb.com`               | ✅ Yes                       |
| **VRBO / HomeAway**        | `vrbo.com`, `homeaway.com` | ✅ Yes                       |
| **TripAdvisor**            | `tripadvisor.com`          | ✅ Yes                       |
| **Expedia**                | `expedia.com`              | ✅ Yes                       |
| **Trip.com**               | `trip.com`                 | ✅ Yes                       |
| Google Calendar            | `calendar.google.com`      | ❌ Not confirmed — see below |
| iOS / iCloud Calendar      | `icloud.com`               | ❌ Not on whitelist          |
| Personal / custom websites | any other domain           | ❌ Blocked                   |

> [!NOTE] > **Why Google Calendar is not a viable relay:** Google Calendar does accept our MHBO iCal URL as a subscription, but it only refreshes subscribed feeds every **24–48 hours** and this interval cannot be reduced by the user. This would create a 3-hop chain (MHBO → Google Calendar → Booking.com) with a combined worst-case delay of **3+ days**. Even if Google Calendar URLs pass Booking.com's validation (unconfirmed), the propagation delay makes it impractical for preventing double bookings. iOS Calendar has the same problem.

#### Additional Constraint: Property Structure

Booking.com's iCal sync tool is only available on properties with:

- **20 room types or fewer**, AND
- **Maximum 1 unit per room type**

If your Booking.com listing uses multiple units per room type (e.g., "3× Standard Room"), the "Sync Calendars" option may not appear in your extranet. In that case, manual blocking or a channel manager are your only options.

#### What Still Works

| Direction                          | Status         | Notes                                                                                               |
| ---------------------------------- | -------------- | --------------------------------------------------------------------------------------------------- |
| Booking.com → MHBO (import)        | ✅ **Works**   | Add Booking.com's `.ics` export URL as a connection in MHBO. MHBO fetches it on your cron schedule. |
| MHBO → Airbnb (export)             | ✅ **Works**   | Airbnb accepts iCal URLs from any domain. Re-polls every 2–3 hours.                                 |
| MHBO → VRBO (export)               | ✅ **Works**   | VRBO also accepts any iCal URL. Re-polls every 4–6 hours.                                           |
| MHBO → Booking.com (export direct) | ❌ **Blocked** | Custom domain rejected since March 2025.                                                            |

#### Workaround Options

**Option A — Airbnb relay (free — best option)**

If your property is listed on Airbnb, this is the recommended free workaround.

1. Your MHBO export URL is already synced to Airbnb (Airbnb accepts any domain)
2. In **Booking.com Extranet → Rates & Availability → Synchronise Calendars → Add calendar connection**
3. Paste your **Airbnb listing's calendar export URL** (Calendar → Availability Settings → Export Calendar)
4. Booking.com accepts it (Airbnb is on the whitelist)

This creates: **MHBO → Airbnb → Booking.com**
Combined delay: ~4–6 hours (Airbnb polls MHBO every 2–3 h + Booking.com polls Airbnb every 1–24 h)

**Option B — VRBO or TripAdvisor relay (free — if listed there)**

Same principle as Option A. If you list on VRBO, TripAdvisor, or Expedia, their calendar export URLs are accepted by Booking.com. Use whichever OTA you already have.

**Option C — Manual date blocking (free, zero-delay)**

When a direct booking arrives on your site, immediately log in to Booking.com Extranet and block those dates manually in the availability calendar. No delay, no double-booking risk. Practical for properties with low direct booking volume.

**Option D — Booking.com Pulse app (free)**

The **Booking.com Pulse** mobile app (iOS/Android) lets you block and unblock dates on your phone in under 30 seconds. For properties with a moderate direct booking rate this is often the fastest response path.

**Option E — Channel manager API (paid, real-time)**

Services like **Smoobu**, **Hostaway**, **Lodgify**, and **Rentals United** connect to Booking.com via their official Connectivity API rather than iCal. This gives true real-time, two-way sync with no polling delay and no domain restrictions. Entry-level plans start around $10–20/month. Recommended for any property receiving more than 5–10 direct bookings per month.

> [!NOTE]
> Booking.com's own iCal **export** URL (copied from Extranet → Sync Calendars → Export) works normally and is unaffected by this restriction. Always configure the MHBO import direction to pull Booking.com bookings into your site — that direction has no domain restriction.

---

## 3.7 Theme Customizer

Match the booking form and calendar to your website's brand.

1. Go to **Hotel Booking > Settings > Themes**
2. Select a **Theme Preset**:
   - Default (Blue)
   - Midnight (Dark)
   - Emerald (Green)
   - Oceanic (Teal)
   - Sunset (Orange)
   - Royal (Purple)
   - Custom Theme

**Custom Theme:**
If you select the "Custom Theme" preset, you can pick specific colors:

- **Primary Color**: Used for buttons and highlights
- **Secondary Color**: Used for secondary text and borders
- **Accent Color**: Used for active states and selections

**Custom CSS:**
Regardless of your theme preset, you can add **Custom CSS** in this section for granular visual control over the form and calendar blocks.

## 3.8 GDPR Compliance Suite

The Modern Hotel Booking Pro version includes a dedicated suite of tools to ensure your hotel complies with EU GDPR and global data privacy laws.

**Enabling Privacy Features:**

1. Go to **Hotel Booking > Settings > GDPR & Privacy**
2. Check the box for **Enable Pro Privacy Suite** to activate data retention and cookie controls.

**Consent Checkbox (Art. 7):**

1. Enable **Require Privacy Consent** to add a mandatory checkbox to your booking form.
2. Select your **Terms & Conditions Page** from the dropdown.
3. Configure your **Consent Text** per language. Use `[privacy_policy]` to link to your WordPress native privacy policy and `[terms_and_conditions]` to link to the page selected above.
   - _Example:_ `I accept the [privacy_policy] and [terms_and_conditions].`

**Automated Data Retention & Erasure:**

- Under **Automated Data Retention**, set the number of days you want to keep booking records.
- **How it works:** A daily background task (cron job) will automatically scan for bookings older than your chosen threshold.
- **Tax Compliance (Art. 17(3)):** When bookings are "erased", the plugin safely _anonymizes_ all Personally Identifiable Information (PII) like names and emails, but retains the financial totals and tax records required by accounting laws.
- **Zero IP Retention:** The plugin does not save guest IP addresses in the database. When enforcing rate limits, IPs are temporarily hashed and stored as fleeting transients (60 seconds) strictly for security protection.

**Data Export & Erasure Requests (Art. 15 & 17):**

- The plugin hooks into the native WordPress **Tools > Export Personal Data** and **Tools > Erase Personal Data**.
- When handling a guest's request for their data, simply use the native WP tools to generate their booking history or to trigger the anonymization of their records.

## 3.9 Analytics Dashboard

**Access:** Go to **Hotel Booking > Analytics**

The Pro Analytics dashboard provides high-level business intelligence and data visualization for your property.

### 3.9.1 Core Metrics & KPIs

| Metric          | Definition                                                                     | Importance                                                        |
| --------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------- |
| **ADR**         | **Average Daily Rate**. Total room revenue divided by occupied rooms.          | Measures the average income per room per day.                     |
| **RevPAR**      | **Revenue Per Available Room**. Total room revenue divided by total inventory. | The ultimate measure of property performance and yield.           |
| **Occupancy %** | Percentage of rooms sold vs. total available.                                  | High occupancy combined with high ADR indicates peak performance. |
| **Lead Time**   | Average days between booking date and check-in.                                | Helps plan marketing and staffing for upcoming blocks.            |

### 3.9.2 Visualization Widgets

- **Revenue Trends**: Interactive chart showing potential revenue (booked) vs. collected revenue (paid).
- **Occupancy Heatmap**: Visual calendar showing peak demand periods.
- **Booking Sources**: Breakdown of Direct vs. iCal (Airbnb, Booking.com) reservations.
- **Room Performance**: Comparison of which room types generate the most revenue.

### 3.9.3 Forecast Analysis

The plugin includes a **30-Day Predictive Engine** that scans your current bookings and historical availability to project future revenue and occupancy. This allows property managers to adjust pricing strategies before high-demand periods arrive.

### 3.9.4 Professional Reporting & Exports

Export raw data for external accounting, auditing, or custom reporting:

- **Financial CSV**: Line-by-line breakdown of every booking, extra, and partial payment.
- **Tax/VAT Report**: Summarized report of tax collected by rate (Accommodation vs. Extras). Essential for quarterly filings.
- **Occupancy Report**: Detailed night-by-night breakdown of room utilization.
- **Transaction Log**: Complete audit trail of all payment gateway success/failure events.

---

## 3.11 Admin Calendar Pricing & Availability Editor

The Admin Calendar (found under **Hotel Booking > Inventory**) is the most powerful tool for granular control over your property's yield.

### 3.11.1 The Bulk Update Tool

Instead of editing each day individually, use the **Bulk Update** button:

1. **Scope Selection**: Choose to update a "Room Type" (affects all rooms of that type) or an "Individual Room".
2. **Date Range**: Define your target window (e.g., "Dec 20 - Jan 5").
3. **Day Filter**: Only apply changes to specific days (e.g., only Fridays and Saturdays).
4. **Override Values**:
   - **Price**: Set a fixed nightly rate.
   - **Availability**: Block out dates (Set to `Closed`) or open them up.
   - **Min/Max Stay**: Force longer stays during peak dates.

### 3.11.2 Import/Export

The Calendar Editor supports **JSON Synchronization**. You can export your pricing overrides as a master file and import them back to restore specific pricing setups for the next year.

## 3.12 Professional Invoicing System

Generate, print, and automate guest invoices directly from the booking manager.

### 3.12.1 Automatic Invoicing

To send invoices automatically:

1. Go to **Hotel Booking > Settings > Communications**.
2. Enable **Attach Invoice to Confirmation Email**.
3. Guests will receive a beautifully formatted HTML/PDF invoice immediately after their booking is confirmed.

### 3.12.2 Manual Management

From the **Bookings List**, click on any reservation and select **Generate Invoice**.

- **Print Version**: Opens a print-friendly view with a "Print" command triggered.
- **Payment Methods**: If the booking has an outstanding balance, the invoice automatically includes your **Bank Transfer** and **Revolut** details (configured in Section 2.5).

---

The Deposit system allows you to collect a portion of the booking total at checkout, with the remaining balance collected later (either on-site or via a manual payment link).

### 3.10.1 Configuration

To configure deposits, go to **Hotel Booking > Settings > Deposits**.

| Setting                | Description                                                                                 |
| ---------------------- | ------------------------------------------------------------------------------------------- |
| **Enable Deposits**    | Toggle the entire deposit system on or off.                                                 |
| **Deposit Type**       | Choose how the deposit is calculated: **Percentage**, **Fixed**, or **First Night's Rate**. |
| **Deposit Value**      | The numerical value for Percentage or Fixed types.                                          |
| **Non-Refundable**     | If checked, the deposit is explicitly marked as non-refundable in emails.                   |
| **Refund Deadline**    | Number of days before check-in when a refundable deposit becomes non-refundable.            |
| **Allow Guest Choice** | If enabled, guests can choose to pay the deposit OR the full amount at checkout.            |

### 3.10.2 Guest Experience

When deposits are enabled, the booking form displays a clear **Payment Options** section. This interface ensures that guests understand their financial commitment before proceeding to checkout.

**The Interface:**

- **Interactive Radio Cards**: Guests select their preferred payment method using large, high-contrast cards. These provide a tactile and modern experience compared to standard dropdowns or radio buttons.
- **Dynamic Pricing**: The cards automatically update to show the exact amount due today.
  - **Pay Deposit Now**: Displays the portion of the total required to secure the room.
  - **Pay in Full**: Displays the complete reservation total.
- **Policy Transparency**: If a deposit is configured as non-refundable, this information is displayed directly within the card, ensuring the guest is fully aware of the cancellation terms before paying.
- **Responsive Layout**: The payment cards utilize a flexible grid system. They appear side-by-side on desktop but automatically stack on mobile and tablet viewports to maintain maximum legibility and ease of use.

> [!TIP] > **Testing the Guest View**: To see exactly how these cards appear to your guests, go to **Settings > Deposits** and enable **"Allow Guest Choice"**. Then, visit your booking page and select a room to trigger the payment options step.

### 3.10.3 Managing Balances

Administrators can track and update balances directly from the booking details page.

1. Go to **Hotel Booking > Bookings**.
2. Click **View** on a booking with a deposit.
3. The **Payment Summary** box shows:
   - **Payment Type**: (Deposit vs Full)
   - **Deposit Paid**: The amount captured at checkout.
   - **Remaining Balance**: The amount still owed.
   - **Balance Status**: (Uncollected vs Collected).
4. Click **Mark Balance Collected** once you receive the final payment. This adds a private note to the booking for audit trails.

> [!TIP] > **Email Automation:** Use the `{deposit_details}` placeholder in your email templates to automatically include a professional summary of the deposit paid, balance remaining, and refund deadline in guest notifications.

---

### 3.14.3 AI Resilience & Cost Optimization (2026 Edition)

The AI Concierge is equipped with a high-availability **Resilience Layer** designed to maintain service stability during API outages or heavy demand spikes, while ensuring the most cost-effective operation.

#### 1. Tiered Circuit Breaker

If the AI provider (e.g., Google Gemini) experiences high demand or temporary errors, the system triggers a **Stateful Circuit Breaker**:

- **Tier 1 (Transient)**: 15-second "cool-down" to allow capacity to reset.
- **Tier 2 (Active)**: 60-second block during persistent fluctuations.
- **Tier 3 (Outage)**: 15-minute "hard block" protecting your server resources during broad service outages.

Guests will see a professional "System Busy" message rather than a technical error, preserving the user experience.

#### 2. Economic Fallback Cascade

The system automatically cascades through available models from cheapest to most capable. The verified June 2026 priority order is:

| Priority | Model                      | Tier    | Notes                          |
| -------- | -------------------------- | ------- | ------------------------------ |
| 1        | **Gemini 3.5 Flash**       | Stable  | Newest GA, recommended primary |
| 2        | **Gemini 3.1 Flash-Lite**  | Stable  | Lowest cost stable option      |
| 3        | **Gemini 3 Flash Preview** | Preview | Computer Use support           |
| 4        | **Gemini 3.1 Pro Preview** | Preview | Flagship preview capability    |
| 5        | **Gemini 2.5 Flash-Lite**  | Stable  | Ultralight stable fallback     |
| 6        | **Gemini 2.5 Flash**       | Stable  | Proven stable last resort      |

> [!NOTE] > **Deprecated models** including the legacy `gemini-1.5` and `gemini-2.0` families are retired. The `gemini-3.1-flash-lite-preview` model reached EOL on May 25, 2026 and is automatically migrated to `gemini-3.5-flash`. If you see model-not-found errors, please use the **Reset Engine Lock** button to clear the resilience cache.

Models are dynamically deprioritized for 5 minutes if they return errors, ensuring the system transparently finds the best available price/performance ratio in real-time.

#### 3. Cost Transparency & Billing Notice

> [!IMPORTANT] > **Dynamic Fallback Costs**: While the system is optimized to use the most cost-efficient models (e.g., Gemini 3.5 Flash), it is designed to prioritize service continuity. During significant API outages or heavy regional demand, the AI Concierge may automatically cascade to more capable — and more expensive — secondary models.
>
> **Key Considerations:**
>
> - **Primary Selection**: Setting `Gemini 3.5 Flash` as your primary model is the recommended way to minimize daily costs while utilizing the latest GA capabilities.
> - **Cascade Implications**: If the primary model fails, the system will temporarily use higher-tier models (up to Gemini 3.1 Pro Preview) to ensure your guests still receive a response. This may result in higher token usage fees during that period.
> - **Monitoring**: Use the **Service Health** dashboard in AI Concierge → General Settings to monitor active "Demand Spikes" and circuit breaker status. Click **Reset Engine Lock** to manually clear cooldowns after an outage resolves.

#### 3.14.4 AI Security & Identity Verification (OTP)

To protect sensitive guest information and ensure the integrity of your reservations, the AI Concierge implements a **"Zero-Trust" Identity Handshake**.

**Protected Actions:**
The AI WILL NOT perform the following actions without first verifying the guest's identity via a 6-digit One-Time Password (OTP) sent to their registered email:

- Resending booking confirmation emails.
- Retrieving guest stay history.
- Modifying an existing reservation (dates, room types, extras).
- Canceling an existing reservation.

**Security Parameters:**

- **Verification Codes**: A cryptographically secure 6-digit numeric code.
- **Expiration**: Codes are valid for exactly **20 minutes**. If the code expires, a new one must be requested.
- **Lockout Policy**: To prevent brute-force attacks, the AI Concierge will lock the verification process after **10 failed attempts** in a single session. If locked, the guest must restart the chat.

> [!IMPORTANT] > **Transactional Email Requirement**: For OTP to function, your WordPress site must be capable of sending emails. We strongly recommend using a dedicated SMTP provider (SendGrid, Mailgun, etc.) to ensure instant delivery of verification codes.

Administrators do not need to configure this; it is active by default for all Pro-tier installations to ensure compliance with enterprise security

## 3.14 AI Concierge System

The AI Concierge is your property's 24/7 intelligent receptionist. Guests can interact via the floating widget on your website using text or high-quality voice synthesis.

### 3.14.1 Key Capabilities

- **Property Q&A**: Answers questions about amenities, check-in times, and local rules based on your "Hotel Info" settings.
- **Real-time Availability**: Checks the live `Pricing` engine to see if rooms are open for specific dates.
- **Direct Reservations**: The AI can guide a guest through the entire booking process and hand them over to the payment gateway once they've selected a room.

### 3.14.2 Voice & Text Support

Guests can toggle between **Text** and **Voice** mode.

- **Voice Mode**: Uses advanced text-to-speech to read responses aloud.
- **Multilingual**: The AI automatically responds in the language the guest uses, supporting all 15 locales of the plugin.

### 3.14.3 Booking Access (OTP Secure)

For guests with existing bookings:

1. The AI asks for their email and **Booking ID**.
2. A secure **One-Time Password (OTP)** is sent to their email.
3. Once verified, the AI can provide check-in instructions, WiFi codes, or update their notes.

### 3.14.10 AI Concierge Analytics

Monitor your AI's performance in **Hotel Booking > Analytics > AI Concierge**.

- **Engagement Rate**: How many visitors chat with the AI.
- **Handover Rate**: How many chats result in a direct booking intent.
- **Common Questions**: Aggregated data on what your guests are asking most.

The Pro version includes a dedicated **Analytics** tab within the AI Concierge dashboard. This provides:

- **Session Logs**: View anonymous transcripts of guest interactions to identify common questions.
- **Conversion Tracking**: Measure how many AI conversations result in a completed booking.
- **Sentiment Analysis**: Understand guest satisfaction trends over time.
- **Model Usage**: Monitor token consumption across primary and fallback providers.

---

## 3.11 Admin Calendar Pricing & Availability Editor

The Admin Calendar is a Pro-only visual editor that lets you set exact prices, block dates, and apply stay restrictions on any individual day — at either the Room Type level or the Individual Room level. Changes appear on the guest-facing booking form immediately.

### 3.11.1 How to Access

The calendar is available from two places:

| Starting point               | Navigation                                                                |
| ---------------------------- | ------------------------------------------------------------------------- |
| **Room Type calendar**       | Hotel Booking > Room Types → click the **Calendar** icon next to any type |
| **Individual Room calendar** | Hotel Booking > Rooms → click the **Calendar** icon next to any room      |

The page header badge tells you which scope you are editing:

- **Room Type** — overrides apply to every room of that type simultaneously
- **Individual Room** — overrides apply to that single room only (highest priority)

---

### 3.11.2 Pricing Priority Order

Every night's price is computed by passing through a waterfall of five layers, lowest to highest priority. A higher layer always wins over a lower one.

|  Priority   | Layer                                  | Where it is set                                                               |
| :---------: | -------------------------------------- | ----------------------------------------------------------------------------- |
| 1 (lowest)  | **Room Type base price**               | Room Types > Base Price field                                                 |
|      2      | **Room custom price**                  | Rooms > Custom Price field (overrides the type base price for that room only) |
|      3      | **Seasonal / Weekend / Holiday rules** | Hotel Booking > Pricing Rules + Settings > Advanced Pricing                   |
|      4      | **Room Type calendar override**        | Calendar Editor on the Room Type page                                         |
| 5 (highest) | **Individual Room calendar override**  | Calendar Editor on the Individual Room page                                   |

**Example:** A room type has a base price of 150 RON. A seasonal rule raises it to 180 RON for summer. A room-type calendar override sets 2 August to 220 RON. An individual room calendar override sets that same room on 2 August to 195 RON. The guest pays **195 RON** — the individual room override wins.

> [!NOTE]
> If a calendar override sets only `min_stay` (and no price), the price still flows through all layers below it. Each field is independent — null means "inherit from lower layers", not "zero".

### 3.11.3 The Calendar View

The calendar opens in **Month view** by default. Each day cell shows:

- The **Resolved Effective Price** for that night (bold if a manual override is set, standard if inherited).
- **Status Indicator** (Colored border/background).
- **Restriction Badges**: `Min` / `Max` (stay length), `↓X` (No Check-in), `↑X` (No Check-out).

> [!NOTE] > **Timezone Awareness:** The calendar operates based on your WordPress site's timezone. A "day" begins at 00:00 and ends at 23:59. If you have guests arriving from different timezones, ensure your site settings match your property's local time to prevent "same-day turnover" conflicts.

### 3.11.4 Visual Status Legend

The calendar uses high-contrast visual cues to help you audit your inventory at a glance:

| Visual Icon / Style    | Meaning                                                                                 | Priority      |
| :--------------------- | :-------------------------------------------------------------------------------------- | :------------ |
| **Purple Border**      | **Individual Room Override**. A price or stay restriction is set on this specific room. | Highest       |
| **Blue Border**        | **Room Type Override**. A rule is applied to all rooms of this type.                    | High          |
| **Striped Background** | **Fully Blocked**. The room is marked as unavailable for this date.                     | Overrides all |
| **Green Highlight**    | **Available**. The room is open for reservations.                                       | Base          |
| **Grey Highlight**     | **Booked**. A confirmed reservation exists for this date.                               | N/A           |

### 3.11.5 Effective Price Detection

The calendar performs a "dry run" of the pricing engine for every day displayed. The number you see on each date is the **Effective Price** — the final amount the guest will pay after all seasonal rules, weekend multipliers, and overrides are applied. This gives you 100% confidence in your pricing strategy before it goes live.

> [!IMPORTANT] > **Zero-Price Hardening:** To prevent guests from placing free bookings, both the frontend calendar and the backend booking engine will automatically reject bookings where the resolved nightly room rate is zero or negative. A valid price must be configured (either as a base price, custom room price, or via calendar overrides) for the selected dates.

### 3.11.6 Intelligent JSON Import

When bulk-importing pricing data via JSON, the system includes a **Conflict Resolver**:

- **Existing Data Detected**: If you import overrides into a calendar that already has data, the system will highlight the conflicts.
- **Smart Merge**: You can choose to "Keep Existing" (skip duplicates) or "Overdrive All" (overwrite existing overrides with the new file).
- **Validation**: All imported dates are validated for ISO compliance and room association to prevent database corruption.

Use the **prev / next** arrows or **today** button to navigate months.

---

### 3.11.7 Editing a Single Day

Click any day cell to open the **Day Panel** on the right.

| Field                  | What it does                                          | Leave blank to…                |
| ---------------------- | ----------------------------------------------------- | ------------------------------ |
| **Nightly Price**      | Override the price for this one night                 | Inherit from the pricing stack |
| **Availability**       | Open / Blocked / Inherit                              | Inherit (stay available)       |
| **Min Stay**           | Minimum consecutive nights required to book this date | Apply no minimum               |
| **Max Stay**           | Maximum consecutive nights allowed                    | Apply no maximum               |
| **No Check-in (CTA)**  | Guests cannot start a stay on this date               | Allow arrivals                 |
| **No Check-out (CTD)** | Guests cannot end a stay on this date                 | Allow departures               |
| **Admin Note**         | Internal reference (never visible to guests)          | No note                        |

Click **Save Changes** to apply. Click **Clear** to remove the override entirely (the date reverts to the pricing stack).

> [!TIP]
> To block a date completely, set **Availability** to **Blocked**. Guests will not be able to select it as a check-in or check-out date.

---

### 3.11.8 Bulk Editing a Date Range

To set the same values across a range of consecutive dates:

1. **Click and drag** across the calendar cells from the start date to the end date
2. The **Bulk Edit Panel** opens, showing the date range
3. Fill in the fields you want to change (leave others blank to leave them unchanged)
4. Click **Apply to Range**

All dates in the range receive exactly the fields you filled in. Fields left blank are not touched — they keep whatever value each day already had.

---

### 3.11.9 Multi-Select Mode

Multi-select lets you apply changes to any arbitrary set of dates — dates that do not need to be contiguous.

**Activating multi-select:**

1. Click the **Multi-select** button in the top-right corner of the calendar toolbar
2. The button turns highlighted to confirm the mode is active
3. Click individual day cells to add or remove them from your selection
4. To add a range, click-and-drag — all dates in the drag are toggled in/out of the selection

**Selection bar:**

Once at least one date is selected, a bar appears below the calendar showing the count of selected days and two actions:

- **Edit Selected** — opens a bulk panel for all selected dates (same fields as the range panel)
- **Clear** — deselects everything without saving

**Applying the edit:**

1. Click **Edit Selected** in the selection bar
2. Fill in only the fields you want to change
3. Click **Apply to All Selected**
4. The panel closes, a toast confirms the save, and the calendar refreshes

To exit multi-select mode, click the **Multi-select** button again or click **Clear**.

> [!NOTE]
> Multi-select mode and single-day click mode are mutually exclusive. While multi-select is active, clicking a day adds it to the selection rather than opening a day panel.

---

### 3.11.10 Export JSON

The **Export JSON** button (toolbar, top-right) downloads a complete pricing snapshot of this room type or room as a JSON file. The file is human-readable and suitable for offline editing or backup storage.

**What the export contains:**

| Section           | Contents                                                                                                                                                            |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `base_price`      | The current base or custom price for this entity                                                                                                                    |
| `pricing_rules`   | All seasonal rules that affect this scope, including global rules (marked `rule_scope: "global"`) and type/room-specific rules                                      |
| `overrides`       | Every manual day override saved in the calendar editor, deduplicated per date                                                                                       |
| `daily_prices`    | 395 days (13 months) of **resolved effective prices** starting from today — the exact price a guest would pay on each night after the full pricing stack is applied |
| `weekend_pricing` | Current weekend pricing settings (reference only)                                                                                                                   |
| `holiday_pricing` | Current holiday pricing settings and dates (reference only)                                                                                                         |

The filename is automatically formatted: `mhbo-rates-{scope}-{id}-YYYY-MM-DD.json`

> [!TIP]
> The `daily_prices` array is the fastest way to audit your pricing for the coming year. Open the file in any text editor or import it into a spreadsheet (convert JSON → CSV) to check every night at a glance.

---

### 3.11.11 Import JSON

The **Import JSON** button (toolbar, top-right) restores or updates data from a previously exported (or manually edited) file.

**Supported workflow:**

1. Export the JSON file
2. Edit the file in any text editor — change prices, add new override entries, adjust pricing rules, or update `base_price`
3. Click **Import JSON** and select your edited file
4. A **Preview** dialog opens before anything is written

**Preview dialog:**

The preview shows exactly what will change:

- **Valid records** — number of override entries that will be processed
- **New dates** — override entries for dates that don't yet exist in the database
- **Conflicts** — dates that already have an override with different values. For each conflict you choose: **Keep existing** or **Use incoming**
- **Pricing rules to replace** — count of type/room-specific rules that will be replaced (global rules in the file are always skipped)
- **Base price change** — if the file's `base_price` differs from the current stored value, the preview warns you with the old and new amounts

5. Resolve any conflicts using the radio buttons
6. Click **Import** to apply

**What each field does on import:**

| JSON field                                               | Import behaviour                                                                                           |
| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `overrides[]`                                            | Each entry is written (upserted) to the database. Conflict resolution applies per date.                    |
| `pricing_rules[]` where `rule_scope: "type"` or `"room"` | All existing rules for this scope are **deleted and replaced** with the imported rules                     |
| `pricing_rules[]` where `rule_scope: "global"`           | **Ignored** — global rules are shared across all room types and are never modified by a single-room import |
| `base_price`                                             | Updated in the database if the value differs from the current one                                          |
| `daily_prices[]`                                         | **Ignored** — always recomputed live from the pricing stack                                                |
| `weekend_pricing` / `holiday_pricing`                    | **Ignored** — these are global settings not scoped to one room type                                        |

> [!IMPORTANT]
> Importing pricing rules is a **replace** operation for the scope, not a merge. All existing type-specific or room-specific rules are deleted and the imported rules are written from scratch. Global rules (visible in the file for reference) are never touched.

> [!TIP]
> To restore a room type's complete pricing state after an accidental deletion, export from a backup file and import it. The `overrides`, `pricing_rules`, and `base_price` are all restored in a single operation.

---

### 3.11.12 Typical Workflows

**Setting a peak-season nightly rate for the next 3 months:**

1. Open the Room Type calendar
2. Click-drag from the first to last date of the peak window
3. Enter the peak price in the Bulk Edit panel
4. Click Apply

**Blocking a room for maintenance:**

1. Open the Individual Room calendar (not the type)
2. Click-drag the maintenance dates
3. Set **Availability → Blocked**
4. Click Apply
5. Those dates are blocked only for that room; other rooms of the same type remain open

**Enforcing a 3-night minimum over a long weekend:**

1. Open the Room Type calendar
2. Use Multi-select to pick the specific dates (e.g., Fri–Mon of a bank holiday)
3. Click **Edit Selected**
4. Set **Min Stay → 3**, leave all other fields blank
5. Click Apply to All Selected

**Annual pricing backup:**

1. On both the Room Type and each Individual Room calendar, click **Export JSON**
2. Store the files in your backup folder or cloud storage
3. To restore, import each file back to the corresponding calendar

---

## 3.12 Professional Invoicing System

The Modern Hotel Booking Pro version includes a comprehensive invoicing system designed to provide your guests with professional, accurate billing summaries for their stays.

### 3.12.1 How to Access

Invoices are generated individually per booking and can be accessed directly from the booking management interface:

1. Go to **Hotel Booking > Bookings**.
2. Find the reservation you wish to bill and click **Edit**.
3. Scroll to the bottom of the page to find the **Actions Dock**.
4. You will see two primary options:
   - **📥 Download Invoice**: Generates a print-ready HTML invoice that opens in a new tab and triggers your browser's print dialog.
   - **📧 Email Invoice**: Automatically dispatches the invoice as a professional HTML email directly to the guest's registered email address.

### 3.12.2 Financial Accuracy & Logic

The invoicing engine is synchronized with the plugin's real-time financial stack, ensuring 100% parity between what you see in the Admin dashboard and what the guest receives.

**Amount Outstanding Calculation:**
The invoice uses a "smart balance" model to show guests exactly what they still owe:
`[Total Price] - [Deposits Received] - [Partial Payments] = [Amount Outstanding]`

**Key Features:**

- **Deposit Awareness**: If a booking requires a deposit that has not yet been received, the invoice adds an explicit **Deposit Required** row in the summary.
- **Dynamic VAT/Tax Breakdown**: If taxes are enabled, the invoice perfectly splits VAT amounts between accommodation and extras, matching the registered rates in your settings.
- **Reference Numbers**: Includes the unique Booking ID (Ref #) as the primary identifying number.

### 3.12.3 Professional Branding & Info

To ensure your invoices look professional and meet legal requirements, make sure to configure your property details in **Hotel Booking > Settings > Business**.

- **Property Logo**: Uploaded via the Company tab, this will appear in the top-left of the invoice.
- **Company Address**: Your full legal address.
- **Registration Numbers**: Your Trade or VAT registration numbers (appears in the header).
- **Tax Registration**: Ensure your tax ID is also mirrored in the **Tax** settings tab for footer display.

### 3.12.4 Smart Conditional Rendering

The invoice is intelligent enough to adapt its layout based on the booking's financial state:

- **Fully Paid Bookings**: For bookings marked as `status_completed` or where the remaining balance is zero, the invoice automatically **hides** the Revolut and Bank Transfer instruction blocks to avoid confusion.
- **Pending Bookings**: For bookings awaiting payment, the invoice includes professional payment cards (Revolut/Banking) in the footer, using the data configured in your **Business** settings.

> [!TIP] > **Multilingual Invoices:** When emailing an invoice, the system automatically detects the `Booking Language` set during the reservation process and renders the invoice labels accordingly, ensuring your global guests receive documentation in their preferred language.

---

## 3.13 Performance & Cache Management

Modern Hotel Booking Pro includes a multi-layered caching engine designed for high-traffic environments and enterprise-scale properties.

### 3.13.1 Scaling Architecture

The plugin detects your server environment and automatically chooses the most efficient data storage method:

1. **External Object Cache (Redis/Memcached)**: If your hosting provides an object cache (detected via `wp_using_ext_object_cache`), the plugin uses it for sub-millisecond data retrieval.
2. **Transient Fallback (Database)**: If no object cache is available, the plugin uses the WordPress Transients API to store expensive calculations in the database.

### 3.13.2 Optimization Tabs

Go to **Hotel Booking > Settings > Performance** to manage your site's speed:

- **Enable Smart Caching**: Toggles the caching of room availability, pricing rules, and room type structures.
- **Micro-Cache (Calendar)**: The visual calendar uses a special 60-second micro-cache that is **automatically invalidated** the moment a booking is created or modified.
- **Object Cache Status**: A real-time indicator showing whether your site is currently using a High-Performance external cache.

### 3.13.3 Cache Maintenance

- **Clear All Cache**: Use this button after making major architectural changes (like changing your tax mode or adding dozens of new rooms) to ensure all frontend fragments are refreshed.
- **Automated Invalidation**: You rarely need to clear cache manually. The plugin includes "Dirty State" detection that invalidates relevant cache keys whenever you click **Save** or **Update** on any room, rule, or setting.

### 3.13.4 Advanced Availability Resilience (Race Protection)

For high-concurrency properties, Version 2.4.4 introduces **Atomic Availability Locking**. When multiple guests attempt to book the last available room at the exact same millisecond, the system utilizes MySQL native locks (`GET_LOCK`) to ensure:

- Only one transaction succeeds.
- Zero double-bookings even during flash sales or demand spikes.
- Real-time inventory accuracy that bypasses standard page caching.

### 3.13.5 High-Performance AJAX Flow

The booking form now utilizes a **Single-Page Submission** model.

- **No Page Refreshes**: Fragments of the page are updated via AJAX, maintaining the guest's scroll position and chat session state.
- **Resilient Nonces**: Security tokens are automatically refreshed in the background, preventing "Session Expired" errors on long-dwell bookings.

---

## 3.14 AI Concierge System

**(Flagship Feature — Version 2.4.4)**

The Modern Hotel Booking AI Concierge is a next-generation guest assistant that acts as a 24/7 virtual receptionist. It uses advanced RAG (Retrieval-Augmented Generation) to understand your hotel's inventory, policies, and special details directly from your site content.

### 3.14.1 Dashboard Navigation

The AI settings are organized into specialized tabs for easier management:

- **General**: Master switch, provider connections (Primary & Fallback), and Knowledge Base status.
- **Assistant Persona**: Custom instructions, AI name, welcome message, and local concierge guide.
- **Appearance**: Branding colors, widget position, and Pro theme selection.
- **AI Discovery (GEO)**: Management of `llms.txt` and `llms-full.txt` manifests.
- **Analytics (Pro)**: Detailed performance metrics, booking conversion tracking, and session logs.

### 3.14.2 Core Configuration

| Feature              | Description                                                                                                         |
| :------------------- | :------------------------------------------------------------------------------------------------------------------ |
| **Primary AI**       | Recommended: **Gemini 3.5 Flash** (Economic) or **GPT-5.5 / Claude 4.6** for complex, agentic reasoning.            |
| **Model Selection**  | Select from pre-optimized May 2026 flagging presets or use **Manual Override**.                                     |
| **Reasoning Effort** | (GPT-5.4, GPT-5.5 & Claude 4.6 only) Adjust the model's 'Thinking' intensity to balance accuracy vs. token cost.    |
| **Knowledge Base**   | Automatically indexed from your Room Types, Rooms, and Pages. Use the **Status Badge** to check for data freshness. |
| **Persona Name**     | Customize the AI's name (e.g., "Marina, the Virtual Host").                                                         |
| **Fall-Fast Logic**  | Automatically detects provider outages and switches to the Fallback without guest interruption.                     |

### 3.14.3 Tiered Reliability & WP 7.0 Native AI Cascade

Version 2.4.4 introduces a robust, multi-path "Cascading Provider" architecture to ensure 100% availability by integrating directly with native WordPress 7.0+ AI features:

- **Unified Prompt Cascade**:
  - **Path 1 (WP 7.0 Core)**: Runs through the native WordPress core `wp_ai_client_prompt()` builder API. This is the preferred pathway for simple text generation when no custom tools or functions are needed.
  - **Path 2 (Standalone SDK)**: Falls back to the bundled `\WordPress\AI\Client` SDK (with Jetpack autoloader checks) if tool-calling/abilities are required or when running on WordPress versions older than 7.0.
  - **Path 3 (Direct API Gateway)**: Executes direct HTTP POST requests to Gemini v1beta, OpenAI, or Anthropic endpoints if core APIs or SDK pathways are unavailable or lack configuration.
- **WP_Error & Missing Connector Fallback**: If the native `wp_ai_client_prompt()` builder returns a `WP_Error` (e.g., if the site-wide Connector under _Settings → Connectors_ is misconfigured, not set up, or fails to connect), the plugin automatically catches the error, logs it, and falls back to Path 2/3 using the local API keys configured in the plugin settings.
- **Unconditional Fallback Key Input**: To facilitate this, the **Primary AI Connection** input fields (Provider, API Key, Model) remain visible and editable in the settings UI even when the native WP 7.0+ AI Client is detected, serving as the explicit fallback credentials.
- **Fail-Fast Auto-Fallback**: If the primary AI endpoint (e.g., Gemini) hits a quota limit or service timeout, the system instantly switches to your secondary configured fallback (e.g., OpenAI) without interrupting the guest.
- **Service Locking**: Temporary outage locks are placed on failing endpoints to prevent redundant connection timeouts, immediately routing subsequent queries through active fallback systems.

### 3.14.4 Premium Guest Experience (v3.0)

The 2026 flagship update introduces a state-of-the-art interaction layer:

- **Real-Time Streaming (SSE)**: The Concierge now utilizes Server-Sent Events (SSE) to deliver responses in real-time. Instead of waiting for a full paragraph, guests see words appear token-by-token, mimicking a live human conversation.
- **Glassmorphic "Thought Bar"**: While the AI "thinks" or executes a tool (like checking availability), a subtle, translucent thought bar appears to keep the guest informed of the progress.
- **Premium Typography**: The widget automatically enqueues the modern **'Outfit'** Google Font for a high-end, rounded aesthetic.

### 3.14.5 Intelligent Guest Memory

Your AI Concierge now features a "long-term memory" for guest interactions:

- **Persistent Context**: The system automatically remembers the guest's **Name, Email, and Phone number** using secure WordPress Transients.
- **Cross-Session Recall**: If a guest provides their details and then refreshes the page or returns later (within 2 hours), the AI will still remember them, allowing for a seamless "Welcome back, Les!" experience.
- **Persistent DB Sessions (Pro)**: Pro users benefit from a permanent database table for chat history, ensuring that a guest's conversation history is preserved even across different devices or browsers after they log in or verify their identity.
- **Pre-filled Bookings**: Because the AI remembers these details, any booking link it generates will be automatically pre-filled with the guest's contact information, significantly increasing conversion rates.

### 3.14.6 Zero-Trust Identity Handshake (OTP)

For sensitive operations—such as retrieving existing booking details or modifying a reservation—the AI Concierge enforces a **6-digit OTP verification** protocol:

1. **Identity Request**: The guest asks for their booking info.
2. **OTP Dispatch**: The system sends a unique 6-digit code to the guest's registered email address (valid for 20 minutes).
3. **Verification**: The guest enters the code in the chat.
4. **Authorized Access**: Only after a successful handshake does the AI disclose PII or perform modifications. This prevents social engineering and unauthorized data leaks.

### 3.14.7 Automated Resilience & Stability

To ensure 100% reliability during peak periods, the AI Concierge includes an automated protection suite:

- **Demand Protection (Retry Logic)**: If the AI provider (Gemini/OpenAI) is experiencing "High Demand" (HTTP 503) or network connectivity issues (e.g., cURL timeouts), the plugin automatically performs an **exponential backoff** retry. It waits a few milliseconds and tries again internally before ever showing an error to the guest.
- **Fail-Fast Cascade Budget**: The system enforces a strict 12-second timeout per model and a 40-second total cascade budget. If a primary model times out, it instantly classifies it as a transient error and routes the query to the next fallback model.
- **Transient Error Handling**: Brief service hiccups are handled gracefully. Guests will see a polite _"System busy, cooling down..."_ message instead of raw technical errors, keeping the guest engaged while the system heals itself.

### 3.14.8 Intelligent Room Splitting (v2.3.7)

For guests requesting multiple rooms or complex group stays, the AI Concierge now utilizes a **Sequential Selection Model**:

- **Pre-flight Availability**: The AI first checks if the total number of requested rooms is available for the period.
- **Guided Selection**: Instead of overwhelming the guest, the AI guides them through selecting each room one-by-one (e.g., "Great! Let's start with Room 1 of 3. Which type would you prefer?").
- **State Persistence**: The AI tracks the progress of the multi-room booking and provides a final summary link that encapsulates the entire group request.

### 3.14.9 Proactive Engagement (Einstein NBA)

The "Einstein" proactive service engine allows the bot to engage guests based on their behavior:

- **Context-Aware Greetings**: If a guest dwells on a page for a configurable time (default 45s), the bot auto-opens with a greeting tailored to that page.
- **Booking Intent Scoring**: The system analyzes guest messages for intent. When intent is high, it automatically surfaces a **"Book Now" CTA** button or escalation bar (WhatsApp/Email/Phone).
- **Auto-Locale Detection**: The chat widget automatically detects the guest's language via Polylang, WPML, or qTranslate-X.

### 3.14.10 AI Discovery Manifests (llms.txt)

To ensure your hotel is accurately represented in AI-powered search engines (Perplexity, ChatGPT Search, Gemini), Modern Hotel Booking v2.4.0 generates specialized manifests:

- **llms.txt**: A summary of your hotel's core details, pricing, and booking links.
- **llms-full.txt**: A secondary, detailed manifest containing your entire Knowledge Base.
- **Auto-Sync (Pro)**: When enabled, the manifests are automatically updated whenever you change your hotel business info or core settings.

### 3.14.11 Advanced Model Context Protocol (MCP)

For professional hospitality environments, the plugin exposes an **MCP Server** endpoint. This allows external AI agents (used by travel agencies or corporate bookers) to securely query your real-time inventory and room details using the 2026 industry-standard protocol.

### 3.14.12 WP 7.0 Native Abilities API Integration

Modern Hotel Booking complies with the WordPress 7.0 native Abilities framework, allowing standard client-side and server-side AI agents to discover and interact with the hotel's custom tools.

- **Categorized Abilities**: To ensure logical classification, all 13 plugin abilities are registered within specific categories on the `wp_abilities_api_categories_init` action hook:
  - `hotel-information`: Governs static queries like fetching hotel info, room details, local tips, policies, and business contact cards.
  - `booking-management`: Handles stateful actions including checking availability, generating booking links, and creating/modifying/cancelling bookings.
  - `guest-services`: Covers guest history checks and applying promotional discount coupons.
- **REST Discovery**: Each ability is registered with the `show_in_rest => true` metadata flag and translatable `label` descriptors. This exposes the abilities to the native WordPress REST endpoint (`/wp-json/wp-abilities/v1/`), allowing browser-based AI scripts using `@wordpress/abilities` to auto-discover available tools.
- **Access and Type Safety**: Guests access guest-facing abilities with guest-level permissions, while administrative mutations (like retrieving guest history) enforce zero-trust OTP handshakes. All inputs and outputs are verified against strict JSON Schemas.

### 3.14.13 User Privacy & Security

- **Data Locality**: All Knowledge Base storage is local to your WordPress database.
- **Sanitized Prompts**: Guest PII is never sent to AI providers unless specifically required for tool execution (e.g., creating a booking draft).
- **Manage Options**: All administrative AI actions (KB refresh, synchronization) require the `manage_options` capability.

---

# 3.15 Coupon System

> **PRO feature.** Requires an active PRO license. The coupon system is disabled automatically on free builds.

The Coupon System lets you offer guests **fixed-amount** or **percentage** discounts at checkout. Coupons can be restricted by date, minimum spend, room type, and usage limit. The AI Concierge can apply coupons on a guest's behalf when both global and per-coupon AI access are enabled.

---

## 3.15.1 Enabling the Coupon System

1. Go to **Hotel Booking → Settings → Coupons**.
2. Toggle **Enable Coupon System** to **On**.
3. (Optional) Toggle **AI Concierge Can Apply Coupons** — controls whether the AI is allowed to apply coupons at all. You can also control this per coupon.
4. Click **Save Settings**.

When disabled, the coupon field is hidden from the booking form and the AI cannot apply coupons regardless of individual coupon settings.

---

## 3.15.2 Creating & Managing Coupons

Navigate to **Hotel Booking → Settings → Coupons** (or the **Coupons** submenu under Hotel Booking PRO). The coupon list table shows all coupons with their status, type, value, uses, and expiry.

### Creating a Coupon

1. Click **Add Coupon**.
2. Fill in the fields in the modal (see [Coupon Fields Reference](#3153-coupon-fields-reference)).
3. Click **Save Coupon**.

### Editing a Coupon

Click the **Edit** (pencil) icon next to any coupon. The same modal opens pre-filled. Click **Save Coupon** to update.

### Deleting a Coupon

Click the **Delete** (trash) icon. A confirmation dialog appears. Deletion is permanent.

### Generating a Code

In the modal, click **Generate** next to the Code field to auto-generate a random 8-character uppercase alphanumeric code (e.g. `SUMMER8K`).

---

## 3.15.3 Coupon Fields Reference

| Field                   | Required | Description                                                                                                                                 |
| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **Code**                | Yes      | The code guests enter at checkout. Case-insensitive. Max 50 characters.                                                                     |
| **Discount Type**       | Yes      | `fixed` — deducts a flat currency amount. `percent` — deducts a percentage of the booking total.                                            |
| **Discount Value**      | Yes      | The amount (e.g. `20` for $20 off, or `15` for 15% off). Must be > 0.                                                                       |
| **Max Percentage Cap**  | No       | (Percentage type only.) The maximum currency amount the discount can reach. Leave blank for uncapped.                                       |
| **Min Booking Total**   | No       | Guests must reach this spend before the coupon activates. Leave blank (or `0`) for no minimum.                                              |
| **Expiry Date**         | No       | YYYY-MM-DD. Coupon stops working after midnight on this date. Leave blank for no expiry.                                                    |
| **Max Uses**            | No       | Total number of times the coupon can be redeemed across all guests. `0` = unlimited.                                                        |
| **Allowed Room Types**  | No       | Restrict to specific room types. Leave blank to allow all rooms. Accepts a comma-separated list (selected from checkboxes).                 |
| **Active**              | Yes      | Toggle the coupon on or off without deleting it. Inactive coupons are rejected at checkout.                                                 |
| **AI Accessible**       | No       | Whether the AI Concierge is allowed to apply this specific coupon. Defaults to **Yes**. Requires the global AI coupon toggle to also be on. |
| **Description / Notes** | No       | Internal admin note. Not shown to guests.                                                                                                   |

---

## 3.15.4 Guest Checkout Experience

### Entering a Code

1. Guest selects room(s) and dates on the booking form.
2. A **Coupon Code** field appears below the price summary (above the payment section).
3. Guest types their code and clicks **Apply**.
4. If valid, a green confirmation message shows: _"Coupon applied: SUMMER20 — $20.00 off"_.
5. The price summary and tax breakdown update in real time to reflect the discount.
6. A "Remove" link lets the guest undo the coupon before paying.

### What Guests See in Confirmation

- **Booking Confirmation Email**: A "Coupon Savings" line appears in the price breakdown showing the code and discount amount.
- **Tax Breakdown Modal**: A green "Coupon Savings" line is shown before the grand total.
- **Admin Booking View**: The booking record shows `coupon_code` and `coupon_discount` in the booking details.
- **Admin Edit Stability**: Saving a booking in the admin dashboard (e.g., to update notes or guest names) **automatically preserves and re-applies** the stored coupon logic. This ensures that the discounted service fee and totals are never accidentally overwritten with pre-discount values.

### Security

The coupon code is carried through the booking form as a hidden field. The **server always re-validates and recalculates the discount** — the client-submitted discount amount is never trusted. A guest cannot manipulate the discount by editing the form.

---

## 3.15.5 AI Concierge & Coupons

When both the **global AI coupon toggle** (Settings → Coupons) and the **per-coupon AI Accessible** flag are enabled, the AI Concierge can apply coupons on behalf of guests.

### How It Works

1. A guest tells the AI: _"Do you have any discount codes?"_ or _"Apply my promo code SUMMER20."_
2. The AI calls the `apply_promo_code` tool internally.
3. The tool validates the coupon using the same server-side rules (expiry, min spend, room type, usage limit).
4. If valid, the coupon is applied to the guest's booking session.
5. If invalid, the AI explains why (e.g. "That code has expired" or "This code requires a minimum booking of $150").

### Access Control

| Setting                                         | Effect                                                                   |
| ----------------------------------------------- | ------------------------------------------------------------------------ |
| Global **Enable Coupon System** = Off           | AI cannot apply any coupons                                              |
| Global **AI Concierge Can Apply Coupons** = Off | AI cannot apply any coupons, even if individual coupons have AI = Yes    |
| Per-coupon **AI Accessible** = No               | AI cannot apply that specific coupon, even if the global AI toggle is on |

This two-level gate means you can enable the AI for most promotional codes while keeping certain coupons (e.g. staff or wholesale codes) exclusive to direct entry.

---

## 3.15.6 Taxes, Deposits & Payment Gateways

### How Discounts Interact with Tax

The coupon discount is applied to the **gross booking total first**, before tax is calculated. Tax is then computed on the **discounted total** only — guests never pay tax on the portion that was discounted.

**Service Fee Recalculation:**
As of Version 2.4.4, the plugin includes **Recalculation Integrity**. When a coupon is applied, the **Service Fee** (e.g., 4% of subtotal) is automatically re-computed on the **discounted amount**.
_Example: If a 10% coupon is applied to a $400 stay, the 4% service fee will be calculated on $360 ($14.40) instead of the original $400 ($16.00)._

Internally, when a coupon is applied, all taxable line items (room rate, extras, service fee) are scaled proportionally by the discount ratio, and `Tax::calculate_booking_tax()` is called again on the adjusted amounts. This ensures the tax breakdown shown to the guest exactly matches the tax charged.

### How Discounts Interact with Deposits

When the [Deposit & Partial Payments](#310-deposit--partial-payments) feature is active:

1. Coupon discount is subtracted from the full booking total.
2. The deposit percentage is then calculated on the **discounted total**.
3. The guest pays the reduced deposit; the remainder is the reduced balance due.

### Payment Gateways

Coupons work with all three payment gateways:

| Gateway            | Behaviour                                                                                                                                                                                                                        |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Stripe**         | Server recalculates discounted total before creating the PaymentIntent. The coupon code is stored in Stripe metadata for reference. Webhook fallback path also re-validates and applies the coupon before recording the booking. |
| **PayPal**         | Server recalculates discounted total before creating the PayPal order. The coupon code is stored in the PayPal order's `custom_id` field.                                                                                        |
| **Pay on Arrival** | Discount is applied and recorded at the time of booking submission. No payment is processed online, but the discounted total is stored and shown in all confirmations.                                                           |

---

## 3.15.7 Troubleshooting Coupons

| Symptom                                   | Likely Cause                                               | Fix                                                                                                                                            |
| ----------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Coupon field not showing on booking form  | Coupon system is disabled                                  | Go to Settings → Coupons, enable the system                                                                                                    |
| "Invalid or expired coupon code"          | Code is wrong, expired, or inactive                        | Check code spelling; verify Expiry Date and Active status in admin                                                                             |
| "Minimum booking amount not met"          | Guest's total is below the Min Booking Total               | Guest must book more, or edit the coupon's minimum                                                                                             |
| "Coupon not valid for selected room type" | Room type restriction is set                               | Edit the coupon and check Allowed Room Types                                                                                                   |
| "Coupon usage limit reached"              | Max Uses has been hit                                      | Increase Max Uses or create a new coupon                                                                                                       |
| AI says it cannot apply coupons           | Global AI toggle is off, or per-coupon AI Accessible is No | Check both settings                                                                                                                            |
| Discount not showing in email             | `coupon_discount` is `0.00` or missing                     | Verify the coupon was applied before payment; check booking record in admin                                                                    |
| Tax not recalculated after discount       | Booking was made before coupon system was active           | Existing bookings are not retroactively adjusted                                                                                               |
| Coupon applied twice                      | Guest submitted form twice before confirmation loaded      | The `uses_count` increment uses an atomic SQL check — a second use by the same guest after the first booking is recorded will not double-count |

---

# Part 4: Multilingual Support

## 4.1 Supported Translation Plugins

| Plugin       | Detection | Support Level |
| ------------ | --------- | ------------- |
| qTranslate-X | Automatic | Full          |
| WPML         | Automatic | Full          |
| Polylang     | Automatic | Full          |
| Bogo         | Automatic | Full          |

### 4.1.1 v2.3.1 Localization Parity

As of Version 2.4.4, the plugin has achieved 100% semantic parity across **15 supported locales**. This means every single professional label, from invoice headers to advanced pricing error messages, is included in the translation inventory.

**Supported Standard Locales (v2.3.5):**

| Locale    | Code    | Coverage |
| :-------- | :------ | :------- |
| English   | `en_US` | 100%     |
| Romanian  | `ro_RO` | 100%     |
| French    | `fr_FR` | 100%     |
| Italian   | `it_IT` | 100%     |
| Spanish   | `es_ES` | 100%     |
| German    | `de_DE` | 100%     |
| Russian   | `ru_RU` | 100%     |
| Bulgarian | `bg_BG` | 100%     |
| Hungarian | `hu_HU` | 100%     |
| Turkish   | `tr_TR` | 100%     |
| Polish    | `pl_PL` | 100%     |
| Greek     | `el_GR` | 100%     |
| Ukrainian | `uk_UA` | 100%     |
| Dutch     | `nl_NL` | 100%     |
| Danish    | `da_DK` | 100%     |
| Swedish   | `sv_SE` | 100%     |

### 4.1.2 SEO & Multilingual Consistency

The booking system generates **Semantic HTML5** that search engines can easily parse. When using qTranslate-X or WPML, the plugin automatically:

- Updates the `lang` attributes for form elements.
- Generates localized ARIA labels for accessibility.
- Ensures metadata (Title/Meta descriptions) associated with the Booking Page reflects the current active language.

## 4.2 Setting Up Multilingual Content

**Room Types & Rooms:**

```
[:en]Deluxe Suite[:ro]Suită Deluxe[:de]Deluxe Suite[:]
```

**Frontend Labels:**

```
[:en]Check In[:ro]Check-in[:de]Anreise[:]
```

## 4.3 Multilingual Email Templates

Email templates support multilingual content. Emails are sent in the language used during booking.

---

# Part 5: Developer Reference

## 5.1 REST API

**Authentication:** Generate API keys at **Settings > API**. Sensitive endpoints (like creating bookings) require this key.

**Security:**

- **API Key Header:** `X-MHBO-API-KEY` (Required for POST requests).
- **Nonce Protection:** Public read-only endpoints require a `X-WP-Nonce` header for CSRF protection.
- **Rate Limiting:** Public API access is limited to 60 requests per minute per IP to prevent brute-force attacks.

**Endpoints:**

| Method | Endpoint                                | Description                         |
| ------ | --------------------------------------- | ----------------------------------- |
| GET    | `/wp-json/mhbo/v1/rooms`                | List all room types                 |
| GET    | `/wp-json/mhbo/v1/availability`         | Check room availability             |
| POST   | `/wp-json/mhbo/v1/bookings`             | Create a booking (API key required) |
| GET    | `/wp-json/mhbo/v1/bookings/{id}`        | Get booking details (Admin only)    |
| GET    | `/wp-json/mhbo/v1/bookings/{reference}` | Get booking via 64-char token       |

**Example Request:**

```bash
curl -X POST https://yoursite.com/wp-json/mhbo/v1/bookings \
  -H "Content-Type: application/json" \
  -H "X-MHBO-API-KEY: your_api_key" \
  -d '{
    "room_id": 123,
    "check_in": "2026-06-15",
    "check_out": "2026-06-18",
    "customer_name": "John Doe",
    "customer_email": "john@example.com"
  }'
```

The Pro version includes a secure webhook system to notify your external applications of booking events in real-time.

### 5.2.1 Configuration

1. Go to **Hotel Booking > Settings > API > Webhooks**.
2. **Enable Webhooks**: Toggle the master switch to activate the listener.
3. **Webhook URL**: Enter the endpoint of your external application (e.g., `https://api.myapp.com/webhooks/mhbo`).
4. **Webhook Secret**: This is the shared secret used for HMAC-SHA256 signature verification.
5. **Enable Debug Logging**: If enabled, the plugin will log every delivery attempt, including request payloads and server responses.

### 5.2.2 Delivery Logs & Troubleshooting

Manage your webhook health from the **Delivery History** section in settings:

- **Status Indicators**:
  - <span style="color:green">● 200</span>: Success. The external server accepted the payload.
  - <span style="color:orange">● 4xx</span>: Client Error. Check your endpoint URL or permissions.
  - <span style="color:red">● 5xx</span>: Server Error. Your external server encountered an issue.
- **Payload Viewer**: Click the chevron to view the **Pretty Printed JSON** response from your server.
- **Clear History**: Use the "Clear History" button to reset logs and free up database space.
- **Test Webhook**: Trigger a mock `booking_created` event to verify your connection.

### 5.2.3 HMAC Security & Verification

Every payload is cryptographically signed using your **Webhook Secret** as the shared secret.

- **Signature Header**: `X-MHBO-Signature`
- **Event Header**: `X-MHBO-Event` (e.g., `booking_created`, `booking_status_changed`, `payment_completed`).

**Signature Verification (PHP):**

```php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_MHBO_SIGNATURE'] ?? '';
$webhook_secret = 'your_secret_from_settings'; // Do not share!

$expected = hash_hmac('sha256', $payload, $webhook_secret);

if (hash_equals($expected, $signature)) {
    // Webhook is authentic and untampered
}
```

> [!IMPORTANT] > **SSRF Protection:** All webhook URLs are strictly validated server-side to prevent Server-Side Request Forgery, ensuring your server only communicates with legitimate external endpoints.

## 5.3 Action Hooks

```php
// After booking is created
do_action('mhbo_booking_created', $booking_id);

// After booking status change
do_action('mhbo_booking_status_changed', $booking_id, $old_status, $new_status);

// After payment completed
do_action('mhbo_payment_completed', $booking_id, $payment_data);
```

## 5.4 Filter Hooks

```php
// Modify calculate stay base price
add_filter('mhbo_calculate_stay_price', function($base_price, $room_id, $date) {
    // Example: Increase base price by $20 during special dates
    return $base_price + 20.00;
}, 10, 3);
```

---

# Part 6: Troubleshooting

## 6.1 Common Issues

### Booking Form Not Appearing

**Solutions:**

1. Clear cache (plugins, CDN, browser)
2. Check for JavaScript errors in browser console
3. Verify theme compatibility (try default theme)
4. Check PHP error logs

### Availability Not Updating

**Solutions:**

1. Check booking statuses (cancelled bookings may still block)
2. Verify iCal sync isn't creating conflicts
3. Check database for orphaned bookings

## 6.2 Payment Problems

### Stripe Payment Fails

**Solutions:**

1. Verify API keys are correct (test vs live mode)
2. Check Stripe Dashboard for declined reason
3. Ensure SSL certificate is valid
4. Verify currency is supported by Stripe

5. Check server firewall for blocked callbacks

### 6.2.2 Stripe/PayPal Modal Initialization

**Problem:** Payment buttons (Stripe/PayPal) do not appear or fail to load inside the booking modal.
**Reason:** In versions prior to 2.3.7, payment scripts sometimes failed to bind to the dynamic DOM elements injected by the modal.
**Solution:** v2.3.7 introduces a "Context-Aware Initialization" engine. If you still encounter issues, ensure your theme doesn't have "Lazy Load" enabled for JavaScript, as this can delay the payment gateway handshake.

### 6.2.3 AI Concierge Issues

**Status 429: "Monthly spending cap reached"**

- **Cause**: Google AI Studio billing is restricted to $0.00 by default.
- **Solution**: Visit the **Google Cloud Console Billing** page and set a non-zero spending cap (e.g., $5.00) to activate the Paid Tier and remove rate limits.

**Status 502: "Bad Gateway" (Local Environments)**

- **Cause**: Long "thinking" cycles can cause local development proxies (like Local by Flywheel) to drop the connection.
- **Solution**: The plugin automatically defaults to a **40-second timeout** for optimized resilience. Ensure your local `max_execution_time` is set to at least 60s.

### 6.2.4 License Activation Restricted

**Problem:** "Maximum activation attempts exceeded."
**Reason:** Security hardening prevents brute-force attempts on license keys.
**Solution:** Wait 1 minute and ensure your license key is typed correctly. If the issue persists, contact support.

### 6.2.4 Webhook SSRF Violation

**Problem:** Webhook URL is not accepted or request is not sent.
**Reason:** The system blocks private IP addresses (localhost, 192.168.x.x, etc.) for webhook URLs as a security measure.
**Solution:** Ensure your Webhook URL points to a public, secure (HTTPS) endpoint.

## 6.3 Tax Calculation Issues

**Solutions:**

1. Verify tax mode (VAT vs Sales Tax)
2. Check if rates are set correctly
3. Verify rounding mode settings

## 6.4 Email Delivery Problems

**Solutions:**

1. Check "Notification Email" setting is correct
2. Test with WordPress email test plugin
3. Verify email isn't in spam folder
4. Consider SMTP plugin for better deliverability

## 6.5 Performance Optimization

### Built-in Object Caching

The plugin includes an optional caching layer for improved performance.

**What Gets Cached:**

- Room availability queries (5-minute expiry)
- Pricing rules (1-hour expiry)
- Room type data (1-hour expiry)

**Configuration:**

1. Go to **Hotel Booking > Settings**
2. Find the **Performance** section
3. Enable/disable caching

> [!TIP] > **Smart Calendar Caching:** The visual calendar display uses a micro-cache algorithm (1-minute expiration) with immediate forced invalidation (`Cache::invalidate_calendar_cache()`) upon any successful booking change. This strategy provides sub-second page loads without sacrificing real-time booking accuracy.

**For Developers:**

```php
// Clear all plugin caches
\MHBO\Core\Cache::flush();

// Clear cache for specific room
\MHBO\Core\Cache::invalidate_room($room_id);
```

### PHP JIT (Optional)

PHP JIT can improve performance for complex calculations. Enable in php.ini:

```ini
opcache.enable=1
opcache.jit=tracing
opcache.jit_buffer_size=64M
```

## 6.6 iCal Sync Issues

### Export URL Returns Empty Calendar

**Cause:** No bookings exist yet for that room — the ICS feed correctly outputs an empty `VCALENDAR` when there is nothing to report.

**Fix:** Create a test booking for the room, then reload the export URL. You should see a `VEVENT` block.

### Export URL Returns 404 or "Not Found"

**Cause:** WordPress Pretty Permalinks have not been flushed since the plugin was activated.

**Fix:** Go to **Settings > Permalinks** and click **Save Changes** (no changes needed — just saving flushes the rewrite rules).

### Status Shows "Pending" and Never Changes

**Cause:** Either no sync has run yet (wait for the next cron cycle) or WordPress Cron is not firing due to low traffic.

**Fix:**

1. Click the **Sync Now (↻)** button in the connection's Actions column to trigger an immediate sync
2. For persistent cron issues, add a system cron task: `curl https://yoursite.com/wp-cron.php?doing_wp_cron` every 5–15 minutes

### Status Shows "Failed" With an Error Message

**Common error messages and their causes:**

| Error                                    | Cause                                                                                          |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `cURL error 6: Could not resolve host`   | The OTA URL is unreachable from your server                                                    |
| `cURL error 60: SSL certificate problem` | The OTA's SSL certificate is invalid or expired                                                |
| `SSRF: unsafe URL`                       | The URL resolves to a private/internal IP — only public OTA URLs are allowed                   |
| `Empty response body`                    | The OTA returned an empty feed — the listing may have been deleted or the URL changed          |
| `No VEVENT blocks found`                 | The OTA returned a valid calendar but with no events — the listing may have no future bookings |

**Fix:** Verify the URL still opens in your browser and returns valid iCal data. If the OTA changed your export URL, delete the connection and re-add it with the new URL.

### Booking.com Says "This iCal URL Isn't Valid"

**Cause:** Since March 2025, Booking.com validates iCal import URLs by domain. Any URL from a custom/personal website — including a valid MHBO `.ics` feed — is rejected. This is not a bug in the plugin; the feed is RFC 5545 compliant.

**What to check first:**

1. Validate your feed at [icalendar.org/validator.html](https://icalendar.org/validator.html) to confirm it passes standard validation
2. Open the Export URL in a private/incognito browser window to confirm it is publicly accessible (not behind a login or maintenance mode)
3. Ensure there are no leading/trailing spaces in the pasted URL

If validation passes and the URL is publicly accessible, the rejection is confirmed to be Booking.com's domain-whitelist policy — not a technical error. See [Section 3.6.9](#369-bookingcom-limitation--workarounds) for workarounds (manual blocking, Airbnb relay, Pulse app, channel manager).

### Airbnb Still Shows Dates as Available After Sync

**Cause:** Airbnb does not poll your export URL in real time. Airbnb re-polls imported calendars approximately every **2–3 hours**.

**Fix:**

1. Verify your MHBO Export URL is saved correctly in Airbnb's calendar settings (Calendar → Availability Settings → Connected Calendars)
2. Wait up to **3 hours** after a new booking before expecting Airbnb to reflect the blocked dates
3. You can request a manual refresh from Airbnb's connected calendars settings

### Booking.com Bookings Not Appearing in MHBO

**Cause:** The import direction (Booking.com → MHBO) needs to be configured separately. Booking.com's restriction only prevents them from importing your URL — you can still import theirs.

**Fix:**

1. Log in to Booking.com Extranet → Rates & Availability → Synchronise Calendars → Export Calendar (iCal)
2. Copy the `.ics` URL
3. In MHBO: Hotel Booking > Rooms → iCal icon → Import External Calendars → add the URL
4. Click Connect Calendar, then Sync Now

Booking.com's extranet also has an **"Import now"** button under Synchronise Calendars to force an immediate refresh of any calendars _they_ are importing from other OTAs.

### Duplicate Bookings Appearing After Adding Booking.com Connection

**Cause:** If you configured the Airbnb-relay workaround (MHBO → Airbnb → Booking.com) AND separately added Booking.com's export feed into MHBO, the same booking may appear twice.

**Fix:** Ensure Conflict Resolution is set to **Local Wins**. The first booking processed wins; subsequent imports of the same `UID` are skipped.

### Duplicate Bookings Appearing

**Cause:** Conflict Resolution is set to "External Wins" and the same date range exists in both MHBO and the OTA feed.

**Fix:** Go to **Hotel Booking > PRO Features > iCal Sync > Settings** and switch **Conflict Resolution** to **Local Wins**. Then manually cancel or delete any duplicate bookings from the Bookings list.

### Receiving Failure Alert Emails

**Cause:** The configured failure threshold was reached (default: 3 consecutive failures). The plugin automatically schedules a retry after each failure.

**Fix:** Check the connection's inline error message for the root cause. Once the feed URL is valid again, click **Sync Now** — a successful sync resets the failure counter.

---

## 6.7 Getting Support

**Before Contacting Support:**

1. Check this documentation
2. Verify system requirements are met
3. Test with default WordPress theme
4. Disable other plugins to check for conflicts
5. Gather error logs and screenshots

**Support Channels:**

- **GitHub Issues:** https://github.com/leslieradue-web/modern-hotel-booking-free/issues
- **WordPress.org Forum:** https://wordpress.org/support/plugin/modern-hotel-booking/

**Information to Include:**

- WordPress version
- PHP version
- Plugin version
- Description of issue
- Steps to reproduce
- Error messages or screenshots

---

**Document Version:** 2.4.4 (Mobile CSS Optimization)
**Last Updated:** April 25, 2026  
**License:** GPLv2 or later
