File: crates/router_env/tests/test_module.rs
 Public functions: 2

use router_env as logger;

#[tracing::instrument(skip_all)]
pub async fn fn_with_colon(val: i32) {
    let a = 13;
    let b = 31;

    logger::log!(
        logger::Level::WARN,
        ?a,
        ?b,
        tag = ?logger::Tag::ApiIncomingRequest,
        category = ?logger::Category::Api,
        flow = "some_flow",
        session_id = "some_session",
        answer = 13,
        message2 = "yyy",
        message = "Experiment",
        val,
    );

    fn_without_colon(131).await;
}

#[tracing::instrument(fields(val3 = "abc"), skip_all)]
pub async fn fn_without_colon(val: i32) {
    let a = 13;
    let b = 31;

    // trace_macros!(true);
    logger::log!(
        logger::Level::INFO,
        ?a,
        ?b,
        tag = ?logger::Tag::ApiIncomingRequest,
        category = ?logger::Category::Api,
        flow = "some_flow",
        session_id = "some_session",
        answer = 13,
        message2 = "yyy",
        message = "Experiment",
        val,
    );
    // trace_macros!(false);
}


pub struct PoEligibility;


pub async fn get_single_org_id(
    state: &SessionState,
    user_role: &UserRole,
) -> UserResult<id_type::OrganizationId> {
    let (_, entity_type) = user_role
        .get_entity_id_and_type()
        .ok_or(UserErrors::InternalServerError)?;
    match entity_type {
        EntityType::Tenant => Ok(state
            .store
            .list_merchant_and_org_ids(&state.into(), 1, None)
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Failed to get merchants list for org")?
            .pop()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("No merchants to get merchant or org id")?
            .1),
        EntityType::Organization | EntityType::Merchant | EntityType::Profile => user_role
            .org_id
            .clone()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("Org_id not found"),
    }
}


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

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

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

impl ConnectorSpecifications for for Paysafe


Documentation: api-reference/v1/routing/routing--retrieve-config.mdx
# Type: Doc File

---
openapi: get /routing/active
---


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


pub struct ProxyRequest {
    /// The request body that needs to be forwarded
    pub request_body: Value,
    /// The destination URL where the request needs to be forwarded
    #[schema(value_type = String, example = "https://api.example.com/endpoint")]
    pub destination_url: url::Url,
    /// The headers that need to be forwarded
    #[schema(value_type = Object, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)]
    pub headers: Headers,
    /// The method that needs to be used for the request
    #[schema(value_type = Method, example = "Post")]
    pub method: Method,
    /// The vault token that is used to fetch sensitive data from the vault
    pub token: String,
    /// The type of token that is used to fetch sensitive data from the vault
    #[schema(value_type = TokenType, example = "payment_method_id")]
    pub token_type: TokenType,
}


impl<T> DatabaseStore for KVRouterStore<T>
where
    RouterStore<T>: DatabaseStore,
    T: DatabaseStore,
{
    type Config = (RouterStore<T>, String, u8, u32, Option<bool>);
    async fn new(
        config: Self::Config,
        tenant_config: &dyn TenantConfig,
        _test_transaction: bool,
    ) -> StorageResult<Self> {
        let (router_store, _, drainer_num_partitions, ttl_for_kv, soft_kill_mode) = config;
        let drainer_stream_name = format!("{}_{}", tenant_config.get_schema(), config.1);
        Ok(Self::from_store(
            router_store,
            drainer_stream_name,
            drainer_num_partitions,
            ttl_for_kv,
            soft_kill_mode,
        ))
    }
    fn get_master_pool(&self) -> &PgPool {
        self.router_store.get_master_pool()
    }
    fn get_replica_pool(&self) -> &PgPool {
        self.router_store.get_replica_pool()
    }

    fn get_accounts_master_pool(&self) -> &PgPool {
        self.router_store.get_accounts_master_pool()
    }

    fn get_accounts_replica_pool(&self) -> &PgPool {
        self.router_store.get_accounts_replica_pool()
    }
}

impl DatabaseStore for for KVRouterStore


impl BusinessPaymentLinkConfig {
    pub fn validate(&self) -> Result<(), &str> {
        let host_domain_valid = self
            .domain_name
            .clone()
            .map(|host_domain| link_utils::validate_strict_domain(&host_domain))
            .unwrap_or(true);
        if !host_domain_valid {
            return Err("Invalid host domain name received in payment_link_config");
        }

        let are_allowed_domains_valid = self
            .allowed_domains
            .clone()
            .map(|allowed_domains| {
                allowed_domains
                    .iter()
                    .all(|allowed_domain| link_utils::validate_wildcard_domain(allowed_domain))
            })
            .unwrap_or(true);
        if !are_allowed_domains_valid {
            return Err("Invalid allowed domain names received in payment_link_config");
        }

        Ok(())
    }
}


pub struct MerchantAdvice {
    code: Option<String>,
    code_raw: Option<String>,
}


pub async fn terminate_auth_select(
    state: SessionState,
    user_token: auth::UserFromSinglePurposeToken,
    req: user_api::AuthSelectRequest,
) -> UserResponse<user_api::TokenResponse> {
    let user_from_db: domain::UserFromStorage = state
        .global_store
        .find_user_by_id(&user_token.user_id)
        .await
        .change_context(UserErrors::InternalServerError)?
        .into();

    let user_email = domain::UserEmail::from_pii_email(user_from_db.get_email())?;
    let auth_methods = state
        .store
        .list_user_authentication_methods_for_email_domain(user_email.extract_domain()?)
        .await
        .change_context(UserErrors::InternalServerError)?;

    let user_authentication_method = match (req.id, auth_methods.is_empty()) {
        (Some(id), _) => auth_methods
            .into_iter()
            .find(|auth_method| auth_method.id == id)
            .ok_or(UserErrors::InvalidUserAuthMethodOperation)?,
        (None, true) => DEFAULT_USER_AUTH_METHOD.clone(),
        (None, false) => return Err(UserErrors::InvalidUserAuthMethodOperation.into()),
    };

    let current_flow = domain::CurrentFlow::new(user_token, domain::SPTFlow::AuthSelect.into())?;
    let mut next_flow = current_flow.next(user_from_db.clone(), &state).await?;

    // Skip SSO if continue with password(TOTP)
    if next_flow.get_flow() == domain::UserFlow::SPTFlow(domain::SPTFlow::SSO)
        && !utils::user::is_sso_auth_type(user_authentication_method.auth_type)
    {
        next_flow = next_flow.skip(user_from_db, &state).await?;
    }
    let token = next_flow.get_token(&state).await?;

    auth::cookies::set_cookie_response(
        user_api::TokenResponse {
            token: token.clone(),
            token_type: next_flow.get_flow().into(),
        },
        token,
    )
}


/// Serialize event into a buffer of bytes using parent span.
    pub fn event_serialize<S>(
        &self,
        span: Option<&SpanRef<'_, S>>,
        event: &Event<'_>,
    ) -> std::io::Result<Vec<u8>>
    where
        S: Subscriber + for<'a> LookupSpan<'a>,
    {
        let mut buffer = Vec::new();
        let mut serializer =
            serde_json::Serializer::with_formatter(&mut buffer, self.formatter.clone());
        let mut map_serializer = serializer.serialize_map(None)?;

        let mut storage = Storage::default();
        event.record(&mut storage);

        let name = span.map_or("?", SpanRef::name);
        Self::event_message(span, event, &mut storage);

        self.common_serialize(&mut map_serializer, event.metadata(), span, &storage, name)?;

        map_serializer.end()?;
        Ok(buffer)
    }


File: crates/masking/src/abs.rs

//! Abstract data types.

use crate::Secret;

/// Interface to expose a reference to an inner secret
pub trait PeekInterface<S> {
    /// Only method providing access to the secret value.
    fn peek(&self) -> &S;

    /// Provide a mutable reference to the inner value.
    fn peek_mut(&mut self) -> &mut S;
}

/// Interface that consumes a option secret and returns the value.
pub trait ExposeOptionInterface<S> {
    /// Expose option.
    fn expose_option(self) -> S;
}

/// Interface that consumes a secret and returns the inner value.
pub trait ExposeInterface<S> {
    /// Consume the secret and return the inner value
    fn expose(self) -> S;
}

impl<S, I> ExposeOptionInterface<Option<S>> for Option<Secret<S, I>>
where
    S: Clone,
    I: crate::Strategy<S>,
{
    fn expose_option(self) -> Option<S> {
        self.map(ExposeInterface::expose)
    }
}

impl<S, I> ExposeInterface<S> for Secret<S, I>
where
    I: crate::Strategy<S>,
{
    fn expose(self) -> S {
        self.inner_secret
    }
}

/// Interface that consumes a secret and converts it to a secret with a different masking strategy.
pub trait SwitchStrategy<FromStrategy, ToStrategy> {
    /// The type returned by `switch_strategy()`.
    type Output;

    /// Consumes the secret and converts it to a secret with a different masking strategy.
    fn switch_strategy(self) -> Self::Output;
}

impl<S, FromStrategy, ToStrategy> SwitchStrategy<FromStrategy, ToStrategy>
    for Secret<S, FromStrategy>
where
    FromStrategy: crate::Strategy<S>,
    ToStrategy: crate::Strategy<S>,
{
    type Output = Secret<S, ToStrategy>;

    fn switch_strategy(self) -> Self::Output {
        Secret::new(self.inner_secret)
    }
}


pub async fn get_session_token_for_click_to_pay(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    merchant_context: &domain::MerchantContext,
    authentication_product_ids: common_types::payments::AuthenticationConnectorAccountMap,
    payment_intent: &payments::PaymentIntent,
    profile_id: &id_type::ProfileId,
) -> RouterResult<api_models::payments::SessionToken> {
    let click_to_pay_mca_id = authentication_product_ids
        .get_click_to_pay_connector_account_id()
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "authentication_product_ids",
        })?;
    let key_manager_state = &(state).into();
    let merchant_connector_account = state
        .store
        .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
            key_manager_state,
            merchant_id,
            &click_to_pay_mca_id,
            merchant_context.get_merchant_key_store(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: click_to_pay_mca_id.get_string_repr().to_string(),
        })?;
    let click_to_pay_metadata: ClickToPayMetaData = merchant_connector_account
        .metadata
        .parse_value("ClickToPayMetaData")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error while parsing ClickToPayMetaData")?;
    let transaction_currency = payment_intent
        .currency
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("currency is not present in payment_data.payment_intent")?;
    let required_amount_type = common_utils::types::StringMajorUnitForConnector;
    let transaction_amount = required_amount_type
        .convert(payment_intent.amount, transaction_currency)
        .change_context(errors::ApiErrorResponse::AmountConversionFailed {
            amount_type: "string major unit",
        })?;

    let customer_details_value = payment_intent
        .customer_details
        .clone()
        .get_required_value("customer_details")?;

    let customer_details: CustomerData = customer_details_value
        .parse_value("CustomerData")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error while parsing customer data from payment intent")?;

    validate_customer_details_for_click_to_pay(&customer_details)?;

    let provider = match merchant_connector_account.connector_name.as_str() {
        "ctp_mastercard" => Some(enums::CtpServiceProvider::Mastercard),
        "ctp_visa" => Some(enums::CtpServiceProvider::Visa),
        _ => None,
    };

    let card_brands = get_card_brands_based_on_active_merchant_connector_account(
        state,
        profile_id,
        merchant_context.get_merchant_key_store(),
    )
    .await?;

    Ok(api_models::payments::SessionToken::ClickToPay(Box::new(
        api_models::payments::ClickToPaySessionResponse {
            dpa_id: click_to_pay_metadata.dpa_id,
            dpa_name: click_to_pay_metadata.dpa_name,
            locale: click_to_pay_metadata.locale,
            card_brands,
            acquirer_bin: click_to_pay_metadata.acquirer_bin,
            acquirer_merchant_id: click_to_pay_metadata.acquirer_merchant_id,
            merchant_category_code: click_to_pay_metadata.merchant_category_code,
            merchant_country_code: click_to_pay_metadata.merchant_country_code,
            transaction_amount,
            transaction_currency_code: transaction_currency,
            phone_number: customer_details.phone.clone(),
            email: customer_details.email.clone(),
            phone_country_code: customer_details.phone_country_code.clone(),
            provider,
            dpa_client_id: click_to_pay_metadata.dpa_client_id.clone(),
        },
    )))
}


impl ConnectorValidation for Itaubank {}

impl ConnectorValidation for for Itaubank


File: crates/common_utils/src/types.rs
 Public functions: 26
 Public structs: 20

//! Types that can be used in other crates
pub mod keymanager;

/// Enum for Authentication Level
pub mod authentication;
/// User related types
pub mod user;

/// types that are wrappers around primitive types
pub mod primitive_wrappers;

use std::{
    borrow::Cow,
    fmt::Display,
    iter::Sum,
    num::NonZeroI64,
    ops::{Add, Mul, Sub},
    primitive::i64,
    str::FromStr,
};

use common_enums::enums;
use diesel::{
    backend::Backend,
    deserialize,
    deserialize::FromSql,
    serialize::{Output, ToSql},
    sql_types,
    sql_types::Jsonb,
    AsExpression, FromSqlRow, Queryable,
};
use error_stack::{report, ResultExt};
pub use primitive_wrappers::bool_wrappers::{
    AlwaysRequestExtendedAuthorization, ExtendedAuthorizationAppliedBool,
    RequestExtendedAuthorizationBool,
};
use rust_decimal::{
    prelude::{FromPrimitive, ToPrimitive},
    Decimal,
};
use semver::Version;
use serde::{de::Visitor, Deserialize, Deserializer, Serialize};
use thiserror::Error;
use time::PrimitiveDateTime;
use utoipa::ToSchema;

use crate::{
    consts::{
        self, MAX_DESCRIPTION_LENGTH, MAX_STATEMENT_DESCRIPTOR_LENGTH, PUBLISHABLE_KEY_LENGTH,
    },
    errors::{CustomResult, ParsingError, PercentageError, ValidationError},
    fp_utils::when,
    impl_enum_str,
};

/// Represents Percentage Value between 0 and 100 both inclusive
#[derive(Clone, Default, Debug, PartialEq, Serialize)]
pub struct Percentage<const PRECISION: u8> {
    // this value will range from 0 to 100, decimal length defined by precision macro
    /// Percentage value ranging between 0 and 100
    percentage: f32,
}

fn get_invalid_percentage_error_message(precision: u8) -> String {
    format!(
        "value should be a float between 0 to 100 and precise to only upto {precision} decimal digits",

    )
}

impl<const PRECISION: u8> Percentage<PRECISION> {
    /// construct percentage using a string representation of float value
    pub fn from_string(value: String) -> CustomResult<Self, PercentageError> {
        if Self::is_valid_string_value(&value)? {
            Ok(Self {
                percentage: value
                    .parse::<f32>()
                    .change_context(PercentageError::InvalidPercentageValue)?,
            })
        } else {
            Err(report!(PercentageError::InvalidPercentageValue))
                .attach_printable(get_invalid_percentage_error_message(PRECISION))
        }
    }
    /// function to get percentage value
    pub fn get_percentage(&self) -> f32 {
        self.percentage
    }

    /// apply the percentage to amount and ceil the result
    #[allow(clippy::as_conversions)]
    pub fn apply_and_ceil_result(
        &self,
        amount: MinorUnit,
    ) -> CustomResult<MinorUnit, PercentageError> {
        let max_amount = i64::MAX / 10000;
        let amount = amount.0;
        if amount > max_amount {
            // value gets rounded off after i64::MAX/10000
            Err(report!(PercentageError::UnableToApplyPercentage {
                percentage: self.percentage,
                amount: MinorUnit::new(amount),
            }))
            .attach_printable(format!(
                "Cannot calculate percentage for amount greater than {max_amount}",
            ))
        } else {
            let percentage_f64 = f64::from(self.percentage);
            let result = (amount as f64 * (percentage_f64 / 100.0)).ceil() as i64;
            Ok(MinorUnit::new(result))
        }
    }

    fn is_valid_string_value(value: &str) -> CustomResult<bool, PercentageError> {
        let float_value = Self::is_valid_float_string(value)?;
        Ok(Self::is_valid_range(float_value) && Self::is_valid_precision_length(value))
    }
    fn is_valid_float_string(value: &str) -> CustomResult<f32, PercentageError> {
        value
            .parse::<f32>()
            .change_context(PercentageError::InvalidPercentageValue)
    }
    fn is_valid_range(value: f32) -> bool {
        (0.0..=100.0).contains(&value)
    }
    fn is_valid_precision_length(value: &str) -> bool {
        if value.contains('.') {
            // if string has '.' then take the decimal part and verify precision length
            match value.split('.').next_back() {
                Some(decimal_part) => {
                    decimal_part.trim_end_matches('0').len() <= <u8 as Into<usize>>::into(PRECISION)
                }
                // will never be None
                None => false,
            }
        } else {
            // if there is no '.' then it is a whole number with no decimal part. So return true
            true
        }
    }
}

// custom serde deserialization function
struct PercentageVisitor<const PRECISION: u8> {}
impl<'de, const PRECISION: u8> Visitor<'de> for PercentageVisitor<PRECISION> {
    type Value = Percentage<PRECISION>;

    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter.write_str("Percentage object")
    }
    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    where
        A: serde::de::MapAccess<'de>,
    {
        let mut percentage_value = None;
        while let Some(key) = map.next_key::<String>()? {
            if key.eq("percentage") {
                if percentage_value.is_some() {
                    return Err(serde::de::Error::duplicate_field("percentage"));
                }
                percentage_value = Some(map.next_value::<serde_json::Value>()?);
            } else {
                // Ignore unknown fields
                let _: serde::de::IgnoredAny = map.next_value()?;
            }
        }
        if let Some(value) = percentage_value {
            let string_value = value.to_string();
            Ok(Percentage::from_string(string_value.clone()).map_err(|_| {
                serde::de::Error::invalid_value(
                    serde::de::Unexpected::Other(&format!("percentage value {string_value}")),
                    &&*get_invalid_percentage_error_message(PRECISION),
                )
            })?)
        } else {
            Err(serde::de::Error::missing_field("percentage"))
        }
    }
}

impl<'de, const PRECISION: u8> Deserialize<'de> for Percentage<PRECISION> {
    fn deserialize<D>(data: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        data.deserialize_map(PercentageVisitor::<PRECISION> {})
    }
}

/// represents surcharge type and value
#[derive(Clone, Debug, PartialEq, Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case", tag = "type", content = "value")]
pub enum Surcharge {
    /// Fixed Surcharge value
    Fixed(MinorUnit),
    /// Surcharge percentage
    Rate(Percentage<{ consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH }>),
}

/// This struct lets us represent a semantic version type
#[derive(Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression, Ord, PartialOrd)]
#[diesel(sql_type = Jsonb)]
#[derive(Serialize, serde::Deserialize)]
pub struct SemanticVersion(#[serde(with = "Version")] Version);

impl SemanticVersion {
    /// returns major version number
    pub fn get_major(&self) -> u64 {
        self.0.major
    }

    /// returns minor version number
    pub fn get_minor(&self) -> u64 {
        self.0.minor
    }
    /// Constructs new SemanticVersion instance
    pub fn new(major: u64, minor: u64, patch: u64) -> Self {
        Self(Version::new(major, minor, patch))
    }
}

impl Display for SemanticVersion {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl FromStr for SemanticVersion {
    type Err = error_stack::Report<ParsingError>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Self(Version::from_str(s).change_context(
            ParsingError::StructParseFailure("SemanticVersion"),
        )?))
    }
}

crate::impl_to_sql_from_sql_json!(SemanticVersion);

/// Amount convertor trait for connector
pub trait AmountConvertor: Send {
    /// Output type for the connector
    type Output;
    /// helps in conversion of connector required amount type
    fn convert(
        &self,
        amount: MinorUnit,
        currency: enums::Currency,
    ) -> Result<Self::Output, error_stack::Report<ParsingError>>;

    /// helps in converting back connector required amount type to core minor unit
    fn convert_back(
        &self,
        amount: Self::Output,
        currency: enums::Currency,
    ) -> Result<MinorUnit, error_stack::Report<ParsingError>>;
}

/// Connector required amount type
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub struct StringMinorUnitForConnector;

impl AmountConvertor for StringMinorUnitForConnector {
    type Output = StringMinorUnit;
    fn convert(
        &self,
        amount: MinorUnit,
        _currency: enums::Currency,
    ) -> Result<Self::Output, error_stack::Report<ParsingError>> {
        amount.to_minor_unit_as_string()
    }

    fn convert_back(
        &self,
        amount: Self::Output,
        _currency: enums::Currency,
    ) -> Result<MinorUnit, error_stack::Report<ParsingError>> {
        amount.to_minor_unit_as_i64()
    }
}

/// Core required conversion type
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)]
pub struct StringMajorUnitForCore;
impl AmountConvertor for StringMajorUnitForCore {
    type Output = StringMajorUnit;
    fn convert(
        &self,
        amount: MinorUnit,
        currency: enums::Currency,
    ) -> Result<Self::Output, error_stack::Report<ParsingError>> {
        amount.to_major_unit_as_string(currency)
    }

    fn convert_back(
        &self,
        amount: StringMajorUnit,
        currency: enums::Currency,
    ) -> Result<MinorUnit, error_stack::Report<ParsingError>> {
        amount.to_minor_unit_as_i64(currency)
    }
}

/// Connector required amount type
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)]
pub struct StringMajorUnitForConnector;

impl AmountConvertor for StringMajorUnitForConnector {
    type Output = StringMajorUnit;
    fn convert(
        &self,
        amount: MinorUnit,
        currency: enums::Currency,
    ) -> Result<Self::Output, error_stack::Report<ParsingError>> {
        amount.to_major_unit_as_string(currency)
    }

    fn convert_back(
        &self,
        amount: StringMajorUnit,
        currency: enums::Currency,
    ) -> Result<MinorUnit, error_stack::Report<ParsingError>> {
        amount.to_minor_unit_as_i64(currency)
    }
}

/// Connector required amount type
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)]
pub struct FloatMajorUnitForConnector;

impl AmountConvertor for FloatMajorUnitForConnector {
    type Output = FloatMajorUnit;
    fn convert(
        &self,
        amount: MinorUnit,
        currency: enums::Currency,
    ) -> Result<Self::Output, error_stack::Report<ParsingError>> {
        amount.to_major_unit_as_f64(currency)
    }
    fn convert_back(
        &self,
        amount: FloatMajorUnit,
        currency: enums::Currency,
    ) -> Result<MinorUnit, error_stack::Report<ParsingError>> {
        amount.to_minor_unit_as_i64(currency)
    }
}

/// Connector required amount type
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)]
pub struct MinorUnitForConnector;

impl AmountConvertor for MinorUnitForConnector {
    type Output = MinorUnit;
    fn convert(
        &self,
        amount: MinorUnit,
        _currency: enums::Currency,
    ) -> Result<Self::Output, error_stack::Report<ParsingError>> {
        Ok(amount)
    }
    fn convert_back(
        &self,
        amount: MinorUnit,
        _currency: enums::Currency,
    ) -> Result<MinorUnit, error_stack::Report<ParsingError>> {
        Ok(amount)
    }
}

/// This Unit struct represents MinorUnit in which core amount works
#[derive(
    Default,
    Debug,
    serde::Deserialize,
    AsExpression,
    serde::Serialize,
    Clone,
    Copy,
    PartialEq,
    Eq,
    Hash,
    ToSchema,
    PartialOrd,
)]
#[diesel(sql_type = sql_types::BigInt)]
pub struct MinorUnit(i64);

impl MinorUnit {
    /// gets amount as i64 value will be removed in future
    pub fn get_amount_as_i64(self) -> i64 {
        self.0
    }

    /// forms a new minor default unit i.e zero
    pub fn zero() -> Self {
        Self(0)
    }

    /// forms a new minor unit from amount
    pub fn new(value: i64) -> Self {
        Self(value)
    }

    /// checks if the amount is greater than the given value
    pub fn is_greater_than(&self, value: i64) -> bool {
        self.get_amount_as_i64() > value
    }

    /// Convert the amount to its major denomination based on Currency and return String
    /// Paypal Connector accepts Zero and Two decimal currency but not three decimal and it should be updated as required for 3 decimal currencies.
    /// Paypal Ref - https://developer.paypal.com/docs/reports/reference/paypal-supported-currencies/
    fn to_major_unit_as_string(
        self,
        currency: enums::Currency,
    ) -> Result<StringMajorUnit, error_stack::Report<ParsingError>> {
        let amount_f64 = self.to_major_unit_as_f64(currency)?;
        let amount_string = if currency.is_zero_decimal_currency() {
            amount_f64.0.to_string()
        } else if currency.is_three_decimal_currency() {
            format!("{:.3}", amount_f64.0)
        } else {
            format!("{:.2}", amount_f64.0)
        };
        Ok(StringMajorUnit::new(amount_string))
    }

    /// Convert the amount to its major denomination based on Currency and return f64
    fn to_major_unit_as_f64(
        self,
        currency: enums::Currency,
    ) -> Result<FloatMajorUnit, error_stack::Report<ParsingError>> {
        let amount_decimal =
            Decimal::from_i64(self.0).ok_or(ParsingError::I64ToDecimalConversionFailure)?;

        let amount = if currency.is_zero_decimal_currency() {
            amount_decimal
        } else if currency.is_three_decimal_currency() {
            amount_decimal / Decimal::from(1000)
        } else {
            amount_decimal / Decimal::from(100)
        };
        let amount_f64 = amount
            .to_f64()
            .ok_or(ParsingError::FloatToDecimalConversionFailure)?;
        Ok(FloatMajorUnit::new(amount_f64))
    }

    ///Convert minor unit to string minor unit
    fn to_minor_unit_as_string(self) -> Result<StringMinorUnit, error_stack::Report<ParsingError>> {
        Ok(StringMinorUnit::new(self.0.to_string()))
    }
}

impl From<NonZeroI64> for MinorUnit {
    fn from(val: NonZeroI64) -> Self {
        Self::new(val.get())
    }
}

impl Display for MinorUnit {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl<DB> FromSql<sql_types::BigInt, DB> for MinorUnit
where
    DB: Backend,
    i64: FromSql<sql_types::BigInt, DB>,
{
    fn from_sql(value: DB::RawValue<'_>) -> deserialize::Result<Self> {
        let val = i64::from_sql(value)?;
        Ok(Self(val))
    }
}

impl<DB> ToSql<sql_types::BigInt, DB> for MinorUnit
where
    DB: Backend,
    i64: ToSql<sql_types::BigInt, DB>,
{
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result {
        self.0.to_sql(out)
    }
}

impl<DB> Queryable<sql_types::BigInt, DB> for MinorUnit
where
    DB: Backend,
    Self: FromSql<sql_types::BigInt, DB>,
{
    type Row = Self;

    fn build(row: Self::Row) -> deserialize::Result<Self> {
        Ok(row)
    }
}

impl Add for MinorUnit {
    type Output = Self;
    fn add(self, a2: Self) -> Self {
        Self(self.0 + a2.0)
    }
}

impl Sub for MinorUnit {
    type Output = Self;
    fn sub(self, a2: Self) -> Self {
        Self(self.0 - a2.0)
    }
}

impl Mul<u16> for MinorUnit {
    type Output = Self;

    fn mul(self, a2: u16) -> Self::Output {
        Self(self.0 * i64::from(a2))
    }
}

impl Sum for MinorUnit {
    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
        iter.fold(Self(0), |a, b| a + b)
    }
}

/// Connector specific types to send
#[derive(
    Default,
    Debug,
    serde::Deserialize,
    AsExpression,
    serde::Serialize,
    Clone,
    PartialEq,
    Eq,
    Hash,
    ToSchema,
    PartialOrd,
)]
#[diesel(sql_type = sql_types::Text)]
pub struct StringMinorUnit(String);

impl StringMinorUnit {
    /// forms a new minor unit in string from amount
    fn new(value: String) -> Self {
        Self(value)
    }

    /// converts to minor unit i64 from minor unit string value
    fn to_minor_unit_as_i64(&self) -> Result<MinorUnit, error_stack::Report<ParsingError>> {
        let amount_string = &self.0;
        let amount_decimal = Decimal::from_str(amount_string).map_err(|e| {
            ParsingError::StringToDecimalConversionFailure {
                error: e.to_string(),
            }
        })?;
        let amount_i64 = amount_decimal
            .to_i64()
            .ok_or(ParsingError::DecimalToI64ConversionFailure)?;
        Ok(MinorUnit::new(amount_i64))
    }
}

impl Display for StringMinorUnit {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl<DB> FromSql<sql_types::Text, DB> for StringMinorUnit
where
    DB: Backend,
    String: FromSql<sql_types::Text, DB>,
{
    fn from_sql(value: DB::RawValue<'_>) -> deserialize::Result<Self> {
        let val = String::from_sql(value)?;
        Ok(Self(val))
    }
}

impl<DB> ToSql<sql_types::Text, DB> for StringMinorUnit
where
    DB: Backend,
    String: ToSql<sql_types::Text, DB>,
{
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result {
        self.0.to_sql(out)
    }
}

impl<DB> Queryable<sql_types::Text, DB> for StringMinorUnit
where
    DB: Backend,
    Self: FromSql<sql_types::Text, DB>,
{
    type Row = Self;

    fn build(row: Self::Row) -> deserialize::Result<Self> {
        Ok(row)
    }
}

/// Connector specific types to send
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)]
pub struct FloatMajorUnit(f64);

impl FloatMajorUnit {
    /// forms a new major unit from amount
    fn new(value: f64) -> Self {
        Self(value)
    }

    /// forms a new major unit with zero amount
    pub fn zero() -> Self {
        Self(0.0)
    }

    /// converts to minor unit as i64 from FloatMajorUnit
    fn to_minor_unit_as_i64(
        self,
        currency: enums::Currency,
    ) -> Result<MinorUnit, error_stack::Report<ParsingError>> {
        let amount_decimal =
            Decimal::from_f64(self.0).ok_or(ParsingError::FloatToDecimalConversionFailure)?;

        let amount = if currency.is_zero_decimal_currency() {
            amount_decimal
        } else if currency.is_three_decimal_currency() {
            amount_decimal * Decimal::from(1000)
        } else {
            amount_decimal * Decimal::from(100)
        };

        let amount_i64 = amount
            .to_i64()
            .ok_or(ParsingError::DecimalToI64ConversionFailure)?;
        Ok(MinorUnit::new(amount_i64))
    }
}

/// Connector specific types to send
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq)]
pub struct StringMajorUnit(String);

impl StringMajorUnit {
    /// forms a new major unit from amount
    fn new(value: String) -> Self {
        Self(value)
    }

    /// Converts to minor unit as i64 from StringMajorUnit
    fn to_minor_unit_as_i64(
        &self,
        currency: enums::Currency,
    ) -> Result<MinorUnit, error_stack::Report<ParsingError>> {
        let amount_decimal = Decimal::from_str(&self.0).map_err(|e| {
            ParsingError::StringToDecimalConversionFailure {
                error: e.to_string(),
            }
        })?;

        let amount = if currency.is_zero_decimal_currency() {
            amount_decimal
        } else if currency.is_three_decimal_currency() {
            amount_decimal * Decimal::from(1000)
        } else {
            amount_decimal * Decimal::from(100)
        };
        let amount_i64 = amount
            .to_i64()
            .ok_or(ParsingError::DecimalToI64ConversionFailure)?;
        Ok(MinorUnit::new(amount_i64))
    }
    /// forms a new StringMajorUnit default unit i.e zero
    pub fn zero() -> Self {
        Self("0".to_string())
    }
    /// Get string amount from struct to be removed in future
    pub fn get_amount_as_string(&self) -> String {
        self.0.clone()
    }
}

#[derive(
    Debug,
    serde::Deserialize,
    AsExpression,
    serde::Serialize,
    Clone,
    PartialEq,
    Eq,
    Hash,
    ToSchema,
    PartialOrd,
)]
#[diesel(sql_type = sql_types::Text)]
/// This domain type can be used for any url
pub struct Url(url::Url);

impl Url {
    /// Get string representation of the url
    pub fn get_string_repr(&self) -> &str {
        self.0.as_str()
    }

    /// wrap the url::Url in Url type
    pub fn wrap(url: url::Url) -> Self {
        Self(url)
    }

    /// Get the inner url
    pub fn into_inner(self) -> url::Url {
        self.0
    }

    /// Add query params to the url
    pub fn add_query_params(mut self, (key, value): (&str, &str)) -> Self {
        let url = self
            .0
            .query_pairs_mut()
            .append_pair(key, value)
            .finish()
            .clone();
        Self(url)
    }
}

impl<DB> ToSql<sql_types::Text, DB> for Url
where
    DB: Backend,
    str: ToSql<sql_types::Text, DB>,
{
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result {
        let url_string = self.0.as_str();
        url_string.to_sql(out)
    }
}

impl<DB> FromSql<sql_types::Text, DB> for Url
where
    DB: Backend,
    String: FromSql<sql_types::Text, DB>,
{
    fn from_sql(value: DB::RawValue<'_>) -> deserialize::Result<Self> {
        let val = String::from_sql(value)?;
        let url = url::Url::parse(&val)?;
        Ok(Self(url))
    }
}

/// A type representing a range of time for filtering, including a mandatory start time and an optional end time.
#[derive(
    Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, ToSchema,
)]
pub struct TimeRange {
    /// The start time to filter payments list or to get list of filters. To get list of filters start time is needed to be passed
    #[serde(with = "crate::custom_serde::iso8601")]
    #[serde(alias = "startTime")]
    pub start_time: PrimitiveDateTime,
    /// The end time to filter payments list or to get list of filters. If not passed the default time is now
    #[serde(default, with = "crate::custom_serde::iso8601::option")]
    #[serde(alias = "endTime")]
    pub end_time: Option<PrimitiveDateTime>,
}

#[cfg(test)]
mod amount_conversion_tests {
    #![allow(clippy::unwrap_used)]
    use super::*;
    const TWO_DECIMAL_CURRENCY: enums::Currency = enums::Currency::USD;
    const THREE_DECIMAL_CURRENCY: enums::Currency = enums::Currency::BHD;
    const ZERO_DECIMAL_CURRENCY: enums::Currency = enums::Currency::JPY;
    #[test]
    fn amount_conversion_to_float_major_unit() {
        let request_amount = MinorUnit::new(999999999);
        let required_conversion = FloatMajorUnitForConnector;

        // Two decimal currency conversions
        let converted_amount = required_conversion
            .convert(request_amount, TWO_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(converted_amount.0, 9999999.99);
        let converted_back_amount = required_conversion
            .convert_back(converted_amount, TWO_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(converted_back_amount, request_amount);

        // Three decimal currency conversions
        let converted_amount = required_conversion
            .convert(request_amount, THREE_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(converted_amount.0, 999999.999);
        let converted_back_amount = required_conversion
            .convert_back(converted_amount, THREE_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(converted_back_amount, request_amount);

        // Zero decimal currency conversions
        let converted_amount = required_conversion
            .convert(request_amount, ZERO_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(converted_amount.0, 999999999.0);

        let converted_back_amount = required_conversion
            .convert_back(converted_amount, ZERO_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(converted_back_amount, request_amount);
    }

    #[test]
    fn amount_conversion_to_string_major_unit() {
        let request_amount = MinorUnit::new(999999999);
        let required_conversion = StringMajorUnitForConnector;

        // Two decimal currency conversions
        let converted_amount_two_decimal_currency = required_conversion
            .convert(request_amount, TWO_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(
            converted_amount_two_decimal_currency.0,
            "9999999.99".to_string()
        );
        let converted_back_amount = required_conversion
            .convert_back(converted_amount_two_decimal_currency, TWO_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(converted_back_amount, request_amount);

        // Three decimal currency conversions
        let converted_amount_three_decimal_currency = required_conversion
            .convert(request_amount, THREE_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(
            converted_amount_three_decimal_currency.0,
            "999999.999".to_string()
        );
        let converted_back_amount = required_conversion
            .convert_back(
                converted_amount_three_decimal_currency,
                THREE_DECIMAL_CURRENCY,
            )
            .unwrap();
        assert_eq!(converted_back_amount, request_amount);

        // Zero decimal currency conversions
        let converted_amount = required_conversion
            .convert(request_amount, ZERO_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(converted_amount.0, "999999999".to_string());

        let converted_back_amount = required_conversion
            .convert_back(converted_amount, ZERO_DECIMAL_CURRENCY)
            .unwrap();
        assert_eq!(converted_back_amount, request_amount);
    }

    #[test]
    fn amount_conversion_to_string_minor_unit() {
        let request_amount = MinorUnit::new(999999999);
        let currency = TWO_DECIMAL_CURRENCY;
        let required_conversion = StringMinorUnitForConnector;
        let converted_amount = required_conversion
            .convert(request_amount, currency)
            .unwrap();
        assert_eq!(converted_amount.0, "999999999".to_string());
        let converted_back_amount = required_conversion
            .convert_back(converted_amount, currency)
            .unwrap();
        assert_eq!(converted_back_amount, request_amount);
    }
}

// Charges structs
#[derive(
    Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression, ToSchema,
)]
#[diesel(sql_type = Jsonb)]
/// Charge specific fields for controlling the revert of funds from either platform or connected account. Check sub-fields for more details.
pub struct ChargeRefunds {
    /// Identifier for charge created for the payment
    pub charge_id: String,

    /// Toggle for reverting the application fee that was collected for the payment.
    /// If set to false, the funds are pulled from the destination account.
    pub revert_platform_fee: Option<bool>,

    /// Toggle for reverting the transfer that was made during the charge.
    /// If set to false, the funds are pulled from the main platform's account.
    pub revert_transfer: Option<bool>,
}

crate::impl_to_sql_from_sql_json!(ChargeRefunds);

/// A common type of domain type that can be used for fields that contain a string with restriction of length
#[derive(Debug, Clone, Serialize, Hash, PartialEq, Eq, AsExpression)]
#[diesel(sql_type = sql_types::Text)]
pub(crate) struct LengthString<const MAX_LENGTH: u16, const MIN_LENGTH: u16>(String);

/// Error generated from violation of constraints for MerchantReferenceId
#[derive(Debug, Error, PartialEq, Eq)]
pub(crate) enum LengthStringError {
    #[error("the maximum allowed length for this field is {0}")]
    /// Maximum length of string violated
    MaxLengthViolated(u16),

    #[error("the minimum required length for this field is {0}")]
    /// Minimum length of string violated
    MinLengthViolated(u16),
}

impl<const MAX_LENGTH: u16, const MIN_LENGTH: u16> LengthString<MAX_LENGTH, MIN_LENGTH> {
    /// Generates new [MerchantReferenceId] from the given input string
    pub fn from(input_string: Cow<'static, str>) -> Result<Self, LengthStringError> {
        let trimmed_input_string = input_string.trim().to_string();
        let length_of_input_string = u16::try_from(trimmed_input_string.len())
            .map_err(|_| LengthStringError::MaxLengthViolated(MAX_LENGTH))?;

        when(length_of_input_string > MAX_LENGTH, || {
            Err(LengthStringError::MaxLengthViolated(MAX_LENGTH))
        })?;

        when(length_of_input_string < MIN_LENGTH, || {
            Err(LengthStringError::MinLengthViolated(MIN_LENGTH))
        })?;

        Ok(Self(trimmed_input_string))
    }

    pub(crate) fn new_unchecked(input_string: String) -> Self {
        Self(input_string)
    }
}

impl<'de, const MAX_LENGTH: u16, const MIN_LENGTH: u16> Deserialize<'de>
    for LengthString<MAX_LENGTH, MIN_LENGTH>
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let deserialized_string = String::deserialize(deserializer)?;
        Self::from(deserialized_string.into()).map_err(serde::de::Error::custom)
    }
}

impl<DB, const MAX_LENGTH: u16, const MIN_LENGTH: u16> FromSql<sql_types::Text, DB>
    for LengthString<MAX_LENGTH, MIN_LENGTH>
where
    DB: Backend,
    String: FromSql<sql_types::Text, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
        let val = String::from_sql(bytes)?;
        Ok(Self(val))
    }
}

impl<DB, const MAX_LENGTH: u16, const MIN_LENGTH: u16> ToSql<sql_types::Text, DB>
    for LengthString<MAX_LENGTH, MIN_LENGTH>
where
    DB: Backend,
    String: ToSql<sql_types::Text, DB>,
{
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result {
        self.0.to_sql(out)
    }
}

impl<DB, const MAX_LENGTH: u16, const MIN_LENGTH: u16> Queryable<sql_types::Text, DB>
    for LengthString<MAX_LENGTH, MIN_LENGTH>
where
    DB: Backend,
    Self: FromSql<sql_types::Text, DB>,
{
    type Row = Self;
    fn build(row: Self::Row) -> deserialize::Result<Self> {
        Ok(row)
    }
}

/// Domain type for description
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, AsExpression)]
#[diesel(sql_type = sql_types::Text)]
pub struct Description(LengthString<MAX_DESCRIPTION_LENGTH, 1>);

impl Description {
    /// Create a new Description Domain type without any length check from a static str
    pub fn from_str_unchecked(input_str: &'static str) -> Self {
        Self(LengthString::new_unchecked(input_str.to_owned()))
    }

    // TODO: Remove this function in future once description in router data is updated to domain type
    /// Get the string representation of the description
    pub fn get_string_repr(&self) -> &str {
        &self.0 .0
    }
}

/// Domain type for Statement Descriptor
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, AsExpression)]
#[diesel(sql_type = sql_types::Text)]
pub struct StatementDescriptor(LengthString<MAX_STATEMENT_DESCRIPTOR_LENGTH, 1>);

impl<DB> Queryable<sql_types::Text, DB> for Description
where
    DB: Backend,
    Self: FromSql<sql_types::Text, DB>,
{
    type Row = Self;

    fn build(row: Self::Row) -> deserialize::Result<Self> {
        Ok(row)
    }
}

impl<DB> FromSql<sql_types::Text, DB> for Description
where
    DB: Backend,
    LengthString<MAX_DESCRIPTION_LENGTH, 1>: FromSql<sql_types::Text, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
        let val = LengthString::<MAX_DESCRIPTION_LENGTH, 1>::from_sql(bytes)?;
        Ok(Self(val))
    }
}

impl<DB> ToSql<sql_types::Text, DB> for Description
where
    DB: Backend,
    LengthString<MAX_DESCRIPTION_LENGTH, 1>: ToSql<sql_types::Text, DB>,
{
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result {
        self.0.to_sql(out)
    }
}

impl<DB> Queryable<sql_types::Text, DB> for StatementDescriptor
where
    DB: Backend,
    Self: FromSql<sql_types::Text, DB>,
{
    type Row = Self;

    fn build(row: Self::Row) -> deserialize::Result<Self> {
        Ok(row)
    }
}

impl<DB> FromSql<sql_types::Text, DB> for StatementDescriptor
where
    DB: Backend,
    LengthString<MAX_STATEMENT_DESCRIPTOR_LENGTH, 1>: FromSql<sql_types::Text, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {


            payment_method_data: payment_data.get_payment_method_data().cloned(),
            amount,
            currency: currency.to_string(),
        }))
    } else {
        let mut next_action_response = None;

        // Early exit for terminal payment statuses - don't evaluate next_action at all
        if payment_intent.status.is_in_terminal_state() {
            next_action_response = None;
        } else {
            let bank_transfer_next_steps = bank_transfer_next_steps_check(payment_attempt.clone())?;

            let next_action_voucher = voucher_next_steps_check(payment_attempt.clone())?;

            let next_action_mobile_payment = mobile_payment_next_steps_check(&payment_attempt)?;

            let next_action_containing_qr_code_url =
                qr_code_next_steps_check(payment_attempt.clone())?;

            let papal_sdk_next_action = paypal_sdk_next_steps_check(payment_attempt.clone())?;

            let next_action_containing_fetch_qr_code_url =
                fetch_qr_code_url_next_steps_check(payment_attempt.clone())?;

            let next_action_containing_wait_screen =
                wait_screen_next_steps_check(payment_attempt.clone())?;

            let next_action_invoke_hidden_frame =
                next_action_invoke_hidden_frame(&payment_attempt)?;

            if payment_intent.status == enums::IntentStatus::RequiresCustomerAction
                || bank_transfer_next_steps.is_some()
                || next_action_voucher.is_some()
                || next_action_containing_qr_code_url.is_some()
                || next_action_containing_wait_screen.is_some()
                || papal_sdk_next_action.is_some()
                || next_action_containing_fetch_qr_code_url.is_some()
                || payment_data.get_authentication().is_some()
            {
                next_action_response = bank_transfer_next_steps
                            .map(|bank_transfer| {
                                api_models::payments::NextActionData::DisplayBankTransferInformation {
                                    bank_transfer_steps_and_charges_details: bank_transfer,
                                }
                            })
                            .or(next_action_voucher.map(|voucher_data| {
                                api_models::payments::NextActionData::DisplayVoucherInformation {
                                    voucher_details: voucher_data,
                                }
                            }))
                            .or(next_action_mobile_payment.map(|mobile_payment_data| {
                                api_models::payments::NextActionData::CollectOtp {
                                    consent_data_required: mobile_payment_data.consent_data_required,
                                }
                            }))
                            .or(next_action_containing_qr_code_url.map(|qr_code_data| {
                                api_models::payments::NextActionData::foreign_from(qr_code_data)
                            }))
                            .or(next_action_containing_fetch_qr_code_url.map(|fetch_qr_code_data| {
                                api_models::payments::NextActionData::FetchQrCodeInformation {
                                    qr_code_fetch_url: fetch_qr_code_data.qr_code_fetch_url
                                }
                            }))
                            .or(papal_sdk_next_action.map(|paypal_next_action_data| {
                                api_models::payments::NextActionData::InvokeSdkClient{
                                    next_action_data: paypal_next_action_data
                                }
                            }))
                            .or(next_action_containing_wait_screen.map(|wait_screen_data| {
                                api_models::payments::NextActionData::WaitScreenInformation {
                                    display_from_timestamp: wait_screen_data.display_from_timestamp,
                                    display_to_timestamp: wait_screen_data.display_to_timestamp,
                                    poll_config: wait_screen_data.poll_config,
                                }
                            }))
                            .or(payment_attempt.authentication_data.as_ref().map(|_| {
                                // Check if iframe redirection is enabled in the business profile
                                let redirect_url = helpers::create_startpay_url(
                                    base_url,
                                    &payment_attempt,
                                    &payment_intent,
                                );
                                // Check if redirection inside popup is enabled in the payment intent
                                if payment_intent.is_iframe_redirection_enabled.unwrap_or(false) {
                                    api_models::payments::NextActionData::RedirectInsidePopup {
                                        popup_url: redirect_url,
                                        redirect_response_url:router_return_url
                                    }
                                } else {
                                    api_models::payments::NextActionData::RedirectToUrl {
                                        redirect_to_url: redirect_url,
                                    }
                                }
                            }))
                            .or(match payment_data.get_authentication(){
                                Some(authentication_store) => {
                                    let authentication = &authentication_store.authentication;
                                    if payment_intent.status == common_enums::IntentStatus::RequiresCustomerAction && authentication_store.cavv.is_none() && authentication.is_separate_authn_required(){
                                        // if preAuthn and separate authentication needed.
                                        let poll_config = payment_data.get_poll_config().unwrap_or_default();
                                        let request_poll_id = core_utils::get_external_authentication_request_poll_id(&payment_intent.payment_id);
                                        let payment_connector_name = payment_attempt.connector
                                            .as_ref()
                                            .get_required_value("connector")?;
                                        Some(api_models::payments::NextActionData::ThreeDsInvoke {
                                            three_ds_data: api_models::payments::ThreeDsData {
                                                three_ds_authentication_url: helpers::create_authentication_url(base_url, &payment_attempt),
                                                three_ds_authorize_url: helpers::create_authorize_url(
                                                    base_url,
                                                    &payment_attempt,
                                                    payment_connector_name,
                                                ),
                                                three_ds_method_details: authentication.three_ds_method_url.as_ref().zip(authentication.three_ds_method_data.as_ref()).map(|(three_ds_method_url,three_ds_method_data )|{
                                                    api_models::payments::ThreeDsMethodData::AcsThreeDsMethodData {
                                                        three_ds_method_data_submission: true,
                                                        three_ds_method_data: Some(three_ds_method_data.clone()),
                                                        three_ds_method_url: Some(three_ds_method_url.to_owned()),
                                                    }
                                                }).unwrap_or(api_models::payments::ThreeDsMethodData::AcsThreeDsMethodData {
                                                        three_ds_method_data_submission: false,
                                                        three_ds_method_data: None,
                                                        three_ds_method_url: None,
                                                }),
                                                poll_config: api_models::payments::PollConfigResponse {poll_id: request_poll_id, delay_in_secs: poll_config.delay_in_secs, frequency: poll_config.frequency},
                                                message_version: authentication.message_version.as_ref()
                                                .map(|version| version.to_string()),
                                                directory_server_id: authentication.directory_server_id.clone(),
                                            },
                                        })
                                    }else{
                                        None
                                    }
                                },
                                None => None
                            })
                            .or(match next_action_invoke_hidden_frame{
                                Some(threeds_invoke_data) => Some(construct_connector_invoke_hidden_frame(
                                    threeds_invoke_data,
                                )?),
                                None => None
                            });
            }
        };

        // next action check for third party sdk session (for ex: Apple pay through trustpay has third party sdk session response)
        if third_party_sdk_session_next_action(&payment_attempt, operation) {
            next_action_response = Some(
                api_models::payments::NextActionData::ThirdPartySdkSessionToken {
                    session_token: payment_data.get_sessions_token().first().cloned(),
                },
            )
        }

        let routed_through = payment_attempt.connector.clone();

        let connector_label = routed_through.as_ref().and_then(|connector_name| {
            core_utils::get_connector_label(
                payment_intent.business_country,
                payment_intent.business_label.as_ref(),
                payment_attempt.business_sub_label.as_ref(),
                connector_name,
            )
        });

        let mandate_data = payment_data.get_setup_mandate().map(|d| api::MandateData {
            customer_acceptance: d.customer_acceptance.clone(),

            mandate_type: d.mandate_type.clone().map(|d| match d {
                hyperswitch_domain_models::mandates::MandateDataType::MultiUse(Some(i)) => {
                    api::MandateType::MultiUse(Some(api::MandateAmountData {
                        amount: i.amount,
                        currency: i.currency,
                        start_date: i.start_date,
                        end_date: i.end_date,
                        metadata: i.metadata,
                    }))
                }
                hyperswitch_domain_models::mandates::MandateDataType::SingleUse(i) => {
                    api::MandateType::SingleUse(api::payments::MandateAmountData {
                        amount: i.amount,
                        currency: i.currency,
                        start_date: i.start_date,
                        end_date: i.end_date,
                        metadata: i.metadata,
                    })
                }
                hyperswitch_domain_models::mandates::MandateDataType::MultiUse(None) => {
                    api::MandateType::MultiUse(None)
                }
            }),
            update_mandate_id: d.update_mandate_id.clone(),
        });

        let order_tax_amount = payment_data
            .get_payment_attempt()
            .net_amount
            .get_order_tax_amount()
            .or_else(|| {
                payment_data
                    .get_payment_intent()
                    .tax_details
                    .clone()
                    .and_then(|tax| {
                        tax.payment_method_type
                            .map(|a| a.order_tax_amount)
                            .or_else(|| tax.default.map(|a| a.order_tax_amount))
                    })
            });
        let connector_mandate_id = payment_data.get_mandate_id().and_then(|mandate| {
            mandate
                .mandate_reference_id
                .as_ref()
                .and_then(|mandate_ref| match mandate_ref {
                    api_models::payments::MandateReferenceId::ConnectorMandateId(
                        connector_mandate_reference_id,
                    ) => connector_mandate_reference_id.get_connector_mandate_id(),
                    _ => None,
                })
        });

        let connector_transaction_id = payment_attempt
            .get_connector_payment_id()
            .map(ToString::to_string);

        let payments_response = api::PaymentsResponse {
            payment_id: payment_intent.payment_id,
            merchant_id: payment_intent.merchant_id,
            status: payment_intent.status,
            amount: payment_attempt.net_amount.get_order_amount(),
            net_amount: payment_attempt.get_total_amount(),
            amount_capturable: payment_attempt.amount_capturable,
            amount_received: payment_intent.amount_captured,
            connector: routed_through,
            client_secret: payment_intent.client_secret.map(Secret::new),
            created: Some(payment_intent.created_at),
            currency: currency.to_string(),
            customer_id: customer.as_ref().map(|cus| cus.clone().customer_id),
            customer: customer_details_response,
            description: payment_intent.description,
            refunds: refunds_response,
            disputes: disputes_response,
            attempts: attempts_response,
            captures: captures_response,
            mandate_id,
            mandate_data,
            setup_future_usage: payment_attempt.setup_future_usage_applied,
            off_session: payment_intent.off_session,
            capture_on: None,
            capture_method: payment_attempt.capture_method,
            payment_method: payment_attempt.payment_method,
            payment_method_data: payment_method_data_response,
            payment_token: payment_attempt.payment_token,
            shipping: payment_data
                .get_address()
                .get_shipping()
                .cloned()
                .map(From::from),
            billing: payment_data
                .get_address()
                .get_payment_billing()
                .cloned()
                .map(From::from),
            order_details: payment_intent.order_details,
            email: customer
                .as_ref()
                .and_then(|cus| cus.email.as_ref().map(|s| s.to_owned())),
            name: customer
                .as_ref()
                .and_then(|cus| cus.name.as_ref().map(|s| s.to_owned())),
            phone: customer
                .as_ref()
                .and_then(|cus| cus.phone.as_ref().map(|s| s.to_owned())),
            return_url: payment_intent.return_url,
            authentication_type: payment_attempt.authentication_type,
            statement_descriptor_name: payment_intent.statement_descriptor_name,
            statement_descriptor_suffix: payment_intent.statement_descriptor_suffix,
            next_action: next_action_response,
            cancellation_reason: payment_attempt.cancellation_reason,
            error_code: payment_attempt
                .error_code
                .filter(|code| code != NO_ERROR_CODE),
            error_message: payment_attempt
                .error_reason
                .or(payment_attempt.error_message)
                .filter(|message| message != NO_ERROR_MESSAGE),
            unified_code: payment_attempt.unified_code,
            unified_message: payment_attempt.unified_message,
            payment_experience: payment_attempt.payment_experience,
            payment_method_type: payment_attempt.payment_method_type,
            connector_label,
            business_country: payment_intent.business_country,
            business_label: payment_intent.business_label,
            business_sub_label: payment_attempt.business_sub_label,
            allowed_payment_method_types: payment_intent.allowed_payment_method_types,
            ephemeral_key: payment_data
                .get_ephemeral_key()
                .map(ForeignFrom::foreign_from),
            manual_retry_allowed: helpers::is_manual_retry_allowed(
                &payment_intent.status,
                &payment_attempt.status,
                connector_request_reference_id_config,
                &merchant_id,
            ),
            connector_transaction_id,
            frm_message,
            metadata: payment_intent.metadata,
            connector_metadata: payment_intent.connector_metadata,
            feature_metadata: payment_intent.feature_metadata,
            reference_id: payment_attempt.connector_response_reference_id,
            payment_link: payment_link_data,
            profile_id: payment_intent.profile_id,
            surcharge_details,
            attempt_count: payment_intent.attempt_count,
            merchant_decision,
            merchant_connector_id: payment_attempt.merchant_connector_id,
            incremental_authorization_allowed: payment_intent.incremental_authorization_allowed,
            authorization_count: payment_intent.authorization_count,
            incremental_authorizations: incremental_authorizations_response,
            external_authentication_details,
            external_3ds_authentication_attempted: payment_attempt
                .external_three_ds_authentication_attempted,
            expires_on: payment_intent.session_expiry,
            fingerprint: payment_intent.fingerprint_id,
            browser_info: payment_attempt.browser_info,
            payment_method_id: payment_attempt.payment_method_id,
            network_transaction_id: payment_attempt.network_transaction_id,
            payment_method_status: payment_data
                .get_payment_method_info()
                .map(|info| info.status),
            updated: Some(payment_intent.modified_at),
            split_payments: payment_attempt.charges,
            frm_metadata: payment_intent.frm_metadata,
            merchant_order_reference_id: payment_intent.merchant_order_reference_id,
            order_tax_amount,
            connector_mandate_id,
            shipping_cost: payment_intent.shipping_cost,
            capture_before: payment_attempt.capture_before,
            extended_authorization_applied: payment_attempt.extended_authorization_applied,
            card_discovery: payment_attempt.card_discovery,
            force_3ds_challenge: payment_intent.force_3ds_challenge,
            force_3ds_challenge_trigger: payment_intent.force_3ds_challenge_trigger,
            issuer_error_code: payment_attempt.issuer_error_code,
            issuer_error_message: payment_attempt.issuer_error_message,
            is_iframe_redirection_enabled: payment_intent.is_iframe_redirection_enabled,
            whole_connector_response: payment_data.get_whole_connector_response(),
            payment_channel: payment_intent.payment_channel,
            enable_partial_authorization: payment_intent.enable_partial_authorization,
        };

        services::ApplicationResponse::JsonWithHeaders((payments_response, headers))
    };

    metrics::PAYMENT_OPS_COUNT.add(
        1,
        router_env::metric_attributes!(
            ("operation", format!("{:?}", operation)),
            ("merchant", merchant_id.clone()),
            ("payment_method_type", payment_method_type),
            ("payment_method", payment_method),
        ),
    );

    Ok(output)
}

#[cfg(feature = "v1")]
pub fn third_party_sdk_session_next_action<Op>(
    payment_attempt: &storage::PaymentAttempt,
    operation: &Op,
) -> bool
where
    Op: Debug,
{
    // If the operation is confirm, we will send session token response in next action
    if format!("{operation:?}").eq("PaymentConfirm") {
        let condition1 = payment_attempt
            .connector
            .as_ref()
            .map(|connector| {
                matches!(connector.as_str(), "trustpay") || matches!(connector.as_str(), "payme")
            })
            .and_then(|is_connector_supports_third_party_sdk| {
                if is_connector_supports_third_party_sdk {
                    payment_attempt
                        .payment_method
                        .map(|pm| matches!(pm, diesel_models::enums::PaymentMethod::Wallet))
                } else {
                    Some(false)
                }
            })
            .unwrap_or(false);

        // This condition to be triggered for open banking connectors, third party SDK session token will be provided
        let condition2 = payment_attempt
            .connector
            .as_ref()
            .map(|connector| matches!(connector.as_str(), "plaid"))
            .and_then(|is_connector_supports_third_party_sdk| {
                if is_connector_supports_third_party_sdk {
                    payment_attempt
                        .payment_method
                        .map(|pm| matches!(pm, diesel_models::enums::PaymentMethod::OpenBanking))
                        .and_then(|first_match| {
                            payment_attempt
                                .payment_method_type
                                .map(|pmt| {
                                    matches!(
                                        pmt,
                                        diesel_models::enums::PaymentMethodType::OpenBankingPIS
                                    )
                                })
                                .map(|second_match| first_match && second_match)
                        })
                } else {
                    Some(false)
                }
            })
            .unwrap_or(false);

        condition1 || condition2
    } else {
        false
    }
}

pub fn qr_code_next_steps_check(
    payment_attempt: storage::PaymentAttempt,
) -> RouterResult<Option<api_models::payments::QrCodeInformation>> {
    let qr_code_steps: Option<Result<api_models::payments::QrCodeInformation, _>> = payment_attempt
        .connector_metadata
        .map(|metadata| metadata.parse_value("QrCodeInformation"));

    let qr_code_instructions = qr_code_steps.transpose().ok().flatten();
    Ok(qr_code_instructions)
}
pub fn paypal_sdk_next_steps_check(
    payment_attempt: storage::PaymentAttempt,
) -> RouterResult<Option<api_models::payments::SdkNextActionData>> {
    let paypal_connector_metadata: Option<Result<api_models::payments::SdkNextActionData, _>> =
        payment_attempt.connector_metadata.map(|metadata| {
            metadata.parse_value("SdkNextActionData").map_err(|_| {
                crate::logger::warn!(
                    "SdkNextActionData parsing failed for paypal_connector_metadata"
                )
            })
        });

    let paypal_next_steps = paypal_connector_metadata.transpose().ok().flatten();
    Ok(paypal_next_steps)
}

pub fn fetch_qr_code_url_next_steps_check(
    payment_attempt: storage::PaymentAttempt,
) -> RouterResult<Option<api_models::payments::FetchQrCodeInformation>> {
    let qr_code_steps: Option<Result<api_models::payments::FetchQrCodeInformation, _>> =
        payment_attempt
            .connector_metadata
            .map(|metadata| metadata.parse_value("FetchQrCodeInformation"));

    let qr_code_fetch_url = qr_code_steps.transpose().ok().flatten();
    Ok(qr_code_fetch_url)
}

pub fn wait_screen_next_steps_check(
    payment_attempt: storage::PaymentAttempt,
) -> RouterResult<Option<api_models::payments::WaitScreenInstructions>> {
    let display_info_with_timer_steps: Option<
        Result<api_models::payments::WaitScreenInstructions, _>,
    > = payment_attempt
        .connector_metadata
        .map(|metadata| metadata.parse_value("WaitScreenInstructions"));

    let display_info_with_timer_instructions =
        display_info_with_timer_steps.transpose().ok().flatten();
    Ok(display_info_with_timer_instructions)
}

pub fn next_action_invoke_hidden_frame(
    payment_attempt: &storage::PaymentAttempt,
) -> RouterResult<Option<api_models::payments::PaymentsConnectorThreeDsInvokeData>> {
    let connector_three_ds_invoke_data: Option<
        Result<api_models::payments::PaymentsConnectorThreeDsInvokeData, _>,
    > = payment_attempt
        .connector_metadata
        .clone()
        .map(|metadata| metadata.parse_value("PaymentsConnectorThreeDsInvokeData"));

    let three_ds_invoke_data = connector_three_ds_invoke_data.transpose().ok().flatten();
    Ok(three_ds_invoke_data)
}

pub fn construct_connector_invoke_hidden_frame(
    connector_three_ds_invoke_data: api_models::payments::PaymentsConnectorThreeDsInvokeData,
) -> RouterResult<api_models::payments::NextActionData> {
    let iframe_data = api_models::payments::IframeData::ThreedsInvokeAndCompleteAutorize {
        three_ds_method_data_submission: connector_three_ds_invoke_data
            .three_ds_method_data_submission,
        three_ds_method_data: Some(connector_three_ds_invoke_data.three_ds_method_data),
        three_ds_method_url: connector_three_ds_invoke_data.three_ds_method_url,
        directory_server_id: connector_three_ds_invoke_data.directory_server_id,
        message_version: connector_three_ds_invoke_data.message_version,
    };

    Ok(api_models::payments::NextActionData::InvokeHiddenIframe { iframe_data })
}

#[cfg(feature = "v1")]
impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse {
    fn foreign_from((pi, pa): (storage::PaymentIntent, storage::PaymentAttempt)) -> Self {
        let connector_transaction_id = pa.get_connector_payment_id().map(ToString::to_string);
        Self {
            payment_id: pi.payment_id,
            merchant_id: pi.merchant_id,
            status: pi.status,
            amount: pi.amount,
            amount_capturable: pa.amount_capturable,
            client_secret: pi.client_secret.map(|s| s.into()),
            created: Some(pi.created_at),
            currency: pi.currency.map(|c| c.to_string()).unwrap_or_default(),
            description: pi.description,
            metadata: pi.metadata,
            order_details: pi.order_details,
            customer_id: pi.customer_id.clone(),
            connector: pa.connector,
            payment_method: pa.payment_method,
            payment_method_type: pa.payment_method_type,
            business_label: pi.business_label,
            business_country: pi.business_country,
            business_sub_label: pa.business_sub_label,
            setup_future_usage: pi.setup_future_usage,
            capture_method: pa.capture_method,
            authentication_type: pa.authentication_type,
            connector_transaction_id,
            attempt_count: pi.attempt_count,
            profile_id: pi.profile_id,
            merchant_connector_id: pa.merchant_connector_id,
            payment_method_data: pa.payment_method_data.and_then(|data| {
                match data.parse_value("PaymentMethodDataResponseWithBilling") {
                    Ok(parsed_data) => Some(parsed_data),
                    Err(e) => {
                        router_env::logger::error!("Failed to parse 'PaymentMethodDataResponseWithBilling' from payment method data. Error: {e:?}");
                        None
                    }
                }
            }),
            merchant_order_reference_id: pi.merchant_order_reference_id,
            customer: pi.customer_details.and_then(|customer_details|
                match customer_details.into_inner().expose().parse_value::<CustomerData>("CustomerData"){
                    Ok(parsed_data) => Some(
                        CustomerDetailsResponse {
                            id: pi.customer_id,
                            name: parsed_data.name,
                            phone: parsed_data.phone,
                            email: parsed_data.email,
                            phone_country_code:parsed_data.phone_country_code
                    }),
                    Err(e) => {
                        router_env::logger::error!("Failed to parse 'CustomerDetailsResponse' from payment method data. Error: {e:?}");
                        None
                    }
                }
            ),
            billing: pi.billing_details.and_then(|billing_details|
                match billing_details.into_inner().expose().parse_value::<Address>("Address") {
                    Ok(parsed_data) => Some(parsed_data),
                    Err(e) => {
                        router_env::logger::error!("Failed to parse 'BillingAddress' from payment method data. Error: {e:?}");
                        None
                    }
                }
            ),
            shipping: pi.shipping_details.and_then(|shipping_details|
                match shipping_details.into_inner().expose().parse_value::<Address>("Address") {
                    Ok(parsed_data) => Some(parsed_data),
                    Err(e) => {
                        router_env::logger::error!("Failed to parse 'ShippingAddress' from payment method data. Error: {e:?}");
                        None
                    }
                }
            ),
            // TODO: fill in details based on requirement
            net_amount: pa.net_amount.get_total_amount(),
            amount_received: None,
            refunds: None,
            disputes: None,
            attempts: None,
            captures: None,
            mandate_id: None,
            mandate_data: None,
            off_session: None,
            capture_on: None,
            payment_token: None,
            email: None,
            name: None,
            phone: None,
            return_url: None,
            statement_descriptor_name: None,
            statement_descriptor_suffix: None,
            next_action: None,
            cancellation_reason: None,
            error_code: None,
            error_message: None,
            unified_code: None,
            unified_message: None,
            payment_experience: None,
            connector_label: None,
            allowed_payment_method_types: None,
            ephemeral_key: None,
            manual_retry_allowed: None,
            frm_message: None,
            connector_metadata: None,
            feature_metadata: None,
            reference_id: None,
            payment_link: None,
            surcharge_details: None,
            merchant_decision: None,
            incremental_authorization_allowed: None,
            authorization_count: None,
            incremental_authorizations: None,
            external_authentication_details: None,
            external_3ds_authentication_attempted: None,
            expires_on: None,
            fingerprint: None,
            browser_info: None,
            payment_method_id: None,
            payment_method_status: None,
            updated: None,
            split_payments: None,
            frm_metadata: None,
            capture_before: pa.capture_before,
            extended_authorization_applied: pa.extended_authorization_applied,
            order_tax_amount: None,
            connector_mandate_id:None,
            shipping_cost: None,
            card_discovery: pa.card_discovery,
            force_3ds_challenge: pi.force_3ds_challenge,
            force_3ds_challenge_trigger: pi.force_3ds_challenge_trigger,
            whole_connector_response: None,
            issuer_error_code: pa.issuer_error_code,
            issuer_error_message: pa.issuer_error_message,
            is_iframe_redirection_enabled:pi.is_iframe_redirection_enabled,
            payment_channel: pi.payment_channel,
            network_transaction_id: None,
            enable_partial_authorization: pi.enable_partial_authorization,
        }
    }
}

#[cfg(feature = "v2")]
impl ForeignFrom<(storage::PaymentIntent, Option<storage::PaymentAttempt>)>
    for api_models::payments::PaymentsListResponseItem
{
    fn foreign_from((pi, pa): (storage::PaymentIntent, Option<storage::PaymentAttempt>)) -> Self {
        Self {
            id: pi.id,
            merchant_id: pi.merchant_id,
            profile_id: pi.profile_id,
            customer_id: pi.customer_id,
            payment_method_id: pa.as_ref().and_then(|p| p.payment_method_id.clone()),
            status: pi.status,
            amount: api_models::payments::PaymentAmountDetailsResponse::foreign_from((
                &pi.amount_details,
                pa.as_ref().map(|p| &p.amount_details),
            )),
            created: pi.created_at,
            payment_method_type: pa.as_ref().and_then(|p| p.payment_method_type.into()),
            payment_method_subtype: pa.as_ref().and_then(|p| p.payment_method_subtype.into()),
            connector: pa.as_ref().and_then(|p| p.connector.clone()),
            merchant_connector_id: pa.as_ref().and_then(|p| p.merchant_connector_id.clone()),
            customer: None,
            merchant_reference_id: pi.merchant_reference_id,
            connector_payment_id: pa.as_ref().and_then(|p| p.connector_payment_id.clone()),
            connector_response_reference_id: pa
                .as_ref()
                .and_then(|p| p.connector_response_reference_id.clone()),
            metadata: pi.metadata,
            description: pi.description.map(|val| val.get_string_repr().to_string()),
            authentication_type: pi.authentication_type,
            capture_method: Some(pi.capture_method),
            setup_future_usage: Some(pi.setup_future_usage),
            attempt_count: pi.attempt_count,
            error: pa
                .as_ref()
                .and_then(|p| p.error.as_ref())
                .map(api_models::payments::ErrorDetails::foreign_from),
            cancellation_reason: pa.as_ref().and_then(|p| p.cancellation_reason.clone()),
            order_details: None,
            return_url: pi.return_url,
            statement_descriptor: pi.statement_descriptor,
            allowed_payment_method_types: pi.allowed_payment_method_types,
            authorization_count: pi.authorization_count,
            modified_at: pa.as_ref().map(|p| p.modified_at),
        }
    }
}

#[cfg(feature = "v1")]
impl ForeignFrom<ephemeral_key::EphemeralKey> for api::ephemeral_key::EphemeralKeyCreateResponse {
    fn foreign_from(from: ephemeral_key::EphemeralKey) -> Self {
        Self {
            customer_id: from.customer_id,
            created_at: from.created_at,
            expires: from.expires,
            secret: from.secret,
        }
    }
}

#[cfg(feature = "v1")]
pub fn bank_transfer_next_steps_check(
    payment_attempt: storage::PaymentAttempt,
) -> RouterResult<Option<api_models::payments::BankTransferNextStepsData>> {
    let bank_transfer_next_step = if let Some(diesel_models::enums::PaymentMethod::BankTransfer) =
        payment_attempt.payment_method
    {
        if payment_attempt.payment_method_type != Some(diesel_models::enums::PaymentMethodType::Pix)
        {
            let bank_transfer_next_steps: Option<api_models::payments::BankTransferNextStepsData> =
                payment_attempt
                    .connector_metadata
                    .map(|metadata| {
                        metadata
                            .parse_value("NextStepsRequirements")
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable(
                                "Failed to parse the Value to NextRequirements struct",
                            )
                    })
                    .transpose()?;
            bank_transfer_next_steps
        } else {
            None
        }
    } else {
        None
    };
    Ok(bank_transfer_next_step)
}

#[cfg(feature = "v1")]
pub fn voucher_next_steps_check(
    payment_attempt: storage::PaymentAttempt,
) -> RouterResult<Option<api_models::payments::VoucherNextStepData>> {
    let voucher_next_step = if let Some(diesel_models::enums::PaymentMethod::Voucher) =
        payment_attempt.payment_method
    {
        let voucher_next_steps: Option<api_models::payments::VoucherNextStepData> = payment_attempt
            .connector_metadata
            .map(|metadata| {
                metadata
                    .parse_value("NextStepsRequirements")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to parse the Value to NextRequirements struct")
            })
            .transpose()?;
        voucher_next_steps
    } else {
        None
    };
    Ok(voucher_next_step)
}

#[cfg(feature = "v1")]
pub fn mobile_payment_next_steps_check(
    payment_attempt: &storage::PaymentAttempt,
) -> RouterResult<Option<api_models::payments::MobilePaymentNextStepData>> {
    let mobile_payment_next_step = if let Some(diesel_models::enums::PaymentMethod::MobilePayment) =
        payment_attempt.payment_method
    {
        let mobile_paymebnt_next_steps: Option<api_models::payments::MobilePaymentNextStepData> =
            payment_attempt
                .connector_metadata
                .clone()
                .map(|metadata| {
                    metadata
                        .parse_value("MobilePaymentNextStepData")
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed to parse the Value to NextRequirements struct")
                })
                .transpose()?;
        mobile_paymebnt_next_steps
    } else {
        None
    };
    Ok(mobile_payment_next_step)
}

impl ForeignFrom<api_models::payments::QrCodeInformation> for api_models::payments::NextActionData {
    fn foreign_from(qr_info: api_models::payments::QrCodeInformation) -> Self {
        match qr_info {
            api_models::payments::QrCodeInformation::QrCodeUrl {
                image_data_url,
                qr_code_url,
                display_to_timestamp,
            } => Self::QrCodeInformation {
                image_data_url: Some(image_data_url),
                qr_code_url: Some(qr_code_url),
                display_to_timestamp,
                border_color: None,
                display_text: None,
            },
            api_models::payments::QrCodeInformation::QrDataUrl {
                image_data_url,
                display_to_timestamp,
            } => Self::QrCodeInformation {
                image_data_url: Some(image_data_url),
                display_to_timestamp,
                qr_code_url: None,
                border_color: None,
                display_text: None,
            },
            api_models::payments::QrCodeInformation::QrCodeImageUrl {
                qr_code_url,
                display_to_timestamp,
            } => Self::QrCodeInformation {
                qr_code_url: Some(qr_code_url),
                image_data_url: None,
                display_to_timestamp,
                border_color: None,
                display_text: None,
            },
            api_models::payments::QrCodeInformation::QrColorDataUrl {
                color_image_data_url,
                display_to_timestamp,
                border_color,
                display_text,
            } => Self::QrCodeInformation {
                qr_code_url: None,
                image_data_url: Some(color_image_data_url),
                display_to_timestamp,
                border_color,
                display_text,
            },
        }
    }
}

#[derive(Clone)]
pub struct PaymentAdditionalData<'a, F>
where
    F: Clone,
{
    router_base_url: String,
    connector_name: String,
    payment_data: PaymentData<F>,
    state: &'a SessionState,
    customer_data: &'a Option<domain::Customer>,
}

#[cfg(feature = "v2")]
impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthorizeData {
    type Error = error_stack::Report<errors::ApiErrorResponse>;

    fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result<Self, Self::Error> {
        let payment_data = additional_data.payment_data.clone();
        let router_base_url = &additional_data.router_base_url;
        let connector_name = &additional_data.connector_name;
        let attempt = &payment_data.payment_attempt;
        let browser_info: Option<types::BrowserInformation> = attempt
            .browser_info
            .clone()
            .map(types::BrowserInformation::from);

        let complete_authorize_url = Some(helpers::create_complete_authorize_url(
            router_base_url,
            attempt,
            connector_name,
            payment_data.creds_identifier.as_deref(),
        ));

        let merchant_connector_account_id_or_connector_name = payment_data
            .payment_attempt
            .merchant_connector_id
            .as_ref()
            .map(|mca_id| mca_id.get_string_repr())
            .unwrap_or(connector_name);

        let webhook_url = Some(helpers::create_webhook_url(
            router_base_url,
            &attempt.merchant_id,
            merchant_connector_account_id_or_connector_name,
        ));
        let router_return_url = Some(helpers::create_redirect_url(
            router_base_url,
            attempt,
            connector_name,
            payment_data.creds_identifier.as_deref(),
        ));

        let payment_method_data = payment_data.payment_method_data.or_else(|| {
            if payment_data.mandate_id.is_some() {
                Some(domain::PaymentMethodData::MandatePayment)
            } else {
                None
            }
        });

        let amount = payment_data
            .payment_attempt
            .get_total_amount()
            .get_amount_as_i64();

        let customer_name = additional_data
            .customer_data
            .as_ref()
            .and_then(|customer_data| {
                customer_data
                    .name
                    .as_ref()
                    .map(|customer| customer.clone().into_inner())
            });

        let customer_id = additional_data
            .customer_data
            .as_ref()
            .and_then(|data| data.get_id().clone().try_into().ok());

        let merchant_order_reference_id = payment_data
            .payment_intent
            .merchant_reference_id
            .map(|s| s.get_string_repr().to_string());

        let shipping_cost = payment_data.payment_intent.amount_details.shipping_cost;

        Ok(Self {
            payment_method_data: payment_method_data
                .unwrap_or(domain::PaymentMethodData::Card(domain::Card::default())),
            amount,
            order_tax_amount: None, // V2 doesn't currently support order tax amount
            email: None,            // V2 doesn't store email directly in payment_intent
            customer_name,
            currency: payment_data.currency,
            confirm: true,
            statement_descriptor_suffix: None,
            statement_descriptor: None,
            capture_method: Some(payment_data.payment_intent.capture_method),
            router_return_url,
            webhook_url,
            complete_authorize_url,
            setup_future_usage: Some(payment_data.payment_intent.setup_future_usage),
            mandate_id: payment_data.mandate_id.clone(),
            off_session: get_off_session(payment_data.mandate_id.as_ref(), None),
            customer_acceptance: None,
            setup_mandate_details: None,
            browser_info,
            order_details: None,
            order_category: None,
            session_token: None,
            enrolled_for_3ds: false,
            related_transaction_id: None,
            payment_experience: None,
            payment_method_type: Some(payment_data.payment_attempt.payment_method_subtype),
            surcharge_details: None,
            customer_id,
            request_incremental_authorization: false,
            metadata: payment_data
                .payment_intent
                .metadata
                .clone()
                .map(|m| m.expose()),
            authentication_data: None,
            request_extended_authorization: None,
            split_payments: None,
            minor_amount: payment_data.payment_attempt.get_total_amount(),
            merchant_order_reference_id,
            integrity_object: None,
            shipping_cost,
            additional_payment_method_data: None,
            merchant_account_id: None,
            merchant_config_currency: None,
            connector_testing_data: None,
            order_id: None,
            locale: None,
            payment_channel: None,
            enable_partial_authorization: None,
        })
    }
}

fn get_off_session(
    mandate_id: Option<&MandateIds>,
    off_session_flag: Option<bool>,
) -> Option<bool> {
    match (mandate_id, off_session_flag) {
        (_, Some(false)) => Some(false),
        (Some(_), _) | (_, Some(true)) => Some(true),
        (None, None) => None,
    }
}

#[cfg(feature = "v1")]
impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthorizeData {
    type Error = error_stack::Report<errors::ApiErrorResponse>;

    fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result<Self, Self::Error> {
        let payment_data = additional_data.payment_data.clone();
        let router_base_url = &additional_data.router_base_url;


pub struct CaptureResponse {
    response_code: u8,
    receipt: String,
    declined_code: Option<String>,
    declined_message: Option<String>,
}


impl<const T: u8> ConnectorValidation for DummyConnector<T> {
    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<CaptureMethod>,
        _payment_method: PaymentMethod,
        _pmt: Option<PaymentMethodType>,
    ) -> CustomResult<(), ConnectorError> {
        let capture_method = capture_method.unwrap_or_default();
        match capture_method {
            CaptureMethod::Automatic
            | CaptureMethod::Manual
            | CaptureMethod::SequentialAutomatic => Ok(()),
            CaptureMethod::ManualMultiple | CaptureMethod::Scheduled => Err(
                construct_not_supported_error_report(capture_method, self.id()),
            ),
        }
    }
}

impl ConnectorValidation for for DummyConnector


/// Generate a new GlobalPaymentId from a cell id
    pub fn generate(cell_id: &crate::id_type::CellId) -> Self {
        let global_id = super::GlobalId::generate(cell_id, super::GlobalEntity::Payment);
        Self(global_id)
    }


pub struct VoidResponse {
    #[serde(rename = "@id")]
    pub id: String,
    #[serde(rename = "@reportGroup")]
    pub report_group: String,
    pub cnp_txn_id: String,
    pub response: WorldpayvantivResponseCode,
    pub response_time: String,
    pub post_date: Option<String>,
    pub message: String,
    pub location: Option<String>,
}


pub fn create_attempt_amount_details(
        &self,
        confirm_intent_request: &api_models::payments::PaymentsConfirmIntentRequest,
    ) -> payment_attempt::AttemptAmountDetails {
        let net_amount = self.calculate_net_amount();

        let surcharge_amount = match self.skip_surcharge_calculation {
            common_enums::SurchargeCalculationOverride::Skip => self.surcharge_amount,
            common_enums::SurchargeCalculationOverride::Calculate => None,
        };

        let tax_on_surcharge = match self.skip_surcharge_calculation {
            common_enums::SurchargeCalculationOverride::Skip => self.tax_on_surcharge,
            common_enums::SurchargeCalculationOverride::Calculate => None,
        };

        let order_tax_amount = match self.skip_external_tax_calculation {
            common_enums::TaxCalculationOverride::Skip => {
                self.tax_details.as_ref().and_then(|tax_details| {
                    tax_details.get_tax_amount(Some(confirm_intent_request.payment_method_subtype))
                })
            }
            common_enums::TaxCalculationOverride::Calculate => None,
        };

        payment_attempt::AttemptAmountDetails::from(payment_attempt::AttemptAmountDetailsSetter {
            net_amount,
            amount_to_capture: None,
            surcharge_amount,
            tax_on_surcharge,
            // This will be updated when we receive response from the connector
            amount_capturable: MinorUnit::zero(),
            shipping_cost: self.shipping_cost,
            order_tax_amount,
        })
    }


pub struct ThreedsecureioAuthenticationRequest {
    pub ds_start_protocol_version: String,
    pub ds_end_protocol_version: String,
    pub acs_start_protocol_version: String,
    pub acs_end_protocol_version: String,
    pub three_dsserver_trans_id: String,
    pub acct_number: cards::CardNumber,
    pub notification_url: String,
    pub three_dscomp_ind: ThreeDSecureIoThreeDsCompletionIndicator,
    pub three_dsrequestor_url: String,
    pub acquirer_bin: String,
    pub acquirer_merchant_id: String,
    pub card_expiry_date: String,
    pub bill_addr_city: String,
    pub bill_addr_country: Secret<String>,
    pub bill_addr_line1: Secret<String>,
    pub bill_addr_post_code: Secret<String>,
    pub bill_addr_state: Secret<String>,
    pub email: Option<common_utils::pii::Email>,
    pub three_dsrequestor_authentication_ind: String,
    pub cardholder_name: Option<Secret<String>>,
    pub device_channel: String,
    pub browser_javascript_enabled: Option<bool>,
    pub browser_accept_header: Option<String>,
    pub browser_ip: Option<Secret<String, common_utils::pii::IpAddress>>,
    pub browser_java_enabled: Option<bool>,
    pub browser_language: Option<String>,
    pub browser_color_depth: Option<String>,
    pub browser_screen_height: Option<String>,
    pub browser_screen_width: Option<String>,
    pub browser_tz: Option<String>,
    pub browser_user_agent: Option<String>,
    pub sdk_app_id: Option<String>,
    pub sdk_enc_data: Option<String>,
    pub sdk_ephem_pub_key: Option<std::collections::HashMap<String, String>>,
    pub sdk_reference_number: Option<String>,
    pub sdk_trans_id: Option<String>,
    pub mcc: String,
    pub merchant_country_code: String,
    pub merchant_name: String,
    pub message_category: String,
    pub message_type: String,
    pub message_version: String,
    pub purchase_amount: String,
    pub purchase_currency: String,
    pub purchase_exponent: String,
    pub purchase_date: String,
    pub trans_type: String,
    pub sdk_max_timeout: Option<String>,
    pub device_render_options: Option<DeviceRenderOptions>,
}


pub struct ConnectNameForProfile<'a>(pub FxHashSet<&'a common_enums::connector_enums::Connector>);


pub struct Paypal {
    /// Email linked with paypal account
    #[schema(value_type = String, example = "john.doe@example.com")]
    pub email: Option<Email>,

    /// mobile number linked to paypal account
    #[schema(value_type = String, example = "16608213349")]
    pub telephone_number: Option<Secret<String>>,

    /// id of the paypal account
    #[schema(value_type = String, example = "G83KXTJ5EHCQ2")]
    pub paypal_id: Option<Secret<String>>,
}


impl AddressConstructor for ShippingAddress {
    fn new(
        name: Option<Secret<String>>,
        street: Option<Secret<String>>,
        city: Option<String>,
        post_code: Option<Secret<String>>,
        country: Option<enums::CountryAlpha2>,
    ) -> Self {
        Self {
            name,
            street,
            city,
            post_code,
            country,
        }
    }
}

impl AddressConstructor for for ShippingAddress