// File: crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs
// Module: hyperswitch_connectors
// Public functions: 10
// Public structs: 47

use common_enums::{enums, CaptureMethod, FutureUsage, PaymentChannel};
use common_types::{
    payments::{
        ApplePayPaymentData, ApplePayPredecryptData, GPayPredecryptData, GpayTokenizationData,
    },
    primitive_wrappers,
};
use common_utils::{
    crypto::{self, GenerateDigest},
    date_time,
    ext_traits::Encode,
    fp_utils,
    id_type::CustomerId,
    pii::{self, Email, IpAddress},
    request::Method,
    types::{FloatMajorUnit, MinorUnit, StringMajorUnit, StringMajorUnitForConnector},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    address::{Address, AddressDetails},
    payment_method_data::{
        self, ApplePayWalletData, BankRedirectData, CardDetailsForNetworkTransactionId,
        GooglePayWalletData, PayLaterData, PaymentMethodData, WalletData,
    },
    router_data::{
        AdditionalPaymentMethodConnectorResponse, ConnectorAuthType, ConnectorResponseData,
        ErrorResponse, L2L3Data, PaymentMethodToken, RouterData,
    },
    router_flow_types::{
        refunds::{Execute, RSync},
        Authorize, Capture, CompleteAuthorize, PSync, PostCaptureVoid, SetupMandate, Void,
    },
    router_request_types::{
        authentication::MessageExtensionAttribute, AuthenticationData, BrowserInformation,
        PaymentsAuthorizeData, PaymentsPreProcessingData, ResponseId, SetupMandateRequestData,
    },
    router_response_types::{
        MandateReference, PaymentsResponseData, RedirectForm, RefundsResponseData,
    },
    types,
};
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::{
    router_flow_types::payouts::PoFulfill, router_response_types::PayoutsResponseData,
};
use hyperswitch_interfaces::{
    consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
    errors::{self},
};
use masking::{ExposeInterface, PeekInterface, Secret};
use serde::{Deserialize, Serialize};
use url::Url;

#[cfg(feature = "payouts")]
use crate::{types::PayoutsResponseRouterData, utils::PayoutsData as _};
use crate::{
    types::{
        PaymentsPreprocessingResponseRouterData, RefundsResponseRouterData, ResponseRouterData,
    },
    utils::{
        self, convert_amount, missing_field_err, AddressData, AddressDetailsData,
        BrowserInformationData, CardData, ForeignTryFrom, PaymentsAuthorizeRequestData,
        PaymentsCancelRequestData, PaymentsPreProcessingRequestData,
        PaymentsSetupMandateRequestData, RouterData as _,
    },
};

pub static NUVEI_AMOUNT_CONVERTOR: &StringMajorUnitForConnector = &StringMajorUnitForConnector;

fn to_boolean(string: String) -> bool {
    let str = string.as_str();
    match str {
        "true" => true,
        "false" => false,
        "yes" => true,
        "no" => false,
        _ => false,
    }
}

// The dimensions of the challenge window for full screen.
const CHALLENGE_WINDOW_SIZE: &str = "05";
// The challenge preference for the challenge flow.
const CHALLENGE_PREFERENCE: &str = "01";

trait NuveiAuthorizePreprocessingCommon {
    fn get_browser_info(&self) -> Option<BrowserInformation>;
    fn get_related_transaction_id(&self) -> Option<String>;
    fn get_complete_authorize_url(&self) -> Option<String>;
    fn get_is_moto(&self) -> Option<bool>;
    fn get_ntid(&self) -> Option<String>;
    fn get_connector_mandate_id(&self) -> Option<String>;
    fn get_return_url_required(
        &self,
    ) -> Result<String, error_stack::Report<errors::ConnectorError>>;
    fn get_capture_method(&self) -> Option<CaptureMethod>;
    fn get_minor_amount_required(
        &self,
    ) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>>;
    fn get_customer_id_required(&self) -> Option<CustomerId>;
    fn get_email_required(&self) -> Result<Email, error_stack::Report<errors::ConnectorError>>;
    fn get_currency_required(
        &self,
    ) -> Result<enums::Currency, error_stack::Report<errors::ConnectorError>>;
    fn get_payment_method_data_required(
        &self,
    ) -> Result<PaymentMethodData, error_stack::Report<errors::ConnectorError>>;
    fn get_is_partial_approval(&self) -> Option<PartialApprovalFlag>;
    fn is_customer_initiated_mandate_payment(&self) -> bool;
    fn get_auth_data(
        &self,
    ) -> Result<Option<AuthenticationData>, error_stack::Report<errors::ConnectorError>> {
        Ok(None)
    }
    fn get_is_stored_credential(&self) -> Option<StoredCredentialMode>;
}

impl NuveiAuthorizePreprocessingCommon for SetupMandateRequestData {
    fn get_browser_info(&self) -> Option<BrowserInformation> {
        self.browser_info.clone()
    }

    fn get_related_transaction_id(&self) -> Option<String> {
        self.related_transaction_id.clone()
    }
    fn get_is_moto(&self) -> Option<bool> {
        match self.payment_channel {
            Some(PaymentChannel::MailOrder) | Some(PaymentChannel::TelephoneOrder) => Some(true),
            _ => None,
        }
    }

    fn get_customer_id_required(&self) -> Option<CustomerId> {
        self.customer_id.clone()
    }

    fn get_complete_authorize_url(&self) -> Option<String> {
        self.complete_authorize_url.clone()
    }

    fn get_connector_mandate_id(&self) -> Option<String> {
        self.mandate_id.as_ref().and_then(|mandate_ids| {
            mandate_ids.mandate_reference_id.as_ref().and_then(
                |mandate_ref_id| match mandate_ref_id {
                    api_models::payments::MandateReferenceId::ConnectorMandateId(id) => {
                        id.get_connector_mandate_id()
                    }
                    _ => None,
                },
            )
        })
    }

    fn get_return_url_required(
        &self,
    ) -> Result<String, error_stack::Report<errors::ConnectorError>> {
        self.get_router_return_url()
    }

    fn get_capture_method(&self) -> Option<CaptureMethod> {
        self.capture_method
    }

    fn get_currency_required(
        &self,
    ) -> Result<enums::Currency, error_stack::Report<errors::ConnectorError>> {
        Ok(self.currency)
    }
    fn get_payment_method_data_required(
        &self,
    ) -> Result<PaymentMethodData, error_stack::Report<errors::ConnectorError>> {
        Ok(self.payment_method_data.clone())
    }

    fn get_minor_amount_required(
        &self,
    ) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>> {
        self.minor_amount
            .ok_or_else(missing_field_err("minor_amount"))
    }

    fn get_is_partial_approval(&self) -> Option<PartialApprovalFlag> {
        self.enable_partial_authorization
            .map(PartialApprovalFlag::from)
    }

    fn get_email_required(&self) -> Result<Email, error_stack::Report<errors::ConnectorError>> {
        self.email.clone().ok_or_else(missing_field_err("email"))
    }
    fn is_customer_initiated_mandate_payment(&self) -> bool {
        (self.customer_acceptance.is_some() || self.setup_mandate_details.is_some())
            && self.setup_future_usage == Some(FutureUsage::OffSession)
    }
    fn get_ntid(&self) -> Option<String> {
        None
    }

    fn get_is_stored_credential(&self) -> Option<StoredCredentialMode> {
        StoredCredentialMode::get_optional_stored_credential(self.is_stored_credential)
    }
}

impl NuveiAuthorizePreprocessingCommon for PaymentsAuthorizeData {
    fn get_browser_info(&self) -> Option<BrowserInformation> {
        self.browser_info.clone()
    }
    fn get_ntid(&self) -> Option<String> {
        self.get_optional_network_transaction_id()
    }
    fn get_related_transaction_id(&self) -> Option<String> {
        self.related_transaction_id.clone()
    }
    fn get_is_moto(&self) -> Option<bool> {
        match self.payment_channel {
            Some(PaymentChannel::MailOrder) | Some(PaymentChannel::TelephoneOrder) => Some(true),
            _ => None,
        }
    }
    fn get_auth_data(
        &self,
    ) -> Result<Option<AuthenticationData>, error_stack::Report<errors::ConnectorError>> {
        Ok(self.authentication_data.clone())
    }
    fn get_customer_id_required(&self) -> Option<CustomerId> {
        self.customer_id.clone()
    }

    fn get_connector_mandate_id(&self) -> Option<String> {
        self.connector_mandate_id().clone()
    }

    fn get_return_url_required(
        &self,
    ) -> Result<String, error_stack::Report<errors::ConnectorError>> {
        self.get_router_return_url()
    }

    fn get_capture_method(&self) -> Option<CaptureMethod> {
        self.capture_method
    }

    fn get_complete_authorize_url(&self) -> Option<String> {
        self.complete_authorize_url.clone()
    }

    fn get_minor_amount_required(
        &self,
    ) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>> {
        Ok(self.minor_amount)
    }

    fn get_currency_required(
        &self,
    ) -> Result<enums::Currency, error_stack::Report<errors::ConnectorError>> {
        Ok(self.currency)
    }
    fn get_payment_method_data_required(
        &self,
    ) -> Result<PaymentMethodData, error_stack::Report<errors::ConnectorError>> {
        Ok(self.payment_method_data.clone())
    }

    fn get_email_required(&self) -> Result<Email, error_stack::Report<errors::ConnectorError>> {
        self.get_email()
    }
    fn is_customer_initiated_mandate_payment(&self) -> bool {
        (self.customer_acceptance.is_some() || self.setup_mandate_details.is_some())
            && self.setup_future_usage == Some(FutureUsage::OffSession)
    }
    fn get_is_partial_approval(&self) -> Option<PartialApprovalFlag> {
        self.enable_partial_authorization
            .map(PartialApprovalFlag::from)
    }

    fn get_is_stored_credential(&self) -> Option<StoredCredentialMode> {
        StoredCredentialMode::get_optional_stored_credential(self.is_stored_credential)
    }
}

impl NuveiAuthorizePreprocessingCommon for PaymentsPreProcessingData {
    fn get_browser_info(&self) -> Option<BrowserInformation> {
        self.browser_info.clone()
    }

    fn get_related_transaction_id(&self) -> Option<String> {
        self.related_transaction_id.clone()
    }

    fn get_is_moto(&self) -> Option<bool> {
        None
    }

    fn get_customer_id_required(&self) -> Option<CustomerId> {
        None
    }
    fn get_email_required(&self) -> Result<Email, error_stack::Report<errors::ConnectorError>> {
        self.get_email()
    }
    fn is_customer_initiated_mandate_payment(&self) -> bool {
        (self.customer_acceptance.is_some() || self.setup_mandate_details.is_some())
            && self.setup_future_usage == Some(FutureUsage::OffSession)
    }

    fn get_connector_mandate_id(&self) -> Option<String> {
        self.connector_mandate_id()
    }

    fn get_return_url_required(
        &self,
    ) -> Result<String, error_stack::Report<errors::ConnectorError>> {
        self.get_router_return_url()
    }

    fn get_capture_method(&self) -> Option<CaptureMethod> {
        self.capture_method
    }

    fn get_complete_authorize_url(&self) -> Option<String> {
        self.complete_authorize_url.clone()
    }

    fn get_minor_amount_required(
        &self,
    ) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>> {
        self.get_minor_amount()
    }

    fn get_currency_required(
        &self,
    ) -> Result<enums::Currency, error_stack::Report<errors::ConnectorError>> {
        self.get_currency()
    }
    fn get_payment_method_data_required(
        &self,
    ) -> Result<PaymentMethodData, error_stack::Report<errors::ConnectorError>> {
        self.payment_method_data.clone().ok_or(
            errors::ConnectorError::MissingRequiredField {
                field_name: "payment_method_data",
            }
            .into(),
        )
    }

    fn get_is_partial_approval(&self) -> Option<PartialApprovalFlag> {
        None
    }

    fn get_ntid(&self) -> Option<String> {
        None
    }

    fn get_is_stored_credential(&self) -> Option<StoredCredentialMode> {
        StoredCredentialMode::get_optional_stored_credential(self.is_stored_credential)
    }
}

#[derive(Debug, Serialize, Default, Deserialize)]
pub struct NuveiMeta {
    pub session_token: Secret<String>,
}

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

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NuveiSessionRequest {
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub client_request_id: String,
    pub time_stamp: date_time::DateTime<date_time::YYYYMMDDHHmmss>,
    pub checksum: Secret<String>,
}

#[derive(Debug, Serialize, Default, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NuveiSessionResponse {
    pub session_token: Secret<String>,
    pub internal_request_id: i64,
    pub status: String,
    pub err_code: i64,
    pub reason: String,
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub version: String,
    pub client_request_id: String,
}

#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct NuveiAmountDetails {
    pub total_tax: Option<StringMajorUnit>,
    pub total_shipping: Option<StringMajorUnit>,
    pub total_handling: Option<StringMajorUnit>,
    pub total_discount: Option<StringMajorUnit>,
}

#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(rename_all = "snake_case")]
pub enum NuveiItemType {
    #[default]
    Physical,
    Discount,
    #[serde(rename = "Shipping_fee")]
    ShippingFee,
    Digital,
    #[serde(rename = "Gift_card")]
    GiftCard,
    #[serde(rename = "Store_credit")]
    StoreCredit,
    Surcharge,
    #[serde(rename = "Sales_tax")]
    SalesTax,
}
impl From<Option<enums::ProductType>> for NuveiItemType {
    fn from(value: Option<enums::ProductType>) -> Self {
        match value {
            Some(enums::ProductType::Digital) => Self::Digital,
            Some(enums::ProductType::Physical) => Self::Physical,
            Some(enums::ProductType::Ride)
            | Some(enums::ProductType::Travel)
            | Some(enums::ProductType::Accommodation) => Self::ShippingFee,
            _ => Self::Physical,
        }
    }
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct NuveiItem {
    pub name: String,
    #[serde(rename = "type")]
    pub item_type: NuveiItemType,
    pub price: StringMajorUnit,
    pub quantity: String,
    pub group_id: Option<String>,
    pub discount: Option<StringMajorUnit>,
    pub discount_rate: Option<String>,
    pub shipping: Option<StringMajorUnit>,
    pub shipping_tax: Option<StringMajorUnit>,
    pub shipping_tax_rate: Option<String>,
    pub tax: Option<StringMajorUnit>,
    pub tax_rate: Option<String>,
    pub image_url: Option<String>,
    pub product_url: Option<String>,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct NuveiPaymentsRequest {
    pub time_stamp: String,
    pub session_token: Secret<String>,
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub client_request_id: Secret<String>,
    pub amount: StringMajorUnit,
    pub currency: enums::Currency,
    /// This ID uniquely identifies your consumer/user in your system.
    pub user_token_id: Option<CustomerId>,
    //unique transaction id
    pub client_unique_id: String,
    pub transaction_type: TransactionType,
    pub is_rebilling: Option<String>,
    pub payment_option: PaymentOption,
    pub is_moto: Option<bool>,
    pub device_details: DeviceDetails,
    pub checksum: Secret<String>,
    pub billing_address: Option<BillingAddress>,
    pub shipping_address: Option<ShippingAddress>,
    pub related_transaction_id: Option<String>,
    pub url_details: Option<UrlDetails>,
    pub amount_details: Option<NuveiAmountDetails>,
    pub items: Option<Vec<NuveiItem>>,
    pub is_partial_approval: Option<PartialApprovalFlag>,
    pub external_scheme_details: Option<ExternalSchemeDetails>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum PartialApprovalFlag {
    #[serde(rename = "1")]
    Enabled,
    #[serde(rename = "0")]
    Disabled,
}

impl From<primitive_wrappers::EnablePartialAuthorizationBool> for PartialApprovalFlag {
    fn from(value: primitive_wrappers::EnablePartialAuthorizationBool) -> Self {
        if value.is_true() {
            Self::Enabled
        } else {
            Self::Disabled
        }
    }
}

#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct UrlDetails {
    pub success_url: String,
    pub failure_url: String,
    pub pending_url: String,
}

#[derive(Debug, Serialize, Default)]
pub struct NuveiInitPaymentRequest {
    pub session_token: Secret<String>,
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub client_request_id: String,
    pub amount: StringMajorUnit,
    pub currency: String,
    pub payment_option: PaymentOption,
    pub checksum: Secret<String>,
}

/// Handles payment request for capture, void and refund flows
#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct NuveiPaymentFlowRequest {
    pub time_stamp: String,
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub client_request_id: String,
    pub amount: StringMajorUnit,
    pub currency: enums::Currency,
    pub related_transaction_id: Option<String>,
    pub checksum: Secret<String>,
}

#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct NuveiPaymentSyncRequest {
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub time_stamp: String,
    pub checksum: Secret<String>,
    pub transaction_id: String,
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub enum TransactionType {
    Auth,
    #[default]
    Sale,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentOption {
    pub card: Option<Card>,
    pub redirect_url: Option<Url>,
    pub user_payment_option_id: Option<String>,
    pub alternative_payment_method: Option<AlternativePaymentMethod>,
    pub billing_address: Option<BillingAddress>,
    pub shipping_address: Option<BillingAddress>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NuveiBIC {
    #[serde(rename = "ABNANL2A")]
    Abnamro,
    #[serde(rename = "ASNBNL21")]
    ASNBank,
    #[serde(rename = "BUNQNL2A")]
    Bunq,
    #[serde(rename = "INGBNL2A")]
    Ing,
    #[serde(rename = "KNABNL2H")]
    Knab,
    #[serde(rename = "RABONL2U")]
    Rabobank,
    #[serde(rename = "RBRBNL21")]
    RegioBank,
    #[serde(rename = "SNSBNL2A")]
    SNSBank,
    #[serde(rename = "TRIONL2U")]
    TriodosBank,
    #[serde(rename = "FVLBNL22")]
    VanLanschotBankiers,
    #[serde(rename = "MOYONL21")]
    Moneyou,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum NuveiCardType {
    Visa,
    MasterCard,
    AmericanExpress,
    Discover,
    DinersClub,
    Interac,
    JCB,
    UnionPay,
    CartesBancaires,
}

impl TryFrom<common_enums::CardNetwork> for NuveiCardType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(card_network: common_enums::CardNetwork) -> Result<Self, Self::Error> {
        match card_network {
            common_enums::CardNetwork::Visa => Ok(Self::Visa),
            common_enums::CardNetwork::Mastercard => Ok(Self::MasterCard),
            common_enums::CardNetwork::AmericanExpress => Ok(Self::AmericanExpress),
            common_enums::CardNetwork::Discover => Ok(Self::Discover),
            common_enums::CardNetwork::DinersClub => Ok(Self::DinersClub),
            common_enums::CardNetwork::JCB => Ok(Self::JCB),
            common_enums::CardNetwork::UnionPay => Ok(Self::UnionPay),
            common_enums::CardNetwork::CartesBancaires => Ok(Self::CartesBancaires),
            common_enums::CardNetwork::Interac => Ok(Self::Interac),
            _ => Err(errors::ConnectorError::NotSupported {
                message: "Card network".to_string(),
                connector: "nuvei",
            }
            .into()),
        }
    }
}

impl TryFrom<&utils::CardIssuer> for NuveiCardType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(card_issuer: &utils::CardIssuer) -> Result<Self, Self::Error> {
        match card_issuer {
            utils::CardIssuer::Visa => Ok(Self::Visa),
            utils::CardIssuer::Master => Ok(Self::MasterCard),
            utils::CardIssuer::AmericanExpress => Ok(Self::AmericanExpress),
            utils::CardIssuer::Discover => Ok(Self::Discover),
            utils::CardIssuer::DinersClub => Ok(Self::DinersClub),
            utils::CardIssuer::JCB => Ok(Self::JCB),
            utils::CardIssuer::CartesBancaires => Ok(Self::CartesBancaires),
            &utils::CardIssuer::UnionPay => Ok(Self::UnionPay),
            _ => Err(errors::ConnectorError::NotSupported {
                message: "Card network".to_string(),
                connector: "nuvei",
            }
            .into()),
        }
    }
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AlternativePaymentMethod {
    pub payment_method: AlternativePaymentMethodType,
    #[serde(rename = "BIC")]
    pub bank_id: Option<NuveiBIC>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AlternativePaymentMethodType {
    #[default]
    #[serde(rename = "apmgw_expresscheckout")]
    Expresscheckout,
    #[serde(rename = "apmgw_Giropay")]
    Giropay,
    #[serde(rename = "apmgw_Sofort")]
    Sofort,
    #[serde(rename = "apmgw_iDeal")]
    Ideal,
    #[serde(rename = "apmgw_EPS")]
    Eps,
    #[serde(rename = "apmgw_Afterpay")]
    AfterPay,
    #[serde(rename = "apmgw_Klarna")]
    Klarna,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BillingAddress {
    pub email: Email,
    pub first_name: Option<Secret<String>>,
    pub last_name: Option<Secret<String>>,
    pub country: api_models::enums::CountryAlpha2,
    pub phone: Option<Secret<String>>,
    pub city: Option<Secret<String>>,
    pub address: Option<Secret<String>>,
    pub street_number: Option<Secret<String>>,
    pub zip: Option<Secret<String>>,
    pub state: Option<Secret<String>>,
    pub cell: Option<Secret<String>>,
    pub address_match: Option<Secret<String>>,
    pub address_line2: Option<Secret<String>>,
    pub address_line3: Option<Secret<String>>,
    pub home_phone: Option<Secret<String>>,
    pub work_phone: Option<Secret<String>>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ShippingAddress {
    pub salutation: Option<Secret<String>>,
    pub first_name: Option<Secret<String>>,
    pub last_name: Option<Secret<String>>,
    pub address: Option<Secret<String>>,
    pub cell: Option<Secret<String>>,
    pub phone: Option<Secret<String>>,
    pub zip: Option<Secret<String>>,
    pub city: Option<Secret<String>>,
    pub country: api_models::enums::CountryAlpha2,
    pub state: Option<Secret<String>>,
    pub email: Email,
    pub county: Option<Secret<String>>,
    pub address_line2: Option<Secret<String>>,
    pub address_line3: Option<Secret<String>>,
    pub street_number: Option<Secret<String>>,
    pub company_name: Option<Secret<String>>,
    pub care_of: Option<Secret<String>>,
}

impl From<&Address> for BillingAddress {
    fn from(address: &Address) -> Self {
        let address_details = address.address.as_ref();
        Self {
            email: address.email.clone().unwrap_or_default(),
            first_name: address.get_optional_first_name(),
            last_name: address_details.and_then(|address| address.get_optional_last_name()),
            country: address_details
                .and_then(|address| address.get_optional_country())
                .unwrap_or_default(),
            phone: address
                .phone
                .as_ref()
                .and_then(|phone| phone.number.clone()),
            city: address_details
                .and_then(|address| address.get_optional_city().map(|city| city.into())),
            address: address_details.and_then(|address| address.get_optional_line1()),
            street_number: None,
            zip: address_details.and_then(|details| details.get_optional_zip()),
            state: address_details.and_then(|details| details.to_state_code_as_optional().ok()?),
            cell: None,
            address_match: None,
            address_line2: address_details.and_then(|address| address.get_optional_line2()),
            address_line3: address_details.and_then(|address| address.get_optional_line3()),
            home_phone: None,
            work_phone: None,
        }
    }
}

impl From<&Address> for ShippingAddress {
    fn from(address: &Address) -> Self {
        let address_details = address.address.as_ref();

        Self {
            email: address.email.clone().unwrap_or_default(),
            first_name: address_details.and_then(|details| details.get_optional_first_name()),
            last_name: address_details.and_then(|details| details.get_optional_last_name()),
            country: address_details
                .and_then(|details| details.get_optional_country())
                .unwrap_or_default(),
            phone: address
                .phone
                .as_ref()
                .and_then(|phone| phone.number.clone()),
            city: address_details
                .and_then(|details| details.get_optional_city().map(|city| city.into())),
            address: address_details.and_then(|details| details.get_optional_line1()),
            street_number: None,
            zip: address_details.and_then(|details| details.get_optional_zip()),
            state: None,
            cell: None,
            address_line2: address_details.and_then(|details| details.get_optional_line2()),
            address_line3: address_details.and_then(|details| details.get_optional_line3()),
            county: None,
            company_name: None,
            care_of: None,
            salutation: None,
        }
    }
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Card {
    pub card_number: Option<cards::CardNumber>,
    pub card_holder_name: Option<Secret<String>>,
    pub expiration_month: Option<Secret<String>>,
    pub expiration_year: Option<Secret<String>>,
    #[serde(rename = "CVV")]
    pub cvv: Option<Secret<String>>,
    pub three_d: Option<ThreeD>,
    pub cc_card_number: Option<Secret<String>>,
    pub bin: Option<Secret<String>>,
    pub last4_digits: Option<Secret<String>>,
    pub cc_exp_month: Option<Secret<String>>,
    pub cc_exp_year: Option<Secret<String>>,
    pub acquirer_id: Option<Secret<String>>,
    pub cvv2_reply: Option<String>,
    pub avs_code: Option<String>,
    pub card_type: Option<String>,
    pub brand: Option<String>,
    pub issuer_bank_name: Option<String>,
    pub issuer_country: Option<String>,
    pub is_prepaid: Option<String>,
    pub external_token: Option<ExternalToken>,
    pub stored_credentials: Option<StoredCredentialMode>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StoredCredentialMode {
    pub stored_credentials_mode: Option<StoredCredentialModeType>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum StoredCredentialModeType {
    #[serde(rename = "0")]
    First,
    #[serde(rename = "1")]
    Used,
}

impl StoredCredentialMode {
    pub fn get_optional_stored_credential(is_stored_credential: Option<bool>) -> Option<Self> {
        is_stored_credential.and_then(|is_stored_credential| {
            if is_stored_credential {
                Some(Self {
                    stored_credentials_mode: Some(StoredCredentialModeType::Used),
                })
            } else {
                None
            }
        })
    }
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalToken {
    pub external_token_provider: ExternalTokenProvider,
    pub mobile_token: Option<Secret<String>>,
    pub cryptogram: Option<Secret<String>>,
    pub eci_provider: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum ExternalTokenProvider {
    #[default]
    GooglePay,
    ApplePay,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalMpi {
    pub eci: Option<String>,
    pub cavv: Secret<String>,
    #[serde(rename = "dsTransID")]
    pub ds_trans_id: Option<String>,
    pub challenge_preference: Option<String>,
    pub exemption_request_reason: Option<String>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreeD {
    pub method_completion_ind: Option<MethodCompletion>,
    pub browser_details: Option<BrowserDetails>,
    pub version: Option<String>,
    #[serde(rename = "notificationURL")]
    pub notification_url: Option<String>,
    #[serde(rename = "merchantURL")]
    pub merchant_url: Option<String>,
    pub acs_url: Option<String>,
    pub acs_challenge_mandate: Option<String>,
    pub c_req: Option<Secret<String>>,
    pub three_d_flow: Option<String>,
    pub external_mpi: Option<ExternalMpi>,
    pub external_transaction_id: Option<String>,
    pub transaction_id: Option<String>,
    pub three_d_reason_id: Option<String>,
    pub three_d_reason: Option<String>,
    pub challenge_preference_reason: Option<String>,
    pub challenge_cancel_reason_id: Option<String>,
    pub challenge_cancel_reason: Option<String>,
    pub is_liability_on_issuer: Option<String>,
    pub is_exemption_request_in_authentication: Option<String>,
    pub flow: Option<String>,
    pub acquirer_decision: Option<String>,
    pub decision_reason: Option<String>,
    pub platform_type: Option<PlatformType>,
    pub v2supported: Option<String>,
    pub v2_additional_params: Option<V2AdditionalParams>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub enum MethodCompletion {
    #[serde(rename = "Y")]
    Success,
    #[serde(rename = "N")]
    Failure,
    #[serde(rename = "U")]
    #[default]
    Unavailable,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub enum PlatformType {
    #[serde(rename = "01")]
    App,
    #[serde(rename = "02")]
    #[default]
    Browser,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserDetails {
    pub accept_header: String,
    pub ip: Secret<String, IpAddress>,
    pub java_enabled: String,
    pub java_script_enabled: String,
    pub language: String,
    pub color_depth: u8,
    pub screen_height: u32,
    pub screen_width: u32,
    pub time_zone: i32,
    pub user_agent: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct V2AdditionalParams {
    pub challenge_window_size: Option<String>,
    /// Recurring Expiry in format YYYYMMDD. REQUIRED if isRebilling = 0, We recommend setting rebillExpiry to a value of no more than 5 years from the date of the initial transaction processing date.
    pub rebill_expiry: Option<String>,
    /// Recurring Frequency in days
    pub rebill_frequency: Option<String>,
    pub challenge_preference: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DeviceDetails {
    pub ip_address: Secret<String, IpAddress>,
}

impl TransactionType {
    fn get_from_capture_method_and_amount_string(
        capture_method: CaptureMethod,
        amount: &str,
    ) -> Self {
        let amount_value = amount.parse::<f64>();
        if capture_method == CaptureMethod::Manual || amount_value == Ok(0.0) {
            Self::Auth
        } else {
            Self::Sale
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct NuveiRedirectionResponse {
    pub cres: Secret<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NuveiACSResponse {
    #[serde(rename = "threeDSServerTransID")]
    pub three_ds_server_trans_id: Secret<String>,
    #[serde(rename = "acsTransID")]
    pub acs_trans_id: Secret<String>,
    pub message_type: String,
    pub message_version: String,
    pub trans_status: Option<LiabilityShift>,
    pub message_extension: Option<Vec<MessageExtensionAttribute>>,
    pub acs_signed_content: Option<serde_json::Value>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum LiabilityShift {
    #[serde(rename = "Y", alias = "1", alias = "y")]
    Success,
    #[serde(rename = "N", alias = "0", alias = "n")]
    Failed,
}

pub fn encode_payload(
    payload: &[&str],
) -> Result<String, error_stack::Report<errors::ConnectorError>> {
    let data = payload.join("");
    let digest = crypto::Sha256
        .generate_digest(data.as_bytes())
        .change_context(errors::ConnectorError::RequestEncodingFailed)
        .attach_printable("error encoding nuvie payload")?;
    Ok(hex::encode(digest))
}

impl From<NuveiPaymentSyncResponse> for NuveiTransactionSyncResponse {
    fn from(value: NuveiPaymentSyncResponse) -> Self {
        match value {
            NuveiPaymentSyncResponse::NuveiDmn(payment_dmn_notification) => {
                Self::from(*payment_dmn_notification)
            }
            NuveiPaymentSyncResponse::NuveiApi(nuvei_transaction_sync_response) => {
                *nuvei_transaction_sync_response
            }
        }
    }
}

impl TryFrom<&types::PaymentsAuthorizeSessionTokenRouterData> for NuveiSessionRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &types::PaymentsAuthorizeSessionTokenRouterData,
    ) -> Result<Self, Self::Error> {
        let connector_meta: NuveiAuthType = NuveiAuthType::try_from(&item.connector_auth_type)?;
        let merchant_id = connector_meta.merchant_id;
        let merchant_site_id = connector_meta.merchant_site_id;
        let client_request_id = item.connector_request_reference_id.clone();
        let time_stamp = date_time::DateTime::<date_time::YYYYMMDDHHmmss>::from(date_time::now());
        let merchant_secret = connector_meta.merchant_secret;
        Ok(Self {
            merchant_id: merchant_id.clone(),
            merchant_site_id: merchant_site_id.clone(),


// Function: should_perform_elimination_routing
// File: crates/common_enums/src/enums.rs
// Module: common_enums

pub fn should_perform_elimination_routing(self) -> bool


// File: crates/common_enums/src/connector_enums.rs
// Module: common_enums
// Public functions: 17

use std::collections::HashSet;

use utoipa::ToSchema;

pub use super::enums::{PaymentMethod, PayoutType};
pub use crate::PaymentMethodType;

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
    strum::EnumIter,
    strum::VariantNames,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
/// RoutableConnectors are the subset of Connectors that are eligible for payments routing
pub enum RoutableConnectors {
    Authipay,
    Adyenplatform,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "stripe_billing_test")]
    #[strum(serialize = "stripe_billing_test")]
    DummyBillingConnector,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "phonypay")]
    #[strum(serialize = "phonypay")]
    DummyConnector1,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "fauxpay")]
    #[strum(serialize = "fauxpay")]
    DummyConnector2,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "pretendpay")]
    #[strum(serialize = "pretendpay")]
    DummyConnector3,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "stripe_test")]
    #[strum(serialize = "stripe_test")]
    DummyConnector4,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "adyen_test")]
    #[strum(serialize = "adyen_test")]
    DummyConnector5,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "checkout_test")]
    #[strum(serialize = "checkout_test")]
    DummyConnector6,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "paypal_test")]
    #[strum(serialize = "paypal_test")]
    DummyConnector7,
    Aci,
    Adyen,
    Affirm,
    Airwallex,
    Amazonpay,
    Archipel,
    Authorizedotnet,
    Bankofamerica,
    Barclaycard,
    Billwerk,
    Bitpay,
    Bambora,
    Blackhawknetwork,
    Bamboraapac,
    Bluesnap,
    Bluecode,
    Boku,
    Braintree,
    Breadpay,
    Cashtocode,
    Celero,
    Chargebee,
    Custombilling,
    Checkbook,
    Checkout,
    Coinbase,
    Coingate,
    Cryptopay,
    Cybersource,
    Datatrans,
    Deutschebank,
    Digitalvirgo,
    Dlocal,
    Dwolla,
    Ebanx,
    Elavon,
    Facilitapay,
    Fiserv,
    Fiservemea,
    Fiuu,
    Flexiti,
    Forte,
    Getnet,
    Gigadat,
    Globalpay,
    Globepay,
    Gocardless,
    Hipay,
    Helcim,
    Iatapay,
    Inespay,
    Itaubank,
    Jpmorgan,
    Klarna,
    Loonio,
    Mifinity,
    Mollie,
    Moneris,
    Multisafepay,
    Nexinets,
    Nexixpay,
    Nmi,
    Nomupay,
    Noon,
    Nordea,
    Novalnet,
    Nuvei,
    // Opayo, added as template code for future usage
    Opennode,
    // Payeezy, As psync and rsync are not supported by this connector, it is added as template code for future usage
    Paybox,
    Payme,
    Payload,
    Payone,
    Paypal,
    Paysafe,
    Paystack,
    Paytm,
    Payu,
    Peachpayments,
    Phonepe,
    Placetopay,
    Powertranz,
    Prophetpay,
    Rapyd,
    Razorpay,
    Recurly,
    Redsys,
    Riskified,
    Santander,
    Shift4,
    Signifyd,
    Silverflow,
    Square,
    Stax,
    Stripe,
    Stripebilling,
    // Taxjar,
    Trustpay,
    Trustpayments,
    // Thunes
    Tokenio,
    // Tsys,
    Tsys,
    // UnifiedAuthenticationService,
    // Vgs
    Volt,
    Wellsfargo,
    // Wellsfargopayout,
    Wise,
    Worldline,
    Worldpay,
    Worldpayvantiv,
    Worldpayxml,
    Xendit,
    Zen,
    Plaid,
    Zsl,
}

// A connector is an integration to fulfill payments
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    ToSchema,
    serde::Deserialize,
    serde::Serialize,
    strum::VariantNames,
    strum::EnumIter,
    strum::Display,
    strum::EnumString,
    Hash,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum Connector {
    Authipay,
    Adyenplatform,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "stripe_billing_test")]
    #[strum(serialize = "stripe_billing_test")]
    DummyBillingConnector,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "phonypay")]
    #[strum(serialize = "phonypay")]
    DummyConnector1,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "fauxpay")]
    #[strum(serialize = "fauxpay")]
    DummyConnector2,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "pretendpay")]
    #[strum(serialize = "pretendpay")]
    DummyConnector3,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "stripe_test")]
    #[strum(serialize = "stripe_test")]
    DummyConnector4,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "adyen_test")]
    #[strum(serialize = "adyen_test")]
    DummyConnector5,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "checkout_test")]
    #[strum(serialize = "checkout_test")]
    DummyConnector6,
    #[cfg(feature = "dummy_connector")]
    #[serde(rename = "paypal_test")]
    #[strum(serialize = "paypal_test")]
    DummyConnector7,
    Aci,
    Adyen,
    Affirm,
    Airwallex,
    Amazonpay,
    Archipel,
    Authorizedotnet,
    Bambora,
    Bamboraapac,
    Bankofamerica,
    Barclaycard,
    Billwerk,
    Bitpay,
    Bluesnap,
    Blackhawknetwork,
    Bluecode,
    Boku,
    Braintree,
    Breadpay,
    Cardinal,
    Cashtocode,
    Celero,
    Chargebee,
    Checkbook,
    Checkout,
    Coinbase,
    Coingate,
    Custombilling,
    Cryptopay,
    CtpMastercard,
    CtpVisa,
    Cybersource,
    Datatrans,
    Deutschebank,
    Digitalvirgo,
    Dlocal,
    Dwolla,
    Ebanx,
    Elavon,
    Facilitapay,
    Fiserv,
    Fiservemea,
    Fiuu,
    Flexiti,
    Forte,
    Getnet,
    Gigadat,
    Globalpay,
    Globepay,
    Gocardless,
    Gpayments,
    Hipay,
    Helcim,
    HyperswitchVault,
    // Hyperwallet, added as template code for future usage
    Inespay,
    Iatapay,
    Itaubank,
    Jpmorgan,
    Juspaythreedsserver,
    Klarna,
    Loonio,
    Mifinity,
    Mollie,
    Moneris,
    Multisafepay,
    Netcetera,
    Nexinets,
    Nexixpay,
    Nmi,
    Nomupay,
    Noon,
    Nordea,
    Novalnet,
    Nuvei,
    // Opayo, added as template code for future usage
    Opennode,
    Paybox,
    // Payeezy, As psync and rsync are not supported by this connector, it is added as template code for future usage
    Payload,
    Payme,
    Payone,
    Paypal,
    Paysafe,
    Paystack,
    Paytm,
    Payu,
    Peachpayments,
    Phonepe,
    Placetopay,
    Powertranz,
    Prophetpay,
    Rapyd,
    Razorpay,
    Recurly,
    Redsys,
    Santander,
    Shift4,
    Silverflow,
    Square,
    Stax,
    Stripe,
    Stripebilling,
    Taxjar,
    Threedsecureio,
    // Tokenio,
    //Thunes,
    Tokenex,
    Tokenio,
    Trustpay,
    Trustpayments,
    Tsys,
    // UnifiedAuthenticationService,
    Vgs,
    Volt,
    Wellsfargo,
    // Wellsfargopayout,
    Wise,
    Worldline,
    Worldpay,
    Worldpayvantiv,
    Worldpayxml,
    Signifyd,
    Plaid,
    Riskified,
    Xendit,
    Zen,
    Zsl,
}

impl Connector {
    #[cfg(feature = "payouts")]
    pub fn supports_instant_payout(self, payout_method: Option<PayoutType>) -> bool {
        matches!(
            (self, payout_method),
            (Self::Paypal, Some(PayoutType::Wallet))
                | (_, Some(PayoutType::Card))
                | (Self::Adyenplatform, _)
                | (Self::Nomupay, _)
        )
    }
    #[cfg(feature = "payouts")]
    pub fn supports_create_recipient(self, payout_method: Option<PayoutType>) -> bool {
        matches!((self, payout_method), (_, Some(PayoutType::Bank)))
    }
    #[cfg(feature = "payouts")]
    pub fn supports_payout_eligibility(self, payout_method: Option<PayoutType>) -> bool {
        matches!((self, payout_method), (_, Some(PayoutType::Card)))
    }
    #[cfg(feature = "payouts")]
    pub fn is_payout_quote_call_required(self) -> bool {
        matches!(self, Self::Wise)
    }
    #[cfg(feature = "payouts")]
    pub fn supports_access_token_for_payout(self, payout_method: Option<PayoutType>) -> bool {
        matches!((self, payout_method), (Self::Paypal, _))
    }
    #[cfg(feature = "payouts")]
    pub fn supports_vendor_disburse_account_create_for_payout(self) -> bool {
        matches!(self, Self::Stripe | Self::Nomupay)
    }
    pub fn supports_access_token(self, payment_method: PaymentMethod) -> bool {
        matches!(
            (self, payment_method),
            (Self::Airwallex, _)
                | (Self::Deutschebank, _)
                | (Self::Globalpay, _)
                | (Self::Jpmorgan, _)
                | (Self::Moneris, _)
                | (Self::Nordea, _)
                | (Self::Paypal, _)
                | (Self::Payu, _)
                | (
                    Self::Trustpay,
                    PaymentMethod::BankRedirect | PaymentMethod::BankTransfer
                )
                | (Self::Iatapay, _)
                | (Self::Volt, _)
                | (Self::Itaubank, _)
                | (Self::Facilitapay, _)
                | (Self::Dwolla, _)
        )
    }
    pub fn requires_order_creation_before_payment(self, payment_method: PaymentMethod) -> bool {
        matches!((self, payment_method), (Self::Razorpay, PaymentMethod::Upi))
    }
    pub fn supports_file_storage_module(self) -> bool {
        matches!(self, Self::Stripe | Self::Checkout | Self::Worldpayvantiv)
    }
    pub fn requires_defend_dispute(self) -> bool {
        matches!(self, Self::Checkout)
    }
    pub fn is_separate_authentication_supported(self) -> bool {
        match self {
            #[cfg(feature = "dummy_connector")]
            Self::DummyBillingConnector => false,
            #[cfg(feature = "dummy_connector")]
            Self::DummyConnector1
            | Self::DummyConnector2
            | Self::DummyConnector3
            | Self::DummyConnector4
            | Self::DummyConnector5
            | Self::DummyConnector6
            | Self::DummyConnector7 => false,
            Self::Aci
            // Add Separate authentication support for connectors
			| Self::Authipay
            | Self::Adyen
            | Self::Affirm
            | Self::Adyenplatform
            | Self::Airwallex
            | Self::Amazonpay
            | Self::Authorizedotnet
            | Self::Bambora
            | Self::Bamboraapac
            | Self::Bankofamerica
            | Self::Barclaycard
            | Self::Billwerk
            | Self::Bitpay
            | Self::Bluesnap
            | Self::Blackhawknetwork
            | Self::Bluecode
            | Self::Boku
            | Self::Braintree
            | Self::Breadpay
            | Self::Cashtocode
            | Self::Celero
            | Self::Chargebee
            | Self::Checkbook
            | Self::Coinbase
            | Self::Coingate
            | Self::Cryptopay
            | Self::Custombilling
            | Self::Deutschebank
            | Self::Digitalvirgo
            | Self::Dlocal
            | Self::Dwolla
            | Self::Ebanx
            | Self::Elavon
            | Self::Facilitapay
            | Self::Fiserv
            | Self::Fiservemea
            | Self::Fiuu
            | Self::Flexiti
            | Self::Forte
            | Self::Getnet
            | Self::Gigadat
            | Self::Globalpay
            | Self::Globepay
            | Self::Gocardless
            | Self::Gpayments
            | Self::Hipay
            | Self::Helcim
            | Self::HyperswitchVault
            | Self::Iatapay
			| Self::Inespay
            | Self::Itaubank
            | Self::Jpmorgan
            | Self::Juspaythreedsserver
            | Self::Klarna
            | Self::Loonio
            | Self::Mifinity
            | Self::Mollie
            | Self::Moneris
            | Self::Multisafepay
            | Self::Nexinets
            | Self::Nexixpay
            | Self::Nomupay
            | Self::Nordea
            | Self::Novalnet
            | Self::Opennode
            | Self::Paybox
            | Self::Payload
            | Self::Payme
            | Self::Payone
            | Self::Paypal
            | Self::Paysafe
            | Self::Paystack
            | Self::Payu
            | Self::Peachpayments
            | Self::Placetopay
            | Self::Powertranz
            | Self::Prophetpay
            | Self::Rapyd
            | Self::Recurly
            | Self::Redsys
            | Self::Santander
            | Self::Shift4
            | Self::Silverflow
            | Self::Square
            | Self::Stax
            | Self::Stripebilling
            | Self::Taxjar
            // | Self::Thunes
            | Self::Trustpay
            | Self::Trustpayments
            // | Self::Tokenio
            | Self::Tsys
            // | Self::UnifiedAuthenticationService
            | Self::Vgs
            | Self::Volt
            | Self::Wellsfargo
            // | Self::Wellsfargopayout
            | Self::Wise
            | Self::Worldline
            | Self::Worldpay
            | Self::Worldpayvantiv
            | Self::Worldpayxml
            | Self::Xendit
            | Self::Zen
            | Self::Zsl
            | Self::Signifyd
            | Self::Plaid
            | Self::Razorpay
            | Self::Riskified
            | Self::Threedsecureio
            | Self::Netcetera
            | Self::CtpMastercard
            | Self::Cardinal
            | Self::CtpVisa
            | Self::Noon
            | Self::Tokenex
            | Self::Tokenio
            | Self::Stripe
            | Self::Datatrans
            | Self::Paytm
            | Self::Phonepe => false,
            Self::Checkout | Self::Nmi |Self::Cybersource | Self::Archipel | Self::Nuvei            => true,
        }
    }

    pub fn is_pre_processing_required_before_authorize(self) -> bool {
        matches!(self, Self::Airwallex)
    }

    pub fn get_payment_methods_supporting_extended_authorization(self) -> HashSet<PaymentMethod> {
        HashSet::from([PaymentMethod::Card])
    }
    pub fn get_payment_method_types_supporting_extended_authorization(
        self,
    ) -> HashSet<PaymentMethodType> {
        HashSet::from([PaymentMethodType::Credit, PaymentMethodType::Debit])
    }

    pub fn is_overcapture_supported_by_connector(self) -> bool {
        matches!(self, Self::Stripe | Self::Adyen)
    }

    pub fn should_acknowledge_webhook_for_resource_not_found_errors(self) -> bool {
        matches!(self, Self::Adyenplatform | Self::Adyen)
    }

    /// Validates if dummy connector can be created
    /// Dummy connectors can be created only if dummy_connector feature is enabled in the configs
    #[cfg(feature = "dummy_connector")]
    pub fn validate_dummy_connector_create(self, is_dummy_connector_enabled: bool) -> bool {
        matches!(
            self,
            Self::DummyConnector1
                | Self::DummyConnector2
                | Self::DummyConnector3
                | Self::DummyConnector4
                | Self::DummyConnector5
                | Self::DummyConnector6
                | Self::DummyConnector7
        ) && !is_dummy_connector_enabled
    }
}

/// Convert the RoutableConnectors to Connector
impl From<RoutableConnectors> for Connector {
    fn from(routable_connector: RoutableConnectors) -> Self {
        match routable_connector {
            RoutableConnectors::Authipay => Self::Authipay,
            RoutableConnectors::Adyenplatform => Self::Adyenplatform,
            #[cfg(feature = "dummy_connector")]
            RoutableConnectors::DummyBillingConnector => Self::DummyBillingConnector,
            #[cfg(feature = "dummy_connector")]
            RoutableConnectors::DummyConnector1 => Self::DummyConnector1,
            #[cfg(feature = "dummy_connector")]
            RoutableConnectors::DummyConnector2 => Self::DummyConnector2,
            #[cfg(feature = "dummy_connector")]
            RoutableConnectors::DummyConnector3 => Self::DummyConnector3,
            #[cfg(feature = "dummy_connector")]
            RoutableConnectors::DummyConnector4 => Self::DummyConnector4,
            #[cfg(feature = "dummy_connector")]
            RoutableConnectors::DummyConnector5 => Self::DummyConnector5,
            #[cfg(feature = "dummy_connector")]
            RoutableConnectors::DummyConnector6 => Self::DummyConnector6,
            #[cfg(feature = "dummy_connector")]
            RoutableConnectors::DummyConnector7 => Self::DummyConnector7,
            RoutableConnectors::Aci => Self::Aci,
            RoutableConnectors::Adyen => Self::Adyen,
            RoutableConnectors::Affirm => Self::Affirm,
            RoutableConnectors::Airwallex => Self::Airwallex,
            RoutableConnectors::Amazonpay => Self::Amazonpay,
            RoutableConnectors::Archipel => Self::Archipel,
            RoutableConnectors::Authorizedotnet => Self::Authorizedotnet,
            RoutableConnectors::Bankofamerica => Self::Bankofamerica,
            RoutableConnectors::Barclaycard => Self::Barclaycard,
            RoutableConnectors::Billwerk => Self::Billwerk,
            RoutableConnectors::Bitpay => Self::Bitpay,
            RoutableConnectors::Bambora => Self::Bambora,
            RoutableConnectors::Bamboraapac => Self::Bamboraapac,
            RoutableConnectors::Bluesnap => Self::Bluesnap,
            RoutableConnectors::Blackhawknetwork => Self::Blackhawknetwork,
            RoutableConnectors::Bluecode => Self::Bluecode,
            RoutableConnectors::Boku => Self::Boku,
            RoutableConnectors::Braintree => Self::Braintree,
            RoutableConnectors::Breadpay => Self::Breadpay,
            RoutableConnectors::Cashtocode => Self::Cashtocode,
            RoutableConnectors::Celero => Self::Celero,
            RoutableConnectors::Chargebee => Self::Chargebee,
            RoutableConnectors::Custombilling => Self::Custombilling,
            RoutableConnectors::Checkbook => Self::Checkbook,
            RoutableConnectors::Checkout => Self::Checkout,
            RoutableConnectors::Coinbase => Self::Coinbase,
            RoutableConnectors::Cryptopay => Self::Cryptopay,
            RoutableConnectors::Cybersource => Self::Cybersource,
            RoutableConnectors::Datatrans => Self::Datatrans,
            RoutableConnectors::Deutschebank => Self::Deutschebank,
            RoutableConnectors::Digitalvirgo => Self::Digitalvirgo,
            RoutableConnectors::Dlocal => Self::Dlocal,
            RoutableConnectors::Dwolla => Self::Dwolla,
            RoutableConnectors::Ebanx => Self::Ebanx,
            RoutableConnectors::Elavon => Self::Elavon,
            RoutableConnectors::Facilitapay => Self::Facilitapay,
            RoutableConnectors::Fiserv => Self::Fiserv,
            RoutableConnectors::Fiservemea => Self::Fiservemea,
            RoutableConnectors::Fiuu => Self::Fiuu,
            RoutableConnectors::Flexiti => Self::Flexiti,
            RoutableConnectors::Forte => Self::Forte,
            RoutableConnectors::Getnet => Self::Getnet,
            RoutableConnectors::Gigadat => Self::Gigadat,
            RoutableConnectors::Globalpay => Self::Globalpay,
            RoutableConnectors::Globepay => Self::Globepay,
            RoutableConnectors::Gocardless => Self::Gocardless,
            RoutableConnectors::Helcim => Self::Helcim,
            RoutableConnectors::Iatapay => Self::Iatapay,
            RoutableConnectors::Itaubank => Self::Itaubank,
            RoutableConnectors::Jpmorgan => Self::Jpmorgan,
            RoutableConnectors::Klarna => Self::Klarna,
            RoutableConnectors::Loonio => Self::Loonio,
            RoutableConnectors::Mifinity => Self::Mifinity,
            RoutableConnectors::Mollie => Self::Mollie,
            RoutableConnectors::Moneris => Self::Moneris,
            RoutableConnectors::Multisafepay => Self::Multisafepay,
            RoutableConnectors::Nexinets => Self::Nexinets,
            RoutableConnectors::Nexixpay => Self::Nexixpay,
            RoutableConnectors::Nmi => Self::Nmi,
            RoutableConnectors::Nomupay => Self::Nomupay,
            RoutableConnectors::Noon => Self::Noon,
            RoutableConnectors::Nordea => Self::Nordea,
            RoutableConnectors::Novalnet => Self::Novalnet,
            RoutableConnectors::Nuvei => Self::Nuvei,
            RoutableConnectors::Opennode => Self::Opennode,
            RoutableConnectors::Paybox => Self::Paybox,
            RoutableConnectors::Payload => Self::Payload,
            RoutableConnectors::Payme => Self::Payme,
            RoutableConnectors::Payone => Self::Payone,
            RoutableConnectors::Paypal => Self::Paypal,
            RoutableConnectors::Paysafe => Self::Paysafe,
            RoutableConnectors::Paystack => Self::Paystack,
            RoutableConnectors::Payu => Self::Payu,
            RoutableConnectors::Peachpayments => Self::Peachpayments,
            RoutableConnectors::Placetopay => Self::Placetopay,
            RoutableConnectors::Powertranz => Self::Powertranz,
            RoutableConnectors::Prophetpay => Self::Prophetpay,
            RoutableConnectors::Rapyd => Self::Rapyd,
            RoutableConnectors::Razorpay => Self::Razorpay,
            RoutableConnectors::Recurly => Self::Recurly,
            RoutableConnectors::Redsys => Self::Redsys,
            RoutableConnectors::Riskified => Self::Riskified,
            RoutableConnectors::Santander => Self::Santander,
            RoutableConnectors::Shift4 => Self::Shift4,
            RoutableConnectors::Signifyd => Self::Signifyd,
            RoutableConnectors::Silverflow => Self::Silverflow,
            RoutableConnectors::Square => Self::Square,
            RoutableConnectors::Stax => Self::Stax,
            RoutableConnectors::Stripe => Self::Stripe,
            RoutableConnectors::Stripebilling => Self::Stripebilling,
            RoutableConnectors::Tokenio => Self::Tokenio,
            RoutableConnectors::Trustpay => Self::Trustpay,
            RoutableConnectors::Trustpayments => Self::Trustpayments,
            // RoutableConnectors::Tokenio => Self::Tokenio,
            RoutableConnectors::Tsys => Self::Tsys,
            RoutableConnectors::Volt => Self::Volt,
            RoutableConnectors::Wellsfargo => Self::Wellsfargo,
            RoutableConnectors::Wise => Self::Wise,
            RoutableConnectors::Worldline => Self::Worldline,
            RoutableConnectors::Worldpay => Self::Worldpay,
            RoutableConnectors::Worldpayvantiv => Self::Worldpayvantiv,
            RoutableConnectors::Worldpayxml => Self::Worldpayxml,
            RoutableConnectors::Zen => Self::Zen,
            RoutableConnectors::Plaid => Self::Plaid,
            RoutableConnectors::Zsl => Self::Zsl,
            RoutableConnectors::Xendit => Self::Xendit,
            RoutableConnectors::Inespay => Self::Inespay,
            RoutableConnectors::Coingate => Self::Coingate,
            RoutableConnectors::Hipay => Self::Hipay,
            RoutableConnectors::Paytm => Self::Paytm,
            RoutableConnectors::Phonepe => Self::Phonepe,
        }
    }
}

impl TryFrom<Connector> for RoutableConnectors {
    type Error = &'static str;

    fn try_from(connector: Connector) -> Result<Self, Self::Error> {
        match connector {
            Connector::Authipay => Ok(Self::Authipay),
            Connector::Adyenplatform => Ok(Self::Adyenplatform),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyBillingConnector => Ok(Self::DummyBillingConnector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector1 => Ok(Self::DummyConnector1),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector2 => Ok(Self::DummyConnector2),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector3 => Ok(Self::DummyConnector3),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector4 => Ok(Self::DummyConnector4),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector5 => Ok(Self::DummyConnector5),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector6 => Ok(Self::DummyConnector6),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector7 => Ok(Self::DummyConnector7),
            Connector::Aci => Ok(Self::Aci),
            Connector::Adyen => Ok(Self::Adyen),
            Connector::Affirm => Ok(Self::Affirm),
            Connector::Airwallex => Ok(Self::Airwallex),
            Connector::Amazonpay => Ok(Self::Amazonpay),
            Connector::Archipel => Ok(Self::Archipel),
            Connector::Authorizedotnet => Ok(Self::Authorizedotnet),
            Connector::Bankofamerica => Ok(Self::Bankofamerica),
            Connector::Barclaycard => Ok(Self::Barclaycard),
            Connector::Billwerk => Ok(Self::Billwerk),
            Connector::Bitpay => Ok(Self::Bitpay),
            Connector::Bambora => Ok(Self::Bambora),
            Connector::Bamboraapac => Ok(Self::Bamboraapac),
            Connector::Bluesnap => Ok(Self::Bluesnap),
            Connector::Blackhawknetwork => Ok(Self::Blackhawknetwork),
            Connector::Bluecode => Ok(Self::Bluecode),
            Connector::Boku => Ok(Self::Boku),
            Connector::Braintree => Ok(Self::Braintree),
            Connector::Breadpay => Ok(Self::Breadpay),
            Connector::Cashtocode => Ok(Self::Cashtocode),
            Connector::Celero => Ok(Self::Celero),
            Connector::Chargebee => Ok(Self::Chargebee),
            Connector::Checkbook => Ok(Self::Checkbook),
            Connector::Checkout => Ok(Self::Checkout),
            Connector::Coinbase => Ok(Self::Coinbase),
            Connector::Coingate => Ok(Self::Coingate),
            Connector::Cryptopay => Ok(Self::Cryptopay),
            Connector::Custombilling => Ok(Self::Custombilling),
            Connector::Cybersource => Ok(Self::Cybersource),
            Connector::Datatrans => Ok(Self::Datatrans),
            Connector::Deutschebank => Ok(Self::Deutschebank),
            Connector::Digitalvirgo => Ok(Self::Digitalvirgo),
            Connector::Dlocal => Ok(Self::Dlocal),
            Connector::Dwolla => Ok(Self::Dwolla),
            Connector::Ebanx => Ok(Self::Ebanx),
            Connector::Elavon => Ok(Self::Elavon),
            Connector::Facilitapay => Ok(Self::Facilitapay),
            Connector::Fiserv => Ok(Self::Fiserv),
            Connector::Fiservemea => Ok(Self::Fiservemea),
            Connector::Fiuu => Ok(Self::Fiuu),
            Connector::Flexiti => Ok(Self::Flexiti),
            Connector::Forte => Ok(Self::Forte),
            Connector::Globalpay => Ok(Self::Globalpay),
            Connector::Globepay => Ok(Self::Globepay),
            Connector::Gocardless => Ok(Self::Gocardless),
            Connector::Helcim => Ok(Self::Helcim),
            Connector::Iatapay => Ok(Self::Iatapay),
            Connector::Itaubank => Ok(Self::Itaubank),
            Connector::Jpmorgan => Ok(Self::Jpmorgan),
            Connector::Klarna => Ok(Self::Klarna),
            Connector::Loonio => Ok(Self::Loonio),
            Connector::Mifinity => Ok(Self::Mifinity),
            Connector::Mollie => Ok(Self::Mollie),
            Connector::Moneris => Ok(Self::Moneris),
            Connector::Multisafepay => Ok(Self::Multisafepay),
            Connector::Nexinets => Ok(Self::Nexinets),
            Connector::Nexixpay => Ok(Self::Nexixpay),
            Connector::Nmi => Ok(Self::Nmi),
            Connector::Nomupay => Ok(Self::Nomupay),
            Connector::Noon => Ok(Self::Noon),
            Connector::Nordea => Ok(Self::Nordea),
            Connector::Novalnet => Ok(Self::Novalnet),
            Connector::Nuvei => Ok(Self::Nuvei),
            Connector::Opennode => Ok(Self::Opennode),
            Connector::Paybox => Ok(Self::Paybox),
            Connector::Payload => Ok(Self::Payload),
            Connector::Payme => Ok(Self::Payme),
            Connector::Payone => Ok(Self::Payone),
            Connector::Paypal => Ok(Self::Paypal),
            Connector::Paysafe => Ok(Self::Paysafe),
            Connector::Paystack => Ok(Self::Paystack),
            Connector::Payu => Ok(Self::Payu),
            Connector::Peachpayments => Ok(Self::Peachpayments),
            Connector::Placetopay => Ok(Self::Placetopay),
            Connector::Powertranz => Ok(Self::Powertranz),
            Connector::Prophetpay => Ok(Self::Prophetpay),
            Connector::Rapyd => Ok(Self::Rapyd),
            Connector::Razorpay => Ok(Self::Razorpay),
            Connector::Riskified => Ok(Self::Riskified),
            Connector::Santander => Ok(Self::Santander),
            Connector::Shift4 => Ok(Self::Shift4),
            Connector::Signifyd => Ok(Self::Signifyd),
            Connector::Silverflow => Ok(Self::Silverflow),
            Connector::Square => Ok(Self::Square),
            Connector::Stax => Ok(Self::Stax),
            Connector::Stripe => Ok(Self::Stripe),
            Connector::Stripebilling => Ok(Self::Stripebilling),
            Connector::Tokenio => Ok(Self::Tokenio),
            Connector::Trustpay => Ok(Self::Trustpay),
            Connector::Trustpayments => Ok(Self::Trustpayments),
            Connector::Tsys => Ok(Self::Tsys),
            Connector::Volt => Ok(Self::Volt),
            Connector::Wellsfargo => Ok(Self::Wellsfargo),
            Connector::Wise => Ok(Self::Wise),
            Connector::Worldline => Ok(Self::Worldline),
            Connector::Worldpay => Ok(Self::Worldpay),
            Connector::Worldpayvantiv => Ok(Self::Worldpayvantiv),
            Connector::Worldpayxml => Ok(Self::Worldpayxml),
            Connector::Xendit => Ok(Self::Xendit),
            Connector::Zen => Ok(Self::Zen),
            Connector::Plaid => Ok(Self::Plaid),
            Connector::Zsl => Ok(Self::Zsl),
            Connector::Recurly => Ok(Self::Recurly),
            Connector::Getnet => Ok(Self::Getnet),
            Connector::Gigadat => Ok(Self::Gigadat),
            Connector::Hipay => Ok(Self::Hipay),
            Connector::Inespay => Ok(Self::Inespay),
            Connector::Redsys => Ok(Self::Redsys),
            Connector::Paytm => Ok(Self::Paytm),
            Connector::Phonepe => Ok(Self::Phonepe),
            Connector::CtpMastercard
            | Connector::Gpayments
            | Connector::HyperswitchVault
            | Connector::Juspaythreedsserver
            | Connector::Netcetera
            | Connector::Taxjar
            | Connector::Threedsecureio
            | Connector::Vgs
            | Connector::CtpVisa
            | Connector::Cardinal
            | Connector::Tokenex => Err("Invalid conversion. Not a routable connector"),
        }
    }
}

// Enum representing different status an invoice can have.
#[derive(Debug, Clone, PartialEq, Eq, strum::Display, strum::EnumString)]
pub enum InvoiceStatus {
    InvoiceCreated,
    PaymentPending,
    PaymentPendingTimeout,
    PaymentSucceeded,
    PaymentFailed,
    PaymentCanceled,
    InvoicePaid,
    ManualReview,
}


// Struct: Payouts
// File: crates/diesel_models/src/payouts.rs
// Module: diesel_models
// Implementations: 0

pub struct Payouts


// Struct: NordeaPaymentsConfirmRequest
// File: crates/hyperswitch_connectors/src/connectors/nordea/requests.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NordeaPaymentsConfirmRequest


// Struct: FraudCheckPre
// File: crates/router/src/core/fraud_check/operation/fraud_check_pre.rs
// Module: router
// Implementations: 0

pub struct FraudCheckPre


// File: crates/storage_impl/src/redis/kv_store.rs
// Module: storage_impl
// Public functions: 2

use std::{fmt::Debug, sync::Arc};

use common_utils::errors::CustomResult;
use diesel_models::enums::MerchantStorageScheme;
use error_stack::report;
use redis_interface::errors::RedisError;
use router_derive::TryGetEnumVariant;
use router_env::logger;
use serde::de;

use crate::{kv_router_store::KVRouterStore, metrics, store::kv::TypedSql, UniqueConstraints};

pub trait KvStorePartition {
    fn partition_number(key: PartitionKey<'_>, num_partitions: u8) -> u32 {
        crc32fast::hash(key.to_string().as_bytes()) % u32::from(num_partitions)
    }

    fn shard_key(key: PartitionKey<'_>, num_partitions: u8) -> String {
        format!("shard_{}", Self::partition_number(key, num_partitions))
    }
}

#[allow(unused)]
#[derive(Clone)]
pub enum PartitionKey<'a> {
    MerchantIdPaymentId {
        merchant_id: &'a common_utils::id_type::MerchantId,
        payment_id: &'a common_utils::id_type::PaymentId,
    },
    CombinationKey {
        combination: &'a str,
    },
    MerchantIdCustomerId {
        merchant_id: &'a common_utils::id_type::MerchantId,
        customer_id: &'a common_utils::id_type::CustomerId,
    },
    #[cfg(feature = "v2")]
    MerchantIdMerchantReferenceId {
        merchant_id: &'a common_utils::id_type::MerchantId,
        merchant_reference_id: &'a str,
    },
    MerchantIdPayoutId {
        merchant_id: &'a common_utils::id_type::MerchantId,
        payout_id: &'a common_utils::id_type::PayoutId,
    },
    MerchantIdPayoutAttemptId {
        merchant_id: &'a common_utils::id_type::MerchantId,
        payout_attempt_id: &'a str,
    },
    MerchantIdMandateId {
        merchant_id: &'a common_utils::id_type::MerchantId,
        mandate_id: &'a str,
    },
    #[cfg(feature = "v2")]
    GlobalId {
        id: &'a str,
    },
    #[cfg(feature = "v2")]
    GlobalPaymentId {
        id: &'a common_utils::id_type::GlobalPaymentId,
    },
}
// PartitionKey::MerchantIdPaymentId {merchant_id, payment_id}
impl std::fmt::Display for PartitionKey<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match *self {
            PartitionKey::MerchantIdPaymentId {
                merchant_id,
                payment_id,
            } => f.write_str(&format!(
                "mid_{}_pid_{}",
                merchant_id.get_string_repr(),
                payment_id.get_string_repr()
            )),
            PartitionKey::CombinationKey { combination } => f.write_str(combination),
            PartitionKey::MerchantIdCustomerId {
                merchant_id,
                customer_id,
            } => f.write_str(&format!(
                "mid_{}_cust_{}",
                merchant_id.get_string_repr(),
                customer_id.get_string_repr()
            )),
            #[cfg(feature = "v2")]
            PartitionKey::MerchantIdMerchantReferenceId {
                merchant_id,
                merchant_reference_id,
            } => f.write_str(&format!(
                "mid_{}_cust_{merchant_reference_id}",
                merchant_id.get_string_repr()
            )),
            PartitionKey::MerchantIdPayoutId {
                merchant_id,
                payout_id,
            } => f.write_str(&format!(
                "mid_{}_po_{}",
                merchant_id.get_string_repr(),
                payout_id.get_string_repr()
            )),
            PartitionKey::MerchantIdPayoutAttemptId {
                merchant_id,
                payout_attempt_id,
            } => f.write_str(&format!(
                "mid_{}_poa_{payout_attempt_id}",
                merchant_id.get_string_repr()
            )),
            PartitionKey::MerchantIdMandateId {
                merchant_id,
                mandate_id,
            } => f.write_str(&format!(
                "mid_{}_mandate_{mandate_id}",
                merchant_id.get_string_repr()
            )),

            #[cfg(feature = "v2")]
            PartitionKey::GlobalId { id } => f.write_str(&format!("global_cust_{id}")),
            #[cfg(feature = "v2")]
            PartitionKey::GlobalPaymentId { id } => {
                f.write_str(&format!("global_payment_{}", id.get_string_repr()))
            }
        }
    }
}

pub trait RedisConnInterface {
    fn get_redis_conn(
        &self,
    ) -> error_stack::Result<Arc<redis_interface::RedisConnectionPool>, RedisError>;
}

/// An enum to represent what operation to do on
pub enum KvOperation<'a, S: serde::Serialize + Debug> {
    Hset((&'a str, String), TypedSql),
    SetNx(&'a S, TypedSql),
    HSetNx(&'a str, &'a S, TypedSql),
    HGet(&'a str),
    Get,
    Scan(&'a str),
}

#[derive(TryGetEnumVariant)]
#[error(RedisError::UnknownResult)]
pub enum KvResult<T: de::DeserializeOwned> {
    HGet(T),
    Get(T),
    Hset(()),
    SetNx(redis_interface::SetnxReply),
    HSetNx(redis_interface::HsetnxReply),
    Scan(Vec<T>),
}

impl<T> std::fmt::Display for KvOperation<'_, T>
where
    T: serde::Serialize + Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            KvOperation::Hset(_, _) => f.write_str("Hset"),
            KvOperation::SetNx(_, _) => f.write_str("Setnx"),
            KvOperation::HSetNx(_, _, _) => f.write_str("HSetNx"),
            KvOperation::HGet(_) => f.write_str("Hget"),
            KvOperation::Get => f.write_str("Get"),
            KvOperation::Scan(_) => f.write_str("Scan"),
        }
    }
}

pub async fn kv_wrapper<'a, T, D, S>(
    store: &KVRouterStore<D>,
    op: KvOperation<'a, S>,
    partition_key: PartitionKey<'a>,
) -> CustomResult<KvResult<T>, RedisError>
where
    T: de::DeserializeOwned,
    D: crate::database::store::DatabaseStore,
    S: serde::Serialize + Debug + KvStorePartition + UniqueConstraints + Sync,
{
    let redis_conn = store.get_redis_conn()?;

    let key = format!("{partition_key}");

    let type_name = std::any::type_name::<T>();
    let operation = op.to_string();

    let ttl = store.ttl_for_kv;

    let result = async {
        match op {
            KvOperation::Hset(value, sql) => {
                logger::debug!(kv_operation= %operation, value = ?value);

                redis_conn
                    .set_hash_fields(&key.into(), value, Some(ttl.into()))
                    .await?;

                store
                    .push_to_drainer_stream::<S>(sql, partition_key)
                    .await?;

                Ok(KvResult::Hset(()))
            }

            KvOperation::HGet(field) => {
                let result = redis_conn
                    .get_hash_field_and_deserialize(&key.into(), field, type_name)
                    .await?;
                Ok(KvResult::HGet(result))
            }

            KvOperation::Scan(pattern) => {
                let result: Vec<T> = redis_conn
                    .hscan_and_deserialize(&key.into(), pattern, None)
                    .await
                    .and_then(|result| {
                        if result.is_empty() {
                            Err(report!(RedisError::NotFound))
                        } else {
                            Ok(result)
                        }
                    })?;
                Ok(KvResult::Scan(result))
            }

            KvOperation::HSetNx(field, value, sql) => {
                logger::debug!(kv_operation= %operation, value = ?value);

                value.check_for_constraints(&redis_conn).await?;

                let result = redis_conn
                    .serialize_and_set_hash_field_if_not_exist(&key.into(), field, value, Some(ttl))
                    .await?;

                if matches!(result, redis_interface::HsetnxReply::KeySet) {
                    store
                        .push_to_drainer_stream::<S>(sql, partition_key)
                        .await?;
                    Ok(KvResult::HSetNx(result))
                } else {
                    Err(report!(RedisError::SetNxFailed))
                }
            }

            KvOperation::SetNx(value, sql) => {
                logger::debug!(kv_operation= %operation, value = ?value);

                let result = redis_conn
                    .serialize_and_set_key_if_not_exist(&key.into(), value, Some(ttl.into()))
                    .await?;

                value.check_for_constraints(&redis_conn).await?;

                if matches!(result, redis_interface::SetnxReply::KeySet) {
                    store
                        .push_to_drainer_stream::<S>(sql, partition_key)
                        .await?;
                    Ok(KvResult::SetNx(result))
                } else {
                    Err(report!(RedisError::SetNxFailed))
                }
            }

            KvOperation::Get => {
                let result = redis_conn
                    .get_and_deserialize_key(&key.into(), type_name)
                    .await?;
                Ok(KvResult::Get(result))
            }
        }
    };

    let attributes = router_env::metric_attributes!(("operation", operation.clone()));
    result
        .await
        .inspect(|_| {
            logger::debug!(kv_operation= %operation, status="success");
            metrics::KV_OPERATION_SUCCESSFUL.add(1, attributes);
        })
        .inspect_err(|err| {
            logger::error!(kv_operation = %operation, status="error", error = ?err);
            metrics::KV_OPERATION_FAILED.add(1, attributes);
        })
}

pub enum Op<'a> {
    Insert,
    Update(PartitionKey<'a>, &'a str, Option<&'a str>),
    Find,
}

impl std::fmt::Display for Op<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Op::Insert => f.write_str("insert"),
            Op::Find => f.write_str("find"),
            Op::Update(p_key, _, updated_by) => {
                f.write_str(&format!("update_{p_key} for updated_by_{updated_by:?}"))
            }
        }
    }
}

pub async fn decide_storage_scheme<T, D>(
    store: &KVRouterStore<T>,
    storage_scheme: MerchantStorageScheme,
    operation: Op<'_>,
) -> MerchantStorageScheme
where
    D: de::DeserializeOwned
        + serde::Serialize
        + Debug
        + KvStorePartition
        + UniqueConstraints
        + Sync,
    T: crate::database::store::DatabaseStore,
{
    if store.soft_kill_mode {
        let ops = operation.to_string();
        let updated_scheme = match operation {
            Op::Insert => MerchantStorageScheme::PostgresOnly,
            Op::Find => MerchantStorageScheme::RedisKv,
            Op::Update(_, _, Some("postgres_only")) => MerchantStorageScheme::PostgresOnly,
            Op::Update(partition_key, field, Some(_updated_by)) => {
                match Box::pin(kv_wrapper::<D, _, _>(
                    store,
                    KvOperation::<D>::HGet(field),
                    partition_key,
                ))
                .await
                {
                    Ok(_) => {
                        metrics::KV_SOFT_KILL_ACTIVE_UPDATE.add(1, &[]);
                        MerchantStorageScheme::RedisKv
                    }
                    Err(_) => MerchantStorageScheme::PostgresOnly,
                }
            }

            Op::Update(_, _, None) => MerchantStorageScheme::PostgresOnly,
        };

        let type_name = std::any::type_name::<D>();
        logger::info!(soft_kill_mode = "decide_storage_scheme", decided_scheme = %updated_scheme, configured_scheme = %storage_scheme,entity = %type_name, operation = %ops);

        updated_scheme
    } else {
        storage_scheme
    }
}


// Function: payments_cancel_post_capture
// File: crates/openapi/src/routes/payments.rs
// Module: openapi

pub fn payments_cancel_post_capture()


// Implementation: impl NetworkTokenizationBuilder
// File: crates/router/src/core/payment_methods/tokenize/payment_method_executor.rs
// Module: router
// Methods: 1 total (1 public)

impl NetworkTokenizationBuilder


// Implementation: impl api::MandateSetup for for Novalnet
// File: crates/hyperswitch_connectors/src/connectors/novalnet.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::MandateSetup for for Novalnet


// Function: fetch_raw_secrets
// File: crates/drainer/src/secrets_transformers.rs
// Module: drainer

// Documentation: # Panics  Will panic even if fetching raw secret fails for at least one config value

pub fn fetch_raw_secrets(
    conf: Settings<SecuredSecret>,
    secret_management_client: &dyn SecretManagementInterface,
) -> Settings<RawSecret>


// Function: perform_success_based_routing
// File: crates/router/src/core/payments/routing.rs
// Module: router

pub fn perform_success_based_routing<F, D>(
    state: &SessionState,
    routable_connectors: Vec<api_routing::RoutableConnectorChoice>,
    profile_id: &common_utils::id_type::ProfileId,
    merchant_id: &common_utils::id_type::MerchantId,
    payment_id: &common_utils::id_type::PaymentId,
    success_based_routing_config_params_interpolator: routing::helpers::DynamicRoutingConfigParamsInterpolator,
    success_based_algo_ref: api_routing::SuccessBasedAlgorithm,
    payment_data: &mut D,
) -> RoutingResult<Vec<api_routing::RoutableConnectorChoice>>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,


// Implementation: impl CustomerUpdateInternal
// File: crates/diesel_models/src/customers.rs
// Module: diesel_models
// Methods: 1 total (1 public)

impl CustomerUpdateInternal


// Function: get_error_message
// File: crates/hyperswitch_connectors/src/connectors/paystack/transformers.rs
// Module: hyperswitch_connectors

pub fn get_error_message(response: PaystackErrorResponse) -> String


// Implementation: impl fred::types::FromRedis for for MsetnxReply
// File: crates/redis_interface/src/types.rs
// Module: redis_interface
// Methods: 1 total (0 public)

impl fred::types::FromRedis for for MsetnxReply


// Function: should_call_unified_connector_service
// File: crates/router/src/core/unified_connector_service.rs
// Module: router

// Documentation: Generic version of should_call_unified_connector_service that works with any type implementing OperationSessionGetters trait

pub fn should_call_unified_connector_service<F: Clone, T, D>(
    state: &SessionState,
    merchant_context: &MerchantContext,
    router_data: &RouterData<F, T, PaymentsResponseData>,
    payment_data: Option<&D>,
) -> RouterResult<bool>
where
    D: OperationSessionGetters<F>,


// Struct: Billing
// File: crates/hyperswitch_connectors/src/connectors/shift4/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Billing


// Struct: ThreeDsMethod
// File: crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ThreeDsMethod


// Struct: MerchantInitiatedTransaction
// File: crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MerchantInitiatedTransaction


// Struct: Paysafe
// File: crates/hyperswitch_connectors/src/connectors/paysafe.rs
// Module: hyperswitch_connectors
// Implementations: 19
// Traits: api::Payment, api::PaymentSession, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentToken, ConnectorCommon, ConnectorValidation, api::PaymentsPreProcessing, api::PaymentsCompleteAuthorize, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct Paysafe

api::Payment api::PaymentSession api::ConnectorAccessToken api::MandateSetup api::PaymentAuthorize api::PaymentSync api::PaymentCapture api::PaymentVoid api::Refund api::RefundExecute api::RefundSync api::PaymentToken ConnectorCommon ConnectorValidation api::PaymentsPreProcessing api::PaymentsCompleteAuthorize webhooks::IncomingWebhook ConnectorSpecifications


// Implementation: impl ConnectorTransactionIdTrait for for Capture
// File: crates/diesel_models/src/query/capture.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl ConnectorTransactionIdTrait for for Capture