File: crates/hyperswitch_connectors/src/connectors/netcetera/transformers.rs
 Public functions: 2
 Public structs: 16

use common_enums::enums;
use common_utils::{ext_traits::OptionExt as _, types::SemanticVersion};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::{ConnectorAuthType, ErrorResponse},
    router_flow_types::authentication::{Authentication, PreAuthentication},
    router_request_types::authentication::{
        AuthNFlowType, ChallengeParams, ConnectorAuthenticationRequestData,
        MessageExtensionAttribute, PreAuthNRequestData,
    },
    router_response_types::AuthenticationResponseData,
};
use hyperswitch_interfaces::{api::CurrencyUnit, errors::ConnectorError};
use masking::Secret;
use serde::{Deserialize, Serialize};

use super::netcetera_types;
use crate::{
    types::{ConnectorAuthenticationRouterData, PreAuthNRouterData, ResponseRouterData},
    utils::{get_card_details, to_connector_meta_from_secret, CardData as _},
};

//TODO: Fill the struct with respective fields
pub struct NetceteraRouterData<T> {
    pub amount: i64, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}

impl<T> TryFrom<(&CurrencyUnit, enums::Currency, i64, T)> for NetceteraRouterData<T> {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        (_currency_unit, _currency, amount, item): (&CurrencyUnit, enums::Currency, i64, T),
    ) -> Result<Self, Self::Error> {
        //Todo :  use utils to convert the amount to the type of amount that a connector accepts
        Ok(Self {
            amount,
            router_data: item,
        })
    }
}

impl<T> TryFrom<(i64, T)> for NetceteraRouterData<T> {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from((amount, router_data): (i64, T)) -> Result<Self, Self::Error> {
        Ok(Self {
            amount,
            router_data,
        })
    }
}

impl
    TryFrom<
        ResponseRouterData<
            PreAuthentication,
            NetceteraPreAuthenticationResponse,
            PreAuthNRequestData,
            AuthenticationResponseData,
        >,
    > for PreAuthNRouterData
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            PreAuthentication,
            NetceteraPreAuthenticationResponse,
            PreAuthNRequestData,
            AuthenticationResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let response = match item.response {
            NetceteraPreAuthenticationResponse::Success(pre_authn_response) => {
                // if card is not enrolled for 3ds, card_range will be None
                let card_range = pre_authn_response.get_card_range_if_available();
                let maximum_supported_3ds_version = card_range
                    .as_ref()
                    .map(|card_range| card_range.highest_common_supported_version.clone())
                    .unwrap_or_else(|| {
                        // Version "0.0.0" will be less that "2.0.0", hence we will treat this card as not eligible for 3ds authentication
                        SemanticVersion::new(0, 0, 0)
                    });
                let three_ds_method_data = card_range.as_ref().and_then(|card_range| {
                    card_range
                        .three_ds_method_data_form
                        .as_ref()
                        .map(|data| data.three_ds_method_data.clone())
                });
                let three_ds_method_url = card_range
                    .as_ref()
                    .and_then(|card_range| card_range.get_three_ds_method_url());
                Ok(AuthenticationResponseData::PreAuthNResponse {
                    threeds_server_transaction_id: pre_authn_response
                        .three_ds_server_trans_id
                        .clone(),
                    maximum_supported_3ds_version: maximum_supported_3ds_version.clone(),
                    connector_authentication_id: pre_authn_response.three_ds_server_trans_id,
                    three_ds_method_data,
                    three_ds_method_url,
                    message_version: maximum_supported_3ds_version,
                    connector_metadata: None,
                    directory_server_id: card_range
                        .as_ref()
                        .and_then(|card_range| card_range.directory_server_id.clone()),
                })
            }
            NetceteraPreAuthenticationResponse::Failure(error_response) => Err(ErrorResponse {
                code: error_response.error_details.error_code,
                message: error_response.error_details.error_description,
                reason: error_response.error_details.error_detail,
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
        };
        Ok(Self {
            response,
            ..item.data.clone()
        })
    }
}

impl
    TryFrom<
        ResponseRouterData<
            Authentication,
            NetceteraAuthenticationResponse,
            ConnectorAuthenticationRequestData,
            AuthenticationResponseData,
        >,
    > for ConnectorAuthenticationRouterData
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            Authentication,
            NetceteraAuthenticationResponse,
            ConnectorAuthenticationRequestData,
            AuthenticationResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let response = match item.response {
            NetceteraAuthenticationResponse::Success(response) => {
                let authn_flow_type = match response.acs_challenge_mandated {
                    Some(ACSChallengeMandatedIndicator::Y) => {
                        AuthNFlowType::Challenge(Box::new(ChallengeParams {
                            acs_url: response.authentication_response.acs_url.clone(),
                            challenge_request: response.encoded_challenge_request,
                            acs_reference_number: response
                                .authentication_response
                                .acs_reference_number,
                            acs_trans_id: response.authentication_response.acs_trans_id,
                            three_dsserver_trans_id: Some(response.three_ds_server_trans_id),
                            acs_signed_content: response.authentication_response.acs_signed_content,
                        }))
                    }
                    Some(ACSChallengeMandatedIndicator::N) | None => AuthNFlowType::Frictionless,
                };

                let challenge_code = response
                    .authentication_request
                    .as_ref()
                    .and_then(|req| req.three_ds_requestor_challenge_ind.as_ref())
                    .and_then(|ind| match ind {
                        ThreedsRequestorChallengeInd::Single(s) => Some(s.clone()),
                        ThreedsRequestorChallengeInd::Multiple(v) => v.first().cloned(),
                    });

                let message_extension = response
                    .authentication_response
                    .message_extension
                    .as_ref()
                    .and_then(|v| match serde_json::to_value(v) {
                        Ok(val) => Some(Secret::new(val)),
                        Err(e) => {
                            router_env::logger::error!(
                                "Failed to serialize message_extension: {:?}",
                                e
                            );
                            None
                        }
                    });

                Ok(AuthenticationResponseData::AuthNResponse {
                    authn_flow_type,
                    authentication_value: response.authentication_value,
                    trans_status: response.trans_status,
                    connector_metadata: None,
                    ds_trans_id: response.authentication_response.ds_trans_id,
                    eci: response.eci,
                    challenge_code,
                    challenge_cancel: None, // Note - challenge_cancel field is received in the RReq and updated in DB during external_authentication_incoming_webhook_flow
                    challenge_code_reason: response.authentication_response.trans_status_reason,
                    message_extension,
                })
            }
            NetceteraAuthenticationResponse::Error(error_response) => Err(ErrorResponse {
                code: error_response.error_details.error_code,
                message: error_response.error_details.error_description,
                reason: error_response.error_details.error_detail,
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
        };
        Ok(Self {
            response,
            ..item.data.clone()
        })
    }
}

pub struct NetceteraAuthType {
    pub(super) certificate: Secret<String>,
    pub(super) private_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for NetceteraAuthType {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type.to_owned() {
            ConnectorAuthType::CertificateAuth {
                certificate,
                private_key,
            } => Ok(Self {
                certificate,
                private_key,
            }),
            _ => Err(ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NetceteraErrorResponse {
    pub three_ds_server_trans_id: Option<String>,
    pub error_details: NetceteraErrorDetails,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NetceteraErrorDetails {
    /// Universally unique identifier for the transaction assigned by the 3DS Server.
    #[serde(rename = "threeDSServerTransID")]
    pub three_ds_server_trans_id: Option<String>,

    /// Universally Unique identifier for the transaction assigned by the ACS.
    #[serde(rename = "acsTransID")]
    pub acs_trans_id: Option<String>,

    /// Universally unique identifier for the transaction assigned by the DS.
    #[serde(rename = "dsTransID")]
    pub ds_trans_id: Option<String>,

    /// Code indicating the type of problem identified.
    pub error_code: String,

    /// Code indicating the 3-D Secure component that identified the error.
    pub error_component: Option<String>,

    /// Text describing the problem identified.
    pub error_description: String,

    /// Additional detail regarding the problem identified.
    pub error_detail: Option<String>,

    /// Universally unique identifier for the transaction assigned by the 3DS SDK.
    #[serde(rename = "sdkTransID")]
    pub sdk_trans_id: Option<String>,

    /// The Message Type that was identified as erroneous.
    pub error_message_type: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct NetceteraMetaData {
    pub mcc: Option<String>,
    pub merchant_country_code: Option<String>,
    pub merchant_name: Option<String>,
    pub endpoint_prefix: String,
    pub three_ds_requestor_name: Option<String>,
    pub three_ds_requestor_id: Option<String>,
    pub merchant_configuration_id: Option<String>,
}

impl TryFrom<&Option<common_utils::pii::SecretSerdeValue>> for NetceteraMetaData {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        meta_data: &Option<common_utils::pii::SecretSerdeValue>,
    ) -> Result<Self, Self::Error> {
        let metadata: Self = to_connector_meta_from_secret::<Self>(meta_data.clone())
            .change_context(ConnectorError::InvalidConnectorConfig { config: "metadata" })?;
        Ok(metadata)
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NetceteraPreAuthenticationRequest {
    cardholder_account_number: cards::CardNumber,
    scheme_id: Option<netcetera_types::SchemeId>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum NetceteraPreAuthenticationResponse {
    Success(Box<NetceteraPreAuthenticationResponseData>),
    Failure(Box<NetceteraErrorResponse>),
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NetceteraPreAuthenticationResponseData {
    #[serde(rename = "threeDSServerTransID")]
    pub three_ds_server_trans_id: String,
    pub card_ranges: Vec<CardRange>,
}

impl NetceteraPreAuthenticationResponseData {
    pub fn get_card_range_if_available(&self) -> Option<CardRange> {
        let card_range = self
            .card_ranges
            .iter()
            .max_by_key(|card_range| &card_range.highest_common_supported_version);
        card_range.cloned()
    }
}

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CardRange {
    pub scheme_id: netcetera_types::SchemeId,
    pub directory_server_id: Option<String>,
    pub acs_protocol_versions: Vec<AcsProtocolVersion>,
    #[serde(rename = "threeDSMethodDataForm")]
    pub three_ds_method_data_form: Option<ThreeDSMethodDataForm>,
    pub highest_common_supported_version: SemanticVersion,
}

impl CardRange {
    pub fn get_three_ds_method_url(&self) -> Option<String> {
        self.acs_protocol_versions
            .iter()
            .find(|acs_protocol_version| {
                acs_protocol_version.version == self.highest_common_supported_version
            })
            .and_then(|acs_version| acs_version.three_ds_method_url.clone())
    }
}

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ThreeDSMethodDataForm {
    // base64 encoded value for 3ds method data collection
    #[serde(rename = "threeDSMethodData")]
    pub three_ds_method_data: String,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct AcsProtocolVersion {
    pub version: SemanticVersion,
    #[serde(rename = "threeDSMethodURL")]
    pub three_ds_method_url: Option<String>,
}

impl TryFrom<&NetceteraRouterData<&PreAuthNRouterData>> for NetceteraPreAuthenticationRequest {
    type Error = error_stack::Report<ConnectorError>;

    fn try_from(value: &NetceteraRouterData<&PreAuthNRouterData>) -> Result<Self, Self::Error> {
        let router_data = value.router_data;
        let is_cobadged_card = || {
            router_data
                .request
                .card
                .card_number
                .is_cobadged_card()
                .change_context(ConnectorError::RequestEncodingFailed)
                .attach_printable("error while checking is_cobadged_card")
        };
        Ok(Self {
            cardholder_account_number: router_data.request.card.card_number.clone(),
            scheme_id: router_data
                .request
                .card
                .card_network
                .clone()
                .map(|card_network| {
                    is_cobadged_card().map(|is_cobadged_card| {
                        is_cobadged_card
                            .then_some(netcetera_types::SchemeId::try_from(card_network))
                    })
                })
                .transpose()?
                .flatten()
                .transpose()?,
        })
    }
}

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
#[serde_with::skip_serializing_none]
pub struct NetceteraAuthenticationRequest {
    /// Specifies the preferred version of 3D Secure protocol to be utilized while executing 3D Secure authentication.
    /// 3DS Server initiates an authentication request with the preferred version and if this version is not supported by
    /// other 3D Secure components, it falls back to the next supported version(s) and continues authentication.
    ///
    /// If the preferred version is enforced by setting  #enforcePreferredProtocolVersion flag, but this version
    /// is not supported by one of the 3D Secure components, 3DS Server does not initiate an authentication and provides
    /// corresponding error message to the customer.
    ///
    /// The accepted values are:
    /// - 2.1.0 -> prefer authentication with 2.1.0 version,
    /// - 2.2.0 -> prefer authentication with 2.2.0 version,
    /// - 2.3.1 -> prefer authentication with 2.3.1 version,
    /// - latest -> prefer authentication with the latest version, the 3DS Server is certified for. 2.3.1 at this moment.
    pub preferred_protocol_version: Option<SemanticVersion>,
    /// Boolean flag that enforces preferred 3D Secure protocol version to be used in 3D Secure authentication.
    /// The value should be set true to enforce preferred version. If value is false or not provided,
    /// 3DS Server can fall back to next supported 3DS protocol version while initiating 3D Secure authentication.
    ///
    /// For application initiated transactions (deviceChannel = '01'), the preferred protocol version must be enforced.
    pub enforce_preferred_protocol_version: Option<bool>,
    pub device_channel: netcetera_types::NetceteraDeviceChannel,
    /// Identifies the category of the message for a specific use case. The accepted values are:
    ///
    /// - 01 -> PA
    /// - 02 -> NPA
    /// - 80 - 99 -> PS Specific Values (80 -> MasterCard Identity Check Insights;
    ///                                85 -> MasterCard Identity Check, Production Validation PA;
    ///                                86 -> MasterCard Identity Check, Production Validation NPA)
    pub message_category: netcetera_types::NetceteraMessageCategory,
    #[serde(rename = "threeDSCompInd")]
    pub three_ds_comp_ind: Option<netcetera_types::ThreeDSMethodCompletionIndicator>,
    /**
     * Contains the 3DS Server Transaction ID used during the previous execution of the 3DS method. Accepted value
     * length is 36 characters. Accepted value is a Canonical format as defined in IETF RFC 4122. May utilise any of the
     * specified versions if the output meets specified requirements.
     *
     * This field is required if the 3DS Requestor reuses previous 3DS Method execution with deviceChannel = 02 (BRW).
     * Available for supporting EMV 3DS 2.3.1 and later versions.
     */
    #[serde(rename = "threeDSMethodId")]
    pub three_ds_method_id: Option<String>,
    #[serde(rename = "threeDSRequestor")]
    pub three_ds_requestor: Option<netcetera_types::ThreeDSRequestor>,
    #[serde(rename = "threeDSServerTransID")]
    pub three_ds_server_trans_id: String,
    #[serde(rename = "threeDSRequestorURL")]
    pub three_ds_requestor_url: Option<String>,
    pub cardholder_account: netcetera_types::CardholderAccount,
    pub cardholder: Option<netcetera_types::Cardholder>,
    pub purchase: Option<netcetera_types::Purchase>,
    pub acquirer: Option<netcetera_types::AcquirerData>,
    pub merchant: Option<netcetera_types::MerchantData>,
    pub broad_info: Option<String>,
    pub device_render_options: Option<netcetera_types::DeviceRenderingOptionsSupported>,
    pub message_extension: Option<Vec<MessageExtensionAttribute>>,
    pub challenge_message_extension: Option<Vec<MessageExtensionAttribute>>,
    pub browser_information: Option<netcetera_types::Browser>,
    #[serde(rename = "threeRIInd")]
    pub three_ri_ind: Option<String>,
    pub sdk_information: Option<netcetera_types::Sdk>,
    pub device: Option<String>,
    pub multi_transaction: Option<String>,
    pub device_id: Option<String>,
    pub user_id: Option<String>,
    pub payee_origin: Option<url::Url>,
}

impl TryFrom<&NetceteraRouterData<&ConnectorAuthenticationRouterData>>
    for NetceteraAuthenticationRequest
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: &NetceteraRouterData<&ConnectorAuthenticationRouterData>,
    ) -> Result<Self, Self::Error> {
        let now = common_utils::date_time::now();
        let request = item.router_data.request.clone();
        let pre_authn_data = request.pre_authentication_data.clone();
        let ip_address = request
            .browser_details
            .as_ref()
            .and_then(|browser| browser.ip_address);
        let three_ds_requestor = netcetera_types::ThreeDSRequestor::new(
            ip_address,
            item.router_data.psd2_sca_exemption_type,
            item.router_data.request.force_3ds_challenge,
            item.router_data
                .request
                .pre_authentication_data
                .message_version
                .clone(),
        );
        let card = get_card_details(request.payment_method_data, "netcetera")?;
        let is_cobadged_card = card
            .card_number
            .clone()
            .is_cobadged_card()
            .change_context(ConnectorError::RequestEncodingFailed)
            .attach_printable("error while checking is_cobadged_card")?;
        let cardholder_account = netcetera_types::CardholderAccount {
            acct_type: None,
            card_expiry_date: Some(card.get_expiry_date_as_yymm()?),
            acct_info: None,
            acct_number: card.card_number,
            scheme_id: card
                .card_network
                .clone()
                .and_then(|card_network| {
                    is_cobadged_card.then_some(netcetera_types::SchemeId::try_from(card_network))
                })
                .transpose()?,
            acct_id: None,
            pay_token_ind: None,
            pay_token_info: None,
            card_security_code: Some(card.card_cvc),
        };
        let currency = request
            .currency
            .get_required_value("currency")
            .change_context(ConnectorError::MissingRequiredField {
                field_name: "currency",
            })?;
        let purchase = netcetera_types::Purchase {
            purchase_instal_data: None,
            merchant_risk_indicator: None,
            purchase_amount: request.amount,
            purchase_currency: currency.iso_4217().to_string(),
            purchase_exponent: currency.number_of_digits_after_decimal_point(),
            purchase_date: Some(
                common_utils::date_time::format_date(
                    now,
                    common_utils::date_time::DateFormat::YYYYMMDDHHmmss,
                )
                .change_context(
                    ConnectorError::RequestEncodingFailedWithReason(
                        "Failed to format Date".to_string(),
                    ),
                )?,
            ),
            recurring_expiry: None,
            recurring_frequency: None,
            // 01 -> Goods and Services, hardcoding this as we serve this usecase only for now
            trans_type: Some("01".to_string()),
            recurring_amount: None,
            recurring_currency: None,
            recurring_exponent: None,
            recurring_date: None,
            amount_ind: None,
            frequency_ind: None,
        };
        let acquirer_details = netcetera_types::AcquirerData {
            acquirer_bin: request.pre_authentication_data.acquirer_bin,
            acquirer_merchant_id: request.pre_authentication_data.acquirer_merchant_id,
            acquirer_country_code: request.pre_authentication_data.acquirer_country_code,
        };
        let connector_meta_data: NetceteraMetaData = item
            .router_data
            .connector_meta_data
            .clone()
            .parse_value("NetceteraMetaData")
            .change_context(ConnectorError::RequestEncodingFailed)?;
        let merchant_data = netcetera_types::MerchantData {
            merchant_configuration_id: connector_meta_data.merchant_configuration_id,
            mcc: connector_meta_data.mcc,
            merchant_country_code: connector_meta_data.merchant_country_code,
            merchant_name: connector_meta_data.merchant_name,
            notification_url: request.return_url.clone(),
            three_ds_requestor_id: connector_meta_data.three_ds_requestor_id,
            three_ds_requestor_name: connector_meta_data.three_ds_requestor_name,
            white_list_status: None,
            trust_list_status: None,
            seller_info: None,
            results_response_notification_url: Some(request.webhook_url),
        };
        let browser_information = match request.device_channel {
            api_models::payments::DeviceChannel::Browser => {
                request.browser_details.map(netcetera_types::Browser::from)
            }
            api_models::payments::DeviceChannel::App => None,
        };
        let sdk_information = match request.device_channel {
            api_models::payments::DeviceChannel::App => {
                request.sdk_information.map(netcetera_types::Sdk::from)
            }
            api_models::payments::DeviceChannel::Browser => None,
        };
        let device_render_options = match request.device_channel {
            api_models::payments::DeviceChannel::App => {
                Some(netcetera_types::DeviceRenderingOptionsSupported {
                    // hard-coded until core provides these values.
                    sdk_interface: netcetera_types::SdkInterface::Both,
                    sdk_ui_type: vec![
                        netcetera_types::SdkUiType::Text,
                        netcetera_types::SdkUiType::SingleSelect,
                        netcetera_types::SdkUiType::MultiSelect,
                        netcetera_types::SdkUiType::Oob,
                        netcetera_types::SdkUiType::HtmlOther,
                    ],
                })
            }
            api_models::payments::DeviceChannel::Browser => None,
        };
        Ok(Self {
            preferred_protocol_version: Some(pre_authn_data.message_version),
            // For Device channel App, we should enforce the preferred protocol version
            enforce_preferred_protocol_version: Some(matches!(
                request.device_channel,
                api_models::payments::DeviceChannel::App
            )),
            device_channel: netcetera_types::NetceteraDeviceChannel::from(request.device_channel),
            message_category: netcetera_types::NetceteraMessageCategory::from(
                request.message_category,
            ),
            three_ds_comp_ind: Some(netcetera_types::ThreeDSMethodCompletionIndicator::from(
                request.threeds_method_comp_ind,
            )),
            three_ds_method_id: None,
            three_ds_requestor: Some(three_ds_requestor),
            three_ds_server_trans_id: pre_authn_data.threeds_server_transaction_id,
            three_ds_requestor_url: Some(request.three_ds_requestor_url),
            cardholder_account,
            cardholder: Some(netcetera_types::Cardholder::try_from((
                request.billing_address,
                request.shipping_address,
            ))?),
            purchase: Some(purchase),
            acquirer: Some(acquirer_details),
            merchant: Some(merchant_data),
            broad_info: None,
            device_render_options,
            message_extension: None,
            challenge_message_extension: None,
            browser_information,
            three_ri_ind: None,
            sdk_information,
            device: None,
            multi_transaction: None,
            device_id: None,
            user_id: None,
            payee_origin: None,
        })
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum NetceteraAuthenticationResponse {
    Error(NetceteraAuthenticationFailureResponse),
    Success(NetceteraAuthenticationSuccessResponse),
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NetceteraAuthenticationSuccessResponse {
    #[serde(rename = "threeDSServerTransID")]
    pub three_ds_server_trans_id: String,
    pub trans_status: common_enums::TransactionStatus,
    pub authentication_value: Option<Secret<String>>,
    pub eci: Option<String>,
    pub acs_challenge_mandated: Option<ACSChallengeMandatedIndicator>,
    pub authentication_request: Option<AuthenticationRequest>,
    pub authentication_response: AuthenticationResponse,
    #[serde(rename = "base64EncodedChallengeRequest")]
    pub encoded_challenge_request: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NetceteraAuthenticationFailureResponse {
    pub error_details: NetceteraErrorDetails,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticationRequest {
    #[serde(rename = "threeDSRequestorChallengeInd")]
    pub three_ds_requestor_challenge_ind: Option<ThreedsRequestorChallengeInd>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ThreedsRequestorChallengeInd {
    Single(String),
    Multiple(Vec<String>),
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticationResponse {
    #[serde(rename = "acsURL")]
    pub acs_url: Option<url::Url>,
    pub acs_reference_number: Option<String>,
    #[serde(rename = "acsTransID")]
    pub acs_trans_id: Option<String>,
    #[serde(rename = "dsTransID")]
    pub ds_trans_id: Option<String>,
    pub acs_signed_content: Option<String>,
    pub trans_status_reason: Option<String>,
    pub message_extension: Option<serde_json::Value>,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub enum ACSChallengeMandatedIndicator {
    /// Challenge is mandated
    Y,
    /// Challenge is not mandated
    N,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ResultsResponseData {
    /// Universally unique transaction identifier assigned by the 3DS Server to identify a single transaction.
    /// It has the same value as the authentication request and conforms to the format defined in IETF RFC 4122.
    #[serde(rename = "threeDSServerTransID")]
    pub three_ds_server_trans_id: String,

    /// Indicates the status of a transaction in terms of its authentication.
    ///
    /// Valid values:
    /// - `Y`: Authentication / Account verification successful.
    /// - `N`: Not authenticated / Account not verified; Transaction denied.
    /// - `U`: Authentication / Account verification could not be performed; technical or other problem.
    /// - `C`: A challenge is required to complete the authentication.
    /// - `R`: Authentication / Account verification Rejected. Issuer is rejecting authentication/verification
    ///   and request that authorization not be attempted.
    /// - `A`: Attempts processing performed; Not authenticated / verified, but a proof of attempt
    ///   authentication / verification is provided.
    /// - `D`: A challenge is required to complete the authentication. Decoupled Authentication confirmed.
    /// - `I`: Informational Only; 3DS Requestor challenge preference acknowledged.
    pub trans_status: Option<common_enums::TransactionStatus>,

    /// Payment System-specific value provided as part of the ACS registration for each supported DS.
    /// Authentication Value may be used to provide proof of authentication.
    pub authentication_value: Option<Secret<String>>,

    /// Payment System-specific value provided by the ACS to indicate the results of the attempt to authenticate
    /// the Cardholder.
    pub eci: Option<String>,

    /// The received Results Request from the Directory Server.
    pub results_request: Option<serde_json::Value>,

    /// The sent Results Response to the Directory Server.
    pub results_response: Option<serde_json::Value>,

    /// Optional object containing error details if any errors occurred during the process.
    pub error_details: Option<NetceteraErrorDetails>,
}


pub fn generate_digest_from_request(&self, payload: &RequestContent) -> String {
        let payload_bytes = match payload {
            RequestContent::RawBytes(bytes) => bytes.clone(),
            _ => payload.get_inner_value().expose().as_bytes().to_vec(),
        };

        self.generate_digest(&payload_bytes)
    }


impl std::ops::Deref for ConnectorCustomerMap {
    type Target =
        std::collections::HashMap<common_utils::id_type::MerchantConnectorAccountId, String>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl std::ops::Deref for for ConnectorCustomerMap


pub async fn get_extended_card_info(
    state: SessionState,
    merchant_id: id_type::MerchantId,
    payment_id: id_type::PaymentId,
) -> RouterResponse<payments_api::ExtendedCardInfoResponse> {
    let redis_conn = state
        .store
        .get_redis_conn()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get redis connection")?;

    let key = helpers::get_redis_key_for_extended_card_info(&merchant_id, &payment_id);
    let payload = redis_conn
        .get_key::<String>(&key.into())
        .await
        .change_context(errors::ApiErrorResponse::ExtendedCardInfoNotFound)?;

    Ok(services::ApplicationResponse::Json(
        payments_api::ExtendedCardInfoResponse { payload },
    ))
}


pub struct Customers;


pub async fn user_from_email(
    state: SessionState,
    req: user_api::UserFromEmailRequest,
) -> UserResponse<user_api::TokenResponse> {
    let token = req.token.expose();
    let email_token = auth::decode_jwt::<email_types::EmailToken>(&token, &state)
        .await
        .change_context(UserErrors::LinkInvalid)?;

    auth::blacklist::check_email_token_in_blacklist(&state, &token).await?;

    let user_from_db: domain::UserFromStorage = state
        .global_store
        .find_user_by_email(&email_token.get_email()?)
        .await
        .change_context(UserErrors::InternalServerError)?
        .into();

    let next_flow =
        domain::NextFlow::from_origin(email_token.get_flow(), user_from_db.clone(), &state).await?;

    let token = next_flow.get_token(&state).await?;

    let response = user_api::TokenResponse {
        token: token.clone(),
        token_type: next_flow.get_flow().into(),
    };
    auth::cookies::set_cookie_response(response, token)
}


File: crates/hyperswitch_interfaces/src/events/routing_api_logs.rs
 Public functions: 12
 Public structs: 1

//! Routing API logs interface

use std::fmt;

use api_models::routing::RoutableConnectorChoice;
use common_utils::request::Method;
use router_env::tracing_actix_web::RequestId;
use serde::Serialize;
use serde_json::json;
use time::OffsetDateTime;

/// RoutingEngine enum
#[derive(Debug, Clone, Copy, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum RoutingEngine {
    /// Dynamo for routing
    IntelligentRouter,
    /// Decision engine for routing
    DecisionEngine,
}

/// Method type enum
#[derive(Debug, Clone, Copy, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ApiMethod {
    /// grpc call
    Grpc,
    /// Rest call
    Rest(Method),
}

impl fmt::Display for ApiMethod {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Grpc => write!(f, "Grpc"),
            Self::Rest(method) => write!(f, "Rest ({method})"),
        }
    }
}

#[derive(Debug, Serialize)]
/// RoutingEvent type
pub struct RoutingEvent {
    tenant_id: common_utils::id_type::TenantId,
    routable_connectors: String,
    payment_connector: Option<String>,
    flow: String,
    request: String,
    response: Option<String>,
    error: Option<String>,
    url: String,
    method: String,
    payment_id: String,
    profile_id: common_utils::id_type::ProfileId,
    merchant_id: common_utils::id_type::MerchantId,
    created_at: i128,
    status_code: Option<u16>,
    request_id: String,
    routing_engine: RoutingEngine,
    routing_approach: Option<String>,
}

impl RoutingEvent {
    /// fn new RoutingEvent
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        tenant_id: common_utils::id_type::TenantId,
        routable_connectors: String,
        flow: &str,
        request: serde_json::Value,
        url: String,
        method: ApiMethod,
        payment_id: String,
        profile_id: common_utils::id_type::ProfileId,
        merchant_id: common_utils::id_type::MerchantId,
        request_id: Option<RequestId>,
        routing_engine: RoutingEngine,
    ) -> Self {
        Self {
            tenant_id,
            routable_connectors,
            flow: flow.to_string(),
            request: request.to_string(),
            response: None,
            error: None,
            url,
            method: method.to_string(),
            payment_id,
            profile_id,
            merchant_id,
            created_at: OffsetDateTime::now_utc().unix_timestamp_nanos(),
            status_code: None,
            request_id: request_id
                .map(|i| i.as_hyphenated().to_string())
                .unwrap_or("NO_REQUEST_ID".to_string()),
            routing_engine,
            payment_connector: None,
            routing_approach: None,
        }
    }

    /// fn set_response_body
    pub fn set_response_body<T: Serialize>(&mut self, response: &T) {
        match masking::masked_serialize(response) {
            Ok(masked) => {
                self.response = Some(masked.to_string());
            }
            Err(er) => self.set_error(json!({"error": er.to_string()})),
        }
    }

    /// fn set_error_response_body
    pub fn set_error_response_body<T: Serialize>(&mut self, response: &T) {
        match masking::masked_serialize(response) {
            Ok(masked) => {
                self.error = Some(masked.to_string());
            }
            Err(er) => self.set_error(json!({"error": er.to_string()})),
        }
    }

    /// fn set_error
    pub fn set_error(&mut self, error: serde_json::Value) {
        self.error = Some(error.to_string());
    }

    /// set response status code
    pub fn set_status_code(&mut self, code: u16) {
        self.status_code = Some(code);
    }

    /// set response status code
    pub fn set_routable_connectors(&mut self, connectors: Vec<RoutableConnectorChoice>) {
        let connectors = connectors
            .into_iter()
            .map(|c| {
                format!(
                    "{:?}:{:?}",
                    c.connector,
                    c.merchant_connector_id
                        .map(|id| id.get_string_repr().to_string())
                        .unwrap_or(String::from(""))
                )
            })
            .collect::<Vec<_>>()
            .join(",");
        self.routable_connectors = connectors;
    }

    /// set payment connector
    pub fn set_payment_connector(&mut self, connector: RoutableConnectorChoice) {
        self.payment_connector = Some(format!(
            "{:?}:{:?}",
            connector.connector,
            connector
                .merchant_connector_id
                .map(|id| id.get_string_repr().to_string())
                .unwrap_or(String::from(""))
        ));
    }

    /// set routing approach
    pub fn set_routing_approach(&mut self, approach: String) {
        self.routing_approach = Some(approach);
    }

    /// Returns the request ID of the event.
    pub fn get_request_id(&self) -> &str {
        &self.request_id
    }

    /// Returns the merchant ID of the event.
    pub fn get_merchant_id(&self) -> &str {
        self.merchant_id.get_string_repr()
    }

    /// Returns the payment ID of the event.
    pub fn get_payment_id(&self) -> &str {
        &self.payment_id
    }

    /// Returns the profile ID of the event.
    pub fn get_profile_id(&self) -> &str {
        self.profile_id.get_string_repr()
    }
}


impl api::Refund for Silverflow {}


pub struct CardInformation {
    pub card_number: CardNumber,
    pub expiry_month: Secret<String>,
    pub expiry_year: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cvv: Option<Secret<String>>,
    pub cardholder_name: Secret<String>,
    pub cardholder_email: pii::Email,
    pub cardholder_phone_number: Secret<String>,
}


pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Role> {
        generics::generic_insert(conn, self).await
    }


pub struct AcceptDisputeResponse {
    pub dispute_status: api_models::enums::DisputeStatus,
    pub connector_status: Option<String>,
}


File: crates/router/src/types/domain/user/user_authentication_method.rs

use std::sync::LazyLock;

use common_enums::{Owner, UserAuthType};
use diesel_models::UserAuthenticationMethod;

pub static DEFAULT_USER_AUTH_METHOD: LazyLock<UserAuthenticationMethod> =
    LazyLock::new(|| UserAuthenticationMethod {
        id: String::from("hyperswitch_default"),
        auth_id: String::from("hyperswitch"),
        owner_id: String::from("hyperswitch"),
        owner_type: Owner::Tenant,
        auth_type: UserAuthType::Password,
        private_config: None,
        public_config: None,
        allow_signup: true,
        created_at: common_utils::date_time::now(),
        last_modified_at: common_utils::date_time::now(),
        email_domain: String::from("hyperswitch"),
    });


File: crates/router/src/types/api/authentication.rs
 Public functions: 1
 Public structs: 4

use std::str::FromStr;

use api_models::enums;
use common_utils::errors::CustomResult;
use error_stack::ResultExt;
pub use hyperswitch_domain_models::{
    router_flow_types::authentication::{
        Authentication, PostAuthentication, PreAuthentication, PreAuthenticationVersionCall,
    },
    router_request_types::authentication::MessageCategory,
};

use crate::{
    connector, core::errors, services::connector_integration_interface::ConnectorEnum,
    types::storage,
};

#[derive(Clone, serde::Deserialize, Debug, serde::Serialize)]
pub struct AcquirerDetails {
    pub acquirer_bin: String,
    pub acquirer_merchant_mid: String,
    pub acquirer_country_code: Option<String>,
}

#[derive(Clone, serde::Deserialize, Debug, serde::Serialize)]
pub struct AuthenticationResponse {
    pub trans_status: common_enums::TransactionStatus,
    pub acs_url: Option<url::Url>,
    pub challenge_request: Option<String>,
    pub acs_reference_number: Option<String>,
    pub acs_trans_id: Option<String>,
    pub three_dsserver_trans_id: Option<String>,
    pub acs_signed_content: Option<String>,
}

impl TryFrom<storage::Authentication> for AuthenticationResponse {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn try_from(authentication: storage::Authentication) -> Result<Self, Self::Error> {
        let trans_status = authentication.trans_status.ok_or(errors::ApiErrorResponse::InternalServerError).attach_printable("trans_status must be populated in authentication table authentication call is successful")?;
        let acs_url = authentication
            .acs_url
            .map(|url| url::Url::from_str(&url))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("not a valid URL")?;
        Ok(Self {
            trans_status,
            acs_url,
            challenge_request: authentication.challenge_request,
            acs_reference_number: authentication.acs_reference_number,
            acs_trans_id: authentication.acs_trans_id,
            three_dsserver_trans_id: authentication.threeds_server_transaction_id,
            acs_signed_content: authentication.acs_signed_content,
        })
    }
}

#[derive(Clone, serde::Deserialize, Debug, serde::Serialize)]
pub struct PostAuthenticationResponse {
    pub trans_status: String,
    pub authentication_value: Option<masking::Secret<String>>,
    pub eci: Option<String>,
}

#[derive(Clone)]
pub struct AuthenticationConnectorData {
    pub connector: ConnectorEnum,
    pub connector_name: enums::AuthenticationConnectors,
}

impl AuthenticationConnectorData {
    pub fn get_connector_by_name(name: &str) -> CustomResult<Self, errors::ApiErrorResponse> {
        let connector_name = enums::AuthenticationConnectors::from_str(name)
            .change_context(errors::ApiErrorResponse::IncorrectConnectorNameGiven)
            .attach_printable_lazy(|| format!("unable to parse connector: {name}"))?;
        let connector = Self::convert_connector(connector_name)?;
        Ok(Self {
            connector,
            connector_name,
        })
    }

    fn convert_connector(
        connector_name: enums::AuthenticationConnectors,
    ) -> CustomResult<ConnectorEnum, errors::ApiErrorResponse> {
        match connector_name {
            enums::AuthenticationConnectors::Threedsecureio => {
                Ok(ConnectorEnum::Old(Box::new(&connector::Threedsecureio)))
            }
            enums::AuthenticationConnectors::Netcetera => {
                Ok(ConnectorEnum::Old(Box::new(&connector::Netcetera)))
            }
            enums::AuthenticationConnectors::Gpayments => {
                Ok(ConnectorEnum::Old(Box::new(connector::Gpayments::new())))
            }
            enums::AuthenticationConnectors::CtpMastercard => {
                Ok(ConnectorEnum::Old(Box::new(&connector::CtpMastercard)))
            }
            enums::AuthenticationConnectors::CtpVisa => Ok(ConnectorEnum::Old(Box::new(
                connector::UnifiedAuthenticationService::new(),
            ))),
            enums::AuthenticationConnectors::UnifiedAuthenticationService => Ok(
                ConnectorEnum::Old(Box::new(connector::UnifiedAuthenticationService::new())),
            ),
            enums::AuthenticationConnectors::Juspaythreedsserver => Ok(ConnectorEnum::Old(
                Box::new(connector::Juspaythreedsserver::new()),
            )),
        }
    }
}


pub fn new(
        state: &SessionState,
        data: ProdIntent,
        theme_id: Option<String>,
        theme_config: EmailThemeConfig,
    ) -> UserResult<Self> {
        Ok(Self {
            recipient_email: domain::UserEmail::from_pii_email(
                state.conf.email.prod_intent_recipient_email.clone(),
            )?,
            settings: state.conf.clone(),
            user_name: data.poc_name.unwrap_or_default(),
            poc_email: data.poc_email.unwrap_or_default(),
            legal_business_name: data.legal_business_name.unwrap_or_default(),
            business_location: data
                .business_location
                .unwrap_or(common_enums::CountryAlpha2::AD)
                .to_string(),
            business_website: data.business_website.unwrap_or_default(),
            theme_id,
            theme_config,
            product_type: data.product_type,
        })
    }


pub async fn find_by_role_id(conn: &PgPooledConn, role_id: &str) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::role_id.eq(role_id.to_owned()),
        )
        .await
    }


pub struct TransactionQuery {
    sale_status: SaleStatus,
    payme_transaction_id: String,
}


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

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


File: crates/external_services/src/grpc_client/dynamic_routing/contract_routing_client.rs

use api_models::routing::{
    ContractBasedRoutingConfig, ContractBasedRoutingConfigBody, ContractBasedTimeScale,
    LabelInformation, RoutableConnectorChoice, RoutableConnectorChoiceWithStatus,
};
use common_utils::{
    ext_traits::OptionExt,
    transformers::{ForeignFrom, ForeignTryFrom},
};
pub use contract_routing::{
    contract_score_calculator_client::ContractScoreCalculatorClient, CalContractScoreConfig,
    CalContractScoreRequest, CalContractScoreResponse, InvalidateContractRequest,
    InvalidateContractResponse, LabelInformation as ProtoLabelInfo, TimeScale,
    UpdateContractRequest, UpdateContractResponse,
};
use error_stack::ResultExt;
use router_env::logger;

use crate::grpc_client::{self, GrpcHeaders};
#[allow(
    missing_docs,
    unused_qualifications,
    clippy::unwrap_used,
    clippy::as_conversions,
    clippy::use_self
)]
pub mod contract_routing {
    tonic::include_proto!("contract_routing");
}
pub use tonic::Code;

use super::{Client, DynamicRoutingError, DynamicRoutingResult};
/// The trait ContractBasedDynamicRouting would have the functions required to support the calculation and updation window
#[async_trait::async_trait]
pub trait ContractBasedDynamicRouting: dyn_clone::DynClone + Send + Sync {
    /// To calculate the contract scores for the list of chosen connectors
    async fn calculate_contract_score(
        &self,
        id: String,
        config: ContractBasedRoutingConfig,
        params: String,
        label_input: Vec<RoutableConnectorChoice>,
        headers: GrpcHeaders,
    ) -> DynamicRoutingResult<CalContractScoreResponse>;
    /// To update the contract scores with the given labels
    async fn update_contracts(
        &self,
        id: String,
        label_info: Vec<LabelInformation>,
        params: String,
        response: Vec<RoutableConnectorChoiceWithStatus>,
        incr_count: u64,
        headers: GrpcHeaders,
    ) -> DynamicRoutingResult<UpdateContractResponse>;
    /// To invalidates the contract scores against the id
    async fn invalidate_contracts(
        &self,
        id: String,
        headers: GrpcHeaders,
    ) -> DynamicRoutingResult<InvalidateContractResponse>;
}

#[async_trait::async_trait]
impl ContractBasedDynamicRouting for ContractScoreCalculatorClient<Client> {
    async fn calculate_contract_score(
        &self,
        id: String,
        config: ContractBasedRoutingConfig,
        params: String,
        label_input: Vec<RoutableConnectorChoice>,
        headers: GrpcHeaders,
    ) -> DynamicRoutingResult<CalContractScoreResponse> {
        let labels = label_input
            .into_iter()
            .map(|conn_choice| conn_choice.to_string())
            .collect::<Vec<_>>();

        let config = config
            .config
            .map(ForeignTryFrom::foreign_try_from)
            .transpose()?;

        let request = grpc_client::create_grpc_request(
            CalContractScoreRequest {
                id,
                params,
                labels,
                config,
            },
            headers,
        );

        let response = self
            .clone()
            .fetch_contract_score(request)
            .await
            .map_err(|err| match err.code() {
                Code::NotFound => DynamicRoutingError::ContractNotFound,
                _ => DynamicRoutingError::ContractBasedRoutingFailure(err.to_string()),
            })?
            .into_inner();

        logger::info!(dynamic_routing_response=?response);

        Ok(response)
    }

    async fn update_contracts(
        &self,
        id: String,
        label_info: Vec<LabelInformation>,
        params: String,
        _response: Vec<RoutableConnectorChoiceWithStatus>,
        incr_count: u64,
        headers: GrpcHeaders,
    ) -> DynamicRoutingResult<UpdateContractResponse> {
        let mut labels_information = label_info
            .into_iter()
            .map(ProtoLabelInfo::foreign_from)
            .collect::<Vec<_>>();

        labels_information
            .iter_mut()
            .for_each(|info| info.current_count += incr_count);

        let request = grpc_client::create_grpc_request(
            UpdateContractRequest {
                id,
                params,
                labels_information,
            },
            headers,
        );

        let response = self
            .clone()
            .update_contract(request)
            .await
            .change_context(DynamicRoutingError::ContractBasedRoutingFailure(
                "Failed to update the contracts".to_string(),
            ))?
            .into_inner();

        logger::info!(dynamic_routing_response=?response);

        Ok(response)
    }
    async fn invalidate_contracts(
        &self,
        id: String,
        headers: GrpcHeaders,
    ) -> DynamicRoutingResult<InvalidateContractResponse> {
        let request = grpc_client::create_grpc_request(InvalidateContractRequest { id }, headers);

        let response = self
            .clone()
            .invalidate_contract(request)
            .await
            .change_context(DynamicRoutingError::ContractBasedRoutingFailure(
                "Failed to invalidate the contracts".to_string(),
            ))?
            .into_inner();
        Ok(response)
    }
}

impl ForeignFrom<ContractBasedTimeScale> for TimeScale {
    fn foreign_from(scale: ContractBasedTimeScale) -> Self {
        Self {
            time_scale: match scale {
                ContractBasedTimeScale::Day => 0,
                _ => 1,
            },
        }
    }
}

impl ForeignTryFrom<ContractBasedRoutingConfigBody> for CalContractScoreConfig {
    type Error = error_stack::Report<DynamicRoutingError>;
    fn foreign_try_from(config: ContractBasedRoutingConfigBody) -> Result<Self, Self::Error> {
        Ok(Self {
            constants: config
                .constants
                .get_required_value("constants")
                .change_context(DynamicRoutingError::MissingRequiredField {
                    field: "constants".to_string(),
                })?,
            time_scale: config.time_scale.clone().map(TimeScale::foreign_from),
        })
    }
}

impl ForeignFrom<LabelInformation> for ProtoLabelInfo {
    fn foreign_from(config: LabelInformation) -> Self {
        Self {
            label: format!(
                "{}:{}",
                config.label.clone(),
                config.mca_id.get_string_repr()
            ),
            target_count: config.target_count,
            target_time: config.target_time,
            current_count: u64::default(),
        }
    }
}


pub struct AuthipayPaymentsRequest {
    request_type: &'static str,
    transaction_amount: Amount,
    payment_method: PaymentMethod,
    // split_shipment: Option<SplitShipment>,
    // incremental_flag: Option<bool>,
}


pub async fn new(
        conf: settings::Settings<SecuredSecret>,
        shut_down_signal: oneshot::Sender<()


pub struct BizEmailProd {
    pub recipient_email: domain::UserEmail,
    pub user_name: Secret<String>,
    pub poc_email: Secret<String>,
    pub legal_business_name: String,
    pub business_location: String,
    pub business_website: String,
    pub settings: std::sync::Arc<configs::Settings>,
    pub theme_id: Option<String>,
    pub theme_config: EmailThemeConfig,
    pub product_type: MerchantProductType,
}


OpenAPI Block Path: components.schemas.AcquirerConfig
{
  "type": "object",
  "description": "Acquirer configuration",
  "required": [
    "acquirer_assigned_merchant_id",
    "merchant_name",
    "network",
    "acquirer_bin",
    "acquirer_fraud_rate"
  ],
  "properties": {
    "acquirer_assigned_merchant_id": {
      "type": "string",
      "description": "The merchant id assigned by the acquirer",
      "example": "M123456789"
    },
    "merchant_name": {
      "type": "string",
      "description": "merchant name",
      "example": "NewAge Retailer"
    },
    "network": {
      "type": "string",
      "description": "Network provider",
      "example": "VISA"
    },
    "acquirer_bin": {
      "type": "string",
      "description": "Acquirer bin",
      "example": "456789"
    },
    "acquirer_ica": {
      "type": "string",
      "description": "Acquirer ica provided by acquirer",
      "example": "401288",
      "nullable": true
    },
    "acquirer_fraud_rate": {
      "type": "string",
      "description": "Fraud rate for the particular acquirer configuration",
      "example": "0.01"
    }
  }
}


pub(crate) fn deserialize_db_op<'de, D>(deserializer: D) -> Result<kv::DBOperation, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let s = serde_json::Value::deserialize(deserializer)?;
    match s {
        serde_json::Value::String(str_val) => {
            serde_json::from_str(&str_val).map_err(serde::de::Error::custom)
        }
        other => Err(serde::de::Error::custom(format!(
            "unexpected data format - expected string got: {other:?}"
        ))),
    }
}


impl PaymentMetricAccumulator for CountAccumulator {
    type MetricOutput = Option<u64>;
    #[inline]
    fn add_metrics_bucket(&mut self, metrics: &PaymentMetricRow) {
        self.count = match (self.count, metrics.count) {
            (None, None) => None,
            (None, i @ Some(_)) | (i @ Some(_), None) => i,
            (Some(a), Some(b)) => Some(a + b),
        }
    }
    #[inline]
    fn collect(self) -> Self::MetricOutput {
        self.count.and_then(|i| u64::try_from(i).ok())
    }
}

impl PaymentMetricAccumulator for for CountAccumulator


    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        handle_vantiv_dispute_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Evidence, SubmitEvidenceRequestData, SubmitEvidenceResponse>
    for Worldpayvantiv
{
    fn get_headers(
        &self,
        req: &SubmitEvidenceRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut headers = vec![
            (
                headers::CONTENT_TYPE.to_string(),
                types::FetchDisputesType::get_content_type(self)
                    .to_string()
                    .into(),
            ),
            (
                headers::ACCEPT.to_string(),
                types::FetchDisputesType::get_content_type(self)
                    .to_string()
                    .into(),
            ),
        ];

        let mut auth_header = self.get_auth_header(&req.connector_auth_type)?;
        headers.append(&mut auth_header);
        Ok(headers)
    }

    fn get_url(
        &self,
        req: &SubmitEvidenceRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}/services/chargebacks/{}",
            connectors.worldpayvantiv.third_base_url.to_owned(),
            req.request.connector_dispute_id
        ))
    }

    fn get_request_body(
        &self,
        req: &SubmitEvidenceRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req_object = worldpayvantiv::ChargebackUpdateRequest::from(req);
        router_env::logger::info!(raw_connector_request=?connector_req_object);
        let connector_req = connector_utils::XmlSerializer::serialize_to_xml_bytes(
            &connector_req_object,
            worldpayvantiv::worldpayvantiv_constants::XML_VERSION,
            Some(worldpayvantiv::worldpayvantiv_constants::XML_ENCODING),
            None,
            None,
        )?;

        Ok(RequestContent::RawBytes(connector_req))
    }

    fn build_request(
        &self,
        req: &SubmitEvidenceRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Put)
                .url(&types::SubmitEvidenceType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::SubmitEvidenceType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::SubmitEvidenceType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &SubmitEvidenceRouterData,
        _event_builder: Option<&mut ConnectorEvent>,
        _res: Response,
    ) -> CustomResult<SubmitEvidenceRouterData, errors::ConnectorError> {
        Ok(SubmitEvidenceRouterData {
            response: Ok(SubmitEvidenceResponse {
                dispute_status: data.request.dispute_status,
                connector_status: None,
            }),
            ..data.clone()
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        handle_vantiv_dispute_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Accept, AcceptDisputeRequestData, AcceptDisputeResponse>
    for Worldpayvantiv
{
    fn get_headers(
        &self,
        req: &AcceptDisputeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut headers = vec![
            (
                headers::CONTENT_TYPE.to_string(),
                types::FetchDisputesType::get_content_type(self)
                    .to_string()
                    .into(),
            ),
            (
                headers::ACCEPT.to_string(),
                types::FetchDisputesType::get_content_type(self)
                    .to_string()
                    .into(),
            ),
        ];

        let mut auth_header = self.get_auth_header(&req.connector_auth_type)?;
        headers.append(&mut auth_header);
        Ok(headers)
    }

    fn get_url(
        &self,
        req: &AcceptDisputeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}/services/chargebacks/{}",
            connectors.worldpayvantiv.third_base_url.to_owned(),
            req.request.connector_dispute_id
        ))
    }

    fn get_request_body(
        &self,
        req: &AcceptDisputeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req_object = worldpayvantiv::ChargebackUpdateRequest::from(req);
        router_env::logger::info!(raw_connector_request=?connector_req_object);
        let connector_req = connector_utils::XmlSerializer::serialize_to_xml_bytes(
            &connector_req_object,
            worldpayvantiv::worldpayvantiv_constants::XML_VERSION,
            Some(worldpayvantiv::worldpayvantiv_constants::XML_ENCODING),
            Some(worldpayvantiv::worldpayvantiv_constants::XML_STANDALONE),
            None,
        )?;

        Ok(RequestContent::RawBytes(connector_req))
    }

    fn build_request(
        &self,
        req: &AcceptDisputeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Put)
                .url(&types::AcceptDisputeType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::AcceptDisputeType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::AcceptDisputeType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &AcceptDisputeRouterData,
        _event_builder: Option<&mut ConnectorEvent>,
        _res: Response,
    ) -> CustomResult<AcceptDisputeRouterData, errors::ConnectorError> {
        Ok(AcceptDisputeRouterData {
            response: Ok(AcceptDisputeResponse {
                dispute_status: data.request.dispute_status,
                connector_status: None,
            }),
            ..data.clone()
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        handle_vantiv_dispute_error_response(res, event_builder)
    }
}

impl UploadFile for Worldpayvantiv {}

impl ConnectorIntegration<Upload, UploadFileRequestData, UploadFileResponse> for Worldpayvantiv {
    fn get_headers(
        &self,
        req: &RouterData<Upload, UploadFileRequestData, UploadFileResponse>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut headers = vec![(
            headers::CONTENT_TYPE.to_string(),
            req.request.file_type.to_string().into(),
        )];

        let mut auth_header = self.get_auth_header(&req.connector_auth_type)?;
        headers.append(&mut auth_header);
        Ok(headers)
    }

    fn get_url(
        &self,
        req: &UploadFileRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let file_type = if req.request.file_type == mime::IMAGE_GIF {
            "gif"
        } else if req.request.file_type == mime::IMAGE_JPEG {
            "jpeg"
        } else if req.request.file_type == mime::IMAGE_PNG {
            "png"
        } else if req.request.file_type == mime::APPLICATION_PDF {
            "pdf"
        } else {
            return Err(errors::ConnectorError::FileValidationFailed {
                reason: "file_type does not match JPEG, JPG, PNG, or PDF format".to_owned(),
            })?;
        };
        let file_name = req.request.file_key.split('/').next_back().ok_or(
            errors::ConnectorError::RequestEncodingFailedWithReason(
                "Failed fetching file_id from file_key".to_string(),
            ),
        )?;
        Ok(format!(
            "{}/services/chargebacks/upload/{}/{file_name}.{file_type}",
            connectors.worldpayvantiv.third_base_url.to_owned(),
            req.request.connector_dispute_id,
        ))
    }

    fn get_request_body(
        &self,
        req: &UploadFileRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Ok(RequestContent::RawBytes(req.request.file.clone()))
    }

    fn build_request(
        &self,
        req: &UploadFileRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::UploadFileType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::UploadFileType::get_headers(self, req, connectors)?)
                .set_body(types::UploadFileType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &UploadFileRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<
        RouterData<Upload, UploadFileRequestData, UploadFileResponse>,
        errors::ConnectorError,
    > {
        let response: worldpayvantiv::ChargebackDocumentUploadResponse =
            connector_utils::deserialize_xml_to_struct(&res.response)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        handle_vantiv_dispute_error_response(res, event_builder)
    }
}

impl RetrieveFile for Worldpayvantiv {}

impl ConnectorIntegration<Retrieve, RetrieveFileRequestData, RetrieveFileResponse>
    for Worldpayvantiv
{
    fn get_headers(
        &self,
        req: &RouterData<Retrieve, RetrieveFileRequestData, RetrieveFileResponse>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.get_auth_header(&req.connector_auth_type)
    }

    fn get_url(
        &self,
        req: &RetrieveFileRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_dispute_id = req.request.connector_dispute_id.clone().ok_or(
            errors::ConnectorError::MissingRequiredField {
                field_name: "dispute_id",
            },
        )?;
        Ok(format!(
            "{}/services/chargebacks/retrieve/{connector_dispute_id}/{}",
            connectors.worldpayvantiv.third_base_url.to_owned(),
            req.request.provider_file_id,
        ))
    }

    fn build_request(
        &self,
        req: &RetrieveFileRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::RetrieveFileType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::RetrieveFileType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RetrieveFileRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RetrieveFileRouterData, errors::ConnectorError> {
        let response: Result<worldpayvantiv::ChargebackDocumentUploadResponse, _> =
            connector_utils::deserialize_xml_to_struct(&res.response);
        match response {
            Ok(response) => {
                event_builder.map(|i| i.set_response_body(&response));
                router_env::logger::info!(connector_response=?response);
                RouterData::try_from(ResponseRouterData {
                    response,
                    data: data.clone(),
                    http_code: res.status_code,
                })
            }
            Err(_) => {
                event_builder.map(|event| event.set_response_body(&serde_json::json!({"connector_response_type": "file", "status_code": res.status_code})));
                router_env::logger::info!(connector_response_type=?"file");
                let response = res.response;
                Ok(RetrieveFileRouterData {
                    response: Ok(RetrieveFileResponse {
                        file_data: response.to_vec(),
                    }),
                    ..data.clone()
                })
            }
        }
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        handle_vantiv_dispute_error_response(res, event_builder)
    }
}

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Worldpayvantiv {
    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))
    }
}

fn handle_vantiv_json_error_response(
    res: Response,
    event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
    let response: Result<worldpayvantiv::VantivSyncErrorResponse, _> = res
        .response
        .parse_struct("VantivSyncErrorResponse")
        .change_context(errors::ConnectorError::ResponseDeserializationFailed);

    match response {
        Ok(response_data) => {
            event_builder.map(|i| i.set_response_body(&response_data));
            router_env::logger::info!(connector_response=?response_data);
            let error_reason = response_data.error_messages.join(" & ");

            Ok(ErrorResponse {
                status_code: res.status_code,
                code: NO_ERROR_CODE.to_string(),
                message: error_reason.clone(),
                reason: Some(error_reason.clone()),
                attempt_status: None,
                connector_transaction_id: None,
                network_decline_code: None,
                network_advice_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        }
        Err(error_msg) => {
            event_builder.map(|event| event.set_error(serde_json::json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
            router_env::logger::error!(deserialization_error =? error_msg);
            connector_utils::handle_json_response_deserialization_failure(res, "worldpayvantiv")
        }
    }
}

fn handle_vantiv_dispute_error_response(
    res: Response,
    event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
    let response: Result<worldpayvantiv::VantivDisputeErrorResponse, _> =
        connector_utils::deserialize_xml_to_struct::<worldpayvantiv::VantivDisputeErrorResponse>(
            &res.response,
        );

    match response {
        Ok(response_data) => {
            event_builder.map(|i| i.set_response_body(&response_data));
            router_env::logger::info!(connector_response=?response_data);
            let error_reason = response_data
                .errors
                .iter()
                .map(|error_info| error_info.error.clone())
                .collect::<Vec<String>>()
                .join(" & ");

            Ok(ErrorResponse {
                status_code: res.status_code,
                code: NO_ERROR_CODE.to_string(),
                message: error_reason.clone(),
                reason: Some(error_reason.clone()),
                attempt_status: None,
                connector_transaction_id: None,
                network_decline_code: None,
                network_advice_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        }
        Err(error_msg) => {
            event_builder.map(|event| event.set_error(serde_json::json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
            router_env::logger::error!(deserialization_error =? error_msg);
            connector_utils::handle_json_response_deserialization_failure(res, "worldpayvantiv")
        }
    }
}

#[async_trait::async_trait]
impl FileUpload for Worldpayvantiv {
    fn validate_file_upload(
        &self,
        purpose: FilePurpose,
        file_size: i32,
        file_type: mime::Mime,
    ) -> CustomResult<(), errors::ConnectorError> {
        match purpose {
            FilePurpose::DisputeEvidence => {
                let supported_file_types = [
                    "image/gif",
                    "image/jpeg",
                    "image/jpg",
                    "application/pdf",
                    "image/png",
                    "image/tiff",
                ];
                if file_size > 2000000 {
                    Err(errors::ConnectorError::FileValidationFailed {
                        reason: "file_size exceeded the max file size of 2MB".to_owned(),
                    })?
                }
                if !supported_file_types.contains(&file_type.to_string().as_str()) {
                    Err(errors::ConnectorError::FileValidationFailed {
                        reason: "file_type does not match JPEG, JPG, PNG, or PDF format".to_owned(),
                    })?
                }
            }
        }
        Ok(())
    }
}

static WORLDPAYVANTIV_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(|| {
        let supported_capture_methods = vec![
            common_enums::CaptureMethod::Automatic,
            common_enums::CaptureMethod::Manual,
            common_enums::CaptureMethod::SequentialAutomatic,
        ];

        let supported_card_network = vec![
            common_enums::CardNetwork::AmericanExpress,
            common_enums::CardNetwork::DinersClub,
            common_enums::CardNetwork::JCB,
            common_enums::CardNetwork::Mastercard,
            common_enums::CardNetwork::Visa,
            common_enums::CardNetwork::Discover,
        ];

        let mut worldpayvantiv_supported_payment_methods = SupportedPaymentMethods::new();

        worldpayvantiv_supported_payment_methods.add(
            common_enums::PaymentMethod::Card,
            common_enums::PaymentMethodType::Credit,
            PaymentMethodDetails {
                mandates: common_enums::FeatureStatus::Supported,
                refunds: common_enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::NotSupported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network.clone(),
                        }
                    }),
                ),
            },
        );

        worldpayvantiv_supported_payment_methods.add(
            common_enums::PaymentMethod::Card,
            common_enums::PaymentMethodType::Debit,
            PaymentMethodDetails {
                mandates: common_enums::FeatureStatus::Supported,
                refunds: common_enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::NotSupported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network.clone(),
                        }
                    }),
                ),
            },
        );

        #[cfg(feature = "v2")]
        worldpayvantiv_supported_payment_methods.add(
            common_enums::PaymentMethod::Card,
            common_enums::PaymentMethodType::Card,
            PaymentMethodDetails {
                mandates: common_enums::FeatureStatus::Supported,
                refunds: common_enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::NotSupported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network.clone(),
                        }
                    }),
                ),
            },
        );

        worldpayvantiv_supported_payment_methods.add(
            common_enums::PaymentMethod::Wallet,
            common_enums::PaymentMethodType::ApplePay,
            PaymentMethodDetails {
                mandates: common_enums::FeatureStatus::Supported,
                refunds: common_enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        worldpayvantiv_supported_payment_methods.add(
            common_enums::PaymentMethod::Wallet,
            common_enums::PaymentMethodType::GooglePay,
            PaymentMethodDetails {
                mandates: common_enums::FeatureStatus::Supported,
                refunds: common_enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        worldpayvantiv_supported_payment_methods
    });

static WORLDPAYVANTIV_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Worldpay Vantiv",
    description: "Worldpay Vantiv, also known as the Worldpay CNP API, is a robust XML-based interface used to process online (card-not-present) transactions such as e-commerce purchases, subscription billing, and digital payments",
    connector_type: common_enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: common_enums::ConnectorIntegrationStatus::Sandbox,
};

static WORLDPAYVANTIV_SUPPORTED_WEBHOOK_FLOWS: [common_enums::EventClass; 0] = [];

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

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

    fn get_supported_webhook_flows(&self) -> Option<&'static [common_enums::EventClass]> {
        Some(&WORLDPAYVANTIV_SUPPORTED_WEBHOOK_FLOWS)
    }
    #[cfg(feature = "v1")]
    fn generate_connector_request_reference_id(
        &self,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
        is_config_enabled_to_send_payment_id_as_connector_request_id: bool,
    ) -> String {
        if is_config_enabled_to_send_payment_id_as_connector_request_id
            && payment_intent.is_payment_id_from_merchant.unwrap_or(false)
        {
            payment_attempt.payment_id.get_string_repr().to_owned()
        } else {
            let max_payment_reference_id_length =
                worldpayvantiv::worldpayvantiv_constants::MAX_PAYMENT_REFERENCE_ID_LENGTH;
            nanoid::nanoid!(max_payment_reference_id_length)
        }
    }
    #[cfg(feature = "v2")]
    fn generate_connector_request_reference_id(
        &self,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> String {
        if payment_intent.is_payment_id_from_merchant.unwrap_or(false) {
            payment_attempt.payment_id.get_string_repr().to_owned()
        } else {
            connector_utils::generate_12_digit_number().to_string()
        }
    }
}


pub struct PaymentMethodSelectedCount;


pub struct MerchantConnectorId {
    #[schema(value_type = String)]
    pub id: id_type::MerchantConnectorAccountId,
}