Bug: feat(users): Add V2 User APIs to Support Modularity for Merchant Accounts



Add v2 routes for supporting dashboard functions:
- create new v2 merchant account
- list v2 merchant accounts for user in org
- switch to a v2 merchant account


Also:
- add merchant account permission for recon
diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs
index aab8f518abe..5e0564b4187 100644
--- a/crates/api_models/src/events/user.rs
+++ b/crates/api_models/src/events/user.rs
@@ -19,8 +19,9 @@ use crate::user::{
     SendVerifyEmailRequest, SignUpRequest, SignUpWithMerchantIdRequest, SsoSignInRequest,
     SwitchMerchantRequest, SwitchOrganizationRequest, SwitchProfileRequest, TokenResponse,
     TwoFactorAuthStatusResponse, TwoFactorStatus, UpdateUserAccountDetailsRequest,
-    UpdateUserAuthenticationMethodRequest, UserFromEmailRequest, UserMerchantCreate,
-    UserOrgMerchantCreateRequest, VerifyEmailRequest, VerifyRecoveryCodeRequest, VerifyTotpRequest,
+    UpdateUserAuthenticationMethodRequest, UserFromEmailRequest, UserMerchantAccountResponse,
+    UserMerchantCreate, UserOrgMerchantCreateRequest, VerifyEmailRequest,
+    VerifyRecoveryCodeRequest, VerifyTotpRequest,
 };
 
 common_utils::impl_api_event_type!(
@@ -39,6 +40,7 @@ common_utils::impl_api_event_type!(
         CreateInternalUserRequest,
         CreateTenantUserRequest,
         UserOrgMerchantCreateRequest,
+        UserMerchantAccountResponse,
         UserMerchantCreate,
         AuthorizeResponse,
         ConnectAccountRequest,
diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs
index ea979bfe735..aa39877f0c7 100644
--- a/crates/api_models/src/user.rs
+++ b/crates/api_models/src/user.rs
@@ -133,6 +133,7 @@ pub struct UserOrgMerchantCreateRequest {
 #[derive(Debug, serde::Deserialize, serde::Serialize)]
 pub struct UserMerchantCreate {
     pub company_name: String,
+    pub product_type: Option<common_enums::MerchantProductType>,
 }
 
 #[derive(serde::Serialize, Debug, Clone)]
@@ -151,6 +152,7 @@ pub struct GetUserDetailsResponse {
     pub profile_id: id_type::ProfileId,
     pub entity_type: EntityType,
     pub theme_id: Option<String>,
+    pub version: common_enums::ApiVersion,
 }
 
 #[derive(Debug, serde::Deserialize, serde::Serialize)]
@@ -382,9 +384,11 @@ pub struct ListOrgsForUserResponse {
 }
 
 #[derive(Debug, serde::Serialize)]
-pub struct ListMerchantsForUserInOrgResponse {
+pub struct UserMerchantAccountResponse {
     pub merchant_id: id_type::MerchantId,
     pub merchant_name: OptionalEncryptableName,
+    pub product_type: Option<common_enums::MerchantProductType>,
+    pub version: common_enums::ApiVersion,
 }
 
 #[derive(Debug, serde::Serialize)]
diff --git a/crates/common_enums/src/enums/accounts.rs b/crates/common_enums/src/enums/accounts.rs
index 9a5247512e4..d9c83312d6b 100644
--- a/crates/common_enums/src/enums/accounts.rs
+++ b/crates/common_enums/src/enums/accounts.rs
@@ -17,9 +17,8 @@ use utoipa::ToSchema;
 #[serde(rename_all = "snake_case")]
 #[strum(serialize_all = "snake_case")]
 pub enum MerchantProductType {
-    Orchestration,
     #[default]
-    Legacy,
+    Orchestration,
     Vault,
     Recon,
     Recovery,
diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs
index b6b7571d32f..fef000a3954 100644
--- a/crates/hyperswitch_domain_models/src/merchant_account.rs
+++ b/crates/hyperswitch_domain_models/src/merchant_account.rs
@@ -140,6 +140,7 @@ pub struct MerchantAccountSetter {
     pub organization_id: common_utils::id_type::OrganizationId,
     pub recon_status: diesel_models::enums::ReconStatus,
     pub is_platform_account: bool,
+    pub version: common_enums::ApiVersion,
     pub product_type: Option<common_enums::MerchantProductType>,
 }
 
@@ -158,6 +159,7 @@ impl From<MerchantAccountSetter> for MerchantAccount {
             organization_id,
             recon_status,
             is_platform_account,
+            version,
             product_type,
         } = item;
         Self {
@@ -172,6 +174,7 @@ impl From<MerchantAccountSetter> for MerchantAccount {
             organization_id,
             recon_status,
             is_platform_account,
+            version,
             product_type,
         }
     }
@@ -191,6 +194,7 @@ pub struct MerchantAccount {
     pub organization_id: common_utils::id_type::OrganizationId,
     pub recon_status: diesel_models::enums::ReconStatus,
     pub is_platform_account: bool,
+    pub version: common_enums::ApiVersion,
     pub product_type: Option<common_enums::MerchantProductType>,
 }
 
@@ -631,6 +635,7 @@ impl super::behaviour::Conversion for MerchantAccount {
                 organization_id: item.organization_id,
                 recon_status: item.recon_status,
                 is_platform_account: item.is_platform_account,
+                version: item.version,
                 product_type: item.product_type,
             })
         }
diff --git a/crates/router/src/consts/user.rs b/crates/router/src/consts/user.rs
index d4eb12e39bf..3601f229827 100644
--- a/crates/router/src/consts/user.rs
+++ b/crates/router/src/consts/user.rs
@@ -1,3 +1,4 @@
+use common_enums;
 use common_utils::consts::MAX_ALLOWED_MERCHANT_NAME_LENGTH;
 
 pub const MAX_NAME_LENGTH: usize = 70;
@@ -45,3 +46,7 @@ pub const EMAIL_SUBJECT_RESET_PASSWORD: &str = "Get back to Hyperswitch - Reset
 pub const EMAIL_SUBJECT_NEW_PROD_INTENT: &str = "New Prod Intent";
 pub const EMAIL_SUBJECT_WELCOME_TO_COMMUNITY: &str =
     "Thank you for signing up on Hyperswitch Dashboard!";
+
+pub const DEFAULT_PROFILE_NAME: &str = "default";
+pub const DEFAULT_PRODUCT_TYPE: common_enums::MerchantProductType =
+    common_enums::MerchantProductType::Orchestration;
diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs
index dca58f11329..106c904ef7b 100644
--- a/crates/router/src/core/admin.rs
+++ b/crates/router/src/core/admin.rs
@@ -673,6 +673,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate {
                     organization_id: organization.get_organization_id(),
                     recon_status: diesel_models::enums::ReconStatus::NotRequested,
                     is_platform_account: false,
+                    version: hyperswitch_domain_models::consts::API_VERSION,
                     product_type: self.product_type,
                 }),
             )
diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs
index 4f19f040bdb..f0a6df94453 100644
--- a/crates/router/src/core/user.rs
+++ b/crates/router/src/core/user.rs
@@ -128,13 +128,62 @@ pub async fn get_user_details(
     .await
     .change_context(UserErrors::InternalServerError)?;
 
+    let key_manager_state = &(&state).into();
+
+    let merchant_key_store = state
+        .store
+        .get_merchant_key_store_by_merchant_id(
+            key_manager_state,
+            &user_from_token.merchant_id,
+            &state.store.get_master_key().to_vec().into(),
+        )
+        .await;
+
+    let version = if let Ok(merchant_key_store) = merchant_key_store {
+        let merchant_account = state
+            .store
+            .find_merchant_account_by_merchant_id(
+                key_manager_state,
+                &user_from_token.merchant_id,
+                &merchant_key_store,
+            )
+            .await;
+
+        if let Ok(merchant_account) = merchant_account {
+            merchant_account.version
+        } else if merchant_account
+            .as_ref()
+            .map_err(|e| e.current_context().is_db_not_found())
+            .err()
+            .unwrap_or(false)
+        {
+            common_enums::ApiVersion::V2
+        } else {
+            Err(merchant_account
+                .err()
+                .map(|e| e.change_context(UserErrors::InternalServerError))
+                .unwrap_or(UserErrors::InternalServerError.into()))?
+        }
+    } else if merchant_key_store
+        .as_ref()
+        .map_err(|e| e.current_context().is_db_not_found())
+        .err()
+        .unwrap_or(false)
+    {
+        common_enums::ApiVersion::V2
+    } else {
+        Err(merchant_key_store
+            .err()
+            .map(|e| e.change_context(UserErrors::InternalServerError))
+            .unwrap_or(UserErrors::InternalServerError.into()))?
+    };
+
     let theme = theme_utils::get_most_specific_theme_using_token_and_min_entity(
         &state,
         &user_from_token,
         EntityType::Profile,
     )
     .await?;
-
     Ok(ApplicationResponse::Json(
         user_api::GetUserDetailsResponse {
             merchant_id: user_from_token.merchant_id,
@@ -149,6 +198,7 @@ pub async fn get_user_details(
             profile_id: user_from_token.profile_id,
             entity_type: role_info.get_entity_type(),
             theme_id: theme.map(|theme| theme.theme_id),
+            version,
         },
     ))
 }
@@ -1466,9 +1516,9 @@ pub async fn create_org_merchant_for_user(
         .insert_organization(db_organization)
         .await
         .change_context(UserErrors::InternalServerError)?;
-
+    let default_product_type = consts::user::DEFAULT_PRODUCT_TYPE;
     let merchant_account_create_request =
-        utils::user::create_merchant_account_request_for_org(req, org)?;
+        utils::user::create_merchant_account_request_for_org(req, org, default_product_type)?;
 
     admin::create_merchant_account(state.clone(), merchant_account_create_request)
         .await
@@ -1482,15 +1532,22 @@ pub async fn create_merchant_account(
     state: SessionState,
     user_from_token: auth::UserFromToken,
     req: user_api::UserMerchantCreate,
-) -> UserResponse<()> {
+) -> UserResponse<user_api::UserMerchantAccountResponse> {
     let user_from_db = user_from_token.get_user_from_db(&state).await?;
 
     let new_merchant = domain::NewUserMerchant::try_from((user_from_db, req, user_from_token))?;
-    new_merchant
+    let domain_merchant_account = new_merchant
         .create_new_merchant_and_insert_in_db(state.to_owned())
         .await?;
 
-    Ok(ApplicationResponse::StatusOk)
+    Ok(ApplicationResponse::Json(
+        user_api::UserMerchantAccountResponse {
+            merchant_id: domain_merchant_account.get_id().to_owned(),
+            merchant_name: domain_merchant_account.merchant_name,
+            product_type: domain_merchant_account.product_type,
+            version: domain_merchant_account.version,
+        },
+    ))
 }
 
 pub async fn list_user_roles_details(
@@ -2858,7 +2915,7 @@ pub async fn list_orgs_for_user(
 pub async fn list_merchants_for_user_in_org(
     state: SessionState,
     user_from_token: auth::UserFromToken,
-) -> UserResponse<Vec<user_api::ListMerchantsForUserInOrgResponse>> {
+) -> UserResponse<Vec<user_api::UserMerchantAccountResponse>> {
     let role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
         &state,
         &user_from_token.role_id,
@@ -2917,19 +2974,18 @@ pub async fn list_merchants_for_user_in_org(
         }
     };
 
-    if merchant_accounts.is_empty() {
-        Err(UserErrors::InternalServerError).attach_printable("No merchant found for a user")?;
-    }
+    // TODO: Add a check to see if merchant accounts are empty, and handle accordingly, when a single route will be used instead
+    // of having two separate routes.
 
     Ok(ApplicationResponse::Json(
         merchant_accounts
             .into_iter()
-            .map(
-                |merchant_account| user_api::ListMerchantsForUserInOrgResponse {
-                    merchant_name: merchant_account.merchant_name.clone(),
-                    merchant_id: merchant_account.get_id().to_owned(),
-                },
-            )
+            .map(|merchant_account| user_api::UserMerchantAccountResponse {
+                merchant_name: merchant_account.merchant_name.clone(),
+                merchant_id: merchant_account.get_id().to_owned(),
+                product_type: merchant_account.product_type.clone(),
+                version: merchant_account.version,
+            })
             .collect::<Vec<_>>(),
     ))
 }
diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs
index 349fb91efc3..fc5e2b94d1c 100644
--- a/crates/router/src/lib.rs
+++ b/crates/router/src/lib.rs
@@ -183,6 +183,7 @@ pub fn mk_app(
         server_app = server_app
             .service(routes::Organization::server(state.clone()))
             .service(routes::MerchantAccount::server(state.clone()))
+            .service(routes::User::server(state.clone()))
             .service(routes::ApiKeys::server(state.clone()))
             .service(routes::Routing::server(state.clone()));
 
@@ -195,7 +196,6 @@ pub fn mk_app(
                 .service(routes::Gsm::server(state.clone()))
                 .service(routes::ApplePayCertificatesMigration::server(state.clone()))
                 .service(routes::PaymentLink::server(state.clone()))
-                .service(routes::User::server(state.clone()))
                 .service(routes::ConnectorOnboarding::server(state.clone()))
                 .service(routes::Verify::server(state.clone()))
                 .service(routes::Analytics::server(state.clone()))
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index 7bb42134aa7..6d687fea2d5 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -2091,6 +2091,43 @@ impl Verify {
 
 pub struct User;
 
+#[cfg(all(feature = "olap", feature = "v2"))]
+impl User {
+    pub fn server(state: AppState) -> Scope {
+        let mut route = web::scope("/v2/user").app_data(web::Data::new(state));
+
+        route = route.service(
+            web::resource("/create_merchant")
+                .route(web::post().to(user::user_merchant_account_create)),
+        );
+        route = route.service(
+            web::scope("/list")
+                .service(
+                    web::resource("/merchant")
+                        .route(web::get().to(user::list_merchants_for_user_in_org)),
+                )
+                .service(
+                    web::resource("/profile")
+                        .route(web::get().to(user::list_profiles_for_user_in_org_and_merchant)),
+                ),
+        );
+
+        route = route.service(
+            web::scope("/switch")
+                .service(
+                    web::resource("/merchant")
+                        .route(web::post().to(user::switch_merchant_for_user_in_org)),
+                )
+                .service(
+                    web::resource("/profile")
+                        .route(web::post().to(user::switch_profile_for_user_in_org_and_merchant)),
+                ),
+        );
+
+        route
+    }
+}
+
 #[cfg(all(feature = "olap", feature = "v1"))]
 impl User {
     pub fn server(state: AppState) -> Scope {
diff --git a/crates/router/src/services/authorization/permission_groups.rs b/crates/router/src/services/authorization/permission_groups.rs
index 0cdb68ec8d7..6333de5f19b 100644
--- a/crates/router/src/services/authorization/permission_groups.rs
+++ b/crates/router/src/services/authorization/permission_groups.rs
@@ -180,7 +180,7 @@ pub static USERS: [Resource; 2] = [Resource::User, Resource::Account];
 
 pub static ACCOUNT: [Resource; 3] = [Resource::Account, Resource::ApiKey, Resource::WebhookEvent];
 
-pub static RECON_OPS: [Resource; 7] = [
+pub static RECON_OPS: [Resource; 8] = [
     Resource::ReconToken,
     Resource::ReconFiles,
     Resource::ReconUpload,
@@ -188,10 +188,12 @@ pub static RECON_OPS: [Resource; 7] = [
     Resource::ReconConfig,
     Resource::ReconAndSettlementAnalytics,
     Resource::ReconReports,
+    Resource::Account,
 ];
 
-pub static RECON_REPORTS: [Resource; 3] = [
+pub static RECON_REPORTS: [Resource; 4] = [
     Resource::ReconToken,
     Resource::ReconAndSettlementAnalytics,
     Resource::ReconReports,
+    Resource::Account,
 ];
diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs
index a51ccfdc710..4854d1b4786 100644
--- a/crates/router/src/types/domain/user.rs
+++ b/crates/router/src/types/domain/user.rs
@@ -19,6 +19,7 @@ use diesel_models::{
     user_role::{UserRole, UserRoleNew},
 };
 use error_stack::{report, ResultExt};
+use hyperswitch_domain_models::api::ApplicationResponse;
 use masking::{ExposeInterface, PeekInterface, Secret};
 use once_cell::sync::Lazy;
 use rand::distributions::{Alphanumeric, DistString};
@@ -37,7 +38,7 @@ use crate::{
     db::GlobalStorageInterface,
     routes::SessionState,
     services::{self, authentication::UserFromToken},
-    types::transformers::ForeignFrom,
+    types::{domain, transformers::ForeignFrom},
     utils::user::password,
 };
 
@@ -390,6 +391,7 @@ pub struct NewUserMerchant {
     merchant_id: id_type::MerchantId,
     company_name: Option<UserCompanyName>,
     new_organization: NewUserOrganization,
+    product_type: Option<common_enums::MerchantProductType>,
 }
 
 impl TryFrom<UserCompanyName> for MerchantName {
@@ -414,6 +416,10 @@ impl NewUserMerchant {
         self.new_organization.clone()
     }
 
+    pub fn get_product_type(&self) -> Option<common_enums::MerchantProductType> {
+        self.product_type.clone()
+    }
+
     pub async fn check_if_already_exists_in_db(&self, state: SessionState) -> UserResult<()> {
         if state
             .store
@@ -450,7 +456,7 @@ impl NewUserMerchant {
             organization_id: self.new_organization.get_organization_id(),
             metadata: None,
             merchant_details: None,
-            product_type: None,
+            product_type: self.get_product_type(),
         })
     }
 
@@ -477,28 +483,115 @@ impl NewUserMerchant {
             enable_payment_response_hash: None,
             redirect_to_merchant_with_http_post: None,
             pm_collect_link_config: None,
-            product_type: None,
+            product_type: self.get_product_type(),
         })
     }
 
+    #[cfg(feature = "v1")]
     pub async fn create_new_merchant_and_insert_in_db(
         &self,
         state: SessionState,
-    ) -> UserResult<()> {
+    ) -> UserResult<domain::MerchantAccount> {
         self.check_if_already_exists_in_db(state.clone()).await?;
 
         let merchant_account_create_request = self
             .create_merchant_account_request()
             .attach_printable("unable to construct merchant account create request")?;
 
-        Box::pin(admin::create_merchant_account(
-            state.clone(),
-            merchant_account_create_request,
+        let ApplicationResponse::Json(merchant_account_response) = Box::pin(
+            admin::create_merchant_account(state.clone(), merchant_account_create_request),
+        )
+        .await
+        .change_context(UserErrors::InternalServerError)
+        .attach_printable("Error while creating a merchant")?
+        else {
+            return Err(UserErrors::InternalServerError.into());
+        };
+
+        let key_manager_state = &(&state).into();
+        let merchant_key_store = state
+            .store
+            .get_merchant_key_store_by_merchant_id(
+                key_manager_state,
+                &merchant_account_response.merchant_id,
+                &state.store.get_master_key().to_vec().into(),
+            )
+            .await
+            .change_context(UserErrors::InternalServerError)
+            .attach_printable("Failed to retrieve merchant key store by merchant_id")?;
+
+        let merchant_account = state
+            .store
+            .find_merchant_account_by_merchant_id(
+                key_manager_state,
+                &merchant_account_response.merchant_id,
+                &merchant_key_store,
+            )
+            .await
+            .change_context(UserErrors::InternalServerError)
+            .attach_printable("Failed to retrieve merchant account by merchant_id")?;
+        Ok(merchant_account)
+    }
+
+    #[cfg(feature = "v2")]
+    pub async fn create_new_merchant_and_insert_in_db(
+        &self,
+        state: SessionState,
+    ) -> UserResult<domain::MerchantAccount> {
+        self.check_if_already_exists_in_db(state.clone()).await?;
+
+        let merchant_account_create_request = self
+            .create_merchant_account_request()
+            .attach_printable("unable to construct merchant account create request")?;
+
+        let ApplicationResponse::Json(merchant_account_response) = Box::pin(
+            admin::create_merchant_account(state.clone(), merchant_account_create_request),
+        )
+        .await
+        .change_context(UserErrors::InternalServerError)
+        .attach_printable("Error while creating a merchant")?
+        else {
+            return Err(UserErrors::InternalServerError.into());
+        };
+
+        let profile_create_request = admin_api::ProfileCreate {
+            profile_name: consts::user::DEFAULT_PROFILE_NAME.to_string(),
+            ..Default::default()
+        };
+
+        let key_manager_state = &(&state).into();
+        let merchant_key_store = state
+            .store
+            .get_merchant_key_store_by_merchant_id(
+                key_manager_state,
+                &merchant_account_response.id,
+                &state.store.get_master_key().to_vec().into(),
+            )
+            .await
+            .change_context(UserErrors::InternalServerError)
+            .attach_printable("Failed to retrieve merchant key store by merchant_id")?;
+
+        let merchant_account = state
+            .store
+            .find_merchant_account_by_merchant_id(
+                key_manager_state,
+                &merchant_account_response.id,
+                &merchant_key_store,
+            )
+            .await
+            .change_context(UserErrors::InternalServerError)
+            .attach_printable("Failed to retrieve merchant account by merchant_id")?;
+
+        Box::pin(admin::create_profile(
+            state,
+            profile_create_request,
+            merchant_account.clone(),
+            merchant_key_store,
         ))
         .await
         .change_context(UserErrors::InternalServerError)
-        .attach_printable("Error while creating a merchant")?;
-        Ok(())
+        .attach_printable("Error while creating a profile")?;
+        Ok(merchant_account)
     }
 }
 
@@ -507,13 +600,13 @@ impl TryFrom<user_api::SignUpRequest> for NewUserMerchant {
 
     fn try_from(value: user_api::SignUpRequest) -> UserResult<Self> {
         let merchant_id = id_type::MerchantId::new_from_unix_timestamp();
-
         let new_organization = NewUserOrganization::from(value);
-
+        let product_type = Some(consts::user::DEFAULT_PRODUCT_TYPE);
         Ok(Self {
             company_name: None,
             merchant_id,
             new_organization,
+            product_type,
         })
     }
 }
@@ -524,11 +617,12 @@ impl TryFrom<user_api::ConnectAccountRequest> for NewUserMerchant {
     fn try_from(value: user_api::ConnectAccountRequest) -> UserResult<Self> {
         let merchant_id = id_type::MerchantId::new_from_unix_timestamp();
         let new_organization = NewUserOrganization::from(value);
-
+        let product_type = Some(consts::user::DEFAULT_PRODUCT_TYPE);
         Ok(Self {
             company_name: None,
             merchant_id,
             new_organization,
+            product_type,
         })
     }
 }
@@ -539,11 +633,13 @@ impl TryFrom<user_api::SignUpWithMerchantIdRequest> for NewUserMerchant {
         let company_name = Some(UserCompanyName::new(value.company_name.clone())?);
         let merchant_id = MerchantId::new(value.company_name.clone())?;
         let new_organization = NewUserOrganization::try_from(value)?;
+        let product_type = Some(consts::user::DEFAULT_PRODUCT_TYPE);
 
         Ok(Self {
             company_name,
             merchant_id: id_type::MerchantId::try_from(merchant_id)?,
             new_organization,
+            product_type,
         })
     }
 }
@@ -563,6 +659,7 @@ impl TryFrom<(user_api::CreateInternalUserRequest, id_type::OrganizationId)> for
             company_name: None,
             merchant_id,
             new_organization,
+            product_type: None,
         })
     }
 }
@@ -576,6 +673,7 @@ impl TryFrom<InviteeUserRequestWithInvitedUserToken> for NewUserMerchant {
             company_name: None,
             merchant_id,
             new_organization,
+            product_type: None,
         })
     }
 }
@@ -588,6 +686,7 @@ impl From<(user_api::CreateTenantUserRequest, MerchantAccountIdentifier)> for Ne
             company_name: None,
             merchant_id,
             new_organization,
+            product_type: None,
         }
     }
 }
@@ -607,6 +706,7 @@ impl TryFrom<UserMerchantCreateRequestWithToken> for NewUserMerchant {
         Ok(Self {
             merchant_id,
             company_name: Some(UserCompanyName::new(value.1.company_name.clone())?),
+            product_type: value.1.product_type.clone(),
             new_organization: NewUserOrganization::from(value),
         })
     }
diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs
index b386185eeca..89612fc1892 100644
--- a/crates/router/src/utils/user.rs
+++ b/crates/router/src/utils/user.rs
@@ -286,6 +286,7 @@ pub fn is_sso_auth_type(auth_type: UserAuthType) -> bool {
 pub fn create_merchant_account_request_for_org(
     req: user_api::UserOrgMerchantCreateRequest,
     org: organization::Organization,
+    product_type: common_enums::MerchantProductType,
 ) -> UserResult<api_models::admin::MerchantAccountCreate> {
     let merchant_id = if matches!(env::which(), env::Env::Production) {
         id_type::MerchantId::try_from(domain::MerchantId::new(req.merchant_name.clone().expose())?)?
@@ -315,7 +316,7 @@ pub fn create_merchant_account_request_for_org(
         enable_payment_response_hash: None,
         redirect_to_merchant_with_http_post: None,
         pm_collect_link_config: None,
-        product_type: None,
+        product_type: Some(product_type),
     })
 }
 

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->

As part of the modularity effort, we are introducing user support to enable dashboard interactions with v2 merchant accounts. This allows users to create, list, and switch between merchant accounts within their organization. This also allows users to list profiles and switch profiles when using v2 merchant accounts.

## Key Changes  

### 1. User Support for v2 Merchant Accounts  
Introduced APIs to facilitate user interactions with v2 merchant accounts:  
- **Create Merchant Account API (v1 & v2)**: Allows users to create a merchant account.  
- **List Merchant Accounts API**: Retrieves all v2 merchant accounts associated with a user in an organization.  
- **Switch Merchant Account API**: Enables users to switch to a different v2 merchant account.  

### 2. Modified Response Structure  
- The response for **Create Merchant Account (v1 & v2)** now returns an object instead of just `StatusOk (200)`. The response object includes:  
  - `merchant_id`  
  - `merchant_name`  
  - `product_type`  
  - `version`  
- The **List Merchant Accounts API** now returns a vector of these objects instead of a raw list.  

### 3. Request Structure Update  
- The **Create Merchant Account API** request now includes an optional `product_type` field.  
- This field is currently optional but will be made mandatory once both the frontend and backend are stabilized.  

### 4. Recon
- Added **Merchant Account** permission for Recon related activities.

### 5. User Support for v2 Profiles
Introduced APIs to facilitate user interactions with v2 profiles:  
- **List Profiles API**: Retrieves all v2 profiles associated with a user in a merchant.  
- **Switch Profile API**: Enables users to switch to a different v2 profile in the merchant.

### 6. Modified get_user_details API:
- Modified **get_user_details API** response type to return the version as well, enabling FE to have more context while trying to fetch the merchant account details.
  
### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->
With the modularization of the payments solution, managing multiple merchant accounts becomes essential. This update ensures a seamless user experience for handling v1 and v2 merchant accounts and profiles within an organization.

## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
The flow looks like this:
- Sign Up / Sign In through V1 merchant account --> Create Merchant Account (V1 or V2) --> List Merchant Accounts (V1 and V2)--> Switch Merchant Account (V1 and V2)

### Testing V2 Flow:
Sign In to a V1 merchant account

#### Create v2 Merchant Account:

```bash
curl --location 'http://localhost:8000/v2/user/create_merchant' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWI5ZGZjNWUtZjA2My00YWY3LThkNzUtYmFmOTgzZmU0ODE4IiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF92S09zQU9odGc2S3hpSldlR3Y2YiIsInJvbGVfaWQiOiJvcmdfYWRtaW4iLCJleHAiOjE3NDE3NTk2MjUsIm9yZ19pZCI6Im9yZ19kUENaYU51bmNaRDY5aVZXb0hKcyIsInByb2ZpbGVfaWQiOiJwcm9fQjV4UWZxUWE4dkNCaXpoc2FnTmkiLCJ0ZW5hbnRfaWQiOiJwdWJsaWMifQ.MMJ68FinbhA4dShNvbrOsofU_nTaw1MjGnUrEkmkeOk' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWI5ZGZjNWUtZjA2My00YWY3LThkNzUtYmFmOTgzZmU0ODE4IiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF92S09zQU9odGc2S3hpSldlR3Y2YiIsInJvbGVfaWQiOiJvcmdfYWRtaW4iLCJleHAiOjE3NDE3NTk2MjUsIm9yZ19pZCI6Im9yZ19kUENaYU51bmNaRDY5aVZXb0hKcyIsInByb2ZpbGVfaWQiOiJwcm9fQjV4UWZxUWE4dkNCaXpoc2FnTmkiLCJ0ZW5hbnRfaWQiOiJwdWJsaWMifQ.MMJ68FinbhA4dShNvbrOsofU_nTaw1MjGnUrEkmkeOk' \
--data '{
    "company_name": "TestingMerchantAccountCreateV2",
    "product_type": "vault"
}   '
```

Sample Output:
```json
{
    "merchant_id": "testingmerchantaccountcreatev2_Ku5OxNIapqaot1P8rlQr",
    "merchant_name": "TestingMerchantAccountCreateV2",
    "product_type": "vault",
    "version": "v2"
}
```

#### List V2 Merchant Accounts for User in Org:

```bash
curl --location 'http://localhost:8000/v2/user/list/merchant' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWI5ZGZjNWUtZjA2My00YWY3LThkNzUtYmFmOTgzZmU0ODE4IiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF92S09zQU9odGc2S3hpSldlR3Y2YiIsInJvbGVfaWQiOiJvcmdfYWRtaW4iLCJleHAiOjE3NDE3NTk2MjUsIm9yZ19pZCI6Im9yZ19kUENaYU51bmNaRDY5aVZXb0hKcyIsInByb2ZpbGVfaWQiOiJwcm9fQjV4UWZxUWE4dkNCaXpoc2FnTmkiLCJ0ZW5hbnRfaWQiOiJwdWJsaWMifQ.MMJ68FinbhA4dShNvbrOsofU_nTaw1MjGnUrEkmkeOk' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWI5ZGZjNWUtZjA2My00YWY3LThkNzUtYmFmOTgzZmU0ODE4IiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF92S09zQU9odGc2S3hpSldlR3Y2YiIsInJvbGVfaWQiOiJvcmdfYWRtaW4iLCJleHAiOjE3NDE3NTk2MjUsIm9yZ19pZCI6Im9yZ19kUENaYU51bmNaRDY5aVZXb0hKcyIsInByb2ZpbGVfaWQiOiJwcm9fQjV4UWZxUWE4dkNCaXpoc2FnTmkiLCJ0ZW5hbnRfaWQiOiJwdWJsaWMifQ.MMJ68FinbhA4dShNvbrOsofU_nTaw1MjGnUrEkmkeOk'
```

Sample Output:
```json
[
    {
        "merchant_id": "merchant_vKOsAOhtg6KxiJWeGv6b",
        "merchant_name": "merchant",
        "product_type": "orchestration",
        "version": "v2"
    },
    {
        "merchant_id": "testingmerchantaccountcreatev2_Ku5OxNIapqaot1P8rlQr",
        "merchant_name": "TestingMerchantAccountCreateV2",
        "product_type": "vault",
        "version": "v2"
    }
]
```

#### Switch to V2 merchant Account:
```bash
curl --location 'http://localhost:8000/v2/user/switch/merchant' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWI5ZGZjNWUtZjA2My00YWY3LThkNzUtYmFmOTgzZmU0ODE4IiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF92S09zQU9odGc2S3hpSldlR3Y2YiIsInJvbGVfaWQiOiJvcmdfYWRtaW4iLCJleHAiOjE3NDE3NTk2MjUsIm9yZ19pZCI6Im9yZ19kUENaYU51bmNaRDY5aVZXb0hKcyIsInByb2ZpbGVfaWQiOiJwcm9fQjV4UWZxUWE4dkNCaXpoc2FnTmkiLCJ0ZW5hbnRfaWQiOiJwdWJsaWMifQ.MMJ68FinbhA4dShNvbrOsofU_nTaw1MjGnUrEkmkeOk' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWI5ZGZjNWUtZjA2My00YWY3LThkNzUtYmFmOTgzZmU0ODE4IiwibWVyY2hhbnRfaWQiOiJ0ZXN0aW5nbWVyY2hhbnRhY2NvdW50Y3JlYXRldjJfS3U1T3hOSWFwcWFvdDFQOHJsUXIiLCJyb2xlX2lkIjoib3JnX2FkbWluIiwiZXhwIjoxNzQxNzU5NzYyLCJvcmdfaWQiOiJvcmdfZFBDWmFOdW5jWkQ2OWlWV29ISnMiLCJwcm9maWxlX2lkIjoicHJvX2huN2RqaVpKckJTMnYxS1hjdWIxIiwidGVuYW50X2lkIjoicHVibGljIn0.kxj8dXx4KBXRl-Ayfy6qVJxZF5hLvwqxouJ2ENubm8Q' \
--data '{
    "merchant_id": "testingmerchantaccountcreatev2_Ku5OxNIapqaot1P8rlQr"
}'
```

Sample Output:
```json
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWI5ZGZjNWUtZjA2My00YWY3LThkNzUtYmFmOTgzZmU0ODE4IiwibWVyY2hhbnRfaWQiOiJ0ZXN0aW5nbWVyY2hhbnRhY2NvdW50Y3JlYXRldjJfS3U1T3hOSWFwcWFvdDFQOHJsUXIiLCJyb2xlX2lkIjoib3JnX2FkbWluIiwiZXhwIjoxNzQxNzU5NzYyLCJvcmdfaWQiOiJvcmdfZFBDWmFOdW5jWkQ2OWlWV29ISnMiLCJwcm9maWxlX2lkIjoicHJvX2huN2RqaVpKckJTMnYxS1hjdWIxIiwidGVuYW50X2lkIjoicHVibGljIn0.kxj8dXx4KBXRl-Ayfy6qVJxZF5hLvwqxouJ2ENubm8Q",
    "token_type": "user_info"
}
```

#### Switch to V2 Profile:
```bash
curl --location 'http://localhost:8000/v2/user/switch/profile' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNzhjMjBkNzQtN2FkYy00NmE5LWEzN2UtNGFhOWMzOWE0ZDY4IiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF9DaGxVcVNSakNyc1pQMTI3VmVUeCIsInJvbGVfaWQiOiJvcmdfYWRtaW4iLCJleHAiOjE3NDE4NjIxOTQsIm9yZ19pZCI6Im9yZ19VSWlNWFBvVFRsZVBXOElRU2V3MyIsInByb2ZpbGVfaWQiOiJwcm9fZnhHdTBKZm1mbUVmQVEwUGpiY1ciLCJ0ZW5hbnRfaWQiOiJwdWJsaWMifQ.8QVJGDmL-ybeQ8Rx8On_ACt7_GsFQVkE8-jZtT9fNXg' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNzhjMjBkNzQtN2FkYy00NmE5LWEzN2UtNGFhOWMzOWE0ZDY4IiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF9DaGxVcVNSakNyc1pQMTI3VmVUeCIsInJvbGVfaWQiOiJvcmdfYWRtaW4iLCJleHAiOjE3NDE4NjIzODYsIm9yZ19pZCI6Im9yZ19VSWlNWFBvVFRsZVBXOElRU2V3MyIsInByb2ZpbGVfaWQiOiJwcm9fVWpKWUpGektSaGtvUmlZVHU4ckMiLCJ0ZW5hbnRfaWQiOiJwdWJsaWMifQ.Yo6_JeAN1fv6GTqFZJWpvsNfm3BhxqPOjP2KRYNMH-A' \
--data '{
    "profile_id": "pro_UjJYJFzKRhkoRiYTu8rC"
}'
```

Sample Output:
```json
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNzhjMjBkNzQtN2FkYy00NmE5LWEzN2UtNGFhOWMzOWE0ZDY4IiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF9DaGxVcVNSakNyc1pQMTI3VmVUeCIsInJvbGVfaWQiOiJvcmdfYWRtaW4iLCJleHAiOjE3NDE4NjIzODYsIm9yZ19pZCI6Im9yZ19VSWlNWFBvVFRsZVBXOElRU2V3MyIsInByb2ZpbGVfaWQiOiJwcm9fVWpKWUpGektSaGtvUmlZVHU4ckMiLCJ0ZW5hbnRfaWQiOiJwdWJsaWMifQ.Yo6_JeAN1fv6GTqFZJWpvsNfm3BhxqPOjP2KRYNMH-A",
    "token_type": "user_info"
}
```
#### List V2 Profile for a v2 merchant account:
```bash
curl --location 'http://localhost:8000/v2/user/list/profile' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNzhjMjBkNzQtN2FkYy00NmE5LWEzN2UtNGFhOWMzOWE0ZDY4IiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF9DaGxVcVNSakNyc1pQMTI3VmVUeCIsInJvbGVfaWQiOiJvcmdfYWRtaW4iLCJleHAiOjE3NDE4NjIxOTQsIm9yZ19pZCI6Im9yZ19VSWlNWFBvVFRsZVBXOElRU2V3MyIsInByb2ZpbGVfaWQiOiJwcm9fZnhHdTBKZm1mbUVmQVEwUGpiY1ciLCJ0ZW5hbnRfaWQiOiJwdWJsaWMifQ.8QVJGDmL-ybeQ8Rx8On_ACt7_GsFQVkE8-jZtT9fNXg' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNzhjMjBkNzQtN2FkYy00NmE5LWEzN2UtNGFhOWMzOWE0ZDY4IiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF9DaGxVcVNSakNyc1pQMTI3VmVUeCIsInJvbGVfaWQiOiJvcmdfYWRtaW4iLCJleHAiOjE3NDE4NjIxOTQsIm9yZ19pZCI6Im9yZ19VSWlNWFBvVFRsZVBXOElRU2V3MyIsInByb2ZpbGVfaWQiOiJwcm9fZnhHdTBKZm1mbUVmQVEwUGpiY1ciLCJ0ZW5hbnRfaWQiOiJwdWJsaWMifQ.8QVJGDmL-ybeQ8Rx8On_ACt7_GsFQVkE8-jZtT9fNXg'
```

Sample Output:
```json
[
    {
        "profile_id": "pro_UjJYJFzKRhkoRiYTu8rC",
        "profile_name": "TestingV4-Profile"
    },
    {
        "profile_id": "pro_fxGu0JfmfmEfAQ0PjbcW",
        "profile_name": "default"
    }
]
```

### Testing V1 flow:

Sign in to V1 merchant account

#### Create V1 merchant Account:
```bash
curl --location 'http://localhost:8080/user/create_merchant' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTk0OTE4YzUtOTM0OS00YjEzLWIzZDItZDc0Mzg0OTk3MWNkIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzQxNTg4NTI2Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTc2MTM0OCwib3JnX2lkIjoib3JnX3NYdWNFTGFKSURtVFU2YVlsbmJZIiwicHJvZmlsZV9pZCI6InByb184QnNld0tIRUlaa3ppdUhKa282YiIsInRlbmFudF9pZCI6InB1YmxpYyJ9.lyeIOXMCXVdDaE7ACHxzoFgAZeCY0rjDRFq1rSxu1NE' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTk0OTE4YzUtOTM0OS00YjEzLWIzZDItZDc0Mzg0OTk3MWNkIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzQxNTg4NTI2Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTc2MTM0OCwib3JnX2lkIjoib3JnX3NYdWNFTGFKSURtVFU2YVlsbmJZIiwicHJvZmlsZV9pZCI6InByb184QnNld0tIRUlaa3ppdUhKa282YiIsInRlbmFudF9pZCI6InB1YmxpYyJ9.lyeIOXMCXVdDaE7ACHxzoFgAZeCY0rjDRFq1rSxu1NE' \
--data '{
    "company_name": "TestingV1",
    "product_type": "recovery"
}'
```

Sample Output:
```json
{
    "merchant_id": "merchant_1741588579",
    "merchant_name": "TestingV1",
    "product_type": "recovery",
    "version": "v1"
}
```

#### List V1 Merchant Accounts for User in Org:
```bash
curl --location 'http://localhost:8080/user/list/merchant' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTk0OTE4YzUtOTM0OS00YjEzLWIzZDItZDc0Mzg0OTk3MWNkIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzQxNTg4NTI2Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTc2MTM0OCwib3JnX2lkIjoib3JnX3NYdWNFTGFKSURtVFU2YVlsbmJZIiwicHJvZmlsZV9pZCI6InByb184QnNld0tIRUlaa3ppdUhKa282YiIsInRlbmFudF9pZCI6InB1YmxpYyJ9.lyeIOXMCXVdDaE7ACHxzoFgAZeCY0rjDRFq1rSxu1NE' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTk0OTE4YzUtOTM0OS00YjEzLWIzZDItZDc0Mzg0OTk3MWNkIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzQxNTg4NTI2Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTc2MTM0OCwib3JnX2lkIjoib3JnX3NYdWNFTGFKSURtVFU2YVlsbmJZIiwicHJvZmlsZV9pZCI6InByb184QnNld0tIRUlaa3ppdUhKa282YiIsInRlbmFudF9pZCI6InB1YmxpYyJ9.lyeIOXMCXVdDaE7ACHxzoFgAZeCY0rjDRFq1rSxu1NE'
```

Sample Output:
```json
[
    {
        "merchant_id": "merchant_1741588526",
        "merchant_name": null,
        "product_type": "orchestration",
        "version": "v1"
    },
    {
        "merchant_id": "merchant_1741588579",
        "merchant_name": "TestingV1",
        "product_type": "recovery",
        "version": "v1"
    }
]
```


#### Switch to V1 Merchant Account:
```bash
curl --location 'http://localhost:8080/user/switch/merchant' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTk0OTE4YzUtOTM0OS00YjEzLWIzZDItZDc0Mzg0OTk3MWNkIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzQxNTg4NTI2Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTc2MTM0OCwib3JnX2lkIjoib3JnX3NYdWNFTGFKSURtVFU2YVlsbmJZIiwicHJvZmlsZV9pZCI6InByb184QnNld0tIRUlaa3ppdUhKa282YiIsInRlbmFudF9pZCI6InB1YmxpYyJ9.lyeIOXMCXVdDaE7ACHxzoFgAZeCY0rjDRFq1rSxu1NE' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTk0OTE4YzUtOTM0OS00YjEzLWIzZDItZDc0Mzg0OTk3MWNkIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzQxNTg4NTc5Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTc2MTUwNSwib3JnX2lkIjoib3JnX3NYdWNFTGFKSURtVFU2YVlsbmJZIiwicHJvZmlsZV9pZCI6InByb19PVnQ0OTIzYmdoS0dVRU13Y1JiOSIsInRlbmFudF9pZCI6InB1YmxpYyJ9.5yi6unFpacer1FO_wcxxQIHRls_mjDjPraIHD6BO0ME' \
--data '{
    "merchant_id": "merchant_1741588579"
}'
```

Sample Output:
```json
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTk0OTE4YzUtOTM0OS00YjEzLWIzZDItZDc0Mzg0OTk3MWNkIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzQxNTg4NTc5Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTc2MTUwNSwib3JnX2lkIjoib3JnX3NYdWNFTGFKSURtVFU2YVlsbmJZIiwicHJvZmlsZV9pZCI6InByb19PVnQ0OTIzYmdoS0dVRU13Y1JiOSIsInRlbmFudF9pZCI6InB1YmxpYyJ9.5yi6unFpacer1FO_wcxxQIHRls_mjDjPraIHD6BO0ME",
    "token_type": "user_info"
}
```

#### get_user_details
```bash
curl --location 'https://integ-api.hyperswitch.io/user' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMTlmN2IzZjctMmEwOS00NTllLTllNGItZTRlODcwZDMwMjEwIiwibWVyY2hhbnRfaWQiOiJ0ZXN0Zmxvd3YyX1pMNmV3aFBiZUhIMWU5dmlVc2JoIiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTk0MzQ0Miwib3JnX2lkIjoib3JnX3MzUGVzYTQxRk1aNng3VVJOT0RPIiwicHJvZmlsZV9pZCI6InByb19zbjVFTFV0eGpFSnphN0dnYzhmRCIsInRlbmFudF9pZCI6InB1YmxpYyJ9.EbmlEihGnAM_DW08BAyyeDHFZlBWmCm8Q0bRlAPnEfM' \
--header 'Cookie: login_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMTlmN2IzZjctMmEwOS00NTllLTllNGItZTRlODcwZDMwMjEwIiwibWVyY2hhbnRfaWQiOiJ0ZXN0Zmxvd3YyX1pMNmV3aFBiZUhIMWU5dmlVc2JoIiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTc0MTk0MzQ0Miwib3JnX2lkIjoib3JnX3MzUGVzYTQxRk1aNng3VVJOT0RPIiwicHJvZmlsZV9pZCI6InByb19zbjVFTFV0eGpFSnphN0dnYzhmRCIsInRlbmFudF9pZCI6InB1YmxpYyJ9.EbmlEihGnAM_DW08BAyyeDHFZlBWmCm8Q0bRlAPnEfM'
```

Sample Output:
```json
{
    "merchant_id": "testflowv2_ZL6ewhPbeHH1e9viUsbh",
    "name": "sandeep.kumar",
    "email": "sandeep.kumar@juspay.in",
    "verification_days_left": null,
    "role_id": "org_admin",
    "org_id": "org_s3Pesa41FMZ6x7URNODO",
    "is_two_factor_auth_setup": false,
    "recovery_codes_left": null,
    "profile_id": "pro_sn5ELUtxjEJza7Ggc8fD",
    "entity_type": "organization",
    "theme_id": null,
    "version": "v2"
}
```

## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
