$level_id) { $main_plan_id = (int) $main_plan_id; $level_id = (int) $level_id; if ($main_plan_id > 0 && $level_id > 0) { $map[$main_plan_id] = $level_id; } } return $map; } function aismart_plugin_membership_user_by_email($email) { $email = sanitize_email((string) $email); if ($email === '') { return new WP_Error('aismart_invalid_email', 'A valid email address is required.'); } $user = get_user_by('email', $email); if ($user instanceof WP_User) { return $user; } $username_base = sanitize_user(strstr($email, '@', true), true); $username_base = $username_base !== '' ? $username_base : 'aismart-user'; $username = $username_base; while (username_exists($username)) { $username = $username_base . '-' . strtolower(wp_generate_password(6, false, false)); } $user_id = wp_insert_user([ 'user_login' => $username, 'user_email' => $email, 'user_pass' => wp_generate_password(32, true, true), 'display_name' => $username_base, 'role' => 'subscriber', ]); return is_wp_error($user_id) ? $user_id : get_user_by('id', (int) $user_id); } function aismart_plugin_sync_membership_by_email($email, $main_plan_id) { global $wpdb; $main_plan_id = (int) $main_plan_id; $map = aismart_plugin_membership_plan_map(); $target_level_id = isset($map[$main_plan_id]) ? (int) $map[$main_plan_id] : 0; if ($main_plan_id <= 0 || $target_level_id <= 0) { return new WP_Error('aismart_unmapped_plan', 'The Main plan does not have a WordPress membership mapping.'); } if (!function_exists('pmpro_changeMembershipLevel')) { return new WP_Error('aismart_pmpro_unavailable', 'Paid Memberships Pro is unavailable.'); } $level_exists = (int) $wpdb->get_var($wpdb->prepare( "SELECT id FROM {$wpdb->pmpro_membership_levels} WHERE id = %d LIMIT 1", $target_level_id )); if ($level_exists !== $target_level_id) { return new WP_Error('aismart_missing_level', 'The mapped WordPress membership level does not exist.'); } $user = aismart_plugin_membership_user_by_email($email); if (is_wp_error($user)) { return $user; } $current = function_exists('pmpro_getMembershipLevelForUser') ? pmpro_getMembershipLevelForUser((int) $user->ID) : null; $current_level_id = isset($current->id) ? (int) $current->id : 0; if ($current_level_id === $target_level_id) { return [ 'wp_user_id' => (int) $user->ID, 'main_plan_id' => $main_plan_id, 'membership_level_id' => $target_level_id, 'changed' => false, ]; } if (!pmpro_changeMembershipLevel($target_level_id, (int) $user->ID)) { return new WP_Error('aismart_membership_sync_failed', 'WordPress membership synchronization failed.'); } return [ 'wp_user_id' => (int) $user->ID, 'main_plan_id' => $main_plan_id, 'membership_level_id' => $target_level_id, 'changed' => true, ]; } function aismart_plugin_membership_sync_permission(WP_REST_Request $request) { $expected = aismart_plugin_membership_sync_token(); $provided = trim((string) $request->get_header('X-Aismart-Sync-Token')); if ($expected === '' || $provided === '' || !hash_equals($expected, $provided)) { return new WP_Error('rest_forbidden', 'Invalid membership synchronization credentials.', ['status' => 403]); } return true; } function aismart_plugin_membership_sync_rest(WP_REST_Request $request) { $result = aismart_plugin_sync_membership_by_email( (string) $request->get_param('email'), (int) $request->get_param('main_plan_id') ); if (is_wp_error($result)) { return new WP_REST_Response([ 'ok' => false, 'message' => $result->get_error_message(), ], $result->get_error_code() === 'aismart_pmpro_unavailable' ? 503 : 422); } return new WP_REST_Response(['ok' => true, 'data' => $result], 200); } function aismart_plugin_register_membership_sync_route() { register_rest_route('aismart/v1', '/force-sync', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => 'aismart_plugin_membership_sync_rest', 'permission_callback' => 'aismart_plugin_membership_sync_permission', 'args' => [ 'email' => [ 'required' => true, 'type' => 'string', 'format' => 'email', 'sanitize_callback' => 'sanitize_email', ], 'main_plan_id' => [ 'required' => true, 'type' => 'integer', 'minimum' => 1, ], ], ]); } add_action('rest_api_init', 'aismart_plugin_register_membership_sync_route');