    SpardaBankWien,
    VolksbankGruppe,
    VolkskreditbankAg,
    VrBankBraunau,
    Moneyou,
    Rabobank,
    Regiobank,
    Revolut,
    SnsBank,
    TriodosBank,
    VanLanschot,
    PlusBank,
    EtransferPocztowy24,
    BankiSpbdzielcze,
    BankNowyBfgSa,
    GetinBank,
    Blik,
    NoblePay,
    #[serde(rename = "ideabank")]
    IdeaBank,
    #[serde(rename = "envelobank")]
    EnveloBank,
    NestPrzelew,
    MbankMtransfer,
    Inteligo,
    PbacZIpko,
    BnpParibas,
    BankPekaoSa,
    VolkswagenBank,
    AliorBank,
    Boz,
}

// This is used only for Disputes
impl From<WebhookEventStatus> for api_models::webhooks::IncomingWebhookEvent {
    fn from(value: WebhookEventStatus) -> Self {
        match value {
            WebhookEventStatus::WarningNeedsResponse => Self::DisputeOpened,
            WebhookEventStatus::WarningClosed => Self::DisputeCancelled,
            WebhookEventStatus::WarningUnderReview => Self::DisputeChallenged,
            WebhookEventStatus::Won => Self::DisputeWon,
            WebhookEventStatus::Lost => Self::DisputeLost,
            WebhookEventStatus::NeedsResponse
            | WebhookEventStatus::UnderReview
            | WebhookEventStatus::ChargeRefunded
            | WebhookEventStatus::Succeeded
            | WebhookEventStatus::RequiresPaymentMethod
            | WebhookEventStatus::RequiresConfirmation
            | WebhookEventStatus::RequiresAction
            | WebhookEventStatus::Processing
            | WebhookEventStatus::RequiresCapture
            | WebhookEventStatus::Canceled
            | WebhookEventStatus::Chargeable
            | WebhookEventStatus::Failed
            | WebhookEventStatus::Unknown => Self::EventNotSupported,
        }
    }
}

impl TryFrom<&enums::BankNames> for StripeBankNames {
    type Error = ConnectorError;
    fn try_from(bank: &enums::BankNames) -> Result<Self, Self::Error> {
        Ok(match bank {
            enums::BankNames::AbnAmro => Self::AbnAmro,
            enums::BankNames::ArzteUndApothekerBank => Self::ArzteUndApothekerBank,
            enums::BankNames::AsnBank => Self::AsnBank,
            enums::BankNames::AustrianAnadiBankAg => Self::AustrianAnadiBankAg,
            enums::BankNames::BankAustria => Self::BankAustria,
            enums::BankNames::BankhausCarlSpangler => Self::BankhausCarlSpangler,
            enums::BankNames::BankhausSchelhammerUndSchatteraAg => {
                Self::BankhausSchelhammerUndSchatteraAg
            }
            enums::BankNames::BawagPskAg => Self::BawagPskAg,
            enums::BankNames::BksBankAg => Self::BksBankAg,
            enums::BankNames::BrullKallmusBankAg => Self::BrullKallmusBankAg,
            enums::BankNames::BtvVierLanderBank => Self::BtvVierLanderBank,
            enums::BankNames::Bunq => Self::Bunq,
            enums::BankNames::CapitalBankGraweGruppeAg => Self::CapitalBankGraweGruppeAg,
            enums::BankNames::Citi => Self::CitiHandlowy,
            enums::BankNames::Dolomitenbank => Self::Dolomitenbank,
            enums::BankNames::EasybankAg => Self::EasybankAg,
            enums::BankNames::ErsteBankUndSparkassen => Self::ErsteBankUndSparkassen,
            enums::BankNames::Handelsbanken => Self::Handelsbanken,
            enums::BankNames::HypoAlpeadriabankInternationalAg => {
                Self::HypoAlpeadriabankInternationalAg
            }

            enums::BankNames::HypoNoeLbFurNiederosterreichUWien => {
                Self::HypoNoeLbFurNiederosterreichUWien
            }
            enums::BankNames::HypoOberosterreichSalzburgSteiermark => {
                Self::HypoOberosterreichSalzburgSteiermark
            }
            enums::BankNames::HypoTirolBankAg => Self::HypoTirolBankAg,
            enums::BankNames::HypoVorarlbergBankAg => Self::HypoVorarlbergBankAg,
            enums::BankNames::HypoBankBurgenlandAktiengesellschaft => {
                Self::HypoBankBurgenlandAktiengesellschaft
            }
            enums::BankNames::Ing => Self::Ing,
            enums::BankNames::Knab => Self::Knab,
            enums::BankNames::MarchfelderBank => Self::MarchfelderBank,
            enums::BankNames::OberbankAg => Self::OberbankAg,
            enums::BankNames::RaiffeisenBankengruppeOsterreich => {
                Self::RaiffeisenBankengruppeOsterreich
            }
            enums::BankNames::Rabobank => Self::Rabobank,
            enums::BankNames::Regiobank => Self::Regiobank,
            enums::BankNames::Revolut => Self::Revolut,
            enums::BankNames::SnsBank => Self::SnsBank,
            enums::BankNames::TriodosBank => Self::TriodosBank,
            enums::BankNames::VanLanschot => Self::VanLanschot,
            enums::BankNames::Moneyou => Self::Moneyou,
            enums::BankNames::SchoellerbankAg => Self::SchoellerbankAg,
            enums::BankNames::SpardaBankWien => Self::SpardaBankWien,
            enums::BankNames::VolksbankGruppe => Self::VolksbankGruppe,
            enums::BankNames::VolkskreditbankAg => Self::VolkskreditbankAg,
            enums::BankNames::VrBankBraunau => Self::VrBankBraunau,
            enums::BankNames::PlusBank => Self::PlusBank,
            enums::BankNames::EtransferPocztowy24 => Self::EtransferPocztowy24,
            enums::BankNames::BankiSpbdzielcze => Self::BankiSpbdzielcze,
            enums::BankNames::BankNowyBfgSa => Self::BankNowyBfgSa,
            enums::BankNames::GetinBank => Self::GetinBank,
            enums::BankNames::Blik => Self::Blik,
            enums::BankNames::NoblePay => Self::NoblePay,
            enums::BankNames::IdeaBank => Self::IdeaBank,
            enums::BankNames::EnveloBank => Self::EnveloBank,
            enums::BankNames::NestPrzelew => Self::NestPrzelew,
            enums::BankNames::MbankMtransfer => Self::MbankMtransfer,
            enums::BankNames::Inteligo => Self::Inteligo,
            enums::BankNames::PbacZIpko => Self::PbacZIpko,
            enums::BankNames::BnpParibas => Self::BnpParibas,
            enums::BankNames::BankPekaoSa => Self::BankPekaoSa,
            enums::BankNames::VolkswagenBank => Self::VolkswagenBank,
            enums::BankNames::AliorBank => Self::AliorBank,
            enums::BankNames::Boz => Self::Boz,

            _ => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            ))?,
        })
    }
}

fn validate_shipping_address_against_payment_method(
    shipping_address: &Option<StripeShippingAddress>,
    payment_method: Option<&StripePaymentMethodType>,
) -> Result<(), error_stack::Report<ConnectorError>> {
    match payment_method {
        Some(StripePaymentMethodType::AfterpayClearpay) => match shipping_address {
            Some(address) => {
                let missing_fields = collect_missing_value_keys!(
                    ("shipping.address.line1", address.line1),
                    ("shipping.address.country", address.country),
                    ("shipping.address.zip", address.zip)
                );

                if !missing_fields.is_empty() {
                    return Err(ConnectorError::MissingRequiredFields {
                        field_names: missing_fields,
                    }
                    .into());
                }
                Ok(())
            }
            None => Err(ConnectorError::MissingRequiredField {
                field_name: "shipping.address",
            }
            .into()),
        },
        _ => Ok(()),
    }
}

impl TryFrom<&PayLaterData> for StripePaymentMethodType {
    type Error = ConnectorError;
    fn try_from(pay_later_data: &PayLaterData) -> Result<Self, Self::Error> {
        match pay_later_data {
            PayLaterData::KlarnaRedirect { .. } => Ok(Self::Klarna),
            PayLaterData::AffirmRedirect {} => Ok(Self::Affirm),
            PayLaterData::AfterpayClearpayRedirect { .. } => Ok(Self::AfterpayClearpay),

            PayLaterData::KlarnaSdk { .. }
            | PayLaterData::PayBrightRedirect {}
            | PayLaterData::WalleyRedirect {}
            | PayLaterData::AlmaRedirect {}
            | PayLaterData::FlexitiRedirect { .. }
            | PayLaterData::AtomeRedirect {}
            | PayLaterData::BreadpayRedirect {} => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )),
        }
    }
}

impl TryFrom<&BankRedirectData> for StripePaymentMethodType {
    type Error = ConnectorError;
    fn try_from(bank_redirect_data: &BankRedirectData) -> Result<Self, Self::Error> {
        match bank_redirect_data {
            BankRedirectData::Giropay { .. } => Ok(Self::Giropay),
            BankRedirectData::Ideal { .. } => Ok(Self::Ideal),
            BankRedirectData::Sofort { .. } => Ok(Self::Sofort),
            BankRedirectData::BancontactCard { .. } => Ok(Self::Bancontact),
            BankRedirectData::Przelewy24 { .. } => Ok(Self::Przelewy24),
            BankRedirectData::Eps { .. } => Ok(Self::Eps),
            BankRedirectData::Blik { .. } => Ok(Self::Blik),
            BankRedirectData::OnlineBankingFpx { .. } => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )),
            BankRedirectData::Bizum {}
            | BankRedirectData::Interac { .. }
            | BankRedirectData::Eft { .. }
            | BankRedirectData::OnlineBankingCzechRepublic { .. }
            | BankRedirectData::OnlineBankingFinland { .. }
            | BankRedirectData::OnlineBankingPoland { .. }
            | BankRedirectData::OnlineBankingSlovakia { .. }
            | BankRedirectData::OnlineBankingThailand { .. }
            | BankRedirectData::OpenBankingUk { .. }
            | BankRedirectData::Trustly { .. }
            | BankRedirectData::LocalBankRedirect {} => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )),
        }
    }
}

fn get_stripe_payment_method_type_from_wallet_data(
    wallet_data: &WalletData,
) -> Result<Option<StripePaymentMethodType>, ConnectorError> {
    match wallet_data {
        WalletData::AliPayRedirect(_) => Ok(Some(StripePaymentMethodType::Alipay)),
        WalletData::ApplePay(_) => Ok(None),
        WalletData::GooglePay(_) => Ok(Some(StripePaymentMethodType::Card)),
        WalletData::WeChatPayQr(_) => Ok(Some(StripePaymentMethodType::Wechatpay)),
        WalletData::CashappQr(_) => Ok(Some(StripePaymentMethodType::Cashapp)),
        WalletData::AmazonPayRedirect(_) => Ok(Some(StripePaymentMethodType::AmazonPay)),
        WalletData::RevolutPay(_) => Ok(Some(StripePaymentMethodType::RevolutPay)),
        WalletData::MobilePayRedirect(_) => Err(ConnectorError::NotImplemented(
            get_unimplemented_payment_method_error_message("stripe"),
        )),
        WalletData::PaypalRedirect(_)
        | WalletData::AliPayQr(_)
        | WalletData::BluecodeRedirect {}
        | WalletData::Paysera(_)
        | WalletData::Skrill(_)
        | WalletData::AmazonPay(_)
        | WalletData::AliPayHkRedirect(_)
        | WalletData::MomoRedirect(_)
        | WalletData::KakaoPayRedirect(_)
        | WalletData::GoPayRedirect(_)
        | WalletData::GcashRedirect(_)
        | WalletData::ApplePayRedirect(_)
        | WalletData::ApplePayThirdPartySdk(_)
        | WalletData::DanaRedirect {}
        | WalletData::GooglePayRedirect(_)
        | WalletData::GooglePayThirdPartySdk(_)
        | WalletData::MbWayRedirect(_)
        | WalletData::PaypalSdk(_)
        | WalletData::Paze(_)
        | WalletData::SamsungPay(_)
        | WalletData::TwintRedirect {}
        | WalletData::VippsRedirect {}
        | WalletData::TouchNGoRedirect(_)
        | WalletData::SwishQr(_)
        | WalletData::WeChatPayRedirect(_)
        | WalletData::Mifinity(_) => Err(ConnectorError::NotImplemented(
            get_unimplemented_payment_method_error_message("stripe"),
        )),
    }
}

impl From<&payment_method_data::BankDebitData> for StripePaymentMethodType {
    fn from(bank_debit_data: &payment_method_data::BankDebitData) -> Self {
        match bank_debit_data {
            payment_method_data::BankDebitData::AchBankDebit { .. } => Self::Ach,
            payment_method_data::BankDebitData::SepaBankDebit { .. } => Self::Sepa,
            payment_method_data::BankDebitData::BecsBankDebit { .. } => Self::Becs,
            payment_method_data::BankDebitData::BacsBankDebit { .. } => Self::Bacs,
        }
    }
}

fn get_bank_debit_data(
    bank_debit_data: &payment_method_data::BankDebitData,
) -> (StripePaymentMethodType, BankDebitData) {
    match bank_debit_data {
        payment_method_data::BankDebitData::AchBankDebit {
            account_number,
            routing_number,
            ..
        } => {
            let ach_data = BankDebitData::Ach {
                account_holder_type: "individual".to_string(),
                account_number: account_number.to_owned(),
                routing_number: routing_number.to_owned(),
            };
            (StripePaymentMethodType::Ach, ach_data)
        }
        payment_method_data::BankDebitData::SepaBankDebit { iban, .. } => {
            let sepa_data: BankDebitData = BankDebitData::Sepa {
                iban: iban.to_owned(),
            };
            (StripePaymentMethodType::Sepa, sepa_data)
        }
        payment_method_data::BankDebitData::BecsBankDebit {
            account_number,
            bsb_number,
            ..
        } => {
            let becs_data = BankDebitData::Becs {
                account_number: account_number.to_owned(),
                bsb_number: bsb_number.to_owned(),
            };
            (StripePaymentMethodType::Becs, becs_data)
        }
        payment_method_data::BankDebitData::BacsBankDebit {
            account_number,
            sort_code,
            ..
        } => {
            let bacs_data = BankDebitData::Bacs {
                account_number: account_number.to_owned(),
                sort_code: Secret::new(sort_code.clone().expose().replace('-', "")),
            };
            (StripePaymentMethodType::Bacs, bacs_data)
        }
    }
}

pub struct PaymentRequestDetails {
    pub auth_type: enums::AuthenticationType,
    pub payment_method_token: Option<PaymentMethodToken>,
    pub is_customer_initiated_mandate_payment: Option<bool>,
    pub billing_address: StripeBillingAddress,
    pub request_incremental_authorization: bool,
    pub request_extended_authorization:
        Option<primitive_wrappers::RequestExtendedAuthorizationBool>,
    pub request_overcapture: Option<StripeRequestOvercaptureBool>,
}

fn create_stripe_payment_method(
    payment_method_data: &PaymentMethodData,
    payment_request_details: PaymentRequestDetails,
) -> Result<
    (
        StripePaymentMethodData,
        Option<StripePaymentMethodType>,
        StripeBillingAddress,
    ),
    error_stack::Report<ConnectorError>,
> {
    match payment_method_data {
        PaymentMethodData::Card(card_details) => {
            let payment_method_auth_type = match payment_request_details.auth_type {
                enums::AuthenticationType::ThreeDs => Auth3ds::Any,
                enums::AuthenticationType::NoThreeDs => Auth3ds::Automatic,
            };
            Ok((
                StripePaymentMethodData::try_from((
                    card_details,
                    payment_method_auth_type,
                    payment_request_details.request_incremental_authorization,
                    payment_request_details.request_extended_authorization,
                    payment_request_details.request_overcapture,
                ))?,
                Some(StripePaymentMethodType::Card),
                payment_request_details.billing_address,
            ))
        }
        PaymentMethodData::PayLater(pay_later_data) => {
            let stripe_pm_type = StripePaymentMethodType::try_from(pay_later_data)?;

            Ok((
                StripePaymentMethodData::PayLater(StripePayLaterData {
                    payment_method_data_type: stripe_pm_type,
                }),
                Some(stripe_pm_type),
                payment_request_details.billing_address,
            ))
        }
        PaymentMethodData::BankRedirect(bank_redirect_data) => {
            let billing_address =
                if payment_request_details.is_customer_initiated_mandate_payment == Some(true) {
                    mandatory_parameters_for_sepa_bank_debit_mandates(
                        &Some(payment_request_details.billing_address.to_owned()),
                        payment_request_details.is_customer_initiated_mandate_payment,
                    )?
                } else {
                    payment_request_details.billing_address
                };
            let pm_type = StripePaymentMethodType::try_from(bank_redirect_data)?;
            let bank_redirect_data = StripePaymentMethodData::try_from(bank_redirect_data)?;

            Ok((bank_redirect_data, Some(pm_type), billing_address))
        }
        PaymentMethodData::Wallet(wallet_data) => {
            let pm_type = get_stripe_payment_method_type_from_wallet_data(wallet_data)?;
            let wallet_specific_data = StripePaymentMethodData::try_from((
                wallet_data,
                payment_request_details.payment_method_token,
            ))?;
            Ok((
                wallet_specific_data,
                pm_type,
                StripeBillingAddress::default(),
            ))
        }
        PaymentMethodData::BankDebit(bank_debit_data) => {
            let (pm_type, bank_debit_data) = get_bank_debit_data(bank_debit_data);

            let pm_data = StripePaymentMethodData::BankDebit(StripeBankDebitData {
                bank_specific_data: bank_debit_data,
            });

            Ok((
                pm_data,
                Some(pm_type),
                payment_request_details.billing_address,
            ))
        }
        PaymentMethodData::BankTransfer(bank_transfer_data) => match bank_transfer_data.deref() {
            payment_method_data::BankTransferData::AchBankTransfer {} => Ok((
                StripePaymentMethodData::BankTransfer(StripeBankTransferData::AchBankTransfer(
                    Box::new(AchTransferData {
                        payment_method_data_type: StripePaymentMethodType::CustomerBalance,
                        bank_transfer_type: StripeCreditTransferTypes::AchCreditTransfer,
                        payment_method_type: StripePaymentMethodType::CustomerBalance,
                        balance_funding_type: BankTransferType::BankTransfers,
                    }),
                )),
                None,
                StripeBillingAddress::default(),
            )),
            payment_method_data::BankTransferData::MultibancoBankTransfer {} => Ok((
                StripePaymentMethodData::BankTransfer(
                    StripeBankTransferData::MultibancoBankTransfers(Box::new(
                        MultibancoTransferData {
                            payment_method_data_type: StripeCreditTransferTypes::Multibanco,
                            payment_method_type: StripeCreditTransferTypes::Multibanco,
                            email: payment_request_details.billing_address.email.ok_or(
                                ConnectorError::MissingRequiredField {
                                    field_name: "billing_address.email",
                                },
                            )?,
                        },
                    )),
                ),
                None,
                StripeBillingAddress::default(),
            )),
            payment_method_data::BankTransferData::SepaBankTransfer {} => Ok((
                StripePaymentMethodData::BankTransfer(StripeBankTransferData::SepaBankTransfer(
                    Box::new(SepaBankTransferData {
                        payment_method_data_type: StripePaymentMethodType::CustomerBalance,
                        bank_transfer_type: BankTransferType::EuBankTransfer,
                        balance_funding_type: BankTransferType::BankTransfers,
                        payment_method_type: StripePaymentMethodType::CustomerBalance,
                        country: payment_request_details.billing_address.country.ok_or(
                            ConnectorError::MissingRequiredField {
                                field_name: "billing_address.country",
                            },
                        )?,
                    }),
                )),
                Some(StripePaymentMethodType::CustomerBalance),
                payment_request_details.billing_address,
            )),
            payment_method_data::BankTransferData::BacsBankTransfer {} => Ok((
                StripePaymentMethodData::BankTransfer(StripeBankTransferData::BacsBankTransfers(
                    Box::new(BacsBankTransferData {
                        payment_method_data_type: StripePaymentMethodType::CustomerBalance,
                        bank_transfer_type: BankTransferType::GbBankTransfer,
                        balance_funding_type: BankTransferType::BankTransfers,
                        payment_method_type: StripePaymentMethodType::CustomerBalance,
                    }),
                )),
                Some(StripePaymentMethodType::CustomerBalance),
                payment_request_details.billing_address,
            )),
            payment_method_data::BankTransferData::Pix { .. } => Err(
                ConnectorError::NotImplemented(get_unimplemented_payment_method_error_message(
                    "stripe",
                ))
                .into(),
            ),
            payment_method_data::BankTransferData::Pse {}
            | payment_method_data::BankTransferData::LocalBankTransfer { .. }
            | payment_method_data::BankTransferData::InstantBankTransfer {}
            | payment_method_data::BankTransferData::InstantBankTransferFinland { .. }
            | payment_method_data::BankTransferData::InstantBankTransferPoland { .. }
            | payment_method_data::BankTransferData::PermataBankTransfer { .. }
            | payment_method_data::BankTransferData::BcaBankTransfer { .. }
            | payment_method_data::BankTransferData::BniVaBankTransfer { .. }
            | payment_method_data::BankTransferData::BriVaBankTransfer { .. }
            | payment_method_data::BankTransferData::CimbVaBankTransfer { .. }
            | payment_method_data::BankTransferData::DanamonVaBankTransfer { .. }
            | payment_method_data::BankTransferData::IndonesianBankTransfer { .. }
            | payment_method_data::BankTransferData::MandiriVaBankTransfer { .. } => Err(
                ConnectorError::NotImplemented(get_unimplemented_payment_method_error_message(
                    "stripe",
                ))
                .into(),
            ),
        },
        PaymentMethodData::Crypto(_) => Err(ConnectorError::NotImplemented(
            get_unimplemented_payment_method_error_message("stripe"),
        )
        .into()),

        PaymentMethodData::GiftCard(giftcard_data) => match giftcard_data.deref() {
            GiftCardData::Givex(_)
            | GiftCardData::PaySafeCard {}
            | GiftCardData::BhnCardNetwork(_) => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
        },
        PaymentMethodData::CardRedirect(cardredirect_data) => match cardredirect_data {
            CardRedirectData::Knet {}
            | CardRedirectData::Benefit {}
            | CardRedirectData::MomoAtm {}
            | CardRedirectData::CardRedirect {} => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
        },
        PaymentMethodData::Reward => Err(ConnectorError::NotImplemented(
            get_unimplemented_payment_method_error_message("stripe"),
        )
        .into()),

        PaymentMethodData::Voucher(voucher_data) => match voucher_data {
            VoucherData::Boleto(_) | VoucherData::Oxxo => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
            VoucherData::Alfamart(_)
            | VoucherData::Efecty
            | VoucherData::PagoEfectivo
            | VoucherData::RedCompra
            | VoucherData::RedPagos
            | VoucherData::Indomaret(_)
            | VoucherData::SevenEleven(_)
            | VoucherData::Lawson(_)
            | VoucherData::MiniStop(_)
            | VoucherData::FamilyMart(_)
            | VoucherData::Seicomart(_)
            | VoucherData::PayEasy(_) => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
        },

        PaymentMethodData::Upi(_)
        | PaymentMethodData::RealTimePayment(_)
        | PaymentMethodData::MobilePayment(_)
        | PaymentMethodData::MandatePayment
        | PaymentMethodData::OpenBanking(_)
        | PaymentMethodData::CardToken(_)
        | PaymentMethodData::NetworkToken(_)
        | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => Err(
            ConnectorError::NotImplemented(get_unimplemented_payment_method_error_message(
                "stripe",
            ))
            .into(),
        ),
    }
}

fn get_stripe_card_network(card_network: common_enums::CardNetwork) -> Option<StripeCardNetwork> {
    match card_network {
        common_enums::CardNetwork::Visa => Some(StripeCardNetwork::Visa),
        common_enums::CardNetwork::Mastercard => Some(StripeCardNetwork::Mastercard),
        common_enums::CardNetwork::CartesBancaires => Some(StripeCardNetwork::CartesBancaires),
        common_enums::CardNetwork::AmericanExpress
        | common_enums::CardNetwork::JCB
        | common_enums::CardNetwork::DinersClub
        | common_enums::CardNetwork::Discover
        | common_enums::CardNetwork::UnionPay
        | common_enums::CardNetwork::Interac
        | common_enums::CardNetwork::RuPay
        | common_enums::CardNetwork::Maestro
        | common_enums::CardNetwork::Star
        | common_enums::CardNetwork::Accel
        | common_enums::CardNetwork::Pulse
        | common_enums::CardNetwork::Nyce => None,
    }
}

impl
    TryFrom<(
        &Card,
        Auth3ds,
        bool,
        Option<primitive_wrappers::RequestExtendedAuthorizationBool>,
        Option<StripeRequestOvercaptureBool>,
    )> for StripePaymentMethodData
{
    type Error = ConnectorError;
    fn try_from(
        (
            card,
            payment_method_auth_type,
            request_incremental_authorization,
            request_extended_authorization,
            request_overcapture,
        ): (
            &Card,
            Auth3ds,
            bool,
            Option<primitive_wrappers::RequestExtendedAuthorizationBool>,
            Option<StripeRequestOvercaptureBool>,
        ),
    ) -> Result<Self, Self::Error> {
        Ok(Self::Card(StripeCardData {
            payment_method_data_type: StripePaymentMethodType::Card,
            payment_method_data_card_number: card.card_number.clone(),
            payment_method_data_card_exp_month: card.card_exp_month.clone(),
            payment_method_data_card_exp_year: card.card_exp_year.clone(),
            payment_method_data_card_cvc: Some(card.card_cvc.clone()),
            payment_method_auth_type: Some(payment_method_auth_type),
            payment_method_data_card_preferred_network: card
                .card_network
                .clone()
                .and_then(get_stripe_card_network),
            request_incremental_authorization: if request_incremental_authorization {
                Some(StripeRequestIncrementalAuthorization::IfAvailable)
            } else {
                None
            },
            request_extended_authorization: if request_extended_authorization
                .map(|request_extended_authorization| request_extended_authorization.is_true())
                .unwrap_or(false)
            {
                Some(StripeRequestExtendedAuthorization::IfAvailable)
            } else {
                None
            },
            request_overcapture,
        }))
    }
}

impl TryFrom<(&WalletData, Option<PaymentMethodToken>)> for StripePaymentMethodData {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        (wallet_data, payment_method_token): (&WalletData, Option<PaymentMethodToken>),
    ) -> Result<Self, Self::Error> {
        match wallet_data {
            WalletData::ApplePay(applepay_data) => {
                let mut apple_pay_decrypt_data =
                    if let Some(PaymentMethodToken::ApplePayDecrypt(decrypt_data)) =
                        payment_method_token
                    {
                        let expiry_year_4_digit = decrypt_data.get_four_digit_expiry_year();
                        Some(Self::Wallet(StripeWallet::ApplePayPredecryptToken(
                            Box::new(StripeApplePayPredecrypt {
                                number: decrypt_data.clone().application_primary_account_number,
                                exp_year: expiry_year_4_digit,
                                exp_month: decrypt_data.application_expiration_month,
                                eci: decrypt_data.payment_data.eci_indicator,
                                cryptogram: decrypt_data.payment_data.online_payment_cryptogram,
                                tokenization_method: "apple_pay".to_string(),
                            }),
                        )))
                    } else {
                        None
                    };

                if apple_pay_decrypt_data.is_none() {
                    apple_pay_decrypt_data =
                        Some(Self::Wallet(StripeWallet::ApplepayToken(StripeApplePay {
                            pk_token: applepay_data.get_applepay_decoded_payment_data()?,
                            pk_token_instrument_name: applepay_data
                                .payment_method
                                .pm_type
                                .to_owned(),
                            pk_token_payment_network: applepay_data
                                .payment_method
                                .network
                                .to_owned(),
                            pk_token_transaction_id: Secret::new(
                                applepay_data.transaction_identifier.to_owned(),
                            ),
                        })));
                };
                let pmd = apple_pay_decrypt_data.ok_or(ConnectorError::MissingApplePayTokenData)?;
                Ok(pmd)
            }
            WalletData::WeChatPayQr(_) => Ok(Self::Wallet(StripeWallet::WechatpayPayment(
                WechatpayPayment {
                    client: WechatClient::Web,
                    payment_method_data_type: StripePaymentMethodType::Wechatpay,
                },
            ))),
            WalletData::AliPayRedirect(_) => {
                Ok(Self::Wallet(StripeWallet::AlipayPayment(AlipayPayment {
                    payment_method_data_type: StripePaymentMethodType::Alipay,
                })))
            }
            WalletData::CashappQr(_) => Ok(Self::Wallet(StripeWallet::Cashapp(CashappPayment {
                payment_method_data_type: StripePaymentMethodType::Cashapp,
            }))),
            WalletData::AmazonPayRedirect(_) => Ok(Self::Wallet(StripeWallet::AmazonpayPayment(
                AmazonpayPayment {
                    payment_method_types: StripePaymentMethodType::AmazonPay,
                },
            ))),
            WalletData::RevolutPay(_) => {
                Ok(Self::Wallet(StripeWallet::RevolutPay(RevolutpayPayment {
                    payment_method_types: StripePaymentMethodType::RevolutPay,
                })))
            }
            WalletData::GooglePay(gpay_data) => Ok(Self::try_from(gpay_data)?),
            WalletData::PaypalRedirect(_) | WalletData::MobilePayRedirect(_) => Err(
                ConnectorError::NotImplemented(get_unimplemented_payment_method_error_message(
                    "stripe",
                ))
                .into(),
            ),
            WalletData::AliPayQr(_)
            | WalletData::Paysera(_)
            | WalletData::BluecodeRedirect {}
            | WalletData::Skrill(_)
            | WalletData::AmazonPay(_)
            | WalletData::AliPayHkRedirect(_)
            | WalletData::MomoRedirect(_)
            | WalletData::KakaoPayRedirect(_)
            | WalletData::GoPayRedirect(_)
            | WalletData::GcashRedirect(_)
            | WalletData::ApplePayRedirect(_)
            | WalletData::ApplePayThirdPartySdk(_)
            | WalletData::DanaRedirect {}
            | WalletData::GooglePayRedirect(_)
            | WalletData::GooglePayThirdPartySdk(_)
            | WalletData::MbWayRedirect(_)
            | WalletData::PaypalSdk(_)
            | WalletData::Paze(_)
            | WalletData::SamsungPay(_)
            | WalletData::TwintRedirect {}
            | WalletData::VippsRedirect {}
            | WalletData::TouchNGoRedirect(_)
            | WalletData::SwishQr(_)
            | WalletData::WeChatPayRedirect(_)
            | WalletData::Mifinity(_) => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
        }
    }
}

impl TryFrom<&BankRedirectData> for StripePaymentMethodData {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(bank_redirect_data: &BankRedirectData) -> Result<Self, Self::Error> {
        let payment_method_data_type = StripePaymentMethodType::try_from(bank_redirect_data)?;
        match bank_redirect_data {
            BankRedirectData::BancontactCard { .. } => Ok(Self::BankRedirect(
                StripeBankRedirectData::StripeBancontactCard(Box::new(StripeBancontactCard {
                    payment_method_data_type,
                })),
            )),
            BankRedirectData::Blik { blik_code } => Ok(Self::BankRedirect(
                StripeBankRedirectData::StripeBlik(Box::new(StripeBlik {
                    payment_method_data_type,
                    code: Secret::new(blik_code.clone().ok_or(
                        ConnectorError::MissingRequiredField {
                            field_name: "blik_code",
                        },
                    )?),
                })),
            )),
            BankRedirectData::Eps { bank_name, .. } => Ok(Self::BankRedirect(
                StripeBankRedirectData::StripeEps(Box::new(StripeEps {
                    payment_method_data_type,
                    bank_name: bank_name
                        .map(|bank_name| StripeBankNames::try_from(&bank_name))
                        .transpose()?,
                })),
            )),
            BankRedirectData::Giropay { .. } => Ok(Self::BankRedirect(
                StripeBankRedirectData::StripeGiropay(Box::new(StripeGiropay {
                    payment_method_data_type,
                })),
            )),
            BankRedirectData::Ideal { bank_name, .. } => {
                let bank_name = bank_name
                    .map(|bank_name| StripeBankNames::try_from(&bank_name))
                    .transpose()?;
                Ok(Self::BankRedirect(StripeBankRedirectData::StripeIdeal(
                    Box::new(StripeIdeal {
                        payment_method_data_type,
                        ideal_bank_name: bank_name,
                    }),
                )))
            }
            BankRedirectData::Przelewy24 { bank_name, .. } => {
                let bank_name = bank_name
                    .map(|bank_name| StripeBankNames::try_from(&bank_name))
                    .transpose()?;
                Ok(Self::BankRedirect(
                    StripeBankRedirectData::StripePrezelewy24(Box::new(StripePrezelewy24 {
                        payment_method_data_type,
                        bank_name,
                    })),
                ))
            }
            BankRedirectData::OnlineBankingFpx { .. } => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
            BankRedirectData::Bizum {}
            | BankRedirectData::Eft { .. }
            | BankRedirectData::Interac { .. }
            | BankRedirectData::OnlineBankingCzechRepublic { .. }
            | BankRedirectData::OnlineBankingFinland { .. }
            | BankRedirectData::OnlineBankingPoland { .. }
            | BankRedirectData::OnlineBankingSlovakia { .. }
            | BankRedirectData::OnlineBankingThailand { .. }
            | BankRedirectData::OpenBankingUk { .. }
            | BankRedirectData::Sofort { .. }
            | BankRedirectData::Trustly { .. }
            | BankRedirectData::LocalBankRedirect {} => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
        }
    }
}

impl TryFrom<&GooglePayWalletData> for StripePaymentMethodData {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(gpay_data: &GooglePayWalletData) -> Result<Self, Self::Error> {
        Ok(Self::Wallet(StripeWallet::GooglepayToken(GooglePayToken {
            token: Secret::new(
                gpay_data
                    .tokenization_data
                    .get_encrypted_google_pay_token()
                    .change_context(ConnectorError::MissingRequiredField {
                        field_name: "gpay wallet_token",
                    })?
                    .as_bytes()
                    .parse_struct::<StripeGpayToken>("StripeGpayToken")
                    .change_context(ConnectorError::InvalidWalletToken {
                        wallet_name: "Google Pay".to_string(),
                    })?
                    .id,
            ),
            payment_type: StripePaymentMethodType::Card,
        })))
    }
}


// Struct: Money
// File: crates/hyperswitch_connectors/src/connectors/mifinity/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Money


// File: crates/openapi/src/routes/revenue_recovery.rs
// Module: openapi
// Public functions: 1

#[cfg(feature = "v2")]
/// Revenue Recovery - Retrieve
///
/// Retrieve the Revenue Recovery Payment Info
#[utoipa::path(
    get,
    path = "/v2/process-trackers/revenue-recovery-workflow/{revenue_recovery_id}",
    params(
        ("recovery_recovery_id" = String, Path, description = "The payment intent id"),
    ),
    responses(
        (status = 200, description = "Revenue Recovery Info Retrieved Successfully", body = RevenueRecoveryResponse),
        (status = 500, description = "Internal server error"),
        (status = 404, description = "Resource missing"),
        (status = 422, description = "Unprocessable request"),
        (status = 403, description = "Forbidden"),
    ),
   tag = "Revenue Recovery",
   operation_id = "Retrieve Revenue Recovery Info",
   security(("jwt_key" = []))
)]
pub async fn revenue_recovery_pt_retrieve_api() {}


// File: crates/analytics/src/auth_events/metrics/frictionless_success_count.rs
// Module: analytics

use std::collections::HashSet;

use api_models::analytics::{
    auth_events::{AuthEventDimensions, AuthEventFilters, AuthEventMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_enums::{AuthenticationStatus, DecoupledAuthenticationType};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

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

#[derive(Default)]
pub(super) struct FrictionlessSuccessCount;

#[async_trait::async_trait]
impl<T> super::AuthEventMetric<T> for FrictionlessSuccessCount
where
    T: AnalyticsDataSource + super::AuthEventMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        auth: &AuthInfo,
        dimensions: &[AuthEventDimensions],
        filters: &AuthEventFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
        let mut query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::Authentications);
        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(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()?;

        query_builder
            .add_filter_clause(
                "authentication_type",
                DecoupledAuthenticationType::Frictionless,
            )
            .switch()?;

        query_builder
            .add_filter_clause("authentication_status", AuthenticationStatus::Success)
            .switch()?;
        filters.set_filter_clause(&mut query_builder).switch()?;
        time_range
            .set_filter_clause(&mut query_builder)
            .attach_printable("Error filtering time range")
            .switch()?;
        auth.set_filter_clause(&mut query_builder).switch()?;

        for dim in dimensions.iter() {
            query_builder
                .add_group_by_clause(dim)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }

        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .execute_query::<AuthEventMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    AuthEventMetricsBucketIdentifier::new(
                        i.authentication_status.as_ref().map(|i| i.0),
                        i.trans_status.as_ref().map(|i| i.0.clone()),
                        i.authentication_type.as_ref().map(|i| i.0),
                        i.error_message.clone(),
                        i.authentication_connector.as_ref().map(|i| i.0),
                        i.message_version.clone(),
                        i.acs_reference_number.clone(),
                        i.mcc.clone(),
                        i.currency.as_ref().map(|i| i.0),
                        i.merchant_country.clone(),
                        i.billing_country.clone(),
                        i.shipping_country.clone(),
                        i.issuer_country.clone(),
                        i.earliest_supported_version.clone(),
                        i.latest_supported_version.clone(),
                        i.whitelist_decision,
                        i.device_manufacturer.clone(),
                        i.device_type.clone(),
                        i.device_brand.clone(),
                        i.device_os.clone(),
                        i.device_display.clone(),
                        i.browser_name.clone(),
                        i.browser_version.clone(),
                        i.issuer_id.clone(),
                        i.scheme_name.clone(),
                        i.exemption_requested,
                        i.exemption_accepted,
                        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<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


// Implementation: impl Responder
// File: crates/router/src/routes/relay.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Struct: AcquirerDetails
// File: crates/hyperswitch_domain_models/src/router_request_types/authentication.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct AcquirerDetails


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

impl api::MandateSetup for for Mifinity


// File: crates/router/src/core/pm_auth.rs
// Module: router
// Public functions: 6

use std::{collections::HashMap, str::FromStr};

use api_models::{
    enums,
    payment_methods::{self, BankAccountAccessCreds},
};
use common_enums::{enums::MerchantStorageScheme, PaymentMethodType};
use hex;
pub mod helpers;
pub mod transformers;

use common_utils::{
    consts,
    crypto::{HmacSha256, SignMessage},
    ext_traits::{AsyncExt, ValueExt},
    generate_id,
    types::{self as util_types, AmountConvertor},
};
use error_stack::ResultExt;
use helpers::PaymentAuthConnectorDataExt;
use hyperswitch_domain_models::payments::PaymentIntent;
use masking::{ExposeInterface, PeekInterface, Secret};
use pm_auth::{
    connector::plaid::transformers::PlaidAuthType,
    types::{
        self as pm_auth_types,
        api::{
            auth_service::{BankAccountCredentials, ExchangeToken, LinkToken},
            BoxedConnectorIntegration, PaymentAuthConnectorData,
        },
    },
};

use crate::{
    core::{
        errors::{self, ApiErrorResponse, RouterResponse, RouterResult, StorageErrorExt},
        payment_methods::cards,
        payments::helpers as oss_helpers,
        pm_auth::helpers as pm_auth_helpers,
    },
    db::StorageInterface,
    logger,
    routes::SessionState,
    services::{pm_auth as pm_auth_services, ApplicationResponse},
    types::{self, domain, storage, transformers::ForeignTryFrom},
};

#[cfg(feature = "v1")]
pub async fn create_link_token(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    payload: api_models::pm_auth::LinkTokenCreateRequest,
    headers: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResponse<api_models::pm_auth::LinkTokenCreateResponse> {
    let db = &*state.store;

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

    let pm_auth_key = payload.payment_id.get_pm_auth_key();

    redis_conn
        .exists::<Vec<u8>>(&pm_auth_key.as_str().into())
        .await
        .change_context(ApiErrorResponse::InvalidRequestData {
            message: "Incorrect payment_id provided in request".to_string(),
        })
        .attach_printable("Corresponding pm_auth_key does not exist in redis")?
        .then_some(())
        .ok_or(ApiErrorResponse::InvalidRequestData {
            message: "Incorrect payment_id provided in request".to_string(),
        })
        .attach_printable("Corresponding pm_auth_key does not exist in redis")?;

    let pm_auth_configs = redis_conn
        .get_and_deserialize_key::<Vec<api_models::pm_auth::PaymentMethodAuthConnectorChoice>>(
            &pm_auth_key.as_str().into(),
            "Vec<PaymentMethodAuthConnectorChoice>",
        )
        .await
        .change_context(ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get payment method auth choices from redis")?;

    let selected_config = pm_auth_configs
        .into_iter()
        .find(|config| {
            config.payment_method == payload.payment_method
                && config.payment_method_type == payload.payment_method_type
        })
        .ok_or(ApiErrorResponse::GenericNotFoundError {
            message: "payment method auth connector name not found".to_string(),
        })?;

    let connector_name = selected_config.connector_name.as_str();

    let connector = PaymentAuthConnectorData::get_connector_by_name(connector_name)?;
    let connector_integration: BoxedConnectorIntegration<
        '_,
        LinkToken,
        pm_auth_types::LinkTokenRequest,
        pm_auth_types::LinkTokenResponse,
    > = connector.connector.get_connector_integration();

    let payment_intent = oss_helpers::verify_payment_intent_time_and_client_secret(
        &state,
        &merchant_context,
        payload.client_secret,
    )
    .await?;

    let billing_country = payment_intent
        .as_ref()
        .async_map(|pi| async {
            oss_helpers::get_address_by_id(
                &state,
                pi.billing_address_id.clone(),
                merchant_context.get_merchant_key_store(),
                &pi.payment_id,
                merchant_context.get_merchant_account().get_id(),
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
        })
        .await
        .transpose()?
        .flatten()
        .and_then(|address| address.country)
        .map(|country| country.to_string());

    #[cfg(feature = "v1")]
    let merchant_connector_account = state
        .store
        .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
            &(&state).into(),
            merchant_context.get_merchant_account().get_id(),
            &selected_config.mca_id,
            merchant_context.get_merchant_key_store(),
        )
        .await
        .change_context(ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: merchant_context
                .get_merchant_account()
                .get_id()
                .get_string_repr()
                .to_owned(),
        })?;

    #[cfg(feature = "v2")]
    let merchant_connector_account = {
        let _ = billing_country;
        todo!()
    };

    let auth_type = helpers::get_connector_auth_type(merchant_connector_account)?;

    let router_data = pm_auth_types::LinkTokenRouterData {
        flow: std::marker::PhantomData,
        merchant_id: Some(merchant_context.get_merchant_account().get_id().clone()),
        connector: Some(connector_name.to_string()),
        request: pm_auth_types::LinkTokenRequest {
            client_name: "HyperSwitch".to_string(),
            country_codes: Some(vec![billing_country.ok_or(
                ApiErrorResponse::MissingRequiredField {
                    field_name: "billing_country",
                },
            )?]),
            language: payload.language,
            user_info: payment_intent.and_then(|pi| pi.customer_id),
            client_platform: headers
                .as_ref()
                .and_then(|header| header.x_client_platform.clone()),
            android_package_name: headers.as_ref().and_then(|header| header.x_app_id.clone()),
            redirect_uri: headers
                .as_ref()
                .and_then(|header| header.x_redirect_uri.clone()),
        },
        response: Ok(pm_auth_types::LinkTokenResponse {
            link_token: "".to_string(),
        }),
        connector_http_status_code: None,
        connector_auth_type: auth_type,
    };

    let connector_resp = pm_auth_services::execute_connector_processing_step(
        &state,
        connector_integration,
        &router_data,
        &connector.connector_name,
    )
    .await
    .change_context(ApiErrorResponse::InternalServerError)
    .attach_printable("Failed while calling link token creation connector api")?;

    let link_token_resp =
        connector_resp
            .response
            .map_err(|err| ApiErrorResponse::ExternalConnectorError {
                code: err.code,
                message: err.message,
                connector: connector.connector_name.to_string(),
                status_code: err.status_code,
                reason: err.reason,
            })?;

    let response = api_models::pm_auth::LinkTokenCreateResponse {
        link_token: link_token_resp.link_token,
        connector: connector.connector_name.to_string(),
    };

    Ok(ApplicationResponse::Json(response))
}

#[cfg(feature = "v2")]
pub async fn create_link_token(
    _state: SessionState,
    _merchant_context: domain::MerchantContext,
    _payload: api_models::pm_auth::LinkTokenCreateRequest,
    _headers: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResponse<api_models::pm_auth::LinkTokenCreateResponse> {
    todo!()
}

impl ForeignTryFrom<&types::ConnectorAuthType> for PlaidAuthType {
    type Error = errors::ConnectorError;

    fn foreign_try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            types::ConnectorAuthType::BodyKey { api_key, key1 } => {
                Ok::<Self, errors::ConnectorError>(Self {
                    client_id: api_key.to_owned(),
                    secret: key1.to_owned(),
                })
            }
            _ => Err(errors::ConnectorError::FailedToObtainAuthType),
        }
    }
}

pub async fn exchange_token_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    payload: api_models::pm_auth::ExchangeTokenCreateRequest,
) -> RouterResponse<()> {
    let db = &*state.store;

    let config = get_selected_config_from_redis(db, &payload).await?;

    let connector_name = config.connector_name.as_str();

    let connector = PaymentAuthConnectorData::get_connector_by_name(connector_name)?;

    #[cfg(feature = "v1")]
    let merchant_connector_account = state
        .store
        .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
            &(&state).into(),
            merchant_context.get_merchant_account().get_id(),
            &config.mca_id,
            merchant_context.get_merchant_key_store(),
        )
        .await
        .change_context(ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: merchant_context
                .get_merchant_account()
                .get_id()
                .get_string_repr()
                .to_owned(),
        })?;

    #[cfg(feature = "v2")]
    let merchant_connector_account: domain::MerchantConnectorAccount = {
        let _ = merchant_context.get_merchant_account();
        let _ = connector;
        let _ = merchant_context.get_merchant_key_store();
        todo!()
    };

    let auth_type = helpers::get_connector_auth_type(merchant_connector_account.clone())?;

    let access_token = get_access_token_from_exchange_api(
        &connector,
        connector_name,
        &payload,
        &auth_type,
        &state,
    )
    .await?;

    let bank_account_details_resp = get_bank_account_creds(
        connector,
        &merchant_context,
        connector_name,
        &access_token,
        auth_type,
        &state,
        None,
    )
    .await?;

    Box::pin(store_bank_details_in_payment_methods(
        payload,
        merchant_context,
        state,
        bank_account_details_resp,
        (connector_name, access_token),
        merchant_connector_account.get_id(),
    ))
    .await?;

    Ok(ApplicationResponse::StatusOk)
}

#[cfg(feature = "v1")]
async fn store_bank_details_in_payment_methods(
    payload: api_models::pm_auth::ExchangeTokenCreateRequest,
    merchant_context: domain::MerchantContext,
    state: SessionState,
    bank_account_details_resp: pm_auth_types::BankAccountCredentialsResponse,
    connector_details: (&str, Secret<String>),
    mca_id: common_utils::id_type::MerchantConnectorAccountId,
) -> RouterResult<()> {
    let db = &*state.clone().store;
    let (connector_name, access_token) = connector_details;

    let payment_intent = db
        .find_payment_intent_by_payment_id_merchant_id(
            &(&state).into(),
            &payload.payment_id,
            merchant_context.get_merchant_account().get_id(),
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(ApiErrorResponse::PaymentNotFound)?;

    let customer_id = payment_intent
        .customer_id
        .ok_or(ApiErrorResponse::CustomerNotFound)?;

    let payment_methods = db
        .find_payment_method_by_customer_id_merchant_id_list(
            &((&state).into()),
            merchant_context.get_merchant_key_store(),
            &customer_id,
            merchant_context.get_merchant_account().get_id(),
            None,
        )
        .await
        .change_context(ApiErrorResponse::InternalServerError)?;

    let mut hash_to_payment_method: HashMap<
        String,
        (
            domain::PaymentMethod,
            payment_methods::PaymentMethodDataBankCreds,
        ),
    > = HashMap::new();
    let key_manager_state = (&state).into();
    for pm in payment_methods {
        if pm.get_payment_method_type() == Some(enums::PaymentMethod::BankDebit)
            && pm.payment_method_data.is_some()
        {
            let bank_details_pm_data = pm
                .payment_method_data
                .clone()
                .map(|x| x.into_inner().expose())
                .map(|v| v.parse_value("PaymentMethodsData"))
                .transpose()
                .unwrap_or_else(|error| {
                    logger::error!(?error);
                    None
                })
                .and_then(|pmd| match pmd {
                    payment_methods::PaymentMethodsData::BankDetails(bank_creds) => {
                        Some(bank_creds)
                    }
                    _ => None,
                })
                .ok_or(ApiErrorResponse::InternalServerError)
                .attach_printable("Unable to parse PaymentMethodsData")?;

            hash_to_payment_method.insert(
                bank_details_pm_data.hash.clone(),
                (pm, bank_details_pm_data),
            );
        }
    }

    let pm_auth_key = state
        .conf
        .payment_method_auth
        .get_inner()
        .pm_auth_key
        .clone()
        .expose();

    let mut update_entries: Vec<(domain::PaymentMethod, storage::PaymentMethodUpdate)> = Vec::new();
    let mut new_entries: Vec<domain::PaymentMethod> = Vec::new();

    for creds in bank_account_details_resp.credentials {
        let (account_number, hash_string) = match creds.account_details {
            pm_auth_types::PaymentMethodTypeDetails::Ach(ach) => (
                ach.account_number.clone(),
                format!(
                    "{}-{}-{}",
                    ach.account_number.peek(),
                    ach.routing_number.peek(),
                    PaymentMethodType::Ach,
                ),
            ),
            pm_auth_types::PaymentMethodTypeDetails::Bacs(bacs) => (
                bacs.account_number.clone(),
                format!(
                    "{}-{}-{}",
                    bacs.account_number.peek(),
                    bacs.sort_code.peek(),
                    PaymentMethodType::Bacs
                ),
            ),
            pm_auth_types::PaymentMethodTypeDetails::Sepa(sepa) => (
                sepa.iban.clone(),
                format!("{}-{}", sepa.iban.expose(), PaymentMethodType::Sepa),
            ),
        };

        let generated_hash = hex::encode(
            HmacSha256::sign_message(&HmacSha256, pm_auth_key.as_bytes(), hash_string.as_bytes())
                .change_context(ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to sign the message")?,
        );

        let contains_account = hash_to_payment_method.get(&generated_hash);
        let mut pmd = payment_methods::PaymentMethodDataBankCreds {
            mask: account_number
                .peek()
                .chars()
                .rev()
                .take(4)
                .collect::<String>()
                .chars()
                .rev()
                .collect::<String>(),
            hash: generated_hash,
            account_type: creds.account_type,
            account_name: creds.account_name,
            payment_method_type: creds.payment_method_type,
            connector_details: vec![payment_methods::BankAccountConnectorDetails {
                connector: connector_name.to_string(),
                mca_id: mca_id.clone(),
                access_token: BankAccountAccessCreds::AccessToken(access_token.clone()),
                account_id: creds.account_id,
            }],
        };

        if let Some((pm, details)) = contains_account {
            pmd.connector_details.extend(
                details
                    .connector_details
                    .clone()
                    .into_iter()
                    .filter(|conn| conn.mca_id != mca_id),
            );

            let payment_method_data = payment_methods::PaymentMethodsData::BankDetails(pmd);

            let encrypted_data = cards::create_encrypted_data(
                &key_manager_state,
                merchant_context.get_merchant_key_store(),
                payment_method_data,
            )
            .await
            .change_context(ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt customer details")?;

            let pm_update = storage::PaymentMethodUpdate::PaymentMethodDataUpdate {
                payment_method_data: Some(encrypted_data.into()),
            };

            update_entries.push((pm.clone(), pm_update));
        } else {
            let payment_method_data = payment_methods::PaymentMethodsData::BankDetails(pmd);
            let encrypted_data = cards::create_encrypted_data(
                &key_manager_state,
                merchant_context.get_merchant_key_store(),
                Some(payment_method_data),
            )
            .await
            .change_context(ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt customer details")?;

            let pm_id = generate_id(consts::ID_LENGTH, "pm");

            let now = common_utils::date_time::now();

            let pm_new = domain::PaymentMethod {
                customer_id: customer_id.clone(),
                merchant_id: merchant_context.get_merchant_account().get_id().clone(),
                payment_method_id: pm_id,
                payment_method: Some(enums::PaymentMethod::BankDebit),
                payment_method_type: Some(creds.payment_method_type),
                status: enums::PaymentMethodStatus::Active,
                payment_method_issuer: None,
                scheme: None,
                metadata: None,
                payment_method_data: Some(encrypted_data),
                payment_method_issuer_code: None,
                accepted_currency: None,
                token: None,
                cardholder_name: None,
                issuer_name: None,
                issuer_country: None,
                payer_country: None,
                is_stored: None,
                swift_code: None,
                direct_debit_token: None,
                created_at: now,
                last_modified: now,
                locker_id: None,
                last_used_at: now,
                connector_mandate_details: None,
                customer_acceptance: None,
                network_transaction_id: None,
                client_secret: None,
                payment_method_billing_address: None,
                updated_by: None,
                version: common_types::consts::API_VERSION,
                network_token_requestor_reference_id: None,
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                vault_source_details: Default::default(),
            };

            new_entries.push(pm_new);
        };
    }

    store_in_db(
        &state,
        merchant_context.get_merchant_key_store(),
        update_entries,
        new_entries,
        db,
        merchant_context.get_merchant_account().storage_scheme,
    )
    .await?;

    Ok(())
}

#[cfg(feature = "v2")]
async fn store_bank_details_in_payment_methods(
    _payload: api_models::pm_auth::ExchangeTokenCreateRequest,
    _merchant_context: domain::MerchantContext,
    _state: SessionState,
    _bank_account_details_resp: pm_auth_types::BankAccountCredentialsResponse,
    _connector_details: (&str, Secret<String>),
    _mca_id: common_utils::id_type::MerchantConnectorAccountId,
) -> RouterResult<()> {
    todo!()
}

async fn store_in_db(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    update_entries: Vec<(domain::PaymentMethod, storage::PaymentMethodUpdate)>,
    new_entries: Vec<domain::PaymentMethod>,
    db: &dyn StorageInterface,
    storage_scheme: MerchantStorageScheme,
) -> RouterResult<()> {
    let key_manager_state = &(state.into());
    let update_entries_futures = update_entries
        .into_iter()
        .map(|(pm, pm_update)| {
            db.update_payment_method(key_manager_state, key_store, pm, pm_update, storage_scheme)
        })
        .collect::<Vec<_>>();

    let new_entries_futures = new_entries
        .into_iter()
        .map(|pm_new| {
            db.insert_payment_method(key_manager_state, key_store, pm_new, storage_scheme)
        })
        .collect::<Vec<_>>();

    let update_futures = futures::future::join_all(update_entries_futures);
    let new_futures = futures::future::join_all(new_entries_futures);

    let (update, new) = tokio::join!(update_futures, new_futures);

    let _ = update
        .into_iter()
        .map(|res| res.map_err(|err| logger::error!("Payment method storage failed {err:?}")));

    let _ = new
        .into_iter()
        .map(|res| res.map_err(|err| logger::error!("Payment method storage failed {err:?}")));

    Ok(())
}

pub async fn get_bank_account_creds(
    connector: PaymentAuthConnectorData,
    merchant_context: &domain::MerchantContext,
    connector_name: &str,
    access_token: &Secret<String>,
    auth_type: pm_auth_types::ConnectorAuthType,
    state: &SessionState,
    bank_account_id: Option<Secret<String>>,
) -> RouterResult<pm_auth_types::BankAccountCredentialsResponse> {
    let connector_integration_bank_details: BoxedConnectorIntegration<
        '_,
        BankAccountCredentials,
        pm_auth_types::BankAccountCredentialsRequest,
        pm_auth_types::BankAccountCredentialsResponse,
    > = connector.connector.get_connector_integration();

    let router_data_bank_details = pm_auth_types::BankDetailsRouterData {
        flow: std::marker::PhantomData,
        merchant_id: Some(merchant_context.get_merchant_account().get_id().clone()),
        connector: Some(connector_name.to_string()),
        request: pm_auth_types::BankAccountCredentialsRequest {
            access_token: access_token.clone(),
            optional_ids: bank_account_id
                .map(|id| pm_auth_types::BankAccountOptionalIDs { ids: vec![id] }),
        },
        response: Ok(pm_auth_types::BankAccountCredentialsResponse {
            credentials: Vec::new(),
        }),
        connector_http_status_code: None,
        connector_auth_type: auth_type,
    };

    let bank_details_resp = pm_auth_services::execute_connector_processing_step(
        state,
        connector_integration_bank_details,
        &router_data_bank_details,
        &connector.connector_name,
    )
    .await
    .change_context(ApiErrorResponse::InternalServerError)
    .attach_printable("Failed while calling bank account details connector api")?;

    let bank_account_details_resp =
        bank_details_resp
            .response
            .map_err(|err| ApiErrorResponse::ExternalConnectorError {
                code: err.code,
                message: err.message,
                connector: connector.connector_name.to_string(),
                status_code: err.status_code,
                reason: err.reason,
            })?;

    Ok(bank_account_details_resp)
}

async fn get_access_token_from_exchange_api(
    connector: &PaymentAuthConnectorData,
    connector_name: &str,
    payload: &api_models::pm_auth::ExchangeTokenCreateRequest,
    auth_type: &pm_auth_types::ConnectorAuthType,
    state: &SessionState,
) -> RouterResult<Secret<String>> {
    let connector_integration: BoxedConnectorIntegration<
        '_,
        ExchangeToken,
        pm_auth_types::ExchangeTokenRequest,
        pm_auth_types::ExchangeTokenResponse,
    > = connector.connector.get_connector_integration();

    let router_data = pm_auth_types::ExchangeTokenRouterData {
        flow: std::marker::PhantomData,
        merchant_id: None,
        connector: Some(connector_name.to_string()),
        request: pm_auth_types::ExchangeTokenRequest {
            public_token: payload.public_token.clone(),
        },
        response: Ok(pm_auth_types::ExchangeTokenResponse {
            access_token: "".to_string(),
        }),
        connector_http_status_code: None,
        connector_auth_type: auth_type.clone(),
    };

    let resp = pm_auth_services::execute_connector_processing_step(
        state,
        connector_integration,
        &router_data,
        &connector.connector_name,
    )
    .await
    .change_context(ApiErrorResponse::InternalServerError)
    .attach_printable("Failed while calling exchange token connector api")?;

    let exchange_token_resp =
        resp.response
            .map_err(|err| ApiErrorResponse::ExternalConnectorError {
                code: err.code,
                message: err.message,
                connector: connector.connector_name.to_string(),
                status_code: err.status_code,
                reason: err.reason,
            })?;

    let access_token = exchange_token_resp.access_token;
    Ok(Secret::new(access_token))
}

async fn get_selected_config_from_redis(
    db: &dyn StorageInterface,
    payload: &api_models::pm_auth::ExchangeTokenCreateRequest,
) -> RouterResult<api_models::pm_auth::PaymentMethodAuthConnectorChoice> {
    let redis_conn = db
        .get_redis_conn()
        .change_context(ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get redis connection")?;

    let pm_auth_key = payload.payment_id.get_pm_auth_key();

    redis_conn
        .exists::<Vec<u8>>(&pm_auth_key.as_str().into())
        .await
        .change_context(ApiErrorResponse::InvalidRequestData {
            message: "Incorrect payment_id provided in request".to_string(),
        })
        .attach_printable("Corresponding pm_auth_key does not exist in redis")?
        .then_some(())
        .ok_or(ApiErrorResponse::InvalidRequestData {
            message: "Incorrect payment_id provided in request".to_string(),
        })
        .attach_printable("Corresponding pm_auth_key does not exist in redis")?;

    let pm_auth_configs = redis_conn
        .get_and_deserialize_key::<Vec<api_models::pm_auth::PaymentMethodAuthConnectorChoice>>(
            &pm_auth_key.as_str().into(),
            "Vec<PaymentMethodAuthConnectorChoice>",
        )
        .await
        .change_context(ApiErrorResponse::GenericNotFoundError {
            message: "payment method auth connector name not found".to_string(),
        })
        .attach_printable("Failed to get payment method auth choices from redis")?;

    let selected_config = pm_auth_configs
        .iter()
        .find(|conf| {
            conf.payment_method == payload.payment_method
                && conf.payment_method_type == payload.payment_method_type
        })
        .ok_or(ApiErrorResponse::GenericNotFoundError {
            message: "payment method auth connector name not found".to_string(),
        })?
        .clone();

    Ok(selected_config)
}

#[cfg(feature = "v2")]
pub async fn retrieve_payment_method_from_auth_service(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    auth_token: &payment_methods::BankAccountTokenData,
    payment_intent: &PaymentIntent,
    _customer: &Option<domain::Customer>,
) -> RouterResult<Option<(domain::PaymentMethodData, enums::PaymentMethod)>> {
    todo!()
}

#[cfg(feature = "v1")]
pub async fn retrieve_payment_method_from_auth_service(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    auth_token: &payment_methods::BankAccountTokenData,
    payment_intent: &PaymentIntent,
    _customer: &Option<domain::Customer>,
) -> RouterResult<Option<(domain::PaymentMethodData, enums::PaymentMethod)>> {
    let db = state.store.as_ref();

    let connector = PaymentAuthConnectorData::get_connector_by_name(
        auth_token.connector_details.connector.as_str(),
    )?;
    let key_manager_state = &state.into();
    let merchant_account = db
        .find_merchant_account_by_merchant_id(
            key_manager_state,
            &payment_intent.merchant_id,
            key_store,
        )
        .await
        .to_not_found_response(ApiErrorResponse::MerchantAccountNotFound)?;

    #[cfg(feature = "v1")]
    let mca = db
        .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
            key_manager_state,
            &payment_intent.merchant_id,
            &auth_token.connector_details.mca_id,
            key_store,
        )
        .await
        .to_not_found_response(ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: auth_token
                .connector_details
                .mca_id
                .get_string_repr()
                .to_string()
                .clone(),
        })
        .attach_printable(
            "error while fetching merchant_connector_account from merchant_id and connector name",
        )?;

    let auth_type = pm_auth_helpers::get_connector_auth_type(mca)?;

    let BankAccountAccessCreds::AccessToken(access_token) =
        &auth_token.connector_details.access_token;

    let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
        merchant_account.clone(),
        key_store.clone(),
    )));
    let bank_account_creds = get_bank_account_creds(
        connector,
        &merchant_context,
        &auth_token.connector_details.connector,
        access_token,
        auth_type,
        state,
        Some(auth_token.connector_details.account_id.clone()),
    )
    .await?;

    let bank_account = bank_account_creds
        .credentials
        .iter()
        .find(|acc| {
            acc.payment_method_type == auth_token.payment_method_type
                && acc.payment_method == auth_token.payment_method
        })
        .ok_or(ApiErrorResponse::InternalServerError)
        .attach_printable("Bank account details not found")?;

    if let (Some(balance), Some(currency)) = (bank_account.balance, payment_intent.currency) {
        let required_conversion = util_types::FloatMajorUnitForConnector;
        let converted_amount = required_conversion
            .convert_back(balance, currency)
            .change_context(ApiErrorResponse::InternalServerError)
            .attach_printable("Could not convert FloatMajorUnit to MinorUnit")?;

        if converted_amount < payment_intent.amount {
            return Err((ApiErrorResponse::PreconditionFailed {
                message: "selected bank account has insufficient balance".to_string(),
            })
            .into());
        }
    }

    let mut bank_type = None;
    if let Some(account_type) = bank_account.account_type.clone() {
        bank_type = common_enums::BankType::from_str(account_type.as_str())
            .map_err(|error| logger::error!(%error,"unable to parse account_type {account_type:?}"))
            .ok();
    }

    let payment_method_data = match &bank_account.account_details {
        pm_auth_types::PaymentMethodTypeDetails::Ach(ach) => {
            domain::PaymentMethodData::BankDebit(domain::BankDebitData::AchBankDebit {
                account_number: ach.account_number.clone(),
                routing_number: ach.routing_number.clone(),
                bank_name: None,
                bank_type,
                bank_holder_type: None,
                card_holder_name: None,
                bank_account_holder_name: None,
            })
        }
        pm_auth_types::PaymentMethodTypeDetails::Bacs(bacs) => {
            domain::PaymentMethodData::BankDebit(domain::BankDebitData::BacsBankDebit {
                account_number: bacs.account_number.clone(),
                sort_code: bacs.sort_code.clone(),
                bank_account_holder_name: None,
            })
        }
        pm_auth_types::PaymentMethodTypeDetails::Sepa(sepa) => {
            domain::PaymentMethodData::BankDebit(domain::BankDebitData::SepaBankDebit {
                iban: sepa.iban.clone(),
                bank_account_holder_name: None,
            })
        }
    };

    Ok(Some((payment_method_data, enums::PaymentMethod::BankDebit)))
}


// Implementation: impl RoutingAlgorithmHelpers
// File: crates/router/src/core/routing/helpers.rs
// Module: router
// Methods: 2 total (1 public)

impl RoutingAlgorithmHelpers


// Implementation: impl api::PaymentCapture for for Nuvei
// File: crates/hyperswitch_connectors/src/connectors/nuvei.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentCapture for for Nuvei


// Function: payout_create_db_entries
// File: crates/router/src/core/payouts.rs
// Module: router

pub fn payout_create_db_entries(
    _state: &SessionState,
    _merchant_context: &domain::MerchantContext,
    _req: &payouts::PayoutCreateRequest,
    _payout_id: &str,
    _profile_id: &str,
    _stored_payout_method_data: Option<&payouts::PayoutMethodData>,
    _locale: &str,
    _customer: Option<&domain::Customer>,
    _payment_method: Option<PaymentMethod>,
) -> RouterResult<PayoutData>


// Function: update_dispute_data
// File: crates/router/src/core/disputes.rs
// Module: router

pub fn update_dispute_data(
    state: &SessionState,
    merchant_context: domain::MerchantContext,
    business_profile: domain::Profile,
    option_dispute: Option<diesel_models::dispute::Dispute>,
    dispute_details: DisputeSyncResponse,
    payment_attempt: domain::PaymentAttempt,
    connector_name: &str,
) -> errors::CustomResult<api_models::disputes::DisputeResponse, errors::ApiErrorResponse>


// Struct: RefundResponse
// File: crates/hyperswitch_connectors/src/connectors/payeezy/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RefundResponse


// Struct: EbanxPayoutCancelRequest
// File: crates/hyperswitch_connectors/src/connectors/ebanx/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct EbanxPayoutCancelRequest


// Implementation: impl PaymentMethodSession
// File: crates/router/src/routes/app.rs
// Module: router
// Methods: 1 total (1 public)

impl PaymentMethodSession


// Module Structure
// File: crates/router/src/core/connector_onboarding.rs
// Module: router

// Public submodules:
pub mod paypal;


// Struct: Aci
// File: crates/hyperswitch_connectors/src/connectors/aci.rs
// Module: hyperswitch_connectors
// Implementations: 17
// Traits: ConnectorCommon, ConnectorValidation, api::Payment, api::PaymentAuthorize, api::PaymentSync, api::PaymentVoid, api::PaymentCapture, api::PaymentSession, api::ConnectorAccessToken, api::PaymentToken, api::MandateSetup, api::Refund, api::RefundExecute, api::RefundSync, IncomingWebhook, ConnectorSpecifications

pub struct Aci

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


// File: crates/router/src/utils/db_utils.rs
// Module: router
// Public functions: 2

use crate::{
    core::errors::{self, utils::RedisErrorExt},
    routes::metrics,
};

/// Generates hscan field pattern. Suppose the field is pa_1234_ref_1211 it will generate
/// pa_1234_ref_*
pub fn generate_hscan_pattern_for_refund(sk: &str) -> String {
    sk.split('_')
        .take(3)
        .chain(["*"])
        .collect::<Vec<&str>>()
        .join("_")
}

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


// Implementation: impl api::PaymentsCompleteAuthorize for for Affirm
// File: crates/hyperswitch_connectors/src/connectors/affirm.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentsCompleteAuthorize for for Affirm


// Struct: RefundAggregateResponse
// File: crates/api_models/src/refunds.rs
// Module: api_models
// Implementations: 0

pub struct RefundAggregateResponse


// Function: delete_payment_intents
// File: crates/diesel_models/src/query/user/sample_data.rs
// Module: diesel_models

pub fn delete_payment_intents(
    conn: &PgPooledConn,
    merchant_id: &common_utils::id_type::MerchantId,
) -> StorageResult<Vec<PaymentIntent>>


// Function: validate_payout_link_render_request_and_get_allowed_domains
// File: crates/router/src/core/payouts/validator.rs
// Module: router

pub fn validate_payout_link_render_request_and_get_allowed_domains(
    request_headers: &header::HeaderMap,
    payout_link: &PayoutLink,
) -> RouterResult<HashSet<String>>


// Struct: ProphetpayRouterData
// File: crates/hyperswitch_connectors/src/connectors/prophetpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ProphetpayRouterData<T>


// Struct: MerchantConnectorUpdate
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 1

pub struct MerchantConnectorUpdate


// Function: update_merchant_active_algorithm_ref
// File: crates/router/src/core/routing/helpers.rs
// Module: router

pub fn update_merchant_active_algorithm_ref(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    config_key: cache::CacheKind<'_>,
    algorithm_id: routing_types::RoutingAlgorithmRef,
) -> RouterResult<()>


// Struct: JuspaythreedsserverAuthType
// File: crates/hyperswitch_connectors/src/connectors/juspaythreedsserver/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct JuspaythreedsserverAuthType


// Function: vault_payment_method
// File: crates/router/src/core/payment_methods.rs
// Module: router

pub fn vault_payment_method(
    state: &SessionState,
    pmd: &domain::PaymentMethodVaultingData,
    merchant_context: &domain::MerchantContext,
    profile: &domain::Profile,
    existing_vault_id: Option<domain::VaultId>,
    customer_id: &id_type::GlobalCustomerId,
) -> RouterResult<(
    pm_types::AddVaultResponse,
    Option<id_type::MerchantConnectorAccountId>,
)>


// Function: new
// File: crates/hyperswitch_connectors/src/connectors/worldpayxml.rs
// Module: hyperswitch_connectors

pub fn new() -> &'static Self


// Struct: PlacetopayPaymentsRequest
// File: crates/hyperswitch_connectors/src/connectors/placetopay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PlacetopayPaymentsRequest


// Implementation: impl api::PaymentIncrementalAuthorization for for Archipel
// File: crates/hyperswitch_connectors/src/connectors/archipel.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentIncrementalAuthorization for for Archipel


// File: crates/router/src/db/authorization.rs
// Module: router

use diesel_models::authorization::AuthorizationUpdateInternal;
use error_stack::report;
use router_env::{instrument, tracing};

use super::{MockDb, Store};
use crate::{
    connection,
    core::errors::{self, CustomResult},
    types::storage,
};

#[async_trait::async_trait]
pub trait AuthorizationInterface {
    async fn insert_authorization(
        &self,
        authorization: storage::AuthorizationNew,
    ) -> CustomResult<storage::Authorization, errors::StorageError>;

    async fn find_all_authorizations_by_merchant_id_payment_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> CustomResult<Vec<storage::Authorization>, errors::StorageError>;

    async fn update_authorization_by_merchant_id_authorization_id(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        authorization_id: String,
        authorization: storage::AuthorizationUpdate,
    ) -> CustomResult<storage::Authorization, errors::StorageError>;
}

#[async_trait::async_trait]
impl AuthorizationInterface for Store {
    #[instrument(skip_all)]
    async fn insert_authorization(
        &self,
        authorization: storage::AuthorizationNew,
    ) -> CustomResult<storage::Authorization, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        authorization
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_all_authorizations_by_merchant_id_payment_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> CustomResult<Vec<storage::Authorization>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::Authorization::find_by_merchant_id_payment_id(&conn, merchant_id, payment_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn update_authorization_by_merchant_id_authorization_id(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        authorization_id: String,
        authorization: storage::AuthorizationUpdate,
    ) -> CustomResult<storage::Authorization, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        storage::Authorization::update_by_merchant_id_authorization_id(
            &conn,
            merchant_id,
            authorization_id,
            authorization,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl AuthorizationInterface for MockDb {
    async fn insert_authorization(
        &self,
        authorization: storage::AuthorizationNew,
    ) -> CustomResult<storage::Authorization, errors::StorageError> {
        let mut authorizations = self.authorizations.lock().await;
        if authorizations.iter().any(|authorization_inner| {
            authorization_inner.authorization_id == authorization.authorization_id
        }) {
            Err(errors::StorageError::DuplicateValue {
                entity: "authorization_id",
                key: None,
            })?
        }
        let authorization = storage::Authorization {
            authorization_id: authorization.authorization_id,
            merchant_id: authorization.merchant_id,
            payment_id: authorization.payment_id,
            amount: authorization.amount,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            status: authorization.status,
            error_code: authorization.error_code,
            error_message: authorization.error_message,
            connector_authorization_id: authorization.connector_authorization_id,
            previously_authorized_amount: authorization.previously_authorized_amount,
        };
        authorizations.push(authorization.clone());
        Ok(authorization)
    }

    async fn find_all_authorizations_by_merchant_id_payment_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> CustomResult<Vec<storage::Authorization>, errors::StorageError> {
        let authorizations = self.authorizations.lock().await;
        let authorizations_found: Vec<storage::Authorization> = authorizations
            .iter()
            .filter(|a| a.merchant_id == *merchant_id && a.payment_id == *payment_id)
            .cloned()
            .collect();

        Ok(authorizations_found)
    }

    async fn update_authorization_by_merchant_id_authorization_id(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        authorization_id: String,
        authorization_update: storage::AuthorizationUpdate,
    ) -> CustomResult<storage::Authorization, errors::StorageError> {
        let mut authorizations = self.authorizations.lock().await;
        authorizations
            .iter_mut()
            .find(|authorization| authorization.authorization_id == authorization_id && authorization.merchant_id == merchant_id)
            .map(|authorization| {
                let authorization_updated =
                    AuthorizationUpdateInternal::from(authorization_update)
                        .create_authorization(authorization.clone());
                *authorization = authorization_updated.clone();
                authorization_updated
            })
            .ok_or(
                errors::StorageError::ValueNotFound(format!(
                    "cannot find authorization for authorization_id = {authorization_id} and merchant_id = {merchant_id:?}"
                ))
                .into(),
            )
    }
}


// Implementation: impl NuveiTransactionSyncResponse
// File: crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs
// Module: hyperswitch_connectors
// Methods: 1 total (1 public)

impl NuveiTransactionSyncResponse


// Struct: DeviceRenderOptions
// File: crates/hyperswitch_connectors/src/connectors/threedsecureio/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DeviceRenderOptions


// Implementation: impl api::PayoutRecipient for for Stripe
// File: crates/hyperswitch_connectors/src/connectors/stripe.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PayoutRecipient for for Stripe


// Function: payment_methods_session_update
// File: crates/router/src/core/payment_methods.rs
// Module: router

pub fn payment_methods_session_update(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
    request: payment_methods::PaymentMethodsSessionUpdateRequest,
) -> RouterResponse<payment_methods::PaymentMethodSessionResponse>


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

pub fn as_bool(self) -> bool


// Implementation: impl Moneris
// File: crates/hyperswitch_connectors/src/connectors/moneris.rs
// Module: hyperswitch_connectors
// Methods: 1 total (1 public)

impl Moneris


// Struct: TrustpaymentsRefundRequestData
// File: crates/hyperswitch_connectors/src/connectors/trustpayments/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TrustpaymentsRefundRequestData


// Struct: WisePayoutQuoteRequest
// File: crates/hyperswitch_connectors/src/connectors/wise/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct WisePayoutQuoteRequest


// Struct: ApplePayCertificatesMigrationResponse
// File: crates/api_models/src/apple_pay_certificates_migration.rs
// Module: api_models
// Implementations: 0

pub struct ApplePayCertificatesMigrationResponse


// Struct: AchVerification
// File: crates/hyperswitch_connectors/src/connectors/barclaycard/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AchVerification


// Implementation: impl Store
// File: crates/drainer/src/stream.rs
// Module: drainer
// Methods: 8 total (1 public)

impl Store


// File: crates/euclid/src/enums.rs
// Module: euclid

pub use common_enums::{
    AuthenticationType, CaptureMethod, CardNetwork, Country, CountryAlpha2, Currency,
    FutureUsage as SetupFutureUsage, PaymentMethod, PaymentMethodType, RoutableConnectors,
};
use strum::VariantNames;

pub trait CollectVariants {
    fn variants<T: FromIterator<String>>() -> T;
}
macro_rules! collect_variants {
    ($the_enum:ident) => {
        impl $crate::enums::CollectVariants for $the_enum {
            fn variants<T>() -> T
            where
                T: FromIterator<String>,
            {
                Self::VARIANTS.iter().map(|s| String::from(*s)).collect()
            }
        }
    };
}

pub(crate) use collect_variants;

collect_variants!(PaymentMethod);
collect_variants!(RoutableConnectors);
collect_variants!(PaymentType);
collect_variants!(MandateType);
collect_variants!(MandateAcceptanceType);
collect_variants!(PaymentMethodType);
collect_variants!(CardNetwork);
collect_variants!(AuthenticationType);
collect_variants!(CaptureMethod);
collect_variants!(Currency);
collect_variants!(Country);
collect_variants!(SetupFutureUsage);
#[cfg(feature = "payouts")]
collect_variants!(PayoutType);
#[cfg(feature = "payouts")]
collect_variants!(PayoutBankTransferType);
#[cfg(feature = "payouts")]
collect_variants!(PayoutWalletType);

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum MandateAcceptanceType {
    Online,
    Offline,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PaymentType {
    SetupMandate,
    NonMandate,
    NewMandate,
    UpdateMandate,
    PptMandate,
}

#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum MandateType {
    SingleUse,
    MultiUse,
}

#[cfg(feature = "payouts")]
#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PayoutBankTransferType {
    Ach,
    Bacs,
    SepaBankTransfer,
}

#[cfg(feature = "payouts")]
#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PayoutWalletType {
    Paypal,
}

#[cfg(feature = "payouts")]
#[derive(
    Clone,
    Debug,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PayoutType {
    Card,
    BankTransfer,
    Wallet,
}


// Implementation: impl api::RefundExecute for for Wellsfargopayout
// File: crates/hyperswitch_connectors/src/connectors/wellsfargopayout.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundExecute for for Wellsfargopayout


// Struct: PaymentResponse
// File: crates/hyperswitch_connectors/src/connectors/elavon/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaymentResponse


// Function: get_tenant
// File: crates/drainer/src/settings.rs
// Module: drainer

pub fn get_tenant(&self, tenant_id: &id_type::TenantId) -> Option<&Tenant>


// Struct: AciErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/aci/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AciErrorResponse


// Implementation: impl Config
// File: crates/router_env/src/logger/config.rs
// Module: router_env
// Methods: 5 total (5 public)

impl Config


// Struct: RazorpayWebhookPayload
// File: crates/hyperswitch_connectors/src/connectors/razorpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RazorpayWebhookPayload


// Struct: VoidBankTransaction
// File: crates/hyperswitch_connectors/src/connectors/facilitapay/responses.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct VoidBankTransaction


// Struct: BillwerkCustomerObject
// File: crates/hyperswitch_connectors/src/connectors/billwerk/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BillwerkCustomerObject


// Function: get_default_customer_billing_address
// File: crates/api_models/src/customers.rs
// Module: api_models

pub fn get_default_customer_billing_address(&self) -> Option<payments::AddressDetails>


// Function: check_two_factor_auth_status_with_attempts
// File: crates/router/src/core/user.rs
// Module: router

pub fn check_two_factor_auth_status_with_attempts(
    state: SessionState,
    user_token: auth::UserIdFromAuth,
) -> UserResponse<user_api::TwoFactorStatus>


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

pub fn route_connector_v1_for_payouts(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    business_profile: &domain::Profile,
    transaction_data: &payouts::PayoutData,
    routing_data: &mut storage::RoutingData,
    eligible_connectors: Option<Vec<enums::RoutableConnectors>>,
) -> RouterResult<ConnectorCallType>