// File: crates/router/src/types.rs
// Module: router
// Public functions: 1
// Public structs: 11

// FIXME: Why were these data types grouped this way?
//
// Folder `types` is strange for Rust ecosystem, nevertheless it might be okay.
// But folder `enum` is even more strange I unlikely okay. Why should not we introduce folders `type`, `structs` and `traits`? :)
// Is it better to split data types according to business logic instead.
// For example, customers/address/dispute/mandate is "models".
// Separation of concerns instead of separation of forms.

pub mod api;
pub mod authentication;
pub mod connector_transformers;
pub mod domain;
#[cfg(feature = "frm")]
pub mod fraud_check;
pub mod payment_methods;
pub mod pm_auth;
use masking::Secret;
pub mod storage;
pub mod transformers;
use std::marker::PhantomData;

pub use api_models::{enums::Connector, mandates};
#[cfg(feature = "payouts")]
pub use api_models::{enums::PayoutConnectors, payouts as payout_types};
#[cfg(feature = "v2")]
use common_utils::errors::CustomResult;
pub use common_utils::{pii, pii::Email, request::RequestContent, types::MinorUnit};
#[cfg(feature = "v2")]
use error_stack::ResultExt;
#[cfg(feature = "frm")]
pub use hyperswitch_domain_models::router_data_v2::FrmFlowData;
use hyperswitch_domain_models::router_flow_types::{
    self,
    access_token_auth::AccessTokenAuth,
    dispute::{Accept, Defend, Dsync, Evidence, Fetch},
    files::{Retrieve, Upload},
    mandate_revoke::MandateRevoke,
    payments::{
        Approve, Authorize, AuthorizeSessionToken, Balance, CalculateTax, Capture,
        CompleteAuthorize, CreateConnectorCustomer, CreateOrder, ExternalVaultProxy,
        IncrementalAuthorization, InitPayment, PSync, PostCaptureVoid, PostProcessing,
        PostSessionTokens, PreProcessing, Reject, SdkSessionUpdate, Session, SetupMandate,
        UpdateMetadata, Void,
    },
    refunds::{Execute, RSync},
    webhooks::VerifyWebhookSource,
};
pub use hyperswitch_domain_models::{
    payment_address::PaymentAddress,
    router_data::{
        AccessToken, AccessTokenAuthenticationResponse, AdditionalPaymentMethodConnectorResponse,
        ConnectorAuthType, ConnectorResponseData, ErrorResponse, GooglePayPaymentMethodDetails,
        GooglePayPredecryptDataInternal, L2L3Data, PaymentMethodBalance, PaymentMethodToken,
        RecurringMandatePaymentData, RouterData,
    },
    router_data_v2::{
        AccessTokenFlowData, AuthenticationTokenFlowData, DisputesFlowData,
        ExternalAuthenticationFlowData, FilesFlowData, MandateRevokeFlowData, PaymentFlowData,
        RefundFlowData, RouterDataV2, UasFlowData, WebhookSourceVerifyData,
    },
    router_request_types::{
        revenue_recovery::{
            BillingConnectorInvoiceSyncRequest, BillingConnectorPaymentsSyncRequest,
            InvoiceRecordBackRequest,
        },
        unified_authentication_service::{
            UasAuthenticationRequestData, UasAuthenticationResponseData,
            UasConfirmationRequestData, UasPostAuthenticationRequestData,
            UasPreAuthenticationRequestData,
        },
        AcceptDisputeRequestData, AccessTokenAuthenticationRequestData, AccessTokenRequestData,
        AuthorizeSessionTokenData, BrowserInformation, ChargeRefunds, ChargeRefundsOptions,
        CompleteAuthorizeData, CompleteAuthorizeRedirectResponse, ConnectorCustomerData,
        CreateOrderRequestData, DefendDisputeRequestData, DestinationChargeRefund,
        DirectChargeRefund, DisputeSyncData, ExternalVaultProxyPaymentsData,
        FetchDisputesRequestData, MandateRevokeRequestData, MultipleCaptureRequestData,
        PaymentMethodTokenizationData, PaymentsApproveData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCancelPostCaptureData, PaymentsCaptureData,
        PaymentsIncrementalAuthorizationData, PaymentsPostProcessingData,
        PaymentsPostSessionTokensData, PaymentsPreProcessingData, PaymentsRejectData,
        PaymentsSessionData, PaymentsSyncData, PaymentsTaxCalculationData,
        PaymentsUpdateMetadataData, RefundsData, ResponseId, RetrieveFileRequestData,
        SdkPaymentsSessionUpdateData, SetupMandateRequestData, SplitRefundsRequest,
        SubmitEvidenceRequestData, SyncRequestType, UploadFileRequestData, VaultRequestData,
        VerifyWebhookSourceRequestData,
    },
    router_response_types::{
        revenue_recovery::{
            BillingConnectorInvoiceSyncResponse, BillingConnectorPaymentsSyncResponse,
            InvoiceRecordBackResponse,
        },
        AcceptDisputeResponse, CaptureSyncResponse, DefendDisputeResponse, DisputeSyncResponse,
        FetchDisputesResponse, MandateReference, MandateRevokeResponseData, PaymentsResponseData,
        PreprocessingResponseId, RefundsResponseData, RetrieveFileResponse, SubmitEvidenceResponse,
        TaxCalculationResponseData, UploadFileResponse, VaultResponseData,
        VerifyWebhookSourceResponseData, VerifyWebhookStatus,
    },
};
#[cfg(feature = "payouts")]
pub use hyperswitch_domain_models::{
    router_data_v2::PayoutFlowData, router_request_types::PayoutsData,
    router_response_types::PayoutsResponseData,
};
#[cfg(feature = "payouts")]
pub use hyperswitch_interfaces::types::{
    PayoutCancelType, PayoutCreateType, PayoutEligibilityType, PayoutFulfillType, PayoutQuoteType,
    PayoutRecipientAccountType, PayoutRecipientType, PayoutSyncType,
};
pub use hyperswitch_interfaces::{
    disputes::DisputePayload,
    types::{
        AcceptDisputeType, ConnectorCustomerType, DefendDisputeType, FetchDisputesType,
        IncrementalAuthorizationType, MandateRevokeType, PaymentsAuthorizeType,
        PaymentsBalanceType, PaymentsCaptureType, PaymentsCompleteAuthorizeType, PaymentsInitType,
        PaymentsPostCaptureVoidType, PaymentsPostProcessingType, PaymentsPostSessionTokensType,
        PaymentsPreAuthorizeType, PaymentsPreProcessingType, PaymentsSessionType, PaymentsSyncType,
        PaymentsUpdateMetadataType, PaymentsVoidType, RefreshTokenType, RefundExecuteType,
        RefundSyncType, Response, RetrieveFileType, SdkSessionUpdateType, SetupMandateType,
        SubmitEvidenceType, TokenizationType, UploadFileType, VerifyWebhookSourceType,
    },
};

#[cfg(feature = "v2")]
use crate::core::errors;
pub use crate::core::payments::CustomerDetails;
#[cfg(feature = "payouts")]
use crate::core::utils::IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_PAYOUTS_FLOW;
use crate::{
    consts,
    core::payments::{OperationSessionGetters, PaymentData},
    services,
    types::transformers::{ForeignFrom, ForeignTryFrom},
};

pub type PaymentsAuthorizeRouterData =
    RouterData<Authorize, PaymentsAuthorizeData, PaymentsResponseData>;
pub type ExternalVaultProxyPaymentsRouterData =
    RouterData<ExternalVaultProxy, ExternalVaultProxyPaymentsData, PaymentsResponseData>;
pub type PaymentsPreProcessingRouterData =
    RouterData<PreProcessing, PaymentsPreProcessingData, PaymentsResponseData>;
pub type PaymentsPostProcessingRouterData =
    RouterData<PostProcessing, PaymentsPostProcessingData, PaymentsResponseData>;
pub type PaymentsAuthorizeSessionTokenRouterData =
    RouterData<AuthorizeSessionToken, AuthorizeSessionTokenData, PaymentsResponseData>;
pub type PaymentsCompleteAuthorizeRouterData =
    RouterData<CompleteAuthorize, CompleteAuthorizeData, PaymentsResponseData>;
pub type PaymentsInitRouterData =
    RouterData<InitPayment, PaymentsAuthorizeData, PaymentsResponseData>;
pub type PaymentsBalanceRouterData =
    RouterData<Balance, PaymentsAuthorizeData, PaymentsResponseData>;
pub type PaymentsSyncRouterData = RouterData<PSync, PaymentsSyncData, PaymentsResponseData>;
pub type PaymentsCaptureRouterData = RouterData<Capture, PaymentsCaptureData, PaymentsResponseData>;
pub type PaymentsIncrementalAuthorizationRouterData = RouterData<
    IncrementalAuthorization,
    PaymentsIncrementalAuthorizationData,
    PaymentsResponseData,
>;
pub type PaymentsTaxCalculationRouterData =
    RouterData<CalculateTax, PaymentsTaxCalculationData, TaxCalculationResponseData>;

pub type CreateOrderRouterData =
    RouterData<CreateOrder, CreateOrderRequestData, PaymentsResponseData>;

pub type SdkSessionUpdateRouterData =
    RouterData<SdkSessionUpdate, SdkPaymentsSessionUpdateData, PaymentsResponseData>;

pub type PaymentsPostSessionTokensRouterData =
    RouterData<PostSessionTokens, PaymentsPostSessionTokensData, PaymentsResponseData>;

pub type PaymentsUpdateMetadataRouterData =
    RouterData<UpdateMetadata, PaymentsUpdateMetadataData, PaymentsResponseData>;

pub type PaymentsCancelRouterData = RouterData<Void, PaymentsCancelData, PaymentsResponseData>;
pub type PaymentsCancelPostCaptureRouterData =
    RouterData<PostCaptureVoid, PaymentsCancelPostCaptureData, PaymentsResponseData>;
pub type PaymentsRejectRouterData = RouterData<Reject, PaymentsRejectData, PaymentsResponseData>;
pub type PaymentsApproveRouterData = RouterData<Approve, PaymentsApproveData, PaymentsResponseData>;
pub type PaymentsSessionRouterData = RouterData<Session, PaymentsSessionData, PaymentsResponseData>;
pub type RefundsRouterData<F> = RouterData<F, RefundsData, RefundsResponseData>;
pub type RefundExecuteRouterData = RouterData<Execute, RefundsData, RefundsResponseData>;
pub type RefundSyncRouterData = RouterData<RSync, RefundsData, RefundsResponseData>;
pub type TokenizationRouterData = RouterData<
    router_flow_types::PaymentMethodToken,
    PaymentMethodTokenizationData,
    PaymentsResponseData,
>;
pub type ConnectorCustomerRouterData =
    RouterData<CreateConnectorCustomer, ConnectorCustomerData, PaymentsResponseData>;

pub type RefreshTokenRouterData = RouterData<AccessTokenAuth, AccessTokenRequestData, AccessToken>;

pub type PaymentsResponseRouterData<R> =
    ResponseRouterData<Authorize, R, PaymentsAuthorizeData, PaymentsResponseData>;
pub type PaymentsCancelResponseRouterData<R> =
    ResponseRouterData<Void, R, PaymentsCancelData, PaymentsResponseData>;
pub type PaymentsCancelPostCaptureResponseRouterData<R> =
    ResponseRouterData<PostCaptureVoid, R, PaymentsCancelPostCaptureData, PaymentsResponseData>;
pub type PaymentsBalanceResponseRouterData<R> =
    ResponseRouterData<Balance, R, PaymentsAuthorizeData, PaymentsResponseData>;
pub type PaymentsSyncResponseRouterData<R> =
    ResponseRouterData<PSync, R, PaymentsSyncData, PaymentsResponseData>;
pub type PaymentsSessionResponseRouterData<R> =
    ResponseRouterData<Session, R, PaymentsSessionData, PaymentsResponseData>;
pub type PaymentsInitResponseRouterData<R> =
    ResponseRouterData<InitPayment, R, PaymentsAuthorizeData, PaymentsResponseData>;
pub type SdkSessionUpdateResponseRouterData<R> =
    ResponseRouterData<SdkSessionUpdate, R, SdkPaymentsSessionUpdateData, PaymentsResponseData>;
pub type PaymentsCaptureResponseRouterData<R> =
    ResponseRouterData<Capture, R, PaymentsCaptureData, PaymentsResponseData>;
pub type PaymentsPreprocessingResponseRouterData<R> =
    ResponseRouterData<PreProcessing, R, PaymentsPreProcessingData, PaymentsResponseData>;
pub type TokenizationResponseRouterData<R> =
    ResponseRouterData<PaymentMethodToken, R, PaymentMethodTokenizationData, PaymentsResponseData>;
pub type ConnectorCustomerResponseRouterData<R> =
    ResponseRouterData<CreateConnectorCustomer, R, ConnectorCustomerData, PaymentsResponseData>;

pub type RefundsResponseRouterData<F, R> =
    ResponseRouterData<F, R, RefundsData, RefundsResponseData>;

pub type SetupMandateRouterData =
    RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>;

pub type AcceptDisputeRouterData =
    RouterData<Accept, AcceptDisputeRequestData, AcceptDisputeResponse>;

pub type VerifyWebhookSourceRouterData = RouterData<
    VerifyWebhookSource,
    VerifyWebhookSourceRequestData,
    VerifyWebhookSourceResponseData,
>;

pub type SubmitEvidenceRouterData =
    RouterData<Evidence, SubmitEvidenceRequestData, SubmitEvidenceResponse>;

pub type UploadFileRouterData = RouterData<Upload, UploadFileRequestData, UploadFileResponse>;

pub type RetrieveFileRouterData =
    RouterData<Retrieve, RetrieveFileRequestData, RetrieveFileResponse>;

pub type DefendDisputeRouterData =
    RouterData<Defend, DefendDisputeRequestData, DefendDisputeResponse>;

pub type FetchDisputesRouterData =
    RouterData<Fetch, FetchDisputesRequestData, FetchDisputesResponse>;

pub type DisputeSyncRouterData = RouterData<Dsync, DisputeSyncData, DisputeSyncResponse>;

pub type MandateRevokeRouterData =
    RouterData<MandateRevoke, MandateRevokeRequestData, MandateRevokeResponseData>;

#[cfg(feature = "payouts")]
pub type PayoutsRouterData<F> = RouterData<F, PayoutsData, PayoutsResponseData>;

#[cfg(feature = "payouts")]
pub type PayoutsResponseRouterData<F, R> =
    ResponseRouterData<F, R, PayoutsData, PayoutsResponseData>;

#[cfg(feature = "payouts")]
pub type PayoutActionData = Vec<(
    storage::Payouts,
    storage::PayoutAttempt,
    Option<domain::Customer>,
    Option<api_models::payments::Address>,
)>;

#[cfg(feature = "payouts")]
pub trait PayoutIndividualDetailsExt {
    type Error;
    fn get_external_account_account_holder_type(&self) -> Result<String, Self::Error>;
}

pub trait Capturable {
    fn get_captured_amount<F>(
        &self,
        _amount_captured: Option<i64>,
        _payment_data: &PaymentData<F>,
    ) -> Option<i64>
    where
        F: Clone,
    {
        None
    }
    fn get_amount_capturable<F>(
        &self,
        _payment_data: &PaymentData<F>,
        _get_amount_capturable: Option<i64>,
        _attempt_status: common_enums::AttemptStatus,
    ) -> Option<i64>
    where
        F: Clone,
    {
        None
    }
}

#[cfg(feature = "v1")]
impl Capturable for PaymentsAuthorizeData {
    fn get_captured_amount<F>(
        &self,
        amount_captured: Option<i64>,
        payment_data: &PaymentData<F>,
    ) -> Option<i64>
    where
        F: Clone,
    {
        amount_captured.or(Some(
            payment_data
                .payment_attempt
                .get_total_amount()
                .get_amount_as_i64(),
        ))
    }

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

#[cfg(feature = "v1")]
impl Capturable for PaymentsCaptureData {
    fn get_captured_amount<F>(
        &self,
        _amount_captured: Option<i64>,
        _payment_data: &PaymentData<F>,
    ) -> Option<i64>
    where
        F: Clone,
    {
        Some(self.amount_to_capture)
    }
    fn get_amount_capturable<F>(
        &self,
        _payment_data: &PaymentData<F>,
        _amount_capturable: Option<i64>,
        attempt_status: common_enums::AttemptStatus,
    ) -> Option<i64>
    where
        F: Clone,
    {
        let intent_status = common_enums::IntentStatus::foreign_from(attempt_status);
        match intent_status {
            common_enums::IntentStatus::Succeeded
            | common_enums::IntentStatus::PartiallyCaptured
            | common_enums::IntentStatus::Conflicted
            | common_enums::IntentStatus::Expired => Some(0),
            common_enums::IntentStatus::Processing
            | common_enums::IntentStatus::Cancelled
            | common_enums::IntentStatus::CancelledPostCapture
            | common_enums::IntentStatus::Failed
            | common_enums::IntentStatus::RequiresCustomerAction
            | common_enums::IntentStatus::RequiresMerchantAction
            | common_enums::IntentStatus::RequiresPaymentMethod
            | common_enums::IntentStatus::RequiresConfirmation
            | common_enums::IntentStatus::RequiresCapture
            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
            | common_enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture => None,
        }
    }
}

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

impl Capturable for SetupMandateRequestData {}
impl Capturable for PaymentsTaxCalculationData {}
impl Capturable for SdkPaymentsSessionUpdateData {}
impl Capturable for PaymentsPostSessionTokensData {}
impl Capturable for PaymentsUpdateMetadataData {}
impl Capturable for PaymentsCancelData {
    fn get_captured_amount<F>(
        &self,
        _amount_captured: Option<i64>,
        payment_data: &PaymentData<F>,
    ) -> Option<i64>
    where
        F: Clone,
    {
        // return previously captured amount
        payment_data
            .payment_intent
            .amount_captured
            .map(|amt| amt.get_amount_as_i64())
    }
    fn get_amount_capturable<F>(
        &self,
        _payment_data: &PaymentData<F>,
        _amount_capturable: Option<i64>,
        attempt_status: common_enums::AttemptStatus,
    ) -> Option<i64>
    where
        F: Clone,
    {
        let intent_status = common_enums::IntentStatus::foreign_from(attempt_status);
        match intent_status {
            common_enums::IntentStatus::Cancelled
            | common_enums::IntentStatus::CancelledPostCapture
            | common_enums::IntentStatus::Processing
            | common_enums::IntentStatus::PartiallyCaptured
            | common_enums::IntentStatus::Conflicted
            | common_enums::IntentStatus::Expired => Some(0),
            common_enums::IntentStatus::Succeeded
            | common_enums::IntentStatus::Failed
            | common_enums::IntentStatus::RequiresCustomerAction
            | common_enums::IntentStatus::RequiresMerchantAction
            | common_enums::IntentStatus::RequiresPaymentMethod
            | common_enums::IntentStatus::RequiresConfirmation
            | common_enums::IntentStatus::RequiresCapture
            | common_enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture
            | common_enums::IntentStatus::PartiallyCapturedAndCapturable => None,
        }
    }
}
impl Capturable for PaymentsCancelPostCaptureData {
    fn get_captured_amount<F>(
        &self,
        _amount_captured: Option<i64>,
        payment_data: &PaymentData<F>,
    ) -> Option<i64>
    where
        F: Clone,
    {
        // return previously captured amount
        payment_data
            .payment_intent
            .amount_captured
            .map(|amt| amt.get_amount_as_i64())
    }
    fn get_amount_capturable<F>(
        &self,
        _payment_data: &PaymentData<F>,
        _amount_capturable: Option<i64>,
        attempt_status: common_enums::AttemptStatus,
    ) -> Option<i64>
    where
        F: Clone,
    {
        let intent_status = common_enums::IntentStatus::foreign_from(attempt_status);
        match intent_status {
            common_enums::IntentStatus::Cancelled
            | common_enums::IntentStatus::CancelledPostCapture
            | common_enums::IntentStatus::PartiallyCaptured
            | common_enums::IntentStatus::Conflicted
            | common_enums::IntentStatus::Expired => Some(0),
            common_enums::IntentStatus::Succeeded
            | common_enums::IntentStatus::Failed
            | common_enums::IntentStatus::RequiresCustomerAction
            | common_enums::IntentStatus::RequiresMerchantAction
            | common_enums::IntentStatus::RequiresPaymentMethod
            | common_enums::IntentStatus::RequiresConfirmation
            | common_enums::IntentStatus::RequiresCapture
            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
            | common_enums::IntentStatus::Processing
            | common_enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture => None,
        }
    }
}
impl Capturable for PaymentsApproveData {}
impl Capturable for PaymentsRejectData {}
impl Capturable for PaymentsSessionData {}
impl Capturable for PaymentsIncrementalAuthorizationData {
    fn get_amount_capturable<F>(
        &self,
        _payment_data: &PaymentData<F>,
        amount_capturable: Option<i64>,
        _attempt_status: common_enums::AttemptStatus,
    ) -> Option<i64>
    where
        F: Clone,
    {
        amount_capturable.or(Some(self.total_amount))
    }
}
impl Capturable for PaymentsSyncData {
    #[cfg(feature = "v1")]
    fn get_captured_amount<F>(
        &self,
        _amount_captured: Option<i64>,
        payment_data: &PaymentData<F>,
    ) -> Option<i64>
    where
        F: Clone,
    {
        payment_data
            .payment_attempt
            .amount_to_capture
            .or(payment_data.payment_intent.amount_captured)
            .or_else(|| Some(payment_data.payment_attempt.get_total_amount()))
            .map(|amt| amt.get_amount_as_i64())
    }

    #[cfg(feature = "v2")]
    fn get_captured_amount<F>(
        &self,
        _amount_captured: Option<i64>,
        payment_data: &PaymentData<F>,
    ) -> Option<i64>
    where
        F: Clone,
    {
        // TODO: add a getter for this
        payment_data
            .payment_attempt
            .amount_details
            .get_amount_to_capture()
            .or_else(|| Some(payment_data.payment_attempt.get_total_amount()))
            .map(|amt| amt.get_amount_as_i64())
    }

    #[cfg(feature = "v1")]
    fn get_amount_capturable<F>(
        &self,
        payment_data: &PaymentData<F>,
        amount_capturable: Option<i64>,
        attempt_status: common_enums::AttemptStatus,
    ) -> Option<i64>
    where
        F: Clone,
    {
        if attempt_status.is_terminal_status() {
            Some(0)
        } else {
            amount_capturable.or(Some(MinorUnit::get_amount_as_i64(
                payment_data.payment_attempt.amount_capturable,
            )))
        }
    }

    #[cfg(feature = "v2")]
    fn get_amount_capturable<F>(
        &self,
        payment_data: &PaymentData<F>,
        amount_capturable: Option<i64>,
        attempt_status: common_enums::AttemptStatus,
    ) -> Option<i64>
    where
        F: Clone,
    {
        if attempt_status.is_terminal_status() {
            Some(0)
        } else {
            None
        }
    }
}

pub struct AddAccessTokenResult {
    pub access_token_result: Result<Option<AccessToken>, ErrorResponse>,
    pub connector_supports_access_token: bool,
}

pub struct PaymentMethodTokenResult {
    pub payment_method_token_result: Result<Option<String>, ErrorResponse>,
    pub is_payment_method_tokenization_performed: bool,
    pub connector_response: Option<ConnectorResponseData>,
}

#[derive(Clone)]
pub struct CreateOrderResult {
    pub create_order_result: Result<String, ErrorResponse>,
}

pub struct PspTokenResult {
    pub token: Result<String, ErrorResponse>,
}

#[derive(Debug, Clone, Copy)]
pub enum Redirection {
    Redirect,
    NoRedirect,
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct PollConfig {
    pub delay_in_secs: i8,
    pub frequency: i8,
}

impl PollConfig {
    pub fn get_poll_config_key(connector: String) -> String {
        format!("poll_config_external_three_ds_{connector}")
    }
}

impl Default for PollConfig {
    fn default() -> Self {
        Self {
            delay_in_secs: consts::DEFAULT_POLL_DELAY_IN_SECS,
            frequency: consts::DEFAULT_POLL_FREQUENCY,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug)]
pub struct RedirectPaymentFlowResponse {
    pub payments_response: api_models::payments::PaymentsResponse,
    pub business_profile: domain::Profile,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug)]
pub struct RedirectPaymentFlowResponse<D> {
    pub payment_data: D,
    pub profile: domain::Profile,
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug)]
pub struct AuthenticatePaymentFlowResponse {
    pub payments_response: api_models::payments::PaymentsResponse,
    pub poll_config: PollConfig,
    pub business_profile: domain::Profile,
}

#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
pub struct ConnectorResponse {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub connector: String,
    pub payment_id: common_utils::id_type::PaymentId,
    pub amount: i64,
    pub connector_transaction_id: String,
    pub return_url: Option<String>,
    pub three_ds_form: Option<services::RedirectForm>,
}

pub struct ResponseRouterData<Flow, R, Request, Response> {
    pub response: R,
    pub data: RouterData<Flow, Request, Response>,
    pub http_code: u16,
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub enum RecipientIdType {
    ConnectorId(Secret<String>),
    LockerId(Secret<String>),
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MerchantAccountData {
    Iban {
        iban: Secret<String>,
        name: String,
        connector_recipient_id: Option<RecipientIdType>,
    },
    Bacs {
        account_number: Secret<String>,
        sort_code: Secret<String>,
        name: String,
        connector_recipient_id: Option<RecipientIdType>,
    },
    FasterPayments {
        account_number: Secret<String>,
        sort_code: Secret<String>,
        name: String,
        connector_recipient_id: Option<RecipientIdType>,
    },
    Sepa {
        iban: Secret<String>,
        name: String,
        connector_recipient_id: Option<RecipientIdType>,
    },
    SepaInstant {
        iban: Secret<String>,
        name: String,
        connector_recipient_id: Option<RecipientIdType>,
    },
    Elixir {
        account_number: Secret<String>,
        iban: Secret<String>,
        name: String,
        connector_recipient_id: Option<RecipientIdType>,
    },
    Bankgiro {
        number: Secret<String>,
        name: String,
        connector_recipient_id: Option<RecipientIdType>,
    },
    Plusgiro {
        number: Secret<String>,
        name: String,
        connector_recipient_id: Option<RecipientIdType>,
    },
}

impl ForeignFrom<MerchantAccountData> for api_models::admin::MerchantAccountData {
    fn foreign_from(from: MerchantAccountData) -> Self {
        match from {
            MerchantAccountData::Iban {
                iban,
                name,
                connector_recipient_id,
            } => Self::Iban {
                iban,
                name,
                connector_recipient_id: match connector_recipient_id {
                    Some(RecipientIdType::ConnectorId(id)) => Some(id.clone()),
                    _ => None,
                },
            },
            MerchantAccountData::Bacs {
                account_number,
                sort_code,
                name,
                connector_recipient_id,
            } => Self::Bacs {
                account_number,
                sort_code,
                name,
                connector_recipient_id: match connector_recipient_id {
                    Some(RecipientIdType::ConnectorId(id)) => Some(id.clone()),
                    _ => None,
                },
            },
            MerchantAccountData::FasterPayments {
                account_number,
                sort_code,
                name,
                connector_recipient_id,
            } => Self::FasterPayments {
                account_number,
                sort_code,
                name,
                connector_recipient_id: match connector_recipient_id {
                    Some(RecipientIdType::ConnectorId(id)) => Some(id.clone()),
                    _ => None,
                },
            },
            MerchantAccountData::Sepa {
                iban,
                name,
                connector_recipient_id,
            } => Self::Sepa {
                iban,
                name,
                connector_recipient_id: match connector_recipient_id {
                    Some(RecipientIdType::ConnectorId(id)) => Some(id.clone()),
                    _ => None,
                },
            },
            MerchantAccountData::SepaInstant {
                iban,
                name,
                connector_recipient_id,
            } => Self::SepaInstant {
                iban,
                name,
                connector_recipient_id: match connector_recipient_id {
                    Some(RecipientIdType::ConnectorId(id)) => Some(id.clone()),
                    _ => None,
                },
            },
            MerchantAccountData::Elixir {
                account_number,
                iban,
                name,
                connector_recipient_id,
            } => Self::Elixir {
                account_number,
                iban,
                name,
                connector_recipient_id: match connector_recipient_id {
                    Some(RecipientIdType::ConnectorId(id)) => Some(id.clone()),
                    _ => None,
                },
            },
            MerchantAccountData::Bankgiro {
                number,
                name,
                connector_recipient_id,
            } => Self::Bankgiro {
                number,
                name,
                connector_recipient_id: match connector_recipient_id {
                    Some(RecipientIdType::ConnectorId(id)) => Some(id.clone()),
                    _ => None,
                },
            },
            MerchantAccountData::Plusgiro {
                number,
                name,
                connector_recipient_id,
            } => Self::Plusgiro {
                number,
                name,
                connector_recipient_id: match connector_recipient_id {
                    Some(RecipientIdType::ConnectorId(id)) => Some(id.clone()),
                    _ => None,
                },
            },
        }
    }
}

impl From<api_models::admin::MerchantAccountData> for MerchantAccountData {
    fn from(from: api_models::admin::MerchantAccountData) -> Self {
        match from {
            api_models::admin::MerchantAccountData::Iban {
                iban,
                name,
                connector_recipient_id,
            } => Self::Iban {
                iban,
                name,
                connector_recipient_id: connector_recipient_id.map(RecipientIdType::ConnectorId),
            },
            api_models::admin::MerchantAccountData::Bacs {
                account_number,
                sort_code,
                name,
                connector_recipient_id,
            } => Self::Bacs {
                account_number,
                sort_code,
                name,
                connector_recipient_id: connector_recipient_id.map(RecipientIdType::ConnectorId),
            },
            api_models::admin::MerchantAccountData::FasterPayments {
                account_number,
                sort_code,
                name,
                connector_recipient_id,
            } => Self::FasterPayments {
                account_number,
                sort_code,
                name,
                connector_recipient_id: connector_recipient_id.map(RecipientIdType::ConnectorId),
            },
            api_models::admin::MerchantAccountData::Sepa {
                iban,
                name,
                connector_recipient_id,
            } => Self::Sepa {
                iban,
                name,
                connector_recipient_id: connector_recipient_id.map(RecipientIdType::ConnectorId),
            },
            api_models::admin::MerchantAccountData::SepaInstant {
                iban,
                name,
                connector_recipient_id,
            } => Self::SepaInstant {
                iban,
                name,
                connector_recipient_id: connector_recipient_id.map(RecipientIdType::ConnectorId),
            },
            api_models::admin::MerchantAccountData::Elixir {
                account_number,
                iban,
                name,
                connector_recipient_id,
            } => Self::Elixir {
                account_number,
                iban,
                name,
                connector_recipient_id: connector_recipient_id.map(RecipientIdType::ConnectorId),
            },
            api_models::admin::MerchantAccountData::Bankgiro {
                number,
                name,
                connector_recipient_id,
            } => Self::Bankgiro {
                number,
                name,
                connector_recipient_id: connector_recipient_id.map(RecipientIdType::ConnectorId),
            },
            api_models::admin::MerchantAccountData::Plusgiro {
                number,
                name,
                connector_recipient_id,
            } => Self::Plusgiro {
                number,
                name,
                connector_recipient_id: connector_recipient_id.map(RecipientIdType::ConnectorId),
            },
        }
    }
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MerchantRecipientData {
    ConnectorRecipientId(Secret<String>),
    WalletId(Secret<String>),
    AccountData(MerchantAccountData),
}

impl ForeignFrom<MerchantRecipientData> for api_models::admin::MerchantRecipientData {
    fn foreign_from(value: MerchantRecipientData) -> Self {
        match value {
            MerchantRecipientData::ConnectorRecipientId(id) => Self::ConnectorRecipientId(id),
            MerchantRecipientData::WalletId(id) => Self::WalletId(id),
            MerchantRecipientData::AccountData(data) => {
                Self::AccountData(api_models::admin::MerchantAccountData::foreign_from(data))
            }
        }
    }
}

impl From<api_models::admin::MerchantRecipientData> for MerchantRecipientData {


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

pub struct FacilitapayAuthResponse


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/signifyd/transformers/api.rs
// Module: hyperswitch_connectors

// Public exports:
pub use hyperswitch_domain_models::router_request_types::fraud_check::RefundMethod;


// Struct: BamboraVoidRequest
// File: crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BamboraVoidRequest


                    } else {
                        Err(get_error_response(
                            &item.response.error_information,
                            &None,
                            None,
                            item.http_code,
                            item.response.id.clone(),
                        ))
                    }
                } else {
                    Ok(RefundsResponseData {
                        connector_refund_id: item.response.id,
                        refund_status,
                    })
                }
            }

            None => Ok(RefundsResponseData {
                connector_refund_id: item.response.id.clone(),
                refund_status: match item.data.response {
                    Ok(response) => response.refund_status,
                    Err(_) => common_enums::RefundStatus::Pending,
                },
            }),
        };

        Ok(Self {
            response,
            ..item.data
        })
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WellsfargoStandardErrorResponse {
    pub error_information: Option<ErrorInformation>,
    pub status: Option<String>,
    pub message: Option<String>,
    pub reason: Option<String>,
    pub details: Option<Vec<Details>>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WellsfargoNotAvailableErrorResponse {
    pub errors: Vec<WellsfargoNotAvailableErrorObject>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WellsfargoNotAvailableErrorObject {
    #[serde(rename = "type")]
    pub error_type: Option<String>,
    pub message: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WellsfargoServerErrorResponse {
    pub status: Option<String>,
    pub message: Option<String>,
    pub reason: Option<Reason>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Reason {
    SystemError,
    ServerTimeout,
    ServiceTimeout,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct WellsfargoAuthenticationErrorResponse {
    pub response: AuthenticationErrorInformation,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum WellsfargoErrorResponse {
    AuthenticationError(Box<WellsfargoAuthenticationErrorResponse>),
    //If the request resource is not available/exists in wellsfargo
    NotAvailableError(Box<WellsfargoNotAvailableErrorResponse>),
    StandardError(Box<WellsfargoStandardErrorResponse>),
}

#[derive(Debug, Deserialize, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Details {
    pub field: String,
    pub reason: String,
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct ErrorInformation {
    pub message: String,
    pub reason: String,
    pub details: Option<Vec<Details>>,
}

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

pub fn get_error_response(
    error_data: &Option<WellsfargoErrorInformation>,
    risk_information: &Option<ClientRiskInformation>,
    attempt_status: Option<enums::AttemptStatus>,
    status_code: u16,
    transaction_id: String,
) -> ErrorResponse {
    let avs_message = risk_information
        .clone()
        .map(|client_risk_information| {
            client_risk_information.rules.map(|rules| {
                rules
                    .iter()
                    .map(|risk_info| {
                        risk_info.name.clone().map_or("".to_string(), |name| {
                            format!(" , {}", name.clone().expose())
                        })
                    })
                    .collect::<Vec<String>>()
                    .join("")
            })
        })
        .unwrap_or(Some("".to_string()));

    let detailed_error_info = error_data
        .clone()
        .map(|error_data| match error_data.details {
            Some(details) => details
                .iter()
                .map(|details| format!("{} : {}", details.field, details.reason))
                .collect::<Vec<_>>()
                .join(", "),
            None => "".to_string(),
        });

    let reason = get_error_reason(
        error_data.clone().and_then(|error_info| error_info.message),
        detailed_error_info,
        avs_message,
    );
    let error_message = error_data.clone().and_then(|error_info| error_info.reason);
    ErrorResponse {
        code: error_message
            .clone()
            .unwrap_or(hyperswitch_interfaces::consts::NO_ERROR_CODE.to_string()),
        message: error_message
            .clone()
            .unwrap_or(hyperswitch_interfaces::consts::NO_ERROR_MESSAGE.to_string()),
        reason,
        status_code,
        attempt_status,
        connector_transaction_id: Some(transaction_id.clone()),
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    }
}
pub fn get_error_reason(
    error_info: Option<String>,
    detailed_error_info: Option<String>,
    avs_error_info: Option<String>,
) -> Option<String> {
    match (error_info, detailed_error_info, avs_error_info) {
        (Some(message), Some(details), Some(avs_message)) => Some(format!(
            "{message}, detailed_error_information: {details}, avs_message: {avs_message}",
        )),
        (Some(message), Some(details), None) => {
            Some(format!("{message}, detailed_error_information: {details}"))
        }
        (Some(message), None, Some(avs_message)) => {
            Some(format!("{message}, avs_message: {avs_message}"))
        }
        (None, Some(details), Some(avs_message)) => {
            Some(format!("{details}, avs_message: {avs_message}"))
        }
        (Some(message), None, None) => Some(message),
        (None, Some(details), None) => Some(details),
        (None, None, Some(avs_message)) => Some(avs_message),
        (None, None, None) => None,
    }
}


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

impl Paytm


// Implementation: impl super::auth_events::filters::AuthEventFilterAnalytics for for SqlxClient
// File: crates/analytics/src/sqlx.rs
// Module: analytics
// Methods: 0 total (0 public)

impl super::auth_events::filters::AuthEventFilterAnalytics for for SqlxClient


// Struct: PaymentMethodDeleteResponse
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Implementations: 0

pub struct PaymentMethodDeleteResponse


// Function: execute
// File: crates/diesel_models/src/kv.rs
// Module: diesel_models

pub fn execute(self, conn: &PgPooledConn) -> crate::StorageResult<DBResult>


// Function: description
// File: crates/hyperswitch_connectors/src/connectors/peachpayments/transformers.rs
// Module: hyperswitch_connectors

pub fn description(&self) -> Option<&String>


// Struct: Webhooks
// File: crates/router/src/routes/app.rs
// Module: router
// Implementations: 2

pub struct Webhooks


// Struct: RoleName
// File: crates/router/src/types/domain/user.rs
// Module: router
// Implementations: 1

pub struct RoleName


// Struct: SellerStatusResponse
// File: crates/router/src/types/api/connector_onboarding/paypal.rs
// Module: router
// Implementations: 1

pub struct SellerStatusResponse


// Struct: DwollaRefundSyncResponse
// File: crates/hyperswitch_connectors/src/connectors/dwolla/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DwollaRefundSyncResponse


// Struct: SdkSessionUpdate
// File: crates/hyperswitch_domain_models/src/router_flow_types/payments.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct SdkSessionUpdate


// Struct: NoLevel
// File: crates/router/src/types/domain/user.rs
// Module: router
// Implementations: 0

pub struct NoLevel


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

impl ConnectorCommon for for Bluecode


// Struct: Timeline
// File: crates/hyperswitch_connectors/src/connectors/coinbase/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Timeline


// Implementation: impl Default for for EnableOvercaptureBool
// File: crates/common_types/src/primitive_wrappers.rs
// Module: common_types
// Methods: 1 total (0 public)

impl Default for for EnableOvercaptureBool


// Struct: MollieMetadata
// File: crates/hyperswitch_connectors/src/connectors/mollie/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MollieMetadata


// Implementation: impl CardInfoBuilder
// File: crates/router/src/core/cards_info.rs
// Module: router
// Methods: 2 total (0 public)

impl CardInfoBuilder


// Struct: VoltRefundWebhookBodyReference
// File: crates/hyperswitch_connectors/src/connectors/volt/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct VoltRefundWebhookBodyReference


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

pub fn refund_data(
    state: SessionState,
    req: types::DummyConnectorRefundRetrieveRequest,
) -> types::DummyConnectorResponse<types::DummyConnectorRefundResponse>


// Implementation: impl ConnectorAuthentication for for Netcetera
// File: crates/hyperswitch_connectors/src/connectors/netcetera.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ConnectorAuthentication for for Netcetera


                        | 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(
                                "Bank of America",
                            ),
                        )
                        .into()),
                    },
                    // If connector_mandate_id is present MandatePayment will be the PMD, the case will be handled in the first `if` clause.
                    // This is a fallback implementation in the event of catastrophe.
                    PaymentMethodData::MandatePayment => {
                        let connector_mandate_id =
                            item.router_data.request.connector_mandate_id().ok_or(
                                errors::ConnectorError::MissingRequiredField {
                                    field_name: "connector_mandate_id",
                                },
                            )?;
                        Self::try_from((item, connector_mandate_id))
                    }
                    PaymentMethodData::CardRedirect(_)
                    | PaymentMethodData::PayLater(_)
                    | PaymentMethodData::BankRedirect(_)
                    | PaymentMethodData::BankDebit(_)
                    | PaymentMethodData::BankTransfer(_)
                    | PaymentMethodData::Crypto(_)
                    | 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(
                                "Bank of America",
                            ),
                        )
                        .into())
                    }
                }
            }
        }
    }
}

fn get_samsung_pay_fluid_data_value(
    samsung_pay_token_data: &hyperswitch_domain_models::payment_method_data::SamsungPayTokenData,
) -> Result<SamsungPayFluidDataValue, error_stack::Report<errors::ConnectorError>> {
    let samsung_pay_header =
        josekit::jwt::decode_header(samsung_pay_token_data.data.clone().peek())
            .change_context(errors::ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to decode samsung pay header")?;

    let samsung_pay_kid_optional = samsung_pay_header.claim("kid").and_then(|kid| kid.as_str());

    let samsung_pay_fluid_data_value = SamsungPayFluidDataValue {
        public_key_hash: Secret::new(
            samsung_pay_kid_optional
                .get_required_value("samsung pay public_key_hash")
                .change_context(errors::ConnectorError::RequestEncodingFailed)?
                .to_string(),
        ),
        version: samsung_pay_token_data.version.clone(),
        data: Secret::new(consts::BASE64_ENGINE.encode(samsung_pay_token_data.data.peek())),
    };

    Ok(samsung_pay_fluid_data_value)
}

use error_stack::ResultExt;

impl
    TryFrom<(
        &BankOfAmericaRouterData<&PaymentsAuthorizeRouterData>,
        Box<SamsungPayWalletData>,
    )> for BankOfAmericaPaymentsRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (item, samsung_pay_data): (
            &BankOfAmericaRouterData<&PaymentsAuthorizeRouterData>,
            Box<SamsungPayWalletData>,
        ),
    ) -> Result<Self, Self::Error> {
        let email = item
            .router_data
            .get_billing_email()
            .or(item.router_data.request.get_email())?;
        let bill_to = build_bill_to(item.router_data.get_optional_billing(), email)?;
        let order_information = OrderInformationWithBill::from((item, Some(bill_to)));

        let samsung_pay_fluid_data_value =
            get_samsung_pay_fluid_data_value(&samsung_pay_data.payment_credential.token_data)?;

        let samsung_pay_fluid_data_str = serde_json::to_string(&samsung_pay_fluid_data_value)
            .change_context(errors::ConnectorError::RequestEncodingFailed)
            .attach_printable("Failed to serialize samsung pay fluid data")?;

        let payment_information =
            PaymentInformation::SamsungPay(Box::new(SamsungPayPaymentInformation {
                fluid_data: FluidData {
                    value: Secret::new(consts::BASE64_ENGINE.encode(samsung_pay_fluid_data_str)),
                    descriptor: Some(
                        consts::BASE64_ENGINE.encode(FLUID_DATA_DESCRIPTOR_FOR_SAMSUNG_PAY),
                    ),
                },
                tokenized_card: SamsungPayTokenizedCard {
                    transaction_type: TransactionType::SamsungPay,
                },
            }));

        let processing_information = ProcessingInformation::try_from((
            item,
            Some(PaymentSolution::SamsungPay),
            Some(samsung_pay_data.payment_credential.card_brand.to_string()),
        ))?;
        let client_reference_information = ClientReferenceInformation::from(item);
        let merchant_defined_information = item
            .router_data
            .request
            .metadata
            .clone()
            .map(convert_metadata_to_merchant_defined_info);

        Ok(Self {
            processing_information,
            payment_information,
            order_information,
            client_reference_information,
            consumer_authentication_information: None,
            merchant_defined_information,
        })
    }
}

impl
    TryFrom<(
        &BankOfAmericaRouterData<&PaymentsAuthorizeRouterData>,
        String,
    )> for BankOfAmericaPaymentsRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (item, connector_mandate_id): (
            &BankOfAmericaRouterData<&PaymentsAuthorizeRouterData>,
            String,
        ),
    ) -> Result<Self, Self::Error> {
        let processing_information = ProcessingInformation::try_from((item, None, None))?;
        let payment_instrument = BankOfAmericaPaymentInstrument {
            id: connector_mandate_id.into(),
        };
        let bill_to =
            item.router_data.request.get_email().ok().and_then(|email| {
                build_bill_to(item.router_data.get_optional_billing(), email).ok()
            });
        let order_information = OrderInformationWithBill::from((item, bill_to));
        let payment_information =
            PaymentInformation::MandatePayment(Box::new(MandatePaymentInformation {
                payment_instrument,
            }));
        let client_reference_information = ClientReferenceInformation::from(item);
        let merchant_defined_information = item
            .router_data
            .request
            .metadata
            .clone()
            .map(convert_metadata_to_merchant_defined_info);
        Ok(Self {
            processing_information,
            payment_information,
            order_information,
            client_reference_information,
            merchant_defined_information,
            consumer_authentication_information: None,
        })
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum BankofamericaPaymentStatus {
    Authorized,
    Succeeded,
    Failed,
    Voided,
    Reversed,
    Pending,
    Declined,
    Rejected,
    Challenge,
    AuthorizedPendingReview,
    AuthorizedRiskDeclined,
    Transmitted,
    InvalidRequest,
    ServerError,
    PendingAuthentication,
    PendingReview,
    Accepted,
    Cancelled,
    //PartialAuthorized, not being consumed yet.
}

fn map_boa_attempt_status(
    (status, auto_capture): (BankofamericaPaymentStatus, bool),
) -> enums::AttemptStatus {
    match status {
        BankofamericaPaymentStatus::Authorized
        | BankofamericaPaymentStatus::AuthorizedPendingReview => {
            if auto_capture {
                // Because BankOfAmerica will return Payment Status as Authorized even in AutoCapture Payment
                enums::AttemptStatus::Charged
            } else {
                enums::AttemptStatus::Authorized
            }
        }
        BankofamericaPaymentStatus::Pending => {
            if auto_capture {
                enums::AttemptStatus::Charged
            } else {
                enums::AttemptStatus::Pending
            }
        }
        BankofamericaPaymentStatus::Succeeded | BankofamericaPaymentStatus::Transmitted => {
            enums::AttemptStatus::Charged
        }
        BankofamericaPaymentStatus::Voided
        | BankofamericaPaymentStatus::Reversed
        | BankofamericaPaymentStatus::Cancelled => enums::AttemptStatus::Voided,
        BankofamericaPaymentStatus::Failed
        | BankofamericaPaymentStatus::Declined
        | BankofamericaPaymentStatus::AuthorizedRiskDeclined
        | BankofamericaPaymentStatus::InvalidRequest
        | BankofamericaPaymentStatus::Rejected
        | BankofamericaPaymentStatus::ServerError => enums::AttemptStatus::Failure,
        BankofamericaPaymentStatus::PendingAuthentication => {
            enums::AttemptStatus::AuthenticationPending
        }
        BankofamericaPaymentStatus::PendingReview
        | BankofamericaPaymentStatus::Challenge
        | BankofamericaPaymentStatus::Accepted => enums::AttemptStatus::Pending,
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BankOfAmericaPaymentsResponse {
    ClientReferenceInformation(Box<BankOfAmericaClientReferenceResponse>),
    ErrorInformation(Box<BankOfAmericaErrorInformationResponse>),
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BankOfAmericaSetupMandatesResponse {
    ClientReferenceInformation(Box<BankOfAmericaClientReferenceResponse>),
    ErrorInformation(Box<BankOfAmericaErrorInformationResponse>),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BankOfAmericaClientReferenceResponse {
    id: String,
    status: BankofamericaPaymentStatus,
    client_reference_information: ClientReferenceInformation,
    processor_information: Option<ClientProcessorInformation>,
    processing_information: Option<ProcessingInformationResponse>,
    payment_information: Option<PaymentInformationResponse>,
    payment_insights_information: Option<PaymentInsightsInformation>,
    risk_information: Option<ClientRiskInformation>,
    token_information: Option<BankOfAmericaTokenInformation>,
    error_information: Option<BankOfAmericaErrorInformation>,
    issuer_information: Option<IssuerInformation>,
    sender_information: Option<SenderInformation>,
    payment_account_information: Option<PaymentAccountInformation>,
    reconciliation_id: Option<String>,
    consumer_authentication_information: Option<ConsumerAuthenticationInformation>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ConsumerAuthenticationInformation {
    eci_raw: Option<String>,
    eci: Option<String>,
    acs_transaction_id: Option<String>,
    cavv: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SenderInformation {
    payment_information: Option<PaymentInformationResponse>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentInsightsInformation {
    response_insights: Option<ResponseInsights>,
    rule_results: Option<RuleResults>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ResponseInsights {
    category_code: Option<String>,
    category: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RuleResults {
    id: Option<String>,
    decision: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentInformationResponse {
    tokenized_card: Option<CardResponseObject>,
    customer: Option<CustomerResponseObject>,
    card: Option<CardResponseObject>,
    scheme: Option<String>,
    bin: Option<String>,
    account_type: Option<String>,
    issuer: Option<String>,
    bin_country: Option<enums::CountryAlpha2>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CustomerResponseObject {
    customer_id: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentAccountInformation {
    card: Option<PaymentAccountCardInformation>,
    features: Option<PaymentAccountFeatureInformation>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentAccountFeatureInformation {
    health_card: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentAccountCardInformation {
    #[serde(rename = "type")]
    card_type: Option<String>,
    hashed_number: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ProcessingInformationResponse {
    payment_solution: Option<String>,
    commerce_indicator: Option<String>,
    commerce_indicator_label: Option<String>,
    authorization_options: Option<AuthorizationOptions>,
    ecommerce_indicator: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthorizationOptions {
    auth_type: Option<String>,
    initiator: Option<Initiator>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Initiator {
    merchant_initiated_transaction: Option<MerchantInitiatedTransactionResponse>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MerchantInitiatedTransactionResponse {
    agreement_id: Option<String>,
    previous_transaction_id: Option<String>,
    original_authorized_amount: Option<StringMajorUnit>,
    reason: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BankOfAmericaTokenInformation {
    payment_instrument: Option<BankOfAmericaPaymentInstrument>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct IssuerInformation {
    country: Option<enums::CountryAlpha2>,
    discretionary_data: Option<String>,
    country_specific_discretionary_data: Option<String>,
    response_code: Option<String>,
    pin_request_indicator: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CardResponseObject {
    suffix: Option<String>,
    prefix: Option<String>,
    expiration_month: Option<Secret<String>>,
    expiration_year: Option<Secret<String>>,
    #[serde(rename = "type")]
    card_type: Option<String>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BankOfAmericaErrorInformationResponse {
    id: String,
    error_information: BankOfAmericaErrorInformation,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BankOfAmericaErrorInformation {
    reason: Option<String>,
    message: Option<String>,
    details: Option<Vec<Details>>,
}

fn map_error_response<F, T>(
    error_response: &BankOfAmericaErrorInformationResponse,
    item: ResponseRouterData<F, BankOfAmericaPaymentsResponse, T, PaymentsResponseData>,
    transaction_status: Option<enums::AttemptStatus>,
) -> RouterData<F, T, PaymentsResponseData> {
    let detailed_error_info = error_response
        .error_information
        .details
        .as_ref()
        .map(|details| {
            details
                .iter()
                .map(|details| format!("{} : {}", details.field, details.reason))
                .collect::<Vec<_>>()
                .join(", ")
        });

    let reason = get_error_reason(
        error_response.error_information.message.clone(),
        detailed_error_info,
        None,
    );
    let response = Err(ErrorResponse {
        code: error_response
            .error_information
            .reason
            .clone()
            .unwrap_or(hyperswitch_interfaces::consts::NO_ERROR_CODE.to_string()),
        message: error_response
            .error_information
            .reason
            .clone()
            .unwrap_or(hyperswitch_interfaces::consts::NO_ERROR_MESSAGE.to_string()),
        reason,
        status_code: item.http_code,
        attempt_status: None,
        connector_transaction_id: Some(error_response.id.clone()),
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    });

    match transaction_status {
        Some(status) => RouterData {
            response,
            status,
            ..item.data
        },
        None => RouterData {
            response,
            ..item.data
        },
    }
}

fn get_error_response_if_failure(
    (info_response, status, http_code): (
        &BankOfAmericaClientReferenceResponse,
        enums::AttemptStatus,
        u16,
    ),
) -> Option<ErrorResponse> {
    if utils::is_payment_failure(status) {
        Some(get_error_response(
            &info_response.error_information,
            &info_response.processor_information,
            &info_response.risk_information,
            Some(status),
            http_code,
            info_response.id.clone(),
        ))
    } else {
        None
    }
}

fn get_payment_response(
    (info_response, status, http_code): (
        &BankOfAmericaClientReferenceResponse,
        enums::AttemptStatus,
        u16,
    ),
) -> Result<PaymentsResponseData, Box<ErrorResponse>> {
    let error_response = get_error_response_if_failure((info_response, status, http_code));
    match error_response {
        Some(error) => Err(Box::new(error)),
        None => {
            let mandate_reference =
                info_response
                    .token_information
                    .clone()
                    .map(|token_info| MandateReference {
                        connector_mandate_id: token_info
                            .payment_instrument
                            .map(|payment_instrument| payment_instrument.id.expose()),
                        payment_method_id: None,
                        mandate_metadata: None,
                        connector_mandate_request_reference_id: None,
                    });

            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(info_response.id.clone()),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(mandate_reference),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(
                    info_response
                        .client_reference_information
                        .code
                        .clone()
                        .unwrap_or(info_response.id.clone()),
                ),
                incremental_authorization_allowed: None,
                charges: None,
            })
        }
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            BankOfAmericaPaymentsResponse,
            PaymentsAuthorizeData,
            PaymentsResponseData,
        >,
    > for RouterData<F, PaymentsAuthorizeData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            BankOfAmericaPaymentsResponse,
            PaymentsAuthorizeData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        match item.response {
            BankOfAmericaPaymentsResponse::ClientReferenceInformation(info_response) => {
                let status = map_boa_attempt_status((
                    info_response.status.clone(),
                    item.data.request.is_auto_capture()?,
                ));
                let response = get_payment_response((&info_response, status, item.http_code))
                    .map_err(|err| *err);
                let connector_response = match item.data.payment_method {
                    common_enums::PaymentMethod::Card => info_response
                        .processor_information
                        .as_ref()
                        .and_then(|processor_information| {
                            info_response
                                .consumer_authentication_information
                                .as_ref()
                                .map(|consumer_auth_information| {
                                    convert_to_additional_payment_method_connector_response(
                                        processor_information,
                                        consumer_auth_information,
                                    )
                                })
                        })
                        .map(ConnectorResponseData::with_additional_payment_method_data),
                    common_enums::PaymentMethod::CardRedirect
                    | common_enums::PaymentMethod::PayLater
                    | common_enums::PaymentMethod::Wallet
                    | common_enums::PaymentMethod::BankRedirect
                    | common_enums::PaymentMethod::BankTransfer
                    | common_enums::PaymentMethod::Crypto
                    | common_enums::PaymentMethod::BankDebit
                    | common_enums::PaymentMethod::Reward
                    | common_enums::PaymentMethod::RealTimePayment
                    | common_enums::PaymentMethod::MobilePayment
                    | common_enums::PaymentMethod::Upi
                    | common_enums::PaymentMethod::Voucher
                    | common_enums::PaymentMethod::OpenBanking
                    | common_enums::PaymentMethod::GiftCard => None,
                };

                Ok(Self {
                    status,
                    response,
                    connector_response,
                    ..item.data
                })
            }
            BankOfAmericaPaymentsResponse::ErrorInformation(ref error_response) => {
                Ok(map_error_response(
                    &error_response.clone(),
                    item,
                    Some(enums::AttemptStatus::Failure),
                ))
            }
        }
    }
}

fn convert_to_additional_payment_method_connector_response(
    processor_information: &ClientProcessorInformation,
    consumer_authentication_information: &ConsumerAuthenticationInformation,
) -> AdditionalPaymentMethodConnectorResponse {
    let payment_checks = Some(serde_json::json!({
        "avs_response": processor_information.avs,
        "card_verification": processor_information.card_verification,
        "approval_code": processor_information.approval_code,
        "consumer_authentication_response": processor_information.consumer_authentication_response,
        "cavv": consumer_authentication_information.cavv,
        "eci": consumer_authentication_information.eci,
        "eci_raw": consumer_authentication_information.eci_raw,
    }));

    let authentication_data = Some(serde_json::json!({
        "retrieval_reference_number": processor_information.retrieval_reference_number,
        "acs_transaction_id": consumer_authentication_information.acs_transaction_id,
        "system_trace_audit_number": processor_information.system_trace_audit_number,
    }));

    AdditionalPaymentMethodConnectorResponse::Card {
        authentication_data,
        payment_checks,
        card_network: None,
        domestic_network: None,
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            BankOfAmericaPaymentsResponse,
            PaymentsCaptureData,
            PaymentsResponseData,
        >,
    > for RouterData<F, PaymentsCaptureData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            BankOfAmericaPaymentsResponse,
            PaymentsCaptureData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        match item.response {
            BankOfAmericaPaymentsResponse::ClientReferenceInformation(info_response) => {
                let status = map_boa_attempt_status((info_response.status.clone(), true));
                let response = get_payment_response((&info_response, status, item.http_code))
                    .map_err(|err| *err);
                Ok(Self {
                    status,
                    response,
                    ..item.data
                })
            }
            BankOfAmericaPaymentsResponse::ErrorInformation(ref error_response) => {
                Ok(map_error_response(&error_response.clone(), item, None))
            }
        }
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            BankOfAmericaPaymentsResponse,
            PaymentsCancelData,
            PaymentsResponseData,
        >,
    > for RouterData<F, PaymentsCancelData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            BankOfAmericaPaymentsResponse,
            PaymentsCancelData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        match item.response {
            BankOfAmericaPaymentsResponse::ClientReferenceInformation(info_response) => {
                let status = map_boa_attempt_status((info_response.status.clone(), false));
                let response = get_payment_response((&info_response, status, item.http_code))
                    .map_err(|err| *err);
                Ok(Self {
                    status,
                    response,
                    ..item.data
                })
            }
            BankOfAmericaPaymentsResponse::ErrorInformation(ref error_response) => {
                Ok(map_error_response(&error_response.clone(), item, None))
            }
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BankOfAmericaTransactionResponse {
    id: String,
    application_information: ApplicationInformation,
    client_reference_information: Option<ClientReferenceInformation>,
    processor_information: Option<ClientProcessorInformation>,
    processing_information: Option<ProcessingInformationResponse>,
    payment_information: Option<PaymentInformationResponse>,
    payment_insights_information: Option<PaymentInsightsInformation>,
    error_information: Option<BankOfAmericaErrorInformation>,
    fraud_marking_information: Option<FraudMarkingInformation>,
    risk_information: Option<ClientRiskInformation>,
    token_information: Option<BankOfAmericaTokenInformation>,
    reconciliation_id: Option<String>,
    consumer_authentication_information: Option<ConsumerAuthenticationInformation>,
}

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

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplicationInformation {
    status: Option<BankofamericaPaymentStatus>,
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            BankOfAmericaTransactionResponse,
            PaymentsSyncData,
            PaymentsResponseData,
        >,
    > for RouterData<F, PaymentsSyncData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            BankOfAmericaTransactionResponse,
            PaymentsSyncData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        match item.response.application_information.status {
            Some(app_status) => {
                let status =
                    map_boa_attempt_status((app_status, item.data.request.is_auto_capture()?));

                let connector_response = match item.data.payment_method {
                    common_enums::PaymentMethod::Card => item
                        .response
                        .processor_information
                        .as_ref()
                        .and_then(|processor_information| {
                            item.response
                                .consumer_authentication_information
                                .as_ref()
                                .map(|consumer_auth_information| {
                                    convert_to_additional_payment_method_connector_response(
                                        processor_information,
                                        consumer_auth_information,
                                    )
                                })
                        })
                        .map(ConnectorResponseData::with_additional_payment_method_data),
                    common_enums::PaymentMethod::CardRedirect
                    | common_enums::PaymentMethod::PayLater
                    | common_enums::PaymentMethod::Wallet
                    | common_enums::PaymentMethod::BankRedirect
                    | common_enums::PaymentMethod::BankTransfer
                    | common_enums::PaymentMethod::Crypto
                    | common_enums::PaymentMethod::BankDebit
                    | common_enums::PaymentMethod::Reward
                    | common_enums::PaymentMethod::RealTimePayment
                    | common_enums::PaymentMethod::MobilePayment
                    | common_enums::PaymentMethod::Upi
                    | common_enums::PaymentMethod::Voucher
                    | common_enums::PaymentMethod::OpenBanking
                    | common_enums::PaymentMethod::GiftCard => None,
                };

                let risk_info: Option<ClientRiskInformation> = None;
                if utils::is_payment_failure(status) {
                    Ok(Self {
                        response: Err(get_error_response(
                            &item.response.error_information,
                            &item.response.processor_information,
                            &risk_info,
                            Some(status),
                            item.http_code,
                            item.response.id.clone(),
                        )),
                        status: enums::AttemptStatus::Failure,
                        connector_response,
                        ..item.data
                    })
                } else {
                    Ok(Self {
                        status,
                        response: Ok(PaymentsResponseData::TransactionResponse {
                            resource_id: ResponseId::ConnectorTransactionId(
                                item.response.id.clone(),
                            ),
                            redirection_data: Box::new(None),
                            mandate_reference: Box::new(None),
                            connector_metadata: None,
                            network_txn_id: None,
                            connector_response_reference_id: item
                                .response
                                .client_reference_information
                                .map(|cref| cref.code)
                                .unwrap_or(Some(item.response.id)),
                            incremental_authorization_allowed: None,
                            charges: None,
                        }),
                        connector_response,
                        ..item.data
                    })
                }
            }
            None => Ok(Self {
                status: item.data.status,
                response: Ok(PaymentsResponseData::TransactionResponse {
                    resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()),
                    redirection_data: Box::new(None),
                    mandate_reference: Box::new(None),
                    connector_metadata: None,
                    network_txn_id: None,
                    connector_response_reference_id: Some(item.response.id),
                    incremental_authorization_allowed: None,
                    charges: None,
                }),
                ..item.data
            }),
        }
    }
}

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

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BankOfAmericaCaptureRequest {
    order_information: OrderInformation,
    client_reference_information: ClientReferenceInformation,
    #[serde(skip_serializing_if = "Option::is_none")]
    merchant_defined_information: Option<Vec<MerchantDefinedInformation>>,
}

impl TryFrom<&BankOfAmericaRouterData<&PaymentsCaptureRouterData>> for BankOfAmericaCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        value: &BankOfAmericaRouterData<&PaymentsCaptureRouterData>,
    ) -> Result<Self, Self::Error> {
        let merchant_defined_information = value
            .router_data
            .request
            .metadata
            .clone()
            .map(convert_metadata_to_merchant_defined_info);
        Ok(Self {
            order_information: OrderInformation {
                amount_details: Amount {
                    total_amount: value.amount.to_owned(),
                    currency: value.router_data.request.currency,
                },
            },
            client_reference_information: ClientReferenceInformation {
                code: Some(value.router_data.connector_request_reference_id.clone()),
            },
            merchant_defined_information,
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BankOfAmericaVoidRequest {
    client_reference_information: ClientReferenceInformation,
    reversal_information: ReversalInformation,
    #[serde(skip_serializing_if = "Option::is_none")]
    merchant_defined_information: Option<Vec<MerchantDefinedInformation>>,
    // The connector documentation does not mention the merchantDefinedInformation field for Void requests. But this has been still added because it works!
}

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

impl TryFrom<&BankOfAmericaRouterData<&PaymentsCancelRouterData>> for BankOfAmericaVoidRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        value: &BankOfAmericaRouterData<&PaymentsCancelRouterData>,
    ) -> Result<Self, Self::Error> {
        let merchant_defined_information = value
            .router_data
            .request
            .metadata
            .clone()
            .map(convert_metadata_to_merchant_defined_info);
        Ok(Self {
            client_reference_information: ClientReferenceInformation {
                code: Some(value.router_data.connector_request_reference_id.clone()),
            },
            reversal_information: ReversalInformation {
                amount_details: Amount {
                    total_amount: value.amount.to_owned(),
                    currency: value.router_data.request.currency.ok_or(
                        errors::ConnectorError::MissingRequiredField {
                            field_name: "Currency",
                        },
                    )?,
                },
                reason: value
                    .router_data
                    .request
                    .cancellation_reason
                    .clone()
                    .ok_or(errors::ConnectorError::MissingRequiredField {
                        field_name: "Cancellation Reason",
                    })?,
            },
            merchant_defined_information,
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BankOfAmericaRefundRequest {
    order_information: OrderInformation,
    client_reference_information: ClientReferenceInformation,
}

impl<F> TryFrom<&BankOfAmericaRouterData<&RefundsRouterData<F>>> for BankOfAmericaRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &BankOfAmericaRouterData<&RefundsRouterData<F>>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            order_information: OrderInformation {
                amount_details: Amount {
                    total_amount: item.amount.clone(),
                    currency: item.router_data.request.currency,
                },
            },
            client_reference_information: ClientReferenceInformation {
                code: Some(item.router_data.request.refund_id.clone()),
            },
        })
    }
}

impl From<BankOfAmericaRefundResponse> for enums::RefundStatus {
    fn from(item: BankOfAmericaRefundResponse) -> Self {
        let error_reason = item
            .error_information
            .and_then(|error_info| error_info.reason);
        match item.status {
            BankofamericaRefundStatus::Succeeded | BankofamericaRefundStatus::Transmitted => {
                Self::Success
            }
            BankofamericaRefundStatus::Cancelled
            | BankofamericaRefundStatus::Failed
            | BankofamericaRefundStatus::Voided => Self::Failure,
            BankofamericaRefundStatus::Pending => Self::Pending,
            BankofamericaRefundStatus::TwoZeroOne => {
                if error_reason == Some("PROCESSOR_DECLINED".to_string()) {
                    Self::Failure
                } else {
                    Self::Pending
                }
            }
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BankOfAmericaRefundResponse {
    id: String,
    status: BankofamericaRefundStatus,
    error_information: Option<BankOfAmericaErrorInformation>,
}

impl TryFrom<RefundsResponseRouterData<Execute, BankOfAmericaRefundResponse>>
    for RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, BankOfAmericaRefundResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = enums::RefundStatus::from(item.response.clone());
        let response = if utils::is_refund_failure(refund_status) {
            Err(get_error_response(
                &item.response.error_information,
                &None,
                &None,
                None,
                item.http_code,
                item.response.id,
            ))
        } else {
            Ok(RefundsResponseData {
                connector_refund_id: item.response.id,
                refund_status,
            })
        };

        Ok(Self {
            response,
            ..item.data
        })
    }
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum BankofamericaRefundStatus {
    Succeeded,
    Transmitted,
    Failed,
    Pending,
    Voided,
    Cancelled,
    #[serde(rename = "201")]
    TwoZeroOne,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RsyncApplicationInformation {
    status: Option<BankofamericaRefundStatus>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BankOfAmericaRsyncResponse {
    id: String,
    application_information: Option<RsyncApplicationInformation>,
    error_information: Option<BankOfAmericaErrorInformation>,
}

impl TryFrom<RefundsResponseRouterData<RSync, BankOfAmericaRsyncResponse>>
    for RefundsRouterData<RSync>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, BankOfAmericaRsyncResponse>,
    ) -> Result<Self, Self::Error> {
        let response = match item
            .response
            .application_information
            .and_then(|application_information| application_information.status)
        {
            Some(status) => {
                let error_reason = item
                    .response
                    .error_information
                    .clone()
                    .and_then(|error_info| error_info.reason);
                let refund_status = match status {
                    BankofamericaRefundStatus::Succeeded
                    | BankofamericaRefundStatus::Transmitted => enums::RefundStatus::Success,
                    BankofamericaRefundStatus::Cancelled
                    | BankofamericaRefundStatus::Failed
                    | BankofamericaRefundStatus::Voided => enums::RefundStatus::Failure,
                    BankofamericaRefundStatus::Pending => enums::RefundStatus::Pending,
                    BankofamericaRefundStatus::TwoZeroOne => {
                        if error_reason == Some("PROCESSOR_DECLINED".to_string()) {
                            enums::RefundStatus::Failure
                        } else {
                            enums::RefundStatus::Pending
                        }
                    }
                };
                if utils::is_refund_failure(refund_status) {
                    if status == BankofamericaRefundStatus::Voided {
                        Err(get_error_response(
                            &Some(BankOfAmericaErrorInformation {
                                message: Some(constants::REFUND_VOIDED.to_string()),
                                reason: Some(constants::REFUND_VOIDED.to_string()),
                                details: None,
                            }),
                            &None,
                            &None,
                            None,
                            item.http_code,
                            item.response.id.clone(),
                        ))
                    } else {
                        Err(get_error_response(
                            &item.response.error_information,
                            &None,
                            &None,
                            None,
                            item.http_code,


// File: crates/router/src/core/metrics.rs
// Module: router

use router_env::{counter_metric, global_meter};

global_meter!(GLOBAL_METER, "ROUTER_API");

counter_metric!(INCOMING_DISPUTE_WEBHOOK_METRIC, GLOBAL_METER); // No. of incoming dispute webhooks
counter_metric!(
    INCOMING_DISPUTE_WEBHOOK_SIGNATURE_FAILURE_METRIC,
    GLOBAL_METER
); // No. of incoming dispute webhooks for which signature verification failed
counter_metric!(
    INCOMING_DISPUTE_WEBHOOK_VALIDATION_FAILURE_METRIC,
    GLOBAL_METER
); // No. of incoming dispute webhooks for which validation failed
counter_metric!(INCOMING_DISPUTE_WEBHOOK_NEW_RECORD_METRIC, GLOBAL_METER); // No. of incoming dispute webhooks for which new record is created in our db
counter_metric!(INCOMING_DISPUTE_WEBHOOK_UPDATE_RECORD_METRIC, GLOBAL_METER); // No. of incoming dispute webhooks for which we have updated the details to existing record
counter_metric!(
    INCOMING_DISPUTE_WEBHOOK_MERCHANT_NOTIFIED_METRIC,
    GLOBAL_METER
); // No. of incoming dispute webhooks which are notified to merchant
counter_metric!(
    ACCEPT_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC,
    GLOBAL_METER
); //No. of status validation failures while accepting a dispute
counter_metric!(
    EVIDENCE_SUBMISSION_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC,
    GLOBAL_METER
); //No. of status validation failures while submitting evidence for a dispute
   //No. of status validation failures while attaching evidence for a dispute
counter_metric!(
    ATTACH_EVIDENCE_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC,
    GLOBAL_METER
);

counter_metric!(INCOMING_PAYOUT_WEBHOOK_METRIC, GLOBAL_METER); // No. of incoming payout webhooks
counter_metric!(
    INCOMING_PAYOUT_WEBHOOK_SIGNATURE_FAILURE_METRIC,
    GLOBAL_METER
); // No. of incoming payout webhooks for which signature verification failed

counter_metric!(WEBHOOK_INCOMING_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_INCOMING_FILTERED_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_SOURCE_VERIFIED_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_OUTGOING_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_OUTGOING_RECEIVED_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_OUTGOING_NOT_RECEIVED_COUNT, GLOBAL_METER);
counter_metric!(WEBHOOK_PAYMENT_NOT_FOUND, GLOBAL_METER);
counter_metric!(
    WEBHOOK_EVENT_TYPE_IDENTIFICATION_FAILURE_COUNT,
    GLOBAL_METER
);

counter_metric!(ROUTING_CREATE_REQUEST_RECEIVED, GLOBAL_METER);
counter_metric!(ROUTING_CREATE_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_MERCHANT_DICTIONARY_RETRIEVE, GLOBAL_METER);
counter_metric!(
    ROUTING_MERCHANT_DICTIONARY_RETRIEVE_SUCCESS_RESPONSE,
    GLOBAL_METER
);
counter_metric!(ROUTING_LINK_CONFIG, GLOBAL_METER);
counter_metric!(ROUTING_LINK_CONFIG_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_RETRIEVE_CONFIG, GLOBAL_METER);
counter_metric!(ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_RETRIEVE_DEFAULT_CONFIG, GLOBAL_METER);
counter_metric!(
    ROUTING_RETRIEVE_DEFAULT_CONFIG_SUCCESS_RESPONSE,
    GLOBAL_METER
);
counter_metric!(ROUTING_RETRIEVE_LINK_CONFIG, GLOBAL_METER);
counter_metric!(ROUTING_RETRIEVE_LINK_CONFIG_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_UNLINK_CONFIG, GLOBAL_METER);
counter_metric!(ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_UPDATE_CONFIG, GLOBAL_METER);
counter_metric!(ROUTING_UPDATE_CONFIG_SUCCESS_RESPONSE, GLOBAL_METER);
counter_metric!(ROUTING_UPDATE_CONFIG_FOR_PROFILE, GLOBAL_METER);
counter_metric!(
    ROUTING_UPDATE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE,
    GLOBAL_METER
);
counter_metric!(ROUTING_RETRIEVE_CONFIG_FOR_PROFILE, GLOBAL_METER);
counter_metric!(
    ROUTING_RETRIEVE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE,
    GLOBAL_METER
);
counter_metric!(DYNAMIC_SUCCESS_BASED_ROUTING, GLOBAL_METER);
counter_metric!(DYNAMIC_CONTRACT_BASED_ROUTING, GLOBAL_METER);

#[cfg(feature = "partial-auth")]
counter_metric!(PARTIAL_AUTH_FAILURE, GLOBAL_METER);

counter_metric!(API_KEY_REQUEST_INITIATED, GLOBAL_METER);
counter_metric!(API_KEY_REQUEST_COMPLETED, GLOBAL_METER);


// Struct: PaysafeConnectorMetadataObject
// File: crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaysafeConnectorMetadataObject


// Struct: Redirects
// File: crates/hyperswitch_connectors/src/connectors/coinbase/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Redirects


// Implementation: impl Default for for OvercaptureEnabledBool
// File: crates/common_types/src/primitive_wrappers.rs
// Module: common_types
// Methods: 1 total (0 public)

impl Default for for OvercaptureEnabledBool


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

pub struct PaymentAccountFeatureInformation


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

impl api::PaymentCapture for for Hyperwallet


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

impl ConnectorSpecifications for for Digitalvirgo


// Function: new
// File: crates/drainer/src/services.rs
// Module: drainer

// Documentation: This panic indicates a critical failure in setting up external services, and the application cannot proceed without a valid HashiCorp client.

pub fn new(config: &crate::Settings, test_transaction: bool, tenant: &Tenant) -> Self