File: crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs
 Public structs: 20

use common_enums::enums;
use common_utils::types::StringMajorUnit;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        RefundsRouterData,
    },
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::CardData as _,
};

//TODO: Fill the struct with respective fields
pub struct FiservemeaRouterData<T> {
    pub amount: StringMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}

impl<T> From<(StringMajorUnit, T)> for FiservemeaRouterData<T> {
    fn from((amount, item): (StringMajorUnit, T)) -> Self {
        //Todo :  use utils to convert the amount to the type of amount that a connector accepts
        Self {
            amount,
            router_data: item,
        }
    }
}

#[derive(Debug, Serialize)]
pub struct FiservemeaTransactionAmount {
    total: StringMajorUnit,
    currency: common_enums::Currency,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaOrder {
    order_id: String,
}

#[derive(Debug, Serialize)]
pub enum FiservemeaRequestType {
    PaymentCardSaleTransaction,
    PaymentCardPreAuthTransaction,
    PostAuthTransaction,
    VoidPreAuthTransactions,
    ReturnTransaction,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaExpiryDate {
    month: Secret<String>,
    year: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaPaymentCard {
    number: cards::CardNumber,
    expiry_date: FiservemeaExpiryDate,
    security_code: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum FiservemeaPaymentMethods {
    PaymentCard(FiservemeaPaymentCard),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaPaymentsRequest {
    request_type: FiservemeaRequestType,
    merchant_transaction_id: String,
    transaction_amount: FiservemeaTransactionAmount,
    order: FiservemeaOrder,
    payment_method: FiservemeaPaymentMethods,
}

impl TryFrom<&FiservemeaRouterData<&PaymentsAuthorizeRouterData>> for FiservemeaPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &FiservemeaRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let card = FiservemeaPaymentCard {
                    number: req_card.card_number.clone(),
                    expiry_date: FiservemeaExpiryDate {
                        month: req_card.card_exp_month.clone(),
                        year: req_card.get_card_expiry_year_2_digit()?,
                    },
                    security_code: req_card.card_cvc,
                };
                let request_type = if matches!(
                    item.router_data.request.capture_method,
                    Some(enums::CaptureMethod::Automatic)
                        | Some(enums::CaptureMethod::SequentialAutomatic)
                ) {
                    FiservemeaRequestType::PaymentCardSaleTransaction
                } else {
                    FiservemeaRequestType::PaymentCardPreAuthTransaction
                };

                Ok(Self {
                    request_type,
                    merchant_transaction_id: item
                        .router_data
                        .request
                        .merchant_order_reference_id
                        .clone()
                        .unwrap_or(item.router_data.connector_request_reference_id.clone()),
                    transaction_amount: FiservemeaTransactionAmount {
                        total: item.amount.clone(),
                        currency: item.router_data.request.currency,
                    },
                    order: FiservemeaOrder {
                        order_id: item.router_data.connector_request_reference_id.clone(),
                    },
                    payment_method: FiservemeaPaymentMethods::PaymentCard(card),
                })
            }
            _ => Err(errors::ConnectorError::NotImplemented(
                "Selected payment method through fiservemea".to_string(),
            )
            .into()),
        }
    }
}

// Auth Struct
#[derive(Clone)]
pub struct FiservemeaAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) secret_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for FiservemeaAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                api_key: api_key.to_owned(),
                secret_key: key1.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

// PaymentsResponse
#[derive(Debug, Serialize, Deserialize)]
pub enum ResponseType {
    BadRequest,
    Unauthenticated,
    Unauthorized,
    NotFound,
    GatewayDeclined,
    EndpointDeclined,
    ServerError,
    EndpointCommunicationError,
    UnsupportedMediaType,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum FiservemeaTransactionType {
    Sale,
    Preauth,
    Credit,
    ForcedTicket,
    Void,
    Return,
    Postauth,
    PayerAuth,
    Disbursement,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum FiservemeaTransactionOrigin {
    Ecom,
    Moto,
    Mail,
    Phone,
    Retail,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum FiservemeaPaymentStatus {
    Approved,
    Waiting,
    Partial,
    ValidationFailed,
    ProcessingFailed,
    Declined,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum FiservemeaPaymentResult {
    Approved,
    Declined,
    Failed,
    Waiting,
    Partial,
    Fraud,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaPaymentCardResponse {
    expiry_date: Option<FiservemeaExpiryDate>,
    bin: Option<String>,
    last4: Option<String>,
    brand: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaPaymentMethodDetails {
    payment_card: Option<FiservemeaPaymentCardResponse>,
    payment_method_type: Option<String>,
    payment_method_brand: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Components {
    subtotal: Option<f64>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AmountDetails {
    total: Option<f64>,
    currency: Option<common_enums::Currency>,
    components: Option<Components>,
}

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

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Processor {
    reference_number: Option<String>,
    authorization_code: Option<String>,
    response_code: Option<String>,
    response_message: Option<String>,
    avs_response: Option<AvsResponse>,
    security_code_response: Option<String>,
}

fn map_status(
    fiservemea_status: Option<FiservemeaPaymentStatus>,
    fiservemea_result: Option<FiservemeaPaymentResult>,
    transaction_type: FiservemeaTransactionType,
) -> common_enums::AttemptStatus {
    match fiservemea_status {
        Some(status) => match status {
            FiservemeaPaymentStatus::Approved => match transaction_type {
                FiservemeaTransactionType::Preauth => common_enums::AttemptStatus::Authorized,
                FiservemeaTransactionType::Void => common_enums::AttemptStatus::Voided,
                FiservemeaTransactionType::Sale | FiservemeaTransactionType::Postauth => {
                    common_enums::AttemptStatus::Charged
                }
                FiservemeaTransactionType::Credit
                | FiservemeaTransactionType::ForcedTicket
                | FiservemeaTransactionType::Return
                | FiservemeaTransactionType::PayerAuth
                | FiservemeaTransactionType::Disbursement => common_enums::AttemptStatus::Failure,
            },
            FiservemeaPaymentStatus::Waiting => common_enums::AttemptStatus::Pending,
            FiservemeaPaymentStatus::Partial => common_enums::AttemptStatus::PartialCharged,
            FiservemeaPaymentStatus::ValidationFailed
            | FiservemeaPaymentStatus::ProcessingFailed
            | FiservemeaPaymentStatus::Declined => common_enums::AttemptStatus::Failure,
        },
        None => match fiservemea_result {
            Some(result) => match result {
                FiservemeaPaymentResult::Approved => match transaction_type {
                    FiservemeaTransactionType::Preauth => common_enums::AttemptStatus::Authorized,
                    FiservemeaTransactionType::Void => common_enums::AttemptStatus::Voided,
                    FiservemeaTransactionType::Sale | FiservemeaTransactionType::Postauth => {
                        common_enums::AttemptStatus::Charged
                    }
                    FiservemeaTransactionType::Credit
                    | FiservemeaTransactionType::ForcedTicket
                    | FiservemeaTransactionType::Return
                    | FiservemeaTransactionType::PayerAuth
                    | FiservemeaTransactionType::Disbursement => {
                        common_enums::AttemptStatus::Failure
                    }
                },
                FiservemeaPaymentResult::Waiting => common_enums::AttemptStatus::Pending,
                FiservemeaPaymentResult::Partial => common_enums::AttemptStatus::PartialCharged,
                FiservemeaPaymentResult::Declined
                | FiservemeaPaymentResult::Failed
                | FiservemeaPaymentResult::Fraud => common_enums::AttemptStatus::Failure,
            },
            None => common_enums::AttemptStatus::Pending,
        },
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaPaymentsResponse {
    response_type: Option<ResponseType>,
    #[serde(rename = "type")]
    fiservemea_type: Option<String>,
    client_request_id: Option<String>,
    api_trace_id: Option<String>,
    ipg_transaction_id: String,
    order_id: Option<String>,
    transaction_type: FiservemeaTransactionType,
    transaction_origin: Option<FiservemeaTransactionOrigin>,
    payment_method_details: Option<FiservemeaPaymentMethodDetails>,
    country: Option<Secret<String>>,
    terminal_id: Option<String>,
    merchant_id: Option<String>,
    merchant_transaction_id: Option<String>,
    transaction_time: Option<i64>,
    approved_amount: Option<AmountDetails>,
    transaction_amount: Option<AmountDetails>,
    transaction_status: Option<FiservemeaPaymentStatus>, // FiservEMEA Docs mention that this field is deprecated. We are using it for now because transaction_result is not present in the response.
    transaction_result: Option<FiservemeaPaymentResult>,
    approval_code: Option<String>,
    error_message: Option<String>,
    transaction_state: Option<String>,
    scheme_transaction_id: Option<String>,
    processor: Option<Processor>,
}

impl<F, T> TryFrom<ResponseRouterData<F, FiservemeaPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, FiservemeaPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: map_status(
                item.response.transaction_status,
                item.response.transaction_result,
                item.response.transaction_type,
            ),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.ipg_transaction_id),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: item.response.order_id,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaCaptureRequest {
    request_type: FiservemeaRequestType,
    transaction_amount: FiservemeaTransactionAmount,
}

impl TryFrom<&FiservemeaRouterData<&PaymentsCaptureRouterData>> for FiservemeaCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &FiservemeaRouterData<&PaymentsCaptureRouterData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            request_type: FiservemeaRequestType::PostAuthTransaction,
            transaction_amount: FiservemeaTransactionAmount {
                total: item.amount.clone(),
                currency: item.router_data.request.currency,
            },
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaVoidRequest {
    request_type: FiservemeaRequestType,
}

impl TryFrom<&PaymentsCancelRouterData> for FiservemeaVoidRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(_item: &PaymentsCancelRouterData) -> Result<Self, Self::Error> {
        Ok(Self {
            request_type: FiservemeaRequestType::VoidPreAuthTransactions,
        })
    }
}

// REFUND :
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservemeaRefundRequest {
    request_type: FiservemeaRequestType,
    transaction_amount: FiservemeaTransactionAmount,
}

impl<F> TryFrom<&FiservemeaRouterData<&RefundsRouterData<F>>> for FiservemeaRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &FiservemeaRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        Ok(Self {
            request_type: FiservemeaRequestType::ReturnTransaction,
            transaction_amount: FiservemeaTransactionAmount {
                total: item.amount.clone(),
                currency: item.router_data.request.currency,
            },
        })
    }
}

fn map_refund_status(
    fiservemea_status: Option<FiservemeaPaymentStatus>,
    fiservemea_result: Option<FiservemeaPaymentResult>,
) -> Result<enums::RefundStatus, errors::ConnectorError> {
    match fiservemea_status {
        Some(status) => match status {
            FiservemeaPaymentStatus::Approved => Ok(enums::RefundStatus::Success),
            FiservemeaPaymentStatus::Partial | FiservemeaPaymentStatus::Waiting => {
                Ok(enums::RefundStatus::Pending)
            }
            FiservemeaPaymentStatus::ValidationFailed
            | FiservemeaPaymentStatus::ProcessingFailed
            | FiservemeaPaymentStatus::Declined => Ok(enums::RefundStatus::Failure),
        },
        None => match fiservemea_result {
            Some(result) => match result {
                FiservemeaPaymentResult::Approved => Ok(enums::RefundStatus::Success),
                FiservemeaPaymentResult::Partial | FiservemeaPaymentResult::Waiting => {
                    Ok(enums::RefundStatus::Pending)
                }
                FiservemeaPaymentResult::Declined
                | FiservemeaPaymentResult::Failed
                | FiservemeaPaymentResult::Fraud => Ok(enums::RefundStatus::Failure),
            },
            None => Err(errors::ConnectorError::MissingRequiredField {
                field_name: "transactionResult",
            }),
        },
    }
}

impl TryFrom<RefundsResponseRouterData<Execute, FiservemeaPaymentsResponse>>
    for RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, FiservemeaPaymentsResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.ipg_transaction_id,
                refund_status: map_refund_status(
                    item.response.transaction_status,
                    item.response.transaction_result,
                )?,
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, FiservemeaPaymentsResponse>>
    for RefundsRouterData<RSync>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, FiservemeaPaymentsResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.ipg_transaction_id,
                refund_status: map_refund_status(
                    item.response.transaction_status,
                    item.response.transaction_result,
                )?,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ErrorDetails {
    pub field: Option<String>,
    pub message: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FiservemeaError {
    pub code: Option<String>,
    pub message: Option<String>,
    pub details: Option<Vec<ErrorDetails>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FiservemeaErrorResponse {
    #[serde(rename = "type")]
    fiservemea_type: Option<String>,
    client_request_id: Option<String>,
    api_trace_id: Option<String>,
    pub response_type: Option<String>,
    pub error: Option<FiservemeaError>,
}


File: crates/analytics/src/payments/filters.rs
 Public functions: 1
 Public structs: 1

use api_models::analytics::{payments::PaymentDimensions, Granularity, TimeRange};
use common_utils::errors::ReportSwitchExt;
use diesel_models::enums::{AttemptStatus, AuthenticationType, Currency, RoutingApproach};
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use crate::{
    enums::AuthInfo,
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
    types::{
        AnalyticsCollection, AnalyticsDataSource, DBEnumWrapper, FiltersError, FiltersResult,
        LoadRow,
    },
};

pub trait PaymentFilterAnalytics: LoadRow<PaymentFilterRow> {}

pub async fn get_payment_filter_for_dimension<T>(
    dimension: PaymentDimensions,
    auth: &AuthInfo,
    time_range: &TimeRange,
    pool: &T,
) -> FiltersResult<Vec<PaymentFilterRow>>
where
    T: AnalyticsDataSource + PaymentFilterAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::Payment);

    query_builder.add_select_column(dimension).switch()?;
    time_range
        .set_filter_clause(&mut query_builder)
        .attach_printable("Error filtering time range")
        .switch()?;

    auth.set_filter_clause(&mut query_builder).switch()?;

    query_builder.set_distinct();

    query_builder
        .execute_query::<PaymentFilterRow, _>(pool)
        .await
        .change_context(FiltersError::QueryBuildingError)?
        .change_context(FiltersError::QueryExecutionFailure)
}

#[derive(Debug, serde::Serialize, Eq, PartialEq, serde::Deserialize)]
pub struct PaymentFilterRow {
    pub currency: Option<DBEnumWrapper<Currency>>,
    pub status: Option<DBEnumWrapper<AttemptStatus>>,
    pub connector: Option<String>,
    pub authentication_type: Option<DBEnumWrapper<AuthenticationType>>,
    pub payment_method: Option<String>,
    pub payment_method_type: Option<String>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub profile_id: Option<String>,
    pub card_network: Option<String>,
    pub merchant_id: Option<String>,
    pub card_last_4: Option<String>,
    pub card_issuer: Option<String>,
    pub error_reason: Option<String>,
    pub first_attempt: Option<bool>,
    pub routing_approach: Option<DBEnumWrapper<RoutingApproach>>,
    pub signature_network: Option<String>,
    pub is_issuer_regulated: Option<bool>,
    pub is_debit_routed: Option<bool>,
}


pub fn new(
        frm_status: Option<String>,
        frm_name: Option<String>,
        frm_transaction_type: Option<String>,
        normalized_time_range: TimeRange,
    ) -> Self {
        Self {
            frm_status,
            frm_name,
            frm_transaction_type,
            time_bucket: normalized_time_range,
            start_time: normalized_time_range.start_time,
        }
    }


impl ConnectorAccessToken for KafkaStore {
    async fn get_access_token(
        &self,
        merchant_id: &id_type::MerchantId,
        merchant_connector_id: &str,
    ) -> CustomResult<Option<AccessToken>, errors::StorageError> {
        self.diesel_store
            .get_access_token(merchant_id, merchant_connector_id)
            .await
    }

    async fn set_access_token(
        &self,
        merchant_id: &id_type::MerchantId,
        merchant_connector_id: &str,
        access_token: AccessToken,
    ) -> CustomResult<(), errors::StorageError> {
        self.diesel_store
            .set_access_token(merchant_id, merchant_connector_id, access_token)
            .await
    }
}

impl ConnectorAccessToken for for KafkaStore


pub trait ValidateRequest<F, R, D> {
    fn validate_request(
        &self,
        request: &R,
        merchant_context: &domain::MerchantContext,
    ) -> RouterResult<ValidateResult>;
}


impl Responder {
    let flow = Flow::RoutingRetrieveDictionary;
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        query.into_inner(),
        |state, auth: auth::AuthenticationData, query_params, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            routing::retrieve_merchant_routing_dictionary(
                state,
                merchant_context,
                auth.profile_id.map(|profile_id| vec![profile_id]),
                query_params.clone(),
                transaction_type
                    .or(query_params.transaction_type)
                    .unwrap_or(enums::TransactionType::Payment),
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::ProfileRoutingRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub fn new() -> &'static Self {
        &Self {
            amount_converter: &StringMinorUnitForConnector,
        }
    }


pub struct TokenizationData {
    pub brand_reference: Option<String>,
}


pub struct AuthenticationAuthenticateRequest {
    /// Authentication ID for the authentication
    #[serde(skip_deserializing)]
    pub authentication_id: id_type::AuthenticationId,
    /// Client secret for the authentication
    #[schema(value_type = String)]
    pub client_secret: Option<masking::Secret<String>>,
    /// SDK Information if request is from SDK
    pub sdk_information: Option<SdkInformation>,
    /// Device Channel indicating whether request is coming from App or Browser
    pub device_channel: DeviceChannel,
    /// Indicates if 3DS method data was successfully completed or not
    pub threeds_method_comp_ind: ThreeDsCompletionIndicator,
}


/// Build connections to all gRPC services
    pub async fn build_connections(
        config: &GrpcClientSettings,
        client: Client,
    ) -> Result<Self, Box<dyn std::error::Error>> {
        let dynamic_routing_config = &config.dynamic_routing_client;
        let connection = match dynamic_routing_config {
            Some(DynamicRoutingClientConfig::Enabled {
                host,
                port,
                service,
            }) => Some((host.clone(), *port, service.clone())),
            _ => None,
        };

        let mut client_map = HashMap::new();

        if let Some(conn) = connection {
            let uri = format!("http://{}:{}", conn.0, conn.1).parse::<tonic::transport::Uri>()?;
            let health_client = HealthClient::with_origin(client, uri);

            client_map.insert(HealthCheckServices::DynamicRoutingService, health_client);
        }

        Ok(Self {
            clients: client_map,
        })
    }


pub struct RoutableConnectorChoice {
    #[serde(skip)]
    pub choice_kind: RoutableChoiceKind,
    pub connector: RoutableConnectors,
    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
}


pub struct UpdateEliminationBucketEventRequest {
    pub id: String,
    pub params: String,
    pub labels_with_bucket_name: Vec<LabelWithBucketNameEventRequest>,
    pub config: Option<EliminationRoutingEventBucketConfig>,
}


pub async fn frm_fulfillment(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<frm_core::types::FrmFulfillmentRequest>,
) -> HttpResponse {
    let flow = Flow::FrmFulfillment;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, auth: services::authentication::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            frm_core::frm_fulfillment_core(state, merchant_context, req)
        },
        &services::authentication::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn api_key_update() {}


pub async fn check_merchant_account_kv_status(
    state: SessionState,
    merchant_id: id_type::MerchantId,
) -> RouterResponse<api_models::admin::ToggleKVResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let key_store = db
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &db.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    // check if the merchant account exists
    let merchant_account = db
        .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store)
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let kv_status = matches!(
        merchant_account.storage_scheme,
        MerchantStorageScheme::RedisKv
    );

    Ok(service_api::ApplicationResponse::Json(
        api_models::admin::ToggleKVResponse {
            merchant_id: merchant_account.get_id().to_owned(),
            kv_enabled: kv_status,
        },
    ))
}


pub struct Authentication;


impl api::PaymentVoid for Bamboraapac {}

impl api::PaymentVoid for for Bamboraapac


pub struct AddVaultResponse {
    pub entity_id: Option<id_type::GlobalCustomerId>,
    pub vault_id: domain::VaultId,
    pub fingerprint_id: Option<String>,
}


pub fn perform_volume_split(
    mut splits: Vec<routing_types::ConnectorVolumeSplit>,
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>> {
    let weights: Vec<u8> = splits.iter().map(|sp| sp.split).collect();
    let weighted_index = distributions::WeightedIndex::new(weights)
        .change_context(errors::RoutingError::VolumeSplitFailed)
        .attach_printable("Error creating weighted distribution for volume split")?;

    let mut rng = rand::thread_rng();
    let idx = weighted_index.sample(&mut rng);

    splits
        .get(idx)
        .ok_or(errors::RoutingError::VolumeSplitFailed)
        .attach_printable("Volume split index lookup failed")?;

    // Panic Safety: We have performed a `get(idx)` operation just above which will
    // ensure that the index is always present, else throw an error.
    let removed = splits.remove(idx);
    splits.insert(0, removed);

    Ok(splits.into_iter().map(|sp| sp.connector).collect())
}


pub async fn routing_retrieve_default_config() {}


impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetAuthEventMetricRequest");
        let flow = AnalyticsFlow::GetAuthMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let org_id = auth.merchant_account.get_org_id();
                let auth: AuthInfo = AuthInfo::OrgLevel {
                    org_id: org_id.clone(),
                };
                analytics::auth_events::get_metrics(&state.pool, &auth, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


Documentation: api-reference/intelligent-router-api-reference/elimination/fetch-eliminated-processor-list.mdx
# Type: Doc File

---
openapi: post /elimination.EliminationAnalyser/GetEliminationStatus
---


pub async fn get_filters(
    pool: &AnalyticsProvider,
    req: GetPaymentFiltersRequest,
    auth: &AuthInfo,
) -> AnalyticsResult<PaymentFiltersResponse> {
    let mut res = PaymentFiltersResponse::default();

    for dim in req.group_by_names {
        let values = match pool {
                        AnalyticsProvider::Sqlx(pool) => {
                get_payment_filter_for_dimension(dim, auth, &req.time_range, pool)
                    .await
            }
                        AnalyticsProvider::Clickhouse(pool) => {
                get_payment_filter_for_dimension(dim, auth, &req.time_range, pool)
                    .await
            }
                    AnalyticsProvider::CombinedCkh(sqlx_poll, ckh_pool) => {
                let ckh_result = get_payment_filter_for_dimension(
                    dim,
                    auth,
                    &req.time_range,
                    ckh_pool,
                )
                .await;
                let sqlx_result = get_payment_filter_for_dimension(
                    dim,
                    auth,
                    &req.time_range,
                    sqlx_poll,
                )
                .await;
                match (&sqlx_result, &ckh_result) {
                    (Ok(ref sqlx_res), Ok(ref ckh_res)) if sqlx_res != ckh_res => {
                        router_env::logger::error!(clickhouse_result=?ckh_res, postgres_result=?sqlx_res, "Mismatch between clickhouse & postgres payments analytics filters")
                    },
                    _ => {}
                };
                ckh_result
            }
                    AnalyticsProvider::CombinedSqlx(sqlx_poll, ckh_pool) => {
                let ckh_result = get_payment_filter_for_dimension(
                    dim,
                    auth,
                    &req.time_range,
                    ckh_pool,
                )
                .await;
                let sqlx_result = get_payment_filter_for_dimension(
                    dim,
                    auth,
                    &req.time_range,
                    sqlx_poll,
                )
                .await;
                match (&sqlx_result, &ckh_result) {
                    (Ok(ref sqlx_res), Ok(ref ckh_res)) if sqlx_res != ckh_res => {
                        router_env::logger::error!(clickhouse_result=?ckh_res, postgres_result=?sqlx_res, "Mismatch between clickhouse & postgres payments analytics filters")
                    },
                    _ => {}
                };
                sqlx_result
            }
        }
        .change_context(AnalyticsError::UnknownError)?
        .into_iter()
        .filter_map(|fil: PaymentFilterRow| match dim {
            PaymentDimensions::Currency => fil.currency.map(|i| i.as_ref().to_string()),
            PaymentDimensions::PaymentStatus => fil.status.map(|i| i.as_ref().to_string()),
            PaymentDimensions::Connector => fil.connector,
            PaymentDimensions::AuthType => fil.authentication_type.map(|i| i.as_ref().to_string()),
            PaymentDimensions::PaymentMethod => fil.payment_method,
            PaymentDimensions::PaymentMethodType => fil.payment_method_type,
            PaymentDimensions::ClientSource => fil.client_source,
            PaymentDimensions::ClientVersion => fil.client_version,
            PaymentDimensions::ProfileId => fil.profile_id,
            PaymentDimensions::CardNetwork => fil.card_network,
            PaymentDimensions::MerchantId => fil.merchant_id,
            PaymentDimensions::CardLast4 => fil.card_last_4,
            PaymentDimensions::CardIssuer => fil.card_issuer,
            PaymentDimensions::ErrorReason => fil.error_reason,
            PaymentDimensions::RoutingApproach => fil.routing_approach.map(|i| i.as_ref().to_string()),
            PaymentDimensions::SignatureNetwork => fil.signature_network,
            PaymentDimensions::IsIssuerRegulated => fil.is_issuer_regulated.map(|b| b.to_string()),
            PaymentDimensions::IsDebitRouted => fil.is_debit_routed.map(|b| b.to_string())
        })
        .collect::<Vec<String>>();
        res.query_data.push(FilterValue {
            dimension: dim,
            values,
        })
    }
    Ok(res)
}


pub struct StripebillingPaymentsResponse {
    status: StripebillingPaymentStatus,
    id: String,
}


pub async fn update_config(
    state: SessionState,
    config_update: &api::ConfigUpdate,
) -> RouterResponse<api::Config> {
    let store = state.store.as_ref();
    let config = store
        .update_config_by_key(&config_update.key, config_update.foreign_into())
        .await
        .to_not_found_response(errors::ApiErrorResponse::ConfigNotFound)?;
    Ok(ApplicationResponse::Json(config.foreign_into()))
}


impl api::PaymentToken for Opayo {}


pub struct RefundResponse {
    id: String,
    status: RefundStatus,
}


pub async fn get_bank_from_hs_locker(
    state: &routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    temp_token: Option<&String>,
    customer_id: &id_type::CustomerId,
    merchant_id: &id_type::MerchantId,
    token_ref: &str,
) -> errors::RouterResult<api::BankPayout> {
    let payment_method = get_payment_method_from_hs_locker(
        state,
        key_store,
        customer_id,
        merchant_id,
        token_ref,
        None,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Error getting payment method from locker")?;
    let pm_parsed: api::PayoutMethodData = payment_method
        .peek()
        .to_string()
        .parse_struct("PayoutMethodData")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    match &pm_parsed {
        api::PayoutMethodData::Bank(bank) => {
            if let Some(token) = temp_token {
                vault::Vault::store_payout_method_data_in_locker(
                    state,
                    Some(token.clone()),
                    &pm_parsed,
                    Some(customer_id.to_owned()),
                    key_store,
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error storing payout method data in temporary locker")?;
            }
            Ok(bank.to_owned())
        }
        api::PayoutMethodData::Card(_) => Err(errors::ApiErrorResponse::InvalidRequestData {
            message: "Expected bank details, found card details instead".to_string(),
        }
        .into()),
        api::PayoutMethodData::Wallet(_) => Err(errors::ApiErrorResponse::InvalidRequestData {
            message: "Expected bank details, found wallet details instead".to_string(),
        }
        .into()),
    }
}


pub struct HeaderAuth<I>(pub I);


pub async fn merchant_account_toggle_kv(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
    json_payload: web::Json<admin::ToggleKVRequest>,
) -> HttpResponse {
    let flow = Flow::ConfigKeyUpdate;
    let mut payload = json_payload.into_inner();
    payload.merchant_id = path.into_inner();

    api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, payload, _| kv_for_merchant(state, payload.merchant_id, payload.kv_enabled),
        &auth::V2AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    )
    .await
}


pub struct VerifyConnectorRequest {
    pub connector_name: enums::Connector,
    pub connector_account_details: admin::ConnectorAuthType,
}


pub struct FiuuWebhooksRefundResponse {
    pub refund_type: FiuuWebhooksRefundType,
    #[serde(rename = "MerchantID")]
    pub merchant_id: Secret<String>,
    #[serde(rename = "RefID")]
    pub ref_id: String,
    #[serde(rename = "RefundID")]
    pub refund_id: String,
    #[serde(rename = "TxnID")]
    pub txn_id: String,
    pub amount: StringMajorUnit,
    pub status: FiuuRefundsWebhookStatus,
    pub signature: Secret<String>,
}


impl AuthenticationUpdateInternal {
    pub fn apply_changeset(self, source: Authentication) -> Authentication {
        let Self {
            connector_authentication_id,
            payment_method_id,
            authentication_type,
            authentication_status,
            authentication_lifecycle_status,
            modified_at: _,
            error_code,
            error_message,
            connector_metadata,
            maximum_supported_version,
            threeds_server_transaction_id,
            authentication_flow_type,
            message_version,
            eci,
            trans_status,
            acquirer_bin,
            acquirer_merchant_id,
            three_ds_method_data,
            three_ds_method_url,
            acs_url,
            challenge_request,
            acs_reference_number,
            acs_trans_id,
            acs_signed_content,
            ds_trans_id,
            directory_server_id,
            acquirer_country_code,
            service_details,
            force_3ds_challenge,
            psd2_sca_exemption_type,
            billing_address,
            shipping_address,
            browser_info,
            email,
            profile_acquirer_id,
            challenge_code,
            challenge_cancel,
            challenge_code_reason,
            message_extension,
        } = self;
        Authentication {
            connector_authentication_id: connector_authentication_id
                .or(source.connector_authentication_id),
            payment_method_id: payment_method_id.unwrap_or(source.payment_method_id),
            authentication_type: authentication_type.or(source.authentication_type),
            authentication_status: authentication_status.unwrap_or(source.authentication_status),
            authentication_lifecycle_status: authentication_lifecycle_status
                .unwrap_or(source.authentication_lifecycle_status),
            modified_at: common_utils::date_time::now(),
            error_code: error_code.or(source.error_code),
            error_message: error_message.or(source.error_message),
            connector_metadata: connector_metadata.or(source.connector_metadata),
            maximum_supported_version: maximum_supported_version
                .or(source.maximum_supported_version),
            threeds_server_transaction_id: threeds_server_transaction_id
                .or(source.threeds_server_transaction_id),
            authentication_flow_type: authentication_flow_type.or(source.authentication_flow_type),
            message_version: message_version.or(source.message_version),
            eci: eci.or(source.eci),
            trans_status: trans_status.or(source.trans_status),
            acquirer_bin: acquirer_bin.or(source.acquirer_bin),
            acquirer_merchant_id: acquirer_merchant_id.or(source.acquirer_merchant_id),
            three_ds_method_data: three_ds_method_data.or(source.three_ds_method_data),
            three_ds_method_url: three_ds_method_url.or(source.three_ds_method_url),
            acs_url: acs_url.or(source.acs_url),
            challenge_request: challenge_request.or(source.challenge_request),
            acs_reference_number: acs_reference_number.or(source.acs_reference_number),
            acs_trans_id: acs_trans_id.or(source.acs_trans_id),
            acs_signed_content: acs_signed_content.or(source.acs_signed_content),
            ds_trans_id: ds_trans_id.or(source.ds_trans_id),
            directory_server_id: directory_server_id.or(source.directory_server_id),
            acquirer_country_code: acquirer_country_code.or(source.acquirer_country_code),
            service_details: service_details.or(source.service_details),
            force_3ds_challenge: force_3ds_challenge.or(source.force_3ds_challenge),
            psd2_sca_exemption_type: psd2_sca_exemption_type.or(source.psd2_sca_exemption_type),
            billing_address: billing_address.or(source.billing_address),
            shipping_address: shipping_address.or(source.shipping_address),
            browser_info: browser_info.or(source.browser_info),
            email: email.or(source.email),
            profile_acquirer_id: profile_acquirer_id.or(source.profile_acquirer_id),
            challenge_code: challenge_code.or(source.challenge_code),
            challenge_cancel: challenge_cancel.or(source.challenge_cancel),
            challenge_code_reason: challenge_code_reason.or(source.challenge_code_reason),
            message_extension: message_extension.or(source.message_extension),
            ..source
        }
    }
}


pub async fn deep_health_check(
    conf: web::Data<Settings>,
    stores: web::Data<HashMap<String, Arc<Store>>>,
) -> impl actix_web::Responder {
    let mut deep_health_res = HashMap::new();
    for (tenant, store) in stores.iter() {
        logger::info!("Tenant: {:?}", tenant);

        let response = match deep_health_check_func(conf.clone(), store).await {
            Ok(response) => serde_json::to_string(&response)
                .map_err(|err| {
                    logger::error!(serialization_error=?err);
                })
                .unwrap_or_default(),
            Err(err) => return log_and_return_error_response(err),
        };
        deep_health_res.insert(tenant.clone(), response);
    }
    services::http_response_json(
        serde_json::to_string(&deep_health_res)
            .map_err(|err| {
                logger::error!(serialization_error=?err);
            })
            .unwrap_or_default(),
    )
}


    #[schema(example = "USD", value_type = Currency)]
    pub currency: common_enums::Currency,
    /// The shipping cost of the order. This has to be collected from the merchant
    pub shipping_cost: Option<MinorUnit>,
    /// Tax amount related to the order. This will be calculated by the external tax provider
    pub order_tax_amount: Option<MinorUnit>,
    /// The action to whether calculate tax by calling external tax provider or not
    #[schema(value_type = TaxCalculationOverride)]
    pub external_tax_calculation: common_enums::TaxCalculationOverride,
    /// The action to whether calculate surcharge or not
    #[schema(value_type = SurchargeCalculationOverride)]
    pub surcharge_calculation: common_enums::SurchargeCalculationOverride,
    /// The surcharge amount to be added to the order, collected from the merchant
    pub surcharge_amount: Option<MinorUnit>,
    /// tax on surcharge amount
    pub tax_on_surcharge: Option<MinorUnit>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, PartialEq, serde::Serialize, ToSchema)]
pub struct PaymentAmountDetailsResponse {
    /// The payment amount. Amount for the payment in the lowest denomination of the currency, (i.e) in cents for USD denomination, in yen for JPY denomination etc. E.g., Pass 100 to charge $1.00 and 1 for 1¥ since ¥ is a zero-decimal currency. Read more about [the Decimal and Non-Decimal Currencies](https://github.com/juspay/hyperswitch/wiki/Decimal-and-Non%E2%80%90Decimal-Currencies)
    #[schema(value_type = u64, example = 6540)]
    #[serde(default, deserialize_with = "amount::deserialize")]
    pub order_amount: MinorUnit,
    /// The currency of the order
    #[schema(example = "USD", value_type = Currency)]
    pub currency: common_enums::Currency,
    /// The shipping cost of the order. This has to be collected from the merchant
    pub shipping_cost: Option<MinorUnit>,
    /// Tax amount related to the order. This will be calculated by the external tax provider
    pub order_tax_amount: Option<MinorUnit>,
    /// The action to whether calculate tax by calling external tax provider or not
    #[schema(value_type = TaxCalculationOverride)]
    pub external_tax_calculation: common_enums::TaxCalculationOverride,
    /// The action to whether calculate surcharge or not
    #[schema(value_type = SurchargeCalculationOverride)]
    pub surcharge_calculation: common_enums::SurchargeCalculationOverride,
    /// The surcharge amount to be added to the order, collected from the merchant
    pub surcharge_amount: Option<MinorUnit>,
    /// tax on surcharge amount
    pub tax_on_surcharge: Option<MinorUnit>,
    /// The total amount of the order including tax, surcharge and shipping cost
    pub net_amount: MinorUnit,
    /// The amount that was requested to be captured for this payment
    pub amount_to_capture: Option<MinorUnit>,
    /// The amount that can be captured on the payment. Either in one go or through multiple captures.
    /// This is applicable in case the capture method was either `manual` or `manual_multiple`
    pub amount_capturable: MinorUnit,
    /// The amount that was captured for this payment. This is the sum of all the captures done on this payment
    pub amount_captured: Option<MinorUnit>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]

pub struct PaymentAttemptAmountDetails {
    /// The total amount of the order including tax, surcharge and shipping cost
    pub net_amount: MinorUnit,
    /// The amount that was requested to be captured for this payment
    pub amount_to_capture: Option<MinorUnit>,
    /// Surcharge amount for the payment attempt.
    /// This is either derived by surcharge rules, or sent by the merchant
    pub surcharge_amount: Option<MinorUnit>,
    /// Tax amount for the payment attempt
    /// This is either derived by surcharge rules, or sent by the merchant
    pub tax_on_surcharge: Option<MinorUnit>,
    /// The total amount that can be captured for this payment attempt.
    pub amount_capturable: MinorUnit,
    /// Shipping cost for the payment attempt.
    /// Shipping cost for the payment attempt.
    pub shipping_cost: Option<MinorUnit>,
    /// Tax amount for the order.
    /// This is either derived by calling an external tax processor, or sent by the merchant
    pub order_tax_amount: Option<MinorUnit>,
}

#[cfg(feature = "v2")]
impl AmountDetails {
    pub fn new(amount_details_setter: AmountDetailsSetter) -> Self {
        Self {
            order_amount: amount_details_setter.order_amount,
            currency: amount_details_setter.currency,
            shipping_cost: amount_details_setter.shipping_cost,
            order_tax_amount: amount_details_setter.order_tax_amount,
            skip_external_tax_calculation: amount_details_setter.skip_external_tax_calculation,
            skip_surcharge_calculation: amount_details_setter.skip_surcharge_calculation,
            surcharge_amount: amount_details_setter.surcharge_amount,
            tax_on_surcharge: amount_details_setter.tax_on_surcharge,
        }
    }
    pub fn order_amount(&self) -> Amount {
        self.order_amount
    }
    pub fn currency(&self) -> common_enums::Currency {
        self.currency
    }
    pub fn shipping_cost(&self) -> Option<MinorUnit> {
        self.shipping_cost
    }
    pub fn order_tax_amount(&self) -> Option<MinorUnit> {
        self.order_tax_amount
    }
    pub fn skip_external_tax_calculation(&self) -> common_enums::TaxCalculationOverride {
        self.skip_external_tax_calculation
    }
    pub fn skip_surcharge_calculation(&self) -> common_enums::SurchargeCalculationOverride {
        self.skip_surcharge_calculation
    }
    pub fn surcharge_amount(&self) -> Option<MinorUnit> {
        self.surcharge_amount
    }
    pub fn tax_on_surcharge(&self) -> Option<MinorUnit> {
        self.tax_on_surcharge
    }
}

#[cfg(feature = "v2")]
impl AmountDetailsUpdate {
    pub fn order_amount(&self) -> Option<Amount> {
        self.order_amount
    }
    pub fn currency(&self) -> Option<common_enums::Currency> {
        self.currency
    }
    pub fn shipping_cost(&self) -> Option<MinorUnit> {
        self.shipping_cost
    }
    pub fn order_tax_amount(&self) -> Option<MinorUnit> {
        self.order_tax_amount
    }
    pub fn skip_external_tax_calculation(&self) -> Option<common_enums::TaxCalculationOverride> {
        self.skip_external_tax_calculation
    }
    pub fn skip_surcharge_calculation(&self) -> Option<common_enums::SurchargeCalculationOverride> {
        self.skip_surcharge_calculation
    }
    pub fn surcharge_amount(&self) -> Option<MinorUnit> {
        self.surcharge_amount
    }
    pub fn tax_on_surcharge(&self) -> Option<MinorUnit> {
        self.tax_on_surcharge
    }
}
#[cfg(feature = "v1")]
#[derive(
    Default,
    Debug,
    serde::Deserialize,
    serde::Serialize,
    Clone,
    ToSchema,
    router_derive::PolymorphicSchema,
    router_derive::ValidateSchema,
)]
#[generate_schemas(PaymentsCreateRequest, PaymentsUpdateRequest, PaymentsConfirmRequest)]
#[serde(deny_unknown_fields)]
pub struct PaymentsRequest {
    /// The primary amount for the payment, provided in the lowest denomination of the specified currency (e.g., 6540 for $65.40 USD). This field is mandatory for creating a payment.
    #[schema(value_type = Option<u64>, example = 6540)]
    #[serde(default, deserialize_with = "amount::deserialize_option")]
    #[mandatory_in(PaymentsCreateRequest = u64)]
    // Makes the field mandatory in PaymentsCreateRequest
    pub amount: Option<Amount>,

    /// Total tax amount applicable to the order, in the lowest denomination of the currency.
    #[schema(value_type = Option<i64>, example = 6540)]
    pub order_tax_amount: Option<MinorUnit>,

    /// The three-letter ISO 4217 currency code (e.g., "USD", "EUR") for the payment amount. This field is mandatory for creating a payment.
    #[schema(example = "USD", value_type = Option<Currency>)]
    #[mandatory_in(PaymentsCreateRequest = Currency)]
    pub currency: Option<api_enums::Currency>,

    /// The amount to be captured from the user's payment method, in the lowest denomination. If not provided, and `capture_method` is `automatic`, the full payment `amount` will be captured. If `capture_method` is `manual`, this can be specified in the `/capture` call. Must be less than or equal to the authorized amount.
    #[schema(value_type = Option<i64>, example = 6540)]
    pub amount_to_capture: Option<MinorUnit>,

    /// The shipping cost for the payment. This is required for tax calculation in some regions.
    #[schema(value_type = Option<i64>, example = 6540)]
    pub shipping_cost: Option<MinorUnit>,

    /// Optional. A merchant-provided unique identifier for the payment, contains 30 characters long (e.g., "pay_mbabizu24mvu3mela5njyhpit4"). If provided, it ensures idempotency for the payment creation request. If omitted, Hyperswitch generates a unique ID for the payment.
    #[schema(
        value_type = Option<String>,
        min_length = 30,
        max_length = 30,
        example = "pay_mbabizu24mvu3mela5njyhpit4"
    )]
    #[serde(default, deserialize_with = "payment_id_type::deserialize_option")]
    pub payment_id: Option<PaymentIdType>,

    /// This is an identifier for the merchant account. This is inferred from the API key
    /// provided during the request
    #[schema(max_length = 255, example = "merchant_1668273825", value_type = Option<String>)]
    #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest, PaymentsConfirmRequest)]
    pub merchant_id: Option<id_type::MerchantId>,

    /// Details of the routing configuration for that payment
    #[schema(value_type = Option<StraightThroughAlgorithm>, example = json!({
        "type": "single",
        "data": {"connector": "stripe", "merchant_connector_id": "mca_123"}
    }))]
    pub routing: Option<serde_json::Value>,

    /// This allows to manually select a connector with which the payment can go through.
    #[schema(value_type = Option<Vec<Connector>>, max_length = 255, example = json!(["stripe", "adyen"]))]
    pub connector: Option<Vec<api_enums::Connector>>,

    #[schema(value_type = Option<CaptureMethod>, example = "automatic")]
    pub capture_method: Option<api_enums::CaptureMethod>,

    #[schema(value_type = Option<AuthenticationType>, example = "no_three_ds", default = "three_ds")]
    pub authentication_type: Option<api_enums::AuthenticationType>,

    /// The billing details of the payment. This address will be used for invoicing.
    pub billing: Option<Address>,

    /// A timestamp (ISO 8601 code) that determines when the payment should be captured.
    /// Providing this field will automatically set `capture` to true
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest, PaymentsConfirmRequest)]
    pub capture_on: Option<PrimitiveDateTime>,

    /// If set to `true`, Hyperswitch attempts to confirm and authorize the payment immediately after creation, provided sufficient payment method details are included. If `false` or omitted (default is `false`), the payment is created with a status such as `requires_payment_method` or `requires_confirmation`, and a separate `POST /payments/{payment_id}/confirm` call is necessary to proceed with authorization.
    #[schema(default = false, example = true)]
    pub confirm: Option<bool>,

    /// Passing this object creates a new customer or attaches an existing customer to the payment
    pub customer: Option<CustomerDetails>,

    /// The identifier for the customer
    #[schema(value_type = Option<String>, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: Option<id_type::CustomerId>,

    /// The customer's email address.
    /// This field will be deprecated soon, use the customer object instead
    #[schema(max_length = 255, value_type = Option<String>, example = "johntest@test.com", deprecated)]
    #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest, PaymentsConfirmRequest)]
    pub email: Option<Email>,

    /// The customer's name.
    /// This field will be deprecated soon, use the customer object instead.
    #[schema(value_type = Option<String>, max_length = 255, example = "John Test", deprecated)]
    #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest, PaymentsConfirmRequest)]
    pub name: Option<Secret<String>>,

    /// The customer's phone number
    /// This field will be deprecated soon, use the customer object instead
    #[schema(value_type = Option<String>, max_length = 255, example = "9123456789", deprecated)]
    #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest, PaymentsConfirmRequest)]
    pub phone: Option<Secret<String>>,

    /// The country code for the customer phone number
    /// This field will be deprecated soon, use the customer object instead
    #[schema(max_length = 255, example = "+1", deprecated)]
    #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest, PaymentsConfirmRequest)]
    pub phone_country_code: Option<String>,

    /// Set to true to indicate that the customer is not in your checkout flow during this payment, and therefore is unable to authenticate. This parameter is intended for scenarios where you collect card details and charge them later. When making a recurring payment by passing a mandate_id, this parameter is mandatory
    #[schema(example = true)]
    pub off_session: Option<bool>,

    /// An arbitrary string attached to the payment. Often useful for displaying to users or for your own internal record-keeping.
    #[schema(example = "It's my first payment request")]
    pub description: Option<String>,

    /// The URL to redirect the customer to after they complete the payment process or authentication. This is crucial for flows that involve off-site redirection (e.g., 3DS, some bank redirects, wallet payments).
    #[schema(value_type = Option<String>, example = "https://hyperswitch.io", max_length = 2048)]
    pub return_url: Option<Url>,

    #[schema(value_type = Option<FutureUsage>, example = "off_session")]
    pub setup_future_usage: Option<api_enums::FutureUsage>,

    #[schema(example = "bank_transfer")]
    #[serde(with = "payment_method_data_serde", default)]
    pub payment_method_data: Option<PaymentMethodDataRequest>,

    #[schema(value_type = Option<PaymentMethod>, example = "card")]
    pub payment_method: Option<api_enums::PaymentMethod>,

    /// As Hyperswitch tokenises the sensitive details about the payments method, it provides the payment_token as a reference to a stored payment method, ensuring that the sensitive details are not exposed in any manner.
    #[schema(example = "187282ab-40ef-47a9-9206-5099ba31e432")]
    pub payment_token: Option<String>,

    /// This is used along with the payment_token field while collecting during saved card payments. This field will be deprecated soon, use the payment_method_data.card_token object instead
    #[schema(value_type = Option<String>, deprecated)]
    #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest, PaymentsConfirmRequest)]
    pub card_cvc: Option<Secret<String>>,

    /// The shipping address for the payment
    pub shipping: Option<Address>,

    /// For non-card charges, you can use this value as the complete description that appears on your customers’ statements. Must contain at least one letter, maximum 22 characters.
    #[schema(max_length = 255, example = "Hyperswitch Router")]
    pub statement_descriptor_name: Option<String>,

    /// Provides information about a card payment that customers see on their statements. Concatenated with the prefix (shortened descriptor) or statement descriptor that’s set on the account to form the complete statement descriptor. Maximum 22 characters for the concatenated descriptor.
    #[schema(max_length = 255, example = "Payment for shoes purchase")]
    pub statement_descriptor_suffix: Option<String>,

    /// Use this object to capture the details about the different products for which the payment is being made. The sum of amount across different products here should be equal to the overall payment amount
    #[schema(value_type = Option<Vec<OrderDetailsWithAmount>>, example = r#"[{
        "product_name": "Apple iPhone 16",
        "quantity": 1,
        "amount" : 69000
        "product_img_link" : "https://dummy-img-link.com"
    }]"#)]
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,

    /// It's a token used for client side verification.
    #[schema(example = "pay_U42c409qyHwOkWo3vK60_secret_el9ksDkiB8hi6j9N78yo")]
    #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest)]
    pub client_secret: Option<String>,

    /// Passing this object during payments creates a mandate. The mandate_type sub object is passed by the server.
    pub mandate_data: Option<MandateData>,

    /// This "CustomerAcceptance" object is passed during Payments-Confirm request, it enlists the type, time, and mode of acceptance properties related to an acceptance done by the customer. The customer_acceptance sub object is usually passed by the SDK or client.
    #[schema(value_type = Option<CustomerAcceptance>)]
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,

    /// A unique identifier to link the payment to a mandate. To do Recurring payments after a mandate has been created, pass the mandate_id instead of payment_method_data
    #[schema(max_length = 64, example = "mandate_iwer89rnjef349dni3")]
    #[remove_in(PaymentsUpdateRequest)]
    pub mandate_id: Option<String>,

    /// Additional details required by 3DS 2.0
    #[schema(value_type = Option<BrowserInformation>, example = r#"{
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled":true
    }"#)]
    pub browser_info: Option<serde_json::Value>,

    /// To indicate the type of payment experience that the payment method would go through
    #[schema(value_type = Option<PaymentExperience>, example = "redirect_to_url")]
    pub payment_experience: Option<api_enums::PaymentExperience>,

    /// Can be used to specify the Payment Method Type
    #[schema(value_type = Option<PaymentMethodType>, example = "google_pay")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,

    /// Business country of the merchant for this payment.
    /// To be deprecated soon. Pass the profile_id instead
    #[schema(value_type = Option<CountryAlpha2>, example = "US")]
    #[remove_in(PaymentsUpdateRequest, PaymentsConfirmRequest)]
    pub business_country: Option<api_enums::CountryAlpha2>,

    /// Business label of the merchant for this payment.
    /// To be deprecated soon. Pass the profile_id instead
    #[schema(example = "food")]
    #[remove_in(PaymentsUpdateRequest, PaymentsConfirmRequest)]
    pub business_label: Option<String>,

    #[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
    pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,

    /// Use this parameter to restrict the Payment Method Types to show for a given PaymentIntent
    #[schema(value_type = Option<Vec<PaymentMethodType>>)]
    pub allowed_payment_method_types: Option<Vec<api_enums::PaymentMethodType>>,

    /// Business sub label for the payment
    #[remove_in(PaymentsUpdateRequest, PaymentsConfirmRequest, PaymentsCreateRequest)]
    pub business_sub_label: Option<String>,

    /// Denotes the retry action
    #[schema(value_type = Option<RetryAction>)]
    #[remove_in(PaymentsCreateRequest)]
    pub retry_action: Option<api_enums::RetryAction>,

    /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
    #[schema(value_type = Option<Object>, example = r#"{ "udf1": "some-value", "udf2": "some-value" }"#)]
    pub metadata: Option<serde_json::Value>,

    /// Some connectors like Apple pay, Airwallex and Noon might require some additional information, find specific details in the child attributes below.
    pub connector_metadata: Option<ConnectorMetadata>,

    /// Additional data that might be required by hyperswitch based on the requested features by the merchants.
    #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest, PaymentsConfirmRequest)]
    pub feature_metadata: Option<FeatureMetadata>,

    /// Whether to generate the payment link for this payment or not (if applicable)
    #[schema(default = false, example = true)]
    pub payment_link: Option<bool>,

    #[schema(value_type = Option<PaymentCreatePaymentLinkConfig>)]
    pub payment_link_config: Option<PaymentCreatePaymentLinkConfig>,

    /// Custom payment link config id set at business profile, send only if business_specific_configs is configured
    pub payment_link_config_id: Option<String>,

    /// The business profile to be used for this payment, if not passed the default business profile associated with the merchant account will be used. It is mandatory in case multiple business profiles have been set up.
    #[remove_in(PaymentsUpdateRequest, PaymentsConfirmRequest)]
    #[schema(value_type = Option<String>)]
    pub profile_id: Option<id_type::ProfileId>,

    #[remove_in(PaymentsConfirmRequest)]
    #[schema(value_type = Option<RequestSurchargeDetails>)]
    pub surcharge_details: Option<RequestSurchargeDetails>,

    /// The type of the payment that differentiates between normal and various types of mandate payments
    #[schema(value_type = Option<PaymentType>)]
    pub payment_type: Option<api_enums::PaymentType>,

    ///Request an incremental authorization, i.e., increase the authorized amount on a confirmed payment before you capture it.
    pub request_incremental_authorization: Option<bool>,

    ///Will be used to expire client secret after certain amount of time to be supplied in seconds
    ///(900) for 15 mins
    #[schema(example = 900)]
    pub session_expiry: Option<u32>,

    /// Additional data related to some frm(Fraud Risk Management) connectors
    #[schema(value_type = Option<Object>, example = r#"{ "coverage_request" : "fraud", "fulfillment_method" : "delivery" }"#)]
    pub frm_metadata: Option<pii::SecretSerdeValue>,

    /// Whether to perform external authentication (if applicable)
    #[schema(example = true)]
    pub request_external_three_ds_authentication: Option<bool>,

    /// Details required for recurring payment
    pub recurring_details: Option<RecurringDetails>,

    /// Fee information to be charged on the payment being collected
    #[schema(value_type = Option<SplitPaymentsRequest>)]
    pub split_payments: Option<common_types::payments::SplitPaymentsRequest>,

    /// Optional boolean value to extent authorization period of this payment
    ///
    /// capture method must be manual or manual_multiple
    #[schema(value_type = Option<bool>, default = false)]
    pub request_extended_authorization: Option<RequestExtendedAuthorizationBool>,

    /// Your unique identifier for this payment or order. This ID helps you reconcile payments on your system. If provided, it is passed to the connector if supported.
    #[schema(
        value_type = Option<String>,
        max_length = 255,
        example = "Custom_Order_id_123"
    )]
    pub merchant_order_reference_id: Option<String>,

    /// Whether to calculate tax for this payment intent
    pub skip_external_tax_calculation: Option<bool>,

    /// Choose what kind of sca exemption is required for this payment
    #[schema(value_type = Option<ScaExemptionType>)]
    pub psd2_sca_exemption_type: Option<api_enums::ScaExemptionType>,

    /// Service details for click to pay external authentication
    #[schema(value_type = Option<CtpServiceDetails>)]
    pub ctp_service_details: Option<CtpServiceDetails>,

    /// Indicates if 3ds challenge is forced
    pub force_3ds_challenge: Option<bool>,

    /// Indicates if 3DS method data was successfully completed or not
    pub threeds_method_comp_ind: Option<ThreeDsCompletionIndicator>,

    /// Indicates if the redirection has to open in the iframe
    pub is_iframe_redirection_enabled: Option<bool>,

    /// If enabled, provides whole connector response
    pub all_keys_required: Option<bool>,

    /// Indicates whether the `payment_id` was provided by the merchant
    /// This value is inferred internally based on the request
    #[serde(skip_deserializing)]
    #[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest, PaymentsConfirmRequest)]
    pub is_payment_id_from_merchant: bool,

    /// Indicates how the payment was initiated (e.g., ecommerce, mail, or telephone).
    #[schema(value_type = Option<PaymentChannel>)]
    pub payment_channel: Option<common_enums::PaymentChannel>,

    /// Your tax status for this order or transaction.
    #[schema(value_type = Option<TaxStatus>)]
    pub tax_status: Option<api_enums::TaxStatus>,

    /// Total amount of the discount you have applied to the order or transaction.
    #[schema(value_type = Option<i64>, example = 6540)]
    pub discount_amount: Option<MinorUnit>,

    /// Tax amount applied to shipping charges.
    pub shipping_amount_tax: Option<MinorUnit>,

    /// Duty or customs fee amount for international transactions.
    pub duty_amount: Option<MinorUnit>,

    /// Date the payer placed the order.
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub order_date: Option<PrimitiveDateTime>,

    /// Allow partial authorization for this payment
    pub enable_partial_authorization: Option<bool>,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct CtpServiceDetails {
    /// merchant transaction id
    pub merchant_transaction_id: Option<String>,
    /// network transaction correlation id
    pub correlation_id: Option<String>,
    /// session transaction flow id
    pub x_src_flow_id: Option<String>,
    /// provider Eg: Visa, Mastercard
    #[schema(value_type = Option<CtpServiceProvider>)]
    pub provider: Option<api_enums::CtpServiceProvider>,
    /// Encrypted payload
    #[schema(value_type = Option<String>)]
    pub encrypted_payload: Option<Secret<String>>,
}

impl CtpServiceDetails {
    pub fn is_network_confirmation_call_required(&self) -> bool {
        self.provider == Some(api_enums::CtpServiceProvider::Mastercard)
    }
}

#[cfg(feature = "v1")]
/// Checks if the inner values of two options are equal
/// Returns true if values are not equal, returns false in other cases
fn are_optional_values_invalid<T: PartialEq>(
    first_option: Option<&T>,
    second_option: Option<&T>,
) -> bool {
    match (first_option, second_option) {
        (Some(first_option), Some(second_option)) => first_option != second_option,
        _ => false,
    }
}

#[cfg(feature = "v1")]
impl PaymentsRequest {
    /// Get the customer id
    ///
    /// First check the id for `customer.id`
    /// If not present, check for `customer_id` at the root level
    pub fn get_customer_id(&self) -> Option<&id_type::CustomerId> {
        self.customer_id
            .as_ref()
            .or(self.customer.as_ref().map(|customer| &customer.id))
    }

    pub fn validate_and_get_request_extended_authorization(
        &self,
    ) -> common_utils::errors::CustomResult<Option<RequestExtendedAuthorizationBool>, ValidationError>
    {
        self.request_extended_authorization
            .as_ref()
            .map(|request_extended_authorization| {
                request_extended_authorization.validate_field_and_get(self)
            })
            .transpose()
    }

    /// Checks if the customer details are passed in both places
    /// If they are passed in both places, check for both the values to be equal
    /// Or else, return the field which has inconsistent data
    pub fn validate_customer_details_in_request(&self) -> Option<Vec<&str>> {
        if let Some(CustomerDetails {
            id,
            name,
            email,
            phone,
            phone_country_code,
            ..
        }) = self.customer.as_ref()
        {
            let invalid_fields = [
                are_optional_values_invalid(self.customer_id.as_ref(), Some(id))
                    .then_some("customer_id and customer.id"),
                are_optional_values_invalid(self.email.as_ref(), email.as_ref())
                    .then_some("email and customer.email"),
                are_optional_values_invalid(self.name.as_ref(), name.as_ref())
                    .then_some("name and customer.name"),
                are_optional_values_invalid(self.phone.as_ref(), phone.as_ref())
                    .then_some("phone and customer.phone"),
                are_optional_values_invalid(
                    self.phone_country_code.as_ref(),
                    phone_country_code.as_ref(),
                )
                .then_some("phone_country_code and customer.phone_country_code"),
            ]
            .into_iter()
            .flatten()
            .collect::<Vec<_>>();

            if invalid_fields.is_empty() {
                None
            } else {
                Some(invalid_fields)
            }
        } else {
            None
        }
    }

    pub fn get_feature_metadata_as_value(
        &self,
    ) -> common_utils::errors::CustomResult<
        Option<serde_json::Value>,
        common_utils::errors::ParsingError,
    > {
        self.feature_metadata
            .as_ref()
            .map(Encode::encode_to_value)
            .transpose()
    }

    pub fn get_connector_metadata_as_value(
        &self,
    ) -> common_utils::errors::CustomResult<
        Option<serde_json::Value>,
        common_utils::errors::ParsingError,
    > {
        self.connector_metadata
            .as_ref()
            .map(Encode::encode_to_value)
            .transpose()
    }

    pub fn get_allowed_payment_method_types_as_value(
        &self,
    ) -> common_utils::errors::CustomResult<
        Option<serde_json::Value>,
        common_utils::errors::ParsingError,
    > {
        self.allowed_payment_method_types
            .as_ref()
            .map(Encode::encode_to_value)
            .transpose()
    }

    pub fn get_order_details_as_value(
        &self,
    ) -> common_utils::errors::CustomResult<
        Option<Vec<pii::SecretSerdeValue>>,
        common_utils::errors::ParsingError,
    > {
        self.order_details
            .as_ref()
            .map(|od| {
                od.iter()
                    .map(|order| order.encode_to_value().map(Secret::new))
                    .collect::<Result<Vec<_>, _>>()
            })
            .transpose()
    }
}

#[cfg(feature = "v1")]
#[cfg(test)]
mod payments_request_test {
    use common_utils::generate_customer_id_of_default_length;

    use super::*;

    #[test]
    fn test_valid_case_where_customer_details_are_passed_only_once() {
        let customer_id = generate_customer_id_of_default_length();
        let payments_request = PaymentsRequest {
            customer_id: Some(customer_id),
            ..Default::default()
        };

        assert!(payments_request
            .validate_customer_details_in_request()
            .is_none());
    }

    #[test]
    fn test_valid_case_where_customer_id_is_passed_in_both_places() {
        let customer_id = generate_customer_id_of_default_length();

        let customer_object = CustomerDetails {
            id: customer_id.clone(),
            name: None,
            email: None,
            phone: None,
            phone_country_code: None,
            tax_registration_id: None,
        };

        let payments_request = PaymentsRequest {
            customer_id: Some(customer_id),
            customer: Some(customer_object),
            ..Default::default()
        };

        assert!(payments_request
            .validate_customer_details_in_request()
            .is_none());
    }

    #[test]
    fn test_invalid_case_where_customer_id_is_passed_in_both_places() {
        let customer_id = generate_customer_id_of_default_length();
        let another_customer_id = generate_customer_id_of_default_length();

        let customer_object = CustomerDetails {
            id: customer_id.clone(),
            name: None,
            email: None,
            phone: None,
            phone_country_code: None,
            tax_registration_id: None,
        };

        let payments_request = PaymentsRequest {
            customer_id: Some(another_customer_id),
            customer: Some(customer_object),
            ..Default::default()
        };

        assert_eq!(
            payments_request.validate_customer_details_in_request(),
            Some(vec!["customer_id and customer.id"])
        );
    }
}

/// Details of surcharge applied on this payment, if applicable
#[derive(
    Default, Debug, Clone, serde::Serialize, serde::Deserialize, Copy, ToSchema, PartialEq,
)]
pub struct RequestSurchargeDetails {
    #[schema(value_type = i64, example = 6540)]
    pub surcharge_amount: MinorUnit,
    pub tax_amount: Option<MinorUnit>,
}

// for v2 use the type from common_utils::types
#[cfg(feature = "v1")]
/// Browser information to be used for 3DS 2.0
#[derive(ToSchema, Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct BrowserInformation {
    /// Color depth supported by the browser
    pub color_depth: Option<u8>,

    /// Whether java is enabled in the browser
    pub java_enabled: Option<bool>,

    /// Whether javascript is enabled in the browser
    pub java_script_enabled: Option<bool>,

    /// Language supported
    pub language: Option<String>,

    /// The screen height in pixels
    pub screen_height: Option<u32>,

    /// The screen width in pixels
    pub screen_width: Option<u32>,

    /// Time zone of the client
    pub time_zone: Option<i32>,

    /// Ip address of the client
    #[schema(value_type = Option<String>)]
    pub ip_address: Option<std::net::IpAddr>,

    /// List of headers that are accepted
    #[schema(
        example = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
    )]
    pub accept_header: Option<String>,

    /// User-agent of the browser
    pub user_agent: Option<String>,

    /// The os type of the client device
    pub os_type: Option<String>,

    /// The os version of the client device
    pub os_version: Option<String>,

    /// The device model of the client
    pub device_model: Option<String>,
}

impl RequestSurchargeDetails {
    pub fn is_surcharge_zero(&self) -> bool {
        self.surcharge_amount == MinorUnit::new(0)
            && self.tax_amount.unwrap_or_default() == MinorUnit::new(0)
    }
    pub fn get_total_surcharge_amount(&self) -> MinorUnit {
        self.surcharge_amount + self.tax_amount.unwrap_or_default()
    }

    pub fn get_surcharge_amount(&self) -> MinorUnit {
        self.surcharge_amount
    }

    pub fn get_tax_amount(&self) -> Option<MinorUnit> {
        self.tax_amount
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, Clone, PartialEq, ToSchema, router_derive::PolymorphicSchema)]
pub struct PaymentAttemptResponse {


impl api::RefundSync for Opennode {}


pub struct BillwerkPaymentsResponse {
    state: BillwerkPaymentState,
    handle: String,
    error: Option<String>,
    error_state: Option<String>,
}


pub async fn organization_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<admin::OrganizationCreateRequest>,
) -> HttpResponse {
    let flow = Flow::OrganizationCreate;
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, _, req, _| create_organization(state, req),
        &auth::V2AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl Responder {
    use hyperswitch_domain_models::payments::PaymentIntentData;

    let flow = Flow::PaymentsUpdateIntent;
    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };

    let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId {
        global_payment_id: path.into_inner(),
        payload: json_payload.into_inner(),
    };

    let global_payment_id = internal_payload.global_payment_id.clone();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        internal_payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account.clone(), auth.key_store.clone()),
            ));
            payments::payments_intent_core::<
                api_types::PaymentUpdateIntent,
                payment_types::PaymentsIntentResponse,
                _,
                _,
                PaymentIntentData<api_types::PaymentUpdateIntent>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                payments::operations::PaymentUpdateIntent,
                req.payload,
                global_payment_id.clone(),
                header_payload.clone(),
            )
        },
        &auth::V2ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl Responder {
    let flow = Flow::UpdateDynamicRoutingConfigs;
    let routing_payload_wrapper = routing_types::EliminationRoutingPayloadWrapper {
        updated_config: json_payload.into_inner(),
        algorithm_id: path.clone().algorithm_id,
        profile_id: path.clone().profile_id,
    };
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        routing_payload_wrapper.clone(),
        |state, _, wrapper: routing_types::EliminationRoutingPayloadWrapper, _| async {
            Box::pin(routing::elimination_routing_update_configs(
                state,
                wrapper.updated_config,
                wrapper.algorithm_id,
                wrapper.profile_id,
            ))
            .await
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuthProfileFromRoute {
                profile_id: routing_payload_wrapper.profile_id,
                required_permission: Permission::ProfileRoutingWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Self> {
        generics::generic_insert(conn, self).await
    }


File: crates/diesel_models/src/user_authentication_method.rs
 Public structs: 3

use common_utils::encryption::Encryption;
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use time::PrimitiveDateTime;

use crate::{enums, schema::user_authentication_methods};

#[derive(Clone, Debug, Identifiable, Queryable, Selectable)]
#[diesel(table_name = user_authentication_methods, check_for_backend(diesel::pg::Pg))]
pub struct UserAuthenticationMethod {
    pub id: String,
    pub auth_id: String,
    pub owner_id: String,
    pub owner_type: enums::Owner,
    pub auth_type: enums::UserAuthType,
    pub private_config: Option<Encryption>,
    pub public_config: Option<serde_json::Value>,
    pub allow_signup: bool,
    pub created_at: PrimitiveDateTime,
    pub last_modified_at: PrimitiveDateTime,
    pub email_domain: String,
}

#[derive(router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = user_authentication_methods)]
pub struct UserAuthenticationMethodNew {
    pub id: String,
    pub auth_id: String,
    pub owner_id: String,
    pub owner_type: enums::Owner,
    pub auth_type: enums::UserAuthType,
    pub private_config: Option<Encryption>,
    pub public_config: Option<serde_json::Value>,
    pub allow_signup: bool,
    pub created_at: PrimitiveDateTime,
    pub last_modified_at: PrimitiveDateTime,
    pub email_domain: String,
}

#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = user_authentication_methods)]
pub struct OrgAuthenticationMethodUpdateInternal {
    pub private_config: Option<Encryption>,
    pub public_config: Option<serde_json::Value>,
    pub last_modified_at: PrimitiveDateTime,
    pub email_domain: Option<String>,
}

pub enum UserAuthenticationMethodUpdate {
    UpdateConfig {
        private_config: Option<Encryption>,
        public_config: Option<serde_json::Value>,
    },
    EmailDomain {
        email_domain: String,
    },
}

impl From<UserAuthenticationMethodUpdate> for OrgAuthenticationMethodUpdateInternal {
    fn from(value: UserAuthenticationMethodUpdate) -> Self {
        let last_modified_at = common_utils::date_time::now();
        match value {
            UserAuthenticationMethodUpdate::UpdateConfig {
                private_config,
                public_config,
            } => Self {
                private_config,
                public_config,
                last_modified_at,
                email_domain: None,
            },
            UserAuthenticationMethodUpdate::EmailDomain { email_domain } => Self {
                private_config: None,
                public_config: None,
                last_modified_at,
                email_domain: Some(email_domain),
            },
        }
    }
}


File: crates/common_utils/src/types/primitive_wrappers.rs
 Public functions: 1
 Public structs: 3

pub(crate) mod bool_wrappers {
    use serde::{Deserialize, Serialize};

    /// Bool that represents if Extended Authorization is Applied or not
    #[derive(
        Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, diesel::expression::AsExpression,
    )]
    #[diesel(sql_type = diesel::sql_types::Bool)]
    pub struct ExtendedAuthorizationAppliedBool(bool);
    impl From<bool> for ExtendedAuthorizationAppliedBool {
        fn from(value: bool) -> Self {
            Self(value)
        }
    }
    impl<DB> diesel::serialize::ToSql<diesel::sql_types::Bool, DB> for ExtendedAuthorizationAppliedBool
    where
        DB: diesel::backend::Backend,
        bool: diesel::serialize::ToSql<diesel::sql_types::Bool, DB>,
    {
        fn to_sql<'b>(
            &'b self,
            out: &mut diesel::serialize::Output<'b, '_, DB>,
        ) -> diesel::serialize::Result {
            self.0.to_sql(out)
        }
    }
    impl<DB> diesel::deserialize::FromSql<diesel::sql_types::Bool, DB>
        for ExtendedAuthorizationAppliedBool
    where
        DB: diesel::backend::Backend,
        bool: diesel::deserialize::FromSql<diesel::sql_types::Bool, DB>,
    {
        fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
            bool::from_sql(value).map(Self)
        }
    }

    /// Bool that represents if Extended Authorization is Requested or not
    #[derive(
        Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, diesel::expression::AsExpression,
    )]
    #[diesel(sql_type = diesel::sql_types::Bool)]
    pub struct RequestExtendedAuthorizationBool(bool);
    impl From<bool> for RequestExtendedAuthorizationBool {
        fn from(value: bool) -> Self {
            Self(value)
        }
    }
    impl RequestExtendedAuthorizationBool {
        /// returns the inner bool value
        pub fn is_true(&self) -> bool {
            self.0
        }
    }
    impl<DB> diesel::serialize::ToSql<diesel::sql_types::Bool, DB> for RequestExtendedAuthorizationBool
    where
        DB: diesel::backend::Backend,
        bool: diesel::serialize::ToSql<diesel::sql_types::Bool, DB>,
    {
        fn to_sql<'b>(
            &'b self,
            out: &mut diesel::serialize::Output<'b, '_, DB>,
        ) -> diesel::serialize::Result {
            self.0.to_sql(out)
        }
    }
    impl<DB> diesel::deserialize::FromSql<diesel::sql_types::Bool, DB>
        for RequestExtendedAuthorizationBool
    where
        DB: diesel::backend::Backend,
        bool: diesel::deserialize::FromSql<diesel::sql_types::Bool, DB>,
    {
        fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
            bool::from_sql(value).map(Self)
        }
    }

    /// Bool that represents if Extended Authorization is always Requested or not
    #[derive(
        Clone, Copy, Debug, Eq, PartialEq, diesel::expression::AsExpression, Serialize, Deserialize,
    )]
    #[diesel(sql_type = diesel::sql_types::Bool)]
    pub struct AlwaysRequestExtendedAuthorization(bool);
    impl<DB> diesel::serialize::ToSql<diesel::sql_types::Bool, DB>
        for AlwaysRequestExtendedAuthorization
    where
        DB: diesel::backend::Backend,
        bool: diesel::serialize::ToSql<diesel::sql_types::Bool, DB>,
    {
        fn to_sql<'b>(
            &'b self,
            out: &mut diesel::serialize::Output<'b, '_, DB>,
        ) -> diesel::serialize::Result {
            self.0.to_sql(out)
        }
    }
    impl<DB> diesel::deserialize::FromSql<diesel::sql_types::Bool, DB>
        for AlwaysRequestExtendedAuthorization
    where
        DB: diesel::backend::Backend,
        bool: diesel::deserialize::FromSql<diesel::sql_types::Bool, DB>,
    {
        fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
            bool::from_sql(value).map(Self)
        }
    }
}


pub async fn profile_acquirer_update() { /* … */
}