pub fn get_locale_from_header(headers: &actix_web::http::header::HeaderMap) -> String {
    get_header_value_by_key(ACCEPT_LANGUAGE.into(), headers)
        .ok()
        .flatten()
        .map(|val| val.to_string())
        .unwrap_or(common_utils::consts::DEFAULT_LOCALE.to_string())
}


pub struct NovalnetCustom {
    lang: String,
}


pub struct JpmorganPaymentMethodTypeCancelResponse {
    pub card: CardCancelResponse,
}


File: crates/router/src/core/errors/user.rs
 Public functions: 1

use common_utils::errors::CustomResult;

use crate::services::ApplicationResponse;

pub type UserResult<T> = CustomResult<T, UserErrors>;
pub type UserResponse<T> = CustomResult<ApplicationResponse<T>, UserErrors>;
pub mod sample_data;

#[derive(Debug, thiserror::Error)]
pub enum UserErrors {
    #[error("User InternalServerError")]
    InternalServerError,
    #[error("InvalidCredentials")]
    InvalidCredentials,
    #[error("UserNotFound")]
    UserNotFound,
    #[error("UserExists")]
    UserExists,
    #[error("LinkInvalid")]
    LinkInvalid,
    #[error("UnverifiedUser")]
    UnverifiedUser,
    #[error("InvalidOldPassword")]
    InvalidOldPassword,
    #[error("EmailParsingError")]
    EmailParsingError,
    #[error("NameParsingError")]
    NameParsingError,
    #[error("PasswordParsingError")]
    PasswordParsingError,
    #[error("UserAlreadyVerified")]
    UserAlreadyVerified,
    #[error("CompanyNameParsingError")]
    CompanyNameParsingError,
    #[error("MerchantAccountCreationError: {0}")]
    MerchantAccountCreationError(String),
    #[error("InvalidEmailError")]
    InvalidEmailError,
    #[error("DuplicateOrganizationId")]
    DuplicateOrganizationId,
    #[error("MerchantIdNotFound")]
    MerchantIdNotFound,
    #[error("MetadataAlreadySet")]
    MetadataAlreadySet,
    #[error("InvalidRoleId")]
    InvalidRoleId,
    #[error("InvalidRoleOperation")]
    InvalidRoleOperation,
    #[error("IpAddressParsingFailed")]
    IpAddressParsingFailed,
    #[error("InvalidMetadataRequest")]
    InvalidMetadataRequest,
    #[error("MerchantIdParsingError")]
    MerchantIdParsingError,
    #[error("ChangePasswordError")]
    ChangePasswordError,
    #[error("InvalidDeleteOperation")]
    InvalidDeleteOperation,
    #[error("MaxInvitationsError")]
    MaxInvitationsError,
    #[error("RoleNotFound")]
    RoleNotFound,
    #[error("InvalidRoleOperationWithMessage")]
    InvalidRoleOperationWithMessage(String),
    #[error("RoleNameParsingError")]
    RoleNameParsingError,
    #[error("RoleNameAlreadyExists")]
    RoleNameAlreadyExists,
    #[error("TotpNotSetup")]
    TotpNotSetup,
    #[error("InvalidTotp")]
    InvalidTotp,
    #[error("TotpRequired")]
    TotpRequired,
    #[error("InvalidRecoveryCode")]
    InvalidRecoveryCode,
    #[error("TwoFactorAuthRequired")]
    TwoFactorAuthRequired,
    #[error("TwoFactorAuthNotSetup")]
    TwoFactorAuthNotSetup,
    #[error("TOTP secret not found")]
    TotpSecretNotFound,
    #[error("User auth method already exists")]
    UserAuthMethodAlreadyExists,
    #[error("Invalid user auth method operation")]
    InvalidUserAuthMethodOperation,
    #[error("Auth config parsing error")]
    AuthConfigParsingError,
    #[error("Invalid SSO request")]
    SSOFailed,
    #[error("profile_id missing in JWT")]
    JwtProfileIdMissing,
    #[error("Maximum attempts reached for TOTP")]
    MaxTotpAttemptsReached,
    #[error("Maximum attempts reached for Recovery Code")]
    MaxRecoveryCodeAttemptsReached,
    #[error("Forbidden tenant id")]
    ForbiddenTenantId,
    #[error("Error Uploading file to Theme Storage")]
    ErrorUploadingFile,
    #[error("Error Retrieving file from Theme Storage")]
    ErrorRetrievingFile,
    #[error("Theme not found")]
    ThemeNotFound,
    #[error("Theme with lineage already exists")]
    ThemeAlreadyExists,
    #[error("Invalid field: {0} in lineage")]
    InvalidThemeLineage(String),
    #[error("Missing required field: email_config")]
    MissingEmailConfig,
    #[error("Invalid Auth Method Operation: {0}")]
    InvalidAuthMethodOperationWithMessage(String),
    #[error("Invalid Clone Connector Operation: {0}")]
    InvalidCloneConnectorOperation(String),
    #[error("Error cloning connector: {0}")]
    ErrorCloningConnector(String),
}

impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorResponse> for UserErrors {
    fn switch(&self) -> api_models::errors::types::ApiErrorResponse {
        use api_models::errors::types::{ApiError, ApiErrorResponse as AER};
        let sub_code = "UR";
        match self {
            Self::InternalServerError => {
                AER::InternalServerError(ApiError::new("HE", 0, self.get_error_message(), None))
            }
            Self::InvalidCredentials => {
                AER::Unauthorized(ApiError::new(sub_code, 1, self.get_error_message(), None))
            }
            Self::UserNotFound => {
                AER::Unauthorized(ApiError::new(sub_code, 2, self.get_error_message(), None))
            }
            Self::UserExists => {
                AER::BadRequest(ApiError::new(sub_code, 3, self.get_error_message(), None))
            }
            Self::LinkInvalid => {
                AER::Unauthorized(ApiError::new(sub_code, 4, self.get_error_message(), None))
            }
            Self::UnverifiedUser => {
                AER::Unauthorized(ApiError::new(sub_code, 5, self.get_error_message(), None))
            }
            Self::InvalidOldPassword => {
                AER::BadRequest(ApiError::new(sub_code, 6, self.get_error_message(), None))
            }
            Self::EmailParsingError => {
                AER::BadRequest(ApiError::new(sub_code, 7, self.get_error_message(), None))
            }
            Self::NameParsingError => {
                AER::BadRequest(ApiError::new(sub_code, 8, self.get_error_message(), None))
            }
            Self::PasswordParsingError => {
                AER::BadRequest(ApiError::new(sub_code, 9, self.get_error_message(), None))
            }
            Self::UserAlreadyVerified => {
                AER::Unauthorized(ApiError::new(sub_code, 11, self.get_error_message(), None))
            }
            Self::CompanyNameParsingError => {
                AER::BadRequest(ApiError::new(sub_code, 14, self.get_error_message(), None))
            }
            Self::MerchantAccountCreationError(_) => AER::InternalServerError(ApiError::new(
                sub_code,
                15,
                self.get_error_message(),
                None,
            )),
            Self::InvalidEmailError => {
                AER::BadRequest(ApiError::new(sub_code, 16, self.get_error_message(), None))
            }
            Self::MerchantIdNotFound => {
                AER::BadRequest(ApiError::new(sub_code, 18, self.get_error_message(), None))
            }
            Self::MetadataAlreadySet => {
                AER::BadRequest(ApiError::new(sub_code, 19, self.get_error_message(), None))
            }
            Self::DuplicateOrganizationId => AER::InternalServerError(ApiError::new(
                sub_code,
                21,
                self.get_error_message(),
                None,
            )),
            Self::InvalidRoleId => {
                AER::BadRequest(ApiError::new(sub_code, 22, self.get_error_message(), None))
            }
            Self::InvalidRoleOperation => {
                AER::BadRequest(ApiError::new(sub_code, 23, self.get_error_message(), None))
            }
            Self::IpAddressParsingFailed => AER::InternalServerError(ApiError::new(
                sub_code,
                24,
                self.get_error_message(),
                None,
            )),
            Self::InvalidMetadataRequest => {
                AER::BadRequest(ApiError::new(sub_code, 26, self.get_error_message(), None))
            }
            Self::MerchantIdParsingError => {
                AER::BadRequest(ApiError::new(sub_code, 28, self.get_error_message(), None))
            }
            Self::ChangePasswordError => {
                AER::BadRequest(ApiError::new(sub_code, 29, self.get_error_message(), None))
            }
            Self::InvalidDeleteOperation => {
                AER::BadRequest(ApiError::new(sub_code, 30, self.get_error_message(), None))
            }
            Self::MaxInvitationsError => {
                AER::BadRequest(ApiError::new(sub_code, 31, self.get_error_message(), None))
            }
            Self::RoleNotFound => {
                AER::BadRequest(ApiError::new(sub_code, 32, self.get_error_message(), None))
            }
            Self::InvalidRoleOperationWithMessage(_) => {
                AER::BadRequest(ApiError::new(sub_code, 33, self.get_error_message(), None))
            }
            Self::RoleNameParsingError => {
                AER::BadRequest(ApiError::new(sub_code, 34, self.get_error_message(), None))
            }
            Self::RoleNameAlreadyExists => {
                AER::BadRequest(ApiError::new(sub_code, 35, self.get_error_message(), None))
            }
            Self::TotpNotSetup => {
                AER::BadRequest(ApiError::new(sub_code, 36, self.get_error_message(), None))
            }
            Self::InvalidTotp => {
                AER::BadRequest(ApiError::new(sub_code, 37, self.get_error_message(), None))
            }
            Self::TotpRequired => {
                AER::BadRequest(ApiError::new(sub_code, 38, self.get_error_message(), None))
            }
            Self::InvalidRecoveryCode => {
                AER::BadRequest(ApiError::new(sub_code, 39, self.get_error_message(), None))
            }
            Self::TwoFactorAuthRequired => {
                AER::BadRequest(ApiError::new(sub_code, 40, self.get_error_message(), None))
            }
            Self::TwoFactorAuthNotSetup => {
                AER::BadRequest(ApiError::new(sub_code, 41, self.get_error_message(), None))
            }
            Self::TotpSecretNotFound => {
                AER::BadRequest(ApiError::new(sub_code, 42, self.get_error_message(), None))
            }
            Self::UserAuthMethodAlreadyExists => {
                AER::BadRequest(ApiError::new(sub_code, 43, self.get_error_message(), None))
            }
            Self::InvalidUserAuthMethodOperation => {
                AER::BadRequest(ApiError::new(sub_code, 44, self.get_error_message(), None))
            }
            Self::AuthConfigParsingError => {
                AER::BadRequest(ApiError::new(sub_code, 45, self.get_error_message(), None))
            }
            Self::SSOFailed => {
                AER::BadRequest(ApiError::new(sub_code, 46, self.get_error_message(), None))
            }
            Self::JwtProfileIdMissing => {
                AER::Unauthorized(ApiError::new(sub_code, 47, self.get_error_message(), None))
            }
            Self::MaxTotpAttemptsReached => {
                AER::BadRequest(ApiError::new(sub_code, 48, self.get_error_message(), None))
            }
            Self::MaxRecoveryCodeAttemptsReached => {
                AER::BadRequest(ApiError::new(sub_code, 49, self.get_error_message(), None))
            }
            Self::ForbiddenTenantId => {
                AER::BadRequest(ApiError::new(sub_code, 50, self.get_error_message(), None))
            }
            Self::ErrorUploadingFile => AER::InternalServerError(ApiError::new(
                sub_code,
                51,
                self.get_error_message(),
                None,
            )),
            Self::ErrorRetrievingFile => AER::InternalServerError(ApiError::new(
                sub_code,
                52,
                self.get_error_message(),
                None,
            )),
            Self::ThemeNotFound => {
                AER::NotFound(ApiError::new(sub_code, 53, self.get_error_message(), None))
            }
            Self::ThemeAlreadyExists => {
                AER::BadRequest(ApiError::new(sub_code, 54, self.get_error_message(), None))
            }
            Self::InvalidThemeLineage(_) => {
                AER::BadRequest(ApiError::new(sub_code, 55, self.get_error_message(), None))
            }
            Self::MissingEmailConfig => {
                AER::BadRequest(ApiError::new(sub_code, 56, self.get_error_message(), None))
            }
            Self::InvalidAuthMethodOperationWithMessage(_) => {
                AER::BadRequest(ApiError::new(sub_code, 57, self.get_error_message(), None))
            }
            Self::InvalidCloneConnectorOperation(_) => {
                AER::BadRequest(ApiError::new(sub_code, 58, self.get_error_message(), None))
            }
            Self::ErrorCloningConnector(_) => AER::InternalServerError(ApiError::new(
                sub_code,
                59,
                self.get_error_message(),
                None,
            )),
        }
    }
}

impl UserErrors {
    pub fn get_error_message(&self) -> String {
        match self {
            Self::InternalServerError => "Something went wrong".to_string(),
            Self::InvalidCredentials => "Incorrect email or password".to_string(),
            Self::UserNotFound => "Email doesn’t exist. Register".to_string(),
            Self::UserExists => "An account already exists with this email".to_string(),
            Self::LinkInvalid => "Invalid or expired link".to_string(),
            Self::UnverifiedUser => "Kindly verify your account".to_string(),
            Self::InvalidOldPassword => {
                "Old password incorrect. Please enter the correct password".to_string()
            }
            Self::EmailParsingError => "Invalid Email".to_string(),
            Self::NameParsingError => "Invalid Name".to_string(),
            Self::PasswordParsingError => "Invalid Password".to_string(),
            Self::UserAlreadyVerified => "User already verified".to_string(),
            Self::CompanyNameParsingError => "Invalid Company Name".to_string(),
            Self::MerchantAccountCreationError(error_message) => error_message.to_string(),
            Self::InvalidEmailError => "Invalid Email".to_string(),
            Self::MerchantIdNotFound => "Invalid Merchant ID".to_string(),
            Self::MetadataAlreadySet => "Metadata already set".to_string(),
            Self::DuplicateOrganizationId => {
                "An Organization with the id already exists".to_string()
            }
            Self::InvalidRoleId => "Invalid Role ID".to_string(),
            Self::InvalidRoleOperation => "User Role Operation Not Supported".to_string(),
            Self::IpAddressParsingFailed => "Something went wrong".to_string(),
            Self::InvalidMetadataRequest => "Invalid Metadata Request".to_string(),
            Self::MerchantIdParsingError => "Invalid Merchant Id".to_string(),
            Self::ChangePasswordError => "Old and new password cannot be same".to_string(),
            Self::InvalidDeleteOperation => "Delete Operation Not Supported".to_string(),
            Self::MaxInvitationsError => "Maximum invite count per request exceeded".to_string(),
            Self::RoleNotFound => "Role Not Found".to_string(),
            Self::InvalidRoleOperationWithMessage(error_message) => error_message.to_string(),
            Self::RoleNameParsingError => "Invalid Role Name".to_string(),
            Self::RoleNameAlreadyExists => "Role name already exists".to_string(),
            Self::TotpNotSetup => "TOTP not setup".to_string(),
            Self::InvalidTotp => "Invalid TOTP".to_string(),
            Self::TotpRequired => "TOTP required".to_string(),
            Self::InvalidRecoveryCode => "Invalid Recovery Code".to_string(),
            Self::MaxTotpAttemptsReached => "Maximum attempts reached for TOTP".to_string(),
            Self::MaxRecoveryCodeAttemptsReached => {
                "Maximum attempts reached for Recovery Code".to_string()
            }
            Self::TwoFactorAuthRequired => "Two factor auth required".to_string(),
            Self::TwoFactorAuthNotSetup => "Two factor auth not setup".to_string(),
            Self::TotpSecretNotFound => "TOTP secret not found".to_string(),
            Self::UserAuthMethodAlreadyExists => "User auth method already exists".to_string(),
            Self::InvalidUserAuthMethodOperation => {
                "Invalid user auth method operation".to_string()
            }
            Self::AuthConfigParsingError => "Auth config parsing error".to_string(),
            Self::SSOFailed => "Invalid SSO request".to_string(),
            Self::JwtProfileIdMissing => "profile_id missing in JWT".to_string(),
            Self::ForbiddenTenantId => "Forbidden tenant id".to_string(),
            Self::ErrorUploadingFile => "Error Uploading file to Theme Storage".to_string(),
            Self::ErrorRetrievingFile => "Error Retrieving file from Theme Storage".to_string(),
            Self::ThemeNotFound => "Theme not found".to_string(),
            Self::ThemeAlreadyExists => "Theme with lineage already exists".to_string(),
            Self::InvalidThemeLineage(field_name) => {
                format!("Invalid field: {field_name} in lineage")
            }
            Self::MissingEmailConfig => "Missing required field: email_config".to_string(),
            Self::InvalidAuthMethodOperationWithMessage(operation) => {
                format!("Invalid Auth Method Operation: {operation}")
            }
            Self::InvalidCloneConnectorOperation(operation) => {
                format!("Invalid Clone Connector Operation: {operation}")
            }
            Self::ErrorCloningConnector(error_message) => {
                format!("Error cloning connector: {error_message}")
            }
        }
    }
}


File: crates/router/src/workflows/payment_method_status_update.rs
 Public structs: 1

use common_utils::ext_traits::ValueExt;
use error_stack::ResultExt;
use scheduler::{
    consumer::types::process_data, utils as pt_utils, workflows::ProcessTrackerWorkflow,
};

use crate::{
    errors,
    logger::error,
    routes::SessionState,
    types::storage::{self, PaymentMethodStatusTrackingData},
};
pub struct PaymentMethodStatusUpdateWorkflow;

#[async_trait::async_trait]
impl ProcessTrackerWorkflow<SessionState> for PaymentMethodStatusUpdateWorkflow {
    #[cfg(feature = "v1")]
    async fn execute_workflow<'a>(
        &'a self,
        state: &'a SessionState,
        process: storage::ProcessTracker,
    ) -> Result<(), errors::ProcessTrackerError> {
        let db = &*state.store;
        let tracking_data: PaymentMethodStatusTrackingData = process
            .tracking_data
            .clone()
            .parse_value("PaymentMethodStatusTrackingData")?;

        let retry_count = process.retry_count;
        let pm_id = tracking_data.payment_method_id;
        let prev_pm_status = tracking_data.prev_status;
        let curr_pm_status = tracking_data.curr_status;
        let merchant_id = tracking_data.merchant_id;
        let key_manager_state = &state.into();
        let key_store = state
            .store
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &merchant_id,
                &state.store.get_master_key().to_vec().into(),
            )
            .await?;

        let merchant_account = db
            .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store)
            .await?;

        let payment_method = db
            .find_payment_method(
                &(state.into()),
                &key_store,
                &pm_id,
                merchant_account.storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to decode billing address")?;

        if payment_method.status != prev_pm_status {
            return db
                .as_scheduler()
                .finish_process_with_business_status(process, "PROCESS_ALREADY_COMPLETED")
                .await
                .map_err(Into::<errors::ProcessTrackerError>::into);
        }

        let pm_update = storage::PaymentMethodUpdate::StatusUpdate {
            status: Some(curr_pm_status),
        };

        let res = db
            .update_payment_method(
                &(state.into()),
                &key_store,
                payment_method,
                pm_update,
                merchant_account.storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment method");

        if let Ok(_pm) = res {
            db.as_scheduler()
                .finish_process_with_business_status(process, "COMPLETED_BY_PT")
                .await?;
        } else {
            let mapping = process_data::PaymentMethodsPTMapping::default();
            let time_delta = if retry_count == 0 {
                Some(mapping.default_mapping.start_after)
            } else {
                pt_utils::get_delay(retry_count + 1, &mapping.default_mapping.frequencies)
            };

            let schedule_time = pt_utils::get_time_from_delta(time_delta);

            match schedule_time {
                Some(s_time) => db
                    .as_scheduler()
                    .retry_process(process, s_time)
                    .await
                    .map_err(Into::<errors::ProcessTrackerError>::into)?,
                None => db
                    .as_scheduler()
                    .finish_process_with_business_status(process, "RETRIES_EXCEEDED")
                    .await
                    .map_err(Into::<errors::ProcessTrackerError>::into)?,
            };
        };

        Ok(())
    }

    #[cfg(feature = "v2")]
    async fn execute_workflow<'a>(
        &'a self,
        _state: &'a SessionState,
        _process: storage::ProcessTracker,
    ) -> Result<(), errors::ProcessTrackerError> {
        todo!()
    }
    async fn error_handler<'a>(
        &'a self,
        _state: &'a SessionState,
        process: storage::ProcessTracker,
        _error: errors::ProcessTrackerError,
    ) -> errors::CustomResult<(), errors::ProcessTrackerError> {
        error!(%process.id, "Failed while executing workflow");
        Ok(())
    }
}


pub struct RiskifiedWebhookBody {
    pub id: String,
    pub status: RiskifiedWebhookStatus,
}


pub struct ShopperDetails {
    reference: id_type::CustomerId,
    email: Option<Email>,
    first_name: Secret<String>,
    last_name: Secret<String>,
}


impl ConnectorSpecifications for Custombilling {}

impl ConnectorSpecifications for for Custombilling


pub async fn network_token_incoming_webhooks_wrapper<W: types::OutgoingWebhookType>(
    flow: &impl router_env::types::FlowMetric,
    state: SessionState,
    req: &actix_web::HttpRequest,
    body: actix_web::web::Bytes,
) -> RouterResponse<serde_json::Value> {
    let start_instant = Instant::now();

    let request_details: IncomingWebhookRequestDetails<'_> = IncomingWebhookRequestDetails {
        method: req.method().clone(),
        uri: req.uri().clone(),
        headers: req.headers(),
        query_params: req.query_string().to_string(),
        body: &body,
    };

    let (application_response, webhooks_response_tracker, serialized_req, merchant_id) = Box::pin(
        network_token_incoming_webhooks_core::<W>(&state, request_details),
    )
    .await?;

    logger::info!(incoming_webhook_payload = ?serialized_req);

    let request_duration = Instant::now()
        .saturating_duration_since(start_instant)
        .as_millis();

    let request_id = RequestId::extract(req)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to extract request id from request")?;
    let auth_type = auth::AuthenticationType::NoAuth;
    let status_code = 200;
    let api_event = ApiEventsType::NetworkTokenWebhook {
        payment_method_id: webhooks_response_tracker.get_payment_method_id(),
    };
    let response_value = serde_json::to_value(&webhooks_response_tracker)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Could not convert webhook effect to string")?;
    let infra = state.infra_components.clone();
    let api_event = ApiEvent::new(
        state.tenant.tenant_id.clone(),
        Some(merchant_id),
        flow,
        &request_id,
        request_duration,
        status_code,
        serialized_req,
        Some(response_value),
        None,
        auth_type,
        None,
        api_event,
        req,
        req.method(),
        infra,
    );
    state.event_handler().log_event(&api_event);
    Ok(application_response)
}


pub struct MolliePaymentsResponse {
    pub resource: String,
    pub id: String,
    pub amount: Amount,
    pub description: Option<String>,
    pub metadata: Option<MollieMetadata>,
    pub status: MolliePaymentStatus,
    pub is_cancelable: Option<bool>,
    pub sequence_type: SequenceType,
    pub redirect_url: Option<String>,
    pub webhook_url: Option<String>,
    #[serde(rename = "_links")]
    pub links: Links,
}


pub struct StripeMandateResponse {
    pub mandate_id: String,
    pub status: StripeMandateStatus,
    pub payment_method_id: String,
    pub payment_method: String,
}


pub struct BillTo {
    first_name: Option<Secret<String>>,
    last_name: Option<Secret<String>>,
    address1: Option<Secret<String>>,
    locality: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    administrative_area: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    postal_code: Option<Secret<String>>,
    country: Option<enums::CountryAlpha2>,
    email: pii::Email,
}


File: crates/router/src/core/revenue_recovery/api.rs
 Public functions: 5

use actix_web::{web, Responder};
use api_models::{payments as payments_api, payments as api_payments};
use common_utils::id_type;
use error_stack::{report, FutureExt, ResultExt};
use hyperswitch_domain_models::{
    merchant_context::{Context, MerchantContext},
    payments as payments_domain,
};

use crate::{
    core::{
        errors::{self, RouterResult},
        payments::{self, operations::Operation},
        webhooks::recovery_incoming,
    },
    db::{
        errors::{RouterResponse, StorageErrorExt},
        storage::revenue_recovery_redis_operation::RedisTokenManager,
    },
    logger,
    routes::{app::ReqState, SessionState},
    services,
    types::{
        api::payments as api_types,
        domain,
        storage::{self, revenue_recovery as revenue_recovery_types},
        transformers::ForeignFrom,
    },
};

pub async fn call_psync_api(
    state: &SessionState,
    global_payment_id: &id_type::GlobalPaymentId,
    revenue_recovery_data: &revenue_recovery_types::RevenueRecoveryPaymentData,
) -> RouterResult<payments_domain::PaymentStatusData<api_types::PSync>> {
    let operation = payments::operations::PaymentGet;
    let req = payments_api::PaymentsRetrieveRequest {
        force_sync: true,
        param: None,
        expand_attempts: true,
        return_raw_connector_response: None,
        merchant_connector_details: None,
    };
    let merchant_context_from_revenue_recovery_data =
        MerchantContext::NormalMerchant(Box::new(Context(
            revenue_recovery_data.merchant_account.clone(),
            revenue_recovery_data.key_store.clone(),
        )));
    // TODO : Use api handler instead of calling get_tracker and payments_operation_core
    // Get the tracker related information. This includes payment intent and payment attempt
    let get_tracker_response = operation
        .to_get_tracker()?
        .get_trackers(
            state,
            global_payment_id,
            &req,
            &merchant_context_from_revenue_recovery_data,
            &revenue_recovery_data.profile,
            &payments_domain::HeaderPayload::default(),
        )
        .await?;

    let (payment_data, _req, _, _, _, _) = Box::pin(payments::payments_operation_core::<
        api_types::PSync,
        _,
        _,
        _,
        payments_domain::PaymentStatusData<api_types::PSync>,
    >(
        state,
        state.get_req_state(),
        merchant_context_from_revenue_recovery_data,
        &revenue_recovery_data.profile,
        operation,
        req,
        get_tracker_response,
        payments::CallConnectorAction::Trigger,
        payments_domain::HeaderPayload::default(),
    ))
    .await?;
    Ok(payment_data)
}

pub async fn call_proxy_api(
    state: &SessionState,
    payment_intent: &payments_domain::PaymentIntent,
    revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
    revenue_recovery: &payments_api::PaymentRevenueRecoveryMetadata,
    payment_processor_token: &str,
) -> RouterResult<payments_domain::PaymentConfirmData<api_types::Authorize>> {
    let operation = payments::operations::proxy_payments_intent::PaymentProxyIntent;
    let recurring_details = api_models::mandates::ProcessorPaymentToken {
        processor_payment_token: payment_processor_token.to_string(),
        merchant_connector_id: Some(revenue_recovery.get_merchant_connector_id_for_api_request()),
    };
    let req = payments_api::ProxyPaymentsRequest {
        return_url: None,
        amount: payments_api::AmountDetails::new(payment_intent.amount_details.clone().into()),
        recurring_details,
        shipping: None,
        browser_info: None,
        connector: revenue_recovery.connector.to_string(),
        merchant_connector_id: revenue_recovery.get_merchant_connector_id_for_api_request(),
    };
    logger::info!(
        "Call made to payments proxy api , with the request body {:?}",
        req
    );
    let merchant_context_from_revenue_recovery_payment_data =
        MerchantContext::NormalMerchant(Box::new(Context(
            revenue_recovery_payment_data.merchant_account.clone(),
            revenue_recovery_payment_data.key_store.clone(),
        )));

    // TODO : Use api handler instead of calling get_tracker and payments_operation_core
    // Get the tracker related information. This includes payment intent and payment attempt
    let get_tracker_response = operation
        .to_get_tracker()?
        .get_trackers(
            state,
            payment_intent.get_id(),
            &req,
            &merchant_context_from_revenue_recovery_payment_data,
            &revenue_recovery_payment_data.profile,
            &payments_domain::HeaderPayload::default(),
        )
        .await?;

    let (payment_data, _req, _, _) = Box::pin(payments::proxy_for_payments_operation_core::<
        api_types::Authorize,
        _,
        _,
        _,
        payments_domain::PaymentConfirmData<api_types::Authorize>,
    >(
        state,
        state.get_req_state(),
        merchant_context_from_revenue_recovery_payment_data,
        revenue_recovery_payment_data.profile.clone(),
        operation,
        req,
        get_tracker_response,
        payments::CallConnectorAction::Trigger,
        payments_domain::HeaderPayload::default(),
        None,
    ))
    .await?;
    Ok(payment_data)
}

pub async fn update_payment_intent_api(
    state: &SessionState,
    global_payment_id: id_type::GlobalPaymentId,
    revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
    update_req: payments_api::PaymentsUpdateIntentRequest,
) -> RouterResult<payments_domain::PaymentIntentData<api_types::PaymentUpdateIntent>> {
    // TODO : Use api handler instead of calling payments_intent_operation_core
    let operation = payments::operations::PaymentUpdateIntent;
    let merchant_context_from_revenue_recovery_payment_data =
        MerchantContext::NormalMerchant(Box::new(Context(
            revenue_recovery_payment_data.merchant_account.clone(),
            revenue_recovery_payment_data.key_store.clone(),
        )));
    let (payment_data, _req, _) = payments::payments_intent_operation_core::<
        api_types::PaymentUpdateIntent,
        _,
        _,
        payments_domain::PaymentIntentData<api_types::PaymentUpdateIntent>,
    >(
        state,
        state.get_req_state(),
        merchant_context_from_revenue_recovery_payment_data,
        revenue_recovery_payment_data.profile.clone(),
        operation,
        update_req,
        global_payment_id,
        payments_domain::HeaderPayload::default(),
    )
    .await?;
    Ok(payment_data)
}

pub async fn record_internal_attempt_api(
    state: &SessionState,
    payment_intent: &payments_domain::PaymentIntent,
    revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
    revenue_recovery_metadata: &payments_api::PaymentRevenueRecoveryMetadata,
    card_info: payments_api::AdditionalCardInfo,
    payment_processor_token: &str,
) -> RouterResult<payments_api::PaymentAttemptRecordResponse> {
    let revenue_recovery_attempt_data =
        recovery_incoming::RevenueRecoveryAttempt::get_revenue_recovery_attempt(
            payment_intent,
            revenue_recovery_metadata,
            &revenue_recovery_payment_data.billing_mca,
            card_info,
            payment_processor_token,
        )
        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
            message: "get_revenue_recovery_attempt was not constructed".to_string(),
        })?;

    let request_payload = revenue_recovery_attempt_data
        .create_payment_record_request(
            state,
            &revenue_recovery_payment_data.billing_mca.id,
            Some(
                revenue_recovery_metadata
                    .active_attempt_payment_connector_id
                    .clone(),
            ),
            Some(revenue_recovery_metadata.connector),
            common_enums::TriggeredBy::Internal,
        )
        .await
        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
            message: "Cannot Create the payment record Request".to_string(),
        })?;

    let merchant_context_from_revenue_recovery_payment_data =
        MerchantContext::NormalMerchant(Box::new(Context(
            revenue_recovery_payment_data.merchant_account.clone(),
            revenue_recovery_payment_data.key_store.clone(),
        )));

    let attempt_response = Box::pin(payments::record_attempt_core(
        state.clone(),
        state.get_req_state(),
        merchant_context_from_revenue_recovery_payment_data,
        revenue_recovery_payment_data.profile.clone(),
        request_payload,
        payment_intent.id.clone(),
        hyperswitch_domain_models::payments::HeaderPayload::default(),
    ))
    .await;

    match attempt_response {
        Ok(services::ApplicationResponse::JsonWithHeaders((attempt_response, _))) => {
            Ok(attempt_response)
        }
        Ok(_) => Err(errors::ApiErrorResponse::PaymentNotFound)
            .attach_printable("Unexpected response from record attempt core"),
        error @ Err(_) => {
            router_env::logger::error!(?error);
            Err(errors::ApiErrorResponse::PaymentNotFound)
                .attach_printable("failed to record attempt for revenue recovery workflow")
        }
    }
}

pub async fn custom_revenue_recovery_core(
    state: SessionState,
    req_state: ReqState,
    merchant_context: MerchantContext,
    profile: domain::Profile,
    request: api_models::payments::RecoveryPaymentsCreate,
) -> RouterResponse<payments_api::RecoveryPaymentsResponse> {
    let store = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let payment_merchant_connector_account_id = request.payment_merchant_connector_id.to_owned();
    // Find the payment & billing merchant connector id at the top level to avoid multiple DB calls.
    let payment_merchant_connector_account = store
        .find_merchant_connector_account_by_id(
            key_manager_state,
            &payment_merchant_connector_account_id,
            merchant_context.get_merchant_key_store(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: payment_merchant_connector_account_id
                .clone()
                .get_string_repr()
                .to_string(),
        })?;
    let billing_connector_account = store
        .find_merchant_connector_account_by_id(
            key_manager_state,
            &request.billing_merchant_connector_id.clone(),
            merchant_context.get_merchant_key_store(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: request
                .billing_merchant_connector_id
                .clone()
                .get_string_repr()
                .to_string(),
        })?;

    let recovery_intent =
        recovery_incoming::RevenueRecoveryInvoice::get_or_create_custom_recovery_intent(
            request.clone(),
            &state,
            &req_state,
            &merchant_context,
            &profile,
        )
        .await
        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
            message: format!(
                "Failed to load recovery intent for merchant reference id : {:?}",
                request.merchant_reference_id.to_owned()
            )
            .to_string(),
        })?;

    let (revenue_recovery_attempt_data, updated_recovery_intent) =
        recovery_incoming::RevenueRecoveryAttempt::load_recovery_attempt_from_api(
            request.clone(),
            &state,
            &req_state,
            &merchant_context,
            &profile,
            recovery_intent.clone(),
            payment_merchant_connector_account,
        )
        .await
        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
            message: format!(
                "Failed to load recovery attempt for merchant reference id : {:?}",
                request.merchant_reference_id.to_owned()
            )
            .to_string(),
        })?;

    let intent_retry_count = updated_recovery_intent
        .feature_metadata
        .as_ref()
        .and_then(|metadata| metadata.get_retry_count())
        .ok_or(report!(errors::ApiErrorResponse::GenericNotFoundError {
            message: "Failed to fetch retry count from intent feature metadata".to_string(),
        }))?;

    router_env::logger::info!("Intent retry count: {:?}", intent_retry_count);
    let recovery_action = recovery_incoming::RecoveryAction {
        action: request.action.to_owned(),
    };
    let mca_retry_threshold = billing_connector_account
        .get_retry_threshold()
        .ok_or(report!(errors::ApiErrorResponse::GenericNotFoundError {
            message: "Failed to fetch retry threshold from billing merchant connector account"
                .to_string(),
        }))?;

    recovery_action
        .handle_action(
            &state,
            &profile,
            &merchant_context,
            &billing_connector_account,
            mca_retry_threshold,
            intent_retry_count,
            &(
                Some(revenue_recovery_attempt_data),
                updated_recovery_intent.clone(),
            ),
        )
        .await
        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
            message: "Unexpected response from recovery core".to_string(),
        })?;

    let response = api_models::payments::RecoveryPaymentsResponse {
        id: updated_recovery_intent.payment_id.to_owned(),
        intent_status: updated_recovery_intent.status.to_owned(),
        merchant_reference_id: updated_recovery_intent.merchant_reference_id.to_owned(),
    };

    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        response,
    ))
}


pub struct TsysAuthType {
    pub(super) device_id: Secret<String>,
    pub(super) transaction_key: Secret<String>,
    pub(super) developer_id: Secret<String>,
}


pub fn get_connector_wallets_details(&self) -> Option<Secret<Value>> {
        self.connector_wallets_details.as_deref().cloned()
    }


pub async fn next(self, user: UserFromStorage, state: &SessionState) -> UserResult<NextFlow> {
        let flows = self.origin.get_flows();
        let remaining_flows = flows.iter().skip(self.current_flow_index + 1);

        for flow in remaining_flows {
            if flow
                .is_required(
                    &user,
                    &self.path,
                    state,
                    self.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id),
                )
                .await?
            {
                return Ok(NextFlow {
                    origin: self.origin.clone(),
                    next_flow: *flow,
                    user,
                    path: self.path,
                    tenant_id: self.tenant_id,
                });
            }
        }
        Err(UserErrors::InternalServerError.into())
    }


/// Panics if `json_payload` array does not contain one `GetActivePaymentsMetricRequest` element.
    pub async fn get_active_payments_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetActivePaymentsMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetActivePaymentsMetricRequest");
        let flow = AnalyticsFlow::GetActivePaymentsMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                analytics::active_payments::get_metrics(
                    &state.pool,
                    &auth.merchant_account.publishable_key,
                    auth.merchant_account.get_id(),
                    req,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub struct PayoutListFilters {
    /// The list of available connector filters
    #[schema(value_type = Vec<PayoutConnectors>)]
    pub connector: Vec<api_enums::PayoutConnectors>,
    /// The list of available currency filters
    #[schema(value_type = Vec<Currency>)]
    pub currency: Vec<common_enums::Currency>,
    /// The list of available payout status filters
    #[schema(value_type = Vec<PayoutStatus>)]
    pub status: Vec<common_enums::PayoutStatus>,
    /// The list of available payout method filters
    #[schema(value_type = Vec<PayoutType>)]
    pub payout_method: Vec<common_enums::PayoutType>,
}


OpenAPI Block Path: components.schemas.SdkNextActionData
{
  "type": "object",
  "required": [
    "next_action"
  ],
  "properties": {
    "next_action": {
      "$ref": "#/components/schemas/NextActionCall"
    },
    "order_id": {
      "type": "string",
      "nullable": true
    }
  }
}


pub fn from_storage(intent: &'a PaymentIntent, infra_values: Option<Value>) -> Self {
        let PaymentIntent {
            id,
            merchant_id,
            status,
            amount_details,
            amount_captured,
            customer_id,
            description,
            return_url,
            metadata,
            statement_descriptor,
            created_at,
            modified_at,
            last_synced,
            setup_future_usage,
            active_attempt_id,
            order_details,
            allowed_payment_method_types,
            connector_metadata,
            feature_metadata,
            attempt_count,
            profile_id,
            payment_link_id,
            frm_merchant_decision,
            updated_by,
            request_incremental_authorization,
            split_txns_enabled,
            authorization_count,
            session_expiry,
            request_external_three_ds_authentication,
            frm_metadata,
            customer_details,
            merchant_reference_id,
            billing_address,
            shipping_address,
            capture_method,
            authentication_type,
            prerouting_algorithm,
            organization_id,
            enable_payment_link,
            apply_mit_exemption,
            customer_present,
            payment_link_config,
            routing_algorithm_id,
            split_payments,
            force_3ds_challenge,
            force_3ds_challenge_trigger,
            processor_merchant_id,
            created_by,
            is_iframe_redirection_enabled,
            is_payment_id_from_merchant,
        } = intent;

        Self {
            payment_id: id,
            merchant_id,
            status: *status,
            amount: amount_details.order_amount,
            currency: amount_details.currency,
            amount_captured: *amount_captured,
            customer_id: customer_id.as_ref(),
            description: description.as_ref(),
            return_url: return_url.as_ref(),
            metadata: metadata.as_ref(),
            statement_descriptor: statement_descriptor.as_ref(),
            created_at: created_at.assume_utc(),
            modified_at: modified_at.assume_utc(),
            last_synced: last_synced.map(|t| t.assume_utc()),
            setup_future_usage: *setup_future_usage,
            off_session: setup_future_usage.is_off_session(),
            active_attempt_id: active_attempt_id.as_ref(),
            attempt_count: *attempt_count,
            profile_id,
            customer_email: None,
            feature_metadata: feature_metadata.as_ref(),
            organization_id,
            order_details: order_details.as_ref(),
            allowed_payment_method_types: allowed_payment_method_types.as_ref(),
            connector_metadata: connector_metadata.as_ref(),
            payment_link_id: payment_link_id.as_ref(),
            updated_by,
            surcharge_applicable: None,
            request_incremental_authorization: *request_incremental_authorization,
            split_txns_enabled: *split_txns_enabled,
            authorization_count: *authorization_count,
            session_expiry: session_expiry.assume_utc(),
            request_external_three_ds_authentication: *request_external_three_ds_authentication,
            frm_metadata: frm_metadata
                .as_ref()
                .map(|frm_metadata| frm_metadata.as_ref()),
            customer_details: customer_details
                .as_ref()
                .map(|customer_details| customer_details.get_inner().as_ref()),
            shipping_cost: amount_details.shipping_cost,
            tax_details: amount_details.tax_details.clone(),
            skip_external_tax_calculation: amount_details.get_external_tax_action_as_bool(),
            request_extended_authorization: None,
            psd2_sca_exemption_type: None,
            split_payments: split_payments.as_ref(),
            platform_merchant_id: None,
            force_3ds_challenge: *force_3ds_challenge,
            force_3ds_challenge_trigger: *force_3ds_challenge_trigger,
            processor_merchant_id,
            created_by: created_by.as_ref(),
            is_iframe_redirection_enabled: *is_iframe_redirection_enabled,
            merchant_reference_id: merchant_reference_id.as_ref(),
            billing_address: billing_address
                .as_ref()
                .map(|billing_address| Secret::new(billing_address.get_inner())),
            shipping_address: shipping_address
                .as_ref()
                .map(|shipping_address| Secret::new(shipping_address.get_inner())),
            capture_method: *capture_method,
            authentication_type: *authentication_type,
            prerouting_algorithm: prerouting_algorithm.as_ref(),
            surcharge_amount: amount_details.surcharge_amount,
            tax_on_surcharge: amount_details.tax_on_surcharge,
            frm_merchant_decision: *frm_merchant_decision,
            enable_payment_link: *enable_payment_link,
            apply_mit_exemption: *apply_mit_exemption,
            customer_present: *customer_present,
            routing_algorithm_id: routing_algorithm_id.as_ref(),
            payment_link_config: payment_link_config.as_ref(),
            infra_values,
        }
    }


pub struct PaymentMetricsAccumulator {
    pub payment_success_rate: SuccessRateAccumulator,
    pub payment_count: CountAccumulator,
    pub payment_success: CountAccumulator,
    pub processed_amount: ProcessedAmountAccumulator,
    pub avg_ticket_size: AverageAccumulator,
    pub payment_error_message: ErrorDistributionAccumulator,
    pub retries_count: CountAccumulator,
    pub retries_amount_processed: RetriesAmountAccumulator,
    pub connector_success_rate: SuccessRateAccumulator,
    pub payments_distribution: PaymentsDistributionAccumulator,
    pub failure_reasons_distribution: FailureReasonsDistributionAccumulator,
    pub debit_routing: DebitRoutingAccumulator,
}


impl api::PaymentToken for Fiservemea {}

impl api::PaymentToken for for Fiservemea


File: crates/analytics/src/payment_intents/core.rs
 Public functions: 3

#![allow(dead_code)]
use std::collections::{HashMap, HashSet};

use api_models::analytics::{
    payment_intents::{
        MetricsBucketResponse, PaymentIntentDimensions, PaymentIntentMetrics,
        PaymentIntentMetricsBucketIdentifier,
    },
    GetPaymentIntentFiltersRequest, GetPaymentIntentMetricRequest, PaymentIntentFilterValue,
    PaymentIntentFiltersResponse, PaymentIntentsAnalyticsMetadata, PaymentIntentsMetricsResponse,
};
use bigdecimal::ToPrimitive;
use common_enums::Currency;
use common_utils::{errors::CustomResult, types::TimeRange};
use currency_conversion::{conversion::convert, types::ExchangeRates};
use error_stack::ResultExt;
use router_env::{
    instrument, logger,
    tracing::{self, Instrument},
};

use super::{
    filters::{get_payment_intent_filter_for_dimension, PaymentIntentFilterRow},
    metrics::PaymentIntentMetricRow,
    sankey::{get_sankey_data, SankeyRow},
    PaymentIntentMetricsAccumulator,
};
use crate::{
    enums::AuthInfo,
    errors::{AnalyticsError, AnalyticsResult},
    metrics,
    payment_intents::PaymentIntentMetricAccumulator,
    AnalyticsProvider,
};

#[derive(Debug)]
pub enum TaskType {
    MetricTask(
        PaymentIntentMetrics,
        CustomResult<
            HashSet<(PaymentIntentMetricsBucketIdentifier, PaymentIntentMetricRow)>,
            AnalyticsError,
        >,
    ),
}

#[instrument(skip_all)]
pub async fn get_sankey(
    pool: &AnalyticsProvider,
    auth: &AuthInfo,
    req: TimeRange,
) -> AnalyticsResult<Vec<SankeyRow>> {
    match pool {
        AnalyticsProvider::Sqlx(_) => Err(AnalyticsError::NotImplemented(
            "Sankey not implemented for sqlx",
        ))?,
        AnalyticsProvider::Clickhouse(ckh_pool)
        | AnalyticsProvider::CombinedCkh(_, ckh_pool)
        | AnalyticsProvider::CombinedSqlx(_, ckh_pool) => {
            let sankey_rows = get_sankey_data(ckh_pool, auth, &req)
                .await
                .change_context(AnalyticsError::UnknownError)?;
            Ok(sankey_rows)
        }
    }
}

#[instrument(skip_all)]
pub async fn get_metrics(
    pool: &AnalyticsProvider,
    ex_rates: &Option<ExchangeRates>,
    auth: &AuthInfo,
    req: GetPaymentIntentMetricRequest,
) -> AnalyticsResult<PaymentIntentsMetricsResponse<MetricsBucketResponse>> {
    let mut metrics_accumulator: HashMap<
        PaymentIntentMetricsBucketIdentifier,
        PaymentIntentMetricsAccumulator,
    > = HashMap::new();

    let mut set = tokio::task::JoinSet::new();
    for metric_type in req.metrics.iter().cloned() {
        let req = req.clone();
        let pool = pool.clone();
        let task_span = tracing::debug_span!(
            "analytics_payment_intents_metrics_query",
            payment_metric = metric_type.as_ref()
        );

        // TODO: lifetime issues with joinset,
        // can be optimized away if joinset lifetime requirements are relaxed
        let auth_scoped = auth.to_owned();
        set.spawn(
            async move {
                let data = pool
                    .get_payment_intent_metrics(
                        &metric_type,
                        &req.group_by_names.clone(),
                        &auth_scoped,
                        &req.filters,
                        req.time_series.map(|t| t.granularity),
                        &req.time_range,
                    )
                    .await
                    .change_context(AnalyticsError::UnknownError);
                TaskType::MetricTask(metric_type, data)
            }
            .instrument(task_span),
        );
    }

    while let Some(task_type) = set
        .join_next()
        .await
        .transpose()
        .change_context(AnalyticsError::UnknownError)?
    {
        match task_type {
            TaskType::MetricTask(metric, data) => {
                let data = data?;
                let attributes = router_env::metric_attributes!(
                    ("metric_type", metric.to_string()),
                    ("source", pool.to_string()),
                );

                let value = u64::try_from(data.len());
                if let Ok(val) = value {
                    metrics::BUCKETS_FETCHED.record(val, attributes);
                    logger::debug!("Attributes: {:?}, Buckets fetched: {}", attributes, val);
                }

                for (id, value) in data {
                    logger::debug!(bucket_id=?id, bucket_value=?value, "Bucket row for metric {metric}");
                    let metrics_builder = metrics_accumulator.entry(id).or_default();
                    match metric {
                        PaymentIntentMetrics::SuccessfulSmartRetries
                        | PaymentIntentMetrics::SessionizedSuccessfulSmartRetries => {
                            metrics_builder
                                .successful_smart_retries
                                .add_metrics_bucket(&value)
                        }
                        PaymentIntentMetrics::TotalSmartRetries
                        | PaymentIntentMetrics::SessionizedTotalSmartRetries => metrics_builder
                            .total_smart_retries
                            .add_metrics_bucket(&value),
                        PaymentIntentMetrics::SmartRetriedAmount
                        | PaymentIntentMetrics::SessionizedSmartRetriedAmount => metrics_builder
                            .smart_retried_amount
                            .add_metrics_bucket(&value),
                        PaymentIntentMetrics::PaymentIntentCount
                        | PaymentIntentMetrics::SessionizedPaymentIntentCount => metrics_builder
                            .payment_intent_count
                            .add_metrics_bucket(&value),
                        PaymentIntentMetrics::PaymentsSuccessRate
                        | PaymentIntentMetrics::SessionizedPaymentsSuccessRate => metrics_builder
                            .payments_success_rate
                            .add_metrics_bucket(&value),
                        PaymentIntentMetrics::SessionizedPaymentProcessedAmount
                        | PaymentIntentMetrics::PaymentProcessedAmount => metrics_builder
                            .payment_processed_amount
                            .add_metrics_bucket(&value),
                        PaymentIntentMetrics::SessionizedPaymentsDistribution => metrics_builder
                            .payments_distribution
                            .add_metrics_bucket(&value),
                    }
                }

                logger::debug!(
                    "Analytics Accumulated Results: metric: {}, results: {:#?}",
                    metric,
                    metrics_accumulator
                );
            }
        }
    }

    let mut success = 0;
    let mut success_without_smart_retries = 0;
    let mut total_smart_retried_amount = 0;
    let mut total_smart_retried_amount_in_usd = 0;
    let mut total_smart_retried_amount_without_smart_retries = 0;
    let mut total_smart_retried_amount_without_smart_retries_in_usd = 0;
    let mut total = 0;
    let mut total_payment_processed_amount = 0;
    let mut total_payment_processed_amount_in_usd = 0;
    let mut total_payment_processed_count = 0;
    let mut total_payment_processed_amount_without_smart_retries = 0;
    let mut total_payment_processed_amount_without_smart_retries_in_usd = 0;
    let mut total_payment_processed_count_without_smart_retries = 0;
    let query_data: Vec<MetricsBucketResponse> = metrics_accumulator
        .into_iter()
        .map(|(id, val)| {
            let mut collected_values = val.collect();
            if let Some(success_count) = collected_values.successful_payments {
                success += success_count;
            }
            if let Some(success_count) = collected_values.successful_payments_without_smart_retries
            {
                success_without_smart_retries += success_count;
            }
            if let Some(total_count) = collected_values.total_payments {
                total += total_count;
            }
            if let Some(retried_amount) = collected_values.smart_retried_amount {
                let amount_in_usd = if let Some(ex_rates) = ex_rates {
                    id.currency
                        .and_then(|currency| {
                            i64::try_from(retried_amount)
                                .inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
                                .ok()
                                .and_then(|amount_i64| {
                                    convert(ex_rates, currency, Currency::USD, amount_i64)
                                        .inspect_err(|e| {
                                            logger::error!("Currency conversion error: {:?}", e)
                                        })
                                        .ok()
                                })
                        })
                        .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
                        .unwrap_or_default()
                } else {
                    None
                };
                collected_values.smart_retried_amount_in_usd = amount_in_usd;
                total_smart_retried_amount += retried_amount;
                total_smart_retried_amount_in_usd += amount_in_usd.unwrap_or(0);
            }
            if let Some(retried_amount) =
                collected_values.smart_retried_amount_without_smart_retries
            {
                let amount_in_usd = if let Some(ex_rates) = ex_rates {
                    id.currency
                        .and_then(|currency| {
                            i64::try_from(retried_amount)
                                .inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
                                .ok()
                                .and_then(|amount_i64| {
                                    convert(ex_rates, currency, Currency::USD, amount_i64)
                                        .inspect_err(|e| {
                                            logger::error!("Currency conversion error: {:?}", e)
                                        })
                                        .ok()
                                })
                        })
                        .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
                        .unwrap_or_default()
                } else {
                    None
                };
                collected_values.smart_retried_amount_without_smart_retries_in_usd = amount_in_usd;
                total_smart_retried_amount_without_smart_retries += retried_amount;
                total_smart_retried_amount_without_smart_retries_in_usd +=
                    amount_in_usd.unwrap_or(0);
            }
            if let Some(amount) = collected_values.payment_processed_amount {
                let amount_in_usd = if let Some(ex_rates) = ex_rates {
                    id.currency
                        .and_then(|currency| {
                            i64::try_from(amount)
                                .inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
                                .ok()
                                .and_then(|amount_i64| {
                                    convert(ex_rates, currency, Currency::USD, amount_i64)
                                        .inspect_err(|e| {
                                            logger::error!("Currency conversion error: {:?}", e)
                                        })
                                        .ok()
                                })
                        })
                        .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
                        .unwrap_or_default()
                } else {
                    None
                };
                collected_values.payment_processed_amount_in_usd = amount_in_usd;
                total_payment_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
                total_payment_processed_amount += amount;
            }
            if let Some(count) = collected_values.payment_processed_count {
                total_payment_processed_count += count;
            }
            if let Some(amount) = collected_values.payment_processed_amount_without_smart_retries {
                let amount_in_usd = if let Some(ex_rates) = ex_rates {
                    id.currency
                        .and_then(|currency| {
                            i64::try_from(amount)
                                .inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
                                .ok()
                                .and_then(|amount_i64| {
                                    convert(ex_rates, currency, Currency::USD, amount_i64)
                                        .inspect_err(|e| {
                                            logger::error!("Currency conversion error: {:?}", e)
                                        })
                                        .ok()
                                })
                        })
                        .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
                        .unwrap_or_default()
                } else {
                    None
                };
                collected_values.payment_processed_amount_without_smart_retries_in_usd =
                    amount_in_usd;
                total_payment_processed_amount_without_smart_retries_in_usd +=
                    amount_in_usd.unwrap_or(0);
                total_payment_processed_amount_without_smart_retries += amount;
            }
            if let Some(count) = collected_values.payment_processed_count_without_smart_retries {
                total_payment_processed_count_without_smart_retries += count;
            }
            MetricsBucketResponse {
                values: collected_values,
                dimensions: id,
            }
        })
        .collect();
    let total_success_rate = match (success, total) {
        (s, t) if t > 0 => Some(f64::from(s) * 100.0 / f64::from(t)),
        _ => None,
    };
    let total_success_rate_without_smart_retries = match (success_without_smart_retries, total) {
        (s, t) if t > 0 => Some(f64::from(s) * 100.0 / f64::from(t)),
        _ => None,
    };
    Ok(PaymentIntentsMetricsResponse {
        query_data,
        meta_data: [PaymentIntentsAnalyticsMetadata {
            total_success_rate,
            total_success_rate_without_smart_retries,
            total_smart_retried_amount: Some(total_smart_retried_amount),
            total_smart_retried_amount_without_smart_retries: Some(
                total_smart_retried_amount_without_smart_retries,
            ),
            total_payment_processed_amount: Some(total_payment_processed_amount),
            total_payment_processed_amount_without_smart_retries: Some(
                total_payment_processed_amount_without_smart_retries,
            ),
            total_smart_retried_amount_in_usd: if ex_rates.is_some() {
                Some(total_smart_retried_amount_in_usd)
            } else {
                None
            },
            total_smart_retried_amount_without_smart_retries_in_usd: if ex_rates.is_some() {
                Some(total_smart_retried_amount_without_smart_retries_in_usd)
            } else {
                None
            },
            total_payment_processed_amount_in_usd: if ex_rates.is_some() {
                Some(total_payment_processed_amount_in_usd)
            } else {
                None
            },
            total_payment_processed_amount_without_smart_retries_in_usd: if ex_rates.is_some() {
                Some(total_payment_processed_amount_without_smart_retries_in_usd)
            } else {
                None
            },
            total_payment_processed_count: Some(total_payment_processed_count),
            total_payment_processed_count_without_smart_retries: Some(
                total_payment_processed_count_without_smart_retries,
            ),
        }],
    })
}

pub async fn get_filters(
    pool: &AnalyticsProvider,
    req: GetPaymentIntentFiltersRequest,
    merchant_id: &common_utils::id_type::MerchantId,
) -> AnalyticsResult<PaymentIntentFiltersResponse> {
    let mut res = PaymentIntentFiltersResponse::default();

    for dim in req.group_by_names {
        let values = match pool {
                        AnalyticsProvider::Sqlx(pool) => {
                get_payment_intent_filter_for_dimension(dim, merchant_id, &req.time_range, pool)
                    .await
            }
                        AnalyticsProvider::Clickhouse(pool) => {
                get_payment_intent_filter_for_dimension(dim, merchant_id, &req.time_range, pool)
                    .await
            }
                    AnalyticsProvider::CombinedCkh(sqlx_poll, ckh_pool) => {
                let ckh_result = get_payment_intent_filter_for_dimension(
                    dim,
                    merchant_id,
                    &req.time_range,
                    ckh_pool,
                )
                .await;
                let sqlx_result = get_payment_intent_filter_for_dimension(
                    dim,
                    merchant_id,
                    &req.time_range,
                    sqlx_poll,
                )
                .await;
                match (&sqlx_result, &ckh_result) {
                    (Ok(ref sqlx_res), Ok(ref ckh_res)) if sqlx_res != ckh_res => {
                        router_env::logger::error!(clickhouse_result=?ckh_res, postgres_result=?sqlx_res, "Mismatch between clickhouse & postgres payment intents analytics filters")
                    },
                    _ => {}
                };
                ckh_result
            }
                    AnalyticsProvider::CombinedSqlx(sqlx_poll, ckh_pool) => {
                let ckh_result = get_payment_intent_filter_for_dimension(
                    dim,
                    merchant_id,
                    &req.time_range,
                    ckh_pool,
                )
                .await;
                let sqlx_result = get_payment_intent_filter_for_dimension(
                    dim,
                    merchant_id,
                    &req.time_range,
                    sqlx_poll,
                )
                .await;
                match (&sqlx_result, &ckh_result) {
                    (Ok(ref sqlx_res), Ok(ref ckh_res)) if sqlx_res != ckh_res => {
                        router_env::logger::error!(clickhouse_result=?ckh_res, postgres_result=?sqlx_res, "Mismatch between clickhouse & postgres payment intents analytics filters")
                    },
                    _ => {}
                };
                sqlx_result
            }
        }
        .change_context(AnalyticsError::UnknownError)?
        .into_iter()
        .filter_map(|fil: PaymentIntentFilterRow| match dim {
            PaymentIntentDimensions::PaymentIntentStatus => fil.status.map(|i| i.as_ref().to_string()),
            PaymentIntentDimensions::Currency => fil.currency.map(|i| i.as_ref().to_string()),
            PaymentIntentDimensions::ProfileId => fil.profile_id,
            PaymentIntentDimensions::Connector => fil.connector,
            PaymentIntentDimensions::AuthType => fil.authentication_type.map(|i| i.as_ref().to_string()),
            PaymentIntentDimensions::PaymentMethod => fil.payment_method,
            PaymentIntentDimensions::PaymentMethodType => fil.payment_method_type,
            PaymentIntentDimensions::CardNetwork => fil.card_network,
            PaymentIntentDimensions::MerchantId => fil.merchant_id,
            PaymentIntentDimensions::CardLast4 => fil.card_last_4,
            PaymentIntentDimensions::CardIssuer => fil.card_issuer,
            PaymentIntentDimensions::ErrorReason => fil.error_reason,
        })
        .collect::<Vec<String>>();
        res.query_data.push(PaymentIntentFilterValue {
            dimension: dim,
            values,
        })
    }
    Ok(res)
}


impl PaymentMethods {
    pub fn server(state: AppState) -> Scope {
        let mut route = web::scope("/v2/payment-methods").app_data(web::Data::new(state));

        #[cfg(feature = "olap")]
        {
            route =
                route.service(web::resource("/filter").route(
                    web::get().to(
                        payment_methods::list_countries_currencies_for_connector_payment_method,
                    ),
                ));
        }
        #[cfg(feature = "oltp")]
        {
            route = route
                .service(
                    web::resource("")
                        .route(web::post().to(payment_methods::create_payment_method_api)),
                )
                .service(
                    web::resource("/create-intent")
                        .route(web::post().to(payment_methods::create_payment_method_intent_api)),
                );

            route = route.service(
                web::scope("/{id}")
                    .service(
                        web::resource("")
                            .route(web::get().to(payment_methods::payment_method_retrieve_api))
                            .route(web::delete().to(payment_methods::payment_method_delete_api)),
                    )
                    .service(web::resource("/list-enabled-payment-methods").route(
                        web::get().to(payment_methods::payment_method_session_list_payment_methods),
                    ))
                    .service(
                        web::resource("/update-saved-payment-method")
                            .route(web::put().to(payment_methods::payment_method_update_api)),
                    )
                    .service(
                        web::resource("/get-token")
                            .route(web::get().to(payment_methods::get_payment_method_token_data)),
                    ),
            );
        }

        route
    }
}


impl ConnectorSpecifications for Bamboraapac {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&BAMBORAAPAC_CONNECTOR_INFO)
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        Some(&*BAMBORAAPAC_SUPPORTED_PAYMENT_METHODS)
    }

    fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
        Some(&BAMBORAAPAC_SUPPORTED_WEBHOOK_FLOWS)
    }
}

impl ConnectorSpecifications for for Bamboraapac


File: crates/hyperswitch_connectors/src/connectors/payeezy/transformers.rs
 Public structs: 14

use cards::CardNumber;
use common_enums::{enums, AttemptStatus, CaptureMethod, Currency, PaymentMethod};
use common_utils::{errors::ParsingError, ext_traits::Encode};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::Execute,
    router_request_types::ResponseId,
    router_response_types::{MandateReference, PaymentsResponseData, RefundsResponseData},
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        RefundsRouterData,
    },
};
use hyperswitch_interfaces::{api::CurrencyUnit, errors::ConnectorError};
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{
        get_amount_as_string, get_unimplemented_payment_method_error_message, to_connector_meta,
        CardData, CardIssuer, RouterData as _,
    },
};

#[derive(Debug, Serialize)]
pub struct PayeezyRouterData<T> {
    pub amount: String,
    pub router_data: T,
}

impl<T> TryFrom<(&CurrencyUnit, Currency, i64, T)> for PayeezyRouterData<T> {
    type Error = error_stack::Report<ConnectorError>;

    fn try_from(
        (currency_unit, currency, amount, router_data): (&CurrencyUnit, Currency, i64, T),
    ) -> Result<Self, Self::Error> {
        let amount = get_amount_as_string(currency_unit, amount, currency)?;
        Ok(Self {
            amount,
            router_data,
        })
    }
}

#[derive(Serialize, Debug)]
pub struct PayeezyCard {
    #[serde(rename = "type")]
    pub card_type: PayeezyCardType,
    pub cardholder_name: Secret<String>,
    pub card_number: CardNumber,
    pub exp_date: Secret<String>,
    pub cvv: Secret<String>,
}

#[derive(Serialize, Debug)]
pub enum PayeezyCardType {
    #[serde(rename = "American Express")]
    AmericanExpress,
    Visa,
    Mastercard,
    Discover,
}

impl TryFrom<CardIssuer> for PayeezyCardType {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(issuer: CardIssuer) -> Result<Self, Self::Error> {
        match issuer {
            CardIssuer::AmericanExpress => Ok(Self::AmericanExpress),
            CardIssuer::Master => Ok(Self::Mastercard),
            CardIssuer::Discover => Ok(Self::Discover),
            CardIssuer::Visa => Ok(Self::Visa),

            CardIssuer::Maestro
            | CardIssuer::DinersClub
            | CardIssuer::JCB
            | CardIssuer::CarteBlanche
            | CardIssuer::CartesBancaires => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("Payeezy"),
            ))?,
        }
    }
}

#[derive(Serialize, Debug)]
#[serde(untagged)]
pub enum PayeezyPaymentMethod {
    PayeezyCard(PayeezyCard),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum PayeezyPaymentMethodType {
    CreditCard,
}

#[derive(Serialize, Debug)]
pub struct PayeezyPaymentsRequest {
    pub merchant_ref: String,
    pub transaction_type: PayeezyTransactionType,
    pub method: PayeezyPaymentMethodType,
    pub amount: String,
    pub currency_code: String,
    pub credit_card: PayeezyPaymentMethod,
    pub stored_credentials: Option<StoredCredentials>,
    pub reference: String,
}

#[derive(Serialize, Debug)]
pub struct StoredCredentials {
    pub sequence: Sequence,
    pub initiator: Initiator,
    pub is_scheduled: bool,
    pub cardbrand_original_transaction_id: Option<Secret<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum Sequence {
    First,
    Subsequent,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum Initiator {
    Merchant,
    CardHolder,
}

impl TryFrom<&PayeezyRouterData<&PaymentsAuthorizeRouterData>> for PayeezyPaymentsRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: &PayeezyRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.payment_method {
            PaymentMethod::Card => get_card_specific_payment_data(item),

            PaymentMethod::CardRedirect
            | PaymentMethod::PayLater
            | PaymentMethod::Wallet
            | PaymentMethod::BankRedirect
            | PaymentMethod::BankTransfer
            | PaymentMethod::Crypto
            | PaymentMethod::BankDebit
            | PaymentMethod::Reward
            | PaymentMethod::RealTimePayment
            | PaymentMethod::MobilePayment
            | PaymentMethod::Upi
            | PaymentMethod::Voucher
            | PaymentMethod::OpenBanking
            | PaymentMethod::GiftCard => {
                Err(ConnectorError::NotImplemented("Payment methods".to_string()).into())
            }
        }
    }
}

fn get_card_specific_payment_data(
    item: &PayeezyRouterData<&PaymentsAuthorizeRouterData>,
) -> Result<PayeezyPaymentsRequest, error_stack::Report<ConnectorError>> {
    let merchant_ref = item.router_data.attempt_id.to_string();
    let method = PayeezyPaymentMethodType::CreditCard;
    let amount = item.amount.clone();
    let currency_code = item.router_data.request.currency.to_string();
    let credit_card = get_payment_method_data(item)?;
    let (transaction_type, stored_credentials) =
        get_transaction_type_and_stored_creds(item.router_data)?;
    Ok(PayeezyPaymentsRequest {
        merchant_ref,
        transaction_type,
        method,
        amount,
        currency_code,
        credit_card,
        stored_credentials,
        reference: item.router_data.connector_request_reference_id.clone(),
    })
}
fn get_transaction_type_and_stored_creds(
    item: &PaymentsAuthorizeRouterData,
) -> Result<(PayeezyTransactionType, Option<StoredCredentials>), error_stack::Report<ConnectorError>>
{
    let connector_mandate_id = item.request.mandate_id.as_ref().and_then(|mandate_ids| {
        match mandate_ids.mandate_reference_id.clone() {
            Some(api_models::payments::MandateReferenceId::ConnectorMandateId(
                connector_mandate_ids,
            )) => connector_mandate_ids.get_connector_mandate_id(),
            _ => None,
        }
    });
    let (transaction_type, stored_credentials) =
        if is_mandate_payment(item, connector_mandate_id.as_ref()) {
            // Mandate payment
            (
                PayeezyTransactionType::Recurring,
                Some(StoredCredentials {
                    // connector_mandate_id is not present then it is a First payment, else it is a Subsequent mandate payment
                    sequence: match connector_mandate_id.is_some() {
                        true => Sequence::Subsequent,
                        false => Sequence::First,
                    },
                    // off_session true denotes the customer not present during the checkout process. In other cases customer present at the checkout.
                    initiator: match item.request.off_session {
                        Some(true) => Initiator::Merchant,
                        _ => Initiator::CardHolder,
                    },
                    is_scheduled: true,
                    // In case of first mandate payment connector_mandate_id would be None, otherwise holds some value
                    cardbrand_original_transaction_id: connector_mandate_id.map(Secret::new),
                }),
            )
        } else {
            match item.request.capture_method {
                Some(CaptureMethod::Manual) => Ok((PayeezyTransactionType::Authorize, None)),
                Some(CaptureMethod::SequentialAutomatic) | Some(CaptureMethod::Automatic) => {
                    Ok((PayeezyTransactionType::Purchase, None))
                }

                Some(CaptureMethod::ManualMultiple) | Some(CaptureMethod::Scheduled) | None => {
                    Err(ConnectorError::FlowNotSupported {
                        flow: item.request.capture_method.unwrap_or_default().to_string(),
                        connector: "Payeezy".to_string(),
                    })
                }
            }?
        };
    Ok((transaction_type, stored_credentials))
}

fn is_mandate_payment(
    item: &PaymentsAuthorizeRouterData,
    connector_mandate_id: Option<&String>,
) -> bool {
    item.request.setup_mandate_details.is_some() || connector_mandate_id.is_some()
}

fn get_payment_method_data(
    item: &PayeezyRouterData<&PaymentsAuthorizeRouterData>,
) -> Result<PayeezyPaymentMethod, error_stack::Report<ConnectorError>> {
    match item.router_data.request.payment_method_data {
        PaymentMethodData::Card(ref card) => {
            let card_type = PayeezyCardType::try_from(card.get_card_issuer()?)?;
            let payeezy_card = PayeezyCard {
                card_type,
                cardholder_name: item
                    .router_data
                    .get_optional_billing_full_name()
                    .unwrap_or(Secret::new("".to_string())),
                card_number: card.card_number.clone(),
                exp_date: card.get_card_expiry_month_year_2_digit_with_delimiter("".to_string())?,
                cvv: card.card_cvc.clone(),
            };
            Ok(PayeezyPaymentMethod::PayeezyCard(payeezy_card))
        }

        PaymentMethodData::CardRedirect(_)
        | PaymentMethodData::Wallet(_)
        | PaymentMethodData::PayLater(_)
        | PaymentMethodData::BankRedirect(_)
        | PaymentMethodData::BankDebit(_)
        | PaymentMethodData::BankTransfer(_)
        | PaymentMethodData::Crypto(_)
        | PaymentMethodData::MandatePayment
        | PaymentMethodData::Reward
        | PaymentMethodData::RealTimePayment(_)
        | PaymentMethodData::MobilePayment(_)
        | PaymentMethodData::Upi(_)
        | PaymentMethodData::Voucher(_)
        | PaymentMethodData::GiftCard(_)
        | PaymentMethodData::OpenBanking(_)
        | PaymentMethodData::CardToken(_)
        | PaymentMethodData::NetworkToken(_)
        | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
            Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("Payeezy"),
            ))?
        }
    }
}

// Auth Struct
pub struct PayeezyAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) api_secret: Secret<String>,
    pub(super) merchant_token: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for PayeezyAuthType {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(item: &ConnectorAuthType) -> Result<Self, Self::Error> {
        if let ConnectorAuthType::SignatureKey {
            api_key,
            key1,
            api_secret,
        } = item
        {
            Ok(Self {
                api_key: api_key.to_owned(),
                api_secret: api_secret.to_owned(),
                merchant_token: key1.to_owned(),
            })
        } else {
            Err(ConnectorError::FailedToObtainAuthType.into())
        }
    }
}
// PaymentsResponse

#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum PayeezyPaymentStatus {
    Approved,
    Declined,
    #[default]
    #[serde(rename = "Not Processed")]
    NotProcessed,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct PayeezyPaymentsResponse {
    pub correlation_id: String,
    pub transaction_status: PayeezyPaymentStatus,
    pub validation_status: String,
    pub transaction_type: PayeezyTransactionType,
    pub transaction_id: String,
    pub transaction_tag: Option<String>,
    pub method: Option<String>,
    pub amount: String,
    pub currency: String,
    pub bank_resp_code: String,
    pub bank_message: String,
    pub gateway_resp_code: String,
    pub gateway_message: String,
    pub stored_credentials: Option<PaymentsStoredCredentials>,
    pub reference: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct PaymentsStoredCredentials {
    cardbrand_original_transaction_id: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct PayeezyCaptureOrVoidRequest {
    transaction_tag: String,
    transaction_type: PayeezyTransactionType,
    amount: String,
    currency_code: String,
}

impl TryFrom<&PayeezyRouterData<&PaymentsCaptureRouterData>> for PayeezyCaptureOrVoidRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(item: &PayeezyRouterData<&PaymentsCaptureRouterData>) -> Result<Self, Self::Error> {
        let metadata: PayeezyPaymentsMetadata =
            to_connector_meta(item.router_data.request.connector_meta.clone())
                .change_context(ConnectorError::RequestEncodingFailed)?;
        Ok(Self {
            transaction_type: PayeezyTransactionType::Capture,
            amount: item.amount.clone(),
            currency_code: item.router_data.request.currency.to_string(),
            transaction_tag: metadata.transaction_tag,
        })
    }
}

impl TryFrom<&PaymentsCancelRouterData> for PayeezyCaptureOrVoidRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(item: &PaymentsCancelRouterData) -> Result<Self, Self::Error> {
        let metadata: PayeezyPaymentsMetadata =
            to_connector_meta(item.request.connector_meta.clone())
                .change_context(ConnectorError::RequestEncodingFailed)?;
        Ok(Self {
            transaction_type: PayeezyTransactionType::Void,
            amount: item
                .request
                .amount
                .ok_or(ConnectorError::RequestEncodingFailed)?
                .to_string(),
            currency_code: item.request.currency.unwrap_or_default().to_string(),
            transaction_tag: metadata.transaction_tag,
        })
    }
}

#[derive(Debug, Deserialize, Serialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum PayeezyTransactionType {
    Authorize,
    Capture,
    Purchase,
    Recurring,
    Void,
    Refund,
    #[default]
    Pending,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct PayeezyPaymentsMetadata {
    transaction_tag: String,
}

impl<F, T> TryFrom<ResponseRouterData<F, PayeezyPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, PayeezyPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let metadata = item
            .response
            .transaction_tag
            .map(|txn_tag| construct_payeezy_payments_metadata(txn_tag).encode_to_value())
            .transpose()
            .change_context(ConnectorError::ResponseHandlingFailed)?;

        let mandate_reference = item
            .response
            .stored_credentials
            .map(|credentials| credentials.cardbrand_original_transaction_id)
            .map(|id| MandateReference {
                connector_mandate_id: Some(id.expose()),
                payment_method_id: None,
                mandate_metadata: None,
                connector_mandate_request_reference_id: None,
            });
        let status = get_status(
            item.response.transaction_status,
            item.response.transaction_type,
        );

        Ok(Self {
            status,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(
                    item.response.transaction_id.clone(),
                ),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(mandate_reference),
                connector_metadata: metadata,
                network_txn_id: None,
                connector_response_reference_id: Some(
                    item.response
                        .reference
                        .unwrap_or(item.response.transaction_id),
                ),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

fn get_status(status: PayeezyPaymentStatus, method: PayeezyTransactionType) -> AttemptStatus {
    match status {
        PayeezyPaymentStatus::Approved => match method {
            PayeezyTransactionType::Authorize => AttemptStatus::Authorized,
            PayeezyTransactionType::Capture
            | PayeezyTransactionType::Purchase
            | PayeezyTransactionType::Recurring => AttemptStatus::Charged,
            PayeezyTransactionType::Void => AttemptStatus::Voided,
            PayeezyTransactionType::Refund | PayeezyTransactionType::Pending => {
                AttemptStatus::Pending
            }
        },
        PayeezyPaymentStatus::Declined | PayeezyPaymentStatus::NotProcessed => match method {
            PayeezyTransactionType::Capture => AttemptStatus::CaptureFailed,
            PayeezyTransactionType::Authorize
            | PayeezyTransactionType::Purchase
            | PayeezyTransactionType::Recurring => AttemptStatus::AuthorizationFailed,
            PayeezyTransactionType::Void => AttemptStatus::VoidFailed,
            PayeezyTransactionType::Refund | PayeezyTransactionType::Pending => {
                AttemptStatus::Pending
            }
        },
    }
}

// REFUND :
// Type definition for RefundRequest
#[derive(Debug, Serialize)]
pub struct PayeezyRefundRequest {
    transaction_tag: String,
    transaction_type: PayeezyTransactionType,
    amount: String,
    currency_code: String,
}

impl<F> TryFrom<&PayeezyRouterData<&RefundsRouterData<F>>> for PayeezyRefundRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(item: &PayeezyRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let metadata: PayeezyPaymentsMetadata =
            to_connector_meta(item.router_data.request.connector_metadata.clone())
                .change_context(ConnectorError::RequestEncodingFailed)?;
        Ok(Self {
            transaction_type: PayeezyTransactionType::Refund,
            amount: item.amount.clone(),
            currency_code: item.router_data.request.currency.to_string(),
            transaction_tag: metadata.transaction_tag,
        })
    }
}

// Type definition for Refund Response

#[derive(Debug, Deserialize, Default, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum RefundStatus {
    Approved,
    Declined,
    #[default]
    #[serde(rename = "Not Processed")]
    NotProcessed,
}

impl From<RefundStatus> for enums::RefundStatus {
    fn from(item: RefundStatus) -> Self {
        match item {
            RefundStatus::Approved => Self::Success,
            RefundStatus::Declined => Self::Failure,
            RefundStatus::NotProcessed => Self::Pending,
        }
    }
}

#[derive(Deserialize, Debug, Serialize)]
pub struct RefundResponse {
    pub correlation_id: String,
    pub transaction_status: RefundStatus,
    pub validation_status: String,
    pub transaction_type: String,
    pub transaction_id: String,
    pub transaction_tag: Option<String>,
    pub method: Option<String>,
    pub amount: String,
    pub currency: String,
    pub bank_resp_code: String,
    pub bank_message: String,
    pub gateway_resp_code: String,
    pub gateway_message: String,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<ParsingError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.transaction_id,
                refund_status: enums::RefundStatus::from(item.response.transaction_status),
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Message {
    pub code: String,
    pub description: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct PayeezyError {
    pub messages: Vec<Message>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct PayeezyErrorResponse {
    pub transaction_status: String,
    #[serde(rename = "Error")]
    pub error: PayeezyError,
}

fn construct_payeezy_payments_metadata(transaction_tag: String) -> PayeezyPaymentsMetadata {
    PayeezyPaymentsMetadata { transaction_tag }
}


pub async fn save_network_token_in_locker(
    _state: &SessionState,
    _merchant_context: &domain::MerchantContext,
    _card_data: &domain::Card,
    _payment_method_request: api::PaymentMethodCreate,
) -> RouterResult<(
    Option<api_models::payment_methods::PaymentMethodResponse>,
    Option<payment_methods::transformers::DataDuplicationCheck>,
    Option<String>,
)> {
    todo!()
}


File: crates/storage_impl/src/mock_db/payout_attempt.rs

use common_utils::errors::CustomResult;
use diesel_models::enums as storage_enums;
use hyperswitch_domain_models::payouts::{
    payout_attempt::{
        PayoutAttempt, PayoutAttemptInterface, PayoutAttemptNew, PayoutAttemptUpdate,
    },
    payouts::Payouts,
};

use super::MockDb;
use crate::errors::StorageError;

#[async_trait::async_trait]
impl PayoutAttemptInterface for MockDb {
    type Error = StorageError;
    async fn update_payout_attempt(
        &self,
        _this: &PayoutAttempt,
        _payout_attempt_update: PayoutAttemptUpdate,
        _payouts: &Payouts,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PayoutAttempt, StorageError> {
        // TODO: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    async fn insert_payout_attempt(
        &self,
        _payout_attempt: PayoutAttemptNew,
        _payouts: &Payouts,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PayoutAttempt, StorageError> {
        // TODO: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    async fn find_payout_attempt_by_merchant_id_payout_attempt_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _payout_attempt_id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PayoutAttempt, StorageError> {
        // TODO: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    async fn find_payout_attempt_by_merchant_id_connector_payout_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_payout_id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PayoutAttempt, StorageError> {
        // TODO: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    async fn get_filters_for_payouts(
        &self,
        _payouts: &[Payouts],
        _merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<
        hyperswitch_domain_models::payouts::payout_attempt::PayoutListFilters,
        StorageError,
    > {
        Err(StorageError::MockDbError)?
    }

    async fn find_payout_attempt_by_merchant_id_merchant_order_reference_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _merchant_order_reference_id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PayoutAttempt, StorageError> {
        Err(StorageError::MockDbError)?
    }
}


pub fn validate(&self) -> Result<(), String> {
                #(#validation_checks)*
                Ok(())
            }


pub struct SquareErrorDetails {
    pub category: Option<String>,
    pub code: Option<String>,
    pub detail: Option<String>,
}


pub async fn retrieve_poll_status(
    state: SessionState,
    req: crate::types::api::PollId,
    merchant_context: domain::MerchantContext,
) -> RouterResponse<PollResponse> {
    let redis_conn = state
        .store
        .get_redis_conn()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get redis connection")?;
    let request_poll_id = req.poll_id;
    // prepend 'poll_{merchant_id}_' to restrict access to only fetching Poll IDs, as this is a freely passed string in the request
    let poll_id = super::utils::get_poll_id(
        merchant_context.get_merchant_account().get_id(),
        request_poll_id.clone(),
    );
    let redis_value = redis_conn
        .get_key::<Option<String>>(&poll_id.as_str().into())
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!(
                "Error while fetching the value for {} from redis",
                poll_id.clone()
            )
        })?
        .ok_or(errors::ApiErrorResponse::PollNotFound {
            id: request_poll_id.clone(),
        })?;
    let status = redis_value
        .parse_enum("PollStatus")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error while parsing PollStatus")?;
    let poll_response = PollResponse {
        poll_id: request_poll_id,
        status,
    };
    Ok(ApplicationResponse::Json(poll_response))
}