$payload Response data under the `data` key. * @return array{success: true, data: array} */ public static function build_success_result(array $payload = []): array { return [ 'success' => true, 'data' => $payload, ]; } /** * Build a failed handler result payload. * * @param array|string $payload Error message string or data array. * @param int $status HTTP status code for the REST response. * @return array{success: false, data: array, status: int} */ public static function build_error_result(array|string $payload, int $status = 400): array { if (is_string($payload)) { $payload = ['message' => $payload]; } return [ 'success' => false, 'data' => $payload, 'status' => $status, ]; } /** * Convert a handler result into a WP_REST_Response ({ success, data }). * * @param array{success?: bool, data?: array, status?: int} $handler_result Service result. * @return WP_REST_Response */ public static function create_rest_response(array $handler_result): WP_REST_Response { $is_success = !empty($handler_result['success']); $payload = is_array($handler_result['data'] ?? null) ? $handler_result['data'] : []; $http_status = (int) ($handler_result['status'] ?? ($is_success ? 200 : 400)); if ($http_status < 100) { $http_status = $is_success ? 200 : 400; } return new WP_REST_Response([ 'success' => $is_success, 'data' => $payload, ], $http_status); } /** * Extract only registered route args (sanitized). Unknown body keys are discarded. * * @param WP_REST_Request $request Incoming REST request. * @return array */ public static function extract_request_parameters(WP_REST_Request $request): array { $route_attributes = $request->get_attributes(); $allowed_keys = array_keys($route_attributes['args'] ?? []); if ($allowed_keys === []) { return []; } $request_parameters = []; foreach ($allowed_keys as $parameter_name) { if (!$request->has_param($parameter_name)) { continue; } $parameter_value = $request->get_param($parameter_name); if ($parameter_value === null) { continue; } $request_parameters[$parameter_name] = $parameter_value; } return $request_parameters; } /** * Normalize whitelisted REST params for FrontendManager (uses Request helpers). * * @param array $params Extracted route parameters. * @return array */ public static function prepare_frontend_input(array $params): array { $check_in = RequestHelper::normalize_ymd_value($params['start_date'] ?? ''); if ($check_in === '') { $check_in = gmdate('Y-m-d'); } $check_out = RequestHelper::normalize_ymd_value($params['end_date'] ?? '', $check_in); if ($check_out === '') { $check_out = $check_in; } $from = RequestHelper::normalize_ymd_value($params['from'] ?? ''); if ($from === '') { $from = gmdate('Y-m-01'); } $to = RequestHelper::normalize_ymd_value($params['to'] ?? ''); if ($to === '') { $to = gmdate('Y-m-t', strtotime($from) ?: time()); } return array_merge($params, [ 'property_id' => absint($params['property_id'] ?? 0), 'post_id' => absint($params['post_id'] ?? 0), 'property_source_id' => sanitize_text_field($params['property_source_id'] ?? ''), 'check_in' => $check_in, 'check_out' => $check_out, 'adults' => array_key_exists('adults', $params) ? absint($params['adults']) : 1, 'kids' => array_key_exists('kids', $params) ? absint($params['kids']) : 0, 'include_price' => filter_var($params['include_price'] ?? false, FILTER_VALIDATE_BOOLEAN) || absint($params['include_price'] ?? 0) === 1, 'from' => $from, 'to' => $to, ]); } /** * Normalize whitelisted REST params for ReservationService::create_reservation_session(). * * @param array $params Extracted route parameters. * @return array */ public static function prepare_create_booking_input(array $params): array { return [ 'property_id' => absint($params['property_id'] ?? 0), 'dates' => sanitize_text_field($params['dates'] ?? ''), 'adults' => absint($params['adults'] ?? 0), 'kids' => absint($params['kids'] ?? 0), ]; } /** * Normalize whitelisted REST params for IntegrationService::verify_availability(). * * @param array $params Extracted route parameters. * @return array */ public static function prepare_verify_availability_input(array $params): array { return [ 'property_id' => absint($params['property_id'] ?? 0), 'check_in' => RequestHelper::normalize_ymd_value($params['check_in'] ?? ''), 'check_out' => RequestHelper::normalize_ymd_value($params['check_out'] ?? ''), ]; } /** * Normalize whitelisted REST params for ReservationService::handle_booking_submission(). * * @param array $params Extracted route parameters. * @return array */ public static function prepare_submit_booking_input(array $params): array { $reservation_service = ReservationService::instance(); $property_id = absint($params['property_id'] ?? 0); $property = null; if ($property_id > 0 && get_post_type($property_id) === 'bizdexul_property') { $property = new PropertyDetails($property_id); } $default_gateway = $property && $property->creates_local_site_booking_only() ? 'offline' : $reservation_service->get_active_gateway_id(); $allowed_gateways = array_values(array_unique(array_filter(array_merge( ['offline', $reservation_service->get_manual_payment()->get_id()], array_keys($reservation_service->get_gateways()) )))); $payment_method_raw = sanitize_key($params['payment_method'] ?? ''); $payment_method = in_array($payment_method_raw, $allowed_gateways, true) ? $payment_method_raw : $default_gateway; $payment_method_id = sanitize_text_field($params['payment_method_id'] ?? ''); $email_raw = sanitize_email($params['email'] ?? ''); $email = is_email($email_raw) ? $email_raw : ''; return [ 'property_id' => $property_id, 'first_name' => sanitize_text_field($params['first_name'] ?? ''), 'last_name' => sanitize_text_field($params['last_name'] ?? ''), 'email' => $email, 'phone' => sanitize_text_field($params['phone'] ?? ''), 'check_in' => RequestHelper::normalize_ymd_value($params['check_in'] ?? ''), 'check_out' => RequestHelper::normalize_ymd_value($params['check_out'] ?? ''), 'adults' => absint($params['adults'] ?? 0), 'kids' => absint($params['kids'] ?? 0), 'trip_details' => sanitize_textarea_field($params['trip_details'] ?? ''), 'special_request' => sanitize_textarea_field($params['special_request'] ?? ''), 'referral' => BookingDetails::sanitize_meta_value('referral', $params['referral'] ?? ''), 'total_amount' => max(0.0, floatval($params['total_amount'] ?? 0)), 'nights_count' => absint($params['nights_count'] ?? 0), 'nights_total' => max(0.0, floatval($params['nights_total'] ?? 0)), 'addons_total' => max(0.0, floatval($params['addons_total'] ?? 0)), 'cleaning_fee' => max(0.0, floatval($params['cleaning_fee'] ?? 0)), 'taxes' => max(0.0, floatval($params['taxes'] ?? 0)), 'payment_method' => $payment_method !== '' ? $payment_method : $default_gateway, 'payment_method_id' => $payment_method_id, 'payment_data' => [ 'payment_method_id' => $payment_method_id, 'email' => $email, 'property_id' => $property_id, ], ]; } /** * Sanitize a JSON string payload, or encode an array of IDs as JSON. * * @param mixed $value Raw request value. * @return string */ public static function sanitize_json_payload($value): string { if (is_array($value)) { return wp_json_encode(RequestHelper::positive_int_list($value)) ?: '[]'; } if (!is_string($value) || $value === '') { return '[]'; } return sanitize_textarea_field($value); } /** * Permission callback: require a valid WordPress REST nonce. * * Accepts `X-WP-Nonce` header or `_wpnonce` request param created with * {@see RestApi::NONCE_ACTION} (`wp_rest`). * * @param WP_REST_Request $request Incoming REST request. * @return bool|WP_Error */ public static function authorize_public_request(WP_REST_Request $request) { $header_nonce = $request->get_header('X-WP-Nonce'); $param_nonce = $request->get_param('_wpnonce'); $nonce = ''; if (is_string($header_nonce) && $header_nonce !== '') { $nonce = $header_nonce; } elseif (is_string($param_nonce) && $param_nonce !== '') { $nonce = $param_nonce; } $nonce = sanitize_text_field(wp_unslash($nonce)); if ($nonce === '') { return new WP_Error( 'bizdex_uplisting_rest_forbidden', __('Invalid or missing REST nonce.', 'advanced-connector-for-uplisting'), ['status' => 403] ); } if (wp_verify_nonce($nonce, RestApi::NONCE_ACTION) === false) { return new WP_Error( 'bizdex_uplisting_rest_forbidden', __('Invalid or missing REST nonce.', 'advanced-connector-for-uplisting'), ['status' => 403] ); } return true; } /** * Permission callback: public nonce + manage_options. * * @param WP_REST_Request $request Incoming REST request. * @return bool|WP_Error */ public static function authorize_admin_request(WP_REST_Request $request) { $public_authorization = self::authorize_public_request($request); if ($public_authorization !== true) { return $public_authorization; } if (!current_user_can('manage_options')) { return new WP_Error( 'bizdex_uplisting_rest_forbidden', __('You are not allowed to perform this action.', 'advanced-connector-for-uplisting'), ['status' => 403] ); } return true; } }