File: crates/router/src/core/payments/helpers.rs
 Public functions: 139
 Public structs: 9

use std::{borrow::Cow, collections::HashSet, net::IpAddr, str::FromStr};

pub use ::payment_methods::helpers::{
    populate_bin_details_for_payment_method_create,
    validate_payment_method_type_against_payment_method,
};
#[cfg(feature = "v2")]
use api_models::ephemeral_key::ClientSecretResponse;
use api_models::{
    mandates::RecurringDetails,
    payments::{additional_info as payment_additional_types, RequestSurchargeDetails},
};
use base64::Engine;
use common_enums::ConnectorType;
#[cfg(feature = "v2")]
use common_utils::id_type::GenerateId;
use common_utils::{
    crypto::Encryptable,
    ext_traits::{AsyncExt, ByteSliceExt, Encode, ValueExt},
    fp_utils, generate_id,
    id_type::{self},
    new_type::{MaskedIban, MaskedSortCode},
    pii, type_name,
    types::{
        keymanager::{Identifier, KeyManagerState, ToEncryptable},
        MinorUnit,
    },
};
use diesel_models::enums;
// TODO : Evaluate all the helper functions ()
use error_stack::{report, ResultExt};
use futures::future::Either;
#[cfg(feature = "v1")]
use hyperswitch_domain_models::payments::payment_intent::CustomerData;
use hyperswitch_domain_models::{
    mandates::MandateData,
    payment_method_data::{GetPaymentMethodType, PazeWalletData},
    payments::{
        self as domain_payments, payment_attempt::PaymentAttempt,
        payment_intent::PaymentIntentFetchConstraints, PaymentIntent,
    },
    router_data::KlarnaSdkResponse,
};
use hyperswitch_interfaces::integrity::{CheckIntegrity, FlowIntegrity, GetIntegrityObject};
use josekit::jwe;
use masking::{ExposeInterface, PeekInterface, SwitchStrategy};
use num_traits::{FromPrimitive, ToPrimitive};
use openssl::{
    derive::Deriver,
    pkey::PKey,
    symm::{decrypt_aead, Cipher},
};
use rand::Rng;
#[cfg(feature = "v2")]
use redis_interface::errors::RedisError;
use router_env::{instrument, logger, tracing};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use x509_parser::parse_x509_certificate;

use super::{
    operations::{BoxedOperation, Operation, PaymentResponse},
    CustomerDetails, PaymentData,
};
use crate::{
    configs::settings::{ConnectorRequestReferenceIdConfig, TempLockerEnableConfig},
    connector,
    consts::{self, BASE64_ENGINE},
    core::{
        authentication,
        errors::{self, CustomResult, RouterResult, StorageErrorExt},
        mandate::helpers::MandateGenericData,
        payment_methods::{
            self,
            cards::{self},
            network_tokenization, vault,
        },
        payments,
        pm_auth::retrieve_payment_method_from_auth_service,
    },
    db::StorageInterface,
    routes::{metrics, payment_methods as payment_methods_handler, SessionState},
    services,
    types::{
        api::{self, admin, enums as api_enums, MandateValidationFieldsExt},
        domain::{self, types},
        storage::{self, enums as storage_enums, ephemeral_key, CardTokenData},
        transformers::{ForeignFrom, ForeignTryFrom},
        AdditionalMerchantData, AdditionalPaymentMethodConnectorResponse, ErrorResponse,
        MandateReference, MerchantAccountData, MerchantRecipientData, PaymentsResponseData,
        RecipientIdType, RecurringMandatePaymentData, RouterData,
    },
    utils::{
        self,
        crypto::{self, SignMessage},
        OptionExt, StringExt,
    },
};
#[cfg(feature = "v2")]
use crate::{core::admin as core_admin, headers, types::ConnectorAuthType};
#[cfg(feature = "v1")]
use crate::{
    core::payment_methods::cards::create_encrypted_data, types::storage::CustomerUpdate::Update,
};

#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn create_or_update_address_for_payment_by_request(
    session_state: &SessionState,
    req_address: Option<&api::Address>,
    address_id: Option<&str>,
    merchant_id: &id_type::MerchantId,
    customer_id: Option<&id_type::CustomerId>,
    merchant_key_store: &domain::MerchantKeyStore,
    payment_id: &id_type::PaymentId,
    storage_scheme: storage_enums::MerchantStorageScheme,
) -> CustomResult<Option<domain::Address>, errors::ApiErrorResponse> {
    let key = merchant_key_store.key.get_inner().peek();
    let db = &session_state.store;
    let key_manager_state = &session_state.into();
    Ok(match address_id {
        Some(id) => match req_address {
            Some(address) => {
                let encrypted_data = types::crypto_operation(
                    &session_state.into(),
                    type_name!(domain::Address),
                    types::CryptoOperation::BatchEncrypt(
                        domain::FromRequestEncryptableAddress::to_encryptable(
                            domain::FromRequestEncryptableAddress {
                                line1: address.address.as_ref().and_then(|a| a.line1.clone()),
                                line2: address.address.as_ref().and_then(|a| a.line2.clone()),
                                line3: address.address.as_ref().and_then(|a| a.line3.clone()),
                                state: address.address.as_ref().and_then(|a| a.state.clone()),
                                first_name: address
                                    .address
                                    .as_ref()
                                    .and_then(|a| a.first_name.clone()),
                                last_name: address
                                    .address
                                    .as_ref()
                                    .and_then(|a| a.last_name.clone()),
                                zip: address.address.as_ref().and_then(|a| a.zip.clone()),
                                phone_number: address
                                    .phone
                                    .as_ref()
                                    .and_then(|phone| phone.number.clone()),
                                email: address
                                    .email
                                    .as_ref()
                                    .map(|a| a.clone().expose().switch_strategy()),
                                origin_zip: address
                                    .address
                                    .as_ref()
                                    .and_then(|a| a.origin_zip.clone()),
                            },
                        ),
                    ),
                    Identifier::Merchant(merchant_key_store.merchant_id.clone()),
                    key,
                )
                .await
                .and_then(|val| val.try_into_batchoperation())
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed while encrypting address")?;
                let encryptable_address =
                    domain::FromRequestEncryptableAddress::from_encryptable(encrypted_data)
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed while encrypting address")?;
                let address_update = storage::AddressUpdate::Update {
                    city: address
                        .address
                        .as_ref()
                        .and_then(|value| value.city.clone()),
                    country: address.address.as_ref().and_then(|value| value.country),
                    line1: encryptable_address.line1,
                    line2: encryptable_address.line2,
                    line3: encryptable_address.line3,
                    state: encryptable_address.state,
                    zip: encryptable_address.zip,
                    first_name: encryptable_address.first_name,
                    last_name: encryptable_address.last_name,
                    phone_number: encryptable_address.phone_number,
                    country_code: address
                        .phone
                        .as_ref()
                        .and_then(|value| value.country_code.clone()),
                    updated_by: storage_scheme.to_string(),
                    email: encryptable_address.email.map(|email| {
                        let encryptable: Encryptable<masking::Secret<String, pii::EmailStrategy>> =
                            Encryptable::new(
                                email.clone().into_inner().switch_strategy(),
                                email.into_encrypted(),
                            );
                        encryptable
                    }),
                    origin_zip: encryptable_address.origin_zip,
                };
                let address = db
                    .find_address_by_merchant_id_payment_id_address_id(
                        key_manager_state,
                        merchant_id,
                        payment_id,
                        id,
                        merchant_key_store,
                        storage_scheme,
                    )
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Error while fetching address")?;
                Some(
                    db.update_address_for_payments(
                        key_manager_state,
                        address,
                        address_update,
                        payment_id.to_owned(),
                        merchant_key_store,
                        storage_scheme,
                    )
                    .await
                    .map(|payment_address| payment_address.address)
                    .to_not_found_response(errors::ApiErrorResponse::AddressNotFound)?,
                )
            }
            None => Some(
                db.find_address_by_merchant_id_payment_id_address_id(
                    key_manager_state,
                    merchant_id,
                    payment_id,
                    id,
                    merchant_key_store,
                    storage_scheme,
                )
                .await
                .map(|payment_address| payment_address.address),
            )
            .transpose()
            .to_not_found_response(errors::ApiErrorResponse::AddressNotFound)?,
        },
        None => match req_address {
            Some(address) => {
                let address =
                    get_domain_address(session_state, address, merchant_id, key, storage_scheme)
                        .await
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed while encrypting address while insert")?;

                let payment_address = domain::PaymentAddress {
                    address,
                    payment_id: payment_id.clone(),
                    customer_id: customer_id.cloned(),
                };

                Some(
                    db.insert_address_for_payments(
                        key_manager_state,
                        payment_id,
                        payment_address,
                        merchant_key_store,
                        storage_scheme,
                    )
                    .await
                    .map(|payment_address| payment_address.address)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed while inserting new address")?,
                )
            }

            None => None,
        },
    })
}

#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn create_or_find_address_for_payment_by_request(
    state: &SessionState,
    req_address: Option<&api::Address>,
    address_id: Option<&str>,
    merchant_id: &id_type::MerchantId,
    customer_id: Option<&id_type::CustomerId>,
    merchant_key_store: &domain::MerchantKeyStore,
    payment_id: &id_type::PaymentId,
    storage_scheme: storage_enums::MerchantStorageScheme,
) -> CustomResult<Option<domain::Address>, errors::ApiErrorResponse> {
    let key = merchant_key_store.key.get_inner().peek();
    let db = &state.store;
    let key_manager_state = &state.into();
    Ok(match address_id {
        Some(id) => Some(
            db.find_address_by_merchant_id_payment_id_address_id(
                key_manager_state,
                merchant_id,
                payment_id,
                id,
                merchant_key_store,
                storage_scheme,
            )
            .await
            .map(|payment_address| payment_address.address),
        )
        .transpose()
        .to_not_found_response(errors::ApiErrorResponse::AddressNotFound)?,
        None => match req_address {
            Some(address) => {
                // generate a new address here
                let address = get_domain_address(state, address, merchant_id, key, storage_scheme)
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed while encrypting address while insert")?;

                let payment_address = domain::PaymentAddress {
                    address,
                    payment_id: payment_id.clone(),
                    customer_id: customer_id.cloned(),
                };

                Some(
                    db.insert_address_for_payments(
                        key_manager_state,
                        payment_id,
                        payment_address,
                        merchant_key_store,
                        storage_scheme,
                    )
                    .await
                    .map(|payment_address| payment_address.address)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed while inserting new address")?,
                )
            }
            None => None,
        },
    })
}

pub async fn get_domain_address(
    session_state: &SessionState,
    address: &api_models::payments::Address,
    merchant_id: &id_type::MerchantId,
    key: &[u8],
    storage_scheme: enums::MerchantStorageScheme,
) -> CustomResult<domain::Address, common_utils::errors::CryptoError> {
    async {
        let address_details = &address.address.as_ref();
        let encrypted_data = types::crypto_operation(
            &session_state.into(),
            type_name!(domain::Address),
            types::CryptoOperation::BatchEncrypt(
                domain::FromRequestEncryptableAddress::to_encryptable(
                    domain::FromRequestEncryptableAddress {
                        line1: address.address.as_ref().and_then(|a| a.line1.clone()),
                        line2: address.address.as_ref().and_then(|a| a.line2.clone()),
                        line3: address.address.as_ref().and_then(|a| a.line3.clone()),
                        state: address.address.as_ref().and_then(|a| a.state.clone()),
                        first_name: address.address.as_ref().and_then(|a| a.first_name.clone()),
                        last_name: address.address.as_ref().and_then(|a| a.last_name.clone()),
                        zip: address.address.as_ref().and_then(|a| a.zip.clone()),
                        phone_number: address
                            .phone
                            .as_ref()
                            .and_then(|phone| phone.number.clone()),
                        email: address
                            .email
                            .as_ref()
                            .map(|a| a.clone().expose().switch_strategy()),
                        origin_zip: address.address.as_ref().and_then(|a| a.origin_zip.clone()),
                    },
                ),
            ),
            Identifier::Merchant(merchant_id.to_owned()),
            key,
        )
        .await
        .and_then(|val| val.try_into_batchoperation())?;
        let encryptable_address =
            domain::FromRequestEncryptableAddress::from_encryptable(encrypted_data)
                .change_context(common_utils::errors::CryptoError::EncodingFailed)?;
        Ok(domain::Address {
            phone_number: encryptable_address.phone_number,
            country_code: address.phone.as_ref().and_then(|a| a.country_code.clone()),
            merchant_id: merchant_id.to_owned(),
            address_id: generate_id(consts::ID_LENGTH, "add"),
            city: address_details.and_then(|address_details| address_details.city.clone()),
            country: address_details.and_then(|address_details| address_details.country),
            line1: encryptable_address.line1,
            line2: encryptable_address.line2,
            line3: encryptable_address.line3,
            state: encryptable_address.state,
            created_at: common_utils::date_time::now(),
            first_name: encryptable_address.first_name,
            last_name: encryptable_address.last_name,
            modified_at: common_utils::date_time::now(),
            zip: encryptable_address.zip,
            updated_by: storage_scheme.to_string(),
            email: encryptable_address.email.map(|email| {
                let encryptable: Encryptable<masking::Secret<String, pii::EmailStrategy>> =
                    Encryptable::new(
                        email.clone().into_inner().switch_strategy(),
                        email.into_encrypted(),
                    );
                encryptable
            }),
            origin_zip: encryptable_address.origin_zip,
        })
    }
    .await
}

pub async fn get_address_by_id(
    state: &SessionState,
    address_id: Option<String>,
    merchant_key_store: &domain::MerchantKeyStore,
    payment_id: &id_type::PaymentId,
    merchant_id: &id_type::MerchantId,
    storage_scheme: storage_enums::MerchantStorageScheme,
) -> CustomResult<Option<domain::Address>, errors::ApiErrorResponse> {
    match address_id {
        None => Ok(None),
        Some(address_id) => {
            let db = &*state.store;
            Ok(db
                .find_address_by_merchant_id_payment_id_address_id(
                    &state.into(),
                    merchant_id,
                    payment_id,
                    &address_id,
                    merchant_key_store,
                    storage_scheme,
                )
                .await
                .map(|payment_address| payment_address.address)
                .ok())
        }
    }
}

#[cfg(feature = "v1")]
pub async fn get_token_pm_type_mandate_details(
    state: &SessionState,
    request: &api::PaymentsRequest,
    mandate_type: Option<api::MandateTransactionType>,
    merchant_context: &domain::MerchantContext,
    payment_method_id: Option<String>,
    payment_intent_customer_id: Option<&id_type::CustomerId>,
) -> RouterResult<MandateGenericData> {
    let mandate_data = request.mandate_data.clone().map(MandateData::foreign_from);
    let (
        payment_token,
        payment_method,
        payment_method_type,
        mandate_data,
        recurring_payment_data,
        mandate_connector_details,
        payment_method_info,
    ) = match mandate_type {
        Some(api::MandateTransactionType::NewMandateTransaction) => (
            request.payment_token.to_owned(),
            request.payment_method,
            request.payment_method_type,
            mandate_data.clone(),
            None,
            None,
            None,
        ),
        Some(api::MandateTransactionType::RecurringMandateTransaction) => {
            match &request.recurring_details {
                Some(recurring_details) => {
                    match recurring_details {
                        RecurringDetails::NetworkTransactionIdAndCardDetails(_) => {
                            (None, request.payment_method, None, None, None, None, None)
                        }
                        RecurringDetails::ProcessorPaymentToken(processor_payment_token) => {
                            if let Some(mca_id) = &processor_payment_token.merchant_connector_id {
                                let db = &*state.store;
                                let key_manager_state = &state.into();

                                #[cfg(feature = "v1")]
                            let connector_name = db
                                .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
                                    key_manager_state,
                                    merchant_context.get_merchant_account().get_id(),
                                    mca_id,
                                    merchant_context.get_merchant_key_store(),
                                )
                                .await
                                .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                                    id: mca_id.clone().get_string_repr().to_string(),
                                })?.connector_name;

                                #[cfg(feature = "v2")]
                            let connector_name = db
                                .find_merchant_connector_account_by_id(key_manager_state, mca_id, merchant_key_store)
                                .await
                                .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                                    id: mca_id.clone().get_string_repr().to_string(),
                                })?.connector_name;
                                (
                                    None,
                                    request.payment_method,
                                    None,
                                    None,
                                    None,
                                    Some(payments::MandateConnectorDetails {
                                        connector: connector_name,
                                        merchant_connector_id: Some(mca_id.clone()),
                                    }),
                                    None,
                                )
                            } else {
                                (None, request.payment_method, None, None, None, None, None)
                            }
                        }
                        RecurringDetails::MandateId(mandate_id) => {
                            let mandate_generic_data = Box::pin(get_token_for_recurring_mandate(
                                state,
                                request,
                                merchant_context,
                                mandate_id.to_owned(),
                            ))
                            .await?;

                            (
                                mandate_generic_data.token,
                                mandate_generic_data.payment_method,
                                mandate_generic_data
                                    .payment_method_type
                                    .or(request.payment_method_type),
                                None,
                                mandate_generic_data.recurring_mandate_payment_data,
                                mandate_generic_data.mandate_connector,
                                mandate_generic_data.payment_method_info,
                            )
                        }
                        RecurringDetails::PaymentMethodId(payment_method_id) => {
                            let payment_method_info = state
                                .store
                                .find_payment_method(
                                    &(state.into()),
                                    merchant_context.get_merchant_key_store(),
                                    payment_method_id,
                                    merchant_context.get_merchant_account().storage_scheme,
                                )
                                .await
                                .to_not_found_response(
                                    errors::ApiErrorResponse::PaymentMethodNotFound,
                                )?;
                            let customer_id = request
                                .get_customer_id()
                                .get_required_value("customer_id")?;

                            verify_mandate_details_for_recurring_payments(
                                &payment_method_info.merchant_id,
                                merchant_context.get_merchant_account().get_id(),
                                &payment_method_info.customer_id,
                                customer_id,
                            )?;

                            (
                                None,
                                payment_method_info.get_payment_method_type(),
                                payment_method_info.get_payment_method_subtype(),
                                None,
                                None,
                                None,
                                Some(payment_method_info),
                            )
                        }
                    }
                }
                None => {
                    if let Some(mandate_id) = request.mandate_id.clone() {
                        let mandate_generic_data = Box::pin(get_token_for_recurring_mandate(
                            state,
                            request,
                            merchant_context,
                            mandate_id,
                        ))
                        .await?;
                        (
                            mandate_generic_data.token,
                            mandate_generic_data.payment_method,
                            mandate_generic_data
                                .payment_method_type
                                .or(request.payment_method_type),
                            None,
                            mandate_generic_data.recurring_mandate_payment_data,
                            mandate_generic_data.mandate_connector,
                            mandate_generic_data.payment_method_info,
                        )
                    } else if request
                        .payment_method_type
                        .map(|payment_method_type_value| {
                            payment_method_type_value
                                .should_check_for_customer_saved_payment_method_type()
                        })
                        .unwrap_or(false)
                    {
                        let payment_request_customer_id = request.get_customer_id();
                        if let Some(customer_id) =
                            payment_request_customer_id.or(payment_intent_customer_id)
                        {
                            let customer_saved_pm_option = match state
                                .store
                                .find_payment_method_by_customer_id_merchant_id_list(
                                    &(state.into()),
                                    merchant_context.get_merchant_key_store(),
                                    customer_id,
                                    merchant_context.get_merchant_account().get_id(),
                                    None,
                                )
                                .await
                            {
                                Ok(customer_payment_methods) => Ok(customer_payment_methods
                                    .iter()
                                    .find(|payment_method| {
                                        payment_method.get_payment_method_subtype()
                                            == request.payment_method_type
                                    })
                                    .cloned()),
                                Err(error) => {
                                    if error.current_context().is_db_not_found() {
                                        Ok(None)
                                    } else {
                                        Err(error)
                                            .change_context(
                                                errors::ApiErrorResponse::InternalServerError,
                                            )
                                            .attach_printable(
                                                "failed to find payment methods for a customer",
                                            )
                                    }
                                }
                            }?;

                            (
                                None,
                                request.payment_method,
                                request.payment_method_type,
                                None,
                                None,
                                None,
                                customer_saved_pm_option,
                            )
                        } else {
                            (
                                None,
                                request.payment_method,
                                request.payment_method_type,
                                None,
                                None,
                                None,
                                None,
                            )
                        }
                    } else {
                        let payment_method_info = payment_method_id
                            .async_map(|payment_method_id| async move {
                                state
                                    .store
                                    .find_payment_method(
                                        &(state.into()),
                                        merchant_context.get_merchant_key_store(),
                                        &payment_method_id,
                                        merchant_context.get_merchant_account().storage_scheme,
                                    )
                                    .await
                                    .to_not_found_response(
                                        errors::ApiErrorResponse::PaymentMethodNotFound,
                                    )
                            })
                            .await
                            .transpose()?;
                        (
                            request.payment_token.to_owned(),
                            request.payment_method,
                            request.payment_method_type,
                            None,
                            None,
                            None,
                            payment_method_info,
                        )
                    }
                }
            }
        }
        None => {
            let payment_method_info = payment_method_id
                .async_map(|payment_method_id| async move {
                    state
                        .store
                        .find_payment_method(
                            &(state.into()),
                            merchant_context.get_merchant_key_store(),
                            &payment_method_id,
                            merchant_context.get_merchant_account().storage_scheme,
                        )
                        .await
                        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)
                })
                .await
                .transpose()?;
            (
                request.payment_token.to_owned(),
                request.payment_method,
                request.payment_method_type,
                mandate_data,
                None,
                None,
                payment_method_info,
            )
        }
    };
    Ok(MandateGenericData {
        token: payment_token,
        payment_method,
        payment_method_type,
        mandate_data,
        recurring_mandate_payment_data: recurring_payment_data,
        mandate_connector: mandate_connector_details,
        payment_method_info,
    })
}

#[cfg(feature = "v1")]
pub async fn get_token_for_recurring_mandate(
    state: &SessionState,
    req: &api::PaymentsRequest,
    merchant_context: &domain::MerchantContext,
    mandate_id: String,
) -> RouterResult<MandateGenericData> {
    let db = &*state.store;

    let mandate = db
        .find_mandate_by_merchant_id_mandate_id(
            merchant_context.get_merchant_account().get_id(),
            mandate_id.as_str(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
    let key_manager_state: KeyManagerState = state.into();
    let original_payment_intent = mandate
        .original_payment_id
        .as_ref()
        .async_map(|payment_id| async {
            db.find_payment_intent_by_payment_id_merchant_id(
                &key_manager_state,
                payment_id,
                &mandate.merchant_id,
                merchant_context.get_merchant_key_store(),
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
            .map_err(|err| logger::error!(mandate_original_payment_not_found=?err))
            .ok()
        })
        .await
        .flatten();

    let original_payment_attempt = original_payment_intent
        .as_ref()
        .async_map(|payment_intent| async {
            db.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                &payment_intent.payment_id,
                &mandate.merchant_id,
                payment_intent.active_attempt.get_id().as_str(),
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
            .map_err(|err| logger::error!(mandate_original_payment_attempt_not_found=?err))
            .ok()
        })
        .await
        .flatten();

    let original_payment_authorized_amount = original_payment_attempt
        .clone()
        .map(|pa| pa.net_amount.get_total_amount().get_amount_as_i64());
    let original_payment_authorized_currency =
        original_payment_intent.clone().and_then(|pi| pi.currency);
    let customer = req.get_customer_id().get_required_value("customer_id")?;

    let payment_method_id = {
        if &mandate.customer_id != customer {
            Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                message: "customer_id must match mandate customer_id".into()
            }))?
        }
        if mandate.mandate_status != storage_enums::MandateStatus::Active {
            Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                message: "mandate is not active".into()
            }))?
        };
        mandate.payment_method_id.clone()
    };
    verify_mandate_details(
        req.amount.get_required_value("amount")?.into(),
        req.currency.get_required_value("currency")?,
        mandate.clone(),
    )?;

    let payment_method = db
        .find_payment_method(
            &(state.into()),
            merchant_context.get_merchant_key_store(),
            payment_method_id.as_str(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

    let token = Uuid::new_v4().to_string();
    let payment_method_type = payment_method.get_payment_method_subtype();
    let mandate_connector_details = payments::MandateConnectorDetails {
        connector: mandate.connector,
        merchant_connector_id: mandate.merchant_connector_id,
    };

    if let Some(enums::PaymentMethod::Card) = payment_method.get_payment_method_type() {
        if state.conf.locker.locker_enabled {
            let _ = cards::get_lookup_key_from_locker(
                state,
                &token,
                &payment_method,
                merchant_context.get_merchant_key_store(),
            )
            .await?;
        }

        if let Some(payment_method_from_request) = req.payment_method {
            let pm: storage_enums::PaymentMethod = payment_method_from_request;
            if payment_method
                .get_payment_method_type()
                .is_some_and(|payment_method| payment_method != pm)
            {
                Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                    message:
                        "payment method in request does not match previously provided payment \
                            method information"
                            .into()
                }))?
            }
        };

        Ok(MandateGenericData {
            token: Some(token),
            payment_method: payment_method.get_payment_method_type(),
            recurring_mandate_payment_data: Some(RecurringMandatePaymentData {
                payment_method_type,
                original_payment_authorized_amount,
                original_payment_authorized_currency,
                mandate_metadata: None,
            }),
            payment_method_type: payment_method.get_payment_method_subtype(),
            mandate_connector: Some(mandate_connector_details),
            mandate_data: None,
            payment_method_info: Some(payment_method),
        })
    } else {
        Ok(MandateGenericData {
            token: None,
            payment_method: payment_method.get_payment_method_type(),
            recurring_mandate_payment_data: Some(RecurringMandatePaymentData {
                payment_method_type,
                original_payment_authorized_amount,
                original_payment_authorized_currency,
                mandate_metadata: None,
            }),
            payment_method_type: payment_method.get_payment_method_subtype(),
            mandate_connector: Some(mandate_connector_details),
            mandate_data: None,
            payment_method_info: Some(payment_method),
        })
    }
}

#[instrument(skip_all)]
/// Check weather the merchant id in the request
/// and merchant id in the merchant account are same.
pub fn validate_merchant_id(
    merchant_id: &id_type::MerchantId,
    request_merchant_id: Option<&id_type::MerchantId>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    // Get Merchant Id from the merchant
    // or get from merchant account

    let request_merchant_id = request_merchant_id.unwrap_or(merchant_id);

    utils::when(merchant_id.ne(request_merchant_id), || {
        Err(report!(errors::ApiErrorResponse::PreconditionFailed {
            message: format!(
                "Invalid `merchant_id`: {} not found in merchant account",
                request_merchant_id.get_string_repr()
            )
        }))
    })
}

#[instrument(skip_all)]
pub fn validate_request_amount_and_amount_to_capture(
    op_amount: Option<api::Amount>,
    op_amount_to_capture: Option<MinorUnit>,
    surcharge_details: Option<RequestSurchargeDetails>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    match (op_amount, op_amount_to_capture) {
        (None, _) => Ok(()),
        (Some(_amount), None) => Ok(()),
        (Some(amount), Some(amount_to_capture)) => {
            match amount {
                api::Amount::Value(amount_inner) => {
                    // If both amount and amount to capture is present
                    // then amount to be capture should be less than or equal to request amount
                    let total_capturable_amount = MinorUnit::new(amount_inner.get())
                        + surcharge_details
                            .map(|surcharge_details| surcharge_details.get_total_surcharge_amount())
                            .unwrap_or_default();
                    utils::when(!amount_to_capture.le(&total_capturable_amount), || {
                        Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                            message: format!(
                            "amount_to_capture is greater than amount capture_amount: {amount_to_capture:?} request_amount: {amount:?}"
                        )
                        }))
                    })
                }
                api::Amount::Zero => {
                    // If the amount is Null but still amount_to_capture is passed this is invalid and
                    Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                        message: "amount_to_capture should not exist for when amount = 0"
                            .to_string()
                    }))
                }
            }
        }
    }
}

#[cfg(feature = "v1")]
/// if capture method = automatic, amount_to_capture(if provided) must be equal to amount
#[instrument(skip_all)]
pub fn validate_amount_to_capture_and_capture_method(
    payment_attempt: Option<&PaymentAttempt>,
    request: &api_models::payments::PaymentsRequest,
) -> CustomResult<(), errors::ApiErrorResponse> {
    let option_net_amount = hyperswitch_domain_models::payments::payment_attempt::NetAmount::from_payments_request_and_payment_attempt(
        request,
        payment_attempt,
    );
    let capture_method = request
        .capture_method
        .or(payment_attempt
            .map(|payment_attempt| payment_attempt.capture_method.unwrap_or_default()))
        .unwrap_or_default();
    if matches!(
        capture_method,
        api_enums::CaptureMethod::Automatic | api_enums::CaptureMethod::SequentialAutomatic
    ) {
        let total_capturable_amount =
            option_net_amount.map(|net_amount| net_amount.get_total_amount());

        let amount_to_capture = request
            .amount_to_capture
            .or(payment_attempt.and_then(|pa| pa.amount_to_capture));

        if let Some((total_capturable_amount, amount_to_capture)) =
            total_capturable_amount.zip(amount_to_capture)
        {
            utils::when(amount_to_capture != total_capturable_amount, || {
                Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                    message: "amount_to_capture must be equal to total_capturable_amount when capture_method = automatic".into()
                }))
            })
        } else {
            Ok(())
        }
    } else {
        Ok(())
    }
}

#[instrument(skip_all)]
pub fn validate_card_data(
    payment_method_data: Option<api::PaymentMethodData>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    if let Some(api::PaymentMethodData::Card(card)) = payment_method_data {
        let cvc = card.card_cvc.peek().to_string();
        if cvc.len() < 3 || cvc.len() > 4 {
            Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                message: "Invalid card_cvc length".to_string()
            }))?
        }
        let card_cvc =
            cvc.parse::<u16>()
                .change_context(errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "card_cvc",
                })?;
        ::cards::CardSecurityCode::try_from(card_cvc).change_context(
            errors::ApiErrorResponse::PreconditionFailed {
                message: "Invalid Card CVC".to_string(),
            },
        )?;

        validate_card_expiry(&card.card_exp_month, &card.card_exp_year)?;
    }
    Ok(())
}

#[instrument(skip_all)]
pub fn validate_card_expiry(
    card_exp_month: &masking::Secret<String>,
    card_exp_year: &masking::Secret<String>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    let exp_month = card_exp_month
        .peek()
        .to_string()
        .parse::<u8>()
        .change_context(errors::ApiErrorResponse::InvalidDataValue {
            field_name: "card_exp_month",
        })?;
    let month = ::cards::CardExpirationMonth::try_from(exp_month).change_context(
        errors::ApiErrorResponse::PreconditionFailed {
            message: "Invalid Expiry Month".to_string(),
        },
    )?;

    let mut year_str = card_exp_year.peek().to_string();
    if year_str.len() == 2 {
        year_str = format!("20{year_str}");
    }
    let exp_year =
        year_str
            .parse::<u16>()
            .change_context(errors::ApiErrorResponse::InvalidDataValue {
                field_name: "card_exp_year",
            })?;
    let year = ::cards::CardExpirationYear::try_from(exp_year).change_context(
        errors::ApiErrorResponse::PreconditionFailed {
            message: "Invalid Expiry Year".to_string(),
        },
    )?;

    let card_expiration = ::cards::CardExpiration { month, year };
    let is_expired = card_expiration.is_expired().change_context(
        errors::ApiErrorResponse::PreconditionFailed {
            message: "Invalid card data".to_string(),
        },
    )?;
    if is_expired {
        Err(report!(errors::ApiErrorResponse::PreconditionFailed {
            message: "Card Expired".to_string()
        }))?
    }

    Ok(())
}

pub fn infer_payment_type(
    amount: api::Amount,
    mandate_type: Option<&api::MandateTransactionType>,
) -> api_enums::PaymentType {
    match mandate_type {
        Some(api::MandateTransactionType::NewMandateTransaction) => {
            if let api::Amount::Value(_) = amount {
                api_enums::PaymentType::NewMandate
            } else {
                api_enums::PaymentType::SetupMandate
            }
        }

        Some(api::MandateTransactionType::RecurringMandateTransaction) => {
            api_enums::PaymentType::RecurringMandate
        }

        None => api_enums::PaymentType::Normal,
    }
}

pub fn validate_mandate(
    req: impl Into<api::MandateValidationFields>,
    is_confirm_operation: bool,
) -> CustomResult<Option<api::MandateTransactionType>, errors::ApiErrorResponse> {
    let req: api::MandateValidationFields = req.into();
    match req.validate_and_get_mandate_type().change_context(
        errors::ApiErrorResponse::MandateValidationFailed {
            reason: "Expected one out of recurring_details and mandate_data but got both".into(),
        },
    )? {
        Some(api::MandateTransactionType::NewMandateTransaction) => {
            validate_new_mandate_request(req, is_confirm_operation)?;
            Ok(Some(api::MandateTransactionType::NewMandateTransaction))
        }
        Some(api::MandateTransactionType::RecurringMandateTransaction) => {
            validate_recurring_mandate(req)?;
            Ok(Some(
                api::MandateTransactionType::RecurringMandateTransaction,
            ))
        }
        None => Ok(None),
    }
}

pub fn validate_recurring_details_and_token(
    recurring_details: &Option<RecurringDetails>,
    payment_token: &Option<String>,
    mandate_id: &Option<String>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    utils::when(
        recurring_details.is_some() && payment_token.is_some(),
        || {


pub struct CmdLineConf {
    /// Config file.
    /// Application will look for "config/config.toml" if this option isn't specified.
    #[arg(short = 'f', long, value_name = "FILE")]
    pub config_path: Option<PathBuf>,
}


pub struct Gocardless {
    amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
}


pub trait PaymentIntentMetric<T>
where
    T: AnalyticsDataSource + PaymentIntentMetricAnalytics,
{
    async fn load_metrics(
        &self,
        dimensions: &[PaymentIntentDimensions],
        auth: &AuthInfo,
        filters: &PaymentIntentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(PaymentIntentMetricsBucketIdentifier, PaymentIntentMetricRow)>>;
}


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

//! Payouts V2 interface
use hyperswitch_domain_models::{
    router_data_v2::flow_common_types::PayoutFlowData,
    router_flow_types::payouts::{
        PoCancel, PoCreate, PoEligibility, PoFulfill, PoQuote, PoRecipient, PoRecipientAccount,
        PoSync,
    },
    router_request_types::PayoutsData,
    router_response_types::PayoutsResponseData,
};

use super::ConnectorCommon;
use crate::api::ConnectorIntegrationV2;

/// trait PayoutCancelV2
pub trait PayoutCancelV2:
    ConnectorIntegrationV2<PoCancel, PayoutFlowData, PayoutsData, PayoutsResponseData>
{
}

/// trait PayoutCreateV2
pub trait PayoutCreateV2:
    ConnectorIntegrationV2<PoCreate, PayoutFlowData, PayoutsData, PayoutsResponseData>
{
}

/// trait PayoutEligibilityV2
pub trait PayoutEligibilityV2:
    ConnectorIntegrationV2<PoEligibility, PayoutFlowData, PayoutsData, PayoutsResponseData>
{
}

/// trait PayoutFulfillV2
pub trait PayoutFulfillV2:
    ConnectorIntegrationV2<PoFulfill, PayoutFlowData, PayoutsData, PayoutsResponseData>
{
}

/// trait PayoutQuoteV2
pub trait PayoutQuoteV2:
    ConnectorIntegrationV2<PoQuote, PayoutFlowData, PayoutsData, PayoutsResponseData>
{
}

/// trait PayoutRecipientV2
pub trait PayoutRecipientV2:
    ConnectorIntegrationV2<PoRecipient, PayoutFlowData, PayoutsData, PayoutsResponseData>
{
}

/// trait PayoutRecipientAccountV2
pub trait PayoutRecipientAccountV2:
    ConnectorIntegrationV2<PoRecipientAccount, PayoutFlowData, PayoutsData, PayoutsResponseData>
{
}

/// trait PayoutSyncV2
pub trait PayoutSyncV2:
    ConnectorIntegrationV2<PoSync, PayoutFlowData, PayoutsData, PayoutsResponseData>
{
}

#[cfg(feature = "payouts")]
/// trait Payouts
pub trait PayoutsV2:
    ConnectorCommon
    + PayoutCancelV2
    + PayoutCreateV2
    + PayoutEligibilityV2
    + PayoutFulfillV2
    + PayoutQuoteV2
    + PayoutRecipientV2
    + PayoutRecipientAccountV2
    + PayoutSyncV2
{
}

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


pub async fn relay<T: RelayInterface>(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_optional: Option<id_type::ProfileId>,
    req: RelayRequestInner<T>,
) -> RouterResponse<relay_api_models::RelayResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let merchant_id = merchant_context.get_merchant_account().get_id();
    let connector_id = &req.connector_id;

    let profile_id_from_auth_layer = profile_id_optional.get_required_value("ProfileId")?;

    let profile = db
        .find_business_profile_by_merchant_id_profile_id(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            merchant_id,
            &profile_id_from_auth_layer,
        )
        .await
        .change_context(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id_from_auth_layer.get_string_repr().to_owned(),
        })?;

    #[cfg(feature = "v1")]
    let connector_account = db
        .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
            key_manager_state,
            merchant_id,
            connector_id,
            merchant_context.get_merchant_key_store(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: connector_id.get_string_repr().to_string(),
        })?;

    #[cfg(feature = "v2")]
    let connector_account = db
        .find_merchant_connector_account_by_id(
            key_manager_state,
            connector_id,
            merchant_context.get_merchant_key_store(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: connector_id.get_string_repr().to_string(),
        })?;

    T::validate_relay_request(&req.data)?;

    let relay_domain = T::get_domain_models(req, merchant_id, profile.get_id());

    let relay_record = db
        .insert_relay(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            relay_domain,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to insert a relay record in db")?;

    let relay_response = T::process_relay(
        &state,
        merchant_context.clone(),
        connector_account,
        &relay_record,
    )
    .await
    .attach_printable("Failed to process relay")?;

    let relay_update_record = db
        .update_relay(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            relay_record,
            relay_response,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

    let response = T::generate_response(relay_update_record)
        .attach_printable("Failed to generate relay response")?;

    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        response,
    ))
}


pub struct EliminationRoutingEventRequest {
    pub id: String,
    pub params: String,
    pub labels: Vec<String>,
    pub config: Option<EliminationRoutingEventBucketConfig>,
}


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

pub use diesel_models::user::*;


pub struct ContractBasedRoutingPayloadWrapper {
    pub updated_config: ContractBasedRoutingConfig,
    pub algorithm_id: common_utils::id_type::RoutingId,
    pub profile_id: common_utils::id_type::ProfileId,
}


impl State for CardInfoAdd {}


pub async fn list_roles_at_entity_level(
    state: SessionState,
    user_from_token: UserFromToken,
    req: role_api::ListRolesAtEntityLevelRequest,
    check_type: role_api::RoleCheckType,
) -> UserResponse<Vec<role_api::MinimalRoleInfo>> {
    let user_entity_type = user_from_token
        .get_role_info_from_db(&state)
        .await
        .attach_printable("Invalid role_id in JWT")?
        .get_entity_type();

    if req.entity_type > user_entity_type {
        return Err(UserErrors::InvalidRoleOperationWithMessage(
            "User is attempting to request list roles above the current entity level".to_string(),
        )
        .into());
    }
    let mut role_info_vec = PREDEFINED_ROLES.values().cloned().collect::<Vec<_>>();

    let tenant_id = user_from_token
        .tenant_id
        .as_ref()
        .unwrap_or(&state.tenant.tenant_id)
        .to_owned();

    let is_lineage_data_required = false;
    let custom_roles = match req.entity_type {
        EntityType::Tenant | EntityType::Organization => state
            .global_store
            .generic_list_roles_by_entity_type(
                ListRolesByEntityPayload::Organization,
                is_lineage_data_required,
                tenant_id,
                user_from_token.org_id,
            )
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Failed to get roles")?,

        EntityType::Merchant => state
            .global_store
            .generic_list_roles_by_entity_type(
                ListRolesByEntityPayload::Merchant(user_from_token.merchant_id),
                is_lineage_data_required,
                tenant_id,
                user_from_token.org_id,
            )
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Failed to get roles")?,

        EntityType::Profile => state
            .global_store
            .generic_list_roles_by_entity_type(
                ListRolesByEntityPayload::Profile(
                    user_from_token.merchant_id,
                    user_from_token.profile_id,
                ),
                is_lineage_data_required,
                tenant_id,
                user_from_token.org_id,
            )
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Failed to get roles")?,
    };

    role_info_vec.extend(custom_roles.into_iter().map(roles::RoleInfo::from));

    let list_minimal_role_info = role_info_vec
        .into_iter()
        .filter_map(|role_info| {
            let check_type = match check_type {
                role_api::RoleCheckType::Invite => role_info.is_invitable(),
                role_api::RoleCheckType::Update => role_info.is_updatable(),
            };
            if check_type && role_info.get_entity_type() == req.entity_type {
                Some(role_api::MinimalRoleInfo {
                    role_id: role_info.get_role_id().to_string(),
                    role_name: role_info.get_role_name().to_string(),
                })
            } else {
                None
            }
        })
        .collect::<Vec<_>>();

    Ok(ApplicationResponse::Json(list_minimal_role_info))
}


pub struct PaymentLinkConfigRequest {
    pub theme: Option<String>,
    pub logo: Option<String>,
    pub seller_name: Option<String>,
    pub sdk_layout: Option<String>,
    pub display_sdk_only: Option<bool>,
    pub enabled_saved_payment_method: Option<bool>,
    pub hide_card_nickname_field: Option<bool>,
    pub show_card_form_by_default: Option<bool>,
    pub background_image: Option<PaymentLinkBackgroundImageConfig>,
    pub details_layout: Option<common_enums::PaymentLinkDetailsLayout>,
    pub payment_button_text: Option<String>,
    pub custom_message_for_card_terms: Option<String>,
    pub payment_button_colour: Option<String>,
    pub skip_status_screen: Option<bool>,
    pub payment_button_text_colour: Option<String>,
    pub background_colour: Option<String>,
    pub sdk_ui_rules: Option<HashMap<String, HashMap<String, String>>>,
    pub payment_link_ui_rules: Option<HashMap<String, HashMap<String, String>>>,
    pub enable_button_only_on_form_ready: Option<bool>,
    pub payment_form_header_text: Option<String>,
    pub payment_form_label_type: Option<common_enums::PaymentLinkSdkLabelType>,
    pub show_card_terms: Option<common_enums::PaymentLinkShowSdkTerms>,
    pub is_setup_mandate_flow: Option<bool>,
    pub color_icon_card_cvc_error: Option<String>,
}


pub struct DeutschebankAccessTokenResponse {
    pub access_token: Secret<String>,
    pub expires_in: i64,
    pub expires_on: i64,
    pub scope: String,
    pub token_type: String,
}


pub fn get_payment_webhook_statuses(&self) -> Cow<'_, [common_enums::IntentStatus]> {
        self.webhook_details
            .as_ref()
            .and_then(|details| details.payment_statuses_enabled.as_ref())
            .filter(|statuses_vec| !statuses_vec.is_empty())
            .map(|statuses_vec| Cow::Borrowed(statuses_vec.as_slice()))
            .unwrap_or_else(|| {
                Cow::Borrowed(common_types::consts::DEFAULT_PAYMENT_WEBHOOK_TRIGGER_STATUSES)
            })
    }


OpenAPI Block Path: components.schemas.PaymentMethod
{
  "type": "string",
  "description": "Indicates the type of payment method. Eg: 'card', 'wallet', etc.",
  "enum": [
    "card",
    "card_redirect",
    "pay_later",
    "wallet",
    "bank_redirect",
    "bank_transfer",
    "crypto",
    "bank_debit",
    "reward",
    "real_time_payment",
    "upi",
    "voucher",
    "gift_card",
    "open_banking",
    "mobile_payment"
  ]
}


impl Responder {
        let flow = AnalyticsFlow::GenerateAuthenticationReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let user = UserInterface::find_user_by_id(&*state.global_store, &user_id)
                    .await
                    .change_context(AnalyticsError::UnknownError)?;

                let user_email = UserEmail::from_pii_email(user.email)
                    .change_context(AnalyticsError::UnknownError)?
                    .get_secret();
                let org_id = auth.merchant_account.get_org_id();
                let merchant_id = auth.merchant_account.get_id();
                let profile_id = auth
                    .profile_id
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?;
                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(merchant_id.clone()),
                    auth: AuthInfo::ProfileLevel {
                        org_id: org_id.clone(),
                        merchant_id: merchant_id.clone(),
                        profile_ids: vec![profile_id.clone()],
                    },
                    email: user_email,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.authentication_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileReportRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


File: crates/analytics/src/refunds/metrics/sessionized_metrics.rs

mod refund_count;
mod refund_error_message;
mod refund_processed_amount;
mod refund_reason;
mod refund_success_count;
mod refund_success_rate;

pub(super) use refund_count::RefundCount;
pub(super) use refund_error_message::RefundErrorMessage;
pub(super) use refund_processed_amount::RefundProcessedAmount;
pub(super) use refund_reason::RefundReason;
pub(super) use refund_success_count::RefundSuccessCount;
pub(super) use refund_success_rate::RefundSuccessRate;

pub use super::{RefundMetric, RefundMetricAnalytics, RefundMetricRow};


impl api::PaymentSession for Ebanx {}


pub struct AutoSettlement {
    pub auto: bool,
}


impl<T> SingleOrListElement<T> {
    fn get_version_checked(message_version: SemanticVersion, value: T) -> Self {
        if message_version.get_major() >= 2 && message_version.get_minor() >= 3 {
            Self::List(vec![value])
        } else {
            Self::Single(value)
        }
    }
}


impl ConnectorSpecifications for Worldpayvantiv {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&WORLDPAYVANTIV_CONNECTOR_INFO)
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        Some(&*WORLDPAYVANTIV_SUPPORTED_PAYMENT_METHODS)
    }

    fn get_supported_webhook_flows(&self) -> Option<&'static [common_enums::EventClass]> {
        Some(&WORLDPAYVANTIV_SUPPORTED_WEBHOOK_FLOWS)
    }
    #[cfg(feature = "v1")]
    fn generate_connector_request_reference_id(
        &self,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
        is_config_enabled_to_send_payment_id_as_connector_request_id: bool,
    ) -> String {
        if is_config_enabled_to_send_payment_id_as_connector_request_id
            && payment_intent.is_payment_id_from_merchant.unwrap_or(false)
        {
            payment_attempt.payment_id.get_string_repr().to_owned()
        } else {
            let max_payment_reference_id_length =
                worldpayvantiv::worldpayvantiv_constants::MAX_PAYMENT_REFERENCE_ID_LENGTH;
            nanoid::nanoid!(max_payment_reference_id_length)
        }
    }
    #[cfg(feature = "v2")]
    fn generate_connector_request_reference_id(
        &self,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> String {
        if payment_intent.is_payment_id_from_merchant.unwrap_or(false) {
            payment_attempt.payment_id.get_string_repr().to_owned()
        } else {
            connector_utils::generate_12_digit_number().to_string()
        }
    }
}

impl ConnectorSpecifications for for Worldpayvantiv


impl api::ConnectorAccessToken for Forte {}

impl api::ConnectorAccessToken for for Forte


pub struct WellsfargoRefundResponse {
    id: String,
    status: WellsfargoRefundStatus,
    error_information: Option<WellsfargoErrorInformation>,
}


pub fn make_dsl_input(
    payments_dsl_input: &routing::PaymentsDslInput<'_>,
) -> RoutingResult<dsl_inputs::BackendInput> {
    let mandate_data = dsl_inputs::MandateData {
        mandate_acceptance_type: payments_dsl_input.setup_mandate.as_ref().and_then(
            |mandate_data| {
                mandate_data
                    .customer_acceptance
                    .as_ref()
                    .map(|customer_accept| match customer_accept.acceptance_type {
                        common_payments_types::AcceptanceType::Online => {
                            euclid_enums::MandateAcceptanceType::Online
                        }
                        common_payments_types::AcceptanceType::Offline => {
                            euclid_enums::MandateAcceptanceType::Offline
                        }
                    })
            },
        ),
        mandate_type: payments_dsl_input
            .setup_mandate
            .as_ref()
            .and_then(|mandate_data| {
                mandate_data
                    .mandate_type
                    .clone()
                    .map(|mandate_type| match mandate_type {
                        hyperswitch_domain_models::mandates::MandateDataType::SingleUse(_) => {
                            euclid_enums::MandateType::SingleUse
                        }
                        hyperswitch_domain_models::mandates::MandateDataType::MultiUse(_) => {
                            euclid_enums::MandateType::MultiUse
                        }
                    })
            }),
        payment_type: Some(
            if payments_dsl_input
                .recurring_details
                .as_ref()
                .is_some_and(|data| {
                    matches!(
                        data,
                        api_models::mandates::RecurringDetails::ProcessorPaymentToken(_)
                    )
                })
            {
                euclid_enums::PaymentType::PptMandate
            } else {
                payments_dsl_input.setup_mandate.map_or_else(
                    || euclid_enums::PaymentType::NonMandate,
                    |_| euclid_enums::PaymentType::SetupMandate,
                )
            },
        ),
    };
    let payment_method_input = dsl_inputs::PaymentMethodInput {
        payment_method: Some(payments_dsl_input.payment_attempt.payment_method_type),
        payment_method_type: Some(payments_dsl_input.payment_attempt.payment_method_subtype),
        card_network: payments_dsl_input
            .payment_method_data
            .as_ref()
            .and_then(|pm_data| match pm_data {
                domain::PaymentMethodData::Card(card) => card.card_network.clone(),

                _ => None,
            }),
    };

    let payment_input = dsl_inputs::PaymentInput {
        amount: payments_dsl_input
            .payment_attempt
            .amount_details
            .get_net_amount(),
        card_bin: payments_dsl_input.payment_method_data.as_ref().and_then(
            |pm_data| match pm_data {
                domain::PaymentMethodData::Card(card) => Some(card.card_number.get_card_isin()),
                _ => None,
            },
        ),
        currency: payments_dsl_input.currency,
        authentication_type: Some(payments_dsl_input.payment_attempt.authentication_type),
        capture_method: Some(payments_dsl_input.payment_intent.capture_method),
        business_country: None,
        billing_country: payments_dsl_input
            .address
            .get_payment_method_billing()
            .and_then(|billing_address| billing_address.address.as_ref())
            .and_then(|address_details| address_details.country)
            .map(api_enums::Country::from_alpha2),
        business_label: None,
        setup_future_usage: Some(payments_dsl_input.payment_intent.setup_future_usage),
    };

    let metadata = payments_dsl_input
        .payment_intent
        .metadata
        .clone()
        .map(|value| value.parse_value("routing_parameters"))
        .transpose()
        .change_context(errors::RoutingError::MetadataParsingError)
        .attach_printable("Unable to parse routing_parameters from metadata of payment_intent")
        .unwrap_or(None);

    Ok(dsl_inputs::BackendInput {
        metadata,
        payment: payment_input,
        payment_method: payment_method_input,
        mandate: mandate_data,
        acquirer_data: None,
        customer_device_data: None,
        issuer_data: None,
    })
}


pub async fn validate_connectors_in_routing_config(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    merchant_id: &id_type::MerchantId,
    profile_id: &id_type::ProfileId,
    routing_algorithm: &routing_types::StaticRoutingAlgorithm,
) -> RouterResult<()> {
    let all_mcas = state
        .store
        .find_merchant_connector_account_by_merchant_id_and_disabled_list(
            &state.into(),
            merchant_id,
            true,
            key_store,
        )
        .await
        .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: merchant_id.get_string_repr().to_owned(),
        })?;
    let name_mca_id_set = all_mcas
        .iter()
        .filter(|mca| mca.profile_id == *profile_id)
        .map(|mca| (&mca.connector_name, mca.get_id()))
        .collect::<FxHashSet<_>>();

    let name_set = all_mcas
        .iter()
        .filter(|mca| mca.profile_id == *profile_id)
        .map(|mca| &mca.connector_name)
        .collect::<FxHashSet<_>>();

    let connector_choice = |choice: &routing_types::RoutableConnectorChoice| {
        if let Some(ref mca_id) = choice.merchant_connector_id {
            error_stack::ensure!(
                name_mca_id_set.contains(&(&choice.connector.to_string(), mca_id.clone())),
                errors::ApiErrorResponse::InvalidRequestData {
                    message: format!(
                        "connector with name '{}' and merchant connector account id '{:?}' not found for the given profile",
                        choice.connector,
                        mca_id,
                    )
                }
            );
        } else {
            error_stack::ensure!(
                name_set.contains(&choice.connector.to_string()),
                errors::ApiErrorResponse::InvalidRequestData {
                    message: format!(
                        "connector with name '{}' not found for the given profile",
                        choice.connector,
                    )
                }
            );
        }

        Ok(())
    };

    match routing_algorithm {
        routing_types::StaticRoutingAlgorithm::Single(choice) => {
            connector_choice(choice)?;
        }

        routing_types::StaticRoutingAlgorithm::Priority(list) => {
            for choice in list {
                connector_choice(choice)?;
            }
        }

        routing_types::StaticRoutingAlgorithm::VolumeSplit(splits) => {
            for split in splits {
                connector_choice(&split.connector)?;
            }
        }

        routing_types::StaticRoutingAlgorithm::Advanced(program) => {
            let check_connector_selection =
                |selection: &routing_types::ConnectorSelection| -> RouterResult<()> {
                    match selection {
                        routing_types::ConnectorSelection::VolumeSplit(splits) => {
                            for split in splits {
                                connector_choice(&split.connector)?;
                            }
                        }

                        routing_types::ConnectorSelection::Priority(list) => {
                            for choice in list {
                                connector_choice(choice)?;
                            }
                        }
                    }

                    Ok(())
                };

            check_connector_selection(&program.default_selection)?;

            for rule in &program.rules {
                check_connector_selection(&rule.connector_selection)?;
            }
        }

        routing_types::StaticRoutingAlgorithm::ThreeDsDecisionRule(_) => {
            Err(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Invalid routing algorithm three_ds decision rule received")?
        }
    }

    Ok(())
}


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


impl ConnectorSpecifications for Mifinity {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&*MIFINITY_CONNECTOR_INFO)
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        Some(&*MIFINITY_SUPPORTED_PAYMENT_METHODS)
    }

    fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
        Some(&*MIFINITY_SUPPORTED_WEBHOOK_FLOWS)
    }
}

impl ConnectorSpecifications for for Mifinity


impl Responder {
    let payload = json_payload.into_inner();
    let flow = Flow::CardsInfoCreate;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload,
        |state, _, payload, _| cards_info::create_card_info(state, payload),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl api::PaymentVoid for Aci {}


impl std::fmt::Display for RoutableConnectorChoice {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let base = self.connector.to_string();
        if let Some(mca_id) = &self.merchant_connector_id {
            return write!(f, "{}:{}", base, mca_id.get_string_repr());
        }
        write!(f, "{base}")
    }
}

impl std::fmt::Display for for RoutableConnectorChoice


OpenAPI Block Path: components.schemas.GooglePayRedirectData
{
  "type": "object"
}


pub fn get(&self, idx: K) -> Option<&V> {
        self.data.get(idx.get_id())
    }


impl api::PaymentCapture for Razorpay {}

impl api::PaymentCapture for for Razorpay


pub struct RevenueRecoveryAttemptData {
    /// transaction amount against invoice, accepted in minor unit.
    pub amount: util_types::MinorUnit,
    /// currency of the transaction
    pub currency: common_enums::Currency,
    /// merchant reference id at billing connector. ex: invoice_id
    pub merchant_reference_id: id_type::PaymentReferenceId,
    /// transaction id reference at payment connector
    pub connector_transaction_id: Option<util_types::ConnectorTransactionId>,
    /// error code sent by billing connector.
    pub error_code: Option<String>,
    /// error message sent by billing connector.
    pub error_message: Option<String>,
    /// mandate token at payment processor end.
    pub processor_payment_method_token: String,
    /// customer id at payment connector for which mandate is attached.
    pub connector_customer_id: String,
    /// Payment gateway identifier id at billing processor.
    pub connector_account_reference_id: String,
    /// timestamp at which transaction has been created at billing connector
    pub transaction_created_at: Option<PrimitiveDateTime>,
    /// transaction status at billing connector equivalent to payment attempt status.
    pub status: common_enums::AttemptStatus,
    /// payment method of payment attempt.
    pub payment_method_type: common_enums::PaymentMethod,
    /// payment method sub type of the payment attempt.
    pub payment_method_sub_type: common_enums::PaymentMethodType,
    /// This field can be returned for both approved and refused Mastercard payments.
    /// This code provides additional information about the type of transaction or the reason why the payment failed.
    /// If the payment failed, the network advice code gives guidance on if and when you can retry the payment.
    pub network_advice_code: Option<String>,
    /// For card errors resulting from a card issuer decline, a brand specific 2, 3, or 4 digit code which indicates the reason the authorization failed.
    pub network_decline_code: Option<String>,
    /// A string indicating how to proceed with an network error if payment gateway provide one. This is used to understand the network error code better.
    pub network_error_message: Option<String>,
    /// Number of attempts made for an invoice
    pub retry_count: Option<u16>,
    /// Time when next invoice will be generated which will be equal to the end time of the current invoice
    pub invoice_next_billing_time: Option<PrimitiveDateTime>,
    /// Time at which the invoice created
    pub invoice_billing_started_at_time: Option<PrimitiveDateTime>,
    /// stripe specific id used to validate duplicate attempts in revenue recovery flow
    pub charge_id: Option<String>,
    /// Additional card details
    pub card_info: api_payments::AdditionalCardInfo,
}


pub struct PowertranzBaseRequest {
    transaction_identifier: String,
    total_amount: Option<f64>,
    refund: Option<bool>,
}


File: crates/euclid/src/frontend/dir/lowering.rs
 Public functions: 1

//! Analysis of the lowering logic for the DIR
//!
//! Consists of certain functions that supports the lowering logic from DIR to VIR.
//! These includes the lowering of the DIR program and vector of rules , and the lowering of ifstatements
//! ,and comparisonsLogic and also the lowering of the enums of value variants from DIR to VIR.
use super::enums;
use crate::{
    dssa::types::{AnalysisError, AnalysisErrorType},
    enums as global_enums,
    frontend::{dir, vir},
    types::EuclidValue,
};

impl From<enums::CardType> for global_enums::PaymentMethodType {
    fn from(value: enums::CardType) -> Self {
        match value {
            enums::CardType::Credit => Self::Credit,
            enums::CardType::Debit => Self::Debit,
            #[cfg(feature = "v2")]
            enums::CardType::Card => Self::Card,
        }
    }
}

impl From<enums::PayLaterType> for global_enums::PaymentMethodType {
    fn from(value: enums::PayLaterType) -> Self {
        match value {
            enums::PayLaterType::Affirm => Self::Affirm,
            enums::PayLaterType::AfterpayClearpay => Self::AfterpayClearpay,
            enums::PayLaterType::Alma => Self::Alma,
            enums::PayLaterType::Flexiti => Self::Flexiti,
            enums::PayLaterType::Klarna => Self::Klarna,
            enums::PayLaterType::PayBright => Self::PayBright,
            enums::PayLaterType::Walley => Self::Walley,
            enums::PayLaterType::Atome => Self::Atome,
            enums::PayLaterType::Breadpay => Self::Breadpay,
        }
    }
}

impl From<enums::WalletType> for global_enums::PaymentMethodType {
    fn from(value: enums::WalletType) -> Self {
        match value {
            enums::WalletType::Bluecode => Self::Bluecode,
            enums::WalletType::GooglePay => Self::GooglePay,
            enums::WalletType::AmazonPay => Self::AmazonPay,
            enums::WalletType::Skrill => Self::Skrill,
            enums::WalletType::Paysera => Self::Paysera,
            enums::WalletType::ApplePay => Self::ApplePay,
            enums::WalletType::Paypal => Self::Paypal,
            enums::WalletType::AliPay => Self::AliPay,
            enums::WalletType::AliPayHk => Self::AliPayHk,
            enums::WalletType::MbWay => Self::MbWay,
            enums::WalletType::MobilePay => Self::MobilePay,
            enums::WalletType::WeChatPay => Self::WeChatPay,
            enums::WalletType::SamsungPay => Self::SamsungPay,
            enums::WalletType::GoPay => Self::GoPay,
            enums::WalletType::KakaoPay => Self::KakaoPay,
            enums::WalletType::Twint => Self::Twint,
            enums::WalletType::Gcash => Self::Gcash,
            enums::WalletType::Vipps => Self::Vipps,
            enums::WalletType::Momo => Self::Momo,
            enums::WalletType::Dana => Self::Dana,
            enums::WalletType::TouchNGo => Self::TouchNGo,
            enums::WalletType::Swish => Self::Swish,
            enums::WalletType::Cashapp => Self::Cashapp,
            enums::WalletType::Venmo => Self::Venmo,
            enums::WalletType::Mifinity => Self::Mifinity,
            enums::WalletType::Paze => Self::Paze,
            enums::WalletType::RevolutPay => Self::RevolutPay,
        }
    }
}

impl From<enums::BankDebitType> for global_enums::PaymentMethodType {
    fn from(value: enums::BankDebitType) -> Self {
        match value {
            enums::BankDebitType::Ach => Self::Ach,
            enums::BankDebitType::Sepa => Self::Sepa,
            enums::BankDebitType::Bacs => Self::Bacs,
            enums::BankDebitType::Becs => Self::Becs,
        }
    }
}
impl From<enums::UpiType> for global_enums::PaymentMethodType {
    fn from(value: enums::UpiType) -> Self {
        match value {
            enums::UpiType::UpiCollect => Self::UpiCollect,
            enums::UpiType::UpiIntent => Self::UpiIntent,
        }
    }
}

impl From<enums::VoucherType> for global_enums::PaymentMethodType {
    fn from(value: enums::VoucherType) -> Self {
        match value {
            enums::VoucherType::Boleto => Self::Boleto,
            enums::VoucherType::Efecty => Self::Efecty,
            enums::VoucherType::PagoEfectivo => Self::PagoEfectivo,
            enums::VoucherType::RedCompra => Self::RedCompra,
            enums::VoucherType::RedPagos => Self::RedPagos,
            enums::VoucherType::Alfamart => Self::Alfamart,
            enums::VoucherType::Indomaret => Self::Indomaret,
            enums::VoucherType::SevenEleven => Self::SevenEleven,
            enums::VoucherType::Lawson => Self::Lawson,
            enums::VoucherType::MiniStop => Self::MiniStop,
            enums::VoucherType::FamilyMart => Self::FamilyMart,
            enums::VoucherType::Seicomart => Self::Seicomart,
            enums::VoucherType::PayEasy => Self::PayEasy,
            enums::VoucherType::Oxxo => Self::Oxxo,
        }
    }
}

impl From<enums::BankTransferType> for global_enums::PaymentMethodType {
    fn from(value: enums::BankTransferType) -> Self {
        match value {
            enums::BankTransferType::Multibanco => Self::Multibanco,
            enums::BankTransferType::Pix => Self::Pix,
            enums::BankTransferType::Pse => Self::Pse,
            enums::BankTransferType::Ach => Self::Ach,
            enums::BankTransferType::SepaBankTransfer => Self::Sepa,
            enums::BankTransferType::Bacs => Self::Bacs,
            enums::BankTransferType::BcaBankTransfer => Self::BcaBankTransfer,
            enums::BankTransferType::BniVa => Self::BniVa,
            enums::BankTransferType::BriVa => Self::BriVa,
            enums::BankTransferType::CimbVa => Self::CimbVa,
            enums::BankTransferType::DanamonVa => Self::DanamonVa,
            enums::BankTransferType::MandiriVa => Self::MandiriVa,
            enums::BankTransferType::PermataBankTransfer => Self::PermataBankTransfer,
            enums::BankTransferType::LocalBankTransfer => Self::LocalBankTransfer,
            enums::BankTransferType::InstantBankTransfer => Self::InstantBankTransfer,
            enums::BankTransferType::InstantBankTransferFinland => Self::InstantBankTransferFinland,
            enums::BankTransferType::InstantBankTransferPoland => Self::InstantBankTransferPoland,
            enums::BankTransferType::IndonesianBankTransfer => Self::IndonesianBankTransfer,
        }
    }
}

impl From<enums::GiftCardType> for global_enums::PaymentMethodType {
    fn from(value: enums::GiftCardType) -> Self {
        match value {
            enums::GiftCardType::PaySafeCard => Self::PaySafeCard,
            enums::GiftCardType::Givex => Self::Givex,
            enums::GiftCardType::BhnCardNetwork => Self::BhnCardNetwork,
        }
    }
}

impl From<enums::CardRedirectType> for global_enums::PaymentMethodType {
    fn from(value: enums::CardRedirectType) -> Self {
        match value {
            enums::CardRedirectType::Benefit => Self::Benefit,
            enums::CardRedirectType::Knet => Self::Knet,
            enums::CardRedirectType::MomoAtm => Self::MomoAtm,
            enums::CardRedirectType::CardRedirect => Self::CardRedirect,
        }
    }
}

impl From<enums::MobilePaymentType> for global_enums::PaymentMethodType {
    fn from(value: enums::MobilePaymentType) -> Self {
        match value {
            enums::MobilePaymentType::DirectCarrierBilling => Self::DirectCarrierBilling,
        }
    }
}

impl From<enums::BankRedirectType> for global_enums::PaymentMethodType {
    fn from(value: enums::BankRedirectType) -> Self {
        match value {
            enums::BankRedirectType::Bizum => Self::Bizum,
            enums::BankRedirectType::Giropay => Self::Giropay,
            enums::BankRedirectType::Ideal => Self::Ideal,
            enums::BankRedirectType::Sofort => Self::Sofort,
            enums::BankRedirectType::Eft => Self::Eft,
            enums::BankRedirectType::Eps => Self::Eps,
            enums::BankRedirectType::BancontactCard => Self::BancontactCard,
            enums::BankRedirectType::Blik => Self::Blik,
            enums::BankRedirectType::Interac => Self::Interac,
            enums::BankRedirectType::LocalBankRedirect => Self::LocalBankRedirect,
            enums::BankRedirectType::OnlineBankingCzechRepublic => Self::OnlineBankingCzechRepublic,
            enums::BankRedirectType::OnlineBankingFinland => Self::OnlineBankingFinland,
            enums::BankRedirectType::OnlineBankingPoland => Self::OnlineBankingPoland,
            enums::BankRedirectType::OnlineBankingSlovakia => Self::OnlineBankingSlovakia,
            enums::BankRedirectType::OnlineBankingFpx => Self::OnlineBankingFpx,
            enums::BankRedirectType::OnlineBankingThailand => Self::OnlineBankingThailand,
            enums::BankRedirectType::OpenBankingUk => Self::OpenBankingUk,
            enums::BankRedirectType::Przelewy24 => Self::Przelewy24,
            enums::BankRedirectType::Trustly => Self::Trustly,
        }
    }
}

impl From<enums::OpenBankingType> for global_enums::PaymentMethodType {
    fn from(value: enums::OpenBankingType) -> Self {
        match value {
            enums::OpenBankingType::OpenBankingPIS => Self::OpenBankingPIS,
        }
    }
}

impl From<enums::CryptoType> for global_enums::PaymentMethodType {
    fn from(value: enums::CryptoType) -> Self {
        match value {
            enums::CryptoType::CryptoCurrency => Self::CryptoCurrency,
        }
    }
}

impl From<enums::RewardType> for global_enums::PaymentMethodType {
    fn from(value: enums::RewardType) -> Self {
        match value {
            enums::RewardType::ClassicReward => Self::ClassicReward,
            enums::RewardType::Evoucher => Self::Evoucher,
        }
    }
}

impl From<enums::RealTimePaymentType> for global_enums::PaymentMethodType {
    fn from(value: enums::RealTimePaymentType) -> Self {
        match value {
            enums::RealTimePaymentType::Fps => Self::Fps,
            enums::RealTimePaymentType::DuitNow => Self::DuitNow,
            enums::RealTimePaymentType::PromptPay => Self::PromptPay,
            enums::RealTimePaymentType::VietQr => Self::VietQr,
        }
    }
}

/// Analyses of the lowering of the DirValues to EuclidValues .
///
/// For example,
/// ```notrust
/// DirValue::PaymentMethod::Cards -> EuclidValue::PaymentMethod::Cards
/// ```notrust
/// This is a function that lowers the Values of the DIR variants into the Value of the VIR variants.
/// The function for each DirValue variant creates a corresponding EuclidValue variants and if there
/// lacks any direct mapping, it return an Error.
fn lower_value(dir_value: dir::DirValue) -> Result<EuclidValue, AnalysisErrorType> {
    Ok(match dir_value {
        dir::DirValue::PaymentMethod(pm) => EuclidValue::PaymentMethod(pm),
        dir::DirValue::CardBin(ci) => EuclidValue::CardBin(ci),
        dir::DirValue::CardType(ct) => EuclidValue::PaymentMethodType(ct.into()),
        dir::DirValue::CardNetwork(cn) => EuclidValue::CardNetwork(cn),
        dir::DirValue::MetaData(md) => EuclidValue::Metadata(md),
        dir::DirValue::PayLaterType(plt) => EuclidValue::PaymentMethodType(plt.into()),
        dir::DirValue::WalletType(wt) => EuclidValue::PaymentMethodType(wt.into()),
        dir::DirValue::UpiType(ut) => EuclidValue::PaymentMethodType(ut.into()),
        dir::DirValue::VoucherType(vt) => EuclidValue::PaymentMethodType(vt.into()),
        dir::DirValue::BankTransferType(btt) => EuclidValue::PaymentMethodType(btt.into()),
        dir::DirValue::GiftCardType(gct) => EuclidValue::PaymentMethodType(gct.into()),
        dir::DirValue::CardRedirectType(crt) => EuclidValue::PaymentMethodType(crt.into()),
        dir::DirValue::BankRedirectType(brt) => EuclidValue::PaymentMethodType(brt.into()),
        dir::DirValue::CryptoType(ct) => EuclidValue::PaymentMethodType(ct.into()),
        dir::DirValue::RealTimePaymentType(rtpt) => EuclidValue::PaymentMethodType(rtpt.into()),
        dir::DirValue::AuthenticationType(at) => EuclidValue::AuthenticationType(at),
        dir::DirValue::CaptureMethod(cm) => EuclidValue::CaptureMethod(cm),
        dir::DirValue::PaymentAmount(pa) => EuclidValue::PaymentAmount(pa),
        dir::DirValue::PaymentCurrency(pc) => EuclidValue::PaymentCurrency(pc),
        dir::DirValue::BusinessCountry(buc) => EuclidValue::BusinessCountry(buc),
        dir::DirValue::BillingCountry(bic) => EuclidValue::BillingCountry(bic),
        dir::DirValue::MandateAcceptanceType(mat) => EuclidValue::MandateAcceptanceType(mat),
        dir::DirValue::MandateType(mt) => EuclidValue::MandateType(mt),
        dir::DirValue::PaymentType(pt) => EuclidValue::PaymentType(pt),
        dir::DirValue::Connector(_) => Err(AnalysisErrorType::UnsupportedProgramKey(
            dir::DirKeyKind::Connector,
        ))?,
        dir::DirValue::BankDebitType(bdt) => EuclidValue::PaymentMethodType(bdt.into()),
        dir::DirValue::RewardType(rt) => EuclidValue::PaymentMethodType(rt.into()),
        dir::DirValue::BusinessLabel(bl) => EuclidValue::BusinessLabel(bl),
        dir::DirValue::SetupFutureUsage(sfu) => EuclidValue::SetupFutureUsage(sfu),
        dir::DirValue::OpenBankingType(ob) => EuclidValue::PaymentMethodType(ob.into()),
        dir::DirValue::MobilePaymentType(mp) => EuclidValue::PaymentMethodType(mp.into()),
        dir::DirValue::IssuerName(str_value) => EuclidValue::IssuerName(str_value),
        dir::DirValue::IssuerCountry(country) => EuclidValue::IssuerCountry(country),
        dir::DirValue::CustomerDevicePlatform(customer_device_platform) => {
            EuclidValue::CustomerDevicePlatform(customer_device_platform)
        }
        dir::DirValue::CustomerDeviceType(customer_device_type) => {
            EuclidValue::CustomerDeviceType(customer_device_type)
        }
        dir::DirValue::CustomerDeviceDisplaySize(customer_device_display_size) => {
            EuclidValue::CustomerDeviceDisplaySize(customer_device_display_size)
        }
        dir::DirValue::AcquirerCountry(country) => EuclidValue::AcquirerCountry(country),
        dir::DirValue::AcquirerFraudRate(num_value) => EuclidValue::AcquirerFraudRate(num_value),
    })
}

fn lower_comparison(
    dir_comparison: dir::DirComparison,
) -> Result<vir::ValuedComparison, AnalysisErrorType> {
    Ok(vir::ValuedComparison {
        values: dir_comparison
            .values
            .into_iter()
            .map(lower_value)
            .collect::<Result<_, _>>()?,
        logic: match dir_comparison.logic {
            dir::DirComparisonLogic::NegativeConjunction => {
                vir::ValuedComparisonLogic::NegativeConjunction
            }
            dir::DirComparisonLogic::PositiveDisjunction => {
                vir::ValuedComparisonLogic::PositiveDisjunction
            }
        },
        metadata: dir_comparison.metadata,
    })
}

fn lower_if_statement(
    dir_if_statement: dir::DirIfStatement,
) -> Result<vir::ValuedIfStatement, AnalysisErrorType> {
    Ok(vir::ValuedIfStatement {
        condition: dir_if_statement
            .condition
            .into_iter()
            .map(lower_comparison)
            .collect::<Result<_, _>>()?,
        nested: dir_if_statement
            .nested
            .map(|v| {
                v.into_iter()
                    .map(lower_if_statement)
                    .collect::<Result<_, _>>()
            })
            .transpose()?,
    })
}

fn lower_rule<O>(dir_rule: dir::DirRule<O>) -> Result<vir::ValuedRule<O>, AnalysisErrorType> {
    Ok(vir::ValuedRule {
        name: dir_rule.name,
        connector_selection: dir_rule.connector_selection,
        statements: dir_rule
            .statements
            .into_iter()
            .map(lower_if_statement)
            .collect::<Result<_, _>>()?,
    })
}

pub fn lower_program<O>(
    dir_program: dir::DirProgram<O>,
) -> Result<vir::ValuedProgram<O>, AnalysisError> {
    Ok(vir::ValuedProgram {
        default_selection: dir_program.default_selection,
        rules: dir_program
            .rules
            .into_iter()
            .map(lower_rule)
            .collect::<Result<_, _>>()
            .map_err(|e| AnalysisError {
                error_type: e,
                metadata: Default::default(),
            })?,
        metadata: dir_program.metadata,
    })
}


pub struct RoutingAlgorithmRecord {
    pub id: id_type::RoutingId,
    pub name: String,
    pub description: Option<String>,
    pub created_by: id_type::ProfileId,
    pub algorithm_data: StaticRoutingAlgorithm,
    pub algorithm_for: TransactionType,
    pub metadata: Option<RoutingMetadata>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
}


pub trait MandateBehaviour {
    fn get_amount(&self) -> i64;
    fn get_setup_future_usage(&self) -> Option<diesel_models::enums::FutureUsage>;
    fn get_mandate_id(&self) -> Option<&payments::MandateIds>;
    fn set_mandate_id(&mut self, new_mandate_id: Option<payments::MandateIds>);
    fn get_payment_method_data(&self) -> domain::payments::PaymentMethodData;
    fn get_setup_mandate_details(
        &self,
    ) -> Option<&hyperswitch_domain_models::mandates::MandateData>;
    fn get_customer_acceptance(&self) -> Option<common_payments_types::CustomerAcceptance>;
}


pub struct MigrateCardResponse {
    pub status_message: String,
    pub status_code: String,
    pub customers_moved: usize,
    pub cards_moved: usize,
}


impl api::PaymentSync for Sift {}