esc_url_raw(rest_url(self::NAMESPACE . '/')), 'nonce' => wp_create_nonce(self::NONCE_ACTION), 'namespace' => self::NAMESPACE, ]; } /** * Register all public booking REST routes. * * @return void */ public static function register_routes(): void { register_rest_route(self::NAMESPACE, '/property/get-price', [ 'methods' => 'POST', 'callback' => [self::class, 'get_price'], 'permission_callback' => 'bizdex_uplisting_authorize_public_request', 'args' => [ 'property_id' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'post_id' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'property_source_id' => [ 'required' => false, 'sanitize_callback' => 'sanitize_text_field', ], 'start_date' => [ 'required' => false, 'sanitize_callback' => 'sanitize_text_field', ], 'end_date' => [ 'required' => false, 'sanitize_callback' => 'sanitize_text_field', ], 'adults' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'kids' => [ 'required' => false, 'sanitize_callback' => 'absint', ], ], ]); register_rest_route(self::NAMESPACE, '/property/check-availability', [ 'methods' => 'POST', 'callback' => [self::class, 'check_availability'], 'permission_callback' => 'bizdex_uplisting_authorize_public_request', 'args' => [ 'property_id' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'post_id' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'property_source_id' => [ 'required' => false, 'sanitize_callback' => 'sanitize_text_field', ], 'include_price' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'adults' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'kids' => [ 'required' => false, 'sanitize_callback' => 'absint', ], ], ]); register_rest_route(self::NAMESPACE, '/property/get-dates', [ 'methods' => 'POST', 'callback' => [self::class, 'get_dates'], 'permission_callback' => 'bizdex_uplisting_authorize_public_request', 'args' => [ 'property_id' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'post_id' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'property_source_id' => [ 'required' => false, 'sanitize_callback' => 'sanitize_text_field', ], 'from' => [ 'required' => false, 'sanitize_callback' => 'sanitize_text_field', ], 'to' => [ 'required' => false, 'sanitize_callback' => 'sanitize_text_field', ], ], ]); register_rest_route(self::NAMESPACE, '/reservation/create-booking', [ 'methods' => 'POST', 'callback' => [self::class, 'create_booking'], 'permission_callback' => 'bizdex_uplisting_authorize_public_request', 'args' => [ 'property_id' => [ 'required' => true, 'sanitize_callback' => 'absint', ], 'dates' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], 'adults' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'kids' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'nights_total' => [ 'required' => false, 'type' => 'number', ], ], ]); register_rest_route(self::NAMESPACE, '/reservation/submit-booking', [ 'methods' => 'POST', 'callback' => [self::class, 'submit_booking'], 'permission_callback' => 'bizdex_uplisting_authorize_public_request', 'args' => [ 'property_id' => [ 'required' => true, 'sanitize_callback' => 'absint', ], 'first_name' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], 'last_name' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], 'email' => [ 'required' => true, 'sanitize_callback' => 'sanitize_email', ], 'phone' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], 'check_in' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], 'check_out' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], 'adults' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'kids' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'trip_details' => [ 'required' => false, 'sanitize_callback' => 'sanitize_textarea_field', ], 'special_request' => [ 'required' => false, 'sanitize_callback' => 'sanitize_textarea_field', ], 'referral' => [ 'required' => false, 'sanitize_callback' => 'sanitize_text_field', ], 'total_amount' => [ 'required' => false, 'type' => 'number', ], 'nights_count' => [ 'required' => false, 'sanitize_callback' => 'absint', ], 'nights_total' => [ 'required' => false, 'type' => 'number', ], 'addons_total' => [ 'required' => false, 'type' => 'number', ], 'cleaning_fee' => [ 'required' => false, 'type' => 'number', ], 'taxes' => [ 'required' => false, 'type' => 'number', ], 'base_price' => [ 'required' => false, 'type' => 'number', ], 'payment_method' => [ 'required' => false, 'sanitize_callback' => 'sanitize_key', ], 'payment_method_id' => [ 'required' => false, 'sanitize_callback' => 'sanitize_text_field', ], 'stripe_payment' => [ 'required' => false, 'sanitize_callback' => 'absint', ], ], ]); register_rest_route(self::NAMESPACE, '/reservation/check-availability', [ 'methods' => 'POST', 'callback' => [self::class, 'check_reservation_availability'], 'permission_callback' => 'bizdex_uplisting_authorize_public_request', 'args' => [ 'property_id' => [ 'required' => true, 'sanitize_callback' => 'absint', ], 'check_in' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], 'check_out' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], ], ]); } /** * Calculate property pricing for selected dates and guests. * * @param WP_REST_Request $request Incoming REST request. * @return WP_REST_Response */ public static function get_price(WP_REST_Request $request): WP_REST_Response { return self::dispatch( 'property/get-price', $request, [RestEndpointHelper::class, 'prepare_frontend_input'], static function (array $input) { return FrontendManager::instance()->get_property_price($input); } ); } /** * Check whether a property is available for the selected date range. * * @param WP_REST_Request $request Incoming REST request. * @return WP_REST_Response */ public static function check_availability(WP_REST_Request $request): WP_REST_Response { return self::dispatch( 'property/check-availability', $request, [RestEndpointHelper::class, 'prepare_frontend_input'], static function (array $input) { return FrontendManager::instance()->get_availability_check($input); } ); } /** * Return calendar rates and blocked dates for a property. * * @param WP_REST_Request $request Incoming REST request. * @return WP_REST_Response */ public static function get_dates(WP_REST_Request $request): WP_REST_Response { return self::dispatch( 'property/get-dates', $request, [RestEndpointHelper::class, 'prepare_frontend_input'], static function (array $input) { return FrontendManager::instance()->get_calendar_rates($input); } ); } /** * Store booking details and return the checkout URL. * * @param WP_REST_Request $request Incoming REST request. * @return WP_REST_Response */ public static function create_booking(WP_REST_Request $request): WP_REST_Response { return self::dispatch( 'reservation/create-booking', $request, [RestEndpointHelper::class, 'prepare_create_booking_input'], static function (array $input) { return ReservationService::instance()->create_reservation_session($input); } ); } /** * Submit a booking from the reservation checkout form. * * @param WP_REST_Request $request Incoming REST request. * @return WP_REST_Response */ public static function submit_booking(WP_REST_Request $request): WP_REST_Response { return self::dispatch( 'reservation/submit-booking', $request, [RestEndpointHelper::class, 'prepare_submit_booking_input'], static function (array $input) { return ReservationService::instance()->handle_booking_submission($input); } ); } /** * Re-verify reservation dates before final booking submission. * * @param WP_REST_Request $request Incoming REST request. * @return WP_REST_Response */ public static function check_reservation_availability(WP_REST_Request $request): WP_REST_Response { return self::dispatch( 'reservation/check-availability', $request, [RestEndpointHelper::class, 'prepare_verify_availability_input'], static function (array $input) { return (new IntegrationService())->verify_availability($input); } ); } /** * Sanitize input, run hooks, execute the handler, and wrap the response. * * @param string $route Route path without leading slash (e.g. property/get-price). * @param WP_REST_Request $request Incoming request. * @param callable(array):array $prepare Input preparer from RestEndpointHelper. * @param callable(array):array $process Business handler returning {success,data,status?}. * @return WP_REST_Response */ private static function dispatch( string $route, WP_REST_Request $request, callable $prepare, callable $process ): WP_REST_Response { $params = RestEndpointHelper::extract_request_parameters($request); $input = $prepare($params); /** * Filter sanitized REST input before the handler runs. * * @param array $input Prepared input. * @param string $route Route path. * @param WP_REST_Request $request Original request. */ $input = apply_filters('bizdex_uplisting_rest_request_input', $input, $route, $request); /** * Fires before a public REST handler runs. * * @param string $route * @param array $input * @param WP_REST_Request $request */ do_action('bizdex_uplisting_rest_before_request', $route, $input, $request); $result = $process($input); /** * Filter the handler result before it becomes a WP_REST_Response. * * @param array $result * @param string $route * @param array $input * @param WP_REST_Request $request */ $result = apply_filters('bizdex_uplisting_rest_response', $result, $route, $input, $request); /** * Fires after a public REST handler runs. * * @param string $route * @param array $result * @param array $input * @param WP_REST_Request $request */ do_action('bizdex_uplisting_rest_after_request', $route, $result, $input, $request); return RestEndpointHelper::create_rest_response($result); } }