// File: crates/router/src/core/unified_connector_service.rs
// Module: router
// Public functions: 15

use std::{str::FromStr, time::Instant};

use api_models::admin;
#[cfg(feature = "v2")]
use base64::Engine;
use common_enums::{connector_enums::Connector, AttemptStatus, GatewaySystem, PaymentMethodType};
#[cfg(feature = "v2")]
use common_utils::consts::BASE64_ENGINE;
use common_utils::{errors::CustomResult, ext_traits::ValueExt};
use diesel_models::types::FeatureMetadata;
use error_stack::ResultExt;
use external_services::grpc_client::{
    unified_connector_service::{ConnectorAuthMetadata, UnifiedConnectorServiceError},
    LineageIds,
};
use hyperswitch_connectors::utils::CardData;
#[cfg(feature = "v2")]
use hyperswitch_domain_models::merchant_connector_account::{
    ExternalVaultConnectorMetadata, MerchantConnectorAccountTypeDetails,
};
use hyperswitch_domain_models::{
    merchant_context::MerchantContext,
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_response_types::PaymentsResponseData,
};
use masking::{ExposeInterface, PeekInterface, Secret};
use router_env::{instrument, logger, tracing};
use unified_connector_service_cards::CardNumber;
use unified_connector_service_client::payments::{
    self as payments_grpc, payment_method::PaymentMethod, CardDetails, CardPaymentMethodType,
    PaymentServiceAuthorizeResponse, RewardPaymentMethodType,
};

#[cfg(feature = "v2")]
use crate::types::api::enums as api_enums;
use crate::{
    consts,
    core::{
        errors::{self, RouterResult},
        payments::{
            helpers::{
                is_ucs_enabled, should_execute_based_on_rollout, MerchantConnectorAccountType,
            },
            OperationSessionGetters, OperationSessionSetters,
        },
        utils::get_flow_name,
    },
    events::connector_api_logs::ConnectorEvent,
    routes::SessionState,
    types::transformers::ForeignTryFrom,
};

pub mod transformers;

// Re-export webhook transformer types for easier access
pub use transformers::WebhookTransformData;

/// Type alias for return type used by unified connector service response handlers
type UnifiedConnectorServiceResult = CustomResult<
    (
        Result<(PaymentsResponseData, AttemptStatus), ErrorResponse>,
        u16,
    ),
    UnifiedConnectorServiceError,
>;

/// Generic version of should_call_unified_connector_service that works with any type
/// implementing OperationSessionGetters trait
pub async fn should_call_unified_connector_service<F: Clone, T, D>(
    state: &SessionState,
    merchant_context: &MerchantContext,
    router_data: &RouterData<F, T, PaymentsResponseData>,
    payment_data: Option<&D>,
) -> RouterResult<bool>
where
    D: OperationSessionGetters<F>,
{
    // Check basic UCS availability first
    if state.grpc_client.unified_connector_service_client.is_none() {
        router_env::logger::debug!(
            "Unified Connector Service client is not available, skipping UCS decision"
        );
        return Ok(false);
    }

    let ucs_config_key = consts::UCS_ENABLED;
    if !is_ucs_enabled(state, ucs_config_key).await {
        router_env::logger::debug!(
            "Unified Connector Service is not enabled, skipping UCS decision"
        );
        return Ok(false);
    }

    // Apply stickiness logic if payment_data is available
    if let Some(payment_data) = payment_data {
        let previous_gateway_system = extract_gateway_system_from_payment_intent(payment_data);

        match previous_gateway_system {
            Some(GatewaySystem::UnifiedConnectorService) => {
                // Payment intent previously used UCS, maintain stickiness to UCS
                router_env::logger::info!(
                    "Payment gateway system decision: UCS (sticky) - payment intent previously used UCS"
                );
                return Ok(true);
            }
            Some(GatewaySystem::Direct) => {
                // Payment intent previously used Direct, maintain stickiness to Direct (return false for UCS)
                router_env::logger::info!(
                    "Payment gateway system decision: Direct (sticky) - payment intent previously used Direct"
                );
                return Ok(false);
            }
            None => {
                // No previous gateway system set, continue with normal gateway system logic
                router_env::logger::debug!(
                    "UCS stickiness: No previous gateway system set, applying normal gateway system logic"
                );
            }
        }
    }

    // Continue with normal UCS gateway system logic
    let merchant_id = merchant_context
        .get_merchant_account()
        .get_id()
        .get_string_repr();

    let connector_name = router_data.connector.clone();
    let connector_enum = Connector::from_str(&connector_name)
        .change_context(errors::ApiErrorResponse::IncorrectConnectorNameGiven)?;

    let payment_method = router_data.payment_method.to_string();
    let flow_name = get_flow_name::<F>()?;

    let is_ucs_only_connector = state
        .conf
        .grpc_client
        .unified_connector_service
        .as_ref()
        .is_some_and(|config| config.ucs_only_connectors.contains(&connector_enum));

    if is_ucs_only_connector {
        router_env::logger::info!(
            "Payment gateway system decision: UCS (forced) - merchant_id={}, connector={}, payment_method={}, flow={}",
            merchant_id, connector_name, payment_method, flow_name
        );
        return Ok(true);
    }

    let config_key = format!(
        "{}_{}_{}_{}_{}",
        consts::UCS_ROLLOUT_PERCENT_CONFIG_PREFIX,
        merchant_id,
        connector_name,
        payment_method,
        flow_name
    );

    let should_execute = should_execute_based_on_rollout(state, &config_key).await?;

    // Log gateway system decision
    if should_execute {
        router_env::logger::info!(
            "Payment gateway system decision: UCS - merchant_id={}, connector={}, payment_method={}, flow={}",
            merchant_id, connector_name, payment_method, flow_name
        );
    } else {
        router_env::logger::info!(
            "Payment gateway system decision: Direct - merchant_id={}, connector={}, payment_method={}, flow={}",
            merchant_id, connector_name, payment_method, flow_name
        );
    }

    Ok(should_execute)
}

/// Extracts the gateway system from the payment intent's feature metadata
/// Returns None if metadata is missing, corrupted, or doesn't contain gateway_system
fn extract_gateway_system_from_payment_intent<F: Clone, D>(
    payment_data: &D,
) -> Option<GatewaySystem>
where
    D: OperationSessionGetters<F>,
{
    #[cfg(feature = "v1")]
    {
        payment_data
            .get_payment_intent()
            .feature_metadata
            .as_ref()
            .and_then(|metadata| {
                // Try to parse the JSON value as FeatureMetadata
                // Log errors but don't fail the flow for corrupted metadata
                match serde_json::from_value::<FeatureMetadata>(metadata.clone()) {
                    Ok(feature_metadata) => feature_metadata.gateway_system,
                    Err(err) => {
                        router_env::logger::warn!(
                            "Failed to parse feature_metadata for gateway_system extraction: {}",
                            err
                        );
                        None
                    }
                }
            })
    }
    #[cfg(feature = "v2")]
    {
        None // V2 does not use feature metadata for gateway system tracking
    }
}

/// Updates the payment intent's feature metadata to track the gateway system being used
#[cfg(feature = "v1")]
pub fn update_gateway_system_in_feature_metadata<F: Clone, D>(
    payment_data: &mut D,
    gateway_system: GatewaySystem,
) -> RouterResult<()>
where
    D: OperationSessionGetters<F> + OperationSessionSetters<F>,
{
    let mut payment_intent = payment_data.get_payment_intent().clone();

    let existing_metadata = payment_intent.feature_metadata.as_ref();

    let mut feature_metadata = existing_metadata
        .and_then(|metadata| serde_json::from_value::<FeatureMetadata>(metadata.clone()).ok())
        .unwrap_or_default();

    feature_metadata.gateway_system = Some(gateway_system);

    let updated_metadata = serde_json::to_value(feature_metadata)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to serialize feature metadata")?;

    payment_intent.feature_metadata = Some(updated_metadata.clone());
    payment_data.set_payment_intent(payment_intent);

    Ok(())
}

pub async fn should_call_unified_connector_service_for_webhooks(
    state: &SessionState,
    merchant_context: &MerchantContext,
    connector_name: &str,
) -> RouterResult<bool> {
    if state.grpc_client.unified_connector_service_client.is_none() {
        logger::debug!(
            connector = connector_name.to_string(),
            "Unified Connector Service client is not available for webhooks"
        );
        return Ok(false);
    }

    let ucs_config_key = consts::UCS_ENABLED;

    if !is_ucs_enabled(state, ucs_config_key).await {
        return Ok(false);
    }

    let merchant_id = merchant_context
        .get_merchant_account()
        .get_id()
        .get_string_repr();

    let config_key = format!(
        "{}_{}_{}_Webhooks",
        consts::UCS_ROLLOUT_PERCENT_CONFIG_PREFIX,
        merchant_id,
        connector_name
    );

    let should_execute = should_execute_based_on_rollout(state, &config_key).await?;

    Ok(should_execute)
}

pub fn build_unified_connector_service_payment_method(
    payment_method_data: hyperswitch_domain_models::payment_method_data::PaymentMethodData,
    payment_method_type: PaymentMethodType,
) -> CustomResult<payments_grpc::PaymentMethod, UnifiedConnectorServiceError> {
    match payment_method_data {
        hyperswitch_domain_models::payment_method_data::PaymentMethodData::Card(card) => {
            let card_exp_month = card
                .get_card_expiry_month_2_digit()
                .attach_printable("Failed to extract 2-digit expiry month from card")
                .change_context(UnifiedConnectorServiceError::InvalidDataFormat {
                    field_name: "card_exp_month",
                })?
                .peek()
                .to_string();

            let card_network = card
                .card_network
                .clone()
                .map(payments_grpc::CardNetwork::foreign_try_from)
                .transpose()?;

            let card_details = CardDetails {
                card_number: Some(
                    CardNumber::from_str(&card.card_number.get_card_no()).change_context(
                        UnifiedConnectorServiceError::RequestEncodingFailedWithReason(
                            "Failed to parse card number".to_string(),
                        ),
                    )?,
                ),
                card_exp_month: Some(card_exp_month.into()),
                card_exp_year: Some(card.get_expiry_year_4_digit().expose().into()),
                card_cvc: Some(card.card_cvc.expose().into()),
                card_holder_name: card.card_holder_name.map(|name| name.expose().into()),
                card_issuer: card.card_issuer.clone(),
                card_network: card_network.map(|card_network| card_network.into()),
                card_type: card.card_type.clone(),
                bank_code: card.bank_code.clone(),
                nick_name: card.nick_name.map(|n| n.expose()),
                card_issuing_country_alpha2: card.card_issuing_country.clone(),
            };

            let grpc_card_type = match payment_method_type {
                PaymentMethodType::Credit => {
                    payments_grpc::card_payment_method_type::CardType::Credit(card_details)
                }
                PaymentMethodType::Debit => {
                    payments_grpc::card_payment_method_type::CardType::Debit(card_details)
                }
                _ => {
                    return Err(UnifiedConnectorServiceError::NotImplemented(format!(
                        "Unimplemented payment method subtype: {payment_method_type:?}"
                    ))
                    .into());
                }
            };

            Ok(payments_grpc::PaymentMethod {
                payment_method: Some(PaymentMethod::Card(CardPaymentMethodType {
                    card_type: Some(grpc_card_type),
                })),
            })
        }
        hyperswitch_domain_models::payment_method_data::PaymentMethodData::Upi(upi_data) => {
            let upi_type = match upi_data {
                hyperswitch_domain_models::payment_method_data::UpiData::UpiCollect(
                    upi_collect_data,
                ) => {
                    let upi_details = payments_grpc::UpiCollect {
                        vpa_id: upi_collect_data.vpa_id.map(|vpa| vpa.expose().into()),
                    };
                    PaymentMethod::UpiCollect(upi_details)
                }
                hyperswitch_domain_models::payment_method_data::UpiData::UpiIntent(_) => {
                    let upi_details = payments_grpc::UpiIntent { app_name: None };
                    PaymentMethod::UpiIntent(upi_details)
                }
            };

            Ok(payments_grpc::PaymentMethod {
                payment_method: Some(upi_type),
            })
        }
        hyperswitch_domain_models::payment_method_data::PaymentMethodData::Reward => {
            match payment_method_type {
                PaymentMethodType::ClassicReward => Ok(payments_grpc::PaymentMethod {
                    payment_method: Some(PaymentMethod::Reward(RewardPaymentMethodType {
                        reward_type: 1,
                    })),
                }),
                PaymentMethodType::Evoucher => Ok(payments_grpc::PaymentMethod {
                    payment_method: Some(PaymentMethod::Reward(RewardPaymentMethodType {
                        reward_type: 2,
                    })),
                }),
                _ => Err(UnifiedConnectorServiceError::NotImplemented(format!(
                    "Unimplemented payment method subtype: {payment_method_type:?}"
                ))
                .into()),
            }
        }
        _ => Err(UnifiedConnectorServiceError::NotImplemented(format!(
            "Unimplemented payment method: {payment_method_data:?}"
        ))
        .into()),
    }
}

pub fn build_unified_connector_service_payment_method_for_external_proxy(
    payment_method_data: hyperswitch_domain_models::payment_method_data::ExternalVaultPaymentMethodData,
    payment_method_type: PaymentMethodType,
) -> CustomResult<payments_grpc::PaymentMethod, UnifiedConnectorServiceError> {
    match payment_method_data {
        hyperswitch_domain_models::payment_method_data::ExternalVaultPaymentMethodData::Card(
            external_vault_card,
        ) => {
            let card_network = external_vault_card
                .card_network
                .clone()
                .map(payments_grpc::CardNetwork::foreign_try_from)
                .transpose()?;
            let card_details = CardDetails {
                card_number: Some(CardNumber::from_str(external_vault_card.card_number.peek()).change_context(
                    UnifiedConnectorServiceError::RequestEncodingFailedWithReason("Failed to parse card number".to_string())
                )?),
                card_exp_month: Some(external_vault_card.card_exp_month.expose().into()),
                card_exp_year: Some(external_vault_card.card_exp_year.expose().into()),
                card_cvc: Some(external_vault_card.card_cvc.expose().into()),
                card_holder_name: external_vault_card.card_holder_name.map(|name| name.expose().into()),
                card_issuer: external_vault_card.card_issuer.clone(),
                card_network: card_network.map(|card_network| card_network.into()),
                card_type: external_vault_card.card_type.clone(),
                bank_code: external_vault_card.bank_code.clone(),
                nick_name: external_vault_card.nick_name.map(|n| n.expose()),
                card_issuing_country_alpha2: external_vault_card.card_issuing_country.clone(),
            };
            let grpc_card_type = match payment_method_type {
                PaymentMethodType::Credit => {
                    payments_grpc::card_payment_method_type::CardType::CreditProxy(card_details)
                }
                PaymentMethodType::Debit => {
                    payments_grpc::card_payment_method_type::CardType::DebitProxy(card_details)
                }
                _ => {
                    return Err(UnifiedConnectorServiceError::NotImplemented(format!(
                        "Unimplemented payment method subtype: {payment_method_type:?}"
                    ))
                    .into());
                }
            };
            Ok(payments_grpc::PaymentMethod {
                payment_method: Some(PaymentMethod::Card(CardPaymentMethodType {
                    card_type: Some(grpc_card_type),
                })),
            })
        }
        hyperswitch_domain_models::payment_method_data::ExternalVaultPaymentMethodData::VaultToken(_) => {
            Err(UnifiedConnectorServiceError::NotImplemented(format!(
                        "Unimplemented payment method subtype: {payment_method_type:?}"
            ))
            .into())
        }
    }
}
pub fn build_unified_connector_service_auth_metadata(
    #[cfg(feature = "v1")] merchant_connector_account: MerchantConnectorAccountType,
    #[cfg(feature = "v2")] merchant_connector_account: MerchantConnectorAccountTypeDetails,
    merchant_context: &MerchantContext,
) -> CustomResult<ConnectorAuthMetadata, UnifiedConnectorServiceError> {
    #[cfg(feature = "v1")]
    let auth_type: ConnectorAuthType = merchant_connector_account
        .get_connector_account_details()
        .parse_value("ConnectorAuthType")
        .change_context(UnifiedConnectorServiceError::FailedToObtainAuthType)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    #[cfg(feature = "v2")]
    let auth_type: ConnectorAuthType = merchant_connector_account
        .get_connector_account_details()
        .change_context(UnifiedConnectorServiceError::FailedToObtainAuthType)
        .attach_printable("Failed to obtain ConnectorAuthType")?;

    let connector_name = {
        #[cfg(feature = "v1")]
        {
            merchant_connector_account
                .get_connector_name()
                .ok_or(UnifiedConnectorServiceError::MissingConnectorName)
                .attach_printable("Missing connector name")?
        }

        #[cfg(feature = "v2")]
        {
            merchant_connector_account
                .get_connector_name()
                .map(|connector| connector.to_string())
                .ok_or(UnifiedConnectorServiceError::MissingConnectorName)
                .attach_printable("Missing connector name")?
        }
    };

    let merchant_id = merchant_context
        .get_merchant_account()
        .get_id()
        .get_string_repr();

    match &auth_type {
        ConnectorAuthType::SignatureKey {
            api_key,
            key1,
            api_secret,
        } => Ok(ConnectorAuthMetadata {
            connector_name,
            auth_type: consts::UCS_AUTH_SIGNATURE_KEY.to_string(),
            api_key: Some(api_key.clone()),
            key1: Some(key1.clone()),
            api_secret: Some(api_secret.clone()),
            auth_key_map: None,
            merchant_id: Secret::new(merchant_id.to_string()),
        }),
        ConnectorAuthType::BodyKey { api_key, key1 } => Ok(ConnectorAuthMetadata {
            connector_name,
            auth_type: consts::UCS_AUTH_BODY_KEY.to_string(),
            api_key: Some(api_key.clone()),
            key1: Some(key1.clone()),
            api_secret: None,
            auth_key_map: None,
            merchant_id: Secret::new(merchant_id.to_string()),
        }),
        ConnectorAuthType::HeaderKey { api_key } => Ok(ConnectorAuthMetadata {
            connector_name,
            auth_type: consts::UCS_AUTH_HEADER_KEY.to_string(),
            api_key: Some(api_key.clone()),
            key1: None,
            api_secret: None,
            auth_key_map: None,
            merchant_id: Secret::new(merchant_id.to_string()),
        }),
        ConnectorAuthType::CurrencyAuthKey { auth_key_map } => Ok(ConnectorAuthMetadata {
            connector_name,
            auth_type: consts::UCS_AUTH_CURRENCY_AUTH_KEY.to_string(),
            api_key: None,
            key1: None,
            api_secret: None,
            auth_key_map: Some(auth_key_map.clone()),
            merchant_id: Secret::new(merchant_id.to_string()),
        }),
        _ => Err(UnifiedConnectorServiceError::FailedToObtainAuthType)
            .attach_printable("Unsupported ConnectorAuthType for header injection"),
    }
}

#[cfg(feature = "v2")]
pub fn build_unified_connector_service_external_vault_proxy_metadata(
    external_vault_merchant_connector_account: MerchantConnectorAccountTypeDetails,
) -> CustomResult<String, UnifiedConnectorServiceError> {
    let external_vault_metadata = external_vault_merchant_connector_account
        .get_metadata()
        .ok_or(UnifiedConnectorServiceError::ParsingFailed)
        .attach_printable("Failed to obtain ConnectorMetadata")?;

    let connector_name = external_vault_merchant_connector_account
        .get_connector_name()
        .map(|connector| connector.to_string())
        .ok_or(UnifiedConnectorServiceError::MissingConnectorName)
        .attach_printable("Missing connector name")?; // always get the connector name from this call

    let external_vault_connector = api_enums::VaultConnectors::from_str(&connector_name)
        .change_context(UnifiedConnectorServiceError::InvalidConnectorName)
        .attach_printable("Failed to parse Vault connector")?;

    let unified_service_vault_metdata = match external_vault_connector {
        api_enums::VaultConnectors::Vgs => {
            let vgs_metadata: ExternalVaultConnectorMetadata = external_vault_metadata
                .expose()
                .parse_value("ExternalVaultConnectorMetadata")
                .change_context(UnifiedConnectorServiceError::ParsingFailed)
                .attach_printable("Failed to parse Vgs connector metadata")?;

            Some(external_services::grpc_client::unified_connector_service::ExternalVaultProxyMetadata::VgsMetadata(
                external_services::grpc_client::unified_connector_service::VgsMetadata {
                    proxy_url: vgs_metadata.proxy_url,
                    certificate: vgs_metadata.certificate,
                }
            ))
        }
        api_enums::VaultConnectors::HyperswitchVault | api_enums::VaultConnectors::Tokenex => None,
    };

    match unified_service_vault_metdata {
        Some(metdata) => {
            let external_vault_metadata_bytes = serde_json::to_vec(&metdata)
                .change_context(UnifiedConnectorServiceError::ParsingFailed)
                .attach_printable("Failed to convert External vault metadata to bytes")?;

            Ok(BASE64_ENGINE.encode(&external_vault_metadata_bytes))
        }
        None => Err(UnifiedConnectorServiceError::NotImplemented(
            "External vault proxy metadata is not supported for {connector_name}".to_string(),
        )
        .into()),
    }
}

pub fn handle_unified_connector_service_response_for_payment_authorize(
    response: PaymentServiceAuthorizeResponse,
) -> UnifiedConnectorServiceResult {
    let status_code = transformers::convert_connector_service_status_code(response.status_code)?;

    let router_data_response =
        Result::<(PaymentsResponseData, AttemptStatus), ErrorResponse>::foreign_try_from(response)?;

    Ok((router_data_response, status_code))
}

pub fn handle_unified_connector_service_response_for_payment_get(
    response: payments_grpc::PaymentServiceGetResponse,
) -> UnifiedConnectorServiceResult {
    let status_code = transformers::convert_connector_service_status_code(response.status_code)?;

    let router_data_response =
        Result::<(PaymentsResponseData, AttemptStatus), ErrorResponse>::foreign_try_from(response)?;

    Ok((router_data_response, status_code))
}

pub fn handle_unified_connector_service_response_for_payment_register(
    response: payments_grpc::PaymentServiceRegisterResponse,
) -> UnifiedConnectorServiceResult {
    let status_code = transformers::convert_connector_service_status_code(response.status_code)?;

    let router_data_response =
        Result::<(PaymentsResponseData, AttemptStatus), ErrorResponse>::foreign_try_from(response)?;

    Ok((router_data_response, status_code))
}

pub fn handle_unified_connector_service_response_for_payment_repeat(
    response: payments_grpc::PaymentServiceRepeatEverythingResponse,
) -> UnifiedConnectorServiceResult {
    let status_code = transformers::convert_connector_service_status_code(response.status_code)?;

    let router_data_response =
        Result::<(PaymentsResponseData, AttemptStatus), ErrorResponse>::foreign_try_from(response)?;

    Ok((router_data_response, status_code))
}

pub fn build_webhook_secrets_from_merchant_connector_account(
    #[cfg(feature = "v1")] merchant_connector_account: &MerchantConnectorAccountType,
    #[cfg(feature = "v2")] merchant_connector_account: &MerchantConnectorAccountTypeDetails,
) -> CustomResult<Option<payments_grpc::WebhookSecrets>, UnifiedConnectorServiceError> {
    // Extract webhook credentials from merchant connector account
    // This depends on how webhook secrets are stored in the merchant connector account

    #[cfg(feature = "v1")]
    let webhook_details = merchant_connector_account
        .get_webhook_details()
        .map_err(|_| UnifiedConnectorServiceError::FailedToObtainAuthType)?;

    #[cfg(feature = "v2")]
    let webhook_details = match merchant_connector_account {
        MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(mca) => {
            mca.connector_webhook_details.as_ref()
        }
        MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None,
    };

    match webhook_details {
        Some(details) => {
            // Parse the webhook details JSON to extract secrets
            let webhook_details: admin::MerchantConnectorWebhookDetails = details
                .clone()
                .parse_value("MerchantConnectorWebhookDetails")
                .change_context(UnifiedConnectorServiceError::FailedToObtainAuthType)
                .attach_printable("Failed to parse MerchantConnectorWebhookDetails")?;

            // Build gRPC WebhookSecrets from parsed details
            Ok(Some(payments_grpc::WebhookSecrets {
                secret: webhook_details.merchant_secret.expose().to_string(),
                additional_secret: webhook_details
                    .additional_secret
                    .map(|secret| secret.expose().to_string()),
            }))
        }
        None => Ok(None),
    }
}

/// High-level abstraction for calling UCS webhook transformation
/// This provides a clean interface similar to payment flow UCS calls
pub async fn call_unified_connector_service_for_webhook(
    state: &SessionState,
    merchant_context: &MerchantContext,
    connector_name: &str,
    body: &actix_web::web::Bytes,
    request_details: &hyperswitch_interfaces::webhooks::IncomingWebhookRequestDetails<'_>,
    merchant_connector_account: Option<
        &hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccount,
    >,
) -> RouterResult<(
    api_models::webhooks::IncomingWebhookEvent,
    bool,
    WebhookTransformData,
)> {
    let ucs_client = state
        .grpc_client
        .unified_connector_service_client
        .as_ref()
        .ok_or_else(|| {
            error_stack::report!(errors::ApiErrorResponse::WebhookProcessingFailure)
                .attach_printable("UCS client is not available for webhook processing")
        })?;

    // Build webhook secrets from merchant connector account
    let webhook_secrets = merchant_connector_account.and_then(|mca| {
        #[cfg(feature = "v1")]
        let mca_type = MerchantConnectorAccountType::DbVal(Box::new(mca.clone()));
        #[cfg(feature = "v2")]
        let mca_type =
            MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(mca.clone()));

        build_webhook_secrets_from_merchant_connector_account(&mca_type)
            .map_err(|e| {
                logger::warn!(
                    build_error=?e,
                    connector_name=connector_name,
                    "Failed to build webhook secrets from merchant connector account in call_unified_connector_service_for_webhook"
                );
                e
            })
            .ok()
            .flatten()
    });

    // Build UCS transform request using new webhook transformers
    let transform_request = transformers::build_webhook_transform_request(
        body,
        request_details,
        webhook_secrets,
        merchant_context
            .get_merchant_account()
            .get_id()
            .get_string_repr(),
        connector_name,
    )?;

    // Build connector auth metadata
    let connector_auth_metadata = merchant_connector_account
        .map(|mca| {
            #[cfg(feature = "v1")]
            let mca_type = MerchantConnectorAccountType::DbVal(Box::new(mca.clone()));
            #[cfg(feature = "v2")]
            let mca_type = MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(
                mca.clone(),
            ));

            build_unified_connector_service_auth_metadata(mca_type, merchant_context)
        })
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to build UCS auth metadata")?
        .ok_or_else(|| {
            error_stack::report!(errors::ApiErrorResponse::InternalServerError).attach_printable(
                "Missing merchant connector account for UCS webhook transformation",
            )
        })?;
    let profile_id = merchant_connector_account
        .as_ref()
        .map(|mca| mca.profile_id.clone())
        .unwrap_or(consts::PROFILE_ID_UNAVAILABLE.clone());
    // Build gRPC headers
    let grpc_headers = state
        .get_grpc_headers_ucs()
        .lineage_ids(LineageIds::new(
            merchant_context.get_merchant_account().get_id().clone(),
            profile_id,
        ))
        .external_vault_proxy_metadata(None)
        .merchant_reference_id(None)
        .build();

    // Make UCS call - client availability already verified
    match ucs_client
        .transform_incoming_webhook(transform_request, connector_auth_metadata, grpc_headers)
        .await
    {
        Ok(response) => {
            let transform_response = response.into_inner();
            let transform_data = transformers::transform_ucs_webhook_response(transform_response)?;

            // UCS handles everything internally - event type, source verification, decoding
            Ok((
                transform_data.event_type,
                transform_data.source_verified,
                transform_data,
            ))
        }
        Err(err) => {
            // When UCS is configured, we don't fall back to direct connector processing
            Err(errors::ApiErrorResponse::WebhookProcessingFailure)
                .attach_printable(format!("UCS webhook processing failed: {err}"))
        }
    }
}

/// Extract webhook content from UCS response for further processing
/// This provides a helper function to extract specific data from UCS responses
pub fn extract_webhook_content_from_ucs_response(
    transform_data: &WebhookTransformData,
) -> Option<&unified_connector_service_client::payments::WebhookResponseContent> {
    transform_data.webhook_content.as_ref()
}

/// UCS Event Logging Wrapper Function
/// This function wraps UCS calls with comprehensive event logging.
/// It logs the actual gRPC request/response data, timing, and error information.
#[instrument(skip_all, fields(connector_name, flow_type, payment_id))]
pub async fn ucs_logging_wrapper<T, F, Fut, Req, Resp, GrpcReq, GrpcResp>(
    router_data: RouterData<T, Req, Resp>,
    state: &SessionState,
    grpc_request: GrpcReq,
    grpc_header_builder: external_services::grpc_client::GrpcHeadersUcsBuilderFinal,
    handler: F,
) -> RouterResult<RouterData<T, Req, Resp>>
where
    T: std::fmt::Debug + Clone + Send + 'static,
    Req: std::fmt::Debug + Clone + Send + Sync + 'static,
    Resp: std::fmt::Debug + Clone + Send + Sync + 'static,
    GrpcReq: serde::Serialize,
    GrpcResp: serde::Serialize,
    F: FnOnce(
            RouterData<T, Req, Resp>,
            GrpcReq,
            external_services::grpc_client::GrpcHeadersUcs,
        ) -> Fut
        + Send,
    Fut: std::future::Future<Output = RouterResult<(RouterData<T, Req, Resp>, GrpcResp)>> + Send,
{
    tracing::Span::current().record("connector_name", &router_data.connector);
    tracing::Span::current().record("flow_type", std::any::type_name::<T>());
    tracing::Span::current().record("payment_id", &router_data.payment_id);

    // Capture request data for logging
    let connector_name = router_data.connector.clone();
    let payment_id = router_data.payment_id.clone();
    let merchant_id = router_data.merchant_id.clone();
    let refund_id = router_data.refund_id.clone();
    let dispute_id = router_data.dispute_id.clone();
    let grpc_header = grpc_header_builder.build();
    // Log the actual gRPC request with masking
    let grpc_request_body = masking::masked_serialize(&grpc_request)
        .unwrap_or_else(|_| serde_json::json!({"error": "failed_to_serialize_grpc_request"}));

    // Update connector call count metrics for UCS operations
    crate::routes::metrics::CONNECTOR_CALL_COUNT.add(
        1,
        router_env::metric_attributes!(
            ("connector", connector_name.clone()),
            (
                "flow",
                std::any::type_name::<T>()
                    .split("::")
                    .last()
                    .unwrap_or_default()
            ),
        ),
    );

    // Execute UCS function and measure timing
    let start_time = Instant::now();
    let result = handler(router_data, grpc_request, grpc_header).await;
    let external_latency = start_time.elapsed().as_millis();

    // Create and emit connector event after UCS call
    let (status_code, response_body, router_result) = match result {
        Ok((updated_router_data, grpc_response)) => {
            let status = updated_router_data
                .connector_http_status_code
                .unwrap_or(200);

            // Log the actual gRPC response
            let grpc_response_body = serde_json::to_value(&grpc_response).unwrap_or_else(
                |_| serde_json::json!({"error": "failed_to_serialize_grpc_response"}),
            );

            (status, Some(grpc_response_body), Ok(updated_router_data))
        }
        Err(error) => {
            // Update error metrics for UCS calls
            crate::routes::metrics::CONNECTOR_ERROR_RESPONSE_COUNT.add(
                1,
                router_env::metric_attributes!(("connector", connector_name.clone(),)),
            );

            let error_body = serde_json::json!({
                "error": error.to_string(),
                "error_type": "ucs_call_failed"
            });
            (500, Some(error_body), Err(error))
        }
    };

    let mut connector_event = ConnectorEvent::new(
        state.tenant.tenant_id.clone(),
        connector_name,
        std::any::type_name::<T>(),
        grpc_request_body,
        "grpc://unified-connector-service".to_string(),
        common_utils::request::Method::Post,
        payment_id,
        merchant_id,
        state.request_id.as_ref(),
        external_latency,
        refund_id,
        dispute_id,
        status_code,
    );

    // Set response body based on status code
    if let Some(body) = response_body {
        match status_code {
            400..=599 => {
                connector_event.set_error_response_body(&body);
            }
            _ => {
                connector_event.set_response_body(&body);
            }
        }
    }

    // Emit event
    state.event_handler.log_event(&connector_event);

    router_result
}


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

pub struct AuthenticationDetails


// Implementation: impl CaptureNew
// File: crates/diesel_models/src/query/capture.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl CaptureNew


// Struct: MomoRedirection
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct MomoRedirection


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

use hyperswitch_domain_models::payment_method_data::{Card, PaymentMethodData};
use masking::Secret;
use router::types::{self, api, storage::enums};
use test_utils::connector_auth;

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

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

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

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

static CONNECTOR: HyperwalletTest = HyperwalletTest {};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Connector dependent test cases goes here

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


// Implementation: impl EncryptedJsonType
// File: crates/hyperswitch_domain_models/src/type_encryption.rs
// Module: hyperswitch_domain_models
// Methods: 2 total (2 public)

impl EncryptedJsonType


// Struct: CreditorBank
// File: crates/hyperswitch_connectors/src/connectors/nordea/requests.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CreditorBank


// Function: build_webhook_transform_request
// File: crates/router/src/core/unified_connector_service/transformers.rs
// Module: router

// Documentation: Build UCS webhook transform request from webhook components

pub fn build_webhook_transform_request(
    _webhook_body: &[u8],
    request_details: &hyperswitch_interfaces::webhooks::IncomingWebhookRequestDetails<'_>,
    webhook_secrets: Option<payments_grpc::WebhookSecrets>,
    merchant_id: &str,
    connector_id: &str,
) -> Result<PaymentServiceTransformRequest, error_stack::Report<errors::ApiErrorResponse>>


// Function: convert_connector_service_status_code
// File: crates/router/src/core/unified_connector_service/transformers.rs
// Module: router

pub fn convert_connector_service_status_code(
    status_code: u32,
) -> Result<u16, error_stack::Report<UnifiedConnectorServiceError>>


// Function: organization_create
// File: crates/openapi/src/routes/organization.rs
// Module: openapi

pub fn organization_create()


// Implementation: impl api::PaymentAuthorize for for Nordea
// File: crates/hyperswitch_connectors/src/connectors/nordea.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Nordea


// Function: routing_update_default_config
// File: crates/router/src/routes/routing.rs
// Module: router

pub fn routing_update_default_config(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::ProfileId>,
    json_payload: web::Json<Vec<routing_types::RoutableConnectorChoice>>,
) -> impl Responder


// Implementation: impl StrongEq for for String
// File: crates/masking/src/strong_secret.rs
// Module: masking
// Methods: 1 total (0 public)

impl StrongEq for for String


// Function: get_entity_id_and_type
// File: crates/diesel_models/src/user_role.rs
// Module: diesel_models

pub fn get_entity_id_and_type(&self) -> Option<(String, EntityType)>


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

impl api::MandateSetup for for Mpgs


// Struct: RequiredFieldsOverrideRequest
// File: crates/api_models/src/payouts.rs
// Module: api_models
// Implementations: 0

pub struct RequiredFieldsOverrideRequest


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

pub struct DirectChargeRefund


// Function: refund_list
// File: crates/router/src/core/refunds.rs
// Module: router

pub fn refund_list(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
    req: api_models::refunds::RefundListRequest,
) -> RouterResponse<api_models::refunds::RefundListResponse>


// Implementation: impl EventInfo for for AuditEvent
// File: crates/router/src/events/audit_events.rs
// Module: router
// Methods: 2 total (0 public)

impl EventInfo for for AuditEvent


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

impl Checkbook


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

impl Cashtocode


// Struct: BankAccountDetailsBacs
// File: crates/pm_auth/src/types.rs
// Module: pm_auth
// Implementations: 0

pub struct BankAccountDetailsBacs


// Struct: NewUserRole
// File: crates/router/src/types/domain/user.rs
// Module: router
// Implementations: 2

pub struct NewUserRole<E: Clone>


// File: crates/hyperswitch_interfaces/src/conversion_impls.rs
// Module: hyperswitch_interfaces

use common_utils::{errors::CustomResult, id_type};
use error_stack::ResultExt;
#[cfg(feature = "frm")]
use hyperswitch_domain_models::router_data_v2::flow_common_types::FrmFlowData;
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::router_data_v2::flow_common_types::PayoutFlowData;
use hyperswitch_domain_models::{
    payment_address::PaymentAddress,
    router_data::{self, RouterData},
    router_data_v2::{
        flow_common_types::{
            AccessTokenFlowData, AuthenticationTokenFlowData, BillingConnectorInvoiceSyncFlowData,
            BillingConnectorPaymentsSyncFlowData, DisputesFlowData, ExternalAuthenticationFlowData,
            ExternalVaultProxyFlowData, FilesFlowData, GetSubscriptionPlanPricesData,
            GetSubscriptionPlansData, GiftCardBalanceCheckFlowData, InvoiceRecordBackData,
            MandateRevokeFlowData, PaymentFlowData, RefundFlowData, SubscriptionCreateData,
            SubscriptionCustomerData, UasFlowData, VaultConnectorFlowData, WebhookSourceVerifyData,
        },
        RouterDataV2,
    },
};

use crate::{connector_integration_interface::RouterDataConversion, errors::ConnectorError};

fn get_irrelevant_id_string(id_name: &str, flow_name: &str) -> String {
    format!("irrelevant {id_name} in {flow_name} flow")
}
fn get_default_router_data<F, Req, Resp>(
    tenant_id: id_type::TenantId,
    flow_name: &str,
    request: Req,
    response: Result<Resp, router_data::ErrorResponse>,
) -> RouterData<F, Req, Resp> {
    RouterData {
        tenant_id,
        flow: std::marker::PhantomData,
        merchant_id: id_type::MerchantId::get_irrelevant_merchant_id(),
        customer_id: None,
        connector_customer: None,
        connector: get_irrelevant_id_string("connector", flow_name),
        payment_id: id_type::PaymentId::get_irrelevant_id(flow_name)
            .get_string_repr()
            .to_owned(),
        attempt_id: get_irrelevant_id_string("attempt_id", flow_name),
        status: common_enums::AttemptStatus::default(),
        payment_method: common_enums::PaymentMethod::default(),
        connector_auth_type: router_data::ConnectorAuthType::default(),
        description: None,
        address: PaymentAddress::default(),
        auth_type: common_enums::AuthenticationType::default(),
        connector_meta_data: None,
        connector_wallets_details: None,
        amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_token: None,
        recurring_mandate_payment_data: None,
        preprocessing_id: None,
        payment_method_balance: None,
        connector_api_version: None,
        request,
        response,
        connector_request_reference_id: get_irrelevant_id_string(
            "connector_request_reference_id",
            flow_name,
        ),
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        test_mode: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        dispute_id: None,
        refund_id: None,
        connector_response: None,
        payment_method_status: None,
        minor_amount_captured: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload: None,
        connector_mandate_request_reference_id: None,
        authentication_id: None,
        psd2_sca_exemption_type: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: None,
        l2_l3_data: None,
        minor_amount_capturable: None,
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp>
    for AuthenticationTokenFlowData
{
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {};
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {} = new_router_data.resource_common_data;
        let request = new_router_data.request.clone();
        let response = new_router_data.response.clone();
        let router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "authentication token",
            request,
            response,
        );
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for AccessTokenFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {};
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {} = new_router_data.resource_common_data;
        let request = new_router_data.request.clone();
        let response = new_router_data.response.clone();
        let router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "access token",
            request,
            response,
        );
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp>
    for GiftCardBalanceCheckFlowData
{
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self;
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let request = new_router_data.request.clone();
        let response = new_router_data.response.clone();
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "gift card balance check",
            request,
            response,
        );
        router_data.connector_auth_type = new_router_data.connector_auth_type;
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for PaymentFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            customer_id: old_router_data.customer_id.clone(),
            connector_customer: old_router_data.connector_customer.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            status: old_router_data.status,
            payment_method: old_router_data.payment_method,
            description: old_router_data.description.clone(),
            address: old_router_data.address.clone(),
            auth_type: old_router_data.auth_type,
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            amount_captured: old_router_data.amount_captured,
            minor_amount_captured: old_router_data.minor_amount_captured,
            access_token: old_router_data.access_token.clone(),
            session_token: old_router_data.session_token.clone(),
            reference_id: old_router_data.reference_id.clone(),
            payment_method_token: old_router_data.payment_method_token.clone(),
            recurring_mandate_payment_data: old_router_data.recurring_mandate_payment_data.clone(),
            preprocessing_id: old_router_data.preprocessing_id.clone(),
            payment_method_balance: old_router_data.payment_method_balance.clone(),
            connector_api_version: old_router_data.connector_api_version.clone(),
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            test_mode: old_router_data.test_mode,
            connector_http_status_code: old_router_data.connector_http_status_code,
            external_latency: old_router_data.external_latency,
            apple_pay_flow: old_router_data.apple_pay_flow.clone(),
            connector_response: old_router_data.connector_response.clone(),
            payment_method_status: old_router_data.payment_method_status,
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            customer_id,
            connector_customer,
            payment_id,
            attempt_id,
            status,
            payment_method,
            description,
            address,
            auth_type,
            connector_meta_data,
            amount_captured,
            minor_amount_captured,
            access_token,
            session_token,
            reference_id,
            payment_method_token,
            recurring_mandate_payment_data,
            preprocessing_id,
            payment_method_balance,
            connector_api_version,
            connector_request_reference_id,
            test_mode,
            connector_http_status_code,
            external_latency,
            apple_pay_flow,
            connector_response,
            payment_method_status,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "payment",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.customer_id = customer_id;
        router_data.connector_customer = connector_customer;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.status = status;
        router_data.payment_method = payment_method;
        router_data.description = description;
        router_data.address = address;
        router_data.auth_type = auth_type;
        router_data.connector_meta_data = connector_meta_data;
        router_data.amount_captured = amount_captured;
        router_data.minor_amount_captured = minor_amount_captured;
        router_data.access_token = access_token;
        router_data.session_token = session_token;
        router_data.reference_id = reference_id;
        router_data.payment_method_token = payment_method_token;
        router_data.recurring_mandate_payment_data = recurring_mandate_payment_data;
        router_data.preprocessing_id = preprocessing_id;
        router_data.payment_method_balance = payment_method_balance;
        router_data.connector_api_version = connector_api_version;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.test_mode = test_mode;
        router_data.connector_http_status_code = connector_http_status_code;
        router_data.external_latency = external_latency;
        router_data.apple_pay_flow = apple_pay_flow;
        router_data.connector_response = connector_response;
        router_data.payment_method_status = payment_method_status;
        router_data.connector_auth_type = new_router_data.connector_auth_type;
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for RefundFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            customer_id: old_router_data.customer_id.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            status: old_router_data.status,
            payment_method: old_router_data.payment_method,
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            amount_captured: old_router_data.amount_captured,
            minor_amount_captured: old_router_data.minor_amount_captured,
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            refund_id: old_router_data.refund_id.clone().ok_or(
                ConnectorError::MissingRequiredField {
                    field_name: "refund_id",
                },
            )?,
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            customer_id,
            payment_id,
            attempt_id,
            status,
            payment_method,
            connector_meta_data,
            amount_captured,
            minor_amount_captured,
            connector_request_reference_id,
            refund_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "refund",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.customer_id = customer_id;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.status = status;
        router_data.payment_method = payment_method;
        router_data.connector_meta_data = connector_meta_data;
        router_data.amount_captured = amount_captured;
        router_data.minor_amount_captured = minor_amount_captured;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.refund_id = Some(refund_id);
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for DisputesFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            payment_method: old_router_data.payment_method,
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            amount_captured: old_router_data.amount_captured,
            minor_amount_captured: old_router_data.minor_amount_captured,
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            dispute_id: old_router_data.dispute_id.clone().ok_or(
                ConnectorError::MissingRequiredField {
                    field_name: "dispute_id",
                },
            )?,
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            payment_id,
            attempt_id,
            payment_method,
            connector_meta_data,
            amount_captured,
            minor_amount_captured,
            connector_request_reference_id,
            dispute_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "Disputes",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.payment_method = payment_method;
        router_data.connector_meta_data = connector_meta_data;
        router_data.amount_captured = amount_captured;
        router_data.minor_amount_captured = minor_amount_captured;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.dispute_id = Some(dispute_id);
        Ok(router_data)
    }
}

#[cfg(feature = "frm")]
impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for FrmFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            payment_method: old_router_data.payment_method,
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            auth_type: old_router_data.auth_type,
            connector_wallets_details: old_router_data.connector_wallets_details.clone(),
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            amount_captured: old_router_data.amount_captured,
            minor_amount_captured: old_router_data.minor_amount_captured,
        };
        Ok(RouterDataV2 {
            tenant_id: old_router_data.tenant_id.clone(),
            flow: std::marker::PhantomData,
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            payment_id,
            attempt_id,
            payment_method,
            connector_request_reference_id,
            auth_type,
            connector_wallets_details,
            connector_meta_data,
            amount_captured,
            minor_amount_captured,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "frm",
            new_router_data.request,
            new_router_data.response,
        );

        router_data.merchant_id = merchant_id;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.payment_method = payment_method;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.auth_type = auth_type;
        router_data.connector_wallets_details = connector_wallets_details;
        router_data.connector_meta_data = connector_meta_data;
        router_data.amount_captured = amount_captured;
        router_data.minor_amount_captured = minor_amount_captured;

        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for FilesFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            payment_id: old_router_data.payment_id.clone(),
            attempt_id: old_router_data.attempt_id.clone(),
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            payment_id,
            attempt_id,
            connector_meta_data,
            connector_request_reference_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "files",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.payment_id = payment_id;
        router_data.attempt_id = attempt_id;
        router_data.connector_meta_data = connector_meta_data;
        router_data.connector_request_reference_id = connector_request_reference_id;

        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for WebhookSourceVerifyData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
        };
        Ok(RouterDataV2 {
            tenant_id: old_router_data.tenant_id.clone(),
            flow: std::marker::PhantomData,
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self { merchant_id } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "webhook source verify",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for MandateRevokeFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            customer_id: old_router_data.customer_id.clone().ok_or(
                ConnectorError::MissingRequiredField {
                    field_name: "customer_id",
                },
            )?,
            payment_id: Some(old_router_data.payment_id.clone()),
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            customer_id,
            payment_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "mandate revoke",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.customer_id = Some(customer_id);
        router_data.payment_id = payment_id
            .unwrap_or_else(|| {
                id_type::PaymentId::get_irrelevant_id("mandate revoke")
                    .get_string_repr()
                    .to_owned()
            })
            .to_owned();
        Ok(router_data)
    }
}

#[cfg(feature = "payouts")]
impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for PayoutFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            customer_id: old_router_data.customer_id.clone(),
            connector_customer: old_router_data.connector_customer.clone(),
            address: old_router_data.address.clone(),
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            connector_wallets_details: old_router_data.connector_wallets_details.clone(),
            connector_request_reference_id: old_router_data.connector_request_reference_id.clone(),
            payout_method_data: old_router_data.payout_method_data.clone(),
            quote_id: old_router_data.quote_id.clone(),
        };
        Ok(RouterDataV2 {
            tenant_id: old_router_data.tenant_id.clone(),
            flow: std::marker::PhantomData,
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            customer_id,
            connector_customer,
            address,
            connector_meta_data,
            connector_wallets_details,
            connector_request_reference_id,
            payout_method_data,
            quote_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "payout",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.customer_id = customer_id;
        router_data.connector_customer = connector_customer;
        router_data.address = address;
        router_data.connector_meta_data = connector_meta_data;
        router_data.connector_wallets_details = connector_wallets_details;
        router_data.connector_request_reference_id = connector_request_reference_id;
        router_data.payout_method_data = payout_method_data;
        router_data.quote_id = quote_id;
        Ok(router_data)
    }
}
impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp>
    for ExternalAuthenticationFlowData
{
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
            connector_meta_data: old_router_data.connector_meta_data.clone(),
            address: old_router_data.address.clone(),
        };
        Ok(RouterDataV2 {
            tenant_id: old_router_data.tenant_id.clone(),
            flow: std::marker::PhantomData,
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            merchant_id,
            connector_meta_data,
            address,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "external authentication",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.merchant_id = merchant_id;
        router_data.connector_meta_data = connector_meta_data;
        router_data.address = address;
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for InvoiceRecordBackData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {};
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "recovery_record_back",
            new_router_data.request,
            new_router_data.response,
        );
        Ok(RouterData {
            connector_auth_type: new_router_data.connector_auth_type.clone(),
            ..router_data
        })
    }
}

macro_rules! default_router_data_conversion {
    ($flow_name:ty) => {
        impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for $flow_name {
            fn from_old_router_data(
                old_router_data: &RouterData<T, Req, Resp>,
            ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
            where
                Self: Sized,
            {
                let resource_common_data = Self {
                    connector_meta_data: old_router_data.connector_meta_data.clone(),
                };
                Ok(RouterDataV2 {
                    flow: std::marker::PhantomData,
                    tenant_id: old_router_data.tenant_id.clone(),
                    resource_common_data,
                    connector_auth_type: old_router_data.connector_auth_type.clone(),
                    request: old_router_data.request.clone(),
                    response: old_router_data.response.clone(),
                })
            }

            fn to_old_router_data(
                new_router_data: RouterDataV2<T, Self, Req, Resp>,
            ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
            where
                Self: Sized,
            {
                let Self {
                    connector_meta_data,
                } = new_router_data.resource_common_data;
                let mut router_data = get_default_router_data(
                    new_router_data.tenant_id.clone(),
                    stringify!($flow_name),
                    new_router_data.request,
                    new_router_data.response,
                );
                router_data.connector_meta_data = connector_meta_data;
                router_data.connector_auth_type = new_router_data.connector_auth_type;

                Ok(router_data)
            }
        }
    };
}
default_router_data_conversion!(GetSubscriptionPlansData);
default_router_data_conversion!(GetSubscriptionPlanPricesData);
default_router_data_conversion!(SubscriptionCreateData);
default_router_data_conversion!(SubscriptionCustomerData);

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for UasFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            authenticate_by: old_router_data.connector.clone(),
            source_authentication_id: old_router_data
                .authentication_id
                .clone()
                .ok_or(ConnectorError::MissingRequiredField {
                    field_name: "source_authentication_id",
                })
                .attach_printable("missing authentication id for uas")?,
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let Self {
            authenticate_by,
            source_authentication_id,
        } = new_router_data.resource_common_data;
        let mut router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "uas",
            new_router_data.request,
            new_router_data.response,
        );
        router_data.connector = authenticate_by;
        router_data.authentication_id = Some(source_authentication_id);
        Ok(router_data)
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp>
    for BillingConnectorPaymentsSyncFlowData
{
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {};
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "BillingConnectorPaymentsSync",
            new_router_data.request,
            new_router_data.response,
        );
        Ok(RouterData {
            connector_auth_type: new_router_data.connector_auth_type.clone(),
            ..router_data
        })
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp>
    for BillingConnectorInvoiceSyncFlowData
{
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {};
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }

    fn to_old_router_data(
        new_router_data: RouterDataV2<T, Self, Req, Resp>,
    ) -> CustomResult<RouterData<T, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let router_data = get_default_router_data(
            new_router_data.tenant_id.clone(),
            "BillingConnectorInvoiceSync",
            new_router_data.request,
            new_router_data.response,
        );
        Ok(RouterData {
            connector_auth_type: new_router_data.connector_auth_type.clone(),
            ..router_data
        })
    }
}

impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for VaultConnectorFlowData {
    fn from_old_router_data(
        old_router_data: &RouterData<T, Req, Resp>,
    ) -> CustomResult<RouterDataV2<T, Self, Req, Resp>, ConnectorError>
    where
        Self: Sized,
    {
        let resource_common_data = Self {
            merchant_id: old_router_data.merchant_id.clone(),
        };
        Ok(RouterDataV2 {
            flow: std::marker::PhantomData,
            tenant_id: old_router_data.tenant_id.clone(),
            resource_common_data,
            connector_auth_type: old_router_data.connector_auth_type.clone(),
            request: old_router_data.request.clone(),
            response: old_router_data.response.clone(),
        })
    }


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

pub struct ReversalInformation


// File: crates/api_models/src/events/chat.rs
// Module: api_models

use common_utils::events::{ApiEventMetric, ApiEventsType};

use crate::chat::{ChatListRequest, ChatListResponse, ChatRequest, ChatResponse};

common_utils::impl_api_event_type!(
    Chat,
    (ChatRequest, ChatResponse, ChatListRequest, ChatListResponse)
);


// Module Structure
// File: crates/drainer/src/settings.rs
// Module: drainer

// Public exports:
pub use router_env::config::{Log, LogConsole, LogFile, LogTelemetry};


// Function: check_for_custom_headers
// File: crates/test_utils/src/newman_runner.rs
// Module: test_utils

pub fn check_for_custom_headers(headers: Option<Vec<String>>, path: &str) -> Option<String>


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

pub struct PaymentsPostProcessingData


// Struct: EncryptionCreateRequest
// File: crates/common_utils/src/types/keymanager.rs
// Module: common_utils
// Implementations: 0

pub struct EncryptionCreateRequest