// File: crates/hyperswitch_domain_models/src/payments.rs

#[cfg(feature = "v2")]
use std::marker::PhantomData;

#[cfg(feature = "v2")]
use api_models::payments::{ConnectorMetadata, SessionToken, VaultSessionDetails};
use common_types::primitive_wrappers;
#[cfg(feature = "v1")]
use common_types::{
    payments::BillingDescriptor,
    primitive_wrappers::{
        AlwaysRequestExtendedAuthorization, EnableOvercaptureBool, RequestExtendedAuthorizationBool,
    },
};
use common_utils::{
    self,
    crypto::Encryptable,
    encryption::Encryption,
    errors::CustomResult,
    ext_traits::ValueExt,
    id_type, pii,
    types::{keymanager::ToEncryptable, CreatedBy, MinorUnit},
};
use diesel_models::payment_intent::TaxDetails;
#[cfg(feature = "v2")]
use error_stack::ResultExt;
use masking::Secret;
use router_derive::ToEncryption;
use rustc_hash::FxHashMap;
use serde_json::Value;
use time::PrimitiveDateTime;

pub mod payment_attempt;
pub mod payment_intent;
#[cfg(feature = "v2")]
pub mod split_payments;

use common_enums as storage_enums;
#[cfg(feature = "v2")]
use diesel_models::types::{FeatureMetadata, OrderDetailsWithAmount};

use self::payment_attempt::PaymentAttempt;
#[cfg(feature = "v2")]
use crate::{
    address::Address, business_profile, customer, errors, merchant_connector_account,
    merchant_connector_account::MerchantConnectorAccountTypeDetails, payment_address,
    payment_method_data, payment_methods, platform, revenue_recovery, routing,
    ApiModelToDieselModelConvertor,
};
#[cfg(feature = "v1")]
use crate::{payment_method_data, RemoteStorageObject};

#[cfg(feature = "v1")]
#[derive(Clone, Debug, PartialEq, serde::Serialize, ToEncryption)]
pub struct PaymentIntent {
    pub payment_id: id_type::PaymentId,
    pub merchant_id: id_type::MerchantId,
    pub status: storage_enums::IntentStatus,
    pub amount: MinorUnit,
    pub shipping_cost: Option<MinorUnit>,
    pub currency: Option<storage_enums::Currency>,
    pub amount_captured: Option<MinorUnit>,
    pub customer_id: Option<id_type::CustomerId>,
    pub description: Option<String>,
    pub return_url: Option<String>,
    pub metadata: Option<Value>,
    pub connector_id: Option<String>,
    pub shipping_address_id: Option<String>,
    pub billing_address_id: Option<String>,
    pub statement_descriptor_name: Option<String>,
    pub statement_descriptor_suffix: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub last_synced: Option<PrimitiveDateTime>,
    pub setup_future_usage: Option<storage_enums::FutureUsage>,
    pub off_session: Option<bool>,
    pub client_secret: Option<String>,
    pub active_attempt: RemoteStorageObject<PaymentAttempt>,
    pub business_country: Option<storage_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub order_details: Option<Vec<pii::SecretSerdeValue>>,
    pub allowed_payment_method_types: Option<Value>,
    pub connector_metadata: Option<Value>,
    pub feature_metadata: Option<Value>,
    pub attempt_count: i16,
    pub profile_id: Option<id_type::ProfileId>,
    pub payment_link_id: Option<String>,
    // Denotes the action(approve or reject) taken by merchant in case of manual review.
    // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment
    pub merchant_decision: Option<String>,
    pub payment_confirm_source: Option<storage_enums::PaymentSource>,

    pub updated_by: String,
    pub surcharge_applicable: Option<bool>,
    pub request_incremental_authorization: Option<storage_enums::RequestIncrementalAuthorization>,
    pub incremental_authorization_allowed: Option<bool>,
    pub authorization_count: Option<i32>,
    pub fingerprint_id: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub session_expiry: Option<PrimitiveDateTime>,
    pub request_external_three_ds_authentication: Option<bool>,
    pub split_payments: Option<common_types::payments::SplitPaymentsRequest>,
    pub frm_metadata: Option<pii::SecretSerdeValue>,
    #[encrypt]
    pub customer_details: Option<Encryptable<Secret<Value>>>,
    #[encrypt]
    pub billing_details: Option<Encryptable<Secret<Value>>>,
    pub merchant_order_reference_id: Option<String>,
    #[encrypt]
    pub shipping_details: Option<Encryptable<Secret<Value>>>,
    pub is_payment_processor_token_flow: Option<bool>,
    pub organization_id: id_type::OrganizationId,
    pub tax_details: Option<TaxDetails>,
    pub skip_external_tax_calculation: Option<bool>,
    pub request_extended_authorization: Option<RequestExtendedAuthorizationBool>,
    pub psd2_sca_exemption_type: Option<storage_enums::ScaExemptionType>,
    pub processor_merchant_id: id_type::MerchantId,
    pub created_by: Option<CreatedBy>,
    pub force_3ds_challenge: Option<bool>,
    pub force_3ds_challenge_trigger: Option<bool>,
    pub is_iframe_redirection_enabled: Option<bool>,
    pub is_payment_id_from_merchant: Option<bool>,
    pub payment_channel: Option<common_enums::PaymentChannel>,
    pub tax_status: Option<storage_enums::TaxStatus>,
    pub discount_amount: Option<MinorUnit>,
    pub order_date: Option<PrimitiveDateTime>,
    pub shipping_amount_tax: Option<MinorUnit>,
    pub duty_amount: Option<MinorUnit>,
    pub enable_partial_authorization: Option<primitive_wrappers::EnablePartialAuthorizationBool>,
    pub enable_overcapture: Option<EnableOvercaptureBool>,
    pub mit_category: Option<common_enums::MitCategory>,
    pub billing_descriptor: Option<BillingDescriptor>,
    pub tokenization: Option<common_enums::Tokenization>,
    pub partner_merchant_identifier_details:
        Option<common_types::payments::PartnerMerchantIdentifierDetails>,
}

impl PaymentIntent {
    #[cfg(feature = "v1")]
    pub fn get_id(&self) -> &id_type::PaymentId {
        &self.payment_id
    }

    #[cfg(feature = "v2")]
    pub fn get_id(&self) -> &id_type::GlobalPaymentId {
        &self.id
    }

    #[cfg(feature = "v2")]
    /// This is the url to which the customer will be redirected to, to complete the redirection flow
    pub fn create_start_redirection_url(
        &self,
        base_url: &str,
        publishable_key: String,
    ) -> CustomResult<url::Url, errors::api_error_response::ApiErrorResponse> {
        let start_redirection_url = &format!(
            "{}/v2/payments/{}/start-redirection?publishable_key={}&profile_id={}",
            base_url,
            self.get_id().get_string_repr(),
            publishable_key,
            self.profile_id.get_string_repr()
        );
        url::Url::parse(start_redirection_url)
            .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
            .attach_printable("Error creating start redirection url")
    }
    #[cfg(feature = "v1")]
    pub fn get_request_extended_authorization_bool_if_connector_supports(
        &self,
        connector: common_enums::connector_enums::Connector,
        always_request_extended_authorization_optional: Option<AlwaysRequestExtendedAuthorization>,
        payment_method_optional: Option<common_enums::PaymentMethod>,
        payment_method_type_optional: Option<common_enums::PaymentMethodType>,
    ) -> Option<RequestExtendedAuthorizationBool> {
        use router_env::logger;

        let is_extended_authorization_supported_by_connector = || {
            let supported_pms = connector.get_payment_methods_supporting_extended_authorization();
            let supported_pmts =
                connector.get_payment_method_types_supporting_extended_authorization();
            // check if payment method or payment method type is supported by the connector
            logger::info!(
                "Extended Authentication Connector:{:?}, Supported payment methods: {:?}, Supported payment method types: {:?}, Payment method Selected: {:?}, Payment method type Selected: {:?}",
                connector,
                supported_pms,
                supported_pmts,
                payment_method_optional,
                payment_method_type_optional
            );
            match (payment_method_optional, payment_method_type_optional) {
                (Some(payment_method), Some(payment_method_type)) => {
                    supported_pms.contains(&payment_method)
                        && supported_pmts.contains(&payment_method_type)
                }
                (Some(payment_method), None) => supported_pms.contains(&payment_method),
                (None, Some(payment_method_type)) => supported_pmts.contains(&payment_method_type),
                (None, None) => false,
            }
        };
        let intent_request_extended_authorization_optional = self.request_extended_authorization;

        let is_extended_authorization_requested = intent_request_extended_authorization_optional
            .map(|should_request_extended_authorization| *should_request_extended_authorization)
            .or(always_request_extended_authorization_optional.map(
                |should_always_request_extended_authorization| {
                    *should_always_request_extended_authorization
                },
            ));

        is_extended_authorization_requested
            .map(|requested| {
                if requested {
                    is_extended_authorization_supported_by_connector()
                } else {
                    false
                }
            })
            .map(RequestExtendedAuthorizationBool::from)
    }

    #[cfg(feature = "v1")]
    pub fn get_enable_overcapture_bool_if_connector_supports(
        &self,
        connector: common_enums::connector_enums::Connector,
        always_enable_overcapture: Option<primitive_wrappers::AlwaysEnableOvercaptureBool>,
        capture_method: &Option<common_enums::CaptureMethod>,
    ) -> Option<EnableOvercaptureBool> {
        let is_overcapture_supported_by_connector =
            connector.is_overcapture_supported_by_connector();
        if matches!(capture_method, Some(common_enums::CaptureMethod::Manual))
            && is_overcapture_supported_by_connector
        {
            self.enable_overcapture
                .or_else(|| always_enable_overcapture.map(EnableOvercaptureBool::from))
        } else {
            None
        }
    }

    #[cfg(feature = "v2")]
    /// This is the url to which the customer will be redirected to, after completing the redirection flow
    pub fn create_finish_redirection_url(
        &self,
        base_url: &str,
        publishable_key: &str,
    ) -> CustomResult<url::Url, errors::api_error_response::ApiErrorResponse> {
        let finish_redirection_url = format!(
            "{base_url}/v2/payments/{}/finish-redirection/{publishable_key}/{}",
            self.id.get_string_repr(),
            self.profile_id.get_string_repr()
        );

        url::Url::parse(&finish_redirection_url)
            .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
            .attach_printable("Error creating finish redirection url")
    }

    pub fn parse_and_get_metadata<T>(
        &self,
        type_name: &'static str,
    ) -> CustomResult<Option<T>, common_utils::errors::ParsingError>
    where
        T: for<'de> masking::Deserialize<'de>,
    {
        self.metadata
            .clone()
            .map(|metadata| metadata.parse_value(type_name))
            .transpose()
    }

    #[cfg(feature = "v1")]
    pub fn merge_metadata(
        &self,
        request_metadata: Value,
    ) -> Result<Value, common_utils::errors::ParsingError> {
        if !request_metadata.is_null() {
            match (&self.metadata, &request_metadata) {
                (Some(Value::Object(existing_map)), Value::Object(req_map)) => {
                    let mut merged = existing_map.clone();
                    merged.extend(req_map.clone());
                    Ok(Value::Object(merged))
                }
                (None, Value::Object(_)) => Ok(request_metadata),
                _ => {
                    router_env::logger::error!(
                        "Expected metadata to be an object, got: {:?}",
                        request_metadata
                    );
                    Err(common_utils::errors::ParsingError::UnknownError)
                }
            }
        } else {
            router_env::logger::error!("Metadata does not contain any key");
            Err(common_utils::errors::ParsingError::UnknownError)
        }
    }

    #[cfg(feature = "v1")]
    pub fn get_billing_descriptor(&self) -> Option<BillingDescriptor> {
        self.billing_descriptor
            .as_ref()
            .map(|descriptor| BillingDescriptor {
                name: descriptor.name.clone(),
                city: descriptor.city.clone(),
                phone: descriptor.phone.clone(),
                statement_descriptor: descriptor
                    .statement_descriptor
                    .clone()
                    .or_else(|| self.statement_descriptor_name.clone()),
                statement_descriptor_suffix: descriptor
                    .statement_descriptor_suffix
                    .clone()
                    .or_else(|| self.statement_descriptor_suffix.clone()),
                reference: descriptor.reference.clone(),
            })
            .or_else(|| {
                // Only build a fallback if at least one descriptor exists
                if self.statement_descriptor_name.is_some()
                    || self.statement_descriptor_suffix.is_some()
                {
                    Some(BillingDescriptor {
                        name: None,
                        city: None,
                        phone: None,
                        reference: None,
                        statement_descriptor: self.statement_descriptor_name.clone(),
                        statement_descriptor_suffix: self.statement_descriptor_suffix.clone(),
                    })
                } else {
                    None
                }
            })
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, PartialEq, serde::Serialize)]
pub struct AmountDetails {
    /// The amount of the order in the lowest denomination of currency
    pub order_amount: MinorUnit,
    /// The currency of the order
    pub currency: common_enums::Currency,
    /// The shipping cost of the order. This has to be collected from the merchant
    pub shipping_cost: Option<MinorUnit>,
    /// Tax details related to the order. This will be calculated by the external tax provider
    pub tax_details: Option<TaxDetails>,
    /// The action to whether calculate tax by calling external tax provider or not
    pub skip_external_tax_calculation: common_enums::TaxCalculationOverride,
    /// The action to whether calculate surcharge or not
    pub skip_surcharge_calculation: common_enums::SurchargeCalculationOverride,
    /// The surcharge amount to be added to the order, collected from the merchant
    pub surcharge_amount: Option<MinorUnit>,
    /// tax on surcharge amount
    pub tax_on_surcharge: Option<MinorUnit>,
    /// The total amount captured for the order. This is the sum of all the captured amounts for the order.
    /// For automatic captures, this will be the same as net amount for the order
    pub amount_captured: Option<MinorUnit>,
}

#[cfg(feature = "v2")]
impl AmountDetails {
    /// Get the action to whether calculate surcharge or not as a boolean value
    fn get_surcharge_action_as_bool(&self) -> bool {
        self.skip_surcharge_calculation.as_bool()
    }

    /// Get the action to whether calculate external tax or not as a boolean value
    pub fn get_external_tax_action_as_bool(&self) -> bool {
        self.skip_external_tax_calculation.as_bool()
    }

    /// Calculate the net amount for the order
    pub fn calculate_net_amount(&self) -> MinorUnit {
        self.order_amount
            + self.shipping_cost.unwrap_or(MinorUnit::zero())
            + self.surcharge_amount.unwrap_or(MinorUnit::zero())
            + self.tax_on_surcharge.unwrap_or(MinorUnit::zero())
    }

    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 fn create_split_attempt_amount_details(
        &self,
        confirm_intent_request: &api_models::payments::PaymentsConfirmIntentRequest,
        split_amount: MinorUnit,
    ) -> payment_attempt::AttemptAmountDetails {
        let net_amount = split_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 fn proxy_create_attempt_amount_details(
        &self,
        _confirm_intent_request: &api_models::payments::ProxyPaymentsRequest,
    ) -> 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(None)),
            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 fn update_from_request(self, req: &api_models::payments::AmountDetailsUpdate) -> Self {
        Self {
            order_amount: req
                .order_amount()
                .unwrap_or(self.order_amount.into())
                .into(),
            currency: req.currency().unwrap_or(self.currency),
            shipping_cost: req.shipping_cost().or(self.shipping_cost),
            tax_details: req
                .order_tax_amount()
                .map(|order_tax_amount| TaxDetails {
                    default: Some(diesel_models::DefaultTax { order_tax_amount }),
                    payment_method_type: None,
                })
                .or(self.tax_details),
            skip_external_tax_calculation: req
                .skip_external_tax_calculation()
                .unwrap_or(self.skip_external_tax_calculation),
            skip_surcharge_calculation: req
                .skip_surcharge_calculation()
                .unwrap_or(self.skip_surcharge_calculation),
            surcharge_amount: req.surcharge_amount().or(self.surcharge_amount),
            tax_on_surcharge: req.tax_on_surcharge().or(self.tax_on_surcharge),
            amount_captured: self.amount_captured,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, PartialEq, serde::Serialize, ToEncryption)]
pub struct PaymentIntent {
    /// The global identifier for the payment intent. This is generated by the system.
    /// The format of the global id is `{cell_id:5}_pay_{time_ordered_uuid:32}`.
    pub id: id_type::GlobalPaymentId,
    /// The identifier for the merchant. This is automatically derived from the api key used to create the payment.
    pub merchant_id: id_type::MerchantId,
    /// The status of payment intent.
    pub status: storage_enums::IntentStatus,
    /// The amount related details of the payment
    pub amount_details: AmountDetails,
    /// The total amount captured for the order. This is the sum of all the captured amounts for the order.
    pub amount_captured: Option<MinorUnit>,
    /// The identifier for the customer. This is the identifier for the customer in the merchant's system.
    pub customer_id: Option<id_type::GlobalCustomerId>,
    /// The description of the order. This will be passed to connectors which support description.
    pub description: Option<common_utils::types::Description>,
    /// The return url for the payment. This is the url to which the user will be redirected after the payment is completed.
    pub return_url: Option<common_utils::types::Url>,
    /// The metadata for the payment intent. This is the metadata that will be passed to the connectors.
    pub metadata: Option<pii::SecretSerdeValue>,
    /// The statement descriptor for the order, this will be displayed in the user's bank statement.
    pub statement_descriptor: Option<common_utils::types::StatementDescriptor>,
    /// The time at which the order was created
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    /// The time at which the order was last modified
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub last_synced: Option<PrimitiveDateTime>,
    pub setup_future_usage: storage_enums::FutureUsage,
    /// The active attempt for the payment intent. This is the payment attempt that is currently active for the payment intent.
    pub active_attempt_id: Option<id_type::GlobalAttemptId>,
    /// This field represents whether there are attempt groups for this payment intent. Used in split payments workflow
    pub active_attempt_id_type: common_enums::ActiveAttemptIDType,
    /// The ID of the active attempt group for the payment intent
    pub active_attempts_group_id: Option<id_type::GlobalAttemptGroupId>,
    /// The order details for the payment.
    pub order_details: Option<Vec<Secret<OrderDetailsWithAmount>>>,
    /// This is the list of payment method types that are allowed for the payment intent.
    /// This field allows the merchant to restrict the payment methods that can be used for the payment intent.
    pub allowed_payment_method_types: Option<Vec<common_enums::PaymentMethodType>>,
    /// This metadata contains connector-specific details like Apple Pay certificates, Airwallex data, Noon order category, Braintree merchant account ID, and Adyen testing data
    pub connector_metadata: Option<ConnectorMetadata>,
    pub feature_metadata: Option<FeatureMetadata>,
    /// Number of attempts that have been made for the order
    pub attempt_count: i16,
    /// The profile id for the payment.
    pub profile_id: id_type::ProfileId,
    /// The payment link id for the payment. This is generated only if `enable_payment_link` is set to true.
    pub payment_link_id: Option<String>,
    /// This Denotes the action(approve or reject) taken by merchant in case of manual review.
    /// Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment
    pub frm_merchant_decision: Option<common_enums::MerchantDecision>,
    /// Denotes the last instance which updated the payment
    pub updated_by: String,
    /// Denotes whether merchant requested for incremental authorization to be enabled for this payment.
    pub request_incremental_authorization: storage_enums::RequestIncrementalAuthorization,
    /// Denotes whether merchant requested for split payments to be enabled for this payment
    pub split_txns_enabled: storage_enums::SplitTxnsEnabled,
    /// Denotes the number of authorizations that have been made for the payment.
    pub authorization_count: Option<i32>,
    /// Denotes the client secret expiry for the payment. This is the time at which the client secret will expire.
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub session_expiry: PrimitiveDateTime,
    /// Denotes whether merchant requested for 3ds authentication to be enabled for this payment.
    pub request_external_three_ds_authentication: common_enums::External3dsAuthenticationRequest,
    /// Metadata related to fraud and risk management
    pub frm_metadata: Option<pii::SecretSerdeValue>,
    /// The details of the customer in a denormalized form. Only a subset of fields are stored.
    #[encrypt]
    pub customer_details: Option<Encryptable<Secret<Value>>>,
    /// The reference id for the order in the merchant's system. This value can be passed by the merchant.
    pub merchant_reference_id: Option<id_type::PaymentReferenceId>,
    /// The billing address for the order in a denormalized form.
    #[encrypt(ty = Value)]
    pub billing_address: Option<Encryptable<Address>>,
    /// The shipping address for the order in a denormalized form.
    #[encrypt(ty = Value)]
    pub shipping_address: Option<Encryptable<Address>>,
    /// Capture method for the payment
    pub capture_method: storage_enums::CaptureMethod,
    /// Authentication type that is requested by the merchant for this payment.
    pub authentication_type: Option<common_enums::AuthenticationType>,
    /// This contains the pre routing results that are done when routing is done during listing the payment methods.
    pub prerouting_algorithm: Option<routing::PaymentRoutingInfo>,
    /// The organization id for the payment. This is derived from the merchant account
    pub organization_id: id_type::OrganizationId,
    /// Denotes the request by the merchant whether to enable a payment link for this payment.
    pub enable_payment_link: common_enums::EnablePaymentLinkRequest,
    /// Denotes the request by the merchant whether to apply MIT exemption for this payment
    pub apply_mit_exemption: common_enums::MitExemptionRequest,
    /// Denotes whether the customer is present during the payment flow. This information may be used for 3ds authentication
    pub customer_present: common_enums::PresenceOfCustomerDuringPayment,
    /// Denotes the override for payment link configuration
    pub payment_link_config: Option<diesel_models::PaymentLinkConfigRequestForPayments>,
    /// The straight through routing algorithm id that is used for this payment. This overrides the default routing algorithm that is configured in business profile.
    pub routing_algorithm_id: Option<id_type::RoutingId>,
    /// Split Payment Data
    pub split_payments: Option<common_types::payments::SplitPaymentsRequest>,

    pub force_3ds_challenge: Option<bool>,
    pub force_3ds_challenge_trigger: Option<bool>,
    /// merchant who owns the credentials of the processor, i.e. processor owner
    pub processor_merchant_id: id_type::MerchantId,
    /// merchant or user who invoked the resource-based API (identifier) and the source (Api, Jwt(Dashboard))
    pub created_by: Option<CreatedBy>,

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

    /// Indicates whether the payment_id was provided by the merchant (true),
    /// or generated internally by Hyperswitch (false)
    pub is_payment_id_from_merchant: Option<bool>,
    /// Denotes whether merchant requested for partial authorization to be enabled for this payment.
    pub enable_partial_authorization: Option<primitive_wrappers::EnablePartialAuthorizationBool>,
}

#[cfg(feature = "v2")]
impl PaymentIntent {
    /// Extract customer_id from payment intent feature metadata
    pub fn extract_connector_customer_id_from_payment_intent(
        &self,
    ) -> Result<String, common_utils::errors::ValidationError> {
        self.feature_metadata
            .as_ref()
            .and_then(|metadata| metadata.payment_revenue_recovery_metadata.as_ref())
            .map(|recovery| {
                recovery
                    .billing_connector_payment_details
                    .connector_customer_id
                    .clone()
            })
            .ok_or(
                common_utils::errors::ValidationError::MissingRequiredField {
                    field_name: "connector_customer_id".to_string(),
                },
            )
    }

    fn get_payment_method_sub_type(&self) -> Option<common_enums::PaymentMethodType> {
        self.feature_metadata
            .as_ref()
            .and_then(|metadata| metadata.get_payment_method_sub_type())
    }

    fn get_payment_method_type(&self) -> Option<common_enums::PaymentMethod> {
        self.feature_metadata
            .as_ref()
            .and_then(|metadata| metadata.get_payment_method_type())
    }

    pub fn get_connector_customer_id_from_feature_metadata(&self) -> Option<String> {
        self.feature_metadata
            .as_ref()
            .and_then(|metadata| metadata.payment_revenue_recovery_metadata.as_ref())
            .map(|recovery_metadata| {
                recovery_metadata
                    .billing_connector_payment_details
                    .connector_customer_id
                    .clone()
            })
    }

    pub fn get_billing_merchant_connector_account_id(
        &self,
    ) -> Option<id_type::MerchantConnectorAccountId> {
        self.feature_metadata.as_ref().and_then(|feature_metadata| {
            feature_metadata.get_billing_merchant_connector_account_id()
        })
    }

    fn get_request_incremental_authorization_value(
        request: &api_models::payments::PaymentsCreateIntentRequest,
    ) -> CustomResult<
        common_enums::RequestIncrementalAuthorization,
        errors::api_error_response::ApiErrorResponse,
    > {
        request.request_incremental_authorization
            .map(|request_incremental_authorization| {
                if request_incremental_authorization == common_enums::RequestIncrementalAuthorization::True {
                    if request.capture_method == Some(common_enums::CaptureMethod::Automatic) {
                        Err(errors::api_error_response::ApiErrorResponse::InvalidRequestData { message: "incremental authorization is not supported when capture_method is automatic".to_owned() })?
                    }
                    Ok(common_enums::RequestIncrementalAuthorization::True)
                } else {
                    Ok(common_enums::RequestIncrementalAuthorization::False)
                }
            })
            .unwrap_or(Ok(common_enums::RequestIncrementalAuthorization::default()))
    }

    pub async fn create_domain_model_from_request(
        payment_id: &id_type::GlobalPaymentId,
        platform: &platform::Platform,
        profile: &business_profile::Profile,
        request: api_models::payments::PaymentsCreateIntentRequest,
        decrypted_payment_intent: DecryptedPaymentIntent,
    ) -> CustomResult<Self, errors::api_error_response::ApiErrorResponse> {
        let request_incremental_authorization =
            Self::get_request_incremental_authorization_value(&request)?;
        let allowed_payment_method_types = request.allowed_payment_method_types;

        let session_expiry =
            common_utils::date_time::now().saturating_add(time::Duration::seconds(
                request.session_expiry.map(i64::from).unwrap_or(
                    profile
                        .session_expiry
                        .unwrap_or(common_utils::consts::DEFAULT_SESSION_EXPIRY),
                ),
            ));
        let order_details = request.order_details.map(|order_details| {
            order_details
                .into_iter()
                .map(|order_detail| Secret::new(OrderDetailsWithAmount::convert_from(order_detail)))
                .collect()
        });

        Ok(Self {
            id: payment_id.clone(),
            merchant_id: platform.get_processor().get_account().get_id().clone(),
            // Intent status would be RequiresPaymentMethod because we are creating a new payment intent
            status: common_enums::IntentStatus::RequiresPaymentMethod,
            amount_details: AmountDetails::from(request.amount_details),
            amount_captured: None,
            customer_id: request.customer_id,
            description: request.description,
            return_url: request.return_url,
            metadata: request.metadata,
            statement_descriptor: request.statement_descriptor,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            last_synced: None,
            setup_future_usage: request.setup_future_usage.unwrap_or_default(),
            active_attempt_id: None,
            active_attempt_id_type: common_enums::ActiveAttemptIDType::AttemptID,
            active_attempts_group_id: None,
            order_details,
            allowed_payment_method_types,
            connector_metadata: request.connector_metadata,
            feature_metadata: request.feature_metadata.map(FeatureMetadata::convert_from),
            // Attempt count is 0 in create intent as no attempt is made yet
            attempt_count: 0,
            profile_id: profile.get_id().clone(),
            payment_link_id: None,
            frm_merchant_decision: None,
            updated_by: platform
                .get_processor()
                .get_account()
                .storage_scheme
                .to_string(),
            request_incremental_authorization,
            // Authorization count is 0 in create intent as no authorization is made yet
            authorization_count: Some(0),
            session_expiry,
            request_external_three_ds_authentication: request
                .request_external_three_ds_authentication
                .unwrap_or_default(),
            split_txns_enabled: profile.split_txns_enabled,
            frm_metadata: request.frm_metadata,
            customer_details: None,
            merchant_reference_id: request.merchant_reference_id,
            billing_address: decrypted_payment_intent
                .billing_address
                .as_ref()
                .map(|data| {
                    data.clone()
                        .deserialize_inner_value(|value| value.parse_value("Address"))
                })
                .transpose()
                .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
                .attach_printable("Unable to decode billing address")?,
            shipping_address: decrypted_payment_intent
                .shipping_address
                .as_ref()
                .map(|data| {
                    data.clone()
                        .deserialize_inner_value(|value| value.parse_value("Address"))
                })
                .transpose()
                .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError)
                .attach_printable("Unable to decode shipping address")?,
            capture_method: request.capture_method.unwrap_or_default(),
            authentication_type: request.authentication_type,
            prerouting_algorithm: None,
            organization_id: platform
                .get_processor()
                .get_account()
                .organization_id
                .clone(),
            enable_payment_link: request.payment_link_enabled.unwrap_or_default(),
            apply_mit_exemption: request.apply_mit_exemption.unwrap_or_default(),
            customer_present: request.customer_present.unwrap_or_default(),
            payment_link_config: request
                .payment_link_config
                .map(ApiModelToDieselModelConvertor::convert_from),
            routing_algorithm_id: request.routing_algorithm_id,
            split_payments: None,
            force_3ds_challenge: None,
            force_3ds_challenge_trigger: None,
            processor_merchant_id: platform.get_processor().get_account().get_id().clone(),
            created_by: None,
            is_iframe_redirection_enabled: None,
            is_payment_id_from_merchant: None,
            enable_partial_authorization: request.enable_partial_authorization,
        })
    }

    pub fn get_revenue_recovery_metadata(
        &self,
    ) -> Option<diesel_models::types::PaymentRevenueRecoveryMetadata> {
        self.feature_metadata
            .as_ref()
            .and_then(|feature_metadata| feature_metadata.payment_revenue_recovery_metadata.clone())
    }

    pub fn get_feature_metadata(&self) -> Option<FeatureMetadata> {
        self.feature_metadata.clone()
    }

    pub fn create_revenue_recovery_attempt_data(
        &self,
        revenue_recovery_metadata: api_models::payments::PaymentRevenueRecoveryMetadata,
        billing_connector_account: &merchant_connector_account::MerchantConnectorAccount,
        card_info: api_models::payments::AdditionalCardInfo,
        payment_processor_token: &str,
    ) -> CustomResult<
        revenue_recovery::RevenueRecoveryAttemptData,
        errors::api_error_response::ApiErrorResponse,
    > {
        let merchant_reference_id = self.merchant_reference_id.clone().ok_or_else(|| {
            error_stack::report!(
                errors::api_error_response::ApiErrorResponse::GenericNotFoundError {
                    message: "mandate reference id not found".to_string()
                }
            )
        })?;

        let connector_account_reference_id = billing_connector_account
            .get_account_reference_id_using_payment_merchant_connector_account_id(
                revenue_recovery_metadata.active_attempt_payment_connector_id,
            )
            .ok_or_else(|| {
                error_stack::report!(
                    errors::api_error_response::ApiErrorResponse::GenericNotFoundError {
                        message: "connector account reference id not found".to_string()
                    }
                )
            })?;

        Ok(revenue_recovery::RevenueRecoveryAttemptData {
            amount: self.amount_details.order_amount,
            currency: self.amount_details.currency,
            merchant_reference_id,
            connector_transaction_id: None, // No connector id
            error_code: None,
            error_message: None,
            processor_payment_method_token: payment_processor_token.to_string(),
            connector_customer_id: revenue_recovery_metadata
                .billing_connector_payment_details
                .connector_customer_id,
            connector_account_reference_id,
            transaction_created_at: None, // would unwrap_or as now
            status: common_enums::AttemptStatus::Started,
            payment_method_type: self
                .get_payment_method_type()
                .unwrap_or(revenue_recovery_metadata.payment_method_type),
            payment_method_sub_type: self
                .get_payment_method_sub_type()
                .unwrap_or(revenue_recovery_metadata.payment_method_subtype),
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            retry_count: None,
            invoice_next_billing_time: None,
            invoice_billing_started_at_time: None,
            // No charge id is present here since it is an internal payment and we didn't call connector yet.
            charge_id: None,
            card_info: card_info.clone(),
        })
    }

    pub fn get_optional_customer_id(
        &self,
    ) -> CustomResult<Option<id_type::CustomerId>, common_utils::errors::ValidationError> {
        self.customer_id
            .as_ref()
            .map(|customer_id| id_type::CustomerId::try_from(customer_id.clone()))
            .transpose()
    }

    pub fn get_currency(&self) -> storage_enums::Currency {
        self.amount_details.currency
    }

    pub fn supports_split_payments(&self) -> bool {
        self.split_txns_enabled == common_enums::SplitTxnsEnabled::Enable
    }

    pub fn is_split_payment(&self) -> bool {
        self.split_txns_enabled == common_enums::SplitTxnsEnabled::Enable
            && self.active_attempt_id_type == common_enums::ActiveAttemptIDType::GroupID
    }

    pub fn is_succeeded(&self) -> bool {
        match self.status {
            common_enums::IntentStatus::Succeeded => true,
            common_enums::IntentStatus::Failed
            | common_enums::IntentStatus::Cancelled
            | common_enums::IntentStatus::CancelledPostCapture
            | common_enums::IntentStatus::PartiallyCaptured
            | common_enums::IntentStatus::Expired
            | common_enums::IntentStatus::Processing
            | common_enums::IntentStatus::RequiresCustomerAction
            | common_enums::IntentStatus::RequiresMerchantAction
            | common_enums::IntentStatus::RequiresPaymentMethod
            | common_enums::IntentStatus::RequiresConfirmation
            | common_enums::IntentStatus::RequiresCapture
            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
            | common_enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture
            | common_enums::IntentStatus::Conflicted => false,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Default, Debug, Clone, serde::Serialize)]
pub struct HeaderPayload {
    pub payment_confirm_source: Option<common_enums::PaymentSource>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub x_hs_latency: Option<bool>,
    pub browser_name: Option<common_enums::BrowserName>,
    pub x_client_platform: Option<common_enums::ClientPlatform>,
    pub x_merchant_domain: Option<String>,
    pub locale: Option<String>,
    pub x_app_id: Option<String>,
    pub x_redirect_uri: Option<String>,
    pub x_reference_id: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ClickToPayMetaData {
    pub dpa_id: String,
    pub dpa_name: String,
    pub locale: String,
    pub acquirer_bin: String,
    pub acquirer_merchant_id: String,
    pub merchant_category_code: String,
    pub merchant_country_code: String,
    pub dpa_client_id: Option<String>,
}

// TODO: uncomment fields as necessary
#[cfg(feature = "v2")]
#[derive(Default, Debug, Clone, serde::Serialize)]
pub struct HeaderPayload {
    /// The source with which the payment is confirmed.
    pub payment_confirm_source: Option<common_enums::PaymentSource>,
    // pub client_source: Option<String>,
    // pub client_version: Option<String>,
    pub x_hs_latency: Option<bool>,
    pub browser_name: Option<common_enums::BrowserName>,
    pub x_client_platform: Option<common_enums::ClientPlatform>,
    pub x_merchant_domain: Option<String>,
    pub locale: Option<String>,
    pub x_app_id: Option<String>,
    pub x_redirect_uri: Option<String>,
    pub x_reference_id: Option<String>,
}

impl HeaderPayload {
    pub fn with_source(payment_confirm_source: common_enums::PaymentSource) -> Self {
        Self {
            payment_confirm_source: Some(payment_confirm_source),
            ..Default::default()
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Clone)]
pub struct PaymentIntentData<F>
where
    F: Clone,
{
    pub flow: PhantomData<F>,
    pub payment_intent: PaymentIntent,
    pub sessions_token: Vec<SessionToken>,
    pub client_secret: Option<Secret<String>>,
    pub vault_session_details: Option<VaultSessionDetails>,
    pub connector_customer_id: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Clone)]
pub struct PaymentAttemptListData<F>
where
    F: Clone,
{
    pub flow: PhantomData<F>,
    pub payment_attempt_list: Vec<PaymentAttempt>,
}

// TODO: Check if this can be merged with existing payment data
#[cfg(feature = "v2")]
#[derive(Clone, Debug)]
pub struct PaymentConfirmData<F>
where
    F: Clone,
{
    pub flow: PhantomData<F>,
    pub payment_intent: PaymentIntent,
    pub payment_attempt: PaymentAttempt,
    pub payment_method_data: Option<payment_method_data::PaymentMethodData>,
    pub payment_address: payment_address::PaymentAddress,
    pub mandate_data: Option<api_models::payments::MandateIds>,
    pub payment_method: Option<payment_methods::PaymentMethod>,
    pub merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,
    pub external_vault_pmd: Option<payment_method_data::ExternalVaultPaymentMethodData>,
    /// The webhook url of the merchant, to which the connector will send the webhook.
    pub webhook_url: Option<String>,
}

#[cfg(feature = "v2")]
impl<F: Clone> PaymentConfirmData<F> {
    pub fn get_connector_customer_id(
        &self,
        customer: Option<&customer::Customer>,
        merchant_connector_account: &MerchantConnectorAccountTypeDetails,
    ) -> Option<String> {
        match merchant_connector_account {
            MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(_) => customer
                .and_then(|customer| customer.get_connector_customer_id(merchant_connector_account))
                .map(|id| id.to_string())
                .or_else(|| {
                    self.payment_intent
                        .get_connector_customer_id_from_feature_metadata()
                }),
            MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None,
        }
    }

    pub fn update_payment_method_data(
        &mut self,
        payment_method_data: payment_method_data::PaymentMethodData,
    ) {
        self.payment_method_data = Some(payment_method_data);
    }

    pub fn update_payment_method_and_pm_id(
        &mut self,
        payment_method_id: id_type::GlobalPaymentMethodId,
        payment_method: payment_methods::PaymentMethod,
    ) {
        self.payment_attempt.payment_method_id = Some(payment_method_id);
        self.payment_method = Some(payment_method);
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug)]
pub struct PaymentStatusData<F>
where
    F: Clone,
{
    pub flow: PhantomData<F>,
    pub payment_intent: PaymentIntent,
    pub payment_attempt: PaymentAttempt,
    pub payment_address: payment_address::PaymentAddress,
    pub attempts: Option<Vec<PaymentAttempt>>,
    /// Should the payment status be synced with connector
    /// This will depend on the payment status and the force sync flag in the request
    pub should_sync_with_connector: bool,
    pub merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,
}

#[cfg(feature = "v2")]
#[derive(Clone)]
pub struct PaymentCaptureData<F>
where
    F: Clone,
{
    pub flow: PhantomData<F>,
    pub payment_intent: PaymentIntent,
    pub payment_attempt: PaymentAttempt,
}

#[cfg(feature = "v2")]
#[derive(Clone)]
pub struct PaymentCancelData<F>
where
    F: Clone,
{
    pub flow: PhantomData<F>,
    pub payment_intent: PaymentIntent,
    pub payment_attempt: PaymentAttempt,
}

#[cfg(feature = "v2")]
impl<F> PaymentStatusData<F>
where
    F: Clone,
{
    pub fn get_payment_id(&self) -> &id_type::GlobalPaymentId {
        &self.payment_intent.id
    }
}

#[cfg(feature = "v2")]
#[derive(Clone)]
pub struct PaymentAttemptRecordData<F>
where
    F: Clone,
{
    pub flow: PhantomData<F>,
    pub payment_intent: PaymentIntent,
    pub payment_attempt: PaymentAttempt,
    pub revenue_recovery_data: RevenueRecoveryData,
    pub payment_address: payment_address::PaymentAddress,
}

#[cfg(feature = "v2")]
#[derive(Clone)]
pub struct RevenueRecoveryData {
    pub billing_connector_id: id_type::MerchantConnectorAccountId,
    pub processor_payment_method_token: String,
    pub connector_customer_id: String,
    pub retry_count: Option<u16>,
    pub invoice_next_billing_time: Option<PrimitiveDateTime>,
    pub triggered_by: storage_enums::enums::TriggeredBy,
    pub card_network: Option<common_enums::CardNetwork>,
    pub card_issuer: Option<String>,
}

#[cfg(feature = "v2")]
impl<F> PaymentAttemptRecordData<F>
where
    F: Clone,
{
    pub fn get_updated_feature_metadata(
        &self,
    ) -> CustomResult<Option<FeatureMetadata>, errors::api_error_response::ApiErrorResponse> {
        let payment_intent_feature_metadata = self.payment_intent.get_feature_metadata();
        let revenue_recovery = self.payment_intent.get_revenue_recovery_metadata();
        let payment_attempt_connector = self.payment_attempt.connector.clone();

        let feature_metadata_first_pg_error_code = revenue_recovery
            .as_ref()
            .and_then(|data| data.first_payment_attempt_pg_error_code.clone());

        let (first_pg_error_code, first_network_advice_code, first_network_decline_code) =
            feature_metadata_first_pg_error_code.map_or_else(
                || {
                    let first_pg_error_code = self
                        .payment_attempt
                        .error
                        .as_ref()
                        .map(|error| error.code.clone());
                    let first_network_advice_code = self
                        .payment_attempt
                        .error
                        .as_ref()
                        .and_then(|error| error.network_advice_code.clone());
                    let first_network_decline_code = self
                        .payment_attempt
                        .error
                        .as_ref()
                        .and_then(|error| error.network_decline_code.clone());
                    (
                        first_pg_error_code,
                        first_network_advice_code,
                        first_network_decline_code,
                    )
                },
                |pg_code| {
                    let advice_code = revenue_recovery
                        .as_ref()
                        .and_then(|data| data.first_payment_attempt_network_advice_code.clone());
                    let decline_code = revenue_recovery
                        .as_ref()
                        .and_then(|data| data.first_payment_attempt_network_decline_code.clone());
                    (Some(pg_code), advice_code, decline_code)
                },
            );

        let billing_connector_payment_method_details = Some(
            diesel_models::types::BillingConnectorPaymentMethodDetails::Card(
                diesel_models::types::BillingConnectorAdditionalCardInfo {
                    card_network: self.revenue_recovery_data.card_network.clone(),
                    card_issuer: self.revenue_recovery_data.card_issuer.clone(),
                },
            ),
        );

        let payment_revenue_recovery_metadata = match payment_attempt_connector {
            Some(connector) => Some(diesel_models::types::PaymentRevenueRecoveryMetadata {
                // Update retry count by one.
                total_retry_count: revenue_recovery.as_ref().map_or(
                    self.revenue_recovery_data
                        .retry_count
                        .map_or_else(|| 1, |retry_count| retry_count),
                    |data| (data.total_retry_count + 1),
                ),
                // Since this is an external system call, marking this payment_connector_transmission to ConnectorCallSucceeded.
                payment_connector_transmission:
                    common_enums::PaymentConnectorTransmission::ConnectorCallUnsuccessful,
                billing_connector_id: self.revenue_recovery_data.billing_connector_id.clone(),
                active_attempt_payment_connector_id: self
                    .payment_attempt
                    .get_attempt_merchant_connector_account_id()?,
                billing_connector_payment_details:
                    diesel_models::types::BillingConnectorPaymentDetails {
                        payment_processor_token: self
                            .revenue_recovery_data
                            .processor_payment_method_token
                            .clone(),
                        connector_customer_id: self
                            .revenue_recovery_data
                            .connector_customer_id
                            .clone(),
                    },
                payment_method_type: self.payment_attempt.payment_method_type,
                payment_method_subtype: self.payment_attempt.payment_method_subtype,
                connector: connector.parse().map_err(|err| {
                    router_env::logger::error!(?err, "Failed to parse connector string to enum");
                    errors::api_error_response::ApiErrorResponse::InternalServerError
                })?,
                invoice_next_billing_time: self.revenue_recovery_data.invoice_next_billing_time,
                invoice_billing_started_at_time: self
                    .revenue_recovery_data
                    .invoice_next_billing_time,
                billing_connector_payment_method_details,
                first_payment_attempt_network_advice_code: first_network_advice_code,
                first_payment_attempt_network_decline_code: first_network_decline_code,
                first_payment_attempt_pg_error_code: first_pg_error_code,
            }),
            None => Err(errors::api_error_response::ApiErrorResponse::InternalServerError)
                .attach_printable("Connector not found in payment attempt")?,
        };
        Ok(Some(FeatureMetadata {
            redirect_response: payment_intent_feature_metadata
                .as_ref()
                .and_then(|data| data.redirect_response.clone()),
            search_tags: payment_intent_feature_metadata
                .as_ref()
                .and_then(|data| data.search_tags.clone()),
            apple_pay_recurring_details: payment_intent_feature_metadata
                .as_ref()
                .and_then(|data| data.apple_pay_recurring_details.clone()),
            payment_revenue_recovery_metadata,
        }))
    }
}

#[derive(Default, Clone, serde::Serialize, Debug)]
pub struct CardAndNetworkTokenDataForVault {
    pub card_data: payment_method_data::Card,
    pub network_token: NetworkTokenDataForVault,
}

#[derive(Default, Clone, serde::Serialize, Debug)]
pub struct NetworkTokenDataForVault {
    pub network_token_data: payment_method_data::NetworkTokenData,
    pub network_token_req_ref_id: String,
}

#[derive(Default, Clone, serde::Serialize, Debug)]
pub struct CardDataForVault {
    pub card_data: payment_method_data::Card,
    pub network_token_req_ref_id: Option<String>,
}

#[derive(Clone, serde::Serialize, Debug)]
pub enum VaultOperation {
    ExistingVaultData(VaultData),
    SaveCardData(CardDataForVault),
    SaveCardAndNetworkTokenData(Box<CardAndNetworkTokenDataForVault>),
}

impl VaultOperation {
    pub fn get_updated_vault_data(
        existing_vault_data: Option<&Self>,
        payment_method_data: &payment_method_data::PaymentMethodData,
    ) -> Option<Self> {
        match (existing_vault_data, payment_method_data) {
            (None, payment_method_data::PaymentMethodData::Card(card)) => {
                Some(Self::ExistingVaultData(VaultData::Card(card.clone())))
            }
            (None, payment_method_data::PaymentMethodData::NetworkToken(nt_data)) => Some(
                Self::ExistingVaultData(VaultData::NetworkToken(nt_data.clone())),
            ),
            (Some(Self::ExistingVaultData(vault_data)), payment_method_data) => {
                match (vault_data, payment_method_data) {
                    (
                        VaultData::Card(card),
                        payment_method_data::PaymentMethodData::NetworkToken(nt_data),
                    ) => Some(Self::ExistingVaultData(VaultData::CardAndNetworkToken(
                        Box::new(CardAndNetworkTokenData {
                            card_data: card.clone(),
                            network_token_data: nt_data.clone(),
                        }),
                    ))),
                    (
                        VaultData::NetworkToken(nt_data),
                        payment_method_data::PaymentMethodData::Card(card),
                    ) => Some(Self::ExistingVaultData(VaultData::CardAndNetworkToken(
                        Box::new(CardAndNetworkTokenData {
                            card_data: card.clone(),
                            network_token_data: nt_data.clone(),
                        }),
                    ))),
                    _ => Some(Self::ExistingVaultData(vault_data.clone())),
                }
            }
            //payment_method_data is not card or network token
            _ => None,
        }
    }
}

#[derive(Clone, serde::Serialize, Debug)]
pub enum VaultData {
    Card(payment_method_data::Card),
    NetworkToken(payment_method_data::NetworkTokenData),
    CardAndNetworkToken(Box<CardAndNetworkTokenData>),
}

#[derive(Default, Clone, serde::Serialize, Debug)]
pub struct CardAndNetworkTokenData {
    pub card_data: payment_method_data::Card,
    pub network_token_data: payment_method_data::NetworkTokenData,
}

impl VaultData {
    pub fn get_card_vault_data(&self) -> Option<payment_method_data::Card> {
        match self {
            Self::Card(card_data) => Some(card_data.clone()),
            Self::NetworkToken(_network_token_data) => None,
            Self::CardAndNetworkToken(vault_data) => Some(vault_data.card_data.clone()),
        }
    }

    pub fn get_network_token_data(&self) -> Option<payment_method_data::NetworkTokenData> {
        match self {
            Self::Card(_card_data) => None,
            Self::NetworkToken(network_token_data) => Some(network_token_data.clone()),
            Self::CardAndNetworkToken(vault_data) => Some(vault_data.network_token_data.clone()),
        }
    }
}


// File: crates/hyperswitch_domain_models/src/transformers.rs

pub trait ForeignFrom<F> {
    fn foreign_from(from: F) -> Self;
}

pub trait ForeignTryFrom<F>: Sized {
    type Error;

    fn foreign_try_from(from: F) -> Result<Self, Self::Error>;
}


// File: crates/hyperswitch_domain_models/src/lib.rs

pub mod address;
pub mod api;
pub mod authentication;
pub mod behaviour;
pub mod bulk_tokenization;
pub mod business_profile;
pub mod callback_mapper;
pub mod card_testing_guard_data;
pub mod cards_info;
pub mod chat;
pub mod configs;
pub mod connector_endpoints;
pub mod consts;
pub mod customer;
pub mod disputes;
pub mod errors;
pub mod ext_traits;
pub mod gsm;
pub mod invoice;
pub mod mandates;
pub mod master_key;
pub mod merchant_account;
pub mod merchant_connector_account;
pub mod merchant_key_store;
pub mod network_tokenization;
pub mod payment_address;
pub mod payment_method_data;
pub mod payment_methods;
pub mod payments;
#[cfg(feature = "payouts")]
pub mod payouts;
pub mod platform;
pub mod refunds;
pub mod relay;
#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
pub mod revenue_recovery;
pub mod router_data;
pub mod router_data_v2;
pub mod router_flow_types;
pub mod router_request_types;
pub mod router_response_types;
pub mod routing;
pub mod subscription;
#[cfg(feature = "tokenization_v2")]
pub mod tokenization;
pub mod transformers;
pub mod type_encryption;
pub mod types;
pub mod vault;

#[cfg(not(feature = "payouts"))]
pub trait PayoutAttemptInterface {}

#[cfg(not(feature = "payouts"))]
pub trait PayoutsInterface {}

use api_models::payments::{
    ApplePayRecurringDetails as ApiApplePayRecurringDetails,
    ApplePayRegularBillingDetails as ApiApplePayRegularBillingDetails,
    FeatureMetadata as ApiFeatureMetadata, OrderDetailsWithAmount as ApiOrderDetailsWithAmount,
    RecurringPaymentIntervalUnit as ApiRecurringPaymentIntervalUnit,
    RedirectResponse as ApiRedirectResponse,
};
#[cfg(feature = "v2")]
use api_models::payments::{
    BillingConnectorAdditionalCardInfo as ApiBillingConnectorAdditionalCardInfo,
    BillingConnectorPaymentDetails as ApiBillingConnectorPaymentDetails,
    BillingConnectorPaymentMethodDetails as ApiBillingConnectorPaymentMethodDetails,
    PaymentRevenueRecoveryMetadata as ApiRevenueRecoveryMetadata,
};
use diesel_models::types::{
    ApplePayRecurringDetails, ApplePayRegularBillingDetails, FeatureMetadata,
    OrderDetailsWithAmount, RecurringPaymentIntervalUnit, RedirectResponse,
};
#[cfg(feature = "v2")]
use diesel_models::types::{
    BillingConnectorAdditionalCardInfo, BillingConnectorPaymentDetails,
    BillingConnectorPaymentMethodDetails, PaymentRevenueRecoveryMetadata,
};

#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
pub enum RemoteStorageObject<T: ForeignIDRef> {
    ForeignID(String),
    Object(T),
}

impl<T: ForeignIDRef> From<T> for RemoteStorageObject<T> {
    fn from(value: T) -> Self {
        Self::Object(value)
    }
}

pub trait ForeignIDRef {
    fn foreign_id(&self) -> String;
}

impl<T: ForeignIDRef> RemoteStorageObject<T> {
    pub fn get_id(&self) -> String {
        match self {
            Self::ForeignID(id) => id.clone(),
            Self::Object(i) => i.foreign_id(),
        }
    }
}

use std::fmt::Debug;

pub trait ApiModelToDieselModelConvertor<F> {
    /// Convert from a foreign type to the current type
    fn convert_from(from: F) -> Self;
    fn convert_back(self) -> F;
}

#[cfg(feature = "v1")]
impl ApiModelToDieselModelConvertor<ApiFeatureMetadata> for FeatureMetadata {
    fn convert_from(from: ApiFeatureMetadata) -> Self {
        let ApiFeatureMetadata {
            redirect_response,
            search_tags,
            apple_pay_recurring_details,
        } = from;

        Self {
            redirect_response: redirect_response.map(RedirectResponse::convert_from),
            search_tags,
            apple_pay_recurring_details: apple_pay_recurring_details
                .map(ApplePayRecurringDetails::convert_from),
            gateway_system: None,
        }
    }

    fn convert_back(self) -> ApiFeatureMetadata {
        let Self {
            redirect_response,
            search_tags,
            apple_pay_recurring_details,
            ..
        } = self;

        ApiFeatureMetadata {
            redirect_response: redirect_response
                .map(|redirect_response| redirect_response.convert_back()),
            search_tags,
            apple_pay_recurring_details: apple_pay_recurring_details
                .map(|value| value.convert_back()),
        }
    }
}

#[cfg(feature = "v2")]
impl ApiModelToDieselModelConvertor<ApiFeatureMetadata> for FeatureMetadata {
    fn convert_from(from: ApiFeatureMetadata) -> Self {
        let ApiFeatureMetadata {
            redirect_response,
            search_tags,
            apple_pay_recurring_details,
            revenue_recovery: payment_revenue_recovery_metadata,
        } = from;

        Self {
            redirect_response: redirect_response.map(RedirectResponse::convert_from),
            search_tags,
            apple_pay_recurring_details: apple_pay_recurring_details
                .map(ApplePayRecurringDetails::convert_from),
            payment_revenue_recovery_metadata: payment_revenue_recovery_metadata
                .map(PaymentRevenueRecoveryMetadata::convert_from),
        }
    }

    fn convert_back(self) -> ApiFeatureMetadata {
        let Self {
            redirect_response,
            search_tags,
            apple_pay_recurring_details,
            payment_revenue_recovery_metadata,
        } = self;

        ApiFeatureMetadata {
            redirect_response: redirect_response
                .map(|redirect_response| redirect_response.convert_back()),
            search_tags,
            apple_pay_recurring_details: apple_pay_recurring_details
                .map(|value| value.convert_back()),
            revenue_recovery: payment_revenue_recovery_metadata.map(|value| value.convert_back()),
        }
    }
}

impl ApiModelToDieselModelConvertor<ApiRedirectResponse> for RedirectResponse {
    fn convert_from(from: ApiRedirectResponse) -> Self {
        let ApiRedirectResponse {
            param,
            json_payload,
        } = from;
        Self {
            param,
            json_payload,
        }
    }

    fn convert_back(self) -> ApiRedirectResponse {
        let Self {
            param,
            json_payload,
        } = self;
        ApiRedirectResponse {
            param,
            json_payload,
        }
    }
}

impl ApiModelToDieselModelConvertor<ApiRecurringPaymentIntervalUnit>
    for RecurringPaymentIntervalUnit
{
    fn convert_from(from: ApiRecurringPaymentIntervalUnit) -> Self {
        match from {
            ApiRecurringPaymentIntervalUnit::Year => Self::Year,
            ApiRecurringPaymentIntervalUnit::Month => Self::Month,
            ApiRecurringPaymentIntervalUnit::Day => Self::Day,
            ApiRecurringPaymentIntervalUnit::Hour => Self::Hour,
            ApiRecurringPaymentIntervalUnit::Minute => Self::Minute,
        }
    }
    fn convert_back(self) -> ApiRecurringPaymentIntervalUnit {
        match self {
            Self::Year => ApiRecurringPaymentIntervalUnit::Year,
            Self::Month => ApiRecurringPaymentIntervalUnit::Month,
            Self::Day => ApiRecurringPaymentIntervalUnit::Day,
            Self::Hour => ApiRecurringPaymentIntervalUnit::Hour,
            Self::Minute => ApiRecurringPaymentIntervalUnit::Minute,
        }
    }
}

impl ApiModelToDieselModelConvertor<ApiApplePayRegularBillingDetails>
    for ApplePayRegularBillingDetails
{
    fn convert_from(from: ApiApplePayRegularBillingDetails) -> Self {
        Self {
            label: from.label,
            recurring_payment_start_date: from.recurring_payment_start_date,
            recurring_payment_end_date: from.recurring_payment_end_date,
            recurring_payment_interval_unit: from
                .recurring_payment_interval_unit
                .map(RecurringPaymentIntervalUnit::convert_from),
            recurring_payment_interval_count: from.recurring_payment_interval_count,
        }
    }

    fn convert_back(self) -> ApiApplePayRegularBillingDetails {
        ApiApplePayRegularBillingDetails {
            label: self.label,
            recurring_payment_start_date: self.recurring_payment_start_date,
            recurring_payment_end_date: self.recurring_payment_end_date,
            recurring_payment_interval_unit: self
                .recurring_payment_interval_unit
                .map(|value| value.convert_back()),
            recurring_payment_interval_count: self.recurring_payment_interval_count,
        }
    }
}

impl ApiModelToDieselModelConvertor<ApiApplePayRecurringDetails> for ApplePayRecurringDetails {
    fn convert_from(from: ApiApplePayRecurringDetails) -> Self {
        Self {
            payment_description: from.payment_description,
            regular_billing: ApplePayRegularBillingDetails::convert_from(from.regular_billing),
            billing_agreement: from.billing_agreement,
            management_url: from.management_url,
        }
    }

    fn convert_back(self) -> ApiApplePayRecurringDetails {
        ApiApplePayRecurringDetails {
            payment_description: self.payment_description,
            regular_billing: self.regular_billing.convert_back(),
            billing_agreement: self.billing_agreement,
            management_url: self.management_url,
        }
    }
}

#[cfg(feature = "v2")]
impl ApiModelToDieselModelConvertor<ApiBillingConnectorAdditionalCardInfo>
    for BillingConnectorAdditionalCardInfo
{
    fn convert_from(from: ApiBillingConnectorAdditionalCardInfo) -> Self {
        Self {
            card_issuer: from.card_issuer,
            card_network: from.card_network,
        }
    }

    fn convert_back(self) -> ApiBillingConnectorAdditionalCardInfo {
        ApiBillingConnectorAdditionalCardInfo {
            card_issuer: self.card_issuer,
            card_network: self.card_network,
        }
    }
}

#[cfg(feature = "v2")]
impl ApiModelToDieselModelConvertor<ApiBillingConnectorPaymentMethodDetails>
    for BillingConnectorPaymentMethodDetails
{
    fn convert_from(from: ApiBillingConnectorPaymentMethodDetails) -> Self {
        match from {
            ApiBillingConnectorPaymentMethodDetails::Card(data) => {
                Self::Card(BillingConnectorAdditionalCardInfo::convert_from(data))
            }
        }
    }

    fn convert_back(self) -> ApiBillingConnectorPaymentMethodDetails {
        match self {
            Self::Card(data) => ApiBillingConnectorPaymentMethodDetails::Card(data.convert_back()),
        }
    }
}

#[cfg(feature = "v2")]
impl ApiModelToDieselModelConvertor<ApiRevenueRecoveryMetadata> for PaymentRevenueRecoveryMetadata {
    fn convert_from(from: ApiRevenueRecoveryMetadata) -> Self {
        Self {
            total_retry_count: from.total_retry_count,
            payment_connector_transmission: from.payment_connector_transmission.unwrap_or_default(),
            billing_connector_id: from.billing_connector_id,
            active_attempt_payment_connector_id: from.active_attempt_payment_connector_id,
            billing_connector_payment_details: BillingConnectorPaymentDetails::convert_from(
                from.billing_connector_payment_details,
            ),
            payment_method_type: from.payment_method_type,
            payment_method_subtype: from.payment_method_subtype,
            connector: from.connector,
            invoice_next_billing_time: from.invoice_next_billing_time,
            billing_connector_payment_method_details: from
                .billing_connector_payment_method_details
                .map(BillingConnectorPaymentMethodDetails::convert_from),
            first_payment_attempt_network_advice_code: from
                .first_payment_attempt_network_advice_code,
            first_payment_attempt_network_decline_code: from
                .first_payment_attempt_network_decline_code,
            first_payment_attempt_pg_error_code: from.first_payment_attempt_pg_error_code,
            invoice_billing_started_at_time: from.invoice_billing_started_at_time,
        }
    }

    fn convert_back(self) -> ApiRevenueRecoveryMetadata {
        ApiRevenueRecoveryMetadata {
            total_retry_count: self.total_retry_count,
            payment_connector_transmission: Some(self.payment_connector_transmission),
            billing_connector_id: self.billing_connector_id,
            active_attempt_payment_connector_id: self.active_attempt_payment_connector_id,
            billing_connector_payment_details: self
                .billing_connector_payment_details
                .convert_back(),
            payment_method_type: self.payment_method_type,
            payment_method_subtype: self.payment_method_subtype,
            connector: self.connector,
            invoice_next_billing_time: self.invoice_next_billing_time,
            billing_connector_payment_method_details: self
                .billing_connector_payment_method_details
                .map(|data| data.convert_back()),
            first_payment_attempt_network_advice_code: self
                .first_payment_attempt_network_advice_code,
            first_payment_attempt_network_decline_code: self
                .first_payment_attempt_network_decline_code,
            first_payment_attempt_pg_error_code: self.first_payment_attempt_pg_error_code,
            invoice_billing_started_at_time: self.invoice_billing_started_at_time,
        }
    }
}

#[cfg(feature = "v2")]
impl ApiModelToDieselModelConvertor<ApiBillingConnectorPaymentDetails>
    for BillingConnectorPaymentDetails
{
    fn convert_from(from: ApiBillingConnectorPaymentDetails) -> Self {
        Self {
            payment_processor_token: from.payment_processor_token,
            connector_customer_id: from.connector_customer_id,
        }
    }

    fn convert_back(self) -> ApiBillingConnectorPaymentDetails {
        ApiBillingConnectorPaymentDetails {
            payment_processor_token: self.payment_processor_token,
            connector_customer_id: self.connector_customer_id,
        }
    }
}

impl ApiModelToDieselModelConvertor<ApiOrderDetailsWithAmount> for OrderDetailsWithAmount {
    fn convert_from(from: ApiOrderDetailsWithAmount) -> Self {
        let ApiOrderDetailsWithAmount {
            product_name,
            quantity,
            amount,
            requires_shipping,
            product_img_link,
            product_id,
            category,
            sub_category,
            brand,
            product_type,
            product_tax_code,
            tax_rate,
            total_tax_amount,
            description,
            sku,
            upc,
            commodity_code,
            unit_of_measure,
            total_amount,
            unit_discount_amount,
        } = from;
        Self {
            product_name,
            quantity,
            amount,
            requires_shipping,
            product_img_link,
            product_id,
            category,
            sub_category,
            brand,
            product_type,
            product_tax_code,
            tax_rate,
            total_tax_amount,
            description,
            sku,
            upc,
            commodity_code,
            unit_of_measure,
            total_amount,
            unit_discount_amount,
        }
    }

    fn convert_back(self) -> ApiOrderDetailsWithAmount {
        let Self {
            product_name,
            quantity,
            amount,
            requires_shipping,
            product_img_link,
            product_id,
            category,
            sub_category,
            brand,
            product_type,
            product_tax_code,
            tax_rate,
            total_tax_amount,
            description,
            sku,
            upc,
            commodity_code,
            unit_of_measure,
            total_amount,
            unit_discount_amount,
        } = self;
        ApiOrderDetailsWithAmount {
            product_name,
            quantity,
            amount,
            requires_shipping,
            product_img_link,
            product_id,
            category,
            sub_category,
            brand,
            product_type,
            product_tax_code,
            tax_rate,
            total_tax_amount,
            description,
            sku,
            upc,
            commodity_code,
            unit_of_measure,
            total_amount,
            unit_discount_amount,
        }
    }
}

#[cfg(feature = "v2")]
impl ApiModelToDieselModelConvertor<api_models::admin::PaymentLinkConfigRequest>
    for diesel_models::payment_intent::PaymentLinkConfigRequestForPayments
{
    fn convert_from(item: api_models::admin::PaymentLinkConfigRequest) -> Self {
        Self {
            theme: item.theme,
            logo: item.logo,
            seller_name: item.seller_name,
            sdk_layout: item.sdk_layout,
            display_sdk_only: item.display_sdk_only,
            enabled_saved_payment_method: item.enabled_saved_payment_method,
            hide_card_nickname_field: item.hide_card_nickname_field,
            show_card_form_by_default: item.show_card_form_by_default,
            details_layout: item.details_layout,
            transaction_details: item.transaction_details.map(|transaction_details| {
                transaction_details
                    .into_iter()
                    .map(|transaction_detail| {
                        diesel_models::PaymentLinkTransactionDetails::convert_from(
                            transaction_detail,
                        )
                    })
                    .collect()
            }),
            background_image: item.background_image.map(|background_image| {
                diesel_models::business_profile::PaymentLinkBackgroundImageConfig::convert_from(
                    background_image,
                )
            }),
            payment_button_text: item.payment_button_text,
            custom_message_for_card_terms: item.custom_message_for_card_terms,
            custom_message_for_payment_method_types: item.custom_message_for_payment_method_types,
            payment_button_colour: item.payment_button_colour,
            skip_status_screen: item.skip_status_screen,
            background_colour: item.background_colour,
            payment_button_text_colour: item.payment_button_text_colour,
            sdk_ui_rules: item.sdk_ui_rules,
            payment_link_ui_rules: item.payment_link_ui_rules,
            enable_button_only_on_form_ready: item.enable_button_only_on_form_ready,
            payment_form_header_text: item.payment_form_header_text,
            payment_form_label_type: item.payment_form_label_type,
            show_card_terms: item.show_card_terms,
            is_setup_mandate_flow: item.is_setup_mandate_flow,
            color_icon_card_cvc_error: item.color_icon_card_cvc_error,
        }
    }
    fn convert_back(self) -> api_models::admin::PaymentLinkConfigRequest {
        let Self {
            theme,
            logo,
            seller_name,
            sdk_layout,
            display_sdk_only,
            enabled_saved_payment_method,
            hide_card_nickname_field,
            show_card_form_by_default,
            transaction_details,
            background_image,
            details_layout,
            payment_button_text,
            custom_message_for_card_terms,
            custom_message_for_payment_method_types,
            payment_button_colour,
            skip_status_screen,
            background_colour,
            payment_button_text_colour,
            sdk_ui_rules,
            payment_link_ui_rules,
            enable_button_only_on_form_ready,
            payment_form_header_text,
            payment_form_label_type,
            show_card_terms,
            is_setup_mandate_flow,
            color_icon_card_cvc_error,
        } = self;
        api_models::admin::PaymentLinkConfigRequest {
            theme,
            logo,
            seller_name,
            sdk_layout,
            display_sdk_only,
            enabled_saved_payment_method,
            hide_card_nickname_field,
            show_card_form_by_default,
            details_layout,
            transaction_details: transaction_details.map(|transaction_details| {
                transaction_details
                    .into_iter()
                    .map(|transaction_detail| transaction_detail.convert_back())
                    .collect()
            }),
            background_image: background_image
                .map(|background_image| background_image.convert_back()),
            payment_button_text,
            custom_message_for_card_terms,
            custom_message_for_payment_method_types,
            payment_button_colour,
            skip_status_screen,
            background_colour,
            payment_button_text_colour,
            sdk_ui_rules,
            payment_link_ui_rules,
            enable_button_only_on_form_ready,
            payment_form_header_text,
            payment_form_label_type,
            show_card_terms,
            is_setup_mandate_flow,
            color_icon_card_cvc_error,
        }
    }
}

#[cfg(feature = "v2")]
impl ApiModelToDieselModelConvertor<api_models::admin::PaymentLinkTransactionDetails>
    for diesel_models::PaymentLinkTransactionDetails
{
    fn convert_from(from: api_models::admin::PaymentLinkTransactionDetails) -> Self {
        Self {
            key: from.key,
            value: from.value,
            ui_configuration: from
                .ui_configuration
                .map(diesel_models::TransactionDetailsUiConfiguration::convert_from),
        }
    }
    fn convert_back(self) -> api_models::admin::PaymentLinkTransactionDetails {
        let Self {
            key,
            value,
            ui_configuration,
        } = self;
        api_models::admin::PaymentLinkTransactionDetails {
            key,
            value,
            ui_configuration: ui_configuration
                .map(|ui_configuration| ui_configuration.convert_back()),
        }
    }
}

#[cfg(feature = "v2")]
impl ApiModelToDieselModelConvertor<api_models::admin::PaymentLinkBackgroundImageConfig>
    for diesel_models::business_profile::PaymentLinkBackgroundImageConfig
{
    fn convert_from(from: api_models::admin::PaymentLinkBackgroundImageConfig) -> Self {
        Self {
            url: from.url,
            position: from.position,
            size: from.size,
        }
    }
    fn convert_back(self) -> api_models::admin::PaymentLinkBackgroundImageConfig {
        let Self {
            url,
            position,
            size,
        } = self;
        api_models::admin::PaymentLinkBackgroundImageConfig {
            url,
            position,
            size,
        }
    }
}

#[cfg(feature = "v2")]
impl ApiModelToDieselModelConvertor<api_models::admin::TransactionDetailsUiConfiguration>
    for diesel_models::TransactionDetailsUiConfiguration
{
    fn convert_from(from: api_models::admin::TransactionDetailsUiConfiguration) -> Self {
        Self {
            position: from.position,
            is_key_bold: from.is_key_bold,
            is_value_bold: from.is_value_bold,
        }
    }
    fn convert_back(self) -> api_models::admin::TransactionDetailsUiConfiguration {
        let Self {
            position,
            is_key_bold,
            is_value_bold,
        } = self;
        api_models::admin::TransactionDetailsUiConfiguration {
            position,
            is_key_bold,
            is_value_bold,
        }
    }
}

#[cfg(feature = "v2")]
impl From<api_models::payments::AmountDetails> for payments::AmountDetails {
    fn from(amount_details: api_models::payments::AmountDetails) -> Self {
        Self {
            order_amount: amount_details.order_amount().into(),
            currency: amount_details.currency(),
            shipping_cost: amount_details.shipping_cost(),
            tax_details: amount_details.order_tax_amount().map(|order_tax_amount| {
                diesel_models::TaxDetails {
                    default: Some(diesel_models::DefaultTax { order_tax_amount }),
                    payment_method_type: None,
                }
            }),
            skip_external_tax_calculation: amount_details.skip_external_tax_calculation(),
            skip_surcharge_calculation: amount_details.skip_surcharge_calculation(),
            surcharge_amount: amount_details.surcharge_amount(),
            tax_on_surcharge: amount_details.tax_on_surcharge(),
            // We will not receive this in the request. This will be populated after calling the connector / processor
            amount_captured: None,
        }
    }
}

#[cfg(feature = "v2")]
impl From<payments::AmountDetails> for api_models::payments::AmountDetailsSetter {
    fn from(amount_details: payments::AmountDetails) -> Self {
        Self {
            order_amount: amount_details.order_amount.into(),
            currency: amount_details.currency,
            shipping_cost: amount_details.shipping_cost,
            order_tax_amount: amount_details
                .tax_details
                .and_then(|tax_detail| tax_detail.get_default_tax_amount()),
            skip_external_tax_calculation: amount_details.skip_external_tax_calculation,
            skip_surcharge_calculation: amount_details.skip_surcharge_calculation,
            surcharge_amount: amount_details.surcharge_amount,
            tax_on_surcharge: amount_details.tax_on_surcharge,
        }
    }
}
#[cfg(feature = "v2")]
impl From<&api_models::payments::PaymentAttemptAmountDetails>
    for payments::payment_attempt::AttemptAmountDetailsSetter
{
    fn from(amount: &api_models::payments::PaymentAttemptAmountDetails) -> Self {
        Self {
            net_amount: amount.net_amount,
            amount_to_capture: amount.amount_to_capture,
            surcharge_amount: amount.surcharge_amount,
            tax_on_surcharge: amount.tax_on_surcharge,
            amount_capturable: amount.amount_capturable,
            shipping_cost: amount.shipping_cost,
            order_tax_amount: amount.order_tax_amount,
        }
    }
}
#[cfg(feature = "v2")]
impl From<&payments::payment_attempt::AttemptAmountDetailsSetter>
    for api_models::payments::PaymentAttemptAmountDetails
{
    fn from(amount: &payments::payment_attempt::AttemptAmountDetailsSetter) -> Self {
        Self {
            net_amount: amount.net_amount,
            amount_to_capture: amount.amount_to_capture,
            surcharge_amount: amount.surcharge_amount,
            tax_on_surcharge: amount.tax_on_surcharge,
            amount_capturable: amount.amount_capturable,
            shipping_cost: amount.shipping_cost,
            order_tax_amount: amount.order_tax_amount,
        }
    }
}
#[cfg(feature = "v2")]
impl From<&api_models::payments::RecordAttemptErrorDetails>
    for payments::payment_attempt::ErrorDetails
{
    fn from(error: &api_models::payments::RecordAttemptErrorDetails) -> Self {
        Self {
            code: error.code.clone(),
            message: error.message.clone(),
            reason: Some(error.message.clone()),
            unified_code: None,
            unified_message: None,
            network_advice_code: error.network_advice_code.clone(),
            network_decline_code: error.network_decline_code.clone(),
            network_error_message: error.network_error_message.clone(),
        }
    }
}


// File: crates/hyperswitch_domain_models/src/connector_endpoints.rs

//! Configs interface
use common_enums::{connector_enums, ApplicationError};
use common_utils::errors::CustomResult;
use masking::Secret;
use serde::Deserialize;

use crate::errors::api_error_response;
// struct Connectors
#[allow(missing_docs, missing_debug_implementations)]
#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)]
#[serde(default)]
pub struct Connectors {
    pub aci: ConnectorParams,
    pub authipay: ConnectorParams,
    pub adyen: AdyenParamsWithThreeBaseUrls,
    pub adyenplatform: ConnectorParams,
    pub affirm: ConnectorParams,
    pub airwallex: ConnectorParams,
    pub amazonpay: ConnectorParams,
    pub applepay: ConnectorParams,
    pub archipel: ConnectorParams,
    pub authorizedotnet: ConnectorParams,
    pub bambora: ConnectorParams,
    pub bamboraapac: ConnectorParams,
    pub bankofamerica: ConnectorParams,
    pub barclaycard: ConnectorParams,
    pub billwerk: ConnectorParams,
    pub bitpay: ConnectorParams,
    pub blackhawknetwork: ConnectorParams,
    pub calida: ConnectorParams,
    pub bluesnap: ConnectorParamsWithSecondaryBaseUrl,
    pub boku: ConnectorParams,
    pub braintree: ConnectorParams,
    pub breadpay: ConnectorParams,
    pub cashtocode: ConnectorParams,
    pub celero: ConnectorParams,
    pub chargebee: ConnectorParams,
    pub checkbook: ConnectorParams,
    pub checkout: ConnectorParams,
    pub coinbase: ConnectorParams,
    pub coingate: ConnectorParams,
    pub cryptopay: ConnectorParams,
    pub ctp_mastercard: NoParams,
    pub ctp_visa: NoParams,
    pub custombilling: NoParams,
    pub cybersource: ConnectorParams,
    pub datatrans: ConnectorParamsWithSecondaryBaseUrl,
    pub deutschebank: ConnectorParams,
    pub digitalvirgo: ConnectorParams,
    pub dlocal: ConnectorParams,
    #[cfg(feature = "dummy_connector")]
    pub dummyconnector: ConnectorParams,
    pub dwolla: ConnectorParams,
    pub ebanx: ConnectorParams,
    pub elavon: ConnectorParams,
    pub envoy: ConnectorParams,
    pub facilitapay: ConnectorParams,
    pub finix: ConnectorParams,
    pub fiserv: ConnectorParams,
    pub fiservemea: ConnectorParams,
    pub fiuu: ConnectorParamsWithThreeUrls,
    pub flexiti: ConnectorParams,
    pub forte: ConnectorParams,
    pub getnet: ConnectorParams,
    pub gigadat: ConnectorParams,
    pub globalpay: ConnectorParams,
    pub globepay: ConnectorParams,
    pub gocardless: ConnectorParams,
    pub gpayments: ConnectorParams,
    pub helcim: ConnectorParams,
    pub hipay: ConnectorParamsWithThreeUrls,
    pub hyperswitch_vault: ConnectorParams,
    pub hyperwallet: ConnectorParams,
    pub iatapay: ConnectorParams,
    pub inespay: ConnectorParams,
    pub itaubank: ConnectorParams,
    pub jpmorgan: ConnectorParams,
    pub juspaythreedsserver: ConnectorParams,
    pub cardinal: NoParams,
    pub katapult: ConnectorParams,
    pub klarna: ConnectorParams,
    pub loonio: ConnectorParams,
    pub mifinity: ConnectorParams,
    pub mollie: ConnectorParams,
    pub moneris: ConnectorParams,
    pub mpgs: ConnectorParams,
    pub multisafepay: ConnectorParams,
    pub netcetera: ConnectorParams,
    pub nexinets: ConnectorParams,
    pub nexixpay: ConnectorParams,
    pub nmi: ConnectorParams,
    pub nomupay: ConnectorParams,
    pub noon: ConnectorParamsWithModeType,
    pub nordea: ConnectorParams,
    pub novalnet: ConnectorParams,
    pub nuvei: ConnectorParams,
    pub opayo: ConnectorParams,
    pub opennode: ConnectorParams,
    pub paybox: ConnectorParamsWithSecondaryBaseUrl,
    pub payeezy: ConnectorParams,
    pub payjustnow: ConnectorParams,
    pub payload: ConnectorParams,
    pub payme: ConnectorParams,
    pub payone: ConnectorParams,
    pub paypal: ConnectorParams,
    pub paysafe: ConnectorParams,
    pub paystack: ConnectorParams,
    pub paytm: ConnectorParams,
    pub payu: ConnectorParams,
    pub peachpayments: ConnectorParams,
    pub phonepe: ConnectorParams,
    pub placetopay: ConnectorParams,
    pub plaid: ConnectorParams,
    pub powertranz: ConnectorParams,
    pub prophetpay: ConnectorParams,
    pub rapyd: ConnectorParams,
    pub razorpay: ConnectorParamsWithKeys,
    pub recurly: ConnectorParams,
    pub redsys: ConnectorParams,
    pub riskified: ConnectorParams,
    pub santander: ConnectorParams,
    pub shift4: ConnectorParams,
    pub sift: ConnectorParams,
    pub silverflow: ConnectorParams,
    pub signifyd: ConnectorParams,
    pub square: ConnectorParams,
    pub stax: ConnectorParams,
    pub stripe: ConnectorParamsWithFileUploadUrl,
    pub stripebilling: ConnectorParams,
    pub taxjar: ConnectorParams,
    pub tesouro: ConnectorParams,
    pub threedsecureio: ConnectorParams,
    pub thunes: ConnectorParams,
    pub tokenex: ConnectorParams,
    pub tokenio: ConnectorParams,
    pub trustpay: ConnectorParamsWithMoreUrls,
    pub trustpayments: ConnectorParams,
    pub tsys: ConnectorParams,
    pub unified_authentication_service: ConnectorParams,
    pub vgs: ConnectorParams,
    pub volt: ConnectorParams,
    pub wellsfargo: ConnectorParams,
    pub wellsfargopayout: ConnectorParams,
    pub wise: ConnectorParams,
    pub worldline: ConnectorParams,
    pub worldpay: ConnectorParams,
    pub worldpayvantiv: ConnectorParamsWithThreeUrls,
    pub worldpayxml: ConnectorParams,
    pub xendit: ConnectorParams,
    pub zift: ConnectorParams,
    pub zen: ConnectorParams,
    pub zsl: ConnectorParams,
}

impl Connectors {
    pub fn get_connector_params(
        &self,
        connector: connector_enums::Connector,
    ) -> CustomResult<ConnectorParams, api_error_response::ApiErrorResponse> {
        match connector {
            connector_enums::Connector::Recurly => Ok(self.recurly.clone()),
            connector_enums::Connector::Stripebilling => Ok(self.stripebilling.clone()),
            connector_enums::Connector::Chargebee => Ok(self.chargebee.clone()),
            _ => Err(api_error_response::ApiErrorResponse::IncorrectConnectorNameGiven.into()),
        }
    }
}

/// struct ConnectorParams
#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)]
#[serde(default)]
pub struct ConnectorParams {
    /// base url
    pub base_url: String,
    /// secondary base url
    pub secondary_base_url: Option<String>,
}

///struct No Param for connectors with no params
#[derive(Debug, Deserialize, Clone, Default)]
pub struct NoParams;

impl NoParams {
    /// function to satisfy connector param validation macro
    pub fn validate(&self, _parent_field: &str) -> Result<(), ApplicationError> {
        Ok(())
    }
}

/// struct ConnectorParamsWithKeys
#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)]
#[serde(default)]
pub struct ConnectorParamsWithKeys {
    /// base url
    pub base_url: String,
    /// api key
    pub api_key: Secret<String>,
    /// merchant ID
    pub merchant_id: Secret<String>,
}

/// struct ConnectorParamsWithModeType
#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)]
#[serde(default)]
pub struct ConnectorParamsWithModeType {
    /// base url
    pub base_url: String,
    /// secondary base url
    pub secondary_base_url: Option<String>,
    /// Can take values like Test or Live for Noon
    pub key_mode: String,
}

/// struct ConnectorParamsWithMoreUrls
#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)]
#[serde(default)]
pub struct ConnectorParamsWithMoreUrls {
    /// base url
    pub base_url: String,
    /// base url for bank redirects
    pub base_url_bank_redirects: String,
}

/// struct ConnectorParamsWithFileUploadUrl
#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)]
#[serde(default)]
pub struct ConnectorParamsWithFileUploadUrl {
    /// base url
    pub base_url: String,
    /// base url for file upload
    pub base_url_file_upload: String,
}

/// struct ConnectorParamsWithThreeBaseUrls
#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)]
#[serde(default)]
pub struct AdyenParamsWithThreeBaseUrls {
    /// base url
    pub base_url: String,
    /// secondary base url
    #[cfg(feature = "payouts")]
    pub payout_base_url: String,
    /// third base url
    pub dispute_base_url: String,
}
/// struct ConnectorParamsWithSecondaryBaseUrl
#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)]
#[serde(default)]
pub struct ConnectorParamsWithSecondaryBaseUrl {
    /// base url
    pub base_url: String,
    /// secondary base url
    pub secondary_base_url: String,
}
/// struct ConnectorParamsWithThreeUrls
#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)]
#[serde(default)]
pub struct ConnectorParamsWithThreeUrls {
    /// base url
    pub base_url: String,
    /// secondary base url
    pub secondary_base_url: String,
    /// third base url
    pub third_base_url: String,
}


// File: crates/hyperswitch_domain_models/src/router_flow_types.rs

pub mod access_token_auth;
pub mod authentication;
pub mod dispute;
pub mod files;
pub mod fraud_check;
pub mod mandate_revoke;
pub mod payments;
pub mod payouts;
pub mod refunds;
pub mod revenue_recovery;
pub mod subscriptions;
pub mod unified_authentication_service;
pub mod vault;
pub mod webhooks;
pub use access_token_auth::*;
pub use dispute::*;
pub use files::*;
pub use fraud_check::*;
pub use payments::*;
pub use payouts::*;
pub use refunds::*;
pub use revenue_recovery::*;
pub use subscriptions::*;
pub use unified_authentication_service::*;
pub use vault::*;
pub use webhooks::*;


// File: crates/hyperswitch_domain_models/src/network_tokenization.rs

#[cfg(feature = "v1")]
use cards::CardNumber;
#[cfg(feature = "v2")]
use cards::NetworkToken;

#[cfg(feature = "v1")]
pub type NetworkTokenNumber = CardNumber;

#[cfg(feature = "v2")]
pub type NetworkTokenNumber = NetworkToken;