File: crates/common_enums/src/connector_enums.rs
 Public functions: 16

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,
    Globalpay,
    Globepay,
    Gocardless,
    Hipay,
    Helcim,
    Iatapay,
    Inespay,
    Itaubank,
    Jpmorgan,
    Klarna,
    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,
    Phonepe,
    Placetopay,
    Powertranz,
    Prophetpay,
    Rapyd,
    Razorpay,
    Recurly,
    Redsys,
    Riskified,
    Santander,
    Shift4,
    Signifyd,
    Silverflow,
    Square,
    Stax,
    Stripe,
    Stripebilling,
    // Taxjar,
    Trustpay,
    // 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,
    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,
    Globalpay,
    Globepay,
    Gocardless,
    Gpayments,
    Hipay,
    Helcim,
    HyperswitchVault,
    // Hyperwallet, added as template code for future usage
    Inespay,
    Iatapay,
    Itaubank,
    Jpmorgan,
    Juspaythreedsserver,
    Klarna,
    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,
    Phonepe,
    Placetopay,
    Powertranz,
    Prophetpay,
    Rapyd,
    Razorpay,
    Recurly,
    Redsys,
    Santander,
    Shift4,
    Silverflow,
    Square,
    Stax,
    Stripe,
    Stripebilling,
    Taxjar,
    Threedsecureio,
    // Tokenio,
    //Thunes,
    Tokenio,
    Trustpay,
    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::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::Mifinity
            | Self::Mollie
            | Self::Moneris
            | Self::Multisafepay
            | Self::Nexinets
            | Self::Nexixpay
            | Self::Nomupay
            | Self::Nordea
            | Self::Novalnet
            | Self::Nuvei
            | Self::Opennode
            | Self::Paybox
            | Self::Payload
            | Self::Payme
            | Self::Payone
            | Self::Paypal
            | Self::Paysafe
            | Self::Paystack
            | Self::Payu
            | 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::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::CtpVisa
            | Self::Noon
            | Self::Tokenio
            | Self::Stripe
            | Self::Datatrans
            | Self::Paytm
            | Self::Phonepe => false,
            Self::Checkout | Self::Nmi |Self::Cybersource | Self::Archipel => 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 should_acknowledge_webhook_for_resource_not_found_errors(self) -> bool {
        matches!(self, Self::Adyenplatform)
    }

    /// 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::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::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::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::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::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::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::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::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 => Err("Invalid conversion. Not a routable connector"),
        }
    }
}


impl api::PaymentsCompleteAuthorize for Mollie {}

impl api::PaymentsCompleteAuthorize for for Mollie


File: crates/analytics/src/payments/metrics/sessionized_metrics/payments_distribution.rs

use std::collections::HashSet;

use api_models::analytics::{
    payments::{PaymentDimensions, PaymentFilters, PaymentMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::PaymentMetricRow;
use crate::{
    enums::AuthInfo,
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};

#[derive(Default)]
pub(crate) struct PaymentsDistribution;

#[async_trait::async_trait]
impl<T> super::PaymentMetric<T> for PaymentsDistribution
where
    T: AnalyticsDataSource + super::PaymentMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[PaymentDimensions],
        auth: &AuthInfo,
        filters: &PaymentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>> {
        let mut query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::PaymentSessionized);

        let mut dimensions = dimensions.to_vec();

        dimensions.push(PaymentDimensions::PaymentStatus);

        for dim in dimensions.iter() {
            query_builder.add_select_column(dim).switch()?;
        }

        query_builder
            .add_select_column(Aggregate::Count {
                field: None,
                alias: Some("count"),
            })
            .switch()?;

        query_builder.add_select_column("first_attempt").switch()?;

        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

        filters.set_filter_clause(&mut query_builder).switch()?;

        auth.set_filter_clause(&mut query_builder).switch()?;

        time_range
            .set_filter_clause(&mut query_builder)
            .attach_printable("Error filtering time range")
            .switch()?;

        for dim in dimensions.iter() {
            query_builder
                .add_group_by_clause(dim)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }
        query_builder
            .add_group_by_clause("first_attempt")
            .attach_printable("Error grouping by first_attempt")
            .switch()?;
        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .execute_query::<PaymentMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    PaymentMetricsBucketIdentifier::new(
                        i.currency.as_ref().map(|i| i.0),
                        None,
                        i.connector.clone(),
                        i.authentication_type.as_ref().map(|i| i.0),
                        i.payment_method.clone(),
                        i.payment_method_type.clone(),
                        i.client_source.clone(),
                        i.client_version.clone(),
                        i.profile_id.clone(),
                        i.card_network.clone(),
                        i.merchant_id.clone(),
                        i.card_last_4.clone(),
                        i.card_issuer.clone(),
                        i.error_reason.clone(),
                        i.routing_approach.as_ref().map(|i| i.0.clone()),
                        i.signature_network.clone(),
                        i.is_issuer_regulated,
                        i.is_debit_routed,
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


impl api::RefundSync for Dlocal {}


impl MerchantConnectorResponse {
    pub fn to_merchant_connector_info(&self, connector_label: &String) -> MerchantConnectorInfo {
        MerchantConnectorInfo {
            connector_label: connector_label.to_string(),
            merchant_connector_id: self.id.clone(),
        }
    }
}


impl ConnectorRedirectResponse for Airwallex {
    fn get_flow_type(
        &self,
        _query_params: &str,
        _json_payload: Option<serde_json::Value>,
        action: PaymentAction,
    ) -> CustomResult<CallConnectorAction, errors::ConnectorError> {
        match action {
            PaymentAction::PSync
            | PaymentAction::CompleteAuthorize
            | PaymentAction::PaymentAuthenticateCompleteAuthorize => {
                Ok(CallConnectorAction::Trigger)
            }
        }
    }
}

impl ConnectorRedirectResponse for for Airwallex


pub fn payments_capture() {}


File: crates/openapi/src/routes/customers.rs
 Public functions: 10

/// Customers - Create
///
/// Creates a customer object and stores the customer details to be reused for future payments.
/// Incase the customer already exists in the system, this API will respond with the customer details.
#[utoipa::path(
    post,
    path = "/customers",
    request_body  (
        content = CustomerRequest,
        examples  (( "Update name and email of a customer" =(
        value =json!( {
            "email": "guest@example.com",
            "name": "John Doe"
        })
        )))
    ),
    responses(
        (status = 200, description = "Customer Created", body = CustomerResponse),
        (status = 400, description = "Invalid data")

    ),
    tag = "Customers",
    operation_id = "Create a Customer",
    security(("api_key" = []))
)]
#[cfg(feature = "v1")]
pub async fn customers_create() {}

/// Customers - Retrieve
///
/// Retrieves a customer's details.
#[utoipa::path(
    get,
    path = "/customers/{customer_id}",
    params (("customer_id" = String, Path, description = "The unique identifier for the Customer")),
    responses(
        (status = 200, description = "Customer Retrieved", body = CustomerResponse),
        (status = 404, description = "Customer was not found")
    ),
    tag = "Customers",
    operation_id = "Retrieve a Customer",
    security(("api_key" = []), ("ephemeral_key" = []))
)]
#[cfg(feature = "v1")]
pub async fn customers_retrieve() {}

/// Customers - Update
///
/// Updates the customer's details in a customer object.
#[utoipa::path(
    post,
    path = "/customers/{customer_id}",
    request_body (
        content = CustomerUpdateRequest,
        examples  (( "Update name and email of a customer" =(
        value =json!( {
            "email": "guest@example.com",
            "name": "John Doe"
        })
        )))
    ),
    params (("customer_id" = String, Path, description = "The unique identifier for the Customer")),
    responses(
        (status = 200, description = "Customer was Updated", body = CustomerResponse),
        (status = 404, description = "Customer was not found")
    ),
    tag = "Customers",
    operation_id = "Update a Customer",
    security(("api_key" = []))
)]
#[cfg(feature = "v1")]
pub async fn customers_update() {}

/// Customers - Delete
///
/// Delete a customer record.
#[utoipa::path(
    delete,
    path = "/customers/{customer_id}",
    params (("customer_id" = String, Path, description = "The unique identifier for the Customer")),
    responses(
        (status = 200, description = "Customer was Deleted", body = CustomerDeleteResponse),
        (status = 404, description = "Customer was not found")
    ),
    tag = "Customers",
    operation_id = "Delete a Customer",
    security(("api_key" = []))
)]
#[cfg(feature = "v1")]
pub async fn customers_delete() {}

/// Customers - List
///
/// Lists all the customers for a particular merchant id.
#[utoipa::path(
    get,
    path = "/customers/list",
    params (("offset" = Option<u32>, Query, description = "Offset for pagination"),
    ("limit" = Option<u16>, Query, description = "Limit for pagination")),
    responses(
        (status = 200, description = "Customers retrieved", body = Vec<CustomerResponse>),
        (status = 400, description = "Invalid Data"),
    ),
    tag = "Customers",
    operation_id = "List all Customers for a Merchant",
    security(("api_key" = []))
)]
#[cfg(feature = "v1")]
pub async fn customers_list() {}

/// Customers - Create
///
/// Creates a customer object and stores the customer details to be reused for future payments.
/// Incase the customer already exists in the system, this API will respond with the customer details.
#[utoipa::path(
    post,
    path = "/v2/customers",
    request_body  (
        content = CustomerRequest,
        examples  (( "Create a customer with name and email" =(
        value =json!( {
            "email": "guest@example.com",
            "name": "John Doe"
        })
        )))
    ),
    responses(
        (status = 200, description = "Customer Created", body = CustomerResponse),
        (status = 400, description = "Invalid data")

    ),
    tag = "Customers",
    operation_id = "Create a Customer",
    security(("api_key" = []))
)]
#[cfg(feature = "v2")]
pub async fn customers_create() {}

/// Customers - Retrieve
///
/// Retrieves a customer's details.
#[utoipa::path(
    get,
    path = "/v2/customers/{id}",
    params (("id" = String, Path, description = "The unique identifier for the Customer")),
    responses(
        (status = 200, description = "Customer Retrieved", body = CustomerResponse),
        (status = 404, description = "Customer was not found")
    ),
    tag = "Customers",
    operation_id = "Retrieve a Customer",
    security(("api_key" = []), ("ephemeral_key" = []))
)]
#[cfg(feature = "v2")]
pub async fn customers_retrieve() {}

/// Customers - Update
///
/// Updates the customer's details in a customer object.
#[utoipa::path(
    post,
    path = "/v2/customers/{id}",
    request_body (
        content = CustomerUpdateRequest,
        examples  (( "Update name and email of a customer" =(
        value =json!( {
            "email": "guest@example.com",
            "name": "John Doe"
        })
        )))
    ),
    params (("id" = String, Path, description = "The unique identifier for the Customer")),
    responses(
        (status = 200, description = "Customer was Updated", body = CustomerResponse),
        (status = 404, description = "Customer was not found")
    ),
    tag = "Customers",
    operation_id = "Update a Customer",
    security(("api_key" = []))
)]
#[cfg(feature = "v2")]
pub async fn customers_update() {}

/// Customers - Delete
///
/// Delete a customer record.
#[utoipa::path(
    delete,
    path = "/v2/customers/{id}",
    params (("id" = String, Path, description = "The unique identifier for the Customer")),
    responses(
        (status = 200, description = "Customer was Deleted", body = CustomerDeleteResponse),
        (status = 404, description = "Customer was not found")
    ),
    tag = "Customers",
    operation_id = "Delete a Customer",
    security(("api_key" = []))
)]
#[cfg(feature = "v2")]
pub async fn customers_delete() {}

/// Customers - List
///
/// Lists all the customers for a particular merchant id.
#[utoipa::path(
    get,
    path = "/v2/customers/list",
    responses(
        (status = 200, description = "Customers retrieved", body = Vec<CustomerResponse>),
        (status = 400, description = "Invalid Data"),
    ),
    tag = "Customers",
    operation_id = "List all Customers for a Merchant",
    security(("api_key" = []))
)]
#[cfg(feature = "v2")]
pub async fn customers_list() {}


impl MerchantConnectorAccountTypeDetails {
    pub fn get_connector_account_details(
        &self,
    ) -> error_stack::Result<router_data::ConnectorAuthType, common_utils::errors::ParsingError>
    {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                merchant_connector_account
                    .connector_account_details
                    .peek()
                    .clone()
                    .parse_value("ConnectorAuthType")
            }
            Self::MerchantConnectorDetails(merchant_connector_details) => {
                merchant_connector_details
                    .merchant_connector_creds
                    .peek()
                    .clone()
                    .parse_value("ConnectorAuthType")
            }
        }
    }

    pub fn is_disabled(&self) -> bool {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                merchant_connector_account.disabled.unwrap_or(false)
            }
            Self::MerchantConnectorDetails(_) => false,
        }
    }

    pub fn get_metadata(&self) -> Option<Secret<Value>> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                merchant_connector_account.metadata.to_owned()
            }
            Self::MerchantConnectorDetails(_) => None,
        }
    }

    pub fn get_id(&self) -> Option<id_type::MerchantConnectorAccountId> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                Some(merchant_connector_account.id.clone())
            }
            Self::MerchantConnectorDetails(_) => None,
        }
    }

    pub fn get_mca_id(&self) -> Option<id_type::MerchantConnectorAccountId> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                Some(merchant_connector_account.get_id())
            }
            Self::MerchantConnectorDetails(_) => None,
        }
    }

    pub fn get_connector_name(&self) -> Option<common_enums::connector_enums::Connector> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                Some(merchant_connector_account.connector_name)
            }
            Self::MerchantConnectorDetails(merchant_connector_details) => {
                Some(merchant_connector_details.connector_name)
            }
        }
    }

    pub fn get_inner_db_merchant_connector_account(&self) -> Option<&MerchantConnectorAccount> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                Some(merchant_connector_account)
            }
            Self::MerchantConnectorDetails(_) => None,
        }
    }
}


impl AuthToken {
    pub async fn new_token(
        user_id: String,
        merchant_id: id_type::MerchantId,
        role_id: String,
        settings: &Settings,
        org_id: id_type::OrganizationId,
        profile_id: id_type::ProfileId,
        tenant_id: Option<id_type::TenantId>,
    ) -> UserResult<String> {
        let exp_duration = std::time::Duration::from_secs(consts::JWT_TOKEN_TIME_IN_SECS);
        let exp = jwt::generate_exp(exp_duration)?.as_secs();
        let token_payload = Self {
            user_id,
            merchant_id,
            role_id,
            exp,
            org_id,
            profile_id,
            tenant_id,
        };
        jwt::generate_jwt(&token_payload, settings).await
    }
}


pub struct PaymentSession;


pub struct PayoutsUpdateInternal {
    pub amount: Option<MinorUnit>,
    pub destination_currency: Option<storage_enums::Currency>,
    pub source_currency: Option<storage_enums::Currency>,
    pub description: Option<String>,
    pub recurring: Option<bool>,
    pub auto_fulfill: Option<bool>,
    pub return_url: Option<String>,
    pub entity_type: Option<storage_enums::PayoutEntityType>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub payout_method_id: Option<String>,
    pub profile_id: Option<id_type::ProfileId>,
    pub status: Option<storage_enums::PayoutStatus>,
    pub attempt_count: Option<i16>,
    pub confirm: Option<bool>,
    pub payout_type: Option<common_enums::PayoutType>,
    pub address_id: Option<String>,
    pub customer_id: Option<id_type::CustomerId>,
}


pub fn from_store(
        store: RouterStore<T>,
        drainer_stream_name: String,
        drainer_num_partitions: u8,
        ttl_for_kv: u32,
        soft_kill: Option<bool>,
    ) -> Self {
        let request_id = store.request_id.clone();

        Self {
            router_store: store,
            drainer_stream_name,
            drainer_num_partitions,
            ttl_for_kv,
            request_id,
            soft_kill_mode: soft_kill.unwrap_or(false),
        }
    }


File: crates/router/src/events/api_logs.rs
 Public functions: 1
 Public structs: 1

use actix_web::HttpRequest;
pub use common_utils::events::{ApiEventMetric, ApiEventsType};
use common_utils::impl_api_event_type;
use router_env::{tracing_actix_web::RequestId, types::FlowMetric};
use serde::Serialize;
use time::OffsetDateTime;

use super::EventType;
#[cfg(feature = "dummy_connector")]
use crate::routes::dummy_connector::types::{
    DummyConnectorPaymentCompleteRequest, DummyConnectorPaymentConfirmRequest,
    DummyConnectorPaymentRequest, DummyConnectorPaymentResponse,
    DummyConnectorPaymentRetrieveRequest, DummyConnectorRefundRequest,
    DummyConnectorRefundResponse, DummyConnectorRefundRetrieveRequest,
};
use crate::{
    core::payments::PaymentsRedirectResponseData,
    services::{authentication::AuthenticationType, kafka::KafkaMessage},
    types::api::{
        AttachEvidenceRequest, Config, ConfigUpdate, CreateFileRequest, DisputeFetchQueryData,
        DisputeId, FileId, FileRetrieveRequest, PollId,
    },
};

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct ApiEvent {
    tenant_id: common_utils::id_type::TenantId,
    merchant_id: Option<common_utils::id_type::MerchantId>,
    api_flow: String,
    created_at_timestamp: i128,
    request_id: String,
    latency: u128,
    status_code: i64,
    #[serde(flatten)]
    auth_type: AuthenticationType,
    request: String,
    user_agent: Option<String>,
    ip_addr: Option<String>,
    url_path: String,
    response: Option<String>,
    error: Option<serde_json::Value>,
    #[serde(flatten)]
    event_type: ApiEventsType,
    hs_latency: Option<u128>,
    http_method: String,
    #[serde(flatten)]
    infra_components: Option<serde_json::Value>,
}

impl ApiEvent {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        tenant_id: common_utils::id_type::TenantId,
        merchant_id: Option<common_utils::id_type::MerchantId>,
        api_flow: &impl FlowMetric,
        request_id: &RequestId,
        latency: u128,
        status_code: i64,
        request: serde_json::Value,
        response: Option<serde_json::Value>,
        hs_latency: Option<u128>,
        auth_type: AuthenticationType,
        error: Option<serde_json::Value>,
        event_type: ApiEventsType,
        http_req: &HttpRequest,
        http_method: &http::Method,
        infra_components: Option<serde_json::Value>,
    ) -> Self {
        Self {
            tenant_id,
            merchant_id,
            api_flow: api_flow.to_string(),
            created_at_timestamp: OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000_000,
            request_id: request_id.as_hyphenated().to_string(),
            latency,
            status_code,
            request: request.to_string(),
            response: response.map(|resp| resp.to_string()),
            auth_type,
            error,
            ip_addr: http_req
                .connection_info()
                .realip_remote_addr()
                .map(ToOwned::to_owned),
            user_agent: http_req
                .headers()
                .get("user-agent")
                .and_then(|user_agent_value| user_agent_value.to_str().ok().map(ToOwned::to_owned)),
            url_path: http_req.path().to_string(),
            event_type,
            hs_latency,
            http_method: http_method.to_string(),
            infra_components,
        }
    }
}

impl KafkaMessage for ApiEvent {
    fn event_type(&self) -> EventType {
        EventType::ApiLogs
    }

    fn key(&self) -> String {
        self.request_id.clone()
    }
}

impl_api_event_type!(
    Miscellaneous,
    (
        Config,
        CreateFileRequest,
        FileId,
        FileRetrieveRequest,
        AttachEvidenceRequest,
        DisputeFetchQueryData,
        ConfigUpdate
    )
);

#[cfg(feature = "dummy_connector")]
impl_api_event_type!(
    Miscellaneous,
    (
        DummyConnectorPaymentCompleteRequest,
        DummyConnectorPaymentRequest,
        DummyConnectorPaymentResponse,
        DummyConnectorPaymentRetrieveRequest,
        DummyConnectorPaymentConfirmRequest,
        DummyConnectorRefundRetrieveRequest,
        DummyConnectorRefundResponse,
        DummyConnectorRefundRequest
    )
);

#[cfg(feature = "v1")]
impl ApiEventMetric for PaymentsRedirectResponseData {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::PaymentRedirectionResponse {
            connector: self.connector.clone(),
            payment_id: match &self.resource_id {
                api_models::payments::PaymentIdType::PaymentIntentId(id) => Some(id.clone()),
                _ => None,
            },
        })
    }
}

#[cfg(feature = "v2")]
impl ApiEventMetric for PaymentsRedirectResponseData {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::PaymentRedirectionResponse {
            payment_id: self.payment_id.clone(),
        })
    }
}

impl ApiEventMetric for DisputeId {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Dispute {
            dispute_id: self.dispute_id.clone(),
        })
    }
}

impl ApiEventMetric for PollId {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Poll {
            poll_id: self.poll_id.clone(),
        })
    }
}


impl ConnectorRedirectResponse for Globalpay {
    fn get_flow_type(
        &self,
        _query_params: &str,
        _json_payload: Option<Value>,
        action: PaymentAction,
    ) -> CustomResult<CallConnectorAction, errors::ConnectorError> {
        match action {
            PaymentAction::PSync
            | PaymentAction::CompleteAuthorize
            | PaymentAction::PaymentAuthenticateCompleteAuthorize => {
                Ok(CallConnectorAction::Trigger)
            }
        }
    }
}

impl ConnectorRedirectResponse for for Globalpay


File: crates/router/src/core/debit_routing.rs
 Public functions: 4
 Public structs: 1

use std::{collections::HashSet, fmt::Debug};

use api_models::{enums as api_enums, open_router};
use common_enums::enums;
use common_utils::{
    errors::CustomResult, ext_traits::ValueExt, id_type, types::keymanager::KeyManagerState,
};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};

use super::{
    payments::{OperationSessionGetters, OperationSessionSetters},
    routing::TransactionData,
};
use crate::{
    core::{
        errors,
        payments::{operations::BoxedOperation, routing},
    },
    logger,
    routes::SessionState,
    settings,
    types::{
        api::{self, ConnectorCallType},
        domain,
    },
    utils::id_type::MerchantConnectorAccountId,
};

pub struct DebitRoutingResult {
    pub debit_routing_connector_call_type: ConnectorCallType,
    pub debit_routing_output: open_router::DebitRoutingOutput,
}

pub async fn perform_debit_routing<F, Req, D>(
    operation: &BoxedOperation<'_, F, Req, D>,
    state: &SessionState,
    business_profile: &domain::Profile,
    payment_data: &mut D,
    connector: Option<ConnectorCallType>,
) -> (
    Option<ConnectorCallType>,
    Option<open_router::DebitRoutingOutput>,
)
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let mut debit_routing_output = None;

    if should_execute_debit_routing(state, business_profile, operation, payment_data).await {
        let debit_routing_config = state.conf.debit_routing_config.clone();
        let debit_routing_supported_connectors = debit_routing_config.supported_connectors.clone();

        // If the business profile does not have a country set, we cannot perform debit routing,
        // because the merchant_business_country will be treated as the acquirer_country,
        // which is used to determine whether a transaction is local or global in the open router.
        // For now, since debit routing is only implemented for USD, we can safely assume the
        // acquirer_country is US if not provided by the merchant.

        let acquirer_country = business_profile
            .merchant_business_country
            .unwrap_or_default();

        if let Some(call_connector_type) = connector.clone() {
            debit_routing_output = match call_connector_type {
                ConnectorCallType::PreDetermined(connector_data) => {
                    logger::info!("Performing debit routing for PreDetermined connector");
                    handle_pre_determined_connector(
                        state,
                        debit_routing_supported_connectors,
                        &connector_data,
                        payment_data,
                        acquirer_country,
                    )
                    .await
                }
                ConnectorCallType::Retryable(connector_data) => {
                    logger::info!("Performing debit routing for Retryable connector");
                    handle_retryable_connector(
                        state,
                        debit_routing_supported_connectors,
                        connector_data,
                        payment_data,
                        acquirer_country,
                    )
                    .await
                }
                ConnectorCallType::SessionMultiple(_) => {
                    logger::info!(
                        "SessionMultiple connector type is not supported for debit routing"
                    );
                    None
                }
                #[cfg(feature = "v2")]
                ConnectorCallType::Skip => {
                    logger::info!("Skip connector type is not supported for debit routing");
                    None
                }
            };
        }
    }

    if let Some(debit_routing_output) = debit_routing_output {
        (
            Some(debit_routing_output.debit_routing_connector_call_type),
            Some(debit_routing_output.debit_routing_output),
        )
    } else {
        // If debit_routing_output is None, return the static routing output (connector)
        logger::info!("Debit routing is not performed, returning static routing output");
        (connector, None)
    }
}

async fn should_execute_debit_routing<F, Req, D>(
    state: &SessionState,
    business_profile: &domain::Profile,
    operation: &BoxedOperation<'_, F, Req, D>,
    payment_data: &D,
) -> bool
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    if business_profile.is_debit_routing_enabled && state.conf.open_router.dynamic_routing_enabled {
        logger::info!("Debit routing is enabled for the profile");

        let debit_routing_config = &state.conf.debit_routing_config;

        if should_perform_debit_routing_for_the_flow(operation, payment_data, debit_routing_config)
        {
            let is_debit_routable_connector_present = check_for_debit_routing_connector_in_profile(
                state,
                business_profile.get_id(),
                payment_data,
            )
            .await;

            if is_debit_routable_connector_present {
                logger::debug!("Debit routable connector is configured for the profile");
                return true;
            }
        }
    }
    false
}

pub fn should_perform_debit_routing_for_the_flow<Op: Debug, F: Clone, D>(
    operation: &Op,
    payment_data: &D,
    debit_routing_config: &settings::DebitRoutingConfig,
) -> bool
where
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    match format!("{operation:?}").as_str() {
        "PaymentConfirm" => {
            logger::info!("Checking if debit routing is required");

            request_validation(payment_data, debit_routing_config)
        }
        _ => false,
    }
}

fn request_validation<F: Clone, D>(
    payment_data: &D,
    debit_routing_config: &settings::DebitRoutingConfig,
) -> bool
where
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    let payment_intent = payment_data.get_payment_intent();
    let payment_attempt = payment_data.get_payment_attempt();

    let is_currency_supported = is_currency_supported(payment_intent, debit_routing_config);

    let is_valid_payment_method = validate_payment_method_for_debit_routing(payment_data);

    payment_intent.setup_future_usage != Some(enums::FutureUsage::OffSession)
        && payment_intent.amount.is_greater_than(0)
        && is_currency_supported
        && payment_attempt.authentication_type == Some(enums::AuthenticationType::NoThreeDs)
        && is_valid_payment_method
}

fn is_currency_supported(
    payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
    debit_routing_config: &settings::DebitRoutingConfig,
) -> bool {
    payment_intent
        .currency
        .map(|currency| {
            debit_routing_config
                .supported_currencies
                .contains(&currency)
        })
        .unwrap_or(false)
}

fn validate_payment_method_for_debit_routing<F: Clone, D>(payment_data: &D) -> bool
where
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    let payment_attempt = payment_data.get_payment_attempt();
    match payment_attempt.payment_method {
        Some(enums::PaymentMethod::Card) => {
            payment_attempt.payment_method_type == Some(enums::PaymentMethodType::Debit)
        }
        Some(enums::PaymentMethod::Wallet) => {
            payment_attempt.payment_method_type == Some(enums::PaymentMethodType::ApplePay)
                && payment_data
                    .get_payment_method_data()
                    .and_then(|data| data.get_wallet_data())
                    .and_then(|data| data.get_apple_pay_wallet_data())
                    .and_then(|data| data.get_payment_method_type())
                    == Some(enums::PaymentMethodType::Debit)
                && matches!(
                    payment_data.get_payment_method_token().cloned(),
                    Some(
                        hyperswitch_domain_models::router_data::PaymentMethodToken::ApplePayDecrypt(
                            _
                        )
                    )
                )
        }
        _ => false,
    }
}

pub async fn check_for_debit_routing_connector_in_profile<
    F: Clone,
    D: OperationSessionGetters<F>,
>(
    state: &SessionState,
    business_profile_id: &id_type::ProfileId,
    payment_data: &D,
) -> bool {
    logger::debug!("Checking for debit routing connector in profile");
    let debit_routing_supported_connectors =
        state.conf.debit_routing_config.supported_connectors.clone();

    let transaction_data = super::routing::PaymentsDslInput::new(
        payment_data.get_setup_mandate(),
        payment_data.get_payment_attempt(),
        payment_data.get_payment_intent(),
        payment_data.get_payment_method_data(),
        payment_data.get_address(),
        payment_data.get_recurring_details(),
        payment_data.get_currency(),
    );

    let fallback_config_optional = super::routing::helpers::get_merchant_default_config(
        &*state.clone().store,
        business_profile_id.get_string_repr(),
        &enums::TransactionType::from(&TransactionData::Payment(transaction_data)),
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .map_err(|error| {
        logger::warn!(?error, "Failed to fetch default connector for a profile");
    })
    .ok();

    let is_debit_routable_connector_present = fallback_config_optional
        .map(|fallback_config| {
            fallback_config.iter().any(|fallback_config_connector| {
                debit_routing_supported_connectors.contains(&api_enums::Connector::from(
                    fallback_config_connector.connector,
                ))
            })
        })
        .unwrap_or(false);

    is_debit_routable_connector_present
}

async fn handle_pre_determined_connector<F, D>(
    state: &SessionState,
    debit_routing_supported_connectors: HashSet<api_enums::Connector>,
    connector_data: &api::ConnectorRoutingData,
    payment_data: &mut D,
    acquirer_country: enums::CountryAlpha2,
) -> Option<DebitRoutingResult>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let db = state.store.as_ref();
    let key_manager_state = &(state).into();
    let merchant_id = payment_data.get_payment_attempt().merchant_id.clone();
    let profile_id = payment_data.get_payment_attempt().profile_id.clone();

    if debit_routing_supported_connectors.contains(&connector_data.connector_data.connector_name) {
        logger::debug!("Chosen connector is supported for debit routing");

        let debit_routing_output =
            get_debit_routing_output::<F, D>(state, payment_data, acquirer_country).await?;

        logger::debug!(
            "Sorted co-badged networks info: {:?}",
            debit_routing_output.co_badged_card_networks_info
        );

        let key_store = db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &merchant_id,
                &db.get_master_key().to_vec().into(),
            )
            .await
            .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)
            .map_err(|error| {
                logger::error!(
                    "Failed to get merchant key store by merchant_id  {:?}",
                    error
                )
            })
            .ok()?;

        let connector_routing_data = build_connector_routing_data(
            state,
            &profile_id,
            &key_store,
            vec![connector_data.clone()],
            debit_routing_output
                .co_badged_card_networks_info
                .clone()
                .get_card_networks(),
        )
        .await
        .map_err(|error| {
            logger::error!(
                "Failed to build connector routing data for debit routing {:?}",
                error
            )
        })
        .ok()?;

        if !connector_routing_data.is_empty() {
            return Some(DebitRoutingResult {
                debit_routing_connector_call_type: ConnectorCallType::Retryable(
                    connector_routing_data,
                ),
                debit_routing_output,
            });
        }
    }

    None
}

pub async fn get_debit_routing_output<
    F: Clone + Send,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
>(
    state: &SessionState,
    payment_data: &mut D,
    acquirer_country: enums::CountryAlpha2,
) -> Option<open_router::DebitRoutingOutput> {
    logger::debug!("Fetching sorted card networks");

    let card_info = extract_card_info(payment_data);

    let saved_co_badged_card_data = card_info.co_badged_card_data;
    let saved_card_type = card_info.card_type;
    let card_isin = card_info.card_isin;

    match (
        saved_co_badged_card_data
            .clone()
            .zip(saved_card_type.clone()),
        card_isin.clone(),
    ) {
        (None, None) => {
            logger::debug!("Neither co-badged data nor ISIN found; skipping routing");
            None
        }
        _ => {
            let co_badged_card_data = saved_co_badged_card_data
                .zip(saved_card_type)
                .and_then(|(co_badged, card_type)| {
                    open_router::DebitRoutingRequestData::try_from((co_badged, card_type))
                        .map(Some)
                        .map_err(|error| {
                            logger::warn!("Failed to convert co-badged card data: {:?}", error);
                        })
                        .ok()
                })
                .flatten();

            if co_badged_card_data.is_none() && card_isin.is_none() {
                logger::debug!("Neither co-badged data nor ISIN found; skipping routing");
                return None;
            }

            let co_badged_card_request = open_router::CoBadgedCardRequest {
                merchant_category_code: enums::DecisionEngineMerchantCategoryCode::Mcc0001,
                acquirer_country,
                co_badged_card_data,
            };

            routing::perform_open_routing_for_debit_routing(
                state,
                co_badged_card_request,
                card_isin,
                payment_data,
            )
            .await
            .map_err(|error| {
                logger::warn!(?error, "Debit routing call to open router failed");
            })
            .ok()
        }
    }
}

#[derive(Debug, Clone)]
struct ExtractedCardInfo {
    co_badged_card_data: Option<api_models::payment_methods::CoBadgedCardData>,
    card_type: Option<String>,
    card_isin: Option<Secret<String>>,
}

impl ExtractedCardInfo {
    fn new(
        co_badged_card_data: Option<api_models::payment_methods::CoBadgedCardData>,
        card_type: Option<String>,
        card_isin: Option<Secret<String>>,
    ) -> Self {
        Self {
            co_badged_card_data,
            card_type,
            card_isin,
        }
    }

    fn empty() -> Self {
        Self::new(None, None, None)
    }
}

fn extract_card_info<F, D>(payment_data: &D) -> ExtractedCardInfo
where
    D: OperationSessionGetters<F>,
{
    extract_from_saved_payment_method(payment_data)
        .unwrap_or_else(|| extract_from_payment_method_data(payment_data))
}

fn extract_from_saved_payment_method<F, D>(payment_data: &D) -> Option<ExtractedCardInfo>
where
    D: OperationSessionGetters<F>,
{
    let payment_methods_data = payment_data
        .get_payment_method_info()?
        .get_payment_methods_data()?;

    if let hyperswitch_domain_models::payment_method_data::PaymentMethodsData::Card(card) =
        payment_methods_data
    {
        return Some(extract_card_info_from_saved_card(&card));
    }

    None
}

fn extract_card_info_from_saved_card(
    card: &hyperswitch_domain_models::payment_method_data::CardDetailsPaymentMethod,
) -> ExtractedCardInfo {
    match (&card.co_badged_card_data, &card.card_isin) {
        (Some(co_badged), _) => {
            logger::debug!("Co-badged card data found in saved payment method");
            ExtractedCardInfo::new(Some(co_badged.clone()), card.card_type.clone(), None)
        }
        (None, Some(card_isin)) => {
            logger::debug!("No co-badged data; using saved card ISIN");
            ExtractedCardInfo::new(None, None, Some(Secret::new(card_isin.clone())))
        }
        _ => ExtractedCardInfo::empty(),
    }
}

fn extract_from_payment_method_data<F, D>(payment_data: &D) -> ExtractedCardInfo
where
    D: OperationSessionGetters<F>,
{
    match payment_data.get_payment_method_data() {
        Some(hyperswitch_domain_models::payment_method_data::PaymentMethodData::Card(card)) => {
            logger::debug!("Using card data from payment request");
            ExtractedCardInfo::new(
                None,
                None,
                Some(Secret::new(card.card_number.get_extended_card_bin())),
            )
        }
        Some(hyperswitch_domain_models::payment_method_data::PaymentMethodData::Wallet(
            wallet_data,
        )) => extract_from_wallet_data(wallet_data, payment_data),
        _ => ExtractedCardInfo::empty(),
    }
}

fn extract_from_wallet_data<F, D>(
    wallet_data: &hyperswitch_domain_models::payment_method_data::WalletData,
    payment_data: &D,
) -> ExtractedCardInfo
where
    D: OperationSessionGetters<F>,
{
    match wallet_data {
        hyperswitch_domain_models::payment_method_data::WalletData::ApplePay(_) => {
            logger::debug!("Using Apple Pay data from payment request");
            let apple_pay_isin = extract_apple_pay_isin(payment_data);
            ExtractedCardInfo::new(None, None, apple_pay_isin)
        }
        _ => ExtractedCardInfo::empty(),
    }
}

fn extract_apple_pay_isin<F, D>(payment_data: &D) -> Option<Secret<String>>
where
    D: OperationSessionGetters<F>,
{
    payment_data.get_payment_method_token().and_then(|token| {
        if let hyperswitch_domain_models::router_data::PaymentMethodToken::ApplePayDecrypt(
            apple_pay_decrypt_data,
        ) = token
        {
            logger::debug!("Using Apple Pay decrypt data from payment method token");
            Some(Secret::new(
                apple_pay_decrypt_data
                    .application_primary_account_number
                    .peek()
                    .chars()
                    .take(8)
                    .collect::<String>(),
            ))
        } else {
            None
        }
    })
}

async fn handle_retryable_connector<F, D>(
    state: &SessionState,
    debit_routing_supported_connectors: HashSet<api_enums::Connector>,
    connector_data_list: Vec<api::ConnectorRoutingData>,
    payment_data: &mut D,
    acquirer_country: enums::CountryAlpha2,
) -> Option<DebitRoutingResult>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let key_manager_state = &(state).into();
    let db = state.store.as_ref();
    let profile_id = payment_data.get_payment_attempt().profile_id.clone();
    let merchant_id = payment_data.get_payment_attempt().merchant_id.clone();
    let is_any_debit_routing_connector_supported =
        connector_data_list.iter().any(|connector_data| {
            debit_routing_supported_connectors
                .contains(&connector_data.connector_data.connector_name)
        });

    if is_any_debit_routing_connector_supported {
        let debit_routing_output =
            get_debit_routing_output::<F, D>(state, payment_data, acquirer_country).await?;
        let key_store = db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &merchant_id,
                &db.get_master_key().to_vec().into(),
            )
            .await
            .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)
            .map_err(|error| {
                logger::error!(
                    "Failed to get merchant key store by merchant_id  {:?}",
                    error
                )
            })
            .ok()?;

        let connector_routing_data = build_connector_routing_data(
            state,
            &profile_id,
            &key_store,
            connector_data_list.clone(),
            debit_routing_output
                .co_badged_card_networks_info
                .clone()
                .get_card_networks(),
        )
        .await
        .map_err(|error| {
            logger::error!(
                "Failed to build connector routing data for debit routing {:?}",
                error
            )
        })
        .ok()?;

        if !connector_routing_data.is_empty() {
            return Some(DebitRoutingResult {
                debit_routing_connector_call_type: ConnectorCallType::Retryable(
                    connector_routing_data,
                ),
                debit_routing_output,
            });
        };
    }

    None
}

async fn build_connector_routing_data(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    key_store: &domain::MerchantKeyStore,
    eligible_connector_data_list: Vec<api::ConnectorRoutingData>,
    fee_sorted_debit_networks: Vec<common_enums::CardNetwork>,
) -> CustomResult<Vec<api::ConnectorRoutingData>, errors::ApiErrorResponse> {
    let key_manager_state = &state.into();
    let debit_routing_config = &state.conf.debit_routing_config;

    let mcas_for_profile =
        fetch_merchant_connector_accounts(state, key_manager_state, profile_id, key_store).await?;

    let mut connector_routing_data = Vec::new();
    let mut has_us_local_network = false;

    for connector_data in eligible_connector_data_list {
        if let Some(routing_data) = process_connector_for_networks(
            &connector_data,
            &mcas_for_profile,
            &fee_sorted_debit_networks,
            debit_routing_config,
            &mut has_us_local_network,
        )? {
            connector_routing_data.extend(routing_data);
        }
    }

    validate_us_local_network_requirement(has_us_local_network)?;
    Ok(connector_routing_data)
}

/// Fetches merchant connector accounts for the given profile
async fn fetch_merchant_connector_accounts(
    state: &SessionState,
    key_manager_state: &KeyManagerState,
    profile_id: &id_type::ProfileId,
    key_store: &domain::MerchantKeyStore,
) -> CustomResult<Vec<domain::MerchantConnectorAccount>, errors::ApiErrorResponse> {
    state
        .store
        .list_enabled_connector_accounts_by_profile_id(
            key_manager_state,
            profile_id,
            key_store,
            common_enums::ConnectorType::PaymentProcessor,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to fetch merchant connector accounts")
}

/// Processes a single connector to find matching networks
fn process_connector_for_networks(
    connector_data: &api::ConnectorRoutingData,
    mcas_for_profile: &[domain::MerchantConnectorAccount],
    fee_sorted_debit_networks: &[common_enums::CardNetwork],
    debit_routing_config: &settings::DebitRoutingConfig,
    has_us_local_network: &mut bool,
) -> CustomResult<Option<Vec<api::ConnectorRoutingData>>, errors::ApiErrorResponse> {
    let Some(merchant_connector_id) = &connector_data.connector_data.merchant_connector_id else {
        logger::warn!("Skipping connector with missing merchant_connector_id");
        return Ok(None);
    };

    let Some(account) = find_merchant_connector_account(mcas_for_profile, merchant_connector_id)
    else {
        logger::warn!(
            "No MCA found for merchant_connector_id: {:?}",
            merchant_connector_id
        );
        return Ok(None);
    };

    let merchant_debit_networks = extract_debit_networks(&account)?;
    let matching_networks = find_matching_networks(
        &merchant_debit_networks,
        fee_sorted_debit_networks,
        &connector_data.connector_data,
        debit_routing_config,
        has_us_local_network,
    );

    Ok(Some(matching_networks))
}

/// Finds a merchant connector account by ID
fn find_merchant_connector_account(
    mcas: &[domain::MerchantConnectorAccount],
    merchant_connector_id: &MerchantConnectorAccountId,
) -> Option<domain::MerchantConnectorAccount> {
    mcas.iter()
        .find(|mca| mca.merchant_connector_id == *merchant_connector_id)
        .cloned()
}

/// Finds networks that match between merchant and fee-sorted networks
fn find_matching_networks(
    merchant_debit_networks: &HashSet<common_enums::CardNetwork>,
    fee_sorted_debit_networks: &[common_enums::CardNetwork],
    connector_data: &api::ConnectorData,
    debit_routing_config: &settings::DebitRoutingConfig,
    has_us_local_network: &mut bool,
) -> Vec<api::ConnectorRoutingData> {
    let is_routing_enabled = debit_routing_config
        .supported_connectors
        .contains(&connector_data.connector_name);

    fee_sorted_debit_networks
        .iter()
        .filter(|network| merchant_debit_networks.contains(network))
        .filter(|network| is_routing_enabled || network.is_signature_network())
        .map(|network| {
            if network.is_us_local_network() {
                *has_us_local_network = true;
            }

            api::ConnectorRoutingData {
                connector_data: connector_data.clone(),
                network: Some(network.clone()),
            }
        })
        .collect()
}

/// Validates that at least one US local network is present
fn validate_us_local_network_requirement(
    has_us_local_network: bool,
) -> CustomResult<(), errors::ApiErrorResponse> {
    if !has_us_local_network {
        return Err(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("At least one US local network is required in routing");
    }
    Ok(())
}

fn extract_debit_networks(
    account: &domain::MerchantConnectorAccount,
) -> CustomResult<HashSet<common_enums::CardNetwork>, errors::ApiErrorResponse> {
    let mut networks = HashSet::new();

    if let Some(values) = &account.payment_methods_enabled {
        for val in values {
            let payment_methods_enabled: api_models::admin::PaymentMethodsEnabled =
                val.to_owned().parse_value("PaymentMethodsEnabled")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to parse enabled payment methods for a merchant connector account in debit routing flow")?;

            if let Some(types) = payment_methods_enabled.payment_method_types {
                for method_type in types {
                    if method_type.payment_method_type
                        == api_models::enums::PaymentMethodType::Debit
                    {
                        if let Some(card_networks) = method_type.card_networks {
                            networks.extend(card_networks);
                        }
                    }
                }
            }
        }
    }

    Ok(networks)
}


pub async fn delete_card_from_hs_locker<'a>(
    state: &routes::SessionState,
    customer_id: &id_type::CustomerId,
    merchant_id: &id_type::MerchantId,
    card_reference: &'a str,
) -> errors::CustomResult<payment_methods::DeleteCardResp, errors::VaultError> {
    let locker = &state.conf.locker;
    let jwekey = &state.conf.jwekey.get_inner();

    let request = payment_methods::mk_delete_card_request_hs(
        jwekey,
        locker,
        customer_id,
        merchant_id,
        card_reference,
        state.tenant.tenant_id.clone(),
        state.request_id,
    )
    .await
    .change_context(errors::VaultError::DeleteCardFailed)
    .attach_printable("Making delete card request failed")?;

    if !locker.mock_locker {
        call_locker_api::<payment_methods::DeleteCardResp>(
            state,
            request,
            "delete_card_from_locker",
            Some(api_enums::LockerChoice::HyperswitchCardVault),
        )
        .await
        .change_context(errors::VaultError::DeleteCardFailed)
    } else {
        Ok(mock_delete_card_hs(&*state.store, card_reference)
            .await
            .change_context(errors::VaultError::DeleteCardFailed)?)
    }
}


impl api::Refund for Datatrans {}


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


impl fred::types::FromRedis for HsetnxReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            fred::types::RedisValue::Integer(1) => Ok(Self::KeySet),
            fred::types::RedisValue::Integer(0) => Ok(Self::KeyNotSet),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected HSETNX command reply",
            )),
        }
    }
}

impl fred::types::FromRedis for for HsetnxReply


impl Capturable for CompleteAuthorizeData {
    fn get_captured_amount<F>(
        &self,
        amount_captured: Option<i64>,
        payment_data: &PaymentData<F>,
    ) -> Option<i64>
    where
        F: Clone,
    {
        amount_captured.or(Some(
            payment_data
                .payment_attempt
                .get_total_amount()
                .get_amount_as_i64(),
        ))
    }
    fn get_amount_capturable<F>(
        &self,
        payment_data: &PaymentData<F>,
        amount_capturable: Option<i64>,
        attempt_status: common_enums::AttemptStatus,
    ) -> Option<i64>
    where
        F: Clone,
    {
        let amount_capturable_from_intent_status = match payment_data
            .get_capture_method()
            .unwrap_or_default()
        {
            common_enums::CaptureMethod::Automatic | common_enums::CaptureMethod::SequentialAutomatic => {
                let intent_status = common_enums::IntentStatus::foreign_from(attempt_status);
                match intent_status {
                    common_enums::IntentStatus::Succeeded
                    | common_enums::IntentStatus::Failed
                    | common_enums::IntentStatus::Conflicted
                    | common_enums::IntentStatus::Expired => Some(0),
                    common_enums::IntentStatus::Cancelled | common_enums::IntentStatus::PartiallyCaptured
                    | common_enums::IntentStatus::CancelledPostCapture
                    | common_enums::IntentStatus::RequiresCustomerAction
                    | common_enums::IntentStatus::RequiresMerchantAction
                    | common_enums::IntentStatus::RequiresPaymentMethod
                    | common_enums::IntentStatus::RequiresConfirmation
                    | common_enums::IntentStatus::RequiresCapture
                    | common_enums::IntentStatus::PartiallyCapturedAndCapturable
                    | common_enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture
                    | common_enums::IntentStatus::Processing => None,
                }
            },
            common_enums::CaptureMethod::Manual => Some(payment_data.payment_attempt.get_total_amount().get_amount_as_i64()),
            // In case of manual multiple, amount capturable must be inferred from all captures.
            common_enums::CaptureMethod::ManualMultiple |
            // Scheduled capture is not supported as of now
            common_enums::CaptureMethod::Scheduled => None,
        };
        amount_capturable
            .or(amount_capturable_from_intent_status)
            .or(Some(
                payment_data
                    .payment_attempt
                    .get_total_amount()
                    .get_amount_as_i64(),
            ))
    }
}

impl Capturable for for CompleteAuthorizeData


pub struct AdminApiAuthWithMerchantIdFromRoute(pub id_type::MerchantId);


pub struct PaymentMethodUpdateResponse {
    pub payment_method_id: String,
    pub status: Option<common_enums::PaymentMethodStatus>,
    pub network_transaction_id: Option<String>,
    pub connector_mandate_details: Option<pii::SecretSerdeValue>,
    pub update_status: UpdateStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub update_error: Option<String>,
    pub line_number: Option<i64>,
}