// File: crates/hyperswitch_interfaces/src/helpers.rs

use common_utils::{errors as common_utils_errors, request};
use error_stack::ResultExt;
use hyperswitch_domain_models::router_data;
use masking;
use router_env::{logger, tracing};

use crate::{api_client, consts, errors, types};
/// Trait for converting from one foreign type to another
pub trait ForeignTryFrom<F>: Sized {
    /// Custom error for conversion failure
    type Error;
    /// Convert from a foreign type to the current type and return an error if the conversion fails
    fn foreign_try_from(from: F) -> Result<Self, Self::Error>;
}

/// Data structure to hold comparison data between Hyperswitch and UCS
#[derive(serde::Serialize, Debug)]
pub struct ComparisonData {
    /// Hyperswitch router data
    pub hyperswitch_data: masking::Secret<serde_json::Value>,
    /// Unified Connector Service router data
    pub unified_connector_service_data: masking::Secret<serde_json::Value>,
}

/// Trait to get comparison service configuration
pub trait GetComparisonServiceConfig {
    /// Retrieve the comparison service configuration if available
    fn get_comparison_service_config(&self) -> Option<types::ComparisonServiceConfig>;
}

/// Generic function to serialize router data and send comparison to external service
/// Works for both payments and refunds
pub async fn serialize_router_data_and_send_to_comparison_service<F, RouterDReq, RouterDResp>(
    state: &dyn api_client::ApiClientWrapper,
    hyperswitch_router_data: router_data::RouterData<F, RouterDReq, RouterDResp>,
    unified_connector_service_router_data: router_data::RouterData<F, RouterDReq, RouterDResp>,
    comparison_service_config: types::ComparisonServiceConfig,
    request_id: Option<String>,
) -> common_utils_errors::CustomResult<(), errors::HttpClientError>
where
    F: Send + Clone + Sync + 'static,
    RouterDReq: Send + Sync + Clone + 'static + serde::Serialize,
    RouterDResp: Send + Sync + Clone + 'static + serde::Serialize,
{
    logger::info!("Simulating UCS call for shadow mode comparison");

    let [hyperswitch_data, unified_connector_service_data] = [
        (hyperswitch_router_data, "hyperswitch"),
        (unified_connector_service_router_data, "ucs"),
    ]
    .map(|(data, source)| {
        serde_json::to_value(data)
            .map(masking::Secret::new)
            .unwrap_or_else(|e| {
                masking::Secret::new(serde_json::json!({
                    "error": e.to_string(),
                    "source": source
                }))
            })
    });

    let comparison_data = ComparisonData {
        hyperswitch_data,
        unified_connector_service_data,
    };
    let _ = send_comparison_data(
        state,
        comparison_data,
        comparison_service_config,
        request_id,
    )
    .await
    .map_err(|e| {
        logger::debug!("Failed to send comparison data: {:?}", e);
    });
    Ok(())
}

/// Sends router data comparison to external service
pub async fn send_comparison_data(
    state: &dyn api_client::ApiClientWrapper,
    comparison_data: ComparisonData,
    comparison_service_config: types::ComparisonServiceConfig,
    request_id: Option<String>,
) -> common_utils_errors::CustomResult<(), errors::HttpClientError> {
    let mut request = request::RequestBuilder::new()
        .method(request::Method::Post)
        .url(comparison_service_config.url.get_string_repr())
        .header(consts::CONTENT_TYPE, "application/json")
        .header(consts::X_FLOW_NAME, "router-data")
        .set_body(request::RequestContent::Json(Box::new(comparison_data)))
        .build();

    if let Some(req_id) = request_id {
        request.add_header(consts::X_REQUEST_ID, masking::Maskable::Normal(req_id));
    }

    let _ = state
        .get_api_client()
        .send_request(
            state,
            request,
            comparison_service_config.timeout_secs,
            false,
        )
        .await
        .inspect_err(|e| {
            tracing::debug!("Error sending comparison data: {:?}", e);
        })
        .change_context(errors::HttpClientError::RequestNotSent(
            "Failed to send comparison data to comparison service".to_string(),
        ))?;

    Ok(())
}


// File: crates/hyperswitch_interfaces/src/webhooks.rs

//! Webhooks interface

use common_utils::{crypto, errors::CustomResult, ext_traits::ValueExt};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    api::ApplicationResponse, errors::api_error_response::ApiErrorResponse,
};
use masking::{ExposeInterface, Secret};

use crate::{api::ConnectorCommon, errors};

/// struct IncomingWebhookRequestDetails
#[derive(Debug)]
pub struct IncomingWebhookRequestDetails<'a> {
    /// method
    pub method: http::Method,
    /// uri
    pub uri: http::Uri,
    /// headers
    pub headers: &'a actix_web::http::header::HeaderMap,
    /// body
    pub body: &'a [u8],
    /// query_params
    pub query_params: String,
}

/// IncomingWebhookFlowError enum defining the error type for incoming webhook
#[derive(Debug)]
pub enum IncomingWebhookFlowError {
    /// Resource not found for the webhook
    ResourceNotFound,
    /// Internal error for the webhook
    InternalError,
}

impl From<&ApiErrorResponse> for IncomingWebhookFlowError {
    fn from(api_error_response: &ApiErrorResponse) -> Self {
        match api_error_response {
            ApiErrorResponse::WebhookResourceNotFound
            | ApiErrorResponse::DisputeNotFound { .. }
            | ApiErrorResponse::PayoutNotFound
            | ApiErrorResponse::MandateNotFound
            | ApiErrorResponse::PaymentNotFound
            | ApiErrorResponse::RefundNotFound
            | ApiErrorResponse::AuthenticationNotFound { .. } => Self::ResourceNotFound,
            _ => Self::InternalError,
        }
    }
}

/// Trait defining incoming webhook
#[async_trait::async_trait]
pub trait IncomingWebhook: ConnectorCommon + Sync {
    /// fn get_webhook_body_decoding_algorithm
    fn get_webhook_body_decoding_algorithm(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::DecodeMessage + Send>, errors::ConnectorError> {
        Ok(Box::new(crypto::NoAlgorithm))
    }

    /// fn get_webhook_body_decoding_message
    fn get_webhook_body_decoding_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        Ok(request.body.to_vec())
    }

    /// fn decode_webhook_body
    async fn decode_webhook_body(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        connector_name: &str,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let algorithm = self.get_webhook_body_decoding_algorithm(request)?;

        let message = self
            .get_webhook_body_decoding_message(request)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        let secret = self
            .get_webhook_source_verification_merchant_secret(
                merchant_id,
                connector_name,
                connector_webhook_details,
            )
            .await
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;

        algorithm
            .decode_message(&secret.secret, message.into())
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)
    }

    /// fn get_webhook_source_verification_algorithm
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
        Ok(Box::new(crypto::NoAlgorithm))
    }

    /// fn get_webhook_source_verification_merchant_secret
    async fn get_webhook_source_verification_merchant_secret(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_name: &str,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<api_models::webhooks::ConnectorWebhookSecrets, errors::ConnectorError> {
        let debug_suffix =
            format!("For merchant_id: {merchant_id:?}, and connector_name: {connector_name}");
        let default_secret = "default_secret".to_string();
        let merchant_secret = match connector_webhook_details {
            Some(merchant_connector_webhook_details) => {
                let connector_webhook_details = merchant_connector_webhook_details
                    .parse_value::<api_models::admin::MerchantConnectorWebhookDetails>(
                        "MerchantConnectorWebhookDetails",
                    )
                    .change_context_lazy(|| errors::ConnectorError::WebhookSourceVerificationFailed)
                    .attach_printable_lazy(|| {
                        format!(
                            "Deserializing MerchantConnectorWebhookDetails failed {debug_suffix}",
                        )
                    })?;
                api_models::webhooks::ConnectorWebhookSecrets {
                    secret: connector_webhook_details
                        .merchant_secret
                        .expose()
                        .into_bytes(),
                    additional_secret: connector_webhook_details.additional_secret,
                }
            }

            None => api_models::webhooks::ConnectorWebhookSecrets {
                secret: default_secret.into_bytes(),
                additional_secret: None,
            },
        };

        //need to fetch merchant secret from config table with caching in future for enhanced performance

        //If merchant has not set the secret for webhook source verification, "default_secret" is returned.
        //So it will fail during verification step and goes to psync flow.
        Ok(merchant_secret)
    }

    /// fn get_webhook_source_verification_signature
    fn get_webhook_source_verification_signature(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        Ok(Vec::new())
    }

    /// fn get_webhook_source_verification_message
    fn get_webhook_source_verification_message(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        Ok(Vec::new())
    }

    /// fn verify_webhook_source
    async fn verify_webhook_source(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>,
        connector_name: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        let algorithm = self
            .get_webhook_source_verification_algorithm(request)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;

        let connector_webhook_secrets = self
            .get_webhook_source_verification_merchant_secret(
                merchant_id,
                connector_name,
                connector_webhook_details,
            )
            .await
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;

        let signature = self
            .get_webhook_source_verification_signature(request, &connector_webhook_secrets)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;

        let message = self
            .get_webhook_source_verification_message(
                request,
                merchant_id,
                &connector_webhook_secrets,
            )
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;

        algorithm
            .verify_signature(&connector_webhook_secrets.secret, &signature, &message)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
    }

    /// fn get_webhook_object_reference_id
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError>;

    /// fn get_status_update_object
    #[cfg(feature = "payouts")]
    fn get_payout_webhook_details(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::PayoutWebhookUpdate, errors::ConnectorError> {
        Ok(api_models::webhooks::PayoutWebhookUpdate {
            error_code: None,
            error_message: None,
        })
    }

    /// fn get_webhook_event_type
    fn get_webhook_event_type(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError>;

    /// fn get_webhook_resource_object
    fn get_webhook_resource_object(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError>;

    /// fn get_webhook_api_response
    fn get_webhook_api_response(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
        _error_kind: Option<IncomingWebhookFlowError>,
    ) -> CustomResult<ApplicationResponse<serde_json::Value>, errors::ConnectorError> {
        Ok(ApplicationResponse::StatusOk)
    }

    /// fn get_dispute_details
    fn get_dispute_details(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<crate::disputes::DisputePayload, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_dispute_details method".to_string()).into())
    }

    /// fn get_external_authentication_details
    fn get_external_authentication_details(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<crate::authentication::ExternalAuthenticationPayload, errors::ConnectorError>
    {
        Err(errors::ConnectorError::NotImplemented(
            "get_external_authentication_details method".to_string(),
        )
        .into())
    }

    /// fn get_mandate_details
    fn get_mandate_details(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<hyperswitch_domain_models::router_flow_types::ConnectorMandateDetails>,
        errors::ConnectorError,
    > {
        Ok(None)
    }

    /// fn get_network_txn_id
    fn get_network_txn_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<hyperswitch_domain_models::router_flow_types::ConnectorNetworkTxnId>,
        errors::ConnectorError,
    > {
        Ok(None)
    }

    /// fn to get additional payment method data from connector if any
    fn get_additional_payment_method_data(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<api_models::payment_methods::PaymentMethodUpdate>,
        errors::ConnectorError,
    > {
        Ok(None)
    }

    #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
    /// get revenue recovery invoice details
    fn get_revenue_recovery_attempt_details(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        hyperswitch_domain_models::revenue_recovery::RevenueRecoveryAttemptData,
        errors::ConnectorError,
    > {
        Err(errors::ConnectorError::NotImplemented(
            "get_revenue_recovery_attempt_details method".to_string(),
        )
        .into())
    }
    #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
    /// get revenue recovery transaction details
    fn get_revenue_recovery_invoice_details(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        hyperswitch_domain_models::revenue_recovery::RevenueRecoveryInvoiceData,
        errors::ConnectorError,
    > {
        Err(errors::ConnectorError::NotImplemented(
            "get_revenue_recovery_invoice_details method".to_string(),
        )
        .into())
    }

    /// get subscription MIT payment data from webhook
    fn get_subscription_mit_payment_data(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        hyperswitch_domain_models::router_flow_types::SubscriptionMitPaymentData,
        errors::ConnectorError,
    > {
        Err(errors::ConnectorError::NotImplemented(
            "get_subscription_mit_payment_data method".to_string(),
        )
        .into())
    }
}


// File: crates/hyperswitch_interfaces/src/encryption_interface.rs

//! Encryption related interface and error types

#![warn(missing_docs, missing_debug_implementations)]

use common_utils::errors::CustomResult;

/// Trait defining the interface for encryption management
#[async_trait::async_trait]
pub trait EncryptionManagementInterface: Sync + Send + dyn_clone::DynClone {
    /// Encrypt the given input data
    async fn encrypt(&self, input: &[u8]) -> CustomResult<Vec<u8>, EncryptionError>;

    /// Decrypt the given input data
    async fn decrypt(&self, input: &[u8]) -> CustomResult<Vec<u8>, EncryptionError>;
}

dyn_clone::clone_trait_object!(EncryptionManagementInterface);

/// Errors that may occur during above encryption functionalities
#[derive(Debug, thiserror::Error)]
pub enum EncryptionError {
    /// An error occurred when encrypting input data.
    #[error("Failed to encrypt input data")]
    EncryptionFailed,

    /// An error occurred when decrypting input data.
    #[error("Failed to decrypt input data")]
    DecryptionFailed,
}


// File: crates/hyperswitch_interfaces/src/errors.rs

//! Errors interface

use common_enums::ApiClientError;
use common_utils::errors::ErrorSwitch;
use hyperswitch_domain_models::errors::api_error_response::ApiErrorResponse;

/// Connector Errors
#[allow(missing_docs, missing_debug_implementations)]
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum ConnectorError {
    #[error("Error while obtaining URL for the integration")]
    FailedToObtainIntegrationUrl,
    #[error("Failed to encode connector request")]
    RequestEncodingFailed,
    #[error("Request encoding failed : {0}")]
    RequestEncodingFailedWithReason(String),
    #[error("Parsing failed")]
    ParsingFailed,
    #[error("Failed to deserialize connector response")]
    ResponseDeserializationFailed,
    #[error("Failed to execute a processing step: {0:?}")]
    ProcessingStepFailed(Option<bytes::Bytes>),
    #[error("The connector returned an unexpected response: {0:?}")]
    UnexpectedResponseError(bytes::Bytes),
    #[error("Failed to parse custom routing rules from merchant account")]
    RoutingRulesParsingError,
    #[error("Failed to obtain preferred connector from merchant account")]
    FailedToObtainPreferredConnector,
    #[error("An invalid connector name was provided")]
    InvalidConnectorName,
    #[error("An invalid Wallet was used")]
    InvalidWallet,
    #[error("Failed to handle connector response")]
    ResponseHandlingFailed,
    #[error("Missing required field: {field_name}")]
    MissingRequiredField { field_name: &'static str },
    #[error("Missing required fields: {field_names:?}")]
    MissingRequiredFields { field_names: Vec<&'static str> },
    #[error("Failed to obtain authentication type")]
    FailedToObtainAuthType,
    #[error("Failed to obtain certificate")]
    FailedToObtainCertificate,
    #[error("Connector meta data not found")]
    NoConnectorMetaData,
    #[error("Connector wallet details not found")]
    NoConnectorWalletDetails,
    #[error("Failed to obtain certificate key")]
    FailedToObtainCertificateKey,
    #[error("This step has not been implemented for: {0}")]
    NotImplemented(String),
    #[error("{message} is not supported by {connector}")]
    NotSupported {
        message: String,
        connector: &'static str,
    },
    #[error("{flow} flow not supported by {connector} connector")]
    FlowNotSupported { flow: String, connector: String },
    #[error("Capture method not supported")]
    CaptureMethodNotSupported,
    #[error("Missing connector mandate ID")]
    MissingConnectorMandateID,
    #[error("Missing connector mandate metadata")]
    MissingConnectorMandateMetadata,
    #[error("Missing connector transaction ID")]
    MissingConnectorTransactionID,
    #[error("Missing connector refund ID")]
    MissingConnectorRefundID,
    #[error("Missing apple pay tokenization data")]
    MissingApplePayTokenData,
    #[error("Webhooks not implemented for this connector")]
    WebhooksNotImplemented,
    #[error("Failed to decode webhook event body")]
    WebhookBodyDecodingFailed,
    #[error("Signature not found for incoming webhook")]
    WebhookSignatureNotFound,
    #[error("Failed to verify webhook source")]
    WebhookSourceVerificationFailed,
    #[error("Could not find merchant secret in DB for incoming webhook source verification")]
    WebhookVerificationSecretNotFound,
    #[error("Merchant secret found for incoming webhook source verification is invalid")]
    WebhookVerificationSecretInvalid,
    #[error("Incoming webhook object reference ID not found")]
    WebhookReferenceIdNotFound,
    #[error("Incoming webhook event type not found")]
    WebhookEventTypeNotFound,
    #[error("Incoming webhook event resource object not found")]
    WebhookResourceObjectNotFound,
    #[error("Could not respond to the incoming webhook event")]
    WebhookResponseEncodingFailed,
    #[error("Invalid Date/time format")]
    InvalidDateFormat,
    #[error("Date Formatting Failed")]
    DateFormattingFailed,
    #[error("Invalid Data format")]
    InvalidDataFormat { field_name: &'static str },
    #[error("Payment Method data / Payment Method Type / Payment Experience Mismatch ")]
    MismatchedPaymentData,
    #[error("Failed to parse {wallet_name} wallet token")]
    InvalidWalletToken { wallet_name: String },
    #[error("Missing Connector Related Transaction ID")]
    MissingConnectorRelatedTransactionID { id: String },
    #[error("File Validation failed")]
    FileValidationFailed { reason: String },
    #[error("Missing 3DS redirection payload: {field_name}")]
    MissingConnectorRedirectionPayload { field_name: &'static str },
    #[error("Failed at connector's end with code '{code}'")]
    FailedAtConnector { message: String, code: String },
    #[error("Payment Method Type not found")]
    MissingPaymentMethodType,
    #[error("Balance in the payment method is low")]
    InSufficientBalanceInPaymentMethod,
    #[error("Server responded with Request Timeout")]
    RequestTimeoutReceived,
    #[error("The given currency method is not configured with the given connector")]
    CurrencyNotSupported {
        message: String,
        connector: &'static str,
    },
    #[error("Invalid Configuration")]
    InvalidConnectorConfig { config: &'static str },
    #[error("Failed to convert amount to required type")]
    AmountConversionFailed,
    #[error("Generic Error")]
    GenericError {
        error_message: String,
        error_object: serde_json::Value,
    },
    #[error("Field {fields} doesn't match with the ones used during mandate creation")]
    MandatePaymentDataMismatch { fields: String },
    #[error("Field '{field_name}' is too long for connector '{connector}'")]
    MaxFieldLengthViolated {
        connector: String,
        field_name: String,
        max_length: usize,
        received_length: usize,
    },
}

impl ConnectorError {
    /// fn is_connector_timeout
    pub fn is_connector_timeout(&self) -> bool {
        self == &Self::RequestTimeoutReceived
    }
}

impl ErrorSwitch<ConnectorError> for common_utils::errors::ParsingError {
    fn switch(&self) -> ConnectorError {
        ConnectorError::ParsingFailed
    }
}

impl ErrorSwitch<ApiErrorResponse> for ConnectorError {
    fn switch(&self) -> ApiErrorResponse {
        match self {
            Self::WebhookSourceVerificationFailed => ApiErrorResponse::WebhookAuthenticationFailed,
            Self::WebhookSignatureNotFound
            | Self::WebhookReferenceIdNotFound
            | Self::WebhookResourceObjectNotFound
            | Self::WebhookBodyDecodingFailed
            | Self::WebhooksNotImplemented => ApiErrorResponse::WebhookBadRequest,
            Self::WebhookEventTypeNotFound => ApiErrorResponse::WebhookUnprocessableEntity,
            Self::WebhookVerificationSecretInvalid => {
                ApiErrorResponse::WebhookInvalidMerchantSecret
            }
            _ => ApiErrorResponse::InternalServerError,
        }
    }
}

// http client errors
#[allow(missing_docs, missing_debug_implementations)]
#[derive(Debug, Clone, thiserror::Error, PartialEq)]
pub enum HttpClientError {
    #[error("Header map construction failed")]
    HeaderMapConstructionFailed,
    #[error("Invalid proxy configuration")]
    InvalidProxyConfiguration,
    #[error("Client construction failed")]
    ClientConstructionFailed,
    #[error("Certificate decode failed")]
    CertificateDecodeFailed,
    #[error("Request body serialization failed")]
    BodySerializationFailed,
    #[error("Unexpected state reached/Invariants conflicted")]
    UnexpectedState,

    #[error("Failed to parse URL")]
    UrlParsingFailed,
    #[error("URL encoding of request payload failed")]
    UrlEncodingFailed,
    #[error("Failed to send request to connector {0}")]
    RequestNotSent(String),
    #[error("Failed to decode response")]
    ResponseDecodingFailed,

    #[error("Server responded with Request Timeout")]
    RequestTimeoutReceived,

    #[error("connection closed before a message could complete")]
    ConnectionClosedIncompleteMessage,

    #[error("Server responded with Internal Server Error")]
    InternalServerErrorReceived,
    #[error("Server responded with Bad Gateway")]
    BadGatewayReceived,
    #[error("Server responded with Service Unavailable")]
    ServiceUnavailableReceived,
    #[error("Server responded with Gateway Timeout")]
    GatewayTimeoutReceived,
    #[error("Server responded with unexpected response")]
    UnexpectedServerResponse,
}

impl ErrorSwitch<ApiClientError> for HttpClientError {
    fn switch(&self) -> ApiClientError {
        match self {
            Self::HeaderMapConstructionFailed => ApiClientError::HeaderMapConstructionFailed,
            Self::InvalidProxyConfiguration => ApiClientError::InvalidProxyConfiguration,
            Self::ClientConstructionFailed => ApiClientError::ClientConstructionFailed,
            Self::CertificateDecodeFailed => ApiClientError::CertificateDecodeFailed,
            Self::BodySerializationFailed => ApiClientError::BodySerializationFailed,
            Self::UnexpectedState => ApiClientError::UnexpectedState,
            Self::UrlParsingFailed => ApiClientError::UrlParsingFailed,
            Self::UrlEncodingFailed => ApiClientError::UrlEncodingFailed,
            Self::RequestNotSent(reason) => ApiClientError::RequestNotSent(reason.clone()),
            Self::ResponseDecodingFailed => ApiClientError::ResponseDecodingFailed,
            Self::RequestTimeoutReceived => ApiClientError::RequestTimeoutReceived,
            Self::ConnectionClosedIncompleteMessage => {
                ApiClientError::ConnectionClosedIncompleteMessage
            }
            Self::InternalServerErrorReceived => ApiClientError::InternalServerErrorReceived,
            Self::BadGatewayReceived => ApiClientError::BadGatewayReceived,
            Self::ServiceUnavailableReceived => ApiClientError::ServiceUnavailableReceived,
            Self::GatewayTimeoutReceived => ApiClientError::GatewayTimeoutReceived,
            Self::UnexpectedServerResponse => ApiClientError::UnexpectedServerResponse,
        }
    }
}


// File: crates/hyperswitch_interfaces/src/api_client.rs

use std::{
    fmt::Debug,
    time::{Duration, Instant},
};

use common_enums::ApiClientError;
use common_utils::{
    consts::{X_CONNECTOR_NAME, X_FLOW_NAME, X_REQUEST_ID},
    errors::CustomResult,
    request::{Request, RequestContent},
};
use error_stack::{report, ResultExt};
use http::Method;
use hyperswitch_domain_models::{
    errors::api_error_response,
    router_data::{ErrorResponse, RouterData},
};
use masking::Maskable;
use reqwest::multipart::Form;
use router_env::{instrument, logger, tracing, RequestId};
use serde_json::json;

use crate::{
    configs,
    connector_integration_interface::{
        BoxedConnectorIntegrationInterface, ConnectorEnum, RouterDataConversion,
    },
    consts,
    errors::ConnectorError,
    events,
    events::connector_api_logs::ConnectorEvent,
    metrics, types,
    types::Proxy,
};

/// A trait representing a converter for connector names to their corresponding enum variants.
pub trait ConnectorConverter: Send + Sync {
    /// Get the connector enum variant by its name
    fn get_connector_enum_by_name(
        &self,
        connector: &str,
    ) -> CustomResult<ConnectorEnum, api_error_response::ApiErrorResponse>;
}
/// A trait representing a builder for HTTP requests.
pub trait RequestBuilder: Send + Sync {
    /// Build a JSON request
    fn json(&mut self, body: serde_json::Value);
    /// Build a URL encoded form request
    fn url_encoded_form(&mut self, body: serde_json::Value);
    /// Set the timeout duration for the request
    fn timeout(&mut self, timeout: Duration);
    /// Build a multipart request
    fn multipart(&mut self, form: Form);
    /// Add a header to the request
    fn header(&mut self, key: String, value: Maskable<String>) -> CustomResult<(), ApiClientError>;
    /// Send the request and return a future that resolves to the response
    fn send(
        self,
    ) -> CustomResult<
        Box<dyn core::future::Future<Output = Result<reqwest::Response, reqwest::Error>> + 'static>,
        ApiClientError,
    >;
}

/// A trait representing an API client capable of making HTTP requests.
#[async_trait::async_trait]
pub trait ApiClient: dyn_clone::DynClone
where
    Self: Send + Sync,
{
    /// Create a new request with the specified HTTP method and URL
    fn request(
        &self,
        method: Method,
        url: String,
    ) -> CustomResult<Box<dyn RequestBuilder>, ApiClientError>;

    /// Create a new request with the specified HTTP method, URL, and client certificate
    fn request_with_certificate(
        &self,
        method: Method,
        url: String,
        certificate: Option<masking::Secret<String>>,
        certificate_key: Option<masking::Secret<String>>,
    ) -> CustomResult<Box<dyn RequestBuilder>, ApiClientError>;

    /// Send a request and return the response
    async fn send_request(
        &self,
        state: &dyn ApiClientWrapper,
        request: Request,
        option_timeout_secs: Option<u64>,
        forward_to_kafka: bool,
    ) -> CustomResult<reqwest::Response, ApiClientError>;

    /// Add a request ID to the client for tracking purposes
    fn add_request_id(&mut self, request_id: RequestId);

    /// Get the current request ID, if any
    fn get_request_id(&self) -> Option<RequestId>;

    /// Get the current request ID as a string, if any
    fn get_request_id_str(&self) -> Option<String>;

    /// Add a flow name to the client for tracking purposes
    fn add_flow_name(&mut self, flow_name: String);
}

dyn_clone::clone_trait_object!(ApiClient);

/// A wrapper trait to get the ApiClient and Proxy from the state
pub trait ApiClientWrapper: Send + Sync {
    /// Get the ApiClient instance
    fn get_api_client(&self) -> &dyn ApiClient;
    /// Get the Proxy configuration
    fn get_proxy(&self) -> Proxy;
    /// Get the request ID as String if any
    fn get_request_id_str(&self) -> Option<String>;
    /// Get the request ID as &RequestId if any
    fn get_request_id(&self) -> Option<RequestId>;
    /// Get the tenant information
    fn get_tenant(&self) -> configs::Tenant;
    /// Get connectors configuration
    fn get_connectors(&self) -> configs::Connectors;
    /// Get the event handler
    fn event_handler(&self) -> &dyn events::EventHandlerInterface;
}

/// Handle the flow by interacting with connector module
/// `connector_request` is applicable only in case if the `CallConnectorAction` is `Trigger`
/// In other cases, It will be created if required, even if it is not passed
#[instrument(skip_all, fields(connector_name, payment_method))]
pub async fn execute_connector_processing_step<
    'b,
    'a,
    T,
    ResourceCommonData: Clone + RouterDataConversion<T, Req, Resp> + 'static,
    Req: Debug + Clone + 'static,
    Resp: Debug + Clone + 'static,
>(
    state: &dyn ApiClientWrapper,
    connector_integration: BoxedConnectorIntegrationInterface<T, ResourceCommonData, Req, Resp>,
    req: &'b RouterData<T, Req, Resp>,
    call_connector_action: common_enums::CallConnectorAction,
    connector_request: Option<Request>,
    return_raw_connector_response: Option<bool>,
) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
where
    T: Clone + Debug + 'static,
    // BoxedConnectorIntegration<T, Req, Resp>: 'b,
{
    // If needed add an error stack as follows
    // connector_integration.build_request(req).attach_printable("Failed to build request");
    tracing::Span::current().record("connector_name", &req.connector);
    tracing::Span::current().record("payment_method", req.payment_method.to_string());
    logger::debug!(connector_request=?connector_request);
    let mut router_data = req.clone();
    match call_connector_action {
        common_enums::CallConnectorAction::HandleResponse(res) => {
            let response = types::Response {
                headers: None,
                response: res.into(),
                status_code: 200,
            };
            connector_integration.handle_response(req, None, response)
        }
        common_enums::CallConnectorAction::UCSConsumeResponse(_)
        | common_enums::CallConnectorAction::UCSHandleResponse(_) => {
            Err(ConnectorError::ProcessingStepFailed(Some(
                "CallConnectorAction UCSHandleResponse/UCSConsumeResponse used in Direct gateway system flow. These actions are only valid in UCS gateway system"
                    .to_string()
                    .into(),
            ))
            .into())
        }
        common_enums::CallConnectorAction::Avoid => Ok(router_data),
        common_enums::CallConnectorAction::StatusUpdate {
            status,
            error_code,
            error_message,
        } => {
            router_data.status = status;
            let error_response = if error_code.is_some() | error_message.is_some() {
                Some(ErrorResponse {
                    code: error_code.unwrap_or(consts::NO_ERROR_CODE.to_string()),
                    message: error_message.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
                    status_code: 200, // This status code is ignored in redirection response it will override with 302 status code.
                    reason: None,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                })
            } else {
                None
            };
            router_data.response = error_response.map(Err).unwrap_or(router_data.response);
            Ok(router_data)
        }
        common_enums::CallConnectorAction::Trigger => {
            metrics::CONNECTOR_CALL_COUNT.add(
                1,
                router_env::metric_attributes!(
                    ("connector", req.connector.to_string()),
                    (
                        "flow",
                        get_flow_name::<T>().unwrap_or_else(|_| "UnknownFlow".to_string())
                    ),
                ),
            );

            let connector_request = match connector_request {
                Some(connector_request) => Some(connector_request),
                None => connector_integration
                    .build_request(req, &state.get_connectors())
                    .inspect_err(|error| {
                        if matches!(
                            error.current_context(),
                            &ConnectorError::RequestEncodingFailed
                                | &ConnectorError::RequestEncodingFailedWithReason(_)
                        ) {
                            metrics::REQUEST_BUILD_FAILURE.add(
                                1,
                                router_env::metric_attributes!((
                                    "connector",
                                    req.connector.clone()
                                )),
                            )
                        }
                    })?,
            };

            match connector_request {
                Some(mut request) => {
                    let masked_request_body = match &request.body {
                        Some(request) => match request {
                            RequestContent::Json(i)
                            | RequestContent::FormUrlEncoded(i)
                            | RequestContent::Xml(i) => i
                                .masked_serialize()
                                .unwrap_or(json!({ "error": "failed to mask serialize"})),
                            RequestContent::FormData((_, i)) => i
                                .masked_serialize()
                                .unwrap_or(json!({ "error": "failed to mask serialize"})),
                            RequestContent::RawBytes(_) => json!({"request_type": "RAW_BYTES"}),
                        },
                        None => serde_json::Value::Null,
                    };
                    let flow_name =
                        get_flow_name::<T>().unwrap_or_else(|_| "UnknownFlow".to_string());
                    request.headers.insert((
                        X_FLOW_NAME.to_string(),
                        Maskable::Masked(masking::Secret::new(flow_name.to_string())),
                    ));
                    let connector_name = req.connector.clone();
                    request.headers.insert((
                        X_CONNECTOR_NAME.to_string(),
                        Maskable::Masked(masking::Secret::new(connector_name.clone().to_string())),
                    ));
                    state.get_request_id().as_ref().map(|id| {
                        let request_id = id.to_string();
                        request.headers.insert((
                            X_REQUEST_ID.to_string(),
                            Maskable::Normal(request_id.clone()),
                        ));
                        request_id
                    });
                    let request_url = request.url.clone();
                    let request_method = request.method;
                    let current_time = Instant::now();
                    let response =
                        call_connector_api(state, request, "execute_connector_processing_step")
                            .await;
                    let external_latency = current_time.elapsed().as_millis();
                    logger::info!(raw_connector_request=?masked_request_body);
                    let status_code = response
                        .as_ref()
                        .map(|i| {
                            i.as_ref()
                                .map_or_else(|value| value.status_code, |value| value.status_code)
                        })
                        .unwrap_or_default();
                    let mut connector_event = ConnectorEvent::new(
                        state.get_tenant().tenant_id.clone(),
                        req.connector.clone(),
                        std::any::type_name::<T>(),
                        masked_request_body,
                        request_url,
                        request_method,
                        req.payment_id.clone(),
                        req.merchant_id.clone(),
                        state.get_request_id().as_ref(),
                        external_latency,
                        req.refund_id.clone(),
                        req.dispute_id.clone(),
                        status_code,
                    );

                    match response {
                        Ok(body) => {
                            let response = match body {
                                Ok(body) => {
                                    let connector_http_status_code = Some(body.status_code);
                                    let handle_response_result = connector_integration
                                        .handle_response(
                                            req,
                                            Some(&mut connector_event),
                                            body.clone(),
                                        )
                                        .inspect_err(|error| {
                                            if error.current_context()
                                                == &ConnectorError::ResponseDeserializationFailed
                                            {
                                                metrics::RESPONSE_DESERIALIZATION_FAILURE.add(
                                                    1,
                                                    router_env::metric_attributes!((
                                                        "connector",
                                                        req.connector.clone(),
                                                    )),
                                                )
                                            }
                                        });
                                    match handle_response_result {
                                        Ok(mut data) => {
                                            state
                                                .event_handler()
                                                .log_connector_event(&connector_event);
                                            data.connector_http_status_code =
                                                connector_http_status_code;
                                            // Add up multiple external latencies in case of multiple external calls within the same request.
                                            data.external_latency = Some(
                                                data.external_latency
                                                    .map_or(external_latency, |val| {
                                                        val + external_latency
                                                    }),
                                            );

                                            store_raw_connector_response_if_required(
                                                return_raw_connector_response,
                                                &mut data,
                                                &body,
                                            )?;

                                            Ok(data)
                                        }
                                        Err(err) => {
                                            connector_event
                                                .set_error(json!({"error": err.to_string()}));

                                            state
                                                .event_handler()
                                                .log_connector_event(&connector_event);
                                            Err(err)
                                        }
                                    }?
                                }
                                Err(body) => {
                                    router_data.connector_http_status_code = Some(body.status_code);
                                    router_data.external_latency = Some(
                                        router_data
                                            .external_latency
                                            .map_or(external_latency, |val| val + external_latency),
                                    );
                                    metrics::CONNECTOR_ERROR_RESPONSE_COUNT.add(
                                        1,
                                        router_env::metric_attributes!((
                                            "connector",
                                            req.connector.clone(),
                                        )),
                                    );

                                    store_raw_connector_response_if_required(
                                        return_raw_connector_response,
                                        &mut router_data,
                                        &body,
                                    )?;

                                    let error = match body.status_code {
                                        500..=511 => {
                                            let error_res = connector_integration
                                                .get_5xx_error_response(
                                                    body,
                                                    Some(&mut connector_event),
                                                )?;
                                            state
                                                .event_handler()
                                                .log_connector_event(&connector_event);
                                            error_res
                                        }
                                        _ => {
                                            let error_res = connector_integration
                                                .get_error_response(
                                                    body,
                                                    Some(&mut connector_event),
                                                )?;
                                            if let Some(status) = error_res.attempt_status {
                                                router_data.status = status;
                                            };
                                            state
                                                .event_handler()
                                                .log_connector_event(&connector_event);
                                            error_res
                                        }
                                    };

                                    router_data.response = Err(error);

                                    router_data
                                }
                            };
                            Ok(response)
                        }
                        Err(error) => {
                            connector_event.set_error(json!({"error": error.to_string()}));
                            state.event_handler().log_connector_event(&connector_event);
                            if error.current_context().is_upstream_timeout() {
                                let error_response = ErrorResponse {
                                    code: consts::REQUEST_TIMEOUT_ERROR_CODE.to_string(),
                                    message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(),
                                    reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()),
                                    status_code: 504,
                                    attempt_status: None,
                                    connector_transaction_id: None,
                                    network_advice_code: None,
                                    network_decline_code: None,
                                    network_error_message: None,
                                    connector_metadata: None,
                                };
                                router_data.response = Err(error_response);
                                router_data.connector_http_status_code = Some(504);
                                router_data.external_latency = Some(
                                    router_data
                                        .external_latency
                                        .map_or(external_latency, |val| val + external_latency),
                                );
                                Ok(router_data)
                            } else {
                                Err(error
                                    .change_context(ConnectorError::ProcessingStepFailed(None)))
                            }
                        }
                    }
                }
                None => Ok(router_data),
            }
        }
    }
}

/// Calls the connector API and handles the response
#[instrument(skip_all)]
pub async fn call_connector_api(
    state: &dyn ApiClientWrapper,
    request: Request,
    flow_name: &str,
) -> CustomResult<Result<types::Response, types::Response>, ApiClientError> {
    let current_time = Instant::now();
    let headers = request.headers.clone();
    let url = request.url.clone();
    let response = state
        .get_api_client()
        .send_request(state, request, None, true)
        .await;

    match response.as_ref() {
        Ok(resp) => {
            let status_code = resp.status().as_u16();
            let elapsed_time = current_time.elapsed();
            logger::info!(
                ?headers,
                url,
                status_code,
                flow=?flow_name,
                ?elapsed_time
            );
        }
        Err(err) => {
            logger::info!(
                call_connector_api_error=?err
            );
        }
    }

    handle_response(response).await
}

/// Handle the response from the API call
#[instrument(skip_all)]
pub async fn handle_response(
    response: CustomResult<reqwest::Response, ApiClientError>,
) -> CustomResult<Result<types::Response, types::Response>, ApiClientError> {
    response
        .map(|response| async {
            logger::info!(?response);
            let status_code = response.status().as_u16();
            let headers = Some(response.headers().to_owned());
            match status_code {
                200..=202 | 302 | 204 => {
                    // If needed add log line
                    // logger:: error!( error_parsing_response=?err);
                    let response = response
                        .bytes()
                        .await
                        .change_context(ApiClientError::ResponseDecodingFailed)
                        .attach_printable("Error while waiting for response")?;
                    Ok(Ok(types::Response {
                        headers,
                        response,
                        status_code,
                    }))
                }

                status_code @ 500..=599 => {
                    let bytes = response.bytes().await.map_err(|error| {
                        report!(error)
                            .change_context(ApiClientError::ResponseDecodingFailed)
                            .attach_printable("Client error response received")
                    })?;
                    // let error = match status_code {
                    //     500 => ApiClientError::InternalServerErrorReceived,
                    //     502 => ApiClientError::BadGatewayReceived,
                    //     503 => ApiClientError::ServiceUnavailableReceived,
                    //     504 => ApiClientError::GatewayTimeoutReceived,
                    //     _ => ApiClientError::UnexpectedServerResponse,
                    // };
                    Ok(Err(types::Response {
                        headers,
                        response: bytes,
                        status_code,
                    }))
                }

                status_code @ 400..=499 => {
                    let bytes = response.bytes().await.map_err(|error| {
                        report!(error)
                            .change_context(ApiClientError::ResponseDecodingFailed)
                            .attach_printable("Client error response received")
                    })?;
                    /* let error = match status_code {
                        400 => ApiClientError::BadRequestReceived(bytes),
                        401 => ApiClientError::UnauthorizedReceived(bytes),
                        403 => ApiClientError::ForbiddenReceived,
                        404 => ApiClientError::NotFoundReceived(bytes),
                        405 => ApiClientError::MethodNotAllowedReceived,
                        408 => ApiClientError::RequestTimeoutReceived,
                        422 => ApiClientError::UnprocessableEntityReceived(bytes),
                        429 => ApiClientError::TooManyRequestsReceived,
                        _ => ApiClientError::UnexpectedServerResponse,
                    };
                    Err(report!(error).attach_printable("Client error response received"))
                        */
                    Ok(Err(types::Response {
                        headers,
                        response: bytes,
                        status_code,
                    }))
                }

                _ => Err(report!(ApiClientError::UnexpectedServerResponse)
                    .attach_printable("Unexpected response from server")),
            }
        })?
        .await
}

/// Store the raw connector response in the router data if required
pub fn store_raw_connector_response_if_required<T, Req, Resp>(
    return_raw_connector_response: Option<bool>,
    router_data: &mut RouterData<T, Req, Resp>,
    body: &types::Response,
) -> CustomResult<(), ConnectorError>
where
    T: Clone + Debug + 'static,
    Req: Debug + Clone + 'static,
    Resp: Debug + Clone + 'static,
{
    if return_raw_connector_response == Some(true) {
        let mut decoded = String::from_utf8(body.response.as_ref().to_vec())
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        if decoded.starts_with('\u{feff}') {
            decoded = decoded.trim_start_matches('\u{feff}').to_string();
        }
        router_data.raw_connector_response = Some(masking::Secret::new(decoded));
    }
    Ok(())
}

/// Get the flow name from the type
#[inline]
pub fn get_flow_name<F>() -> CustomResult<String, api_error_response::ApiErrorResponse> {
    Ok(std::any::type_name::<F>()
        .to_string()
        .rsplit("::")
        .next()
        .ok_or(api_error_response::ApiErrorResponse::InternalServerError)
        .attach_printable("Flow stringify failed")?
        .to_string())
}


// File: crates/hyperswitch_interfaces/src/connector_integration_v2.rs

//! definition of the new connector integration trait

use common_utils::{
    errors::CustomResult,
    request::{Method, Request, RequestBuilder, RequestContent},
};
use hyperswitch_domain_models::{router_data::ErrorResponse, router_data_v2::RouterDataV2};
use masking::Maskable;
use serde_json::json;

use crate::{
    api::{self, subscriptions_v2, CaptureSyncMethod},
    errors,
    events::connector_api_logs::ConnectorEvent,
    metrics, types, webhooks,
};

/// ConnectorV2 trait
pub trait ConnectorV2:
    Send
    + api::refunds_v2::RefundV2
    + api::payments_v2::PaymentV2
    + api::ConnectorRedirectResponse
    + webhooks::IncomingWebhook
    + api::ConnectorAuthenticationTokenV2
    + api::ConnectorAccessTokenV2
    + api::disputes_v2::DisputeV2
    + api::files_v2::FileUploadV2
    + api::ConnectorTransactionId
    + api::PayoutsV2
    + api::ConnectorVerifyWebhookSourceV2
    + api::FraudCheckV2
    + api::ConnectorMandateRevokeV2
    + api::authentication_v2::ExternalAuthenticationV2
    + api::UnifiedAuthenticationServiceV2
    + api::revenue_recovery_v2::RevenueRecoveryV2
    + api::ExternalVaultV2
    + subscriptions_v2::SubscriptionsV2
{
}
impl<
        T: api::refunds_v2::RefundV2
            + api::payments_v2::PaymentV2
            + api::ConnectorRedirectResponse
            + Send
            + webhooks::IncomingWebhook
            + api::ConnectorAuthenticationTokenV2
            + api::ConnectorAccessTokenV2
            + api::disputes_v2::DisputeV2
            + api::files_v2::FileUploadV2
            + api::ConnectorTransactionId
            + api::PayoutsV2
            + api::ConnectorVerifyWebhookSourceV2
            + api::FraudCheckV2
            + api::ConnectorMandateRevokeV2
            + api::authentication_v2::ExternalAuthenticationV2
            + api::UnifiedAuthenticationServiceV2
            + api::revenue_recovery_v2::RevenueRecoveryV2
            + api::ExternalVaultV2
            + subscriptions_v2::SubscriptionsV2,
    > ConnectorV2 for T
{
}

/// Alias for Box<&'static (dyn ConnectorV2 + Sync)>
pub type BoxedConnectorV2 = Box<&'static (dyn ConnectorV2 + Sync)>;

impl api::ConnectorAccessTokenSuffix for BoxedConnectorV2 {}

/// alias for Box of a type that implements trait ConnectorIntegrationV2
pub type BoxedConnectorIntegrationV2<'a, Flow, ResourceCommonData, Req, Resp> =
    Box<&'a (dyn ConnectorIntegrationV2<Flow, ResourceCommonData, Req, Resp> + Send + Sync)>;

/// trait with a function that returns BoxedConnectorIntegrationV2
pub trait ConnectorIntegrationAnyV2<Flow, ResourceCommonData, Req, Resp>:
    Send + Sync + 'static
{
    /// function what returns BoxedConnectorIntegrationV2
    fn get_connector_integration_v2(
        &self,
    ) -> BoxedConnectorIntegrationV2<'_, Flow, ResourceCommonData, Req, Resp>;
}

impl<S, Flow, ResourceCommonData, Req, Resp>
    ConnectorIntegrationAnyV2<Flow, ResourceCommonData, Req, Resp> for S
where
    S: ConnectorIntegrationV2<Flow, ResourceCommonData, Req, Resp> + Send + Sync,
{
    fn get_connector_integration_v2(
        &self,
    ) -> BoxedConnectorIntegrationV2<'_, Flow, ResourceCommonData, Req, Resp> {
        Box::new(self)
    }
}

/// The new connector integration trait with an additional ResourceCommonData generic parameter
pub trait ConnectorIntegrationV2<Flow, ResourceCommonData, Req, Resp>:
    ConnectorIntegrationAnyV2<Flow, ResourceCommonData, Req, Resp> + Sync + api::ConnectorCommon
{
    /// returns a vec of tuple of header key and value
    fn get_headers(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        Ok(vec![])
    }

    /// returns content type
    fn get_content_type(&self) -> &'static str {
        mime::APPLICATION_JSON.essence_str()
    }

    /// primarily used when creating signature based on request method of payment flow
    fn get_http_method(&self) -> Method {
        Method::Post
    }

    /// returns url
    fn get_url(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<String, errors::ConnectorError> {
        metrics::UNIMPLEMENTED_FLOW
            .add(1, router_env::metric_attributes!(("connector", self.id())));
        Ok(String::new())
    }

    /// returns request body
    fn get_request_body(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Option<RequestContent>, errors::ConnectorError> {
        Ok(None)
    }

    /// returns form data
    fn get_request_form_data(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Option<reqwest::multipart::Form>, errors::ConnectorError> {
        Ok(None)
    }

    /// builds the request and returns it
    fn build_request_v2(
        &self,
        req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(self.get_http_method())
                .url(self.get_url(req)?.as_str())
                .attach_default_headers()
                .headers(self.get_headers(req)?)
                .set_optional_body(self.get_request_body(req)?)
                .add_certificate(self.get_certificate(req)?)
                .add_certificate_key(self.get_certificate_key(req)?)
                .build(),
        ))
    }

    /// accepts the raw api response and decodes it
    fn handle_response_v2(
        &self,
        data: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
        event_builder: Option<&mut ConnectorEvent>,
        _res: types::Response,
    ) -> CustomResult<RouterDataV2<Flow, ResourceCommonData, Req, Resp>, errors::ConnectorError>
    where
        Flow: Clone,
        ResourceCommonData: Clone,
        Req: Clone,
        Resp: Clone,
    {
        event_builder.map(|e| e.set_error(json!({"error": "Not Implemented"})));
        Ok(data.clone())
    }

    /// accepts the raw api error response and decodes it
    fn get_error_response_v2(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        event_builder.map(|event| event.set_error(json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
        Ok(ErrorResponse::get_not_implemented())
    }

    /// accepts the raw 5xx error response and decodes it
    fn get_5xx_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        event_builder.map(|event| event.set_error(json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
        let error_message = match res.status_code {
            500 => "internal_server_error",
            501 => "not_implemented",
            502 => "bad_gateway",
            503 => "service_unavailable",
            504 => "gateway_timeout",
            505 => "http_version_not_supported",
            506 => "variant_also_negotiates",
            507 => "insufficient_storage",
            508 => "loop_detected",
            510 => "not_extended",
            511 => "network_authentication_required",
            _ => "unknown_error",
        };
        Ok(ErrorResponse {
            code: res.status_code.to_string(),
            message: error_message.to_string(),
            reason: String::from_utf8(res.response.to_vec()).ok(),
            status_code: res.status_code,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }

    // whenever capture sync is implemented at the connector side, this method should be overridden
    /// retunes the capture sync method
    fn get_multiple_capture_sync_method(
        &self,
    ) -> CustomResult<CaptureSyncMethod, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("multiple capture sync".into()).into())
    }

    /// returns certificate string
    fn get_certificate(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Option<masking::Secret<String>>, errors::ConnectorError> {
        Ok(None)
    }

    /// returns private key string
    fn get_certificate_key(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Option<masking::Secret<String>>, errors::ConnectorError> {
        Ok(None)
    }
}


// File: crates/hyperswitch_interfaces/src/authentication.rs

//! Authentication interface

/// struct ExternalAuthenticationPayload
#[derive(Clone, serde::Deserialize, Debug, serde::Serialize, PartialEq, Eq)]
pub struct ExternalAuthenticationPayload {
    /// trans_status
    pub trans_status: common_enums::TransactionStatus,
    /// authentication_value
    pub authentication_value: Option<masking::Secret<String>>,
    /// eci
    pub eci: Option<String>,
    /// Indicates whether the challenge was canceled by the user or system.
    pub challenge_cancel: Option<String>,
    /// Reason for the challenge code, if applicable.
    pub challenge_code_reason: Option<String>,
}


// File: crates/hyperswitch_interfaces/src/api.rs

//! API interface

/// authentication module
pub mod authentication;
/// authentication_v2 module
pub mod authentication_v2;
pub mod disputes;
pub mod disputes_v2;
pub mod files;
pub mod files_v2;
#[cfg(feature = "frm")]
pub mod fraud_check;
#[cfg(feature = "frm")]
pub mod fraud_check_v2;
pub mod gateway;
pub mod payments;
pub mod payments_v2;
#[cfg(feature = "payouts")]
pub mod payouts;
#[cfg(feature = "payouts")]
pub mod payouts_v2;
pub mod refunds;
pub mod refunds_v2;
pub mod revenue_recovery;
pub mod revenue_recovery_v2;
pub mod subscriptions;
pub mod subscriptions_v2;
pub mod vault;
pub mod vault_v2;

use std::fmt::Debug;

use common_enums::{
    enums::{
        self, CallConnectorAction, CaptureMethod, EventClass, PaymentAction, PaymentMethodType,
    },
    PaymentMethod,
};
use common_utils::{
    errors::CustomResult,
    request::{Method, Request, RequestContent},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    connector_endpoints::Connectors,
    errors::api_error_response::ApiErrorResponse,
    payment_method_data::PaymentMethodData,
    router_data::{
        AccessToken, AccessTokenAuthenticationResponse, ConnectorAuthType, ErrorResponse,
        RouterData,
    },
    router_data_v2::{
        flow_common_types::{AuthenticationTokenFlowData, WebhookSourceVerifyData},
        AccessTokenFlowData, MandateRevokeFlowData, UasFlowData,
    },
    router_flow_types::{
        mandate_revoke::MandateRevoke, AccessTokenAuth, AccessTokenAuthentication, Authenticate,
        AuthenticationConfirmation, PostAuthenticate, PreAuthenticate, VerifyWebhookSource,
    },
    router_request_types::{
        self,
        unified_authentication_service::{
            UasAuthenticationRequestData, UasAuthenticationResponseData,
            UasConfirmationRequestData, UasPostAuthenticationRequestData,
            UasPreAuthenticationRequestData,
        },
        AccessTokenAuthenticationRequestData, AccessTokenRequestData, MandateRevokeRequestData,
        VerifyWebhookSourceRequestData,
    },
    router_response_types::{
        self, ConnectorInfo, MandateRevokeResponseData, PaymentMethodDetails,
        SupportedPaymentMethods, VerifyWebhookSourceResponseData,
    },
};
use masking::Maskable;
use serde_json::json;

#[cfg(feature = "frm")]
pub use self::fraud_check::*;
#[cfg(feature = "frm")]
pub use self::fraud_check_v2::*;
#[cfg(feature = "payouts")]
pub use self::payouts::*;
#[cfg(feature = "payouts")]
pub use self::payouts_v2::*;
pub use self::{payments::*, refunds::*, vault::*, vault_v2::*};
use crate::{
    api::subscriptions::Subscriptions, connector_integration_v2::ConnectorIntegrationV2, consts,
    errors, events::connector_api_logs::ConnectorEvent, metrics, types, webhooks,
};

/// Connector trait
pub trait Connector:
    Send
    + Refund
    + Payment
    + ConnectorRedirectResponse
    + webhooks::IncomingWebhook
    + ConnectorAccessToken
    + ConnectorAuthenticationToken
    + disputes::Dispute
    + files::FileUpload
    + ConnectorTransactionId
    + Payouts
    + ConnectorVerifyWebhookSource
    + FraudCheck
    + ConnectorMandateRevoke
    + authentication::ExternalAuthentication
    + TaxCalculation
    + UnifiedAuthenticationService
    + revenue_recovery::RevenueRecovery
    + ExternalVault
    + Subscriptions
{
}

impl<
        T: Refund
            + Payment
            + ConnectorRedirectResponse
            + Send
            + webhooks::IncomingWebhook
            + ConnectorAccessToken
            + ConnectorAuthenticationToken
            + disputes::Dispute
            + files::FileUpload
            + ConnectorTransactionId
            + Payouts
            + ConnectorVerifyWebhookSource
            + FraudCheck
            + ConnectorMandateRevoke
            + authentication::ExternalAuthentication
            + TaxCalculation
            + UnifiedAuthenticationService
            + revenue_recovery::RevenueRecovery
            + ExternalVault
            + Subscriptions,
    > Connector for T
{
}

/// Alias for Box<&'static (dyn Connector + Sync)>
pub type BoxedConnector = Box<&'static (dyn Connector + Sync)>;

/// type BoxedConnectorIntegration
pub type BoxedConnectorIntegration<'a, T, Req, Resp> =
    Box<&'a (dyn ConnectorIntegration<T, Req, Resp> + Send + Sync)>;

/// trait ConnectorIntegrationAny
pub trait ConnectorIntegrationAny<T, Req, Resp>: Send + Sync + 'static {
    /// fn get_connector_integration
    fn get_connector_integration(&self) -> BoxedConnectorIntegration<'_, T, Req, Resp>;
}

impl<S, T, Req, Resp> ConnectorIntegrationAny<T, Req, Resp> for S
where
    S: ConnectorIntegration<T, Req, Resp> + Send + Sync,
{
    fn get_connector_integration(&self) -> BoxedConnectorIntegration<'_, T, Req, Resp> {
        Box::new(self)
    }
}

/// trait ConnectorIntegration
pub trait ConnectorIntegration<T, Req, Resp>:
    ConnectorIntegrationAny<T, Req, Resp> + Sync + ConnectorCommon
{
    /// fn get_headers
    fn get_headers(
        &self,
        _req: &RouterData<T, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        Ok(vec![])
    }

    /// fn get_content_type
    fn get_content_type(&self) -> &'static str {
        mime::APPLICATION_JSON.essence_str()
    }

    /// fn get_content_type
    fn get_accept_type(&self) -> &'static str {
        mime::APPLICATION_JSON.essence_str()
    }

    /// primarily used when creating signature based on request method of payment flow
    fn get_http_method(&self) -> Method {
        Method::Post
    }

    /// fn get_url
    fn get_url(
        &self,
        _req: &RouterData<T, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(String::new())
    }

    /// fn get_request_body
    fn get_request_body(
        &self,
        _req: &RouterData<T, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Ok(RequestContent::Json(Box::new(json!(r#"{}"#))))
    }

    /// fn get_request_form_data
    fn get_request_form_data(
        &self,
        _req: &RouterData<T, Req, Resp>,
    ) -> CustomResult<Option<reqwest::multipart::Form>, errors::ConnectorError> {
        Ok(None)
    }

    /// fn build_request
    fn build_request(
        &self,
        req: &RouterData<T, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        metrics::UNIMPLEMENTED_FLOW.add(
            1,
            router_env::metric_attributes!(("connector", req.connector.clone())),
        );
        Ok(None)
    }

    /// fn handle_response
    fn handle_response(
        &self,
        data: &RouterData<T, Req, Resp>,
        event_builder: Option<&mut ConnectorEvent>,
        _res: types::Response,
    ) -> CustomResult<RouterData<T, Req, Resp>, errors::ConnectorError>
    where
        T: Clone,
        Req: Clone,
        Resp: Clone,
    {
        event_builder.map(|e| e.set_error(json!({"error": "Not Implemented"})));
        Ok(data.clone())
    }

    /// fn get_error_response
    fn get_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        event_builder.map(|event| event.set_error(json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
        Ok(ErrorResponse::get_not_implemented())
    }

    /// fn get_5xx_error_response
    fn get_5xx_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        event_builder.map(|event| event.set_error(json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
        let error_message = match res.status_code {
            500 => "internal_server_error",
            501 => "not_implemented",
            502 => "bad_gateway",
            503 => "service_unavailable",
            504 => "gateway_timeout",
            505 => "http_version_not_supported",
            506 => "variant_also_negotiates",
            507 => "insufficient_storage",
            508 => "loop_detected",
            510 => "not_extended",
            511 => "network_authentication_required",
            _ => "unknown_error",
        };
        Ok(ErrorResponse {
            code: res.status_code.to_string(),
            message: error_message.to_string(),
            reason: String::from_utf8(res.response.to_vec()).ok(),
            status_code: res.status_code,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }

    /// whenever capture sync is implemented at the connector side, this method should be overridden
    fn get_multiple_capture_sync_method(
        &self,
    ) -> CustomResult<CaptureSyncMethod, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("multiple capture sync".into()).into())
    }

    /// fn get_certificate
    fn get_certificate(
        &self,
        _req: &RouterData<T, Req, Resp>,
    ) -> CustomResult<Option<String>, errors::ConnectorError> {
        Ok(None)
    }

    /// fn get_certificate_key
    fn get_certificate_key(
        &self,
        _req: &RouterData<T, Req, Resp>,
    ) -> CustomResult<Option<String>, errors::ConnectorError> {
        Ok(None)
    }
}

/// Sync Methods for multiple captures
#[derive(Debug)]
pub enum CaptureSyncMethod {
    /// For syncing multiple captures individually
    Individual,
    /// For syncing multiple captures together
    Bulk,
}

/// Connector accepted currency unit as either "Base" or "Minor"
#[derive(Debug)]
pub enum CurrencyUnit {
    /// Base currency unit
    Base,
    /// Minor currency unit
    Minor,
}

/// The trait that provides the common
pub trait ConnectorCommon {
    /// Name of the connector (in lowercase).
    fn id(&self) -> &'static str;

    /// Connector accepted currency unit as either "Base" or "Minor"
    fn get_currency_unit(&self) -> CurrencyUnit {
        CurrencyUnit::Minor // Default implementation should be remove once it is implemented in all connectors
    }

    /// HTTP header used for authorization.
    fn get_auth_header(
        &self,
        _auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        Ok(Vec::new())
    }

    /// HTTP `Content-Type` to be used for POST requests.
    /// Defaults to `application/json`.
    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    // FIXME write doc - think about this
    // fn headers(&self) -> Vec<(&str, &str)>;

    /// The base URL for interacting with the connector's API.
    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str;

    /// common error response for a connector if it is same in all case
    fn build_error_response(
        &self,
        res: types::Response,
        _event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        Ok(ErrorResponse {
            status_code: res.status_code,
            code: consts::NO_ERROR_CODE.to_string(),
            message: consts::NO_ERROR_MESSAGE.to_string(),
            reason: None,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorAccessTokenSuffix for BoxedConnector {}

/// Current flow information passed to the connector specifications trait
///
/// In order to make some desicion about the preprocessing or alternate flow
#[derive(Clone, Debug)]
pub enum CurrentFlowInfo<'a> {
    /// Authorize flow information
    Authorize {
        /// The authentication type being used
        auth_type: &'a enums::AuthenticationType,
        /// The payment authorize request data
        request_data: &'a router_request_types::PaymentsAuthorizeData,
    },
    /// CompleteAuthorize flow information
    CompleteAuthorize {
        /// The payment authorize request data
        request_data: &'a router_request_types::CompleteAuthorizeData,
    },
}

/// Alternate API flow that must be made instead of the current flow.
/// For example, PreAuthenticate flow must be made instead of Authorize flow.
#[derive(Debug, Clone, Copy)]
pub enum AlternateFlow {
    /// Pre-authentication flow
    PreAuthenticate,
}

/// The Preprocessing flow that must be made before the current flow.
///
/// For example, PreProcessing flow must be made before Authorize flow.
/// Or PostAuthenticate flow must be made before CompleteAuthorize flow for cybersource.
#[derive(Debug, Clone, Copy)]
pub enum PreProcessingFlowName {
    /// Authentication flow must be made before the actual flow
    Authenticate,
    /// Post-authentication flow must be made before the actual flow
    PostAuthenticate,
}

/// Response of the preprocessing flow
#[derive(Debug)]
pub struct PreProcessingFlowResponse<'a> {
    /// Payment response data from the preprocessing flow
    pub response: &'a Result<router_response_types::PaymentsResponseData, ErrorResponse>,
    /// Attempt status after the preprocessing flow
    pub attempt_status: enums::AttemptStatus,
}

/// The trait that provides specifications about the connector
pub trait ConnectorSpecifications {
    /// Preprocessing flow name if any, that must be made before the current flow.
    fn get_preprocessing_flow_if_needed(
        &self,
        _current_flow: CurrentFlowInfo<'_>,
    ) -> Option<PreProcessingFlowName> {
        None
    }
    /// Based on the current flow and preprocessing_flow_response, decide if the main flow must be called or not
    ///
    /// By default, always continue with the main flow after the preprocessing flow.
    fn decide_should_continue_after_preprocessing(
        &self,
        _current_flow: CurrentFlowInfo<'_>,
        _pre_processing_flow_name: PreProcessingFlowName,
        _preprocessing_flow_response: PreProcessingFlowResponse<'_>,
    ) -> bool {
        true
    }
    /// If Some is returned, the returned api flow must be made instead of the current flow.
    fn get_alternate_flow_if_needed(
        &self,
        _current_flow: CurrentFlowInfo<'_>,
    ) -> Option<AlternateFlow> {
        None
    }
    /// Details related to payment method supported by the connector
    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        None
    }

    /// Supported webhooks flows
    fn get_supported_webhook_flows(&self) -> Option<&'static [EventClass]> {
        None
    }

    /// About the connector
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        None
    }

    /// Check if connector should make another request to create an access token
    /// Connectors should override this method if they require an authentication token to create a new access token
    fn authentication_token_for_token_creation(&self) -> bool {
        false
    }

    /// Check if connector should make another request to create an customer
    /// Connectors should override this method if they require to create a connector customer
    fn should_call_connector_customer(
        &self,
        _payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> bool {
        false
    }

    /// Whether SDK session token generation is enabled for this connector
    fn is_sdk_client_token_generation_enabled(&self) -> bool {
        false
    }

    /// Payment method types that support SDK session token generation
    fn supported_payment_method_types_for_sdk_client_token_generation(
        &self,
    ) -> Vec<PaymentMethodType> {
        vec![]
    }

    /// Validate if SDK session token generation is allowed for given payment method type
    fn validate_sdk_session_token_for_payment_method(
        &self,
        current_core_payment_method_type: &PaymentMethodType,
    ) -> bool {
        self.is_sdk_client_token_generation_enabled()
            && self
                .supported_payment_method_types_for_sdk_client_token_generation()
                .contains(current_core_payment_method_type)
    }

    #[cfg(not(feature = "v2"))]
    /// Generate connector request reference ID
    fn generate_connector_request_reference_id(
        &self,
        _payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
        is_config_enabled_to_send_payment_id_as_connector_request_id: bool,
    ) -> String {
        // Send payment_id if config is enabled for a merchant, else send attempt_id
        if is_config_enabled_to_send_payment_id_as_connector_request_id {
            payment_attempt.payment_id.get_string_repr().to_owned()
        } else {
            payment_attempt.attempt_id.to_owned()
        }
    }

    #[cfg(feature = "v2")]
    /// Generate connector request reference ID
    fn generate_connector_request_reference_id(
        &self,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> String {
        payment_intent
            .merchant_reference_id
            .as_ref()
            .map(|id| id.get_string_repr().to_owned())
            .unwrap_or_else(|| payment_attempt.id.get_string_repr().to_owned())
    }

    #[cfg(feature = "v1")]
    /// Generate connector customer reference ID for payments
    fn generate_connector_customer_id(
        &self,
        _customer_id: &Option<common_utils::id_type::CustomerId>,
        _merchant_id: &common_utils::id_type::MerchantId,
    ) -> Option<String> {
        None
    }

    #[cfg(feature = "v2")]
    /// Generate connector customer reference ID for payments
    fn generate_connector_customer_id(
        &self,
        _customer_id: &Option<common_utils::id_type::CustomerId>,
        _merchant_id: &common_utils::id_type::MerchantId,
    ) -> Option<String> {
        todo!()
    }

    /// Check if connector needs tokenization call before setup mandate flow
    fn should_call_tokenization_before_setup_mandate(&self) -> bool {
        true
    }
}

/// Extended trait for connector common to allow functions with generic type
pub trait ConnectorCommonExt<Flow, Req, Resp>:
    ConnectorCommon + ConnectorIntegration<Flow, Req, Resp>
{
    /// common header builder when every request for the connector have same headers
    fn build_headers(
        &self,
        _req: &RouterData<Flow, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        Ok(Vec::new())
    }
}

/// trait ConnectorMandateRevoke
pub trait ConnectorMandateRevoke:
    ConnectorIntegration<MandateRevoke, MandateRevokeRequestData, MandateRevokeResponseData>
{
}

/// trait ConnectorMandateRevokeV2
pub trait ConnectorMandateRevokeV2:
    ConnectorIntegrationV2<
    MandateRevoke,
    MandateRevokeFlowData,
    MandateRevokeRequestData,
    MandateRevokeResponseData,
>
{
}

/// trait ConnectorAuthenticationToken
pub trait ConnectorAuthenticationToken:
    ConnectorIntegration<
    AccessTokenAuthentication,
    AccessTokenAuthenticationRequestData,
    AccessTokenAuthenticationResponse,
>
{
}

/// trait ConnectorAuthenticationTokenV2
pub trait ConnectorAuthenticationTokenV2:
    ConnectorIntegrationV2<
    AccessTokenAuthentication,
    AuthenticationTokenFlowData,
    AccessTokenAuthenticationRequestData,
    AccessTokenAuthenticationResponse,
>
{
}

/// trait ConnectorAccessToken
pub trait ConnectorAccessToken:
    ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken>
{
}

/// trait ConnectorAccessTokenV2
pub trait ConnectorAccessTokenV2:
    ConnectorIntegrationV2<AccessTokenAuth, AccessTokenFlowData, AccessTokenRequestData, AccessToken>
{
}

/// trait ConnectorVerifyWebhookSource
pub trait ConnectorVerifyWebhookSource:
    ConnectorIntegration<
    VerifyWebhookSource,
    VerifyWebhookSourceRequestData,
    VerifyWebhookSourceResponseData,
>
{
}

/// trait ConnectorVerifyWebhookSourceV2
pub trait ConnectorVerifyWebhookSourceV2:
    ConnectorIntegrationV2<
    VerifyWebhookSource,
    WebhookSourceVerifyData,
    VerifyWebhookSourceRequestData,
    VerifyWebhookSourceResponseData,
>
{
}

/// trait UnifiedAuthenticationService
pub trait UnifiedAuthenticationService:
    ConnectorCommon
    + UasPreAuthentication
    + UasPostAuthentication
    + UasAuthenticationConfirmation
    + UasAuthentication
{
}

/// trait UasPreAuthentication
pub trait UasPreAuthentication:
    ConnectorIntegration<
    PreAuthenticate,
    UasPreAuthenticationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasPostAuthentication
pub trait UasPostAuthentication:
    ConnectorIntegration<
    PostAuthenticate,
    UasPostAuthenticationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasAuthenticationConfirmation
pub trait UasAuthenticationConfirmation:
    ConnectorIntegration<
    AuthenticationConfirmation,
    UasConfirmationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasAuthentication
pub trait UasAuthentication:
    ConnectorIntegration<Authenticate, UasAuthenticationRequestData, UasAuthenticationResponseData>
{
}

/// trait UnifiedAuthenticationServiceV2
pub trait UnifiedAuthenticationServiceV2:
    ConnectorCommon
    + UasPreAuthenticationV2
    + UasPostAuthenticationV2
    + UasAuthenticationV2
    + UasAuthenticationConfirmationV2
{
}

///trait UasPreAuthenticationV2
pub trait UasPreAuthenticationV2:
    ConnectorIntegrationV2<
    PreAuthenticate,
    UasFlowData,
    UasPreAuthenticationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasPostAuthenticationV2
pub trait UasPostAuthenticationV2:
    ConnectorIntegrationV2<
    PostAuthenticate,
    UasFlowData,
    UasPostAuthenticationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasAuthenticationConfirmationV2
pub trait UasAuthenticationConfirmationV2:
    ConnectorIntegrationV2<
    AuthenticationConfirmation,
    UasFlowData,
    UasConfirmationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasAuthenticationV2
pub trait UasAuthenticationV2:
    ConnectorIntegrationV2<
    Authenticate,
    UasFlowData,
    UasAuthenticationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait ConnectorValidation
pub trait ConnectorValidation: ConnectorCommon + ConnectorSpecifications {
    /// Validate, the payment request against the connector supported features
    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<CaptureMethod>,
        payment_method: PaymentMethod,
        pmt: Option<PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        let capture_method = capture_method.unwrap_or_default();
        let is_default_capture_method =
            [CaptureMethod::Automatic, CaptureMethod::SequentialAutomatic]
                .contains(&capture_method);
        let is_feature_supported = match self.get_supported_payment_methods() {
            Some(supported_payment_methods) => {
                let connector_payment_method_type_info = get_connector_payment_method_type_info(
                    supported_payment_methods,
                    payment_method,
                    pmt,
                    self.id(),
                )?;

                connector_payment_method_type_info
                    .map(|payment_method_type_info| {
                        payment_method_type_info
                            .supported_capture_methods
                            .contains(&capture_method)
                    })
                    .unwrap_or(true)
            }
            None => is_default_capture_method,
        };

        if is_feature_supported {
            Ok(())
        } else {
            Err(errors::ConnectorError::NotSupported {
                message: capture_method.to_string(),
                connector: self.id(),
            }
            .into())
        }
    }

    /// fn validate_mandate_payment
    fn validate_mandate_payment(
        &self,
        pm_type: Option<PaymentMethodType>,
        _pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        let connector = self.id();
        match pm_type {
            Some(pm_type) => Err(errors::ConnectorError::NotSupported {
                message: format!("{pm_type} mandate payment"),
                connector,
            }
            .into()),
            None => Err(errors::ConnectorError::NotSupported {
                message: " mandate payment".to_string(),
                connector,
            }
            .into()),
        }
    }

    /// fn validate_psync_reference_id
    fn validate_psync_reference_id(
        &self,
        data: &router_request_types::PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        data.connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)
            .map(|_| ())
    }

    /// fn is_webhook_source_verification_mandatory
    fn is_webhook_source_verification_mandatory(&self) -> bool {
        false
    }
}

/// trait ConnectorRedirectResponse
pub trait ConnectorRedirectResponse {
    /// fn get_flow_type
    fn get_flow_type(
        &self,
        _query_params: &str,
        _json_payload: Option<serde_json::Value>,
        _action: PaymentAction,
    ) -> CustomResult<CallConnectorAction, errors::ConnectorError> {
        Ok(CallConnectorAction::Avoid)
    }
}

/// Empty trait for when payouts feature is disabled
#[cfg(not(feature = "payouts"))]
pub trait Payouts {}
/// Empty trait for when payouts feature is disabled
#[cfg(not(feature = "payouts"))]
pub trait PayoutsV2 {}

/// Empty trait for when frm feature is disabled
#[cfg(not(feature = "frm"))]
pub trait FraudCheck {}
/// Empty trait for when frm feature is disabled
#[cfg(not(feature = "frm"))]
pub trait FraudCheckV2 {}

fn get_connector_payment_method_type_info(
    supported_payment_method: &SupportedPaymentMethods,
    payment_method: PaymentMethod,
    payment_method_type: Option<PaymentMethodType>,
    connector: &'static str,
) -> CustomResult<Option<PaymentMethodDetails>, errors::ConnectorError> {
    let payment_method_details =
        supported_payment_method
            .get(&payment_method)
            .ok_or_else(|| errors::ConnectorError::NotSupported {
                message: payment_method.to_string(),
                connector,
            })?;

    payment_method_type
        .map(|pmt| {
            payment_method_details.get(&pmt).cloned().ok_or_else(|| {
                errors::ConnectorError::NotSupported {
                    message: format!("{payment_method} {pmt}"),
                    connector,
                }
                .into()
            })
        })
        .transpose()
}

/// ConnectorTransactionId trait
pub trait ConnectorTransactionId: ConnectorCommon + Sync {
    /// fn connector_transaction_id
    fn connector_transaction_id(
        &self,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> Result<Option<String>, ApiErrorResponse> {
        Ok(payment_attempt
            .get_connector_payment_id()
            .map(ToString::to_string))
    }
}

/// Trait ConnectorAccessTokenSuffix
pub trait ConnectorAccessTokenSuffix {
    /// Function to get dynamic access token key suffix from Connector
    fn get_access_token_key<F, Req, Res>(
        &self,
        router_data: &RouterData<F, Req, Res>,
        merchant_connector_id_or_connector_name: String,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(common_utils::access_token::get_default_access_token_key(
            &router_data.merchant_id,
            merchant_connector_id_or_connector_name,
        ))
    }
}


// File: crates/hyperswitch_interfaces/src/secrets_interface/secret_handler.rs

//! Module containing trait for raw secret retrieval

use common_utils::errors::CustomResult;

use crate::secrets_interface::{
    secret_state::{RawSecret, SecretStateContainer, SecuredSecret},
    SecretManagementInterface, SecretsManagementError,
};

/// Trait defining the interface for retrieving a raw secret value, given a secured value
#[async_trait::async_trait]
pub trait SecretsHandler
where
    Self: Sized,
{
    /// Construct `Self` with raw secret value and transitions its type from `SecuredSecret` to `RawSecret`
    async fn convert_to_raw_secret(
        value: SecretStateContainer<Self, SecuredSecret>,
        kms_client: &dyn SecretManagementInterface,
    ) -> CustomResult<SecretStateContainer<Self, RawSecret>, SecretsManagementError>;
}


// File: crates/hyperswitch_interfaces/src/secrets_interface/secret_state.rs

//! Module to manage encrypted and decrypted states for a given type.

use std::marker::PhantomData;

use serde::{Deserialize, Deserializer};

/// Trait defining the states of a secret
pub trait SecretState {}

/// Decrypted state
#[derive(Debug, Clone, Deserialize, Default)]
pub struct RawSecret {}

/// Encrypted state
#[derive(Debug, Clone, Deserialize, Default)]
pub struct SecuredSecret {}

impl SecretState for RawSecret {}
impl SecretState for SecuredSecret {}

/// Struct for managing the encrypted and decrypted states of a given type
#[derive(Debug, Clone, Default)]
pub struct SecretStateContainer<T, S: SecretState> {
    inner: T,
    marker: PhantomData<S>,
}

impl<T: Clone, S: SecretState> SecretStateContainer<T, S> {
    /// Get the inner data while consuming self
    #[inline]
    pub fn into_inner(self) -> T {
        self.inner
    }

    /// Get the reference to inner value
    #[inline]
    pub fn get_inner(&self) -> &T {
        &self.inner
    }
}

impl<'de, T: Deserialize<'de>, S: SecretState> Deserialize<'de> for SecretStateContainer<T, S> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let val = Deserialize::deserialize(deserializer)?;
        Ok(Self {
            inner: val,
            marker: PhantomData,
        })
    }
}

impl<T> SecretStateContainer<T, SecuredSecret> {
    /// Transition the secret state from `SecuredSecret` to `RawSecret`
    pub fn transition_state(
        mut self,
        decryptor_fn: impl FnOnce(T) -> T,
    ) -> SecretStateContainer<T, RawSecret> {
        self.inner = decryptor_fn(self.inner);
        SecretStateContainer {
            inner: self.inner,
            marker: PhantomData,
        }
    }
}