// File: crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs
// Module: hyperswitch_connectors
// Public functions: 3
// Public structs: 85

use api_models::payments;
#[cfg(feature = "payouts")]
use api_models::payouts::PayoutMethodData;
use base64::Engine;
use common_enums::{enums, FutureUsage};
use common_types::payments::{ApplePayPredecryptData, GPayPredecryptData};
use common_utils::{
    consts, date_time,
    ext_traits::{OptionExt, ValueExt},
    pii,
    types::{SemanticVersion, StringMajorUnit},
};
use error_stack::ResultExt;
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::{
    address::{AddressDetails, PhoneDetails},
    router_flow_types::PoFulfill,
    router_response_types::PayoutsResponseData,
    types::PayoutsRouterData,
};
use hyperswitch_domain_models::{
    network_tokenization::NetworkTokenNumber,
    payment_method_data::{
        ApplePayWalletData, GooglePayWalletData, NetworkTokenData, PaymentMethodData,
        SamsungPayWalletData, WalletData,
    },
    router_data::{
        AdditionalPaymentMethodConnectorResponse, ConnectorAuthType, ConnectorResponseData,
        ErrorResponse, PaymentMethodToken, RouterData,
    },
    router_flow_types::{
        payments::Authorize,
        refunds::{Execute, RSync},
        SetupMandate,
    },
    router_request_types::{
        authentication::MessageExtensionAttribute, CompleteAuthorizeData, PaymentsAuthenticateData,
        PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData,
        PaymentsPostAuthenticateData, PaymentsPreAuthenticateData, PaymentsPreProcessingData,
        PaymentsSyncData, ResponseId, SetupMandateRequestData,
    },
    router_response_types::{
        MandateReference, PaymentsResponseData, RedirectForm, RefundsResponseData,
    },
    types::{
        PaymentsAuthenticateRouterData, PaymentsAuthorizeRouterData, PaymentsCancelRouterData,
        PaymentsCaptureRouterData, PaymentsCompleteAuthorizeRouterData,
        PaymentsIncrementalAuthorizationRouterData, PaymentsPostAuthenticateRouterData,
        PaymentsPreAuthenticateRouterData, PaymentsPreProcessingRouterData, RefundsRouterData,
        SetupMandateRouterData,
    },
};
use hyperswitch_interfaces::{api, errors};
use masking::{ExposeInterface, PeekInterface, Secret};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use utils::ForeignTryFrom;

#[cfg(feature = "payouts")]
use crate::types::PayoutsResponseRouterData;
#[cfg(feature = "payouts")]
use crate::utils::PayoutsData;
use crate::{
    constants,
    types::{RefundsResponseRouterData, ResponseRouterData},
    unimplemented_payment_method,
    utils::{
        self, AddressDetailsData, CardData, CardIssuer, NetworkTokenData as _,
        PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData,
        PaymentsPreProcessingRequestData, PaymentsSetupMandateRequestData, PaymentsSyncRequestData,
        RecurringMandateData, RouterData as OtherRouterData,
    },
};

#[derive(Debug, Serialize)]
pub struct CybersourceRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}

impl<T> From<(StringMajorUnit, T)> for CybersourceRouterData<T> {
    fn from((amount, router_data): (StringMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data,
        }
    }
}

impl From<CardIssuer> for String {
    fn from(card_issuer: CardIssuer) -> Self {
        let card_type = match card_issuer {
            CardIssuer::AmericanExpress => "003",
            CardIssuer::Master => "002",
            //"042" is the type code for Masetro Cards(International). For Maestro Cards(UK-Domestic) the mapping should be "024"
            CardIssuer::Maestro => "042",
            CardIssuer::Visa => "001",
            CardIssuer::Discover => "004",
            CardIssuer::DinersClub => "005",
            CardIssuer::CarteBlanche => "006",
            CardIssuer::JCB => "007",
            CardIssuer::CartesBancaires => "036",
            CardIssuer::UnionPay => "062",
        };
        card_type.to_string()
    }
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CybersourceConnectorMetadataObject {
    pub disable_avs: Option<bool>,
    pub disable_cvn: Option<bool>,
}

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

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CybersourceZeroMandateRequest {
    processing_information: ProcessingInformation,
    payment_information: PaymentInformation,
    order_information: OrderInformationWithBill,
    client_reference_information: ClientReferenceInformation,
}

impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &SetupMandateRouterData) -> Result<Self, Self::Error> {
        let email = item.get_billing_email().or(item.request.get_email())?;
        let bill_to = build_bill_to(item.get_optional_billing(), email)?;

        let order_information = OrderInformationWithBill {
            amount_details: Amount {
                total_amount: StringMajorUnit::zero(),
                currency: item.request.currency,
            },
            bill_to: Some(bill_to),
        };
        let connector_merchant_config =
            CybersourceConnectorMetadataObject::try_from(&item.connector_meta_data)?;

        let (action_list, action_token_types, authorization_options) = (
            Some(vec![CybersourceActionsList::TokenCreate]),
            Some(vec![
                CybersourceActionsTokenType::PaymentInstrument,
                CybersourceActionsTokenType::Customer,
            ]),
            Some(CybersourceAuthorizationOptions {
                initiator: Some(CybersourcePaymentInitiator {
                    initiator_type: Some(CybersourcePaymentInitiatorTypes::Customer),
                    credential_stored_on_file: Some(true),
                    stored_credential_used: None,
                }),
                merchant_intitiated_transaction: None,
                ignore_avs_result: connector_merchant_config.disable_avs,
                ignore_cv_result: connector_merchant_config.disable_cvn,
            }),
        );

        let client_reference_information = ClientReferenceInformation {
            code: Some(item.connector_request_reference_id.clone()),
        };

        let (payment_information, solution) = match item.request.payment_method_data.clone() {
            PaymentMethodData::Card(ccard) => {
                let card_type = match ccard
                    .card_network
                    .clone()
                    .and_then(get_cybersource_card_type)
                {
                    Some(card_network) => Some(card_network.to_string()),
                    None => ccard.get_card_issuer().ok().map(String::from),
                };

                (
                    PaymentInformation::Cards(Box::new(CardPaymentInformation {
                        card: Card {
                            number: ccard.card_number,
                            expiration_month: ccard.card_exp_month,
                            expiration_year: ccard.card_exp_year,
                            security_code: Some(ccard.card_cvc),
                            card_type,
                            type_selection_indicator: Some("1".to_owned()),
                        },
                    })),
                    None,
                )
            }

            PaymentMethodData::Wallet(wallet_data) => match wallet_data {
                WalletData::ApplePay(apple_pay_data) => match item.payment_method_token.clone() {
                    Some(payment_method_token) => match payment_method_token {
                        PaymentMethodToken::ApplePayDecrypt(decrypt_data) => {
                            let expiration_month = decrypt_data.get_expiry_month().change_context(
                                errors::ConnectorError::InvalidDataFormat {
                                    field_name: "expiration_month",
                                },
                            )?;
                            let expiration_year = decrypt_data.get_four_digit_expiry_year();
                            (
                                PaymentInformation::ApplePay(Box::new(
                                    ApplePayPaymentInformation {
                                        tokenized_card: TokenizedCard {
                                            number: decrypt_data.application_primary_account_number,
                                            cryptogram: Some(
                                                decrypt_data.payment_data.online_payment_cryptogram,
                                            ),
                                            transaction_type: TransactionType::InApp,
                                            expiration_year,
                                            expiration_month,
                                        },
                                    },
                                )),
                                Some(PaymentSolution::ApplePay),
                            )
                        }
                        PaymentMethodToken::Token(_) => Err(unimplemented_payment_method!(
                            "Apple Pay",
                            "Manual",
                            "Cybersource"
                        ))?,
                        PaymentMethodToken::PazeDecrypt(_) => {
                            Err(unimplemented_payment_method!("Paze", "Cybersource"))?
                        }
                        PaymentMethodToken::GooglePayDecrypt(_) => {
                            Err(unimplemented_payment_method!("Google Pay", "Cybersource"))?
                        }
                    },
                    None => {
                        let apple_pay_encrypted_data = apple_pay_data
                            .payment_data
                            .get_encrypted_apple_pay_payment_data_mandatory()
                            .change_context(errors::ConnectorError::MissingRequiredField {
                                field_name: "Apple pay encrypted data",
                            })?;
                        (
                            PaymentInformation::ApplePayToken(Box::new(
                                ApplePayTokenPaymentInformation {
                                    fluid_data: FluidData {
                                        value: Secret::from(apple_pay_encrypted_data.clone()),
                                        descriptor: Some(FLUID_DATA_DESCRIPTOR.to_string()),
                                    },
                                    tokenized_card: ApplePayTokenizedCard {
                                        transaction_type: TransactionType::InApp,
                                    },
                                },
                            )),
                            Some(PaymentSolution::ApplePay),
                        )
                    }
                },
                WalletData::GooglePay(google_pay_data) => (
                    PaymentInformation::GooglePayToken(Box::new(
                        GooglePayTokenPaymentInformation {
                            fluid_data: FluidData {
                                value: Secret::from(
                                    consts::BASE64_ENGINE.encode(
                                        google_pay_data
                                            .tokenization_data
                                            .get_encrypted_google_pay_token()
                                            .change_context(
                                                errors::ConnectorError::MissingRequiredField {
                                                    field_name: "gpay wallet_token",
                                                },
                                            )?,
                                    ),
                                ),
                                descriptor: None,
                            },
                            tokenized_card: GooglePayTokenizedCard {
                                transaction_type: TransactionType::InApp,
                            },
                        },
                    )),
                    Some(PaymentSolution::GooglePay),
                ),
                WalletData::SamsungPay(samsung_pay_data) => (
                    (get_samsung_pay_payment_information(&samsung_pay_data)
                        .attach_printable("Failed to get samsung pay payment information")?),
                    Some(PaymentSolution::SamsungPay),
                ),
                WalletData::AliPayQr(_)
                | WalletData::AliPayRedirect(_)
                | WalletData::AliPayHkRedirect(_)
                | WalletData::AmazonPayRedirect(_)
                | WalletData::Paysera(_)
                | WalletData::Skrill(_)
                | WalletData::BluecodeRedirect {}
                | WalletData::MomoRedirect(_)
                | WalletData::KakaoPayRedirect(_)
                | WalletData::GoPayRedirect(_)
                | WalletData::GcashRedirect(_)
                | WalletData::ApplePayRedirect(_)
                | WalletData::ApplePayThirdPartySdk(_)
                | WalletData::DanaRedirect {}
                | WalletData::GooglePayRedirect(_)
                | WalletData::GooglePayThirdPartySdk(_)
                | WalletData::MbWayRedirect(_)
                | WalletData::MobilePayRedirect(_)
                | WalletData::PaypalRedirect(_)
                | WalletData::PaypalSdk(_)
                | WalletData::Paze(_)
                | WalletData::AmazonPay(_)
                | WalletData::TwintRedirect {}
                | WalletData::VippsRedirect {}
                | WalletData::TouchNGoRedirect(_)
                | WalletData::WeChatPayRedirect(_)
                | WalletData::WeChatPayQr(_)
                | WalletData::CashappQr(_)
                | WalletData::SwishQr(_)
                | WalletData::Mifinity(_)
                | WalletData::RevolutPay(_) => Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("Cybersource"),
                ))?,
            },
            PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("Cybersource"),
                ))?
            }
        };

        let processing_information = ProcessingInformation {
            capture: Some(false),
            capture_options: None,
            action_list,
            action_token_types,
            authorization_options,
            commerce_indicator: String::from("internet"),
            payment_solution: solution.map(String::from),
        };
        Ok(Self {
            processing_information,
            payment_information,
            order_information,
            client_reference_information,
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CybersourcePaymentsRequest {
    processing_information: ProcessingInformation,
    payment_information: PaymentInformation,
    order_information: OrderInformationWithBill,
    client_reference_information: ClientReferenceInformation,
    #[serde(skip_serializing_if = "Option::is_none")]
    consumer_authentication_information: Option<CybersourceConsumerAuthInformation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    merchant_defined_information: Option<Vec<MerchantDefinedInformation>>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ProcessingInformation {
    action_list: Option<Vec<CybersourceActionsList>>,
    action_token_types: Option<Vec<CybersourceActionsTokenType>>,
    authorization_options: Option<CybersourceAuthorizationOptions>,
    commerce_indicator: String,
    capture: Option<bool>,
    capture_options: Option<CaptureOptions>,
    payment_solution: Option<String>,
}

#[derive(Debug, Serialize)]
pub enum CybersourceParesStatus {
    #[serde(rename = "Y")]
    AuthenticationSuccessful,
    #[serde(rename = "A")]
    AuthenticationAttempted,
    #[serde(rename = "N")]
    AuthenticationFailed,
    #[serde(rename = "U")]
    AuthenticationNotCompleted,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CybersourceConsumerAuthInformation {
    ucaf_collection_indicator: Option<String>,
    cavv: Option<Secret<String>>,
    ucaf_authentication_data: Option<Secret<String>>,
    xid: Option<String>,
    directory_server_transaction_id: Option<Secret<String>>,
    specification_version: Option<SemanticVersion>,
    /// This field specifies the 3ds version
    pa_specification_version: Option<SemanticVersion>,
    /// Verification response enrollment status.
    ///
    /// This field is supported only on Asia, Middle East, and Africa Gateway.
    ///
    /// For external authentication, this field will always be "Y"
    veres_enrolled: Option<String>,
    /// Raw electronic commerce indicator (ECI)
    eci_raw: Option<String>,
    /// This field is supported only on Asia, Middle East, and Africa Gateway
    /// Also needed for Credit Mutuel-CIC in France and Mastercard Identity Check transactions
    /// This field is only applicable for Mastercard and Visa Transactions
    pares_status: Option<CybersourceParesStatus>,
    //This field is used to send the authentication date in yyyyMMDDHHMMSS format
    authentication_date: Option<String>,
    /// This field indicates the 3D Secure transaction flow. It is only supported for secure transactions in France.
    /// The possible values are - CH (Challenge), FD (Frictionless with delegation), FR (Frictionless)
    effective_authentication_type: Option<EffectiveAuthenticationType>,
    /// This field indicates the authentication type or challenge presented to the cardholder at checkout.
    challenge_code: Option<String>,
    /// This field indicates the reason for payer authentication response status. It is only supported for secure transactions in France.
    signed_pares_status_reason: Option<String>,
    /// This field indicates the reason why strong authentication was cancelled. It is only supported for secure transactions in France.
    challenge_cancel_code: Option<String>,
    /// This field indicates the score calculated by the 3D Securing platform. It is only supported for secure transactions in France.
    network_score: Option<u32>,
    /// This is the transaction ID generated by the access control server. This field is supported only for secure transactions in France.
    acs_transaction_id: Option<String>,
    /// This is the algorithm for generating a cardholder authentication verification value (CAVV) or universal cardholder authentication field (UCAF) data.
    cavv_algorithm: Option<String>,
}

#[derive(Debug, Serialize)]
pub enum EffectiveAuthenticationType {
    CH,
    FR,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MerchantDefinedInformation {
    key: u8,
    value: String,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CybersourceActionsList {
    TokenCreate,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum CybersourceActionsTokenType {
    Customer,
    PaymentInstrument,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CybersourceAuthorizationOptions {
    initiator: Option<CybersourcePaymentInitiator>,
    merchant_intitiated_transaction: Option<MerchantInitiatedTransaction>,
    ignore_avs_result: Option<bool>,
    ignore_cv_result: Option<bool>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MerchantInitiatedTransaction {
    reason: Option<String>,
    previous_transaction_id: Option<Secret<String>>,
    //Required for recurring mandates payment
    original_authorized_amount: Option<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CybersourcePaymentInitiator {
    #[serde(rename = "type")]
    initiator_type: Option<CybersourcePaymentInitiatorTypes>,
    credential_stored_on_file: Option<bool>,
    stored_credential_used: Option<bool>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum CybersourcePaymentInitiatorTypes {
    Customer,
    Merchant,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CaptureOptions {
    capture_sequence_number: u32,
    total_capture_count: u32,
    is_final: Option<bool>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NetworkTokenizedCard {
    number: NetworkTokenNumber,
    expiration_month: Secret<String>,
    expiration_year: Secret<String>,
    cryptogram: Option<Secret<String>>,
    transaction_type: TransactionType,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NetworkTokenPaymentInformation {
    tokenized_card: NetworkTokenizedCard,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CardPaymentInformation {
    card: Card,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenizedCard {
    number: cards::CardNumber,
    expiration_month: Secret<String>,
    expiration_year: Secret<String>,
    cryptogram: Option<Secret<String>>,
    transaction_type: TransactionType,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplePayTokenizedCard {
    transaction_type: TransactionType,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplePayTokenPaymentInformation {
    fluid_data: FluidData,
    tokenized_card: ApplePayTokenizedCard,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplePayPaymentInformation {
    tokenized_card: TokenizedCard,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MandatePaymentTokenizedCard {
    transaction_type: TransactionType,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MandateCard {
    type_selection_indicator: Option<String>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MandatePaymentInformation {
    payment_instrument: CybersoucrePaymentInstrument,
    #[serde(skip_serializing_if = "Option::is_none")]
    tokenized_card: Option<MandatePaymentTokenizedCard>,
    card: Option<MandateCard>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FluidData {
    value: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    descriptor: Option<String>,
}

pub const FLUID_DATA_DESCRIPTOR: &str = "RklEPUNPTU1PTi5BUFBMRS5JTkFQUC5QQVlNRU5U";

pub const FLUID_DATA_DESCRIPTOR_FOR_SAMSUNG_PAY: &str = "FID=COMMON.SAMSUNG.INAPP.PAYMENT";

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayTokenPaymentInformation {
    fluid_data: FluidData,
    tokenized_card: GooglePayTokenizedCard,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayTokenizedCard {
    transaction_type: TransactionType,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayPaymentInformation {
    tokenized_card: TokenizedCard,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SamsungPayTokenizedCard {
    transaction_type: TransactionType,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SamsungPayPaymentInformation {
    fluid_data: FluidData,
    tokenized_card: SamsungPayTokenizedCard,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SamsungPayFluidDataValue {
    public_key_hash: Secret<String>,
    version: String,
    data: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum PaymentInformation {
    Cards(Box<CardPaymentInformation>),
    GooglePayToken(Box<GooglePayTokenPaymentInformation>),
    GooglePay(Box<GooglePayPaymentInformation>),
    ApplePay(Box<ApplePayPaymentInformation>),
    ApplePayToken(Box<ApplePayTokenPaymentInformation>),
    MandatePayment(Box<MandatePaymentInformation>),
    SamsungPay(Box<SamsungPayPaymentInformation>),
    NetworkToken(Box<NetworkTokenPaymentInformation>),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CybersoucrePaymentInstrument {
    id: Secret<String>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Card {
    number: cards::CardNumber,
    expiration_month: Secret<String>,
    expiration_year: Secret<String>,
    security_code: Option<Secret<String>>,
    #[serde(rename = "type")]
    card_type: Option<String>,
    type_selection_indicator: Option<String>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderInformationWithBill {
    amount_details: Amount,
    bill_to: Option<BillTo>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderInformationIncrementalAuthorization {
    amount_details: AdditionalAmount,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderInformation {
    amount_details: Amount,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Amount {
    total_amount: StringMajorUnit,
    currency: api_models::enums::Currency,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AdditionalAmount {
    additional_amount: StringMajorUnit,
    currency: String,
}

#[derive(Debug, Serialize)]
pub enum PaymentSolution {
    ApplePay,
    GooglePay,
    SamsungPay,
}

#[derive(Debug, Serialize)]
pub enum TransactionType {
    #[serde(rename = "1")]
    InApp,
    #[serde(rename = "2")]
    ContactlessNFC,
    #[serde(rename = "3")]
    StoredCredentials,
}

impl From<PaymentSolution> for String {
    fn from(solution: PaymentSolution) -> Self {
        let payment_solution = match solution {
            PaymentSolution::ApplePay => "001",
            PaymentSolution::GooglePay => "012",
            PaymentSolution::SamsungPay => "008",
        };
        payment_solution.to_string()
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BillTo {
    first_name: Option<Secret<String>>,
    last_name: Option<Secret<String>>,
    address1: Option<Secret<String>>,
    locality: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    administrative_area: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    postal_code: Option<Secret<String>>,
    country: Option<enums::CountryAlpha2>,
    email: pii::Email,
}

impl From<&CybersourceRouterData<&PaymentsPreAuthenticateRouterData>>
    for ClientReferenceInformation
{
    fn from(item: &CybersourceRouterData<&PaymentsPreAuthenticateRouterData>) -> Self {
        Self {
            code: Some(item.router_data.connector_request_reference_id.clone()),
        }
    }
}

impl From<&CybersourceRouterData<&PaymentsAuthorizeRouterData>> for ClientReferenceInformation {
    fn from(item: &CybersourceRouterData<&PaymentsAuthorizeRouterData>) -> Self {
        Self {
            code: Some(item.router_data.connector_request_reference_id.clone()),
        }
    }
}

impl From<&CybersourceRouterData<&PaymentsCompleteAuthorizeRouterData>>
    for ClientReferenceInformation
{
    fn from(item: &CybersourceRouterData<&PaymentsCompleteAuthorizeRouterData>) -> Self {
        Self {
            code: Some(item.router_data.connector_request_reference_id.clone()),
        }
    }
}

impl
    TryFrom<(
        &CybersourceRouterData<&PaymentsAuthorizeRouterData>,
        Option<PaymentSolution>,
        Option<String>,
    )> for ProcessingInformation
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (item, solution, network): (
            &CybersourceRouterData<&PaymentsAuthorizeRouterData>,
            Option<PaymentSolution>,
            Option<String>,
        ),
    ) -> Result<Self, Self::Error> {
        let mut commerce_indicator = solution
            .as_ref()
            .map(|pm_solution| match pm_solution {
                PaymentSolution::ApplePay | PaymentSolution::SamsungPay => network
                    .as_ref()
                    .map(|card_network| match card_network.to_lowercase().as_str() {
                        "amex" => "internet",
                        "discover" => "internet",
                        "mastercard" => "spa",
                        "visa" => "internet",
                        _ => "internet",
                    })
                    .unwrap_or("internet"),
                PaymentSolution::GooglePay => "internet",
            })
            .unwrap_or("internet")
            .to_string();

        let connector_merchant_config =
            CybersourceConnectorMetadataObject::try_from(&item.router_data.connector_meta_data)?;

        let (action_list, action_token_types, authorization_options) = if item
            .router_data
            .request
            .setup_future_usage
            == Some(FutureUsage::OffSession)
            && (item.router_data.request.customer_acceptance.is_some()
                || item
                    .router_data
                    .request
                    .setup_mandate_details
                    .clone()
                    .is_some_and(|mandate_details| mandate_details.customer_acceptance.is_some()))
        {
            (
                Some(vec![CybersourceActionsList::TokenCreate]),
                Some(vec![
                    CybersourceActionsTokenType::PaymentInstrument,
                    CybersourceActionsTokenType::Customer,
                ]),
                Some(CybersourceAuthorizationOptions {
                    initiator: Some(CybersourcePaymentInitiator {
                        initiator_type: Some(CybersourcePaymentInitiatorTypes::Customer),
                        credential_stored_on_file: Some(true),
                        stored_credential_used: None,
                    }),
                    ignore_avs_result: connector_merchant_config.disable_avs,
                    ignore_cv_result: connector_merchant_config.disable_cvn,
                    merchant_intitiated_transaction: None,
                }),
            )
        } else if item.router_data.request.mandate_id.is_some() {
            match item
                .router_data
                .request
                .mandate_id
                .clone()
                .and_then(|mandate_id| mandate_id.mandate_reference_id)
            {
                Some(payments::MandateReferenceId::ConnectorMandateId(_)) => {
                    let original_amount = item
                        .router_data
                        .recurring_mandate_payment_data
                        .as_ref()
                        .and_then(|recurring_mandate_payment_data| {
                            recurring_mandate_payment_data.original_payment_authorized_amount
                        });

                    let original_currency = item
                        .router_data
                        .recurring_mandate_payment_data
                        .as_ref()
                        .and_then(|recurring_mandate_payment_data| {
                            recurring_mandate_payment_data.original_payment_authorized_currency
                        });

                    let original_authorized_amount = match original_amount.zip(original_currency) {
                        Some((original_amount, original_currency)) => {
                            Some(utils::get_amount_as_string(
                                &api::CurrencyUnit::Base,
                                original_amount,
                                original_currency,
                            )?)
                        }
                        None => None,
                    };
                    (
                        None,
                        None,
                        Some(CybersourceAuthorizationOptions {
                            initiator: None,
                            merchant_intitiated_transaction: Some(MerchantInitiatedTransaction {
                                reason: None,
                                original_authorized_amount,
                                previous_transaction_id: None,
                            }),
                            ignore_avs_result: connector_merchant_config.disable_avs,
                            ignore_cv_result: connector_merchant_config.disable_cvn,
                        }),
                    )
                }
                Some(payments::MandateReferenceId::NetworkMandateId(network_transaction_id)) => {
                    let (original_amount, original_currency) = match network
                        .clone()
                        .map(|network| network.to_lowercase())
                        .as_deref()
                    {
                        //This is to make original_authorized_amount mandatory for discover card networks in NetworkMandateId flow
                        Some("004") => {
                            let original_amount = Some(
                                item.router_data
                                    .get_recurring_mandate_payment_data()?
                                    .get_original_payment_amount()?,
                            );
                            let original_currency = Some(
                                item.router_data
                                    .get_recurring_mandate_payment_data()?
                                    .get_original_payment_currency()?,
                            );
                            (original_amount, original_currency)
                        }
                        _ => {
                            let original_amount = item
                                .router_data
                                .recurring_mandate_payment_data
                                .as_ref()
                                .and_then(|recurring_mandate_payment_data| {
                                    recurring_mandate_payment_data
                                        .original_payment_authorized_amount
                                });

                            let original_currency = item
                                .router_data
                                .recurring_mandate_payment_data
                                .as_ref()
                                .and_then(|recurring_mandate_payment_data| {
                                    recurring_mandate_payment_data
                                        .original_payment_authorized_currency
                                });

                            (original_amount, original_currency)
                        }
                    };
                    let original_authorized_amount = match original_amount.zip(original_currency) {
                        Some((original_amount, original_currency)) => Some(
                            utils::to_currency_base_unit(original_amount, original_currency)?,
                        ),
                        None => None,
                    };
                    commerce_indicator = "recurring".to_string();
                    (
                        None,
                        None,
                        Some(CybersourceAuthorizationOptions {
                            initiator: Some(CybersourcePaymentInitiator {
                                initiator_type: Some(CybersourcePaymentInitiatorTypes::Merchant),
                                credential_stored_on_file: None,
                                stored_credential_used: Some(true),
                            }),
                            merchant_intitiated_transaction: Some(MerchantInitiatedTransaction {
                                reason: Some("7".to_string()),
                                original_authorized_amount,
                                previous_transaction_id: Some(Secret::new(network_transaction_id)),
                            }),
                            ignore_avs_result: connector_merchant_config.disable_avs,
                            ignore_cv_result: connector_merchant_config.disable_cvn,
                        }),
                    )
                }
                Some(payments::MandateReferenceId::NetworkTokenWithNTI(mandate_data)) => {
                    let (original_amount, original_currency) = match network
                        .clone()
                        .map(|network| network.to_lowercase())
                        .as_deref()
                    {
                        //This is to make original_authorized_amount mandatory for discover card networks in NetworkMandateId flow
                        Some("004") => {
                            let original_amount = Some(
                                item.router_data
                                    .get_recurring_mandate_payment_data()?
                                    .get_original_payment_amount()?,
                            );
                            let original_currency = Some(
                                item.router_data
                                    .get_recurring_mandate_payment_data()?
                                    .get_original_payment_currency()?,
                            );
                            (original_amount, original_currency)
                        }
                        _ => {
                            let original_amount = item
                                .router_data
                                .recurring_mandate_payment_data
                                .as_ref()
                                .and_then(|recurring_mandate_payment_data| {
                                    recurring_mandate_payment_data
                                        .original_payment_authorized_amount
                                });

                            let original_currency = item
                                .router_data
                                .recurring_mandate_payment_data
                                .as_ref()
                                .and_then(|recurring_mandate_payment_data| {
                                    recurring_mandate_payment_data
                                        .original_payment_authorized_currency
                                });

                            (original_amount, original_currency)
                        }
                    };
                    let original_authorized_amount = match original_amount.zip(original_currency) {
                        Some((original_amount, original_currency)) => Some(
                            utils::to_currency_base_unit(original_amount, original_currency)?,
                        ),
                        None => None,
                    };
                    commerce_indicator = "recurring".to_string(); //
                    (
                        None,
                        None,
                        Some(CybersourceAuthorizationOptions {
                            initiator: Some(CybersourcePaymentInitiator {
                                initiator_type: Some(CybersourcePaymentInitiatorTypes::Merchant),
                                credential_stored_on_file: None,
                                stored_credential_used: Some(true),
                            }),
                            merchant_intitiated_transaction: Some(MerchantInitiatedTransaction {
                                reason: Some("7".to_string()), // 7 is for MIT using NTI
                                original_authorized_amount,
                                previous_transaction_id: Some(Secret::new(
                                    mandate_data.network_transaction_id,
                                )),
                            }),
                            ignore_avs_result: connector_merchant_config.disable_avs,
                            ignore_cv_result: connector_merchant_config.disable_cvn,
                        }),
                    )
                }
                None => (None, None, None),
            }
        } else {
            (
                None,
                None,
                Some(CybersourceAuthorizationOptions {
                    initiator: None,
                    merchant_intitiated_transaction: None,
                    ignore_avs_result: connector_merchant_config.disable_avs,
                    ignore_cv_result: connector_merchant_config.disable_cvn,
                }),
            )
        };
        // this logic is for external authenticated card
        let commerce_indicator_for_external_authentication = item
            .router_data
            .request
            .authentication_data
            .as_ref()
            .and_then(|authn_data| {
                authn_data
                    .eci
                    .clone()
                    .map(|eci| get_commerce_indicator_for_external_authentication(network, eci))
            });

        Ok(Self {
            capture: Some(matches!(
                item.router_data.request.capture_method,
                Some(enums::CaptureMethod::Automatic) | None
            )),
            payment_solution: solution.map(String::from),
            action_list,
            action_token_types,
            authorization_options,
            capture_options: None,
            commerce_indicator: commerce_indicator_for_external_authentication
                .unwrap_or(commerce_indicator),
        })
    }
}

fn get_commerce_indicator_for_external_authentication(
    card_network: Option<String>,
    eci: String,
) -> String {
    let card_network_lower_case = card_network
        .as_ref()
        .map(|card_network| card_network.to_lowercase());
    match eci.as_str() {
        "00" | "01" | "02" => {
            if matches!(
                card_network_lower_case.as_deref(),
                Some("mastercard") | Some("maestro")
            ) {
                "spa"
            } else {
                "internet"
            }
        }
        "05" => match card_network_lower_case.as_deref() {
            Some("amex") => "aesk",
            Some("discover") => "dipb",
            Some("mastercard") => "spa",
            Some("visa") => "vbv",
            Some("diners") => "pb",
            Some("upi") => "up3ds",
            _ => "internet",
        },
        "06" => match card_network_lower_case.as_deref() {
            Some("amex") => "aesk_attempted",
            Some("discover") => "dipb_attempted",
            Some("mastercard") => "spa",
            Some("visa") => "vbv_attempted",
            Some("diners") => "pb_attempted",


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

pub fn get_connector_data(
    connectors: &mut IntoIter<api::ConnectorRoutingData>,
) -> RouterResult<api::ConnectorRoutingData>


// Implementation: impl api::Refund for for UnifiedAuthenticationService
// File: crates/hyperswitch_connectors/src/connectors/unified_authentication_service.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Refund for for UnifiedAuthenticationService


// Function: get_sdk_event
// File: crates/analytics/src/sdk_events/events.rs
// Module: analytics

pub fn get_sdk_event<T>(
    publishable_key: &String,
    request: SdkEventsRequest,
    pool: &T,
) -> FiltersResult<Vec<SdkEventsResult>>
where
    T: AnalyticsDataSource + SdkEventsFilterAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,


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

impl Responder


// File: crates/common_utils/src/id_type/authentication.rs
// Module: common_utils
// Public functions: 2

crate::id_type!(
    AuthenticationId,
    "A type for authentication_id that can be used for authentication IDs"
);
crate::impl_id_type_methods!(AuthenticationId, "authentication_id");

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

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

impl AuthenticationId {
    /// Generate Authentication Id from prefix
    pub fn generate_authentication_id(prefix: &'static str) -> Self {
        Self(crate::generate_ref_id_with_default_length(prefix))
    }

    /// Get external authentication request poll id
    pub fn get_external_authentication_request_poll_id(&self) -> String {
        format!("external_authentication_{}", self.get_string_repr())
    }
}

impl crate::events::ApiEventMetric for AuthenticationId {
    fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
        Some(crate::events::ApiEventsType::Authentication {
            authentication_id: self.clone(),
        })
    }
}

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

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

crate::impl_default_id_type!(AuthenticationId, "authentication");


// Struct: BreadpayTransactionRequest
// File: crates/hyperswitch_connectors/src/connectors/breadpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BreadpayTransactionRequest


// Implementation: impl ConnectorSpecifications for for Tsys
// File: crates/hyperswitch_connectors/src/connectors/tsys.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl ConnectorSpecifications for for Tsys


// Struct: Hypersense
// File: crates/router/src/routes/app.rs
// Module: router
// Implementations: 1

pub struct Hypersense


// Struct: UserIdFromAuth
// File: crates/router/src/services/authentication.rs
// Module: router
// Implementations: 0

pub struct UserIdFromAuth


// Function: payment_data
// File: crates/router/src/routes/dummy_connector/core.rs
// Module: router

pub fn payment_data(
    state: SessionState,
    req: types::DummyConnectorPaymentRetrieveRequest,
) -> types::DummyConnectorResponse<types::DummyConnectorPaymentResponse>


// Implementation: impl api::PaymentSession for for Helcim
// File: crates/hyperswitch_connectors/src/connectors/helcim.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSession for for Helcim


// Function: payouts_list_filters
// File: crates/openapi/src/routes/payouts.rs
// Module: openapi

pub fn payouts_list_filters()


// Function: populate_browser_info
// File: crates/router/src/routes/payments/helpers.rs
// Module: router

pub fn populate_browser_info(
    req: &actix_web::HttpRequest,
    payload: &mut api::PaymentsRequest,
    header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
) -> RouterResult<()>


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

pub struct CreditCardResponseInfo


// Implementation: impl VirInterpreterBackend
// File: crates/euclid/src/backend/vir_interpreter.rs
// Module: euclid
// Methods: 5 total (0 public)

impl VirInterpreterBackend


// Implementation: impl api::PaymentSync for for Paystack
// File: crates/hyperswitch_connectors/src/connectors/paystack.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Paystack


// Struct: ChargebeePaymentMethod
// File: crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ChargebeePaymentMethod


// Function: get_total_surcharge_amount
// File: crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
// Module: hyperswitch_domain_models

pub fn get_total_surcharge_amount(&self) -> Option<MinorUnit>


// Trait: ApiEventMetricAnalytics
// File: crates/analytics/src/api_event/metrics.rs
// Module: analytics

pub trait ApiEventMetricAnalytics: LoadRow<ApiEventMetricRow> + LoadRow<LatencyAvg>


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

impl api::RefundExecute for for Payeezy


// Function: get_file_storage_client
// File: crates/external_services/src/file_storage.rs
// Module: external_services

// Documentation: Retrieves the appropriate file storage client based on the file storage configuration.

pub fn get_file_storage_client(&self) -> Arc<dyn FileStorageInterface>


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

impl api::PaymentCapture for for Thunes


// Implementation: impl EliminationRoutingAlgorithm
// File: crates/api_models/src/routing.rs
// Module: api_models
// Methods: 1 total (1 public)

impl EliminationRoutingAlgorithm


// Implementation: impl ConstraintGraph
// File: crates/hyperswitch_constraint_graph/src/graph.rs
// Module: hyperswitch_constraint_graph
// Methods: 9 total (3 public)

impl ConstraintGraph


// Implementation: impl ConnectorCommon for for Bambora
// File: crates/hyperswitch_connectors/src/connectors/bambora.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl ConnectorCommon for for Bambora


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

pub fn get_merchant_reference_id(&self) -> Option<id_type::CustomerId>


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

impl Responder


// Implementation: impl WalletData
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Methods: 3 total (3 public)

impl WalletData


// File: crates/router/tests/connectors/helcim.rs
// Module: router

use masking::Secret;
use router::types::{self, domain, storage::enums};
use test_utils::connector_auth;

use crate::utils::{self, ConnectorActions};

#[derive(Clone, Copy)]
struct HelcimTest;
impl ConnectorActions for HelcimTest {}
impl utils::Connector for HelcimTest {
    fn get_data(&self) -> types::api::ConnectorData {
        use router::connector::Helcim;
        utils::construct_connector_data_old(
            Box::new(Helcim::new()),
            types::Connector::Helcim,
            types::api::GetToken::Connector,
            None,
        )
    }

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .helcim
                .expect("Missing connector authentication configuration")
                .into(),
        )
    }

    fn get_name(&self) -> String {
        "helcim".to_string()
    }
}

static CONNECTOR: HelcimTest = HelcimTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    None
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}

// Captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Partially captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(
            payment_method_details(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized,);
}

// Voids a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_void_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_void_payment(
            payment_method_details(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: String::from(""),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}

// Refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Synchronizes a refund using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_manually_captured_refund() {
    let refund_response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_make_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_auto_captured_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                capture_method: Some(enums::CaptureMethod::Automatic),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged,);
}

// Refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_auto_captured_payment() {
    let response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        refund_response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() {
    CONNECTOR
        .make_payment_and_multiple_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
}

// Synchronizes a refund using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_refund() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Cards Negative scenarios
// Creates a payment with incorrect CVC.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_cvc() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_cvc: Secret::new("12345".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's security code is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry month.
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_exp_month: Secret::new("20".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration month is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry year.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_expiry_year() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_exp_year: Secret::new("2000".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration year is invalid.".to_string(),
    );
}

// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let void_response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        void_response.response.unwrap_err().message,
        "You cannot cancel this PaymentIntent because it has a status of succeeded."
    );
}

// Captures a payment using invalid connector payment id.
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let capture_response = CONNECTOR
        .capture_payment("123456789".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        capture_response.response.unwrap_err().message,
        String::from("No such payment_intent: '123456789'")
    );
}

// Refunds a payment with refund amount higher than payment amount.
#[actix_web::test]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
    let response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 150,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Refund amount (₹1.50) is greater than charge amount (₹1.00)",
    );
}

// Connector dependent test cases goes here

// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests


// Struct: RefundListConstraints
// File: crates/hyperswitch_domain_models/src/refunds.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct RefundListConstraints