impl SignatureCalculationData for RedsysOperationRequest {
    fn get_merchant_parameters(&self) -> Result<String, Error> {
        self.encode_to_string_of_json()
            .change_context(errors::ConnectorError::RequestEncodingFailed)
    }

    fn get_order_id(&self) -> String {
        self.ds_merchant_order.clone()
    }
}

impl SignatureCalculationData for for RedsysOperationRequest


impl BillingConnectorPaymentsSyncFlowRouterData {
    async fn construct_router_data_for_billing_connector_payment_sync_call(
        state: &SessionState,
        connector_name: &str,
        merchant_connector_account: &hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccount,
        merchant_context: &domain::MerchantContext,
        billing_connector_psync_id: &str,
    ) -> CustomResult<Self, errors::RevenueRecoveryError> {
        let auth_type: types::ConnectorAuthType = helpers::MerchantConnectorAccountType::DbVal(
            Box::new(merchant_connector_account.clone()),
        )
        .get_connector_account_details()
        .parse_value("ConnectorAuthType")
        .change_context(errors::RevenueRecoveryError::BillingConnectorPaymentsSyncFailed)?;

        let connector = common_enums::connector_enums::Connector::from_str(connector_name)
            .change_context(errors::RevenueRecoveryError::BillingConnectorInvoiceSyncFailed)
            .attach_printable("Cannot find connector from the connector_name")?;

        let connector_params =
            hyperswitch_domain_models::connector_endpoints::Connectors::get_connector_params(
                &state.conf.connectors,
                connector,
            )
            .change_context(errors::RevenueRecoveryError::BillingConnectorPaymentsSyncFailed)
            .attach_printable(format!(
                "cannot find connector params for this connector {connector} in this flow",
            ))?;

        let router_data = types::RouterDataV2 {
            flow: PhantomData::<router_flow_types::BillingConnectorPaymentsSync>,
            tenant_id: state.tenant.tenant_id.clone(),
            resource_common_data: flow_common_types::BillingConnectorPaymentsSyncFlowData,
            connector_auth_type: auth_type,
            request: revenue_recovery_request::BillingConnectorPaymentsSyncRequest {
                connector_params,
                billing_connector_psync_id: billing_connector_psync_id.to_string(),
            },
            response: Err(types::ErrorResponse::default()),
        };

        let old_router_data =
            flow_common_types::BillingConnectorPaymentsSyncFlowData::to_old_router_data(
                router_data,
            )
            .change_context(errors::RevenueRecoveryError::BillingConnectorPaymentsSyncFailed)
            .attach_printable(
                "Cannot construct router data for making the billing connector payments api call",
            )?;

        Ok(Self(old_router_data))
    }

    fn inner(self) -> router_types::BillingConnectorPaymentsSyncRouterData {
        self.0
    }
}


pub fn validated_limit(&self) -> u32 {
        self.limit.unwrap_or(50).min(1000)
    }


impl Customers {
    pub fn server(config: routes::AppState) -> Scope {
        web::scope("/customers")
            .app_data(web::Data::new(config))
            .service(web::resource("").route(web::post().to(customer_create)))
            .service(
                web::resource("/{customer_id}")
                    .route(web::get().to(customer_retrieve))
                    .route(web::post().to(customer_update))
                    .route(web::delete().to(customer_delete)),
            )
            .service(
                web::resource("/{customer_id}/payment_methods")
                    .route(web::get().to(list_customer_payment_method_api)),
            )
    }
}


pub struct AVSDetails {
    pub message: String,
}


pub struct FeatureMatrix;


pub async fn do_retry(
    state: &routes::SessionState,
    connector: api::ConnectorData,
    merchant_context: &domain::MerchantContext,
    payout_data: &mut PayoutData,
) -> RouterResult<()> {
    metrics::AUTO_RETRY_PAYOUT_COUNT.add(1, &[]);

    modify_trackers(state, &connector, merchant_context, payout_data).await?;

    Box::pin(call_connector_payout(
        state,
        merchant_context,
        &connector,
        payout_data,
    ))
    .await
}


pub async fn should_call_frm<F, D>(
    merchant_context: &domain::MerchantContext,
    payment_data: &D,
    state: &SessionState,
) -> RouterResult<(
    bool,
    Option<FrmRoutingAlgorithm>,
    Option<common_utils::id_type::ProfileId>,
    Option<FrmConfigsObject>,
)>
where
    F: Send + Clone,
    D: payments::OperationSessionGetters<F> + Send + Sync + Clone,
{
    use common_utils::ext_traits::OptionExt;
    use masking::ExposeInterface;

    let db = &*state.store;
    match merchant_context
        .get_merchant_account()
        .frm_routing_algorithm
        .clone()
    {
        Some(frm_routing_algorithm_value) => {
            let frm_routing_algorithm_struct: FrmRoutingAlgorithm = frm_routing_algorithm_value
                .clone()
                .parse_value("FrmRoutingAlgorithm")
                .change_context(errors::ApiErrorResponse::MissingRequiredField {
                    field_name: "frm_routing_algorithm",
                })
                .attach_printable("Data field not found in frm_routing_algorithm")?;

            let profile_id = payment_data
                .get_payment_intent()
                .profile_id
                .as_ref()
                .get_required_value("profile_id")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("profile_id is not set in payment_intent")?
                .clone();

            #[cfg(feature = "v1")]
            let merchant_connector_account_from_db_option = db
                .find_merchant_connector_account_by_profile_id_connector_name(
                    &state.into(),
                    &profile_id,
                    &frm_routing_algorithm_struct.data,
                    merchant_context.get_merchant_key_store(),
                )
                .await
                .map_err(|error| {
                    logger::error!(
                        "{:?}",
                        error.change_context(
                            errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                                id: merchant_context
                                    .get_merchant_account()
                                    .get_id()
                                    .get_string_repr()
                                    .to_owned(),
                            }
                        )
                    )
                })
                .ok();
            let enabled_merchant_connector_account_from_db_option =
                merchant_connector_account_from_db_option.and_then(|mca| {
                    if mca.disabled.unwrap_or(false) {
                        logger::info!("No eligible connector found for FRM");
                        None
                    } else {
                        Some(mca)
                    }
                });

            #[cfg(feature = "v2")]
            let merchant_connector_account_from_db_option: Option<
                domain::MerchantConnectorAccount,
            > = {
                let _ = key_store;
                let _ = frm_routing_algorithm_struct;
                let _ = profile_id;
                todo!()
            };

            match enabled_merchant_connector_account_from_db_option {
                Some(merchant_connector_account_from_db) => {
                    let frm_configs_option = merchant_connector_account_from_db
                        .frm_configs
                        .ok_or(errors::ApiErrorResponse::MissingRequiredField {
                            field_name: "frm_configs",
                        })
                        .ok();
                    match frm_configs_option {
                        Some(frm_configs_value) => {
                            let frm_configs_struct: Vec<api_models::admin::FrmConfigs> = frm_configs_value
                                .into_iter()
                                .map(|config| { config
                                    .expose()
                                    .parse_value("FrmConfigs")
                                    .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                                            field_name: "frm_configs".to_string(),
                                            expected_format: r#"[{ "gateway": "stripe", "payment_methods": [{ "payment_method": "card","flow": "post"}]}]"#.to_string(),
                                        })
                                })
                                .collect::<Result<Vec<_>, _>>()?;

                            let mut is_frm_connector_enabled = false;
                            let mut is_frm_pm_enabled = false;
                            let connector = payment_data.get_payment_attempt().connector.clone();
                            let filtered_frm_config = frm_configs_struct
                                .iter()
                                .filter(|frm_config| match (&connector, &frm_config.gateway) {
                                    (Some(current_connector), Some(configured_connector)) => {
                                        let is_enabled =
                                            *current_connector == configured_connector.to_string();
                                        if is_enabled {
                                            is_frm_connector_enabled = true;
                                        }
                                        is_enabled
                                    }
                                    (None, _) | (_, None) => true,
                                })
                                .collect::<Vec<_>>();
                            let filtered_payment_methods = filtered_frm_config
                                .iter()
                                .map(|frm_config| {
                                    let filtered_frm_config_by_pm = frm_config
                                        .payment_methods
                                        .iter()
                                        .filter(|frm_config_pm| {
                                            match (
                                                payment_data.get_payment_attempt().payment_method,
                                                frm_config_pm.payment_method,
                                            ) {
                                                (
                                                    Some(current_pm),
                                                    Some(configured_connector_pm),
                                                ) => {
                                                    let is_enabled = current_pm.to_string()
                                                        == configured_connector_pm.to_string();
                                                    if is_enabled {
                                                        is_frm_pm_enabled = true;
                                                    }
                                                    is_enabled
                                                }
                                                (None, _) | (_, None) => true,
                                            }
                                        })
                                        .collect::<Vec<_>>();
                                    filtered_frm_config_by_pm
                                })
                                .collect::<Vec<_>>()
                                .concat();
                            let is_frm_enabled = is_frm_connector_enabled && is_frm_pm_enabled;
                            logger::debug!(
                                "is_frm_connector_enabled {:?}, is_frm_pm_enabled:  {:?}, is_frm_enabled :{:?}",
                                is_frm_connector_enabled,
                                is_frm_pm_enabled,
                                is_frm_enabled
                            );
                            // filtered_frm_config...
                            // Panic Safety: we are first checking if the object is present... only if present, we try to fetch index 0
                            let frm_configs_object = FrmConfigsObject {
                                frm_enabled_gateway: filtered_frm_config
                                    .first()
                                    .and_then(|c| c.gateway),
                                frm_enabled_pm: filtered_payment_methods
                                    .first()
                                    .and_then(|pm| pm.payment_method),
                                // flow type should be consumed from payment_method.flow. To provide backward compatibility, if we don't find it there, we consume it from payment_method.payment_method_types[0].flow_type.
                                frm_preferred_flow_type: filtered_payment_methods
                                    .first()
                                    .and_then(|pm| pm.flow.clone())
                                    .or(filtered_payment_methods.first().and_then(|pm| {
                                        pm.payment_method_types.as_ref().and_then(|pmt| {
                                            pmt.first().map(|pmts| pmts.flow.clone())
                                        })
                                    }))
                                    .ok_or(errors::ApiErrorResponse::InvalidDataFormat {
                                            field_name: "frm_configs".to_string(),
                                            expected_format: r#"[{ "gateway": "stripe", "payment_methods": [{ "payment_method": "card","flow": "post"}]}]"#.to_string(),
                                    })?,
                            };
                            logger::debug!(
                                "frm_routing_configs: {:?} {:?} {:?} {:?}",
                                frm_routing_algorithm_struct,
                                profile_id,
                                frm_configs_object,
                                is_frm_enabled
                            );
                            Ok((
                                is_frm_enabled,
                                Some(frm_routing_algorithm_struct),
                                Some(profile_id),
                                Some(frm_configs_object),
                            ))
                        }
                        None => {
                            logger::error!("Cannot find frm_configs for FRM provider");
                            Ok((false, None, None, None))
                        }
                    }
                }
                None => {
                    logger::error!("Cannot find merchant connector account for FRM provider");
                    Ok((false, None, None, None))
                }
            }
        }
        _ => Ok((false, None, None, None)),
    }
}


pub async fn update_with_payment_method_id(
        self,
        conn: &PgPooledConn,
        payment_method: payment_method::PaymentMethodUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::payment_method_id.eq(self.payment_method_id.to_owned()),
            payment_method,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }


pub struct ProfileAcquirerResponse {
    /// The unique identifier of the profile acquirer
    #[schema(value_type= String,example = "pro_acq_LCRdERuylQvNQ4qh3QE0")]
    pub profile_acquirer_id: common_utils::id_type::ProfileAcquirerId,
    /// The merchant id assigned by the acquirer
    #[schema(value_type= String,example = "M123456789")]
    pub acquirer_assigned_merchant_id: String,
    /// Merchant name
    #[schema(value_type= String,example = "NewAge Retailer")]
    pub merchant_name: String,
    /// Network provider
    #[schema(value_type= String,example = "VISA")]
    pub network: common_enums::enums::CardNetwork,
    /// Acquirer bin
    #[schema(value_type= String,example = "456789")]
    pub acquirer_bin: String,
    /// Acquirer ica provided by acquirer
    #[schema(value_type= Option<String>,example = "401288")]
    pub acquirer_ica: Option<String>,
    /// Fraud rate for the particular acquirer configuration
    #[schema(value_type= f64,example = 0.01)]
    pub acquirer_fraud_rate: f64,
    /// Parent profile id to link the acquirer account with
    #[schema(value_type= String,example = "pro_ky0yNyOXXlA5hF8JzE5q")]
    pub profile_id: common_utils::id_type::ProfileId,
}


pub struct CheckbookErrorResponse {
    pub status_code: u16,
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
}


File: crates/config_importer/src/main.rs

mod cli;

use std::io::{BufWriter, Write};

use anyhow::Context;

/// The separator used in environment variable names.
const ENV_VAR_SEPARATOR: &str = "__";

#[cfg(not(feature = "preserve_order"))]
type EnvironmentVariableMap = std::collections::HashMap<String, String>;

#[cfg(feature = "preserve_order")]
type EnvironmentVariableMap = indexmap::IndexMap<String, String>;

fn main() -> anyhow::Result<()> {
    let args = <cli::Args as clap::Parser>::parse();

    // Read input TOML file
    let toml_contents =
        std::fs::read_to_string(args.input_file).context("Failed to read input file")?;
    let table = toml_contents
        .parse::<toml::Table>()
        .context("Failed to parse TOML file contents")?;

    // Parse TOML file contents to a `HashMap` of environment variable name and value pairs
    let env_vars = table
        .iter()
        .flat_map(|(key, value)| process_toml_value(&args.prefix, key, value))
        .collect::<EnvironmentVariableMap>();

    let writer: BufWriter<Box<dyn Write>> = match args.output_file {
        // Write to file if output file is specified
        Some(file) => BufWriter::new(Box::new(
            std::fs::OpenOptions::new()
                .create(true)
                .write(true)
                .truncate(true)
                .open(file)
                .context("Failed to open output file")?,
        )),
        // Write to stdout otherwise
        None => BufWriter::new(Box::new(std::io::stdout().lock())),
    };

    // Write environment variables in specified format
    match args.output_format {
        cli::OutputFormat::KubernetesJson => {
            let k8s_env_vars = env_vars
                .into_iter()
                .map(|(name, value)| KubernetesEnvironmentVariable { name, value })
                .collect::<Vec<_>>();
            serde_json::to_writer_pretty(writer, &k8s_env_vars)
                .context("Failed to serialize environment variables as JSON")?
        }
    }

    Ok(())
}

fn process_toml_value(
    prefix: impl std::fmt::Display + Clone,
    key: impl std::fmt::Display + Clone,
    value: &toml::Value,
) -> Vec<(String, String)> {
    let key_with_prefix = format!("{prefix}{ENV_VAR_SEPARATOR}{key}").to_ascii_uppercase();

    match value {
        toml::Value::String(s) => vec![(key_with_prefix, s.to_owned())],
        toml::Value::Integer(i) => vec![(key_with_prefix, i.to_string())],
        toml::Value::Float(f) => vec![(key_with_prefix, f.to_string())],
        toml::Value::Boolean(b) => vec![(key_with_prefix, b.to_string())],
        toml::Value::Datetime(dt) => vec![(key_with_prefix, dt.to_string())],
        toml::Value::Array(values) => {
            if values.is_empty() {
                return vec![(key_with_prefix, String::new())];
            }

            // This logic does not support / account for arrays of tables or arrays of arrays.
            let (_processed_keys, processed_values) = values
                .iter()
                .flat_map(|v| process_toml_value(prefix.clone(), key.clone(), v))
                .unzip::<_, _, Vec<String>, Vec<String>>();
            vec![(key_with_prefix, processed_values.join(","))]
        }
        toml::Value::Table(map) => map
            .into_iter()
            .flat_map(|(k, v)| process_toml_value(key_with_prefix.clone(), k, v))
            .collect(),
    }
}

/// The Kubernetes environment variable structure containing a name and a value.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct KubernetesEnvironmentVariable {
    name: String,
    value: String,
}


pub struct CreateFileRequest {
    pub file: Vec<u8>,
    pub file_name: Option<String>,
    pub file_size: i32,
    #[serde_as(as = "serde_with::DisplayFromStr")]
    pub file_type: mime::Mime,
    pub purpose: FilePurpose,
    pub dispute_id: Option<String>,
}


pub struct PaymentsRequest {
    #[serde(flatten)]
    pub payment_data: WorldlinePaymentMethod,
    pub order: Order,
    pub shipping: Option<Shipping>,
}


File: crates/router/src/routes/user.rs
 Public functions: 50

pub mod theme;

use actix_web::{web, HttpRequest, HttpResponse};
#[cfg(feature = "dummy_connector")]
use api_models::user::sample_data::SampleDataRequest;
use api_models::{
    errors::types::ApiErrorResponse,
    user::{self as user_api},
};
use common_enums::TokenPurpose;
use common_utils::errors::ReportSwitchExt;
use router_env::Flow;

use super::AppState;
use crate::{
    core::{api_locking, user as user_core},
    services::{
        api,
        authentication::{self as auth},
        authorization::permissions::Permission,
    },
    utils::user::dashboard_metadata::{parse_string_to_enums, set_ip_address_if_required},
};

pub async fn get_user_details(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
    let flow = Flow::GetUserDetails;
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        (),
        |state, user, _, _| user_core::get_user_details(state, user),
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "email")]
pub async fn user_signup_with_merchant_id(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::SignUpWithMerchantIdRequest>,
    query: web::Query<user_api::AuthIdAndThemeIdQueryParam>,
) -> HttpResponse {
    let flow = Flow::UserSignUpWithMerchantId;
    let req_payload = json_payload.into_inner();
    let query_params = query.into_inner();
    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &http_req,
        req_payload.clone(),
        |state, _, req_body, _| {
            user_core::signup_with_merchant_id(
                state,
                req_body,
                query_params.auth_id.clone(),
                query_params.theme_id.clone(),
            )
        },
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn user_signup(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::SignUpRequest>,
) -> HttpResponse {
    let flow = Flow::UserSignUp;
    let req_payload = json_payload.into_inner();
    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &http_req,
        req_payload.clone(),
        |state, _: (), req_body, _| async move {
            user_core::signup_token_only_flow(state, req_body).await
        },
        &auth::NoAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn user_signin(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::SignInRequest>,
) -> HttpResponse {
    let flow = Flow::UserSignIn;
    let req_payload = json_payload.into_inner();
    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &http_req,
        req_payload.clone(),
        |state, _: (), req_body, _| async move {
            user_core::signin_token_only_flow(state, req_body).await
        },
        &auth::NoAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "email")]
pub async fn user_connect_account(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::ConnectAccountRequest>,
    query: web::Query<user_api::AuthIdAndThemeIdQueryParam>,
) -> HttpResponse {
    let flow = Flow::UserConnectAccount;
    let req_payload = json_payload.into_inner();
    let query_params = query.into_inner();
    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &http_req,
        req_payload.clone(),
        |state, _: (), req_body, _| {
            user_core::connect_account(
                state,
                req_body,
                query_params.auth_id.clone(),
                query_params.theme_id.clone(),
            )
        },
        &auth::NoAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn signout(state: web::Data<AppState>, http_req: HttpRequest) -> HttpResponse {
    let flow = Flow::Signout;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &http_req,
        (),
        |state, user, _, _| user_core::signout(state, user),
        &auth::AnyPurposeOrLoginTokenAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn change_password(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::ChangePasswordRequest>,
) -> HttpResponse {
    let flow = Flow::ChangePassword;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &http_req,
        json_payload.into_inner(),
        |state, user, req, _| user_core::change_password(state, req, user),
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn set_dashboard_metadata(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::dashboard_metadata::SetMetaDataRequest>,
) -> HttpResponse {
    let flow = Flow::SetDashboardMetadata;
    let mut payload = json_payload.into_inner();

    if let Err(e) = ReportSwitchExt::<(), ApiErrorResponse>::switch(set_ip_address_if_required(
        &mut payload,
        req.headers(),
    )) {
        return api::log_and_return_error_response(e);
    }

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        user_core::dashboard_metadata::set_metadata,
        &auth::JWTAuth {
            permission: Permission::ProfileAccountWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn get_multiple_dashboard_metadata(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<user_api::dashboard_metadata::GetMultipleMetaDataRequest>,
) -> HttpResponse {
    let flow = Flow::GetMultipleDashboardMetadata;
    let payload = match ReportSwitchExt::<_, ApiErrorResponse>::switch(parse_string_to_enums(
        query.into_inner().keys,
    )) {
        Ok(payload) => payload,
        Err(e) => {
            return api::log_and_return_error_response(e);
        }
    };
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        user_core::dashboard_metadata::get_multiple_metadata,
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn internal_user_signup(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::CreateInternalUserRequest>,
) -> HttpResponse {
    let flow = Flow::InternalUserSignup;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &http_req,
        json_payload.into_inner(),
        |state, _, req, _| user_core::create_internal_user(state, req),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn create_tenant_user(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::CreateTenantUserRequest>,
) -> HttpResponse {
    let flow = Flow::TenantUserCreate;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &http_req,
        json_payload.into_inner(),
        |state, _, req, _| user_core::create_tenant_user(state, req),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
pub async fn create_platform(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::PlatformAccountCreateRequest>,
) -> HttpResponse {
    let flow = Flow::CreatePlatformAccount;
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, user: auth::UserFromToken, json_payload, _| {
            user_core::create_platform_account(state, user, json_payload)
        },
        &auth::JWTAuth {
            permission: Permission::OrganizationAccountWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
pub async fn user_org_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::UserOrgMerchantCreateRequest>,
) -> HttpResponse {
    let flow = Flow::UserOrgMerchantCreate;
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, _auth: auth::UserFromToken, json_payload, _| {
            user_core::create_org_merchant_for_user(state, json_payload)
        },
        &auth::JWTAuth {
            permission: Permission::TenantAccountWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn user_merchant_account_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::UserMerchantCreate>,
) -> HttpResponse {
    let flow = Flow::UserMerchantAccountCreate;
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, auth: auth::UserFromToken, json_payload, _| {
            user_core::create_merchant_account(state, auth, json_payload)
        },
        &auth::JWTAuth {
            permission: Permission::OrganizationAccountWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(all(feature = "dummy_connector", feature = "v1"))]
pub async fn generate_sample_data(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    payload: web::Json<SampleDataRequest>,
) -> impl actix_web::Responder {
    use crate::core::user::sample_data;

    let flow = Flow::GenerateSampleData;
    Box::pin(api::server_wrap(
        flow,
        state,
        &http_req,
        payload.into_inner(),
        sample_data::generate_sample_data_for_user,
        &auth::JWTAuth {
            permission: Permission::MerchantPaymentWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(all(feature = "dummy_connector", feature = "v1"))]
pub async fn delete_sample_data(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    payload: web::Json<SampleDataRequest>,
) -> impl actix_web::Responder {
    use crate::core::user::sample_data;

    let flow = Flow::DeleteSampleData;
    Box::pin(api::server_wrap(
        flow,
        state,
        &http_req,
        payload.into_inner(),
        sample_data::delete_sample_data_for_user,
        &auth::JWTAuth {
            permission: Permission::MerchantAccountWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn list_user_roles_details(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<user_api::GetUserRoleDetailsRequest>,
) -> HttpResponse {
    let flow = Flow::GetUserRoleDetails;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload.into_inner(),
        user_core::list_user_roles_details,
        &auth::JWTAuth {
            permission: Permission::ProfileUserRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn rotate_password(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<user_api::RotatePasswordRequest>,
) -> HttpResponse {
    let flow = Flow::RotatePassword;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload.into_inner(),
        user_core::rotate_password,
        &auth::SinglePurposeJWTAuth(TokenPurpose::ForceSetPassword),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "email")]
pub async fn forgot_password(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<user_api::ForgotPasswordRequest>,
    query: web::Query<user_api::AuthIdAndThemeIdQueryParam>,
) -> HttpResponse {
    let flow = Flow::ForgotPassword;
    let query_params = query.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload.into_inner(),
        |state, _: (), payload, _| {
            user_core::forgot_password(
                state,
                payload,
                query_params.auth_id.clone(),
                query_params.theme_id.clone(),
            )
        },
        &auth::NoAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "email")]
pub async fn reset_password(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<user_api::ResetPasswordRequest>,
) -> HttpResponse {
    let flow = Flow::ResetPassword;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload.into_inner(),
        |state, user, payload, _| user_core::reset_password_token_only_flow(state, user, payload),
        &auth::SinglePurposeJWTAuth(TokenPurpose::ResetPassword),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn invite_multiple_user(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<Vec<user_api::InviteUserRequest>>,
    auth_id_query_param: web::Query<user_api::AuthIdAndThemeIdQueryParam>,
) -> HttpResponse {
    let flow = Flow::InviteMultipleUser;
    let auth_id = auth_id_query_param.into_inner().auth_id;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload.into_inner(),
        |state, user, payload, req_state| {
            user_core::invite_multiple_user(state, user, payload, req_state, auth_id.clone())
        },
        &auth::JWTAuth {
            permission: Permission::ProfileUserWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "email")]
pub async fn resend_invite(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<user_api::ReInviteUserRequest>,
    query: web::Query<user_api::AuthIdAndThemeIdQueryParam>,
) -> HttpResponse {
    let flow = Flow::ReInviteUser;
    let auth_id = query.into_inner().auth_id;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload.into_inner(),
        |state, user, req_payload, _| {
            user_core::resend_invite(state, user, req_payload, auth_id.clone())
        },
        &auth::JWTAuth {
            permission: Permission::ProfileUserWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "email")]
pub async fn accept_invite_from_email(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<user_api::AcceptInviteFromEmailRequest>,
) -> HttpResponse {
    let flow = Flow::AcceptInviteFromEmail;
    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &req,
        payload.into_inner(),
        |state, user, req_payload, _| {
            user_core::accept_invite_from_email_token_only_flow(state, user, req_payload)
        },
        &auth::SinglePurposeJWTAuth(TokenPurpose::AcceptInvitationFromEmail),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "email")]
pub async fn verify_email(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::VerifyEmailRequest>,
) -> HttpResponse {
    let flow = Flow::VerifyEmail;
    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &http_req,
        json_payload.into_inner(),
        |state, user, req_payload, _| {
            user_core::verify_email_token_only_flow(state, user, req_payload)
        },
        &auth::SinglePurposeJWTAuth(TokenPurpose::VerifyEmail),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "email")]
pub async fn verify_email_request(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::SendVerifyEmailRequest>,
    query: web::Query<user_api::AuthIdAndThemeIdQueryParam>,
) -> HttpResponse {
    let flow = Flow::VerifyEmailRequest;
    let query_params = query.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &http_req,
        json_payload.into_inner(),
        |state, _: (), req_body, _| {
            user_core::send_verification_mail(
                state,
                req_body,
                query_params.auth_id.clone(),
                query_params.theme_id.clone(),
            )
        },
        &auth::NoAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn update_user_account_details(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::UpdateUserAccountDetailsRequest>,
) -> HttpResponse {
    let flow = Flow::UpdateUserAccountDetails;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        user_core::update_user_details,
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "email")]
pub async fn user_from_email(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::UserFromEmailRequest>,
) -> HttpResponse {
    let flow = Flow::UserFromEmail;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, _: (), req_body, _| user_core::user_from_email(state, req_body),
        &auth::NoAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn totp_begin(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
    let flow = Flow::TotpBegin;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        (),
        |state, user, _, _| user_core::begin_totp(state, user),
        &auth::SinglePurposeJWTAuth(TokenPurpose::TOTP),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn totp_reset(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
    let flow = Flow::TotpReset;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        (),
        |state, user, _, _| user_core::reset_totp(state, user),
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn totp_verify(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::VerifyTotpRequest>,
) -> HttpResponse {
    let flow = Flow::TotpVerify;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, user, req_body, _| user_core::verify_totp(state, user, req_body),
        &auth::SinglePurposeOrLoginTokenAuth(TokenPurpose::TOTP),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn verify_recovery_code(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::VerifyRecoveryCodeRequest>,
) -> HttpResponse {
    let flow = Flow::RecoveryCodeVerify;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, user, req_body, _| user_core::verify_recovery_code(state, user, req_body),
        &auth::SinglePurposeOrLoginTokenAuth(TokenPurpose::TOTP),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn totp_update(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::VerifyTotpRequest>,
) -> HttpResponse {
    let flow = Flow::TotpUpdate;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, user, req_body, _| user_core::update_totp(state, user, req_body),
        &auth::SinglePurposeOrLoginTokenAuth(TokenPurpose::TOTP),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn generate_recovery_codes(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
    let flow = Flow::RecoveryCodesGenerate;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        (),
        |state, user, _, _| user_core::generate_recovery_codes(state, user),
        &auth::SinglePurposeOrLoginTokenAuth(TokenPurpose::TOTP),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn terminate_two_factor_auth(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<user_api::SkipTwoFactorAuthQueryParam>,
) -> HttpResponse {
    let flow = Flow::TerminateTwoFactorAuth;
    let skip_two_factor_auth = query.into_inner().skip_two_factor_auth.unwrap_or(false);

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        (),
        |state, user, _, _| user_core::terminate_two_factor_auth(state, user, skip_two_factor_auth),
        &auth::SinglePurposeJWTAuth(TokenPurpose::TOTP),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn check_two_factor_auth_status(
    state: web::Data<AppState>,
    req: HttpRequest,
) -> HttpResponse {
    let flow = Flow::TwoFactorAuthStatus;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        (),
        |state, user, _, _| user_core::check_two_factor_auth_status(state, user),
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn check_two_factor_auth_status_with_attempts(
    state: web::Data<AppState>,
    req: HttpRequest,
) -> HttpResponse {
    let flow = Flow::TwoFactorAuthStatus;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        (),
        |state, user, _, _| user_core::check_two_factor_auth_status_with_attempts(state, user),
        &auth::SinglePurposeOrLoginTokenAuth(TokenPurpose::TOTP),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
pub async fn get_sso_auth_url(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<user_api::GetSsoAuthUrlRequest>,
) -> HttpResponse {
    let flow = Flow::GetSsoAuthUrl;
    let payload = query.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload,
        |state, _: (), req, _| user_core::get_sso_auth_url(state, req),
        &auth::NoAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn sso_sign(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::SsoSignInRequest>,
) -> HttpResponse {
    let flow = Flow::SignInWithSso;
    let payload = json_payload.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload,
        |state, user: Option<auth::UserFromSinglePurposeToken>, payload, _| {
            user_core::sso_sign(state, payload, user)
        },
        auth::auth_type(
            &auth::NoAuth,
            &auth::SinglePurposeJWTAuth(TokenPurpose::SSO),
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn create_user_authentication_method(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::CreateUserAuthenticationMethodRequest>,
) -> HttpResponse {
    let flow = Flow::CreateUserAuthenticationMethod;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, _, req_body, _| user_core::create_user_authentication_method(state, req_body),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn update_user_authentication_method(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::UpdateUserAuthenticationMethodRequest>,
) -> HttpResponse {
    let flow = Flow::UpdateUserAuthenticationMethod;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, _, req_body, _| user_core::update_user_authentication_method(state, req_body),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn list_user_authentication_methods(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<user_api::GetUserAuthenticationMethodsRequest>,
) -> HttpResponse {
    let flow = Flow::ListUserAuthenticationMethods;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        query.into_inner(),
        |state, _: (), req, _| user_core::list_user_authentication_methods(state, req),
        &auth::NoAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn terminate_auth_select(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::AuthSelectRequest>,
) -> HttpResponse {
    let flow = Flow::AuthSelect;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, user, req, _| user_core::terminate_auth_select(state, user, req),
        &auth::SinglePurposeJWTAuth(TokenPurpose::AuthSelect),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn transfer_user_key(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<user_api::UserKeyTransferRequest>,
) -> HttpResponse {
    let flow = Flow::UserTransferKey;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload.into_inner(),
        |state, _, req, _| user_core::transfer_user_key_store_keymanager(state, req),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn list_orgs_for_user(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
    let flow = Flow::ListOrgForUser;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        (),
        |state, user_from_token, _, _| user_core::list_orgs_for_user(state, user_from_token),
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn list_merchants_for_user_in_org(
    state: web::Data<AppState>,
    req: HttpRequest,
) -> HttpResponse {
    let flow = Flow::ListMerchantsForUserInOrg;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        (),
        |state, user_from_token, _, _| {
            user_core::list_merchants_for_user_in_org(state, user_from_token)
        },
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn list_profiles_for_user_in_org_and_merchant(
    state: web::Data<AppState>,
    req: HttpRequest,
) -> HttpResponse {
    let flow = Flow::ListProfileForUserInOrgAndMerchant;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        (),
        |state, user_from_token, _, _| {
            user_core::list_profiles_for_user_in_org_and_merchant_account(state, user_from_token)
        },
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn switch_org_for_user(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::SwitchOrganizationRequest>,
) -> HttpResponse {
    let flow = Flow::SwitchOrg;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &http_req,
        json_payload.into_inner(),
        |state, user, req, _| user_core::switch_org_for_user(state, req, user),
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn switch_merchant_for_user_in_org(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::SwitchMerchantRequest>,
) -> HttpResponse {
    let flow = Flow::SwitchMerchantV2;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &http_req,
        json_payload.into_inner(),
        |state, user, req, _| user_core::switch_merchant_for_user_in_org(state, req, user),
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn switch_profile_for_user_in_org_and_merchant(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::SwitchProfileRequest>,
) -> HttpResponse {
    let flow = Flow::SwitchProfile;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &http_req,
        json_payload.into_inner(),
        |state, user, req, _| {
            user_core::switch_profile_for_user_in_org_and_merchant(state, req, user)
        },
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
pub async fn clone_connector(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::CloneConnectorRequest>,
) -> HttpResponse {
    let flow = Flow::CloneConnector;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, _: auth::UserFromToken, req, _| user_core::clone_connector(state, req),
        &auth::JWTAuth {
            permission: Permission::MerchantInternalConnectorWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn update(
        self,
        conn: &PgPooledConn,
        payout_update: PayoutsUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::payout_id
                .eq(self.payout_id.to_owned())
                .and(dsl::merchant_id.eq(self.merchant_id.to_owned())),
            PayoutsUpdateInternal::from(payout_update),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            Ok(mut payouts) => payouts
                .pop()
                .ok_or(error_stack::report!(errors::DatabaseError::NotFound)),
        }
    }


pub(crate) fn get_status(status: &str) -> IncomingWebhookEvent {
    match status {
        "dispute_opened" => IncomingWebhookEvent::DisputeOpened,
        "dispute_lost" => IncomingWebhookEvent::DisputeLost,
        "dispute_won" => IncomingWebhookEvent::DisputeWon,
        "dispute_accepted" | "dispute_auto_accepted" => IncomingWebhookEvent::DisputeAccepted,
        "dispute_expired" => IncomingWebhookEvent::DisputeExpired,
        "dispute_disputed" => IncomingWebhookEvent::DisputeChallenged,
        _ => IncomingWebhookEvent::EventNotSupported,
    }
}


pub struct AirwallexDisputeObject {
    pub payment_intent_id: String,
    pub dispute_amount: MinorUnit,
    pub dispute_currency: enums::Currency,
    pub stage: AirwallexDisputeStage,
    pub dispute_id: String,
    pub dispute_reason_type: Option<String>,
    pub dispute_original_reason_code: Option<String>,
    pub status: String,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub created_at: Option<PrimitiveDateTime>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub updated_at: Option<PrimitiveDateTime>,
}


pub async fn connector_update(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::MerchantConnectorAccountId,
    )


File: crates/router/src/services/authentication/decision.rs
 Public functions: 5
 Public structs: 2

use common_utils::{errors::CustomResult, request::RequestContent};
use masking::{ErasedMaskSerialize, Secret};
use serde::Serialize;
use storage_impl::errors::ApiClientError;

use crate::{
    core::metrics,
    routes::{app::settings::DecisionConfig, SessionState},
};

// # Consts
//

const DECISION_ENDPOINT: &str = "/rule";
const RULE_ADD_METHOD: common_utils::request::Method = common_utils::request::Method::Post;
const RULE_DELETE_METHOD: common_utils::request::Method = common_utils::request::Method::Delete;

pub const REVOKE: &str = "REVOKE";
pub const ADD: &str = "ADD";

// # Types
//

/// [`RuleRequest`] is a request body used to register a new authentication method in the proxy.
#[derive(Debug, Serialize)]
pub struct RuleRequest {
    /// [`tag`] similar to a partition key, which can be used by the decision service to tag rules
    /// by partitioning identifiers. (e.g. `tenant_id`)
    pub tag: String,
    /// [`variant`] is the type of authentication method to be registered.
    #[serde(flatten)]
    pub variant: AuthRuleType,
    /// [`expiry`] is the time **in seconds** after which the rule should be removed
    pub expiry: Option<u64>,
}

#[derive(Debug, Serialize)]
pub struct RuleDeleteRequest {
    pub tag: String,
    #[serde(flatten)]
    pub variant: AuthType,
}

#[derive(Debug, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AuthType {
    /// [`ApiKey`] is an authentication method that uses an API key. This is used with [`ApiKey`]
    ApiKey { api_key: Secret<String> },
}

#[derive(Debug, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AuthRuleType {
    /// [`ApiKey`] is an authentication method that uses an API key. This is used with [`ApiKey`]
    /// and [`PublishableKey`] authentication methods.
    ApiKey {
        api_key: Secret<String>,
        identifiers: Identifiers,
    },
}

#[allow(clippy::enum_variant_names)]
#[derive(Debug, Serialize, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Identifiers {
    /// [`ApiKey`] is an authentication method that uses an API key. This is used with [`ApiKey`]
    ApiKey {
        merchant_id: common_utils::id_type::MerchantId,
        key_id: common_utils::id_type::ApiKeyId,
    },
    /// [`PublishableKey`] is an authentication method that uses a publishable key. This is used with [`PublishableKey`]
    PublishableKey { merchant_id: String },
}

// # Decision Service
//

pub async fn add_api_key(
    state: &SessionState,
    api_key: Secret<String>,
    merchant_id: common_utils::id_type::MerchantId,
    key_id: common_utils::id_type::ApiKeyId,
    expiry: Option<u64>,
) -> CustomResult<(), ApiClientError> {
    let decision_config = if let Some(config) = &state.conf.decision {
        config
    } else {
        return Ok(());
    };

    let rule = RuleRequest {
        tag: state.tenant.schema.clone(),
        expiry,
        variant: AuthRuleType::ApiKey {
            api_key,
            identifiers: Identifiers::ApiKey {
                merchant_id,
                key_id,
            },
        },
    };

    call_decision_service(state, decision_config, rule, RULE_ADD_METHOD).await
}

pub async fn add_publishable_key(
    state: &SessionState,
    api_key: Secret<String>,
    merchant_id: common_utils::id_type::MerchantId,
    expiry: Option<u64>,
) -> CustomResult<(), ApiClientError> {
    let decision_config = if let Some(config) = &state.conf.decision {
        config
    } else {
        return Ok(());
    };

    let rule = RuleRequest {
        tag: state.tenant.schema.clone(),
        expiry,
        variant: AuthRuleType::ApiKey {
            api_key,
            identifiers: Identifiers::PublishableKey {
                merchant_id: merchant_id.get_string_repr().to_owned(),
            },
        },
    };

    call_decision_service(state, decision_config, rule, RULE_ADD_METHOD).await
}

async fn call_decision_service<T: ErasedMaskSerialize + Send + 'static>(
    state: &SessionState,
    decision_config: &DecisionConfig,
    rule: T,
    method: common_utils::request::Method,
) -> CustomResult<(), ApiClientError> {
    let mut request = common_utils::request::Request::new(
        method,
        &(decision_config.base_url.clone() + DECISION_ENDPOINT),
    );

    request.set_body(RequestContent::Json(Box::new(rule)));
    request.add_default_headers();

    let response = state
        .api_client
        .send_request(state, request, None, false)
        .await;

    match response {
        Err(error) => {
            router_env::error!("Failed while calling the decision service: {:?}", error);
            Err(error)
        }
        Ok(response) => {
            router_env::info!("Decision service response: {:?}", response);
            Ok(())
        }
    }
}

pub async fn revoke_api_key(
    state: &SessionState,
    api_key: Secret<String>,
) -> CustomResult<(), ApiClientError> {
    let decision_config = if let Some(config) = &state.conf.decision {
        config
    } else {
        return Ok(());
    };

    let rule = RuleDeleteRequest {
        tag: state.tenant.schema.clone(),
        variant: AuthType::ApiKey { api_key },
    };

    call_decision_service(state, decision_config, rule, RULE_DELETE_METHOD).await
}

/// Safety: i64::MAX < u64::MAX
#[allow(clippy::as_conversions)]
pub fn convert_expiry(expiry: time::PrimitiveDateTime) -> u64 {
    let now = common_utils::date_time::now();
    let duration = expiry - now;
    let output = duration.whole_seconds();

    match output {
        i64::MIN..=0 => 0,
        _ => output as u64,
    }
}

pub fn spawn_tracked_job<E, F>(future: F, request_type: &'static str)
where
    E: std::fmt::Debug,
    F: futures::Future<Output = Result<(), E>> + Send + 'static,
{
    metrics::API_KEY_REQUEST_INITIATED
        .add(1, router_env::metric_attributes!(("type", request_type)));
    tokio::spawn(async move {
        match future.await {
            Ok(_) => {
                metrics::API_KEY_REQUEST_COMPLETED
                    .add(1, router_env::metric_attributes!(("type", request_type)));
            }
            Err(e) => {
                router_env::error!("Error in tracked job: {:?}", e);
            }
        }
    });
}


Documentation: api-reference/essentials/error_codes.mdx
# Type: Doc File

---
title: Error Codes
---

Hyperswitch uses error codes, types, and messages to communicate errors during API calls. There are two main types of error codes: Error Codes and Unified Error Codes.
1. **Error Codes** refer to the error code sent by the connector.
2. **Unified Error Codes** refer to the generic error code sent by the Hyperswitch server, based on the connector's error code. Hyperswitch groups the different error codes from connectors into more generic Unified Error Codes for structured relay of PSP errors, helping merchants derive patterns and determine the next steps for ongoing transactions.

There are four types of Error Codes and five types of Unified Error Codes.

**1. Error Codes:**
The section below contains all the error codes and their corresponding error messages.
| Error Code | Type                  | Description                                                  |
| ---------- | --------------------- | ------------------------------------------------------------ |
| IR         | Invalid Request Error | Error caused due to invalid fields and values in API request |
| CE         | Connector Error       | Errors originating from connector’s end                      |
| HE         | Hyperswitch Error     | Errors originating from Hyperswitch’s end                    |
| WE         | Webhook Error         | Errors related to Webhooks                |

| Error Codes | HTTP Status codes                     | Error Type            | Error message                                                                                | Error Handling                                                                                |
| ----------- | ------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| IR_00       | 501                                   | server_not_available  | This API is under development and will be made available soon                                                                                | No action required. If you require this feature, please reach out to Hyperswitch support                                                                                |
| IR_01       | 401                                   | invalid_request_error | API key not provided or invalid API key used. Provide the API key in the Authorization header using api-key (e.g api-key: API_KEY) or create a new API key from the dashboard | Provide the API key in the Authorization header using api-key (e.g api-key: API_KEY) or create a new API key from the dashboard                                                                |
| IR_02       | 404                                   | invalid_request_error | Unrecognized request URL                                                                                | Please recheck and enter the correct request URL. Refer to our API documentation                                                                                |
| IR_03       | 405                                   | invalid_request_error | The HTTP method is not applicable for this API                                                                                | Please recheck the HTTP method used in the request. Refer to our API documentation                                                                                |
| IR_04       | 400                                   | invalid_request_error | Missing required param: “field_name”                                                                                | Please pass the missing required parameter. Refer to our API documentation                                                                                |
| IR_05       | 422                                   | invalid_request_error | “field_name” contains invalid data. Expected format is “expected_format”                                                                                | Please pass the data in the expected format. Refer to our API documentation                                                                                |
| IR_06       | 400                                   | invalid_request_error | “message”                                                                                | Refer to our API documentation for required fields and format                                                                                |
| IR_07       | 400                                   | invalid_request_error | Invalid value provided: “field_name”                                                                                | Provide a valid value for the required fields in the expected format. Refer to our API documentation                                                                                |
| IR_08       | 400                                   | invalid_request_error | Client secret was not provided                                                                                | Provide the client secret received in payments/create API response                                                                                |
| IR_09       | 400                                   | invalid_request_error | The client_secret provided does not match the client_secret associated with the Payment                                                                             | Provide the same client secret received in payments/create API response for the corresponding payment                                                                                |
| IR_10       | 400                                   | invalid_request_error | Customer has an existing mandate/subscription                                                                                | Cancel the active mandates/subscriptions for the customer before proceeding to delete the customer data                                                                              |
| IR_11       | 400                                   | invalid_request_error | Customer has already been redacted                                                                                | Customer has already been redacted. No action required                                                                                |
| IR_12       | 400                                   | invalid_request_error | Reached the maximum refund attempts                                                                                | Maximum refund attempts reached for this payment. Please contact Hyperswitch support for attempting more refunds for the same payment                                                |
| IR_13       | 400                                   | invalid_request_error | Refund amount exceeds the payment amount                                                                                | Please verify and pass a refund amount equal to or less than the payment amount                                                                                |
| IR_14       | 400                                   | invalid_request_error | This Payment could not be “current_flow” because it has a “field_name” of “current_value”. The expected state is “states”                                           | Please verify the status of the payment and make sure that you are performing an action that is allowed for the current status of the payment                                        |
| IR_15       | 400                                   | invalid_request_error | Invalid Ephemeral Key for the customer                                                                                | Please pass the right Ephemeral key for the customer                                                                                |
| IR_16       | 400                                   | invalid_request_error | “message”                                                                                | Typically used when information involving multiple fields or previously provided information doesn’t satisfy a condition. Refer to our API documentation for required fields and format |
| IR_17       | 401                                   | invalid_request_error | Access forbidden, an invalid JWT token was used                                                                                | Provide a valid JWT token to access the APIs                                                                                |
| IR_18       | 401                                   | invalid_request_error | "message"                                                                                | The user is not authorised to update the customer, Contact Org. Admin for the appropriate access.                                                                                |
| IR_19       | 400                                      | invalid_request_error | "message"                                                                                | Please check and retry with correct details. Refer to our API documentation                                                                                |
| IR_20       | 400                                      | invalid_request_error | "flow" not supported by the "connector"                                                                                | Requested flow is not supported for this Connector.                                                                                |
| IR_21       | 400                                      | invalid_request_error | Missing required params                                                                                | Please add the required params in the request. Refer to our API documentation                                                                                |
| IR_22       | 403                                      | invalid_request_error | Access forbidden. Not authorized to access this "resource"                                                                                | Contact Org. Admin for the appropriate access.                                                                                |
| IR_23       | 400                                      | invalid_request_error | "message"                                                                                | Use a supported file provider. Refer to our API documentation                                                                                |
| IR_24       | 422                                      | processing_error      | Invalid "wallet_name" wallet token                                                                                | Share the correct wallet token.                                                                                |
| IR_25       | 400                                      | invalid_request_error | Cannot delete the default payment method                                                                                | Check if the Payment method is activated. Refer to Control centre to check this.                                                                                |
| IR_26       | 400                                      | invalid_request_error | Invalid Cookie                                                                                | Recheck the site setting for the cookies.                                                                                |
| IR_27       | 404                                      | invalid_request_error | Extended card info does not exist                                                                                | Recheck the card info shared in the request.                                                                                |
| IR_28       | 400                                      | invalid_request_error | "message"                                                                                | Use a valid currency. Refer to our API documentation                                                                                |
| IR_29       | 422                                     | invalid_request_error | "message"                                                                                | The data format is invalid for this request. Refer to our API documentation                                                                                |
| IR_30       | 400                                      | invalid_request_error | Merchant connector account is configured with invalid config                                                                                | Correct the config for merchant connector account                                                                                |
| IR_31       | 400                                    | invalid_request_error | Card with the provided iin does not exist                                                                                | Check the IIN (Issuer Identification Number) and ensure it is correct.                                                                                |
| IR_32       | 400                                    | invalid_request_error | The provided card IIN length is invalid, please provide an iin with 6 or 8 digits                                                                                | Provide a valid IIN with either 6 or 8 digits.                                                                                |
| IR_33       | 400                                    | invalid_request_error | File not found / valid in the request                                                                                | Ensure the required file is included in the request and is valid. Refer to our API documentation                                                                                |
| IR_34       | 400                                    | invalid_request_error | Dispute id not found in the request                                                                                | Ensure that a valid dispute ID is included in the request.                                                                                |
| IR_35       | 400                                    | invalid_request_error | File purpose not found in the request or is invalid                                                                                | Specify a valid file purpose in the request.                                                                                |
| IR_36       | 400                                    | invalid_request_error | File content type not found / valid                                                                                | Ensure the file content type is specified and is valid.                                                                                |
| IR_37       | 404                                    | invalid_request_error | "message"                                                                                | Check the request for the resource being accessed and ensure it exists.                                                                                |
| IR_38       | 400                                    | invalid_request_error | "message"                                                                                | Check for any duplicate entries in the request and correct them.                                                                                |
| IR_39       | 400                                    | invalid_request_error | required payment method is not configured or configured incorrectly for all configured connectors                                                                   | Verify that the required payment method is correctly configured for all connectors in use.                                                                                |
| CE_00       | Status codes shared by the connectors | connector_error       | “message”                                                                                | The error code and message passed from the connectors. Refer to the respective connector’s documentation for more information on the error                                           |
| CE_01       | 400                                   | processing_error      | Payment failed during authorization with the connector. Retry payment                                                                                | Retry the payment again as payment failed at the connector during authorization                                                                                |
| CE_02       | 400                                   | processing_error      | Payment failed during authentication with the connector. Retry payment                                                                                | Retry the payment again as payment failed at the connector during authentication                                                                                |
| CE_03       | 400                                   | processing_error      | Capture attempt failed while processing with the connector                                                                                | Capture failed for the payment at the connector. Please retry the payment                                                                                |
| CE_04       | 400                                   | processing_error      | The card data is invalid                                                                                | Invalid card data passed. Please pass valid card data                                                                                |
| CE_05       | 400                                   | processing_error      | The card has expired                                                                                | Card expired. Please pass valid card data                                                                                |
| CE_06       | 400                                   | processing_error      | Refund failed while processing with the connector. Retry refund                                                                                | Refund failed to process at the connector. Please retry refund                                                                                |
| CE_07       | 400                                   | processing_error      | Verification failed while processing with the connector. Retry operation                                                                                | Retry the operation again as verification failed at the connector                                                                                |
| CE_08       | 400                                      | processing_error      | Dispute operation failed while processing with connector. Retry operation                                                                                | Retry the operation again as dispute failed at the connector                                                                                |
| CE_09       | 400                                      | invalid_request_error      | Payout validation failed                                                                                | Retry the operation again with correct Payout details.                                                                                |
| HE_00       | 422,500                                   | server_not_available      | Resource not available right now, Please try again later.                                                                                | Please Wait for a few moments and try again. If the error still persists, please reach out to Hyperswitch support                                                                              |
| HE_01       | 400,422                                   | duplicate_request       | Requested operation(Customer, Payments, Merchants, Refunds etc.) for these identifier already exists.                                                              | Please verify the Details(Customer, Payments, Merchants, Refunds, as applicable on the basis of request) and enter valid details.                                                                                |
| HE_02       | 404                                   | object_not_found      | Requested object(Customer, Payments, Merchants, Refunds etc.) does not exist in our records                                                                         | Please verify the Details(Customer, Payments, Merchants, Refunds, as applicable on the basis of request) and enter valid details.                                                                               |
| HE_03       | 500                                   | validation_error      | Validation Failed for the requested operation with the given details.                                                                                | Please verify the details again and enter valid details                                                                                |
| HE_04       | 404                                   | object_not_found          | Requested object(Customer, Payments, Merchants, Refunds etc.) does not exist in our records                                                                     | Please verify the Details(Customer, Payments, Merchants, Refunds, as applicable on the basis of request) and enter valid details.                                                                                |
| HE_05       | 500                                   | processing_error          | Missing or invalid tenant details.                                                                                | Please verify the tenant Details and try again.                                                                                |
| WE_01       | 400                                   | invalid_request_error    | Failed to authenticate the webhook                                                                                | Please verify the authentication credentials and try again.                                                                         |
| WE_02       | 400                                   | invalid_request_error              | Bad request received in webhook                                                                                | Check the request parameters and format, then try again.                                                                            |
| WE_03       | 500                                   | router_error       | There was some issue processing the webhook                                                                                | Please try again later. If the issue persists, contact Hyperswitch support.                                                                     |
| WE_04       | 404                                   | object_not_found        | Webhook resource not found                                                                                | Ensure the webhook URL is correct and the resource exists.                                                                          |
| WE_05       | 400                                   | invalid_request_error     | Unable to process the webhook body                                                                                | Ensure the webhook body is correctly formatted and try again.                                                                       |
| WE_06       | 400                                   | invalid_request_error   | Merchant Secret set by merchant for webhook source verification is invalid                                                                                | Verify the Merchant Secret, then try again.                                                              |

**2. Unified Error codes:**
The section below contains all the unified error codes and their corresponding error messages.

| Unified Error Code | Unified Error Categorisation          | Unified Error message               |
| ------------------ | ------------------------------------- | ----------------------------------- |
| UE_1000            | Customer Error                        | Issue with payment method details.  |
| UE_2000            | Connector Declines                    | Issue with Configurations.          |
| UE_3000            | Connector Error                       | Technical issue with PSP.           |
| UE_4000            | Integration Error                     | Issue in the integration.           |
| UE_9000            | Others                                | Something went wrong.               |


impl EmailData for InviteUser {
    async fn get_email_data(&self, base_url: &str) -> CustomResult<EmailContents, EmailError> {
        let token = EmailToken::new_token(
            self.recipient_email.clone(),
            Some(self.entity.clone()),
            domain::Origin::AcceptInvitationFromEmail,
            &self.settings,
        )
        .await
        .change_context(EmailError::TokenGenerationFailure)?;

        let invite_user_link = get_link_with_token(
            base_url,
            token,
            "accept_invite_from_email",
            &self.auth_id,
            &self.theme_id,
        );
        let body = html::get_html_body(EmailBody::AcceptInviteFromEmail {
            link: invite_user_link,
            user_name: self.user_name.clone().get_secret().expose(),
            entity_name: self.theme_config.entity_name.clone(),
            entity_logo_url: self.theme_config.entity_logo_url.clone(),
            primary_color: self.theme_config.primary_color.clone(),
            background_color: self.theme_config.background_color.clone(),
            foreground_color: self.theme_config.foreground_color.clone(),
        });

        Ok(EmailContents {
            subject: format!(
                "You have been invited to join {} Community!",
                self.theme_config.entity_name
            ),
            body: external_services::email::IntermediateString::new(body),
            recipient: self.recipient_email.clone().into_inner(),
        })
    }
}