File: crates/hyperswitch_connectors/src/connectors/elavon/transformers.rs
 Public structs: 12

use cards::CardNumber;
use common_enums::{enums, Currency};
use common_utils::{pii::Email, types::StringMajorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::{PaymentsAuthorizeData, ResponseId},
    router_response_types::{MandateReference, PaymentsResponseData, RefundsResponseData},
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{consts::NO_ERROR_CODE, errors};
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Deserializer, Serialize};

use crate::{
    types::{
        PaymentsCaptureResponseRouterData, PaymentsSyncResponseRouterData,
        RefundsResponseRouterData, ResponseRouterData,
    },
    utils::{CardData, PaymentsAuthorizeRequestData, RefundsRequestData, RouterData as _},
};

pub struct ElavonRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}

impl<T> From<(StringMajorUnit, T)> for ElavonRouterData<T> {
    fn from((amount, item): (StringMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum TransactionType {
    CcSale,
    CcAuthOnly,
    CcComplete,
    CcReturn,
    TxnQuery,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub enum SyncTransactionType {
    Sale,
    AuthOnly,
    Return,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum ElavonPaymentsRequest {
    Card(CardPaymentRequest),
    MandatePayment(MandatePaymentRequest),
}
#[derive(Debug, Serialize)]
pub struct CardPaymentRequest {
    pub ssl_transaction_type: TransactionType,
    pub ssl_account_id: Secret<String>,
    pub ssl_user_id: Secret<String>,
    pub ssl_pin: Secret<String>,
    pub ssl_amount: StringMajorUnit,
    pub ssl_card_number: CardNumber,
    pub ssl_exp_date: Secret<String>,
    pub ssl_cvv2cvc2: Secret<String>,
    pub ssl_email: Email,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ssl_add_token: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ssl_get_token: Option<String>,
    pub ssl_transaction_currency: Currency,
}
#[derive(Debug, Serialize)]
pub struct MandatePaymentRequest {
    pub ssl_transaction_type: TransactionType,
    pub ssl_account_id: Secret<String>,
    pub ssl_user_id: Secret<String>,
    pub ssl_pin: Secret<String>,
    pub ssl_amount: StringMajorUnit,
    pub ssl_email: Email,
    pub ssl_token: Secret<String>,
}

impl TryFrom<&ElavonRouterData<&PaymentsAuthorizeRouterData>> for ElavonPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &ElavonRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let auth = ElavonAuthType::try_from(&item.router_data.connector_auth_type)?;
        if item.router_data.is_three_ds() {
            Err(errors::ConnectorError::NotSupported {
                message: "Card 3DS".to_string(),
                connector: "Elavon",
            })?
        };
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => Ok(Self::Card(CardPaymentRequest {
                ssl_transaction_type: match item.router_data.request.is_auto_capture()? {
                    true => TransactionType::CcSale,
                    false => TransactionType::CcAuthOnly,
                },
                ssl_account_id: auth.account_id.clone(),
                ssl_user_id: auth.user_id.clone(),
                ssl_pin: auth.pin.clone(),
                ssl_amount: item.amount.clone(),
                ssl_card_number: req_card.card_number.clone(),
                ssl_exp_date: req_card.get_expiry_date_as_mmyy()?,
                ssl_cvv2cvc2: req_card.card_cvc,
                ssl_email: item.router_data.get_billing_email()?,
                ssl_add_token: match item.router_data.request.is_mandate_payment() {
                    true => Some("Y".to_string()),
                    false => None,
                },
                ssl_get_token: match item.router_data.request.is_mandate_payment() {
                    true => Some("Y".to_string()),
                    false => None,
                },
                ssl_transaction_currency: item.router_data.request.currency,
            })),
            PaymentMethodData::MandatePayment => Ok(Self::MandatePayment(MandatePaymentRequest {
                ssl_transaction_type: match item.router_data.request.is_auto_capture()? {
                    true => TransactionType::CcSale,
                    false => TransactionType::CcAuthOnly,
                },
                ssl_account_id: auth.account_id.clone(),
                ssl_user_id: auth.user_id.clone(),
                ssl_pin: auth.pin.clone(),
                ssl_amount: item.amount.clone(),
                ssl_email: item.router_data.get_billing_email()?,
                ssl_token: Secret::new(item.router_data.request.get_connector_mandate_id()?),
            })),
            _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
        }
    }
}

pub struct ElavonAuthType {
    pub(super) account_id: Secret<String>,
    pub(super) user_id: Secret<String>,
    pub(super) pin: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for ElavonAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => Ok(Self {
                account_id: api_key.to_owned(),
                user_id: key1.to_owned(),
                pin: api_secret.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
enum SslResult {
    #[serde(rename = "0")]
    ImportedBatchFile,
    #[serde(other)]
    DeclineOrUnauthorized,
}

#[derive(Debug, Clone, Serialize)]
pub struct ElavonPaymentsResponse {
    pub result: ElavonResult,
}
#[derive(Debug, Clone, Serialize)]
pub enum ElavonResult {
    Success(PaymentResponse),
    Error(ElavonErrorResponse),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ElavonErrorResponse {
    error_code: Option<String>,
    error_message: String,
    error_name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentResponse {
    ssl_result: SslResult,
    ssl_txn_id: String,
    ssl_result_message: String,
    ssl_token: Option<Secret<String>>,
}
impl<'de> Deserialize<'de> for ElavonPaymentsResponse {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Deserialize, Debug)]
        #[serde(rename = "txn")]
        struct XmlResponse {
            // Error fields
            #[serde(rename = "errorCode", default)]
            error_code: Option<String>,
            #[serde(rename = "errorMessage", default)]
            error_message: Option<String>,
            #[serde(rename = "errorName", default)]
            error_name: Option<String>,

            // Success fields
            #[serde(rename = "ssl_result", default)]
            ssl_result: Option<SslResult>,
            #[serde(rename = "ssl_txn_id", default)]
            ssl_txn_id: Option<String>,
            #[serde(rename = "ssl_result_message", default)]
            ssl_result_message: Option<String>,
            #[serde(rename = "ssl_token", default)]
            ssl_token: Option<Secret<String>>,
        }

        let xml_res = XmlResponse::deserialize(deserializer)?;

        let result = match (xml_res.error_message.clone(), xml_res.error_name.clone()) {
            (Some(error_message), Some(error_name)) => ElavonResult::Error(ElavonErrorResponse {
                error_code: xml_res.error_code.clone(),
                error_message,
                error_name,
            }),
            _ => {
                if let (Some(ssl_result), Some(ssl_txn_id), Some(ssl_result_message)) = (
                    xml_res.ssl_result.clone(),
                    xml_res.ssl_txn_id.clone(),
                    xml_res.ssl_result_message.clone(),
                ) {
                    ElavonResult::Success(PaymentResponse {
                        ssl_result,
                        ssl_txn_id,
                        ssl_result_message,
                        ssl_token: xml_res.ssl_token.clone(),
                    })
                } else {
                    return Err(serde::de::Error::custom(
                        "Invalid Response XML structure - neither error nor success",
                    ));
                }
            }
        };

        Ok(Self { result })
    }
}
impl<F>
    TryFrom<
        ResponseRouterData<F, ElavonPaymentsResponse, PaymentsAuthorizeData, PaymentsResponseData>,
    > for RouterData<F, PaymentsAuthorizeData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            ElavonPaymentsResponse,
            PaymentsAuthorizeData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let status =
            get_payment_status(&item.response.result, item.data.request.is_auto_capture()?);
        let response = match &item.response.result {
            ElavonResult::Error(error) => Err(ErrorResponse {
                code: error
                    .error_code
                    .clone()
                    .unwrap_or(NO_ERROR_CODE.to_string()),
                message: error.error_message.clone(),
                reason: Some(error.error_message.clone()),
                attempt_status: None,
                connector_transaction_id: None,
                status_code: item.http_code,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
            ElavonResult::Success(response) => {
                if status == enums::AttemptStatus::Failure {
                    Err(ErrorResponse {
                        code: response.ssl_result_message.clone(),
                        message: response.ssl_result_message.clone(),
                        reason: Some(response.ssl_result_message.clone()),
                        attempt_status: None,
                        connector_transaction_id: Some(response.ssl_txn_id.clone()),
                        status_code: item.http_code,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(
                            response.ssl_txn_id.clone(),
                        ),
                        redirection_data: Box::new(None),
                        mandate_reference: Box::new(Some(MandateReference {
                            connector_mandate_id: response
                                .ssl_token
                                .as_ref()
                                .map(|secret| secret.clone().expose()),
                            payment_method_id: None,
                            mandate_metadata: None,
                            connector_mandate_request_reference_id: None,
                        })),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: Some(response.ssl_txn_id.clone()),
                        incremental_authorization_allowed: None,
                        charges: None,
                    })
                }
            }
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub enum TransactionSyncStatus {
    PEN, // Pended
    OPN, // Unpended / release / open
    REV, // Review
    STL, // Settled
    PST, // Failed due to post-auth rule
    FPR, // Failed due to fraud prevention rules
    PRE, // Failed due to pre-auth rule
}

#[derive(Debug, Serialize)]
#[serde(rename = "txn")]
pub struct PaymentsCaptureRequest {
    pub ssl_transaction_type: TransactionType,
    pub ssl_account_id: Secret<String>,
    pub ssl_user_id: Secret<String>,
    pub ssl_pin: Secret<String>,
    pub ssl_amount: StringMajorUnit,
    pub ssl_txn_id: String,
}
#[derive(Debug, Serialize)]
#[serde(rename = "txn")]
pub struct PaymentsVoidRequest {
    pub ssl_transaction_type: TransactionType,
    pub ssl_account_id: Secret<String>,
    pub ssl_user_id: Secret<String>,
    pub ssl_pin: Secret<String>,
    pub ssl_txn_id: String,
}

#[derive(Debug, Serialize)]
#[serde(rename = "txn")]
pub struct ElavonRefundRequest {
    pub ssl_transaction_type: TransactionType,
    pub ssl_account_id: Secret<String>,
    pub ssl_user_id: Secret<String>,
    pub ssl_pin: Secret<String>,
    pub ssl_amount: StringMajorUnit,
    pub ssl_txn_id: String,
}

#[derive(Debug, Serialize)]
#[serde(rename = "txn")]
pub struct SyncRequest {
    pub ssl_transaction_type: TransactionType,
    pub ssl_account_id: Secret<String>,
    pub ssl_user_id: Secret<String>,
    pub ssl_pin: Secret<String>,
    pub ssl_txn_id: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename = "txn")]
pub struct ElavonSyncResponse {
    pub ssl_trans_status: TransactionSyncStatus,
    pub ssl_transaction_type: SyncTransactionType,
    pub ssl_txn_id: String,
}
impl TryFrom<&RefundSyncRouterData> for SyncRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &RefundSyncRouterData) -> Result<Self, Self::Error> {
        let auth = ElavonAuthType::try_from(&item.connector_auth_type)?;
        Ok(Self {
            ssl_txn_id: item.request.get_connector_refund_id()?,
            ssl_transaction_type: TransactionType::TxnQuery,
            ssl_account_id: auth.account_id.clone(),
            ssl_user_id: auth.user_id.clone(),
            ssl_pin: auth.pin.clone(),
        })
    }
}
impl TryFrom<&PaymentsSyncRouterData> for SyncRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentsSyncRouterData) -> Result<Self, Self::Error> {
        let auth = ElavonAuthType::try_from(&item.connector_auth_type)?;
        Ok(Self {
            ssl_txn_id: item
                .request
                .connector_transaction_id
                .get_connector_transaction_id()
                .change_context(errors::ConnectorError::MissingConnectorTransactionID)?,
            ssl_transaction_type: TransactionType::TxnQuery,
            ssl_account_id: auth.account_id.clone(),
            ssl_user_id: auth.user_id.clone(),
            ssl_pin: auth.pin.clone(),
        })
    }
}
impl<F> TryFrom<&ElavonRouterData<&RefundsRouterData<F>>> for ElavonRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &ElavonRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let auth = ElavonAuthType::try_from(&item.router_data.connector_auth_type)?;
        Ok(Self {
            ssl_txn_id: item.router_data.request.connector_transaction_id.clone(),
            ssl_amount: item.amount.clone(),
            ssl_transaction_type: TransactionType::CcReturn,
            ssl_account_id: auth.account_id.clone(),
            ssl_user_id: auth.user_id.clone(),
            ssl_pin: auth.pin.clone(),
        })
    }
}

impl TryFrom<&ElavonRouterData<&PaymentsCaptureRouterData>> for PaymentsCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &ElavonRouterData<&PaymentsCaptureRouterData>) -> Result<Self, Self::Error> {
        let auth = ElavonAuthType::try_from(&item.router_data.connector_auth_type)?;
        Ok(Self {
            ssl_txn_id: item.router_data.request.connector_transaction_id.clone(),
            ssl_amount: item.amount.clone(),
            ssl_transaction_type: TransactionType::CcComplete,
            ssl_account_id: auth.account_id.clone(),
            ssl_user_id: auth.user_id.clone(),
            ssl_pin: auth.pin.clone(),
        })
    }
}

impl TryFrom<PaymentsSyncResponseRouterData<ElavonSyncResponse>> for PaymentsSyncRouterData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: PaymentsSyncResponseRouterData<ElavonSyncResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: get_sync_status(item.data.status, &item.response),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.ssl_txn_id),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}
impl TryFrom<RefundsResponseRouterData<RSync, ElavonSyncResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, ElavonSyncResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.ssl_txn_id.clone(),
                refund_status: get_refund_status(item.data.request.refund_status, &item.response),
            }),
            ..item.data
        })
    }
}

impl TryFrom<PaymentsCaptureResponseRouterData<ElavonPaymentsResponse>>
    for PaymentsCaptureRouterData
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: PaymentsCaptureResponseRouterData<ElavonPaymentsResponse>,
    ) -> Result<Self, Self::Error> {
        let status = map_payment_status(&item.response.result, enums::AttemptStatus::Charged);
        let response = match &item.response.result {
            ElavonResult::Error(error) => Err(ErrorResponse {
                code: error
                    .error_code
                    .clone()
                    .unwrap_or(NO_ERROR_CODE.to_string()),
                message: error.error_message.clone(),
                reason: Some(error.error_message.clone()),
                attempt_status: None,
                connector_transaction_id: None,
                status_code: item.http_code,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
            ElavonResult::Success(response) => {
                if status == enums::AttemptStatus::Failure {
                    Err(ErrorResponse {
                        code: response.ssl_result_message.clone(),
                        message: response.ssl_result_message.clone(),
                        reason: Some(response.ssl_result_message.clone()),
                        attempt_status: None,
                        connector_transaction_id: None,
                        status_code: item.http_code,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(
                            response.ssl_txn_id.clone(),
                        ),
                        redirection_data: Box::new(None),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: Some(response.ssl_txn_id.clone()),
                        incremental_authorization_allowed: None,
                        charges: None,
                    })
                }
            }
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}
impl TryFrom<RefundsResponseRouterData<Execute, ElavonPaymentsResponse>>
    for RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, ElavonPaymentsResponse>,
    ) -> Result<Self, Self::Error> {
        let status = enums::RefundStatus::from(&item.response.result);
        let response = match &item.response.result {
            ElavonResult::Error(error) => Err(ErrorResponse {
                code: error
                    .error_code
                    .clone()
                    .unwrap_or(NO_ERROR_CODE.to_string()),
                message: error.error_message.clone(),
                reason: Some(error.error_message.clone()),
                attempt_status: None,
                connector_transaction_id: None,
                status_code: item.http_code,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
            ElavonResult::Success(response) => {
                if status == enums::RefundStatus::Failure {
                    Err(ErrorResponse {
                        code: response.ssl_result_message.clone(),
                        message: response.ssl_result_message.clone(),
                        reason: Some(response.ssl_result_message.clone()),
                        attempt_status: None,
                        connector_transaction_id: None,
                        status_code: item.http_code,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(RefundsResponseData {
                        connector_refund_id: response.ssl_txn_id.clone(),
                        refund_status: enums::RefundStatus::from(&item.response.result),
                    })
                }
            }
        };
        Ok(Self {
            response,
            ..item.data
        })
    }
}

trait ElavonResponseValidator {
    fn is_successful(&self) -> bool;
}
impl ElavonResponseValidator for ElavonResult {
    fn is_successful(&self) -> bool {
        matches!(self, Self::Success(response) if response.ssl_result == SslResult::ImportedBatchFile)
    }
}

fn map_payment_status(
    item: &ElavonResult,
    success_status: enums::AttemptStatus,
) -> enums::AttemptStatus {
    if item.is_successful() {
        success_status
    } else {
        enums::AttemptStatus::Failure
    }
}

impl From<&ElavonResult> for enums::RefundStatus {
    fn from(item: &ElavonResult) -> Self {
        if item.is_successful() {
            Self::Success
        } else {
            Self::Failure
        }
    }
}
fn get_refund_status(
    prev_status: enums::RefundStatus,
    item: &ElavonSyncResponse,
) -> enums::RefundStatus {
    match item.ssl_trans_status {
        TransactionSyncStatus::REV | TransactionSyncStatus::OPN | TransactionSyncStatus::PEN => {
            prev_status
        }
        TransactionSyncStatus::STL => enums::RefundStatus::Success,
        TransactionSyncStatus::PST | TransactionSyncStatus::FPR | TransactionSyncStatus::PRE => {
            enums::RefundStatus::Failure
        }
    }
}
impl From<&ElavonSyncResponse> for enums::AttemptStatus {
    fn from(item: &ElavonSyncResponse) -> Self {
        match item.ssl_trans_status {
            TransactionSyncStatus::REV
            | TransactionSyncStatus::OPN
            | TransactionSyncStatus::PEN => Self::Pending,
            TransactionSyncStatus::STL => match item.ssl_transaction_type {
                SyncTransactionType::Sale => Self::Charged,
                SyncTransactionType::AuthOnly => Self::Authorized,
                SyncTransactionType::Return => Self::Pending,
            },
            TransactionSyncStatus::PST
            | TransactionSyncStatus::FPR
            | TransactionSyncStatus::PRE => Self::Failure,
        }
    }
}
fn get_sync_status(
    prev_status: enums::AttemptStatus,
    item: &ElavonSyncResponse,
) -> enums::AttemptStatus {
    match item.ssl_trans_status {
        TransactionSyncStatus::REV | TransactionSyncStatus::OPN | TransactionSyncStatus::PEN => {
            prev_status
        }
        TransactionSyncStatus::STL => match item.ssl_transaction_type {
            SyncTransactionType::Sale => enums::AttemptStatus::Charged,
            SyncTransactionType::AuthOnly => enums::AttemptStatus::Authorized,
            SyncTransactionType::Return => enums::AttemptStatus::Pending,
        },
        TransactionSyncStatus::PST | TransactionSyncStatus::FPR | TransactionSyncStatus::PRE => {
            enums::AttemptStatus::Failure
        }
    }
}

fn get_payment_status(item: &ElavonResult, is_auto_capture: bool) -> enums::AttemptStatus {
    if item.is_successful() {
        if is_auto_capture {
            enums::AttemptStatus::Charged
        } else {
            enums::AttemptStatus::Authorized
        }
    } else {
        enums::AttemptStatus::Failure
    }
}


pub fn get_payment_method(&self) -> Option<common_enums::PaymentMethod> {
        match self {
            Self::Card(_) | Self::NetworkToken(_) | Self::CardDetailsForNetworkTransactionId(_) => {
                Some(common_enums::PaymentMethod::Card)
            }
            Self::CardRedirect(_) => Some(common_enums::PaymentMethod::CardRedirect),
            Self::Wallet(_) => Some(common_enums::PaymentMethod::Wallet),
            Self::PayLater(_) => Some(common_enums::PaymentMethod::PayLater),
            Self::BankRedirect(_) => Some(common_enums::PaymentMethod::BankRedirect),
            Self::BankDebit(_) => Some(common_enums::PaymentMethod::BankDebit),
            Self::BankTransfer(_) => Some(common_enums::PaymentMethod::BankTransfer),
            Self::Crypto(_) => Some(common_enums::PaymentMethod::Crypto),
            Self::Reward => Some(common_enums::PaymentMethod::Reward),
            Self::RealTimePayment(_) => Some(common_enums::PaymentMethod::RealTimePayment),
            Self::Upi(_) => Some(common_enums::PaymentMethod::Upi),
            Self::Voucher(_) => Some(common_enums::PaymentMethod::Voucher),
            Self::GiftCard(_) => Some(common_enums::PaymentMethod::GiftCard),
            Self::OpenBanking(_) => Some(common_enums::PaymentMethod::OpenBanking),
            Self::MobilePayment(_) => Some(common_enums::PaymentMethod::MobilePayment),
            Self::CardToken(_) | Self::MandatePayment => None,
        }
    }


pub struct RefundResponse {
    id: String,
    status: RefundStatus,
}


pub struct FeatureMetadata {
    /// Redirection response coming in request as metadata field only for redirection scenarios
    pub redirect_response: Option<RedirectResponse>,
    /// Additional tags to be used for global search
    pub search_tags: Option<Vec<HashedString<WithType>>>,
    /// Recurring payment details required for apple pay Merchant Token
    pub apple_pay_recurring_details: Option<ApplePayRecurringDetails>,
    /// The system that the gateway is integrated with, e.g., `Direct`(through hyperswitch), `UnifiedConnectorService`(through ucs), etc.
    pub gateway_system: Option<common_enums::GatewaySystem>,
}


impl api::UasPostAuthentication for UnifiedAuthenticationService {}

impl api::UasPostAuthentication for for UnifiedAuthenticationService


impl api::ConnectorAccessToken for Chargebee {}

impl api::ConnectorAccessToken for for Chargebee


pub async fn internal_sync_refund_with_gateway(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payment_attempt: &storage::PaymentAttempt,
    payment_intent: &storage::PaymentIntent,
    refund: &diesel_refund::Refund,
    merchant_connector_details: common_types::domain::MerchantConnectorAuthDetails,
) -> errors::RouterResult<diesel_refund::Refund> {
    let connector_enum = merchant_connector_details.connector_name;

    let connector: api::ConnectorData = api::ConnectorData::get_connector_by_name(
        &state.conf.connectors,
        &connector_enum.to_string(),
        api::GetToken::Connector,
        None,
    )?;

    let merchant_connector_account =
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(
            merchant_connector_details,
        );

    let mut router_data = core_utils::construct_refund_router_data::<api::RSync>(
        state,
        connector_enum,
        merchant_context,
        payment_intent,
        payment_attempt,
        refund,
        &merchant_connector_account,
    )
    .await?;

    let add_access_token_result = Box::pin(access_token::add_access_token(
        state,
        &connector,
        merchant_context,
        &router_data,
        None,
    ))
    .await?;

    access_token::update_router_data_with_access_token_result(
        &add_access_token_result,
        &mut router_data,
        &payments::CallConnectorAction::Trigger,
    );

    let connector_response = Box::pin(call_connector_service(
        state,
        &connector,
        add_access_token_result,
        router_data,
    ))
    .await
    .to_refund_failed_response()?;

    let connector_response = perform_integrity_check(connector_response);

    let refund_update =
        build_refund_update_for_rsync(&connector, merchant_context, connector_response);

    let response = state
        .store
        .update_refund(
            refund.to_owned(),
            refund_update,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::RefundNotFound)
        .attach_printable_lazy(|| {
            format!(
                "Unable to update refund with refund_id: {}",
                refund.id.get_string_repr()
            )
        })?;

    // Implement outgoing webhook here
    Ok(response)
}


pub struct ArchipelRouterData<T> {
    pub amount: MinorUnit,
    pub tenant_id: ArchipelTenantId,
    pub router_data: T,
}


impl ConnectorValidation for Fiserv {}


impl api::ConnectorCustomer for Authorizedotnet {}

impl api::ConnectorCustomer for for Authorizedotnet


File: crates/common_utils/src/id_type/api_key.rs
 Public functions: 1

crate::id_type!(
    ApiKeyId,
    "A type for key_id that can be used for API key IDs"
);
crate::impl_id_type_methods!(ApiKeyId, "key_id");

// This is to display the `ApiKeyId` as ApiKeyId(abcd)
crate::impl_debug_id_type!(ApiKeyId);
crate::impl_try_from_cow_str_id_type!(ApiKeyId, "key_id");

crate::impl_serializable_secret_id_type!(ApiKeyId);
crate::impl_queryable_id_type!(ApiKeyId);
crate::impl_to_sql_from_sql_id_type!(ApiKeyId);

impl ApiKeyId {
    /// Generate Api Key Id from prefix
    pub fn generate_key_id(prefix: &'static str) -> Self {
        Self(crate::generate_ref_id_with_default_length(prefix))
    }
}

impl crate::events::ApiEventMetric for ApiKeyId {
    fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
        Some(crate::events::ApiEventsType::ApiKey {
            key_id: self.clone(),
        })
    }
}

impl crate::events::ApiEventMetric for (super::MerchantId, ApiKeyId) {
    fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
        Some(crate::events::ApiEventsType::ApiKey {
            key_id: self.1.clone(),
        })
    }
}

impl crate::events::ApiEventMetric for (&super::MerchantId, &ApiKeyId) {
    fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
        Some(crate::events::ApiEventsType::ApiKey {
            key_id: self.1.clone(),
        })
    }
}

crate::impl_default_id_type!(ApiKeyId, "key");


impl PaymentSession for Plaid {}


pub async fn try_redis_get_else_try_database_get<F, RFut, DFut, T>(
    redis_fut: RFut,
    database_call_closure: F,
) -> error_stack::Result<T, errors::StorageError>
where
    F: FnOnce() -> DFut,
    RFut: futures::Future<Output = error_stack::Result<T, redis_interface::errors::RedisError>>,
    DFut: futures::Future<Output = error_stack::Result<T, errors::StorageError>>,
{
    let redis_output = redis_fut.await;
    match redis_output {
        Ok(output) => Ok(output),
        Err(redis_error) => match redis_error.current_context() {
            redis_interface::errors::RedisError::NotFound => {
                metrics::KV_MISS.add(1, &[]);
                database_call_closure().await
            }
            // Keeping the key empty here since the error would never go here.
            _ => Err(redis_error.to_redis_failed_response("")),
        },
    }
}


/// Hashmap to store mca_id's with product names
pub struct AuthenticationConnectorAccountMap(
    HashMap<enums::AuthenticationProduct, common_utils::id_type::MerchantConnectorAccountId>,
);


pub fn new() -> &'static Self {
        &Self {
            amount_converter: &StringMinorUnitForConnector,
        }
    }


pub async fn get_profile_payment_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetPaymentFiltersRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetPaymentFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let org_id = auth.merchant_account.get_org_id();
                let merchant_id = auth.merchant_account.get_id();
                let profile_id = auth
                    .profile_id
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?;
                let auth: AuthInfo = AuthInfo::ProfileLevel {
                    org_id: org_id.clone(),
                    merchant_id: merchant_id.clone(),
                    profile_ids: vec![profile_id.clone()],
                };
                analytics::payments::get_filters(&state.pool, req, &auth)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


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


pub async fn create_payout_link_db_entry(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    payout_link_data: &PayoutLinkData,
    return_url: Option<String>,
) -> RouterResult<PayoutLink> {
    let db: &dyn StorageInterface = &*state.store;

    let link_data = serde_json::to_value(payout_link_data)
        .map_err(|_| report!(errors::ApiErrorResponse::InternalServerError))
        .attach_printable("Failed to convert PayoutLinkData to Value")?;

    let payout_link = GenericLinkNew {
        link_id: payout_link_data.payout_link_id.to_string(),
        primary_reference: payout_link_data.payout_id.get_string_repr().to_string(),
        merchant_id: merchant_id.to_owned(),
        link_type: common_enums::GenericLinkType::PayoutLink,
        link_status: GenericLinkStatus::PayoutLink(PayoutLinkStatus::Initiated),
        link_data,
        url: payout_link_data.link.to_string().into(),
        return_url,
        expiry: common_utils::date_time::now()
            + Duration::seconds(payout_link_data.session_expiry.into()),
        ..Default::default()
    };

    db.insert_payout_link(payout_link)
        .await
        .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError {
            message: "payout link already exists".to_string(),
        })
}


impl api::RefundExecute for Gocardless {}

impl api::RefundExecute for for Gocardless


/// High-level abstraction for calling UCS webhook transformation
/// This provides a clean interface similar to payment flow UCS calls
pub async fn call_unified_connector_service_for_webhook(
    state: &SessionState,
    merchant_context: &MerchantContext,
    connector_name: &str,
    body: &actix_web::web::Bytes,
    request_details: &hyperswitch_interfaces::webhooks::IncomingWebhookRequestDetails<'_>,
    merchant_connector_account: Option<
        &hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccount,
    >,
) -> RouterResult<(
    api_models::webhooks::IncomingWebhookEvent,
    bool,
    WebhookTransformData,
)> {
    let ucs_client = state
        .grpc_client
        .unified_connector_service_client
        .as_ref()
        .ok_or_else(|| {
            error_stack::report!(errors::ApiErrorResponse::WebhookProcessingFailure)
                .attach_printable("UCS client is not available for webhook processing")
        })?;

    // Build webhook secrets from merchant connector account
    let webhook_secrets = merchant_connector_account.and_then(|mca| {
        #[cfg(feature = "v1")]
        let mca_type = MerchantConnectorAccountType::DbVal(Box::new(mca.clone()));
        #[cfg(feature = "v2")]
        let mca_type =
            MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(mca.clone()));

        build_webhook_secrets_from_merchant_connector_account(&mca_type)
            .map_err(|e| {
                logger::warn!(
                    build_error=?e,
                    connector_name=connector_name,
                    "Failed to build webhook secrets from merchant connector account in call_unified_connector_service_for_webhook"
                );
                e
            })
            .ok()
            .flatten()
    });

    // Build UCS transform request using new webhook transformers
    let transform_request = transformers::build_webhook_transform_request(
        body,
        request_details,
        webhook_secrets,
        merchant_context
            .get_merchant_account()
            .get_id()
            .get_string_repr(),
        connector_name,
    )?;

    // Build connector auth metadata
    let connector_auth_metadata = merchant_connector_account
        .map(|mca| {
            #[cfg(feature = "v1")]
            let mca_type = MerchantConnectorAccountType::DbVal(Box::new(mca.clone()));
            #[cfg(feature = "v2")]
            let mca_type = MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(
                mca.clone(),
            ));

            build_unified_connector_service_auth_metadata(mca_type, merchant_context)
        })
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to build UCS auth metadata")?
        .ok_or_else(|| {
            error_stack::report!(errors::ApiErrorResponse::InternalServerError).attach_printable(
                "Missing merchant connector account for UCS webhook transformation",
            )
        })?;

    // Build gRPC headers
    let grpc_headers = external_services::grpc_client::GrpcHeaders {
        tenant_id: state.tenant.tenant_id.get_string_repr().to_string(),
        request_id: Some(utils::generate_id(consts::ID_LENGTH, "webhook_req")),
    };

    // Make UCS call - client availability already verified
    match ucs_client
        .transform_incoming_webhook(transform_request, connector_auth_metadata, grpc_headers)
        .await
    {
        Ok(response) => {
            let transform_response = response.into_inner();
            let transform_data = transformers::transform_ucs_webhook_response(transform_response)?;

            // UCS handles everything internally - event type, source verification, decoding
            Ok((
                transform_data.event_type,
                transform_data.source_verified,
                transform_data,
            ))
        }
        Err(err) => {
            // When UCS is configured, we don't fall back to direct connector processing
            Err(errors::ApiErrorResponse::WebhookProcessingFailure)
                .attach_printable(format!("UCS webhook processing failed: {err}"))
        }
    }
}


        .and_then(|business_profile| business_profile.is_connector_agnostic_mit_enabled)
        .unwrap_or(false);

    for pm in resp.into_iter() {
        let parent_payment_method_token = generate_id(consts::ID_LENGTH, "token");

        let payment_method = pm
            .get_payment_method_type()
            .get_required_value("payment_method")?;

        let pm_list_context = get_pm_list_context(
            state,
            &payment_method,
            merchant_context.get_merchant_key_store(),
            &pm,
            Some(parent_payment_method_token.clone()),
            true,
            false,
            &merchant_context,
        )
        .await?;

        if pm_list_context.is_none() {
            continue;
        }

        let pm_list_context = pm_list_context.get_required_value("PaymentMethodListContext")?;

        // Retrieve the masked bank details to be sent as a response
        let bank_details = if payment_method == enums::PaymentMethod::BankDebit {
            get_masked_bank_details(&pm).await.unwrap_or_else(|error| {
                logger::error!(?error);
                None
            })
        } else {
            None
        };

        let payment_method_billing = pm
            .payment_method_billing_address
            .clone()
            .map(|decrypted_data| decrypted_data.into_inner().expose())
            .map(|decrypted_value| decrypted_value.parse_value("payment method billing address"))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("unable to decrypt payment method billing address details")?;
        let connector_mandate_details = pm
            .get_common_mandate_reference()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to deserialize to Payment Mandate Reference ")?;
        let mca_enabled = get_mca_status(
            state,
            merchant_context.get_merchant_key_store(),
            profile_id.clone(),
            merchant_context.get_merchant_account().get_id(),
            is_connector_agnostic_mit_enabled,
            Some(connector_mandate_details),
            pm.network_transaction_id.as_ref(),
        )
        .await?;

        let requires_cvv = if is_connector_agnostic_mit_enabled {
            requires_cvv
                && !(off_session_payment_flag
                    && (pm.connector_mandate_details.is_some()
                        || pm.network_transaction_id.is_some()))
        } else {
            requires_cvv && !(off_session_payment_flag && pm.connector_mandate_details.is_some())
        };
        // Need validation for enabled payment method ,querying MCA
        let pma = api::CustomerPaymentMethod {
            payment_token: parent_payment_method_token.to_owned(),
            payment_method_id: pm.payment_method_id.clone(),
            customer_id: pm.customer_id.clone(),
            payment_method,
            payment_method_type: pm.get_payment_method_subtype(),
            payment_method_issuer: pm.payment_method_issuer,
            card: pm_list_context.card_details,
            metadata: pm.metadata,
            payment_method_issuer_code: pm.payment_method_issuer_code,
            recurring_enabled: mca_enabled,
            installment_payment_enabled: Some(false),
            payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]),
            created: Some(pm.created_at),
            #[cfg(feature = "payouts")]
            bank_transfer: pm_list_context.bank_transfer_details,
            bank: bank_details,
            surcharge_details: None,
            requires_cvv,
            last_used_at: Some(pm.last_used_at),
            default_payment_method_set: customer.default_payment_method_id.is_some()
                && customer.default_payment_method_id == Some(pm.payment_method_id),
            billing: payment_method_billing,
        };
        if requires_cvv || mca_enabled.unwrap_or(false) {
            customer_pms.push(pma.to_owned());
        }

        let redis_conn = state
            .store
            .get_redis_conn()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to get redis connection")?;

        let intent_fulfillment_time = business_profile
            .as_ref()
            .and_then(|b_profile| b_profile.get_order_fulfillment_time())
            .unwrap_or(consts::DEFAULT_INTENT_FULFILLMENT_TIME);

        let hyperswitch_token_data = pm_list_context
            .hyperswitch_token_data
            .get_required_value("PaymentTokenData")?;

        ParentPaymentMethodToken::create_key_for_token((
            &parent_payment_method_token,
            pma.payment_method,
        ))
        .insert(intent_fulfillment_time, hyperswitch_token_data, state)
        .await?;

        if let Some(metadata) = pma.metadata {
            let pm_metadata_vec: payment_methods::PaymentMethodMetadata = metadata
                .parse_value("PaymentMethodMetadata")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable(
                    "Failed to deserialize metadata to PaymentmethodMetadata struct",
                )?;

            for pm_metadata in pm_metadata_vec.payment_method_tokenization {
                let key = format!(
                    "pm_token_{}_{}_{}",
                    parent_payment_method_token, pma.payment_method, pm_metadata.0
                );

                redis_conn
                    .set_key_with_expiry(&key.into(), pm_metadata.1, intent_fulfillment_time)
                    .await
                    .change_context(errors::StorageError::KVError)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to add data in redis")?;
            }
        }
    }

    let mut response = api::CustomerPaymentMethodsListResponse {
        customer_payment_methods: customer_pms,
        is_guest_customer: payment_intent.as_ref().map(|_| false), //to return this key only when the request is tied to a payment intent
    };

    Box::pin(perform_surcharge_ops(
        payment_intent,
        state,
        merchant_context,
        business_profile,
        &mut response,
    ))
    .await?;

    Ok(services::ApplicationResponse::Json(response))
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
pub async fn get_pm_list_context(
    state: &routes::SessionState,
    payment_method: &enums::PaymentMethod,
    #[cfg(feature = "payouts")] key_store: &domain::MerchantKeyStore,
    #[cfg(not(feature = "payouts"))] _key_store: &domain::MerchantKeyStore,
    pm: &domain::PaymentMethod,
    #[cfg(feature = "payouts")] parent_payment_method_token: Option<String>,
    #[cfg(not(feature = "payouts"))] _parent_payment_method_token: Option<String>,
    is_payment_associated: bool,
    force_fetch_card_from_vault: bool,
    merchant_context: &domain::MerchantContext,
) -> Result<Option<PaymentMethodListContext>, error_stack::Report<errors::ApiErrorResponse>> {
    let cards = PmCards {
        state,
        merchant_context,
    };
    let payment_method_retrieval_context = match payment_method {
        enums::PaymentMethod::Card => {
            let card_details = if force_fetch_card_from_vault {
                Some(cards.get_card_details_from_locker(pm).await?)
            } else {
                cards.get_card_details_with_locker_fallback(pm).await?
            };

            card_details.as_ref().map(|card| PaymentMethodListContext {
                card_details: Some(card.clone()),
                #[cfg(feature = "payouts")]
                bank_transfer_details: None,
                hyperswitch_token_data: is_payment_associated.then_some(
                    PaymentTokenData::permanent_card(
                        Some(pm.get_id().clone()),
                        pm.locker_id.clone().or(Some(pm.get_id().clone())),
                        pm.locker_id.clone().unwrap_or(pm.get_id().clone()),
                        pm.network_token_requestor_reference_id
                            .clone()
                            .or(Some(pm.get_id().clone())),
                    ),
                ),
            })
        }

        enums::PaymentMethod::BankDebit => {
            // Retrieve the pm_auth connector details so that it can be tokenized
            let bank_account_token_data = get_bank_account_connector_details(pm)
                .await
                .unwrap_or_else(|err| {
                    logger::error!(error=?err);
                    None
                });

            bank_account_token_data.map(|data| {
                let token_data = PaymentTokenData::AuthBankDebit(data);

                PaymentMethodListContext {
                    card_details: None,
                    #[cfg(feature = "payouts")]
                    bank_transfer_details: None,
                    hyperswitch_token_data: is_payment_associated.then_some(token_data),
                }
            })
        }

        enums::PaymentMethod::Wallet => Some(PaymentMethodListContext {
            card_details: None,
            #[cfg(feature = "payouts")]
            bank_transfer_details: None,
            hyperswitch_token_data: is_payment_associated
                .then_some(PaymentTokenData::wallet_token(pm.get_id().clone())),
        }),

        #[cfg(feature = "payouts")]
        enums::PaymentMethod::BankTransfer => Some(PaymentMethodListContext {
            card_details: None,
            bank_transfer_details: Some(
                get_bank_from_hs_locker(
                    state,
                    key_store,
                    parent_payment_method_token.as_ref(),
                    &pm.customer_id,
                    &pm.merchant_id,
                    pm.locker_id.as_ref().unwrap_or(pm.get_id()),
                )
                .await?,
            ),
            hyperswitch_token_data: parent_payment_method_token
                .map(|token| PaymentTokenData::temporary_generic(token.clone())),
        }),

        _ => Some(PaymentMethodListContext {
            card_details: None,
            #[cfg(feature = "payouts")]
            bank_transfer_details: None,
            hyperswitch_token_data: is_payment_associated.then_some(
                PaymentTokenData::temporary_generic(generate_id(consts::ID_LENGTH, "token")),
            ),
        }),
    };

    Ok(payment_method_retrieval_context)
}

#[cfg(feature = "v1")]
async fn perform_surcharge_ops(
    payment_intent: Option<storage::PaymentIntent>,
    state: &routes::SessionState,
    merchant_context: domain::MerchantContext,
    business_profile: Option<Profile>,
    response: &mut api::CustomerPaymentMethodsListResponse,
) -> Result<(), error_stack::Report<errors::ApiErrorResponse>> {
    let payment_attempt = payment_intent
        .as_ref()
        .async_map(|payment_intent| async {
            state
                .store
                .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                    payment_intent.get_id(),
                    merchant_context.get_merchant_account().get_id(),
                    &payment_intent.active_attempt.get_id(),
                    merchant_context.get_merchant_account().storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
        })
        .await
        .transpose()?;
    if let Some((payment_attempt, payment_intent, business_profile)) = payment_attempt
        .zip(payment_intent)
        .zip(business_profile)
        .map(|((pa, pi), bp)| (pa, pi, bp))
    {
        call_surcharge_decision_management_for_saved_card(
            state,
            &merchant_context,
            &business_profile,
            &payment_attempt,
            payment_intent,
            response,
        )
        .await?;
    }

    Ok(())
}

#[cfg(feature = "v2")]
pub async fn perform_surcharge_ops(
    _payment_intent: Option<storage::PaymentIntent>,
    _state: &routes::SessionState,
    _merchant_context: &domain::MerchantContext,
    _business_profile: Option<Profile>,
    _response: &mut api_models::payment_methods::CustomerPaymentMethodsListResponse,
) -> Result<(), error_stack::Report<errors::ApiErrorResponse>> {
    todo!()
}

#[cfg(feature = "v1")]
pub async fn get_mca_status(
    state: &routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    profile_id: Option<id_type::ProfileId>,
    merchant_id: &id_type::MerchantId,
    is_connector_agnostic_mit_enabled: bool,
    connector_mandate_details: Option<CommonMandateReference>,
    network_transaction_id: Option<&String>,
) -> errors::RouterResult<Option<bool>> {
    if is_connector_agnostic_mit_enabled && network_transaction_id.is_some() {
        return Ok(Some(true));
    }
    if let Some(connector_mandate_details) = connector_mandate_details {
        let mcas = state
            .store
            .find_merchant_connector_account_by_merchant_id_and_disabled_list(
                &state.into(),
                merchant_id,
                true,
                key_store,
            )
            .await
            .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                id: merchant_id.get_string_repr().to_owned(),
            })?;

        return Ok(Some(
            mcas.is_merchant_connector_account_id_in_connector_mandate_details(
                profile_id.as_ref(),
                &connector_mandate_details,
            ),
        ));
    }
    Ok(Some(false))
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
pub async fn get_mca_status(
    state: &routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    profile_id: Option<id_type::ProfileId>,
    merchant_id: &id_type::MerchantId,
    is_connector_agnostic_mit_enabled: bool,
    connector_mandate_details: Option<&CommonMandateReference>,
    network_transaction_id: Option<&String>,
    merchant_connector_accounts: &domain::MerchantConnectorAccounts,
) -> bool {
    if is_connector_agnostic_mit_enabled && network_transaction_id.is_some() {
        return true;
    }
    match connector_mandate_details {
        Some(connector_mandate_details) => merchant_connector_accounts
            .is_merchant_connector_account_id_in_connector_mandate_details(
                profile_id.as_ref(),
                connector_mandate_details,
            ),
        None => false,
    }
}

pub async fn decrypt_generic_data<T>(
    state: &routes::SessionState,
    data: Option<Encryption>,
    key_store: &domain::MerchantKeyStore,
) -> errors::RouterResult<Option<T>>
where
    T: serde::de::DeserializeOwned,
{
    let key = key_store.key.get_inner().peek();
    let identifier = Identifier::Merchant(key_store.merchant_id.clone());
    let decrypted_data = domain::types::crypto_operation::<serde_json::Value, masking::WithType>(
        &state.into(),
        type_name!(T),
        domain::types::CryptoOperation::DecryptOptional(data),
        identifier,
        key,
    )
    .await
    .and_then(|val| val.try_into_optionaloperation())
    .change_context(errors::StorageError::DecryptionError)
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("unable to decrypt data")?;

    decrypted_data
        .map(|decrypted_data| decrypted_data.into_inner().expose())
        .map(|decrypted_value| decrypted_value.parse_value("generic_data"))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("unable to parse generic data value")
}

#[cfg(feature = "v1")]
pub async fn get_card_details_from_locker(
    state: &routes::SessionState,
    pm: &domain::PaymentMethod,
) -> errors::RouterResult<api::CardDetailFromLocker> {
    let card = get_card_from_locker(
        state,
        &pm.customer_id,
        &pm.merchant_id,
        pm.locker_id.as_ref().unwrap_or(pm.get_id()),
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Error getting card from card vault")?;

    payment_methods::get_card_detail(pm, card)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Get Card Details Failed")
}

#[cfg(feature = "v1")]
pub async fn get_lookup_key_from_locker(
    state: &routes::SessionState,
    payment_token: &str,
    pm: &domain::PaymentMethod,
    merchant_key_store: &domain::MerchantKeyStore,
) -> errors::RouterResult<api::CardDetailFromLocker> {
    let card_detail = get_card_details_from_locker(state, pm).await?;
    let card = card_detail.clone();

    let resp = TempLockerCardSupport::create_payment_method_data_in_temp_locker(
        state,
        payment_token,
        card,
        pm,
        merchant_key_store,
    )
    .await?;
    Ok(resp)
}

pub async fn get_masked_bank_details(
    pm: &domain::PaymentMethod,
) -> errors::RouterResult<Option<MaskedBankDetails>> {
    #[cfg(feature = "v1")]
    let payment_method_data = pm
        .payment_method_data
        .clone()
        .map(|x| x.into_inner().expose())
        .map(
            |v| -> Result<PaymentMethodsData, error_stack::Report<errors::ApiErrorResponse>> {
                v.parse_value::<PaymentMethodsData>("PaymentMethodsData")
                    .change_context(errors::StorageError::DeserializationFailed)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to deserialize Payment Method Auth config")
            },
        )
        .transpose()?;

    #[cfg(feature = "v2")]
    let payment_method_data = pm.payment_method_data.clone().map(|x| x.into_inner());

    match payment_method_data {
        Some(pmd) => match pmd {
            PaymentMethodsData::Card(_) => Ok(None),
            PaymentMethodsData::BankDetails(bank_details) => Ok(Some(MaskedBankDetails {
                mask: bank_details.mask,
            })),
            PaymentMethodsData::WalletDetails(_) => Ok(None),
        },
        None => Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable("Unable to fetch payment method data"),
    }
}

#[cfg(feature = "v1")]
pub async fn get_bank_account_connector_details(
    pm: &domain::PaymentMethod,
) -> errors::RouterResult<Option<BankAccountTokenData>> {
    let payment_method_data = pm
        .payment_method_data
        .clone()
        .map(|x| x.into_inner().expose())
        .map(
            |v| -> Result<PaymentMethodsData, error_stack::Report<errors::ApiErrorResponse>> {
                v.parse_value::<PaymentMethodsData>("PaymentMethodsData")
                    .change_context(errors::StorageError::DeserializationFailed)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to deserialize Payment Method Auth config")
            },
        )
        .transpose()?;

    match payment_method_data {
        Some(pmd) => match pmd {
            PaymentMethodsData::Card(_) => Err(errors::ApiErrorResponse::UnprocessableEntity {
                message: "Card is not a valid entity".to_string(),
            }
            .into()),
            PaymentMethodsData::WalletDetails(_) => {
                Err(errors::ApiErrorResponse::UnprocessableEntity {
                    message: "Wallet is not a valid entity".to_string(),
                }
                .into())
            }
            PaymentMethodsData::BankDetails(bank_details) => {
                let connector_details = bank_details
                    .connector_details
                    .first()
                    .ok_or(errors::ApiErrorResponse::InternalServerError)?;

                let pm_type = pm
                    .get_payment_method_subtype()
                    .get_required_value("payment_method_type")
                    .attach_printable("PaymentMethodType not found")?;

                let pm = pm
                    .get_payment_method_type()
                    .get_required_value("payment_method")
                    .attach_printable("PaymentMethod not found")?;

                let token_data = BankAccountTokenData {
                    payment_method_type: pm_type,
                    payment_method: pm,
                    connector_details: connector_details.clone(),
                };

                Ok(Some(token_data))
            }
        },
        None => Ok(None),
    }
}

pub async fn update_last_used_at(
    payment_method: &domain::PaymentMethod,
    state: &routes::SessionState,
    storage_scheme: MerchantStorageScheme,
    key_store: &domain::MerchantKeyStore,
) -> errors::RouterResult<()> {
    let update_last_used = storage::PaymentMethodUpdate::LastUsedUpdate {
        last_used_at: common_utils::date_time::now(),
    };

    state
        .store
        .update_payment_method(
            &(state.into()),
            key_store,
            payment_method.clone(),
            update_last_used,
            storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update the last_used_at in db")?;

    Ok(())
}
#[cfg(feature = "payouts")]
pub async fn get_bank_from_hs_locker(
    state: &routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    temp_token: Option<&String>,
    customer_id: &id_type::CustomerId,
    merchant_id: &id_type::MerchantId,
    token_ref: &str,
) -> errors::RouterResult<api::BankPayout> {
    let payment_method = get_payment_method_from_hs_locker(
        state,
        key_store,
        customer_id,
        merchant_id,
        token_ref,
        None,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Error getting payment method from locker")?;
    let pm_parsed: api::PayoutMethodData = payment_method
        .peek()
        .to_string()
        .parse_struct("PayoutMethodData")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    match &pm_parsed {
        api::PayoutMethodData::Bank(bank) => {
            if let Some(token) = temp_token {
                vault::Vault::store_payout_method_data_in_locker(
                    state,
                    Some(token.clone()),
                    &pm_parsed,
                    Some(customer_id.to_owned()),
                    key_store,
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error storing payout method data in temporary locker")?;
            }
            Ok(bank.to_owned())
        }
        api::PayoutMethodData::Card(_) => Err(errors::ApiErrorResponse::InvalidRequestData {
            message: "Expected bank details, found card details instead".to_string(),
        }
        .into()),
        api::PayoutMethodData::Wallet(_) => Err(errors::ApiErrorResponse::InvalidRequestData {
            message: "Expected bank details, found wallet details instead".to_string(),
        }
        .into()),
    }
}

#[cfg(feature = "v1")]
pub struct TempLockerCardSupport;

#[cfg(feature = "v1")]
impl TempLockerCardSupport {
    #[instrument(skip_all)]
    async fn create_payment_method_data_in_temp_locker(
        state: &routes::SessionState,
        payment_token: &str,
        card: api::CardDetailFromLocker,
        pm: &domain::PaymentMethod,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> errors::RouterResult<api::CardDetailFromLocker> {
        let card_number = card.card_number.clone().get_required_value("card_number")?;
        let card_exp_month = card
            .expiry_month
            .clone()
            .expose_option()
            .get_required_value("expiry_month")?;
        let card_exp_year = card
            .expiry_year
            .clone()
            .expose_option()
            .get_required_value("expiry_year")?;
        let card_holder_name = card
            .card_holder_name
            .clone()
            .expose_option()
            .unwrap_or_default();
        let value1 = payment_methods::mk_card_value1(
            card_number,
            card_exp_year,
            card_exp_month,
            Some(card_holder_name),
            None,
            None,
            None,
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error getting Value1 for locker")?;
        let value2 = payment_methods::mk_card_value2(
            None,
            None,
            None,
            Some(pm.customer_id.clone()),
            Some(pm.get_id().to_string()),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error getting Value2 for locker")?;

        let value1 = vault::VaultPaymentMethod::Card(value1);
        let value2 = vault::VaultPaymentMethod::Card(value2);

        let value1 = value1
            .encode_to_string_of_json()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Wrapped value1 construction failed when saving card to locker")?;

        let value2 = value2
            .encode_to_string_of_json()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Wrapped value2 construction failed when saving card to locker")?;

        let lookup_key = vault::create_tokenize(
            state,
            value1,
            Some(value2),
            payment_token.to_string(),
            merchant_key_store.key.get_inner(),
        )
        .await?;
        vault::add_delete_tokenized_data_task(
            &*state.store,
            &lookup_key,
            enums::PaymentMethod::Card,
        )
        .await?;
        metrics::TOKENIZED_DATA_COUNT.add(1, &[]);
        metrics::TASKS_ADDED_COUNT.add(
            1,
            router_env::metric_attributes!(("flow", "DeleteTokenizeData")),
        );
        Ok(card)
    }
}

pub async fn create_encrypted_data<T>(
    key_manager_state: &KeyManagerState,
    key_store: &domain::MerchantKeyStore,
    data: T,
) -> Result<Encryptable<Secret<serde_json::Value>>, error_stack::Report<errors::StorageError>>
where
    T: Debug + serde::Serialize,
{
    let key = key_store.key.get_inner().peek();
    let identifier = Identifier::Merchant(key_store.merchant_id.clone());

    let encoded_data = Encode::encode_to_value(&data)
        .change_context(errors::StorageError::SerializationFailed)
        .attach_printable("Unable to encode data")?;

    let secret_data = Secret::<_, masking::WithType>::new(encoded_data);

    let encrypted_data = domain::types::crypto_operation(
        key_manager_state,
        type_name!(payment_method::PaymentMethod),
        domain::types::CryptoOperation::Encrypt(secret_data),
        identifier.clone(),
        key,
    )
    .await
    .and_then(|val| val.try_into_operation())
    .change_context(errors::StorageError::EncryptionError)
    .attach_printable("Unable to encrypt data")?;

    Ok(encrypted_data)
}

pub async fn list_countries_currencies_for_connector_payment_method(
    state: routes::SessionState,
    req: ListCountriesCurrenciesRequest,
    _profile_id: Option<id_type::ProfileId>,
) -> errors::RouterResponse<ListCountriesCurrenciesResponse> {
    Ok(services::ApplicationResponse::Json(
        list_countries_currencies_for_connector_payment_method_util(
            state.conf.pm_filters.clone(),
            req.connector,
            req.payment_method_type,
        )
        .await,
    ))
}

// This feature will be more efficient as a WASM function rather than as an API.
// So extracting this logic to a separate function so that it can be used in WASM as well.
pub async fn list_countries_currencies_for_connector_payment_method_util(
    connector_filters: settings::ConnectorFilters,
    connector: api_enums::Connector,
    payment_method_type: api_enums::PaymentMethodType,
) -> ListCountriesCurrenciesResponse {
    let payment_method_type =
        settings::PaymentMethodFilterKey::PaymentMethodType(payment_method_type);

    let (currencies, country_codes) = connector_filters
        .0
        .get(&connector.to_string())
        .and_then(|filter| filter.0.get(&payment_method_type))
        .map(|filter| (filter.currency.clone(), filter.country.clone()))
        .unwrap_or_else(|| {
            connector_filters
                .0
                .get("default")
                .and_then(|filter| filter.0.get(&payment_method_type))
                .map_or((None, None), |filter| {
                    (filter.currency.clone(), filter.country.clone())
                })
        });

    let currencies =
        currencies.unwrap_or_else(|| api_enums::Currency::iter().collect::<HashSet<_>>());
    let country_codes =
        country_codes.unwrap_or_else(|| api_enums::CountryAlpha2::iter().collect::<HashSet<_>>());

    ListCountriesCurrenciesResponse {
        currencies,
        countries: country_codes
            .into_iter()
            .map(|country_code| CountryCodeWithName {
                code: country_code,
                name: common_enums::Country::from_alpha2(country_code),
            })
            .collect(),
    }
}

#[cfg(feature = "v1")]
pub async fn tokenize_card_flow(
    state: &routes::SessionState,
    req: domain::CardNetworkTokenizeRequest,
    merchant_context: &domain::MerchantContext,
) -> errors::RouterResult<api::CardNetworkTokenizeResponse> {
    match req.data {
        domain::TokenizeDataRequest::Card(ref card_req) => {
            let executor = tokenize::CardNetworkTokenizeExecutor::new(
                state,
                merchant_context,
                card_req,
                &req.customer,
            );
            let builder =
                tokenize::NetworkTokenizationBuilder::<tokenize::TokenizeWithCard>::default();
            execute_card_tokenization(executor, builder, card_req).await
        }
        domain::TokenizeDataRequest::ExistingPaymentMethod(ref payment_method) => {
            let executor = tokenize::CardNetworkTokenizeExecutor::new(
                state,
                merchant_context,
                payment_method,
                &req.customer,
            );
            let builder =
                tokenize::NetworkTokenizationBuilder::<tokenize::TokenizeWithPmId>::default();
            Box::pin(execute_payment_method_tokenization(
                executor,
                builder,
                payment_method,
            ))
            .await
        }
    }
}

#[cfg(feature = "v1")]
pub async fn execute_card_tokenization(
    executor: tokenize::CardNetworkTokenizeExecutor<'_, domain::TokenizeCardRequest>,
    builder: tokenize::NetworkTokenizationBuilder<'_, tokenize::TokenizeWithCard>,
    req: &domain::TokenizeCardRequest,
) -> errors::RouterResult<api::CardNetworkTokenizeResponse> {
    // Validate request and get optional customer
    let optional_customer = executor
        .validate_request_and_fetch_optional_customer()
        .await?;
    let builder = builder.set_validate_result();

    // Perform BIN lookup and validate card network
    let optional_card_info = executor
        .fetch_bin_details_and_validate_card_network(
            req.raw_card_number.clone(),
            req.card_issuer.as_ref(),
            req.card_network.as_ref(),
            req.card_type.as_ref(),
            req.card_issuing_country.as_ref(),
        )
        .await?;
    let builder = builder.set_card_details(req, optional_card_info);

    // Create customer if not present
    let customer = match optional_customer {
        Some(customer) => customer,
        None => executor.create_customer().await?,
    };
    let builder = builder.set_customer(&customer);

    // Tokenize card
    let (optional_card, optional_cvc) = builder.get_optional_card_and_cvc();
    let domain_card = optional_card
        .get_required_value("card")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    let network_token_details = executor
        .tokenize_card(&customer.id, &domain_card, optional_cvc)
        .await?;
    let builder = builder.set_token_details(&network_token_details);

    // Store card and token in locker
    let store_card_and_token_resp = executor
        .store_card_and_token_in_locker(&network_token_details, &domain_card, &customer.id)
        .await?;
    let builder = builder.set_stored_card_response(&store_card_and_token_resp);
    let builder = builder.set_stored_token_response(&store_card_and_token_resp);

    // Create payment method
    let payment_method = executor
        .create_payment_method(
            &store_card_and_token_resp,
            &network_token_details,
            &domain_card,
            &customer.id,
        )
        .await?;
    let builder = builder.set_payment_method_response(&payment_method);

    Ok(builder.build())
}

#[cfg(feature = "v1")]
pub async fn execute_payment_method_tokenization(
    executor: tokenize::CardNetworkTokenizeExecutor<'_, domain::TokenizePaymentMethodRequest>,
    builder: tokenize::NetworkTokenizationBuilder<'_, tokenize::TokenizeWithPmId>,
    req: &domain::TokenizePaymentMethodRequest,
) -> errors::RouterResult<api::CardNetworkTokenizeResponse> {
    // Fetch payment method
    let payment_method = executor
        .fetch_payment_method(&req.payment_method_id)
        .await?;
    let builder = builder.set_payment_method(&payment_method);

    // Validate payment method and customer
    let (locker_id, customer) = executor
        .validate_request_and_locker_reference_and_customer(&payment_method)
        .await?;
    let builder = builder.set_validate_result(&customer);

    // Fetch card from locker
    let card_details = get_card_from_locker(
        executor.state,
        &customer.id,
        executor.merchant_account.get_id(),
        &locker_id,
    )
    .await?;

    // Perform BIN lookup and validate card network
    let optional_card_info = executor
        .fetch_bin_details_and_validate_card_network(
            card_details.card_number.clone(),
            None,
            None,
            None,
            None,
        )
        .await?;
    let builder = builder.set_card_details(&card_details, optional_card_info, req.card_cvc.clone());

    // Tokenize card
    let (optional_card, optional_cvc) = builder.get_optional_card_and_cvc();
    let domain_card = optional_card.get_required_value("card")?;
    let network_token_details = executor
        .tokenize_card(&customer.id, &domain_card, optional_cvc)
        .await?;
    let builder = builder.set_token_details(&network_token_details);

    // Store token in locker
    let store_token_resp = executor
        .store_network_token_in_locker(
            &network_token_details,
            &customer.id,
            card_details.name_on_card.clone(),
            card_details.nick_name.clone().map(Secret::new),
        )
        .await?;
    let builder = builder.set_stored_token_response(&store_token_resp);

    // Update payment method
    let updated_payment_method = executor
        .update_payment_method(
            &store_token_resp,
            payment_method,
            &network_token_details,
            &domain_card,
        )
        .await?;
    let builder = builder.set_payment_method(&updated_payment_method);

    Ok(builder.build())
}


pub struct NoonActionOrder {
    id: String,
}


pub struct UnifiedConnectorServiceClientConfig {
    /// Base URL of the gRPC Server
    pub base_url: Url,

    /// Contains the connection timeout duration in seconds
    pub connection_timeout: u64,

    /// Set of external services/connectors available for the unified connector service
    #[serde(default, deserialize_with = "deserialize_hashset")]
    pub ucs_only_connectors: HashSet<Connector>,

    /// Set of connectors for which psync is disabled in unified connector service
    #[serde(default, deserialize_with = "deserialize_hashset")]
    pub ucs_psync_disabled_connectors: HashSet<Connector>,
}


pub struct BluesnapWalletTokenResponse {
    wallet_type: String,
    wallet_token: Secret<String>,
}


pub struct RoutingAlgorithmHelpers<'h> {
    pub name_mca_id_set: ConnectNameAndMCAIdForProfile<'h>,
    pub name_set: ConnectNameForProfile<'h>,
    pub routing_algorithm: &'h routing_types::StaticRoutingAlgorithm,
}


OpenAPI Block Path: components.schemas.VenmoAdditionalData
{
  "type": "object",
  "description": "Masked payout method details for venmo wallet payout method",
  "properties": {
    "telephone_number": {
      "type": "string",
      "description": "mobile number linked to venmo account",
      "example": "******* 3349",
      "nullable": true
    }
  }
}


pub struct AmazonPayRedirect {}


impl api::Refund for Bankofamerica {}


pub fn set_n(&mut self,n: u32)


pub struct NoEncryption;


impl api::ConnectorAccessToken for Nomupay {}

impl api::ConnectorAccessToken for for Nomupay


pub struct MetricsBucketResponse {
    #[serde(flatten)]
    pub values: AuthEventMetricsBucketValue,
    #[serde(flatten)]
    pub dimensions: AuthEventMetricsBucketIdentifier,
}


/// function to satisfy connector param validation macro
    pub fn validate(&self, _parent_field: &str) -> Result<(), ApplicationError> {
        Ok(())
    }


pub struct PaymentIntentMetricsAccumulator {
    pub successful_smart_retries: CountAccumulator,
    pub total_smart_retries: CountAccumulator,
    pub smart_retried_amount: SmartRetriedAmountAccumulator,
    pub payment_intent_count: CountAccumulator,
    pub payments_success_rate: PaymentsSuccessRateAccumulator,
    pub payment_processed_amount: ProcessedAmountAccumulator,
    pub payments_distribution: PaymentsDistributionAccumulator,
}


pub async fn find_by_merchant_id_merchant_connector_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_connector_id: &common_utils::id_type::MerchantConnectorAccountId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::merchant_connector_id.eq(merchant_connector_id.to_owned())),
        )
        .await
    }


pub struct FiservemeaError {
    pub code: Option<String>,
    pub message: Option<String>,
    pub details: Option<Vec<ErrorDetails>>,
}


impl api::Payment for Phonepe {}


impl NetworkTokenWebhookResponse {
    fn get_network_token_requestor_ref_id(&self) -> String {
        match self {
            Self::PanMetadataUpdate(data) => data.card.card_reference.clone(),
            Self::NetworkTokenMetadataUpdate(data) => data.token.card_reference.clone(),
        }
    }

    pub fn get_response_data(self) -> Box<dyn NetworkTokenWebhookResponseExt> {
        match self {
            Self::PanMetadataUpdate(data) => Box::new(data),
            Self::NetworkTokenMetadataUpdate(data) => Box::new(data),
        }
    }

    pub async fn fetch_merchant_id_payment_method_id_customer_id_from_callback_mapper(
        &self,
        state: &SessionState,
    ) -> RouterResult<(id_type::MerchantId, String, id_type::CustomerId)> {
        let network_token_requestor_ref_id = &self.get_network_token_requestor_ref_id();

        let db = &*state.store;
        let callback_mapper_data = db
            .find_call_back_mapper_by_id(network_token_requestor_ref_id)
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to fetch callback mapper data")?;

        Ok(callback_mapper_data
            .data
            .get_network_token_webhook_details())
    }
}


pub struct ThreedsInvokeRequest {
    three_d_s_server_trans_i_d: String,
    three_d_s_method_notification_u_r_l: String,
}


pub struct TokenizedCardDetails {
    pub masked_card_number: String,
    pub expiry_month: Secret<String>,
    pub expiry_year: Secret<String>,
    pub card_brand: String,
}


pub struct PaymentsStatusDetails {
    state: AmazonpayPaymentStatus,
    reason_code: Option<String>,
    reason_description: Option<String>,
    last_updated_timestamp: String,
}


pub fn from_storage(payouts: &'a Payouts, payout_attempt: &'a PayoutAttempt) -> Self {
        Self {
            payout_id: &payouts.payout_id,
            payout_attempt_id: &payout_attempt.payout_attempt_id,
            merchant_id: &payouts.merchant_id,
            customer_id: payouts.customer_id.as_ref(),
            address_id: payouts.address_id.as_ref(),
            profile_id: &payouts.profile_id,
            payout_method_id: payouts.payout_method_id.as_ref(),
            payout_type: payouts.payout_type,
            amount: payouts.amount,
            destination_currency: payouts.destination_currency,
            source_currency: payouts.source_currency,
            description: payouts.description.as_ref(),
            recurring: payouts.recurring,
            auto_fulfill: payouts.auto_fulfill,
            return_url: payouts.return_url.as_ref(),
            entity_type: payouts.entity_type,
            metadata: payouts.metadata.clone(),
            created_at: payouts.created_at.assume_utc(),
            last_modified_at: payouts.last_modified_at.assume_utc(),
            attempt_count: payouts.attempt_count,
            status: payouts.status,
            priority: payouts.priority,
            connector: payout_attempt.connector.as_ref(),
            connector_payout_id: payout_attempt.connector_payout_id.as_ref(),
            is_eligible: payout_attempt.is_eligible,
            error_message: payout_attempt.error_message.as_ref(),
            error_code: payout_attempt.error_code.as_ref(),
            business_country: payout_attempt.business_country,
            business_label: payout_attempt.business_label.as_ref(),
            merchant_connector_id: payout_attempt.merchant_connector_id.as_ref(),
        }
    }


pub struct SantanderAuthUpdateResponse {
    #[serde(rename = "camelCase")]
    pub refresh_url: String,
    pub token_type: String,
    pub client_id: String,
    pub access_token: Secret<String>,
    pub scopes: String,
    pub expires_in: i64,
}


pub struct PlaidBankAccountCredentialsInternational {
    pub account_id: String,
    pub iban: String,
    pub bic: String,
}


pub fn server(state: AppState) -> Scope {
        let mut route = web::scope("/v2/payments").app_data(web::Data::new(state));
        route = route
            .service(
                web::resource("/create-intent")
                    .route(web::post().to(payments::payments_create_intent)),
            )
            .service(web::resource("/filter").route(web::get().to(payments::get_payment_filters)))
            .service(
                web::resource("/profile/filter")
                    .route(web::get().to(payments::get_payment_filters_profile)),
            )
            .service(
                web::resource("")
                    .route(web::post().to(payments::payments_create_and_confirm_intent)),
            )
            .service(web::resource("/list").route(web::get().to(payments::payments_list)))
            .service(
                web::resource("/aggregate").route(web::get().to(payments::get_payments_aggregates)),
            )
            .service(
                web::resource("/recovery")
                    .route(web::post().to(payments::recovery_payments_create)),
            )
            .service(
                web::resource("/profile/aggregate")
                    .route(web::get().to(payments::get_payments_aggregates_profile)),
            );

        route =
            route
                .service(web::resource("/ref/{merchant_reference_id}").route(
                    web::get().to(payments::payment_get_intent_using_merchant_reference_id),
                ));

        route = route.service(
            web::scope("/{payment_id}")
                .service(
                    web::resource("/confirm-intent")
                        .route(web::post().to(payments::payment_confirm_intent)),
                )
                // TODO: Deprecated. Remove this in favour of /list-attempts
                .service(
                    web::resource("/list_attempts")
                        .route(web::get().to(payments::list_payment_attempts)),
                )
                .service(
                    web::resource("/list-attempts")
                        .route(web::get().to(payments::list_payment_attempts)),
                )
                .service(
                    web::resource("/proxy-confirm-intent")
                        .route(web::post().to(payments::proxy_confirm_intent)),
                )
                .service(
                    web::resource("/confirm-intent/external-vault-proxy")
                        .route(web::post().to(payments::confirm_intent_with_external_vault_proxy)),
                )
                .service(
                    web::resource("/get-intent")
                        .route(web::get().to(payments::payments_get_intent)),
                )
                .service(
                    web::resource("/update-intent")
                        .route(web::put().to(payments::payments_update_intent)),
                )
                .service(
                    web::resource("/create-external-sdk-tokens")
                        .route(web::post().to(payments::payments_connector_session)),
                )
                .service(
                    web::resource("")
                        .route(web::get().to(payments::payment_status))
                        .route(web::post().to(payments::payments_status_with_gateway_creds)),
                )
                .service(
                    web::resource("/start-redirection")
                        .route(web::get().to(payments::payments_start_redirection)),
                )
                .service(
                    web::resource("/payment-methods")
                        .route(web::get().to(payments::list_payment_methods)),
                )
                .service(
                    web::resource("/finish-redirection/{publishable_key}/{profile_id}")
                        .route(web::get().to(payments::payments_finish_redirection)),
                )
                .service(
                    web::resource("/capture").route(web::post().to(payments::payments_capture)),
                ),
        );

        route
    }


impl api::PaymentAuthorize for Santander {}

impl api::PaymentAuthorize for for Santander


impl api::PaymentSession for Opennode {}

impl api::PaymentSession for for Opennode


pub fn get_req_state(&self) -> ReqState {
        ReqState {
            event_context: events::EventContext::new(self.event_handler.clone()),
        }
    }


OpenAPI Block Path: components.schemas.GpayShippingAddressParameters
{
  "type": "object",
  "required": [
    "phone_number_required"
  ],
  "properties": {
    "phone_number_required": {
      "type": "boolean",
      "description": "Is shipping phone number required"
    }
  }
}


impl webhooks::IncomingWebhook for Katapult {
    fn get_webhook_object_reference_id(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_resource_object(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }
}

impl webhooks::IncomingWebhook for for Katapult


pub struct TokenizedCard {
    number: cards::CardNumber,
    expiration_month: Secret<String>,
    expiration_year: Secret<String>,
    cryptogram: Secret<String>,
    transaction_type: TransactionType,
}


impl ConnectorCustomerMap {
    /// Creates a new `ConnectorCustomerMap` from a HashMap
    pub fn new(
        map: std::collections::HashMap<common_utils::id_type::MerchantConnectorAccountId, String>,
    ) -> Self {
        Self(map)
    }
}


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

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

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

impl ConnectorSpecifications for for Payu


pub fn get_two_letter_country_code() -> JsResult {
    let country_code_with_name = CountryAlpha2::iter()
        .map(|country_code| CountryCodeWithName {
            code: country_code,
            name: common_enums::Country::from_alpha2(country_code),
        })
        .collect::<Vec<_>>();

    Ok(serde_wasm_bindgen::to_value(&country_code_with_name)?)
}


pub struct ChargebeeInvoiceContent {
    pub invoice: ChargebeeInvoiceData,
    pub subscription: Option<ChargebeeSubscriptionData>,
}


pub fn new() -> &'static Self {
        &Self {
            amount_converter: &FloatMajorUnitForConnector,
        }
    }


pub async fn connector_list(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsList;
    let merchant_id = path.into_inner();

    api::server_wrap(
        flow,
        state,
        &req,
        merchant_id.to_owned(),
        |state, _auth, merchant_id, _| list_payment_connectors(state, merchant_id, None),
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuthWithMerchantIdFromRoute(merchant_id.clone())),
            &auth::JWTAuthMerchantFromRoute {
                merchant_id,
                required_permission: Permission::MerchantConnectorRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    )
    .await
}


pub async fn get_merchant_pm_filter_graph(
    state: &SessionState,
    key: &str,
) -> Option<Arc<hyperswitch_constraint_graph::ConstraintGraph<dir::DirValue>>> {
    PM_FILTERS_CGRAPH_CACHE
        .get_val::<Arc<hyperswitch_constraint_graph::ConstraintGraph<dir::DirValue>>>(CacheKey {
            key: key.to_string(),
            prefix: state.tenant.redis_key_prefix.clone(),
        })
        .await
}


File: crates/router/src/types/api/files.rs
 Public structs: 3

use api_models::enums::FileUploadProvider;
pub use hyperswitch_domain_models::router_flow_types::files::{Retrieve, Upload};
pub use hyperswitch_interfaces::api::files::{FilePurpose, FileUpload, RetrieveFile, UploadFile};
use masking::{Deserialize, Serialize};
use serde_with::serde_as;

pub use super::files_v2::{FileUploadV2, RetrieveFileV2, UploadFileV2};
use crate::{
    core::errors,
    types::{self, transformers::ForeignTryFrom},
};

#[derive(Default, Debug, Deserialize, Serialize)]
pub struct FileId {
    pub file_id: String,
}

#[derive(Default, Debug, Deserialize, Serialize)]
pub struct FileRetrieveRequest {
    pub file_id: String,
    pub dispute_id: Option<String>,
}

#[derive(Debug)]
pub enum FileDataRequired {
    Required,
    NotRequired,
}

impl ForeignTryFrom<FileUploadProvider> for types::Connector {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn foreign_try_from(item: FileUploadProvider) -> Result<Self, Self::Error> {
        match item {
            FileUploadProvider::Stripe => Ok(Self::Stripe),
            FileUploadProvider::Checkout => Ok(Self::Checkout),
            FileUploadProvider::Worldpayvantiv => Ok(Self::Worldpayvantiv),
            FileUploadProvider::Router => Err(errors::ApiErrorResponse::NotSupported {
                message: "File upload provider is not a connector".to_owned(),
            }
            .into()),
        }
    }
}

impl ForeignTryFrom<&types::Connector> for FileUploadProvider {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn foreign_try_from(item: &types::Connector) -> Result<Self, Self::Error> {
        match *item {
            types::Connector::Stripe => Ok(Self::Stripe),
            types::Connector::Checkout => Ok(Self::Checkout),
            types::Connector::Worldpayvantiv => Ok(Self::Worldpayvantiv),
            _ => Err(errors::ApiErrorResponse::NotSupported {
                message: "Connector not supported as file provider".to_owned(),
            }
            .into()),
        }
    }
}

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


impl Responder {
    let flow = Flow::DecisionManagerUpsertConfig;
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, auth: auth::AuthenticationData, update_decision, _| {
            conditional_config::upsert_conditional_config(
                state,
                auth.key_store,
                update_decision,
                auth.profile,
            )
        },
        #[cfg(not(feature = "release"))]
        auth::auth_type(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::JWTAuth {
                permission: Permission::ProfileThreeDsDecisionManagerWrite,
            },
            req.headers(),
        ),
        #[cfg(feature = "release")]
        &auth::JWTAuth {
            permission: Permission::ProfileThreeDsDecisionManagerWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct RefundResponse {
    pub id: String,
    pub status: RefundStatus,
    pub amount: FloatMajorUnit,
    pub currency: String,
}


pub struct SantanderBoletoErrorResponse {
    #[serde(rename = "_errorCode")]
    pub error_code: i64,

    #[serde(rename = "_message")]
    pub error_message: String,

    #[serde(rename = "_details")]
    pub issuer_error_message: String,

    #[serde(rename = "_timestamp")]
    pub timestamp: String,

    #[serde(rename = "_traceId")]
    pub trace_id: String,

    #[serde(rename = "_errors")]
    pub errors: Option<Vec<ErrorObject>>,
}


impl api::Payment for Placetopay {}


impl api::RefundSync for Getnet {}


File: crates/euclid/src/backend/inputs.rs
 Public structs: 7

use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};

use crate::{
    enums,
    frontend::dir::enums::{CustomerDeviceDisplaySize, CustomerDevicePlatform, CustomerDeviceType},
};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MandateData {
    pub mandate_acceptance_type: Option<enums::MandateAcceptanceType>,
    pub mandate_type: Option<enums::MandateType>,
    pub payment_type: Option<enums::PaymentType>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentMethodInput {
    pub payment_method: Option<enums::PaymentMethod>,
    pub payment_method_type: Option<enums::PaymentMethodType>,
    pub card_network: Option<enums::CardNetwork>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentInput {
    pub amount: common_utils::types::MinorUnit,
    pub currency: enums::Currency,
    pub authentication_type: Option<enums::AuthenticationType>,
    pub card_bin: Option<String>,
    pub capture_method: Option<enums::CaptureMethod>,
    pub business_country: Option<enums::Country>,
    pub billing_country: Option<enums::Country>,
    pub business_label: Option<String>,
    pub setup_future_usage: Option<enums::SetupFutureUsage>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AcquirerDataInput {
    pub country: Option<enums::Country>,
    pub fraud_rate: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomerDeviceDataInput {
    pub platform: Option<CustomerDevicePlatform>,
    pub device_type: Option<CustomerDeviceType>,
    pub display_size: Option<CustomerDeviceDisplaySize>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IssuerDataInput {
    pub name: Option<String>,
    pub country: Option<enums::Country>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackendInput {
    pub metadata: Option<FxHashMap<String, String>>,
    pub payment: PaymentInput,
    pub payment_method: PaymentMethodInput,
    pub acquirer_data: Option<AcquirerDataInput>,
    pub customer_device_data: Option<CustomerDeviceDataInput>,
    pub issuer_data: Option<IssuerDataInput>,
    pub mandate: MandateData,
}


pub struct NetceteraErrorResponse {
    pub three_ds_server_trans_id: Option<String>,
    pub error_details: NetceteraErrorDetails,
}