// File: crates/router/src/types.rs

// 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, ExtendAuthorization,
        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,
        BillingDetails, ConnectorAuthType, ConnectorResponseData, CustomerInfo, ErrorResponse,
        GooglePayPaymentMethodDetails, GooglePayPredecryptDataInternal, L2L3Data, OrderInfo,
        PaymentMethodBalance, PaymentMethodToken, RecurringMandatePaymentData, RouterData, TaxInfo,
    },
    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, PaymentsAuthenticateData,
        PaymentsAuthorizeData, PaymentsCancelData, PaymentsCancelPostCaptureData,
        PaymentsCaptureData, PaymentsExtendAuthorizationData, PaymentsIncrementalAuthorizationData,
        PaymentsPostAuthenticateData, PaymentsPostProcessingData, PaymentsPostSessionTokensData,
        PaymentsPreAuthenticateData, 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;
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 PaymentsExtendAuthorizationRouterData =
    RouterData<ExtendAuthorization, PaymentsExtendAuthorizationData, 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 PaymentsExtendAuthorizationResponseRouterData<R> = ResponseRouterData<
    ExtendAuthorization,
    R,
    PaymentsExtendAuthorizationData,
    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(amount_captured.map(MinorUnit::new))
            .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
        }
    }
}
impl Capturable for PaymentsExtendAuthorizationData {
    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::Failed
            | common_enums::IntentStatus::Expired
            | common_enums::IntentStatus::Succeeded => Some(0),
            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,
        }
    }
}

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>,
}

/// Data extracted from UCS response
pub struct UcsAuthorizeResponseData {
    pub router_data_response:
        Result<(PaymentsResponseData, common_enums::AttemptStatus), ErrorResponse>,
    pub status_code: u16,
    pub connector_customer_id: Option<String>,
    pub connector_response: Option<ConnectorResponseData>,
}

pub struct UcsRepeatPaymentResponseData {
    pub router_data_response:
        Result<(PaymentsResponseData, common_enums::AttemptStatus), ErrorResponse>,
    pub status_code: u16,
    pub connector_customer_id: Option<String>,
    pub connector_response: Option<ConnectorResponseData>,
}

pub struct UcsSetupMandateResponseData {
    pub router_data_response:
        Result<(PaymentsResponseData, common_enums::AttemptStatus), ErrorResponse>,
    pub status_code: u16,
    pub connector_customer_id: Option<String>,
}

#[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 {
    fn from(value: api_models::admin::MerchantRecipientData) -> Self {
        match value {
            api_models::admin::MerchantRecipientData::ConnectorRecipientId(id) => {
                Self::ConnectorRecipientId(id)
            }
            api_models::admin::MerchantRecipientData::WalletId(id) => Self::WalletId(id),
            api_models::admin::MerchantRecipientData::AccountData(data) => {
                Self::AccountData(data.into())
            }
        }
    }
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AdditionalMerchantData {
    OpenBankingRecipientData(MerchantRecipientData),
}

impl ForeignFrom<api_models::admin::AdditionalMerchantData> for AdditionalMerchantData {
    fn foreign_from(value: api_models::admin::AdditionalMerchantData) -> Self {
        match value {
            api_models::admin::AdditionalMerchantData::OpenBankingRecipientData(data) => {
                Self::OpenBankingRecipientData(MerchantRecipientData::from(data))
            }
        }
    }
}

impl ForeignFrom<AdditionalMerchantData> for api_models::admin::AdditionalMerchantData {
    fn foreign_from(value: AdditionalMerchantData) -> Self {
        match value {
            AdditionalMerchantData::OpenBankingRecipientData(data) => {
                Self::OpenBankingRecipientData(
                    api_models::admin::MerchantRecipientData::foreign_from(data),
                )
            }
        }
    }
}

impl ForeignFrom<api_models::admin::ConnectorAuthType> for ConnectorAuthType {
    fn foreign_from(value: api_models::admin::ConnectorAuthType) -> Self {
        match value {
            api_models::admin::ConnectorAuthType::TemporaryAuth => Self::TemporaryAuth,
            api_models::admin::ConnectorAuthType::HeaderKey { api_key } => {
                Self::HeaderKey { api_key }
            }
            api_models::admin::ConnectorAuthType::BodyKey { api_key, key1 } => {
                Self::BodyKey { api_key, key1 }
            }
            api_models::admin::ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => Self::SignatureKey {
                api_key,
                key1,
                api_secret,
            },
            api_models::admin::ConnectorAuthType::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            } => Self::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            },
            api_models::admin::ConnectorAuthType::CurrencyAuthKey { auth_key_map } => {
                Self::CurrencyAuthKey { auth_key_map }
            }
            api_models::admin::ConnectorAuthType::NoKey => Self::NoKey,
            api_models::admin::ConnectorAuthType::CertificateAuth {
                certificate,
                private_key,
            } => Self::CertificateAuth {
                certificate,
                private_key,
            },
        }
    }
}

impl ForeignFrom<ConnectorAuthType> for api_models::admin::ConnectorAuthType {
    fn foreign_from(from: ConnectorAuthType) -> Self {
        match from {
            ConnectorAuthType::TemporaryAuth => Self::TemporaryAuth,
            ConnectorAuthType::HeaderKey { api_key } => Self::HeaderKey { api_key },
            ConnectorAuthType::BodyKey { api_key, key1 } => Self::BodyKey { api_key, key1 },
            ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => Self::SignatureKey {
                api_key,
                key1,
                api_secret,
            },
            ConnectorAuthType::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            } => Self::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            },
            ConnectorAuthType::CurrencyAuthKey { auth_key_map } => {
                Self::CurrencyAuthKey { auth_key_map }
            }
            ConnectorAuthType::NoKey => Self::NoKey,
            ConnectorAuthType::CertificateAuth {
                certificate,
                private_key,
            } => Self::CertificateAuth {
                certificate,
                private_key,
            },
        }
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ConnectorsList {
    pub connectors: Vec<String>,
}

impl ForeignFrom<&PaymentsAuthorizeRouterData> for AuthorizeSessionTokenData {
    fn foreign_from(data: &PaymentsAuthorizeRouterData) -> Self {
        Self {
            amount_to_capture: data.amount_captured,
            currency: data.request.currency,
            connector_transaction_id: data.payment_id.clone(),
            amount: Some(data.request.amount),
        }
    }
}

impl ForeignFrom<&ExternalVaultProxyPaymentsRouterData> for AuthorizeSessionTokenData {
    fn foreign_from(data: &ExternalVaultProxyPaymentsRouterData) -> Self {
        Self {
            amount_to_capture: data.amount_captured,
            currency: data.request.currency,
            connector_transaction_id: data.payment_id.clone(),
            amount: Some(data.request.amount),
        }
    }
}

impl<'a> ForeignFrom<&'a SetupMandateRouterData> for AuthorizeSessionTokenData {
    fn foreign_from(data: &'a SetupMandateRouterData) -> Self {
        Self {
            amount_to_capture: data.request.amount,
            currency: data.request.currency,
            connector_transaction_id: data.payment_id.clone(),
            amount: data.request.amount,
        }
    }
}

pub trait Tokenizable {
    fn set_session_token(&mut self, token: Option<String>);
}

impl Tokenizable for SetupMandateRequestData {
    fn set_session_token(&mut self, _token: Option<String>) {}
}

impl Tokenizable for PaymentsAuthorizeData {
    fn set_session_token(&mut self, token: Option<String>) {
        self.session_token = token;
    }
}

impl Tokenizable for CompleteAuthorizeData {
    fn set_session_token(&mut self, _token: Option<String>) {}
}

impl Tokenizable for ExternalVaultProxyPaymentsData {
    fn set_session_token(&mut self, token: Option<String>) {
        self.session_token = token;
    }
}

impl ForeignFrom<&SetupMandateRouterData> for PaymentsAuthorizeData {
    fn foreign_from(data: &SetupMandateRouterData) -> Self {
        Self {
            currency: data.request.currency,
            payment_method_data: data.request.payment_method_data.clone(),
            confirm: data.request.confirm,
            mandate_id: data.request.mandate_id.clone(),
            setup_future_usage: data.request.setup_future_usage,
            off_session: data.request.off_session,
            setup_mandate_details: data.request.setup_mandate_details.clone(),
            router_return_url: data.request.router_return_url.clone(),
            email: data.request.email.clone(),
            customer_name: data.request.customer_name.clone(),
            amount: 0,
            order_tax_amount: Some(MinorUnit::zero()),
            minor_amount: MinorUnit::new(0),
            capture_method: None,
            webhook_url: None,
            complete_authorize_url: None,
            browser_info: data.request.browser_info.clone(),
            order_details: None,
            order_category: None,
            session_token: None,
            enrolled_for_3ds: true,
            related_transaction_id: None,
            payment_experience: None,
            payment_method_type: None,
            customer_id: None,
            surcharge_details: None,
            request_incremental_authorization: data.request.request_incremental_authorization,
            metadata: None,
            request_extended_authorization: None,
            authentication_data: None,
            customer_acceptance: data.request.customer_acceptance.clone(),
            split_payments: None, // TODO: allow charges on mandates?
            merchant_order_reference_id: None,
            integrity_object: None,
            additional_payment_method_data: None,
            shipping_cost: data.request.shipping_cost,
            merchant_account_id: None,
            merchant_config_currency: None,
            connector_testing_data: data.request.connector_testing_data.clone(),
            order_id: None,
            locale: None,
            payment_channel: None,
            enable_partial_authorization: data.request.enable_partial_authorization,
            enable_overcapture: None,
            is_stored_credential: data.request.is_stored_credential,
            mit_category: None,
            billing_descriptor: data.request.billing_descriptor.clone(),
            tokenization: None,
            partner_merchant_identifier_details: data
                .request
                .partner_merchant_identifier_details
                .clone(),
        }
    }
}

impl<F1, F2, T1, T2> ForeignFrom<(&RouterData<F1, T1, PaymentsResponseData>, T2)>
    for RouterData<F2, T2, PaymentsResponseData>
{
    fn foreign_from(item: (&RouterData<F1, T1, PaymentsResponseData>, T2)) -> Self {
        let data = item.0;
        let request = item.1;
        Self {
            flow: PhantomData,
            request,
            merchant_id: data.merchant_id.clone(),
            connector: data.connector.clone(),
            attempt_id: data.attempt_id.clone(),
            tenant_id: data.tenant_id.clone(),
            status: data.status,
            payment_method: data.payment_method,
            payment_method_type: data.payment_method_type,
            connector_auth_type: data.connector_auth_type.clone(),
            description: data.description.clone(),
            address: data.address.clone(),
            auth_type: data.auth_type,
            connector_meta_data: data.connector_meta_data.clone(),
            connector_wallets_details: data.connector_wallets_details.clone(),
            amount_captured: data.amount_captured,
            minor_amount_captured: data.minor_amount_captured,
            access_token: data.access_token.clone(),
            response: data.response.clone(),
            payment_id: data.payment_id.clone(),
            session_token: data.session_token.clone(),
            reference_id: data.reference_id.clone(),
            customer_id: data.customer_id.clone(),
            payment_method_token: None,
            preprocessing_id: None,
            connector_customer: data.connector_customer.clone(),
            recurring_mandate_payment_data: data.recurring_mandate_payment_data.clone(),
            connector_request_reference_id: data.connector_request_reference_id.clone(),
            #[cfg(feature = "payouts")]
            payout_method_data: data.payout_method_data.clone(),
            #[cfg(feature = "payouts")]
            quote_id: data.quote_id.clone(),
            test_mode: data.test_mode,
            payment_method_status: None,
            payment_method_balance: data.payment_method_balance.clone(),
            connector_api_version: data.connector_api_version.clone(),
            connector_http_status_code: data.connector_http_status_code,
            external_latency: data.external_latency,
            apple_pay_flow: data.apple_pay_flow.clone(),
            frm_metadata: data.frm_metadata.clone(),
            dispute_id: data.dispute_id.clone(),
            refund_id: data.refund_id.clone(),
            connector_response: data.connector_response.clone(),
            integrity_check: Ok(()),
            additional_merchant_data: data.additional_merchant_data.clone(),
            header_payload: data.header_payload.clone(),
            connector_mandate_request_reference_id: data
                .connector_mandate_request_reference_id
                .clone(),
            authentication_id: data.authentication_id.clone(),
            psd2_sca_exemption_type: data.psd2_sca_exemption_type,
            raw_connector_response: data.raw_connector_response.clone(),
            is_payment_id_from_merchant: data.is_payment_id_from_merchant,
            l2_l3_data: data.l2_l3_data.clone(),
            minor_amount_capturable: data.minor_amount_capturable,
            authorized_amount: data.authorized_amount,
        }
    }
}

#[cfg(feature = "payouts")]
impl<F1, F2>
    ForeignFrom<(
        &RouterData<F1, PayoutsData, PayoutsResponseData>,
        PayoutsData,
    )> for RouterData<F2, PayoutsData, PayoutsResponseData>
{
    fn foreign_from(
        item: (
            &RouterData<F1, PayoutsData, PayoutsResponseData>,
            PayoutsData,
        ),
    ) -> Self {
        let data = item.0;
        let request = item.1;
        Self {
            flow: PhantomData,
            request,
            merchant_id: data.merchant_id.clone(),
            connector: data.connector.clone(),
            attempt_id: data.attempt_id.clone(),
            tenant_id: data.tenant_id.clone(),
            status: data.status,
            payment_method: data.payment_method,
            payment_method_type: data.payment_method_type,
            connector_auth_type: data.connector_auth_type.clone(),
            description: data.description.clone(),
            address: data.address.clone(),
            auth_type: data.auth_type,
            connector_meta_data: data.connector_meta_data.clone(),
            connector_wallets_details: data.connector_wallets_details.clone(),
            amount_captured: data.amount_captured,
            minor_amount_captured: data.minor_amount_captured,
            access_token: data.access_token.clone(),
            response: data.response.clone(),
            payment_id: data.payment_id.clone(),
            session_token: data.session_token.clone(),
            reference_id: data.reference_id.clone(),
            customer_id: data.customer_id.clone(),
            payment_method_token: None,
            recurring_mandate_payment_data: None,
            preprocessing_id: None,
            connector_customer: data.connector_customer.clone(),
            connector_request_reference_id: data.connector_request_reference_id.clone(),
            payout_method_data: data.payout_method_data.clone(),
            quote_id: data.quote_id.clone(),
            test_mode: data.test_mode,
            payment_method_balance: None,
            payment_method_status: None,
            connector_api_version: None,
            connector_http_status_code: data.connector_http_status_code,
            external_latency: data.external_latency,
            apple_pay_flow: None,
            frm_metadata: None,
            refund_id: None,
            dispute_id: None,
            connector_response: data.connector_response.clone(),
            integrity_check: Ok(()),
            header_payload: data.header_payload.clone(),
            authentication_id: None,
            psd2_sca_exemption_type: None,
            additional_merchant_data: data.additional_merchant_data.clone(),
            connector_mandate_request_reference_id: None,
            raw_connector_response: None,
            is_payment_id_from_merchant: data.is_payment_id_from_merchant,
            l2_l3_data: None,
            minor_amount_capturable: None,
            authorized_amount: None,
        }
    }
}

#[cfg(feature = "v2")]
impl ForeignFrom<&domain::MerchantConnectorAccountFeatureMetadata>
    for api_models::admin::MerchantConnectorAccountFeatureMetadata
{
    fn foreign_from(item: &domain::MerchantConnectorAccountFeatureMetadata) -> Self {
        let revenue_recovery = item
            .revenue_recovery
            .as_ref()
            .map(
                |revenue_recovery_metadata| api_models::admin::RevenueRecoveryMetadata {
                    max_retry_count: revenue_recovery_metadata.max_retry_count,
                    billing_connector_retry_threshold: revenue_recovery_metadata
                        .billing_connector_retry_threshold,
                    billing_account_reference: revenue_recovery_metadata
                        .mca_reference
                        .recovery_to_billing
                        .clone(),
                },
            );
        Self { revenue_recovery }
    }
}

#[cfg(feature = "v2")]
impl ForeignTryFrom<&api_models::admin::MerchantConnectorAccountFeatureMetadata>
    for domain::MerchantConnectorAccountFeatureMetadata
{
    type Error = errors::ApiErrorResponse;
    fn foreign_try_from(
        feature_metadata: &api_models::admin::MerchantConnectorAccountFeatureMetadata,
    ) -> Result<Self, Self::Error> {
        let revenue_recovery = feature_metadata
            .revenue_recovery
            .as_ref()
            .map(|revenue_recovery_metadata| {
                domain::AccountReferenceMap::new(
                    revenue_recovery_metadata.billing_account_reference.clone(),
                )
                .map(|mca_reference| domain::RevenueRecoveryMetadata {
                    max_retry_count: revenue_recovery_metadata.max_retry_count,
                    billing_connector_retry_threshold: revenue_recovery_metadata
                        .billing_connector_retry_threshold,
                    mca_reference,
                })
            })
            .transpose()?;

        Ok(Self { revenue_recovery })
    }
}


// File: crates/router/src/events.rs

use std::collections::HashMap;

use common_utils::types::TenantConfig;
use error_stack::ResultExt;
use events::{EventsError, Message, MessagingInterface};
use hyperswitch_interfaces::events as events_interfaces;
use masking::ErasedMaskSerialize;
use router_env::logger;
use serde::{Deserialize, Serialize};
use storage_impl::errors::{ApplicationError, StorageError, StorageResult};
use time::PrimitiveDateTime;

use crate::{
    db::KafkaProducer,
    services::kafka::{KafkaMessage, KafkaSettings},
};

pub mod api_logs;
pub mod audit_events;
pub mod connector_api_logs;
pub mod event_logger;
pub mod outgoing_webhook_logs;
pub mod routing_api_logs;
#[derive(Debug, Serialize, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum EventType {
    PaymentIntent,
    FraudCheck,
    PaymentAttempt,
    Refund,
    ApiLogs,
    ConnectorApiLogs,
    OutgoingWebhookLogs,
    Dispute,
    AuditEvent,
    #[cfg(feature = "payouts")]
    Payout,
    Consolidated,
    Authentication,
    RoutingApiLogs,
    RevenueRecovery,
}

#[derive(Debug, Default, Deserialize, Clone)]
#[serde(tag = "source")]
#[serde(rename_all = "lowercase")]
pub enum EventsConfig {
    Kafka {
        kafka: Box<KafkaSettings>,
    },
    #[default]
    Logs,
}

#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
pub enum EventsHandler {
    Kafka(KafkaProducer),
    Logs(event_logger::EventLogger),
}

impl Default for EventsHandler {
    fn default() -> Self {
        Self::Logs(event_logger::EventLogger {})
    }
}

impl events_interfaces::EventHandlerInterface for EventsHandler {
    fn log_connector_event(&self, event: &events_interfaces::connector_api_logs::ConnectorEvent) {
        self.log_event(event);
    }
}

impl EventsConfig {
    pub async fn get_event_handler(&self) -> StorageResult<EventsHandler> {
        Ok(match self {
            Self::Kafka { kafka } => EventsHandler::Kafka(
                KafkaProducer::create(kafka)
                    .await
                    .change_context(StorageError::InitializationError)?,
            ),
            Self::Logs => EventsHandler::Logs(event_logger::EventLogger::default()),
        })
    }

    pub fn validate(&self) -> Result<(), ApplicationError> {
        match self {
            Self::Kafka { kafka } => kafka.validate(),
            Self::Logs => Ok(()),
        }
    }
}

impl EventsHandler {
    pub fn log_event<T: KafkaMessage>(&self, event: &T) {
        match self {
            Self::Kafka(kafka) => kafka.log_event(event).unwrap_or_else(|e| {
                logger::error!("Failed to log event: {:?}", e);
            }),
            Self::Logs(logger) => logger.log_event(event),
        };
    }
    pub fn add_tenant(&mut self, tenant_config: &dyn TenantConfig) {
        if let Self::Kafka(kafka_producer) = self {
            kafka_producer.set_tenancy(tenant_config);
        }
    }
}

impl MessagingInterface for EventsHandler {
    type MessageClass = EventType;

    fn send_message<T>(
        &self,
        data: T,
        metadata: HashMap<String, String>,
        timestamp: PrimitiveDateTime,
    ) -> error_stack::Result<(), EventsError>
    where
        T: Message<Class = Self::MessageClass> + ErasedMaskSerialize,
    {
        match self {
            Self::Kafka(a) => a.send_message(data, metadata, timestamp),
            Self::Logs(a) => a.send_message(data, metadata, timestamp),
        }
    }
}


// File: crates/router/src/env.rs

#[doc(inline)]
pub use router_env::*;


// File: crates/router/src/configs.rs

use hyperswitch_interfaces::secrets_interface::secret_state::RawSecret;

pub(crate) mod defaults;
pub mod secrets_transformers;
pub mod settings;
mod validations;

pub type Settings = settings::Settings<RawSecret>;


// File: crates/router/src/lib.rs

#[cfg(all(feature = "stripe", feature = "v1"))]
pub mod compatibility;
pub mod configs;
pub mod connection;
pub mod connector;
pub mod consts;
pub mod core;
pub mod cors;
pub mod db;
pub mod env;
pub mod locale;
pub(crate) mod macros;

pub mod routes;
pub mod workflows;

#[cfg(feature = "olap")]
pub mod analytics;
pub mod analytics_validator;
pub mod events;
pub mod middleware;
pub mod services;
pub mod types;
pub mod utils;

use actix_web::{
    body::MessageBody,
    dev::{Server, ServerHandle, ServiceFactory, ServiceRequest},
    middleware::ErrorHandlers,
};
use http::StatusCode;
use hyperswitch_interfaces::secrets_interface::secret_state::SecuredSecret;
use router_env::tracing::Instrument;
use routes::{AppState, SessionState};
use storage_impl::errors::ApplicationResult;
use tokio::sync::{mpsc, oneshot};

pub use self::env::logger;
pub(crate) use self::macros::*;
use crate::{configs::settings, core::errors};

#[cfg(feature = "mimalloc")]
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;

// Import translate fn in root
use crate::locale::{_rust_i18n_t, _rust_i18n_try_translate};

/// Header Constants
pub mod headers {
    pub const ACCEPT: &str = "Accept";
    pub const ACCEPT_LANGUAGE: &str = "Accept-Language";
    pub const KEY: &str = "key";
    pub const API_KEY: &str = "API-KEY";
    pub const APIKEY: &str = "apikey";
    pub const X_CC_API_KEY: &str = "X-CC-Api-Key";
    pub const API_TOKEN: &str = "Api-Token";
    pub const AUTHORIZATION: &str = "Authorization";
    pub const CONTENT_TYPE: &str = "Content-Type";
    pub const DATE: &str = "Date";
    pub const IDEMPOTENCY_KEY: &str = "Idempotency-Key";
    pub const NONCE: &str = "nonce";
    pub const TIMESTAMP: &str = "Timestamp";
    pub const TOKEN: &str = "token";
    pub const USER_AGENT: &str = "User-Agent";
    pub const X_API_KEY: &str = "X-API-KEY";
    pub const X_API_VERSION: &str = "X-ApiVersion";
    pub const X_FORWARDED_FOR: &str = "X-Forwarded-For";
    pub const X_MERCHANT_ID: &str = "X-Merchant-Id";
    pub const X_INTERNAL_API_KEY: &str = "X-Internal-Api-Key";
    pub const X_ORGANIZATION_ID: &str = "X-Organization-Id";
    pub const X_LOGIN: &str = "X-Login";
    pub const X_TRANS_KEY: &str = "X-Trans-Key";
    pub const X_VERSION: &str = "X-Version";
    pub const X_CC_VERSION: &str = "X-CC-Version";
    pub const X_ACCEPT_VERSION: &str = "X-Accept-Version";
    pub const X_DATE: &str = "X-Date";
    pub const X_WEBHOOK_SIGNATURE: &str = "X-Webhook-Signature-512";
    pub const X_REQUEST_ID: &str = "X-Request-Id";
    pub const X_PROFILE_ID: &str = "X-Profile-Id";
    pub const STRIPE_COMPATIBLE_WEBHOOK_SIGNATURE: &str = "Stripe-Signature";
    pub const STRIPE_COMPATIBLE_CONNECT_ACCOUNT: &str = "Stripe-Account";
    pub const X_CLIENT_VERSION: &str = "X-Client-Version";
    pub const X_CLIENT_SOURCE: &str = "X-Client-Source";
    pub const X_PAYMENT_CONFIRM_SOURCE: &str = "X-Payment-Confirm-Source";
    pub const CONTENT_LENGTH: &str = "Content-Length";
    pub const BROWSER_NAME: &str = "x-browser-name";
    pub const X_CLIENT_PLATFORM: &str = "x-client-platform";
    pub const X_MERCHANT_DOMAIN: &str = "x-merchant-domain";
    pub const X_APP_ID: &str = "x-app-id";
    pub const X_REDIRECT_URI: &str = "x-redirect-uri";
    pub const X_TENANT_ID: &str = "x-tenant-id";
    pub const X_CLIENT_SECRET: &str = "X-Client-Secret";
    pub const X_CUSTOMER_ID: &str = "X-Customer-Id";
    pub const X_CONNECTED_MERCHANT_ID: &str = "x-connected-merchant-id";
    // Header value for X_CONNECTOR_HTTP_STATUS_CODE differs by version.
    // Constant name is kept the same for consistency across versions.
    #[cfg(feature = "v1")]
    pub const X_CONNECTOR_HTTP_STATUS_CODE: &str = "connector_http_status_code";
    #[cfg(feature = "v2")]
    pub const X_CONNECTOR_HTTP_STATUS_CODE: &str = "x-connector-http-status-code";

    pub const X_REFERENCE_ID: &str = "X-Reference-Id";
}

pub mod pii {
    //! Personal Identifiable Information protection.

    pub(crate) use common_utils::pii::Email;
    #[doc(inline)]
    pub use masking::*;
}

pub fn mk_app(
    state: AppState,
    request_body_limit: usize,
) -> actix_web::App<
    impl ServiceFactory<
        ServiceRequest,
        Config = (),
        Response = actix_web::dev::ServiceResponse<impl MessageBody>,
        Error = actix_web::Error,
        InitError = (),
    >,
> {
    let mut server_app = get_application_builder(
        request_body_limit,
        state.conf.cors.clone(),
        state.conf.trace_header.clone(),
    );

    #[cfg(feature = "dummy_connector")]
    {
        use routes::DummyConnector;
        server_app = server_app.service(DummyConnector::server(state.clone()));
    }

    #[cfg(any(feature = "olap", feature = "oltp"))]
    {
        #[cfg(feature = "olap")]
        {
            // This is a more specific route as compared to `MerchantConnectorAccount`
            // so it is registered before `MerchantConnectorAccount`.
            #[cfg(feature = "v1")]
            {
                server_app = server_app
                    .service(routes::ProfileNew::server(state.clone()))
                    .service(routes::Forex::server(state.clone()))
                    .service(routes::ProfileAcquirer::server(state.clone()));
            }

            server_app = server_app.service(routes::Profile::server(state.clone()));
        }
        server_app = server_app
            .service(routes::Payments::server(state.clone()))
            .service(routes::Customers::server(state.clone()))
            .service(routes::Configs::server(state.clone()))
            .service(routes::MerchantConnectorAccount::server(state.clone()))
            .service(routes::RelayWebhooks::server(state.clone()))
            .service(routes::Webhooks::server(state.clone()))
            .service(routes::Hypersense::server(state.clone()))
            .service(routes::Relay::server(state.clone()))
            .service(routes::ThreeDsDecisionRule::server(state.clone()));

        #[cfg(feature = "oltp")]
        {
            server_app = server_app.service(routes::PaymentMethods::server(state.clone()));
        }

        #[cfg(all(feature = "v2", feature = "oltp"))]
        {
            server_app = server_app
                .service(routes::PaymentMethodSession::server(state.clone()))
                .service(routes::Refunds::server(state.clone()));
        }
        #[cfg(all(feature = "v2", feature = "oltp", feature = "tokenization_v2"))]
        {
            server_app = server_app.service(routes::Tokenization::server(state.clone()));
        }
        #[cfg(feature = "v1")]
        {
            server_app = server_app
                .service(routes::Refunds::server(state.clone()))
                .service(routes::Mandates::server(state.clone()))
                .service(routes::Authentication::server(state.clone()));
        }
    }

    #[cfg(all(feature = "oltp", any(feature = "v1", feature = "v2"),))]
    {
        server_app = server_app.service(routes::EphemeralKey::server(state.clone()))
    }
    #[cfg(all(feature = "oltp", feature = "v1"))]
    {
        server_app = server_app.service(routes::Poll::server(state.clone()))
    }

    #[cfg(feature = "olap")]
    {
        server_app = server_app
            .service(routes::Organization::server(state.clone()))
            .service(routes::MerchantAccount::server(state.clone()))
            .service(routes::User::server(state.clone()))
            .service(routes::ApiKeys::server(state.clone()))
            .service(routes::Routing::server(state.clone()))
            .service(routes::Chat::server(state.clone()));

        #[cfg(all(feature = "olap", any(feature = "v1", feature = "v2")))]
        {
            server_app = server_app.service(routes::Verify::server(state.clone()));
        }

        #[cfg(feature = "v1")]
        {
            server_app = server_app
                .service(routes::Files::server(state.clone()))
                .service(routes::Disputes::server(state.clone()))
                .service(routes::Blocklist::server(state.clone()))
                .service(routes::Subscription::server(state.clone()))
                .service(routes::Gsm::server(state.clone()))
                .service(routes::ApplePayCertificatesMigration::server(state.clone()))
                .service(routes::PaymentLink::server(state.clone()))
                .service(routes::ConnectorOnboarding::server(state.clone()))
                .service(routes::Analytics::server(state.clone()))
                .service(routes::WebhookEvents::server(state.clone()))
                .service(routes::FeatureMatrix::server(state.clone()));
        }

        #[cfg(feature = "v2")]
        {
            server_app = server_app
                .service(routes::UserDeprecated::server(state.clone()))
                .service(routes::ProcessTrackerDeprecated::server(state.clone()))
                .service(routes::ProcessTracker::server(state.clone()))
                .service(routes::Gsm::server(state.clone()))
                .service(routes::RecoveryDataBackfill::server(state.clone()));
        }
    }

    #[cfg(all(feature = "payouts", feature = "v1"))]
    {
        server_app = server_app
            .service(routes::Payouts::server(state.clone()))
            .service(routes::PayoutLink::server(state.clone()));
    }

    #[cfg(all(feature = "stripe", feature = "v1"))]
    {
        server_app = server_app
            .service(routes::StripeApis::server(state.clone()))
            .service(routes::Cards::server(state.clone()));
    }

    #[cfg(all(feature = "oltp", feature = "v2"))]
    {
        server_app = server_app.service(routes::Proxy::server(state.clone()));
    }

    #[cfg(all(feature = "recon", feature = "v1"))]
    {
        server_app = server_app.service(routes::Recon::server(state.clone()));
    }

    server_app = server_app.service(routes::Cache::server(state.clone()));
    server_app = server_app.service(routes::Health::server(state.clone()));

    server_app
}

/// Starts the server
///
/// # Panics
///
///  Unwrap used because without the value we can't start the server
#[allow(clippy::expect_used, clippy::unwrap_used)]
pub async fn start_server(conf: settings::Settings<SecuredSecret>) -> ApplicationResult<Server> {
    logger::debug!(startup_config=?conf);
    let server = conf.server.clone();
    let (tx, rx) = oneshot::channel();
    let api_client = Box::new(services::ProxyClient::new(&conf.proxy).map_err(|error| {
        errors::ApplicationError::ApiClientError(error.current_context().clone())
    })?);
    let state = Box::pin(AppState::new(conf, tx, api_client)).await;
    let request_body_limit = server.request_body_limit;

    let server_builder =
        actix_web::HttpServer::new(move || mk_app(state.clone(), request_body_limit))
            .bind((server.host.as_str(), server.port))?
            .workers(server.workers)
            .shutdown_timeout(server.shutdown_timeout);

    #[cfg(feature = "tls")]
    let server = match server.tls {
        None => server_builder.run(),
        Some(tls_conf) => {
            let cert_file =
                &mut std::io::BufReader::new(std::fs::File::open(tls_conf.certificate).map_err(
                    |err| errors::ApplicationError::InvalidConfigurationValueError(err.to_string()),
                )?);
            let key_file =
                &mut std::io::BufReader::new(std::fs::File::open(tls_conf.private_key).map_err(
                    |err| errors::ApplicationError::InvalidConfigurationValueError(err.to_string()),
                )?);

            let cert_chain = rustls_pemfile::certs(cert_file)
                .collect::<Result<Vec<_>, _>>()
                .map_err(|err| {
                    errors::ApplicationError::InvalidConfigurationValueError(err.to_string())
                })?;

            let mut keys = rustls_pemfile::pkcs8_private_keys(key_file)
                .map(|key| key.map(rustls::pki_types::PrivateKeyDer::Pkcs8))
                .collect::<Result<Vec<_>, _>>()
                .map_err(|err| {
                    errors::ApplicationError::InvalidConfigurationValueError(err.to_string())
                })?;

            // exit if no keys could be parsed
            if keys.is_empty() {
                return Err(errors::ApplicationError::InvalidConfigurationValueError(
                    "Could not locate PKCS8 private keys.".into(),
                ));
            }

            let config_builder = rustls::ServerConfig::builder().with_no_client_auth();
            let config = config_builder
                .with_single_cert(cert_chain, keys.remove(0))
                .map_err(|err| {
                    errors::ApplicationError::InvalidConfigurationValueError(err.to_string())
                })?;

            server_builder
                .bind_rustls_0_22(
                    (tls_conf.host.unwrap_or(server.host).as_str(), tls_conf.port),
                    config,
                )?
                .run()
        }
    };

    #[cfg(not(feature = "tls"))]
    let server = server_builder.run();

    let _task_handle = tokio::spawn(receiver_for_error(rx, server.handle()).in_current_span());
    Ok(server)
}

pub async fn receiver_for_error(rx: oneshot::Receiver<()>, mut server: impl Stop) {
    match rx.await {
        Ok(_) => {
            logger::error!("The redis server failed ");
            server.stop_server().await;
        }
        Err(err) => {
            logger::error!("Channel receiver error: {err}");
        }
    }
}

#[async_trait::async_trait]
pub trait Stop {
    async fn stop_server(&mut self);
}

#[async_trait::async_trait]
impl Stop for ServerHandle {
    async fn stop_server(&mut self) {
        let _ = self.stop(true).await;
    }
}
#[async_trait::async_trait]
impl Stop for mpsc::Sender<()> {
    async fn stop_server(&mut self) {
        let _ = self.send(()).await.map_err(|err| logger::error!("{err}"));
    }
}

pub fn get_application_builder(
    request_body_limit: usize,
    cors: settings::CorsSettings,
    trace_header: settings::TraceHeaderConfig,
) -> actix_web::App<
    impl ServiceFactory<
        ServiceRequest,
        Config = (),
        Response = actix_web::dev::ServiceResponse<impl MessageBody>,
        Error = actix_web::Error,
        InitError = (),
    >,
> {
    let json_cfg = actix_web::web::JsonConfig::default()
        .limit(request_body_limit)
        .content_type_required(true)
        .error_handler(utils::error_parser::custom_json_error_handler);

    actix_web::App::new()
        .app_data(json_cfg)
        .wrap(ErrorHandlers::new().handler(
            StatusCode::NOT_FOUND,
            errors::error_handlers::custom_error_handlers,
        ))
        .wrap(ErrorHandlers::new().handler(
            StatusCode::METHOD_NOT_ALLOWED,
            errors::error_handlers::custom_error_handlers,
        ))
        .wrap(middleware::default_response_headers())
        .wrap(cors::cors(cors))
        // this middleware works only for Http1.1 requests
        .wrap(middleware::Http400RequestDetailsLogger)
        .wrap(middleware::AddAcceptLanguageHeader)
        .wrap(middleware::RequestResponseMetrics)
        .wrap(middleware::LogSpanInitializer)
        .wrap(router_env::tracing_actix_web::TracingLogger::<
            router_env::CustomRootSpanBuilder,
        >::new())
        .wrap(
            router_env::RequestIdentifier::new(&trace_header.header_name)
                .use_incoming_id(trace_header.id_reuse_strategy),
        )
}


// File: crates/router/src/cors.rs

// use actix_web::http::header;

use crate::configs::settings;

pub fn cors(config: settings::CorsSettings) -> actix_cors::Cors {
    let allowed_methods = config.allowed_methods.iter().map(|s| s.as_str());

    let mut cors = actix_cors::Cors::default()
        .allowed_methods(allowed_methods)
        .allow_any_header()
        .expose_any_header()
        .max_age(config.max_age);

    if config.wildcard_origin {
        cors = cors.allow_any_origin()
    } else {
        for origin in &config.origins {
            cors = cors.allowed_origin(origin);
        }
        // Only allow this in case if it's not wildcard origins. ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
        cors = cors.supports_credentials();
    }

    cors
}


// File: crates/router/src/services.rs

pub mod api;
pub mod authentication;
pub mod authorization;
pub mod connector_integration_interface;
#[cfg(feature = "email")]
pub mod email;
pub mod encryption;
#[cfg(feature = "olap")]
pub mod jwt;
pub mod kafka;
pub mod logger;
pub mod pm_auth;

pub mod card_testing_guard;
#[cfg(feature = "olap")]
pub mod openidconnect;

use std::sync::Arc;

use common_utils::types::{keymanager, TenantConfig};
use error_stack::ResultExt;
pub use hyperswitch_interfaces::connector_integration_v2::{
    BoxedConnectorIntegrationV2, ConnectorIntegrationAnyV2, ConnectorIntegrationV2,
};
use masking::{ExposeInterface, StrongSecret};
#[cfg(feature = "kv_store")]
use storage_impl::kv_router_store::KVRouterStore;
use storage_impl::{errors::StorageResult, redis::RedisStore, RouterStore};
use tokio::sync::oneshot;

pub use self::{api::*, encryption::*};
use crate::{configs::Settings, core::errors};

#[cfg(not(feature = "olap"))]
pub type StoreType = storage_impl::database::store::Store;
#[cfg(feature = "olap")]
pub type StoreType = storage_impl::database::store::ReplicaStore;

#[cfg(not(feature = "kv_store"))]
pub type Store = RouterStore<StoreType>;
#[cfg(feature = "kv_store")]
pub type Store = KVRouterStore<StoreType>;

/// # Panics
///
/// Will panic if hex decode of master key fails
#[allow(clippy::expect_used)]
pub async fn get_store(
    config: &Settings,
    tenant: &dyn TenantConfig,
    cache_store: Arc<RedisStore>,
    test_transaction: bool,
    key_manager_state: keymanager::KeyManagerState,
) -> StorageResult<Store> {
    let master_config = config.master_database.clone().into_inner();

    #[cfg(feature = "olap")]
    let replica_config = config.replica_database.clone().into_inner();

    #[allow(clippy::expect_used)]
    let master_enc_key = hex::decode(config.secrets.get_inner().master_enc_key.clone().expose())
        .map(StrongSecret::new)
        .expect("Failed to decode master key from hex");

    #[cfg(not(feature = "olap"))]
    let conf = master_config.into();
    #[cfg(feature = "olap")]
    // this would get abstracted, for all cases
    #[allow(clippy::useless_conversion)]
    let conf = (master_config.into(), replica_config.into());

    let store: RouterStore<StoreType> = if test_transaction {
        RouterStore::test_store(
            conf,
            tenant,
            &config.redis,
            master_enc_key,
            Some(key_manager_state.clone()),
        )
        .await?
    } else {
        RouterStore::from_config(
            conf,
            tenant,
            master_enc_key,
            cache_store,
            storage_impl::redis::cache::IMC_INVALIDATION_CHANNEL,
            Some(key_manager_state.clone()),
        )
        .await?
    };

    #[cfg(feature = "kv_store")]
    let store = KVRouterStore::from_store(
        store,
        config.drainer.stream_name.clone(),
        config.drainer.num_partitions,
        config.kv_config.ttl,
        config.kv_config.soft_kill,
        Some(key_manager_state),
    );

    Ok(store)
}

#[allow(clippy::expect_used)]
pub async fn get_cache_store(
    config: &Settings,
    shut_down_signal: oneshot::Sender<()>,
    _test_transaction: bool,
) -> StorageResult<Arc<RedisStore>> {
    RouterStore::<StoreType>::cache_store(&config.redis, shut_down_signal).await
}

#[inline]
pub fn generate_aes256_key() -> errors::CustomResult<[u8; 32], common_utils::errors::CryptoError> {
    use ring::rand::SecureRandom;

    let rng = ring::rand::SystemRandom::new();
    let mut key: [u8; 256 / 8] = [0_u8; 256 / 8];
    rng.fill(&mut key)
        .change_context(common_utils::errors::CryptoError::EncodingFailed)?;
    Ok(key)
}


// File: crates/router/src/compatibility.rs

pub mod stripe;
pub mod wrap;


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

pub mod address;
pub mod api_keys;
pub mod authentication;
pub mod authorization;
pub mod blocklist;
pub mod blocklist_fingerprint;
pub mod blocklist_lookup;
pub mod business_profile;
pub mod callback_mapper;
pub mod capture;
pub mod configs;
pub mod customers;
pub mod dashboard_metadata;
pub mod dispute;
pub mod dynamic_routing_stats;
pub mod ephemeral_key;
pub mod events;
pub mod file;
pub mod fraud_check;
pub mod generic_link;
pub mod gsm;
pub mod health_check;
pub mod hyperswitch_ai_interaction;
pub mod kafka_store;
pub mod locker_mock_up;
pub mod mandate;
pub mod merchant_account;
pub mod merchant_connector_account;
pub mod merchant_key_store;
pub mod organization;
pub mod payment_link;
pub mod payment_method_session;
pub mod refund;
pub mod relay;
pub mod reverse_lookup;
pub mod role;
pub mod routing_algorithm;
pub mod unified_translations;
pub mod user;
pub mod user_authentication_method;
pub mod user_key_store;
pub mod user_role;

use ::payment_methods::state::PaymentMethodsStorageInterface;
use common_utils::{id_type, types::keymanager::KeyManagerState};
use diesel_models::{
    fraud_check::{FraudCheck, FraudCheckUpdate},
    organization::{Organization, OrganizationNew, OrganizationUpdate},
};
use error_stack::ResultExt;
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::payouts::{
    payout_attempt::PayoutAttemptInterface, payouts::PayoutsInterface,
};
use hyperswitch_domain_models::{
    cards_info::CardsInfoInterface,
    master_key::MasterKeyInterface,
    payment_methods::PaymentMethodInterface,
    payments::{payment_attempt::PaymentAttemptInterface, payment_intent::PaymentIntentInterface},
};
#[cfg(not(feature = "payouts"))]
use hyperswitch_domain_models::{PayoutAttemptInterface, PayoutsInterface};
use redis_interface::errors::RedisError;
use router_env::logger;
use storage_impl::{
    errors::StorageError, redis::kv_store::RedisConnInterface, tokenization, MockDb,
};

pub use self::kafka_store::KafkaStore;
use self::{fraud_check::FraudCheckInterface, organization::OrganizationInterface};
pub use crate::{
    core::errors::{self, ProcessTrackerError},
    errors::CustomResult,
    services::{
        kafka::{KafkaError, KafkaProducer, MQResult},
        Store,
    },
    types::{
        domain,
        storage::{self},
        AccessToken,
    },
};

#[derive(PartialEq, Eq)]
pub enum StorageImpl {
    Postgresql,
    PostgresqlTest,
    Mock,
}

#[async_trait::async_trait]
pub trait StorageInterface:
    Send
    + Sync
    + dyn_clone::DynClone
    + address::AddressInterface
    + api_keys::ApiKeyInterface
    + blocklist_lookup::BlocklistLookupInterface
    + configs::ConfigInterface<Error = StorageError>
    + capture::CaptureInterface
    + customers::CustomerInterface<Error = StorageError>
    + dashboard_metadata::DashboardMetadataInterface
    + dispute::DisputeInterface
    + ephemeral_key::EphemeralKeyInterface
    + ephemeral_key::ClientSecretInterface
    + events::EventInterface
    + file::FileMetadataInterface
    + FraudCheckInterface
    + locker_mock_up::LockerMockUpInterface
    + mandate::MandateInterface
    + merchant_account::MerchantAccountInterface<Error = StorageError>
    + merchant_connector_account::ConnectorAccessToken
    + merchant_connector_account::MerchantConnectorAccountInterface<Error = StorageError>
    + PaymentAttemptInterface<Error = StorageError>
    + PaymentIntentInterface<Error = StorageError>
    + PaymentMethodInterface<Error = StorageError>
    + blocklist::BlocklistInterface
    + blocklist_fingerprint::BlocklistFingerprintInterface
    + dynamic_routing_stats::DynamicRoutingStatsInterface
    + scheduler::SchedulerInterface
    + PayoutAttemptInterface<Error = StorageError>
    + PayoutsInterface<Error = StorageError>
    + refund::RefundInterface
    + reverse_lookup::ReverseLookupInterface
    + CardsInfoInterface<Error = StorageError>
    + merchant_key_store::MerchantKeyStoreInterface<Error = StorageError>
    + MasterKeyInterface
    + payment_link::PaymentLinkInterface
    + RedisConnInterface
    + RequestIdStore
    + business_profile::ProfileInterface<Error = StorageError>
    + routing_algorithm::RoutingAlgorithmInterface
    + gsm::GsmInterface
    + unified_translations::UnifiedTranslationsInterface
    + authorization::AuthorizationInterface
    + user::sample_data::BatchSampleDataInterface
    + health_check::HealthCheckDbInterface
    + user_authentication_method::UserAuthenticationMethodInterface
    + hyperswitch_ai_interaction::HyperswitchAiInteractionInterface
    + authentication::AuthenticationInterface
    + generic_link::GenericLinkInterface
    + relay::RelayInterface
    + user::theme::ThemeInterface
    + payment_method_session::PaymentMethodsSessionInterface
    + tokenization::TokenizationInterface
    + callback_mapper::CallbackMapperInterface
    + storage_impl::subscription::SubscriptionInterface<Error = StorageError>
    + storage_impl::invoice::InvoiceInterface<Error = StorageError>
    + 'static
{
    fn get_scheduler_db(&self) -> Box<dyn scheduler::SchedulerInterface>;
    fn get_payment_methods_store(&self) -> Box<dyn PaymentMethodsStorageInterface>;
    fn get_subscription_store(&self)
        -> Box<dyn subscriptions::state::SubscriptionStorageInterface>;
    fn get_cache_store(&self) -> Box<dyn RedisConnInterface + Send + Sync + 'static>;
    fn set_key_manager_state(&mut self, key_manager_state: KeyManagerState);
}

#[async_trait::async_trait]
pub trait GlobalStorageInterface:
    Send
    + Sync
    + dyn_clone::DynClone
    + user::UserInterface
    + user_role::UserRoleInterface
    + user_key_store::UserKeyStoreInterface
    + role::RoleInterface
    + RedisConnInterface
    + 'static
{
    fn get_cache_store(&self) -> Box<dyn RedisConnInterface + Send + Sync + 'static>;
}

#[async_trait::async_trait]
pub trait AccountsStorageInterface:
    Send
    + Sync
    + dyn_clone::DynClone
    + OrganizationInterface
    + merchant_account::MerchantAccountInterface<Error = StorageError>
    + business_profile::ProfileInterface<Error = StorageError>
    + merchant_connector_account::MerchantConnectorAccountInterface<Error = StorageError>
    + merchant_key_store::MerchantKeyStoreInterface<Error = StorageError>
    + dashboard_metadata::DashboardMetadataInterface
    + 'static
{
}

pub trait CommonStorageInterface:
    StorageInterface
    + GlobalStorageInterface
    + AccountsStorageInterface
    + PaymentMethodsStorageInterface
{
    fn get_storage_interface(&self) -> Box<dyn StorageInterface>;
    fn get_global_storage_interface(&self) -> Box<dyn GlobalStorageInterface>;
    fn get_accounts_storage_interface(&self) -> Box<dyn AccountsStorageInterface>;
}

#[async_trait::async_trait]
impl StorageInterface for Store {
    fn get_scheduler_db(&self) -> Box<dyn scheduler::SchedulerInterface> {
        Box::new(self.clone())
    }
    fn get_payment_methods_store(&self) -> Box<dyn PaymentMethodsStorageInterface> {
        Box::new(self.clone())
    }

    fn get_cache_store(&self) -> Box<dyn RedisConnInterface + Send + Sync + 'static> {
        Box::new(self.clone())
    }

    fn get_subscription_store(
        &self,
    ) -> Box<dyn subscriptions::state::SubscriptionStorageInterface> {
        Box::new(self.clone())
    }

    fn set_key_manager_state(&mut self, key_manager_state: KeyManagerState) {
        self.key_manager_state = Some(key_manager_state.clone());
        #[cfg(feature = "kv_store")]
        self.router_store.set_key_manager_state(key_manager_state);
    }
}

#[async_trait::async_trait]
impl GlobalStorageInterface for Store {
    fn get_cache_store(&self) -> Box<dyn RedisConnInterface + Send + Sync + 'static> {
        Box::new(self.clone())
    }
}

impl AccountsStorageInterface for Store {}

#[async_trait::async_trait]
impl StorageInterface for MockDb {
    fn get_scheduler_db(&self) -> Box<dyn scheduler::SchedulerInterface> {
        Box::new(self.clone())
    }
    fn get_payment_methods_store(&self) -> Box<dyn PaymentMethodsStorageInterface> {
        Box::new(self.clone())
    }

    fn get_cache_store(&self) -> Box<dyn RedisConnInterface + Send + Sync + 'static> {
        Box::new(self.clone())
    }
    fn get_subscription_store(
        &self,
    ) -> Box<dyn subscriptions::state::SubscriptionStorageInterface> {
        Box::new(self.clone())
    }
    fn set_key_manager_state(&mut self, key_manager_state: KeyManagerState) {
        self.key_manager_state = Some(key_manager_state);
    }
}

#[async_trait::async_trait]
impl GlobalStorageInterface for MockDb {
    fn get_cache_store(&self) -> Box<dyn RedisConnInterface + Send + Sync + 'static> {
        Box::new(self.clone())
    }
}

impl AccountsStorageInterface for MockDb {}

impl CommonStorageInterface for MockDb {
    fn get_global_storage_interface(&self) -> Box<dyn GlobalStorageInterface> {
        Box::new(self.clone())
    }
    fn get_storage_interface(&self) -> Box<dyn StorageInterface> {
        Box::new(self.clone())
    }

    fn get_accounts_storage_interface(&self) -> Box<dyn AccountsStorageInterface> {
        Box::new(self.clone())
    }
}

impl CommonStorageInterface for Store {
    fn get_global_storage_interface(&self) -> Box<dyn GlobalStorageInterface> {
        Box::new(self.clone())
    }
    fn get_storage_interface(&self) -> Box<dyn StorageInterface> {
        Box::new(self.clone())
    }
    fn get_accounts_storage_interface(&self) -> Box<dyn AccountsStorageInterface> {
        Box::new(self.clone())
    }
}

pub trait RequestIdStore {
    fn add_request_id(&mut self, _request_id: String) {}
    fn get_request_id(&self) -> Option<String> {
        None
    }
}

impl RequestIdStore for MockDb {}

impl RequestIdStore for Store {
    fn add_request_id(&mut self, request_id: String) {
        self.request_id = Some(request_id)
    }

    fn get_request_id(&self) -> Option<String> {
        self.request_id.clone()
    }
}

pub async fn get_and_deserialize_key<T>(
    db: &dyn StorageInterface,
    key: &str,
    type_name: &'static str,
) -> CustomResult<T, RedisError>
where
    T: serde::de::DeserializeOwned,
{
    use common_utils::ext_traits::ByteSliceExt;

    let bytes = db.get_key(key).await?;
    bytes
        .parse_struct(type_name)
        .change_context(RedisError::JsonDeserializationFailed)
}

dyn_clone::clone_trait_object!(StorageInterface);
dyn_clone::clone_trait_object!(GlobalStorageInterface);
dyn_clone::clone_trait_object!(AccountsStorageInterface);

impl RequestIdStore for KafkaStore {
    fn add_request_id(&mut self, request_id: String) {
        self.diesel_store.add_request_id(request_id)
    }
}

#[async_trait::async_trait]
impl FraudCheckInterface for KafkaStore {
    async fn insert_fraud_check_response(
        &self,
        new: storage::FraudCheckNew,
    ) -> CustomResult<FraudCheck, StorageError> {
        let frm = self.diesel_store.insert_fraud_check_response(new).await?;
        if let Err(er) = self
            .kafka_producer
            .log_fraud_check(&frm, None, self.tenant_id.clone())
            .await
        {
            logger::error!(message = "Failed to log analytics event for fraud check", error_message = ?er);
        }
        Ok(frm)
    }
    async fn update_fraud_check_response_with_attempt_id(
        &self,
        this: FraudCheck,
        fraud_check: FraudCheckUpdate,
    ) -> CustomResult<FraudCheck, StorageError> {
        let frm = self
            .diesel_store
            .update_fraud_check_response_with_attempt_id(this, fraud_check)
            .await?;
        if let Err(er) = self
            .kafka_producer
            .log_fraud_check(&frm, None, self.tenant_id.clone())
            .await
        {
            logger::error!(message="Failed to log analytics event for fraud check {frm:?}", error_message=?er)
        }
        Ok(frm)
    }
    async fn find_fraud_check_by_payment_id(
        &self,
        payment_id: id_type::PaymentId,
        merchant_id: id_type::MerchantId,
    ) -> CustomResult<FraudCheck, StorageError> {
        let frm = self
            .diesel_store
            .find_fraud_check_by_payment_id(payment_id, merchant_id)
            .await?;
        if let Err(er) = self
            .kafka_producer
            .log_fraud_check(&frm, None, self.tenant_id.clone())
            .await
        {
            logger::error!(message="Failed to log analytics event for fraud check {frm:?}", error_message=?er)
        }
        Ok(frm)
    }
    async fn find_fraud_check_by_payment_id_if_present(
        &self,
        payment_id: id_type::PaymentId,
        merchant_id: id_type::MerchantId,
    ) -> CustomResult<Option<FraudCheck>, StorageError> {
        let frm = self
            .diesel_store
            .find_fraud_check_by_payment_id_if_present(payment_id, merchant_id)
            .await?;

        if let Some(fraud_check) = frm.clone() {
            if let Err(er) = self
                .kafka_producer
                .log_fraud_check(&fraud_check, None, self.tenant_id.clone())
                .await
            {
                logger::error!(message="Failed to log analytics event for frm {frm:?}", error_message=?er);
            }
        }
        Ok(frm)
    }
}

#[async_trait::async_trait]
impl OrganizationInterface for KafkaStore {
    async fn insert_organization(
        &self,
        organization: OrganizationNew,
    ) -> CustomResult<Organization, StorageError> {
        self.diesel_store.insert_organization(organization).await
    }
    async fn find_organization_by_org_id(
        &self,
        org_id: &id_type::OrganizationId,
    ) -> CustomResult<Organization, StorageError> {
        self.diesel_store.find_organization_by_org_id(org_id).await
    }

    async fn update_organization_by_org_id(
        &self,
        org_id: &id_type::OrganizationId,
        update: OrganizationUpdate,
    ) -> CustomResult<Organization, StorageError> {
        self.diesel_store
            .update_organization_by_org_id(org_id, update)
            .await
    }
}


// File: crates/router/src/routes.rs

pub mod admin;
pub mod api_keys;
pub mod app;
#[cfg(feature = "v1")]
pub mod apple_pay_certificates_migration;
pub mod authentication;
#[cfg(all(feature = "olap", feature = "v1"))]
pub mod blocklist;
pub mod cache;
pub mod cards_info;
pub mod configs;
#[cfg(feature = "olap")]
pub mod connector_onboarding;
#[cfg(any(feature = "olap", feature = "oltp"))]
pub mod currency;
pub mod customers;
pub mod disputes;
#[cfg(feature = "dummy_connector")]
pub mod dummy_connector;
pub mod ephemeral_key;
pub mod feature_matrix;
pub mod files;
#[cfg(feature = "frm")]
pub mod fraud_check;
pub mod gsm;
pub mod health;
pub mod hypersense;
pub mod lock_utils;
#[cfg(feature = "v1")]
pub mod locker_migration;
pub mod mandates;
pub mod metrics;
#[cfg(feature = "v1")]
pub mod payment_link;
pub mod payment_methods;
pub mod payments;
#[cfg(feature = "payouts")]
pub mod payout_link;
#[cfg(feature = "payouts")]
pub mod payouts;
#[cfg(any(feature = "olap", feature = "oltp"))]
pub mod pm_auth;
pub mod poll;
#[cfg(feature = "olap")]
pub mod profile_acquirer;
#[cfg(feature = "olap")]
pub mod profiles;
#[cfg(feature = "recon")]
pub mod recon;
pub mod refunds;
#[cfg(feature = "v2")]
pub mod revenue_recovery_data_backfill;
#[cfg(feature = "v2")]
pub mod revenue_recovery_redis;
#[cfg(feature = "olap")]
pub mod routing;
#[cfg(feature = "v1")]
pub mod subscription;
pub mod three_ds_decision_rule;
pub mod tokenization;
#[cfg(feature = "olap")]
pub mod user;
#[cfg(feature = "olap")]
pub mod user_role;
#[cfg(feature = "olap")]
pub mod verification;
#[cfg(feature = "olap")]
pub mod verify_connector;
#[cfg(all(feature = "olap", feature = "v1"))]
pub mod webhook_events;
pub mod webhooks;

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
pub mod recovery_webhooks;

pub mod relay;

#[cfg(feature = "olap")]
pub mod process_tracker;

#[cfg(feature = "v2")]
pub mod proxy;

pub mod chat;

#[cfg(feature = "dummy_connector")]
pub use self::app::DummyConnector;
#[cfg(feature = "v2")]
pub use self::app::PaymentMethodSession;
#[cfg(all(feature = "oltp", feature = "v2"))]
pub use self::app::Proxy;
#[cfg(all(feature = "olap", feature = "recon", feature = "v1"))]
pub use self::app::Recon;
pub use self::app::{
    ApiKeys, AppState, ApplePayCertificatesMigration, Authentication, Cache, Cards, Chat, Configs,
    ConnectorOnboarding, Customers, Disputes, EphemeralKey, FeatureMatrix, Files, Forex, Gsm,
    Health, Hypersense, Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink,
    PaymentMethods, Payments, Poll, ProcessTracker, ProcessTrackerDeprecated, Profile,
    ProfileAcquirer, ProfileNew, Refunds, Relay, RelayWebhooks, SessionState, ThreeDsDecisionRule,
    User, UserDeprecated, Webhooks,
};
#[cfg(feature = "olap")]
pub use self::app::{Blocklist, Organization, Routing, Subscription, Verify, WebhookEvents};
#[cfg(feature = "payouts")]
pub use self::app::{PayoutLink, Payouts};
#[cfg(feature = "v2")]
pub use self::app::{RecoveryDataBackfill, Tokenization};
#[cfg(all(feature = "stripe", feature = "v1"))]
pub use super::compatibility::stripe::StripeApis;
#[cfg(feature = "olap")]
pub use crate::analytics::routes::{self as analytics, Analytics};