    /// The shipping address for the payment. This will override the shipping address provided in the create-intent request
    pub shipping: Option<Address>,

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

    /// Additional details required by 3DS 2.0
    #[schema(value_type = Option<BrowserInformation>)]
    pub browser_info: Option<common_utils::types::BrowserInformation>,

    /// The payment_method_id to be associated with the payment
    #[schema(value_type = Option<String>)]
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,

    #[schema(example = "187282ab-40ef-47a9-9206-5099ba31e432")]
    pub payment_token: Option<String>,

    /// Merchant connector details used to make payments.
    #[schema(value_type = Option<MerchantConnectorAuthDetails>)]
    pub merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,

    /// If true, returns stringified connector raw response body
    pub return_raw_connector_response: Option<bool>,
}

// This struct contains the union of fields in `PaymentsCreateIntentRequest` and
// `PaymentsConfirmIntentRequest`
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(deny_unknown_fields)]
#[cfg(feature = "v2")]
pub struct PaymentsRequest {
    /// The amount details for the payment
    pub amount_details: AmountDetails,

    /// Unique identifier for the payment. This ensures idempotency for multiple payments
    /// that have been done by a single merchant.
    #[schema(
        value_type = Option<String>,
        min_length = 30,
        max_length = 30,
        example = "pay_mbabizu24mvu3mela5njyhpit4"
    )]
    pub merchant_reference_id: Option<id_type::PaymentReferenceId>,

    /// The routing algorithm id to be used for the payment
    #[schema(value_type = Option<String>)]
    pub routing_algorithm_id: Option<id_type::RoutingId>,

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

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

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

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

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

    /// Set to `present` to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be `absent` when merchant's doing merchant initiated payments and customer is not present while doing the payment.
    #[schema(example = "present", value_type = Option<PresenceOfCustomerDuringPayment>)]
    pub customer_present: Option<common_enums::PresenceOfCustomerDuringPayment>,

    /// A description for the payment
    #[schema(example = "It's my first payment request", value_type = Option<String>)]
    pub description: Option<common_utils::types::Description>,

    /// The URL to which you want the user to be redirected after the completion of the payment operation
    #[schema(value_type = Option<String>, example = "https://hyperswitch.io")]
    pub return_url: Option<common_utils::types::Url>,

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

    /// Apply MIT exemption for a payment
    #[schema(value_type = Option<MitExemptionRequest>)]
    pub apply_mit_exemption: Option<common_enums::MitExemptionRequest>,

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

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

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

    /// Metadata is useful for storing additional, unstructured information on an object.
    #[schema(value_type = Option<Object>, example = r#"{ "udf1": "some-value", "udf2": "some-value" }"#)]
    pub metadata: Option<pii::SecretSerdeValue>,

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

    /// Additional data that might be required by hyperswitch based on the requested features by the merchants.
    pub feature_metadata: Option<FeatureMetadata>,

    /// Whether to generate the payment link for this payment or not (if applicable)
    #[schema(value_type = Option<EnablePaymentLinkRequest>)]
    pub payment_link_enabled: Option<common_enums::EnablePaymentLinkRequest>,

    /// Configure a custom payment link for the particular payment
    #[schema(value_type = Option<PaymentLinkConfigRequest>)]
    pub payment_link_config: Option<admin::PaymentLinkConfigRequest>,

    ///Request an incremental authorization, i.e., increase the authorized amount on a confirmed payment before you capture it.
    #[schema(value_type = Option<RequestIncrementalAuthorization>)]
    pub request_incremental_authorization: Option<common_enums::RequestIncrementalAuthorization>,

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

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

    /// Whether to perform external authentication (if applicable)
    #[schema(value_type = Option<External3dsAuthenticationRequest>)]
    pub request_external_three_ds_authentication:
        Option<common_enums::External3dsAuthenticationRequest>,

    /// The payment instrument data to be used for the payment
    pub payment_method_data: PaymentMethodDataRequest,

    /// The payment method type to be used for the payment. This should match with the `payment_method_data` provided
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,

    /// The payment method subtype to be used for the payment. This should match with the `payment_method_data` provided
    #[schema(value_type = PaymentMethodType, example = "apple_pay")]
    pub payment_method_subtype: api_enums::PaymentMethodType,

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

    /// Additional details required by 3DS 2.0
    #[schema(value_type = Option<BrowserInformation>)]
    pub browser_info: Option<common_utils::types::BrowserInformation>,

    /// The payment_method_id to be associated with the payment
    #[schema(value_type = Option<String>)]
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,

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

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

    /// Merchant connector details used to make payments.
    #[schema(value_type = Option<MerchantConnectorAuthDetails>)]
    pub merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,

    /// Stringified connector raw response body. Only returned if `return_raw_connector_response` is true
    pub return_raw_connector_response: Option<bool>,
}

#[cfg(feature = "v2")]
impl From<&PaymentsRequest> for PaymentsCreateIntentRequest {
    fn from(request: &PaymentsRequest) -> Self {
        Self {
            amount_details: request.amount_details.clone(),
            merchant_reference_id: request.merchant_reference_id.clone(),
            routing_algorithm_id: request.routing_algorithm_id.clone(),
            capture_method: request.capture_method,
            authentication_type: request.authentication_type,
            billing: request.billing.clone(),
            shipping: request.shipping.clone(),
            customer_id: request.customer_id.clone(),
            customer_present: request.customer_present,
            description: request.description.clone(),
            return_url: request.return_url.clone(),
            setup_future_usage: request.setup_future_usage,
            apply_mit_exemption: request.apply_mit_exemption,
            statement_descriptor: request.statement_descriptor.clone(),
            order_details: request.order_details.clone(),
            allowed_payment_method_types: request.allowed_payment_method_types.clone(),
            metadata: request.metadata.clone(),
            connector_metadata: request.connector_metadata.clone(),
            feature_metadata: request.feature_metadata.clone(),
            payment_link_enabled: request.payment_link_enabled,
            payment_link_config: request.payment_link_config.clone(),
            request_incremental_authorization: request.request_incremental_authorization,
            session_expiry: request.session_expiry,
            frm_metadata: request.frm_metadata.clone(),
            request_external_three_ds_authentication: request
                .request_external_three_ds_authentication,
            force_3ds_challenge: request.force_3ds_challenge,
            merchant_connector_details: request.merchant_connector_details.clone(),
        }
    }
}

#[cfg(feature = "v2")]
impl From<&PaymentsRequest> for PaymentsConfirmIntentRequest {
    fn from(request: &PaymentsRequest) -> Self {
        Self {
            return_url: request.return_url.clone(),
            payment_method_data: request.payment_method_data.clone(),
            payment_method_type: request.payment_method_type,
            payment_method_subtype: request.payment_method_subtype,
            shipping: request.shipping.clone(),
            customer_acceptance: request.customer_acceptance.clone(),
            browser_info: request.browser_info.clone(),
            payment_method_id: request.payment_method_id.clone(),
            payment_token: None,
            merchant_connector_details: request.merchant_connector_details.clone(),
            return_raw_connector_response: request.return_raw_connector_response,
        }
    }
}

// Serialize is implemented because, this will be serialized in the api events.
// Usually request types should not have serialize implemented.
//
/// Request body for Payment Status
#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaymentsRetrieveRequest {
    /// A boolean used to indicate if the payment status should be fetched from the connector
    /// If this is set to true, the status will be fetched from the connector
    #[serde(default)]
    pub force_sync: bool,
    /// A boolean used to indicate if all the attempts needs to be fetched for the intent.
    /// If this is set to true, attempts list will be available in the response.
    #[serde(default)]
    pub expand_attempts: bool,
    /// These are the query params that are sent in case of redirect response.
    /// These can be ingested by the connector to take necessary actions.
    pub param: Option<String>,
    /// If true, returns stringified connector raw response body
    pub return_raw_connector_response: Option<bool>,
    /// Merchant connector details used to make payments.
    #[schema(value_type = Option<MerchantConnectorAuthDetails>)]
    pub merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
/// Request for Payment Status
pub struct PaymentsStatusRequest {
    /// A boolean used to indicate if the payment status should be fetched from the connector
    /// If this is set to true, the status will be fetched from the connector
    #[serde(default)]
    pub force_sync: bool,
    /// A boolean used to indicate if all the attempts needs to be fetched for the intent.
    /// If this is set to true, attempts list will be available in the response.
    #[serde(default)]
    pub expand_attempts: bool,
    /// These are the query params that are sent in case of redirect response.
    /// These can be ingested by the connector to take necessary actions.
    pub param: Option<String>,
    /// If true, returns stringified connector raw response body
    pub return_raw_connector_response: Option<bool>,
}

/// Error details for the payment
#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, ToSchema)]
pub struct ErrorDetails {
    /// The error code
    pub code: String,
    /// The error message
    pub message: String,
    /// The unified error code across all connectors.
    /// This can be relied upon for taking decisions based on the error.
    pub unified_code: Option<String>,
    /// The unified error message across all connectors.
    /// If there is a translation available, this will have the translated message
    pub unified_message: Option<String>,
    /// This field can be returned for both approved and refused Mastercard payments.
    /// This code provides additional information about the type of transaction or the reason why the payment failed.
    /// If the payment failed, the network advice code gives guidance on if and when you can retry the payment.
    pub network_advice_code: Option<String>,
    /// For card errors resulting from a card issuer decline, a brand specific 2, 3, or 4 digit code which indicates the reason the authorization failed.
    pub network_decline_code: Option<String>,
    /// A string indicating how to proceed with an network error if payment gateway provide one. This is used to understand the network error code better.
    pub network_error_message: Option<String>,
}

/// Token information that can be used to initiate transactions by the merchant.
#[cfg(feature = "v2")]
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct ConnectorTokenDetails {
    /// A token that can be used to make payments directly with the connector.
    #[schema(example = "pm_9UhMqBMEOooRIvJFFdeW")]
    pub token: String,

    /// The reference id sent to the connector when creating the token
    pub connector_token_request_reference_id: Option<String>,
}

/// Response for Payment Intent Confirm
/// Few fields should be expandable, we need not return these in the normal response
/// But when explicitly requested for expanded objects, these can be returned
/// For example
/// shipping, billing, customer, payment_method
#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
pub struct PaymentsResponse {
    /// Unique identifier for the payment. This ensures idempotency for multiple payments
    /// that have been done by a single merchant.
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "12345_pay_01926c58bc6e77c09e809964e72af8c8",
        value_type = String,
    )]
    pub id: id_type::GlobalPaymentId,

    #[schema(value_type = IntentStatus, example = "succeeded")]
    pub status: api_enums::IntentStatus,

    /// Amount related information for this payment and attempt
    pub amount: PaymentAmountDetailsResponse,

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

    /// The connector used for the payment
    #[schema(example = "stripe")]
    pub connector: Option<String>,

    /// Time when the payment was created
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created: PrimitiveDateTime,

    /// The payment method information provided for making a payment
    #[schema(value_type = Option<PaymentMethodDataResponseWithBilling>)]
    #[serde(serialize_with = "serialize_payment_method_data_response")]
    pub payment_method_data: Option<PaymentMethodDataResponseWithBilling>,

    /// The payment method type for this payment attempt
    #[schema(value_type = Option<PaymentMethod>, example = "wallet")]
    pub payment_method_type: Option<api_enums::PaymentMethod>,

    #[schema(value_type = Option<PaymentMethodType>, example = "apple_pay")]
    pub payment_method_subtype: Option<api_enums::PaymentMethodType>,

    /// A unique identifier for a payment provided by the connector
    #[schema(value_type = Option<String>, example = "993672945374576J")]
    pub connector_transaction_id: Option<String>,

    /// reference(Identifier) to the payment at connector side
    #[schema(value_type = Option<String>, example = "993672945374576J")]
    pub connector_reference_id: Option<String>,

    /// Identifier of the connector ( merchant connector account ) which was chosen to make the payment
    #[schema(value_type = Option<String>)]
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,

    /// The browser information used for this payment
    #[schema(value_type = Option<BrowserInformation>)]
    pub browser_info: Option<common_utils::types::BrowserInformation>,

    /// Error details for the payment if any
    pub error: Option<ErrorDetails>,

    /// The shipping address associated with the payment intent
    pub shipping: Option<Address>,

    /// The billing address associated with the payment intent
    pub billing: Option<Address>,

    /// List of payment attempts associated with payment intent
    pub attempts: Option<Vec<PaymentAttemptResponse>>,

    /// Connector token information that can be used to make payments directly by the merchant.
    pub connector_token_details: Option<ConnectorTokenDetails>,

    /// The payment_method_id associated with the payment
    #[schema(value_type = Option<String>)]
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,

    /// Additional information required for redirection
    pub next_action: Option<NextActionData>,

    /// The url to which user must be redirected to after completion of the purchase
    #[schema(value_type = Option<String>)]
    pub return_url: Option<common_utils::types::Url>,

    /// The authentication type that was requested for this order
    #[schema(value_type = Option<AuthenticationType>, example = "no_three_ds", default = "no_three_ds")]
    pub authentication_type: Option<api_enums::AuthenticationType>,

    /// The authentication type that was appliced for this order
    /// This depeneds on the 3DS rules configured, If not a default authentication type will be applied
    #[schema(value_type = Option<AuthenticationType>, example = "no_three_ds", default = "no_three_ds")]
    pub authentication_type_applied: Option<api_enums::AuthenticationType>,

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

    /// Unique identifier for the payment. This ensures idempotency for multiple payments
    /// that have been done by a single merchant.
    #[schema(
        value_type = Option<String>,
        min_length = 30,
        max_length = 30,
        example = "pay_mbabizu24mvu3mela5njyhpit4"
    )]
    pub merchant_reference_id: Option<id_type::PaymentReferenceId>,

    /// Stringified connector raw response body. Only returned if `return_raw_connector_response` is true
    #[schema(value_type = Option<String>)]
    pub raw_connector_response: Option<Secret<String>>,

    /// Additional data that might be required by hyperswitch based on the additional features.
    pub feature_metadata: Option<FeatureMetadata>,
}

#[cfg(feature = "v2")]
impl PaymentAttemptListResponse {
    pub fn find_attempt_in_attempts_list_using_connector_transaction_id(
        &self,
        connector_transaction_id: &common_utils::types::ConnectorTransactionId,
    ) -> Option<PaymentAttemptResponse> {
        self.payment_attempt_list.iter().find_map(|attempt| {
            attempt
                .connector_payment_id
                .as_ref()
                .filter(|txn_id| *txn_id == connector_transaction_id)
                .map(|_| attempt.clone())
        })
    }
    pub fn find_attempt_in_attempts_list_using_charge_id(
        &self,
        charge_id: String,
    ) -> Option<PaymentAttemptResponse> {
        self.payment_attempt_list.iter().find_map(|attempt| {
            attempt.feature_metadata.as_ref().and_then(|metadata| {
                metadata.revenue_recovery.as_ref().and_then(|recovery| {
                    recovery
                        .charge_id
                        .as_ref()
                        .filter(|id| **id == charge_id)
                        .map(|_| attempt.clone())
                })
            })
        })
    }
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[cfg(feature = "v2")]
pub struct PaymentStartRedirectionRequest {
    /// Global Payment ID
    pub id: id_type::GlobalPaymentId,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[cfg(feature = "v2")]
pub struct PaymentStartRedirectionParams {
    /// The identifier for the Merchant Account.
    pub publishable_key: String,
    /// The identifier for business profile
    pub profile_id: id_type::ProfileId,
}

/// Details of external authentication
#[derive(Setter, Clone, Default, Debug, PartialEq, serde::Serialize, ToSchema)]
pub struct ExternalAuthenticationDetailsResponse {
    /// Authentication Type - Challenge / Frictionless
    #[schema(value_type = Option<DecoupledAuthenticationType>)]
    pub authentication_flow: Option<enums::DecoupledAuthenticationType>,
    /// Electronic Commerce Indicator (eci)
    pub electronic_commerce_indicator: Option<String>,
    /// Authentication Status
    #[schema(value_type = AuthenticationStatus)]
    pub status: enums::AuthenticationStatus,
    /// DS Transaction ID
    pub ds_transaction_id: Option<String>,
    /// Message Version
    pub version: Option<String>,
    /// Error Code
    pub error_code: Option<String>,
    /// Error Message
    pub error_message: Option<String>,
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)]
#[serde(deny_unknown_fields)]
pub struct PaymentListConstraints {
    /// The identifier for customer
    #[schema(
        max_length = 64,
        min_length = 1,
        example = "cus_y3oqhf46pyzuxjbcn2giaqnb44",
        value_type = Option<String>,
    )]
    pub customer_id: Option<id_type::CustomerId>,

    /// A cursor for use in pagination, fetch the next list after some object
    #[schema(example = "pay_fafa124123", value_type = Option<String>)]
    pub starting_after: Option<id_type::PaymentId>,

    /// A cursor for use in pagination, fetch the previous list before some object
    #[schema(example = "pay_fafa124123", value_type = Option<String>)]
    pub ending_before: Option<id_type::PaymentId>,

    /// limit on the number of objects to return
    #[schema(default = 10, maximum = 100)]
    #[serde(default = "default_payments_list_limit")]
    pub limit: u32,

    /// The time at which payment is created
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<PrimitiveDateTime>,

    /// Time less than the payment created time
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,
        with = "common_utils::custom_serde::iso8601::option",
        rename = "created.lt"
    )]
    pub created_lt: Option<PrimitiveDateTime>,

    /// Time greater than the payment created time
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,
        with = "common_utils::custom_serde::iso8601::option",
        rename = "created.gt"
    )]
    pub created_gt: Option<PrimitiveDateTime>,

    /// Time less than or equals to the payment created time
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,
        with = "common_utils::custom_serde::iso8601::option",
        rename = "created.lte"
    )]
    pub created_lte: Option<PrimitiveDateTime>,

    /// Time greater than or equals to the payment created time
    #[schema(example = "2022-09-10T10:11:12Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    #[serde(rename = "created.gte")]
    pub created_gte: Option<PrimitiveDateTime>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, utoipa::IntoParams)]
#[serde(deny_unknown_fields)]
pub struct PaymentListConstraints {
    /// The identifier for payment
    #[param(example = "pay_fafa124123", value_type = Option<String>)]
    pub payment_id: Option<id_type::GlobalPaymentId>,

    /// The identifier for business profile
    #[param(example = "pay_fafa124123", value_type = Option<String>)]
    pub profile_id: Option<id_type::ProfileId>,

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

    /// A cursor for use in pagination, fetch the next list after some object
    #[param(example = "pay_fafa124123", value_type = Option<String>)]
    pub starting_after: Option<id_type::GlobalPaymentId>,

    /// A cursor for use in pagination, fetch the previous list before some object
    #[param(example = "pay_fafa124123", value_type = Option<String>)]
    pub ending_before: Option<id_type::GlobalPaymentId>,

    /// limit on the number of objects to return
    #[param(default = 10, maximum = 100)]
    #[serde(default = "default_payments_list_limit")]
    pub limit: u32,

    /// The starting point within a list of objects
    pub offset: Option<u32>,

    /// The time at which payment is created
    #[param(example = "2022-09-10T10:11:12Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<PrimitiveDateTime>,

    /// Time less than the payment created time
    #[param(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,
        with = "common_utils::custom_serde::iso8601::option",
        rename = "created.lt"
    )]
    pub created_lt: Option<PrimitiveDateTime>,

    /// Time greater than the payment created time
    #[param(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,
        with = "common_utils::custom_serde::iso8601::option",
        rename = "created.gt"
    )]
    pub created_gt: Option<PrimitiveDateTime>,

    /// Time less than or equals to the payment created time
    #[param(example = "2022-09-10T10:11:12Z")]
    #[serde(
        default,
        with = "common_utils::custom_serde::iso8601::option",
        rename = "created.lte"
    )]
    pub created_lte: Option<PrimitiveDateTime>,

    /// Time greater than or equals to the payment created time
    #[param(example = "2022-09-10T10:11:12Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    #[serde(rename = "created.gte")]
    pub created_gte: Option<PrimitiveDateTime>,

    /// The start amount to filter list of transactions which are greater than or equal to the start amount
    pub start_amount: Option<i64>,
    /// The end amount to filter list of transactions which are less than or equal to the end amount
    pub end_amount: Option<i64>,
    /// The connector to filter payments list
    #[param(value_type = Option<Vec<Connector>>)]
    #[serde(deserialize_with = "parse_comma_separated", default)]
    pub connector: Option<Vec<api_enums::Connector>>,
    /// The currency to filter payments list
    #[param(value_type = Option<Vec<Currency>>)]
    #[serde(deserialize_with = "parse_comma_separated", default)]
    pub currency: Option<Vec<enums::Currency>>,
    /// The payment status to filter payments list
    #[param(value_type = Option<Vec<IntentStatus>>)]
    #[serde(deserialize_with = "parse_comma_separated", default)]
    pub status: Option<Vec<enums::IntentStatus>>,
    /// The payment method type to filter payments list
    #[param(value_type = Option<Vec<PaymentMethod>>)]
    #[serde(deserialize_with = "parse_comma_separated", default)]
    pub payment_method_type: Option<Vec<enums::PaymentMethod>>,
    /// The payment method subtype to filter payments list
    #[param(value_type = Option<Vec<PaymentMethodType>>)]
    #[serde(deserialize_with = "parse_comma_separated", default)]
    pub payment_method_subtype: Option<Vec<enums::PaymentMethodType>>,
    /// The authentication type to filter payments list
    #[param(value_type = Option<Vec<AuthenticationType>>)]
    #[serde(deserialize_with = "parse_comma_separated", default)]
    pub authentication_type: Option<Vec<enums::AuthenticationType>>,
    /// The merchant connector id to filter payments list
    #[param(value_type = Option<Vec<String>>)]
    #[serde(deserialize_with = "parse_comma_separated", default)]
    pub merchant_connector_id: Option<Vec<id_type::MerchantConnectorAccountId>>,
    /// The field on which the payments list should be sorted
    #[serde(default)]
    pub order_on: SortOn,
    /// The order in which payments list should be sorted
    #[serde(default)]
    pub order_by: SortBy,
    /// The card networks to filter payments list
    #[param(value_type = Option<Vec<CardNetwork>>)]
    #[serde(deserialize_with = "parse_comma_separated", default)]
    pub card_network: Option<Vec<enums::CardNetwork>>,
    /// The identifier for merchant order reference id
    pub merchant_order_reference_id: Option<String>,
}

#[cfg(feature = "v2")]
impl PaymentListConstraints {
    pub fn has_no_attempt_filters(&self) -> bool {
        self.connector.is_none()
            && self.payment_method_type.is_none()
            && self.payment_method_subtype.is_none()
            && self.authentication_type.is_none()
            && self.merchant_connector_id.is_none()
            && self.card_network.is_none()
    }
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, serde::Serialize, ToSchema)]
pub struct PaymentListResponse {
    /// The number of payments included in the list
    pub size: usize,
    // The list of payments response objects
    pub data: Vec<PaymentsResponse>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Serialize, ToSchema)]
pub struct PaymentListResponse {
    /// The number of payments included in the current response
    pub count: usize,
    /// The total number of available payments for given constraints
    pub total_count: i64,
    /// The list of payments response objects
    pub data: Vec<PaymentsListResponseItem>,
}
#[derive(Setter, Clone, Default, Debug, PartialEq, serde::Serialize, ToSchema)]
pub struct IncrementalAuthorizationResponse {
    /// The unique identifier of authorization
    pub authorization_id: String,
    /// Amount the authorization has been made for
    #[schema(value_type = i64, example = 6540)]
    pub amount: MinorUnit,
    #[schema(value_type= AuthorizationStatus)]
    /// The status of the authorization
    pub status: common_enums::AuthorizationStatus,
    /// Error code sent by the connector for authorization
    pub error_code: Option<String>,
    /// Error message sent by the connector for authorization
    pub error_message: Option<String>,
    /// Previously authorized amount for the payment
    pub previously_authorized_amount: MinorUnit,
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, serde::Serialize)]
pub struct PaymentListResponseV2 {
    /// The number of payments included in the list for given constraints
    pub count: usize,
    /// The total number of available payments for given constraints
    pub total_count: i64,
    /// The list of payments response objects
    pub data: Vec<PaymentsResponse>,
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct PaymentListFilterConstraints {
    /// The identifier for payment
    pub payment_id: Option<id_type::PaymentId>,
    /// The identifier for business profile
    pub profile_id: Option<id_type::ProfileId>,
    /// The identifier for customer
    pub customer_id: Option<id_type::CustomerId>,
    /// The limit on the number of objects. The default limit is 10 and max limit is 20
    #[serde(default = "default_payments_list_limit")]
    pub limit: u32,
    /// The starting point within a list of objects
    pub offset: Option<u32>,
    /// The amount to filter payments list
    pub amount_filter: Option<AmountFilter>,
    /// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc).
    #[serde(flatten)]
    pub time_range: Option<common_utils::types::TimeRange>,
    /// The list of connectors to filter payments list
    pub connector: Option<Vec<api_enums::Connector>>,
    /// The list of currencies to filter payments list
    pub currency: Option<Vec<enums::Currency>>,
    /// The list of payment status to filter payments list
    pub status: Option<Vec<enums::IntentStatus>>,
    /// The list of payment methods to filter payments list
    pub payment_method: Option<Vec<enums::PaymentMethod>>,
    /// The list of payment method types to filter payments list
    pub payment_method_type: Option<Vec<enums::PaymentMethodType>>,
    /// The list of authentication types to filter payments list
    pub authentication_type: Option<Vec<enums::AuthenticationType>>,
    /// The list of merchant connector ids to filter payments list for selected label
    pub merchant_connector_id: Option<Vec<id_type::MerchantConnectorAccountId>>,
    /// The order in which payments list should be sorted
    #[serde(default)]
    pub order: Order,


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


impl api::MandateSetup for Nexixpay {}


File: crates/euclid/src/frontend/ast/lowering.rs
 Public functions: 2

//! Analysis for the Lowering logic in ast
//!
//!Certain functions that can be used to perform the complete lowering of ast to dir.
//!This includes lowering of enums, numbers, strings as well as Comparison logics.

use std::str::FromStr;

use crate::{
    dssa::types::{AnalysisError, AnalysisErrorType},
    enums::CollectVariants,
    frontend::{
        ast,
        dir::{self, enums as dir_enums, EuclidDirFilter},
    },
    types::{self, DataType},
};

/// lowers the provided key (enum variant) & value to the respective DirValue
///
/// For example
/// ```notrust
/// CardType = Visa
/// ```notrust
///
/// This serves for the purpose were we have the DirKey as an explicit Enum type and value as one
/// of the member of the same Enum.
/// So particularly it lowers a predefined Enum from DirKey to an Enum of DirValue.
macro_rules! lower_enum {
    ($key:ident, $value:ident) => {
        match $value {
            ast::ValueType::EnumVariant(ev) => Ok(vec![dir::DirValue::$key(
                dir_enums::$key::from_str(&ev).map_err(|_| AnalysisErrorType::InvalidVariant {
                    key: dir::DirKeyKind::$key.to_string(),
                    got: ev,
                    expected: dir_enums::$key::variants(),
                })?,
            )]),

            ast::ValueType::EnumVariantArray(eva) => eva
                .into_iter()
                .map(|ev| {
                    Ok(dir::DirValue::$key(
                        dir_enums::$key::from_str(&ev).map_err(|_| {
                            AnalysisErrorType::InvalidVariant {
                                key: dir::DirKeyKind::$key.to_string(),
                                got: ev,
                                expected: dir_enums::$key::variants(),
                            }
                        })?,
                    ))
                })
                .collect(),

            _ => Err(AnalysisErrorType::InvalidType {
                key: dir::DirKeyKind::$key.to_string(),
                expected: DataType::EnumVariant,
                got: $value.get_type(),
            }),
        }
    };
}

/// lowers the provided key for a numerical value
///
/// For example
/// ```notrust
/// payment_amount = 17052001
/// ```notrust
/// This is for the cases in which there are numerical values involved and they are lowered
/// accordingly on basis of the supplied key, currently payment_amount is the only key having this
/// use case
macro_rules! lower_number {
    ($key:ident, $value:ident, $comp:ident) => {
        match $value {
            ast::ValueType::Number(num) => Ok(vec![dir::DirValue::$key(types::NumValue {
                number: num,
                refinement: $comp.into(),
            })]),

            ast::ValueType::NumberArray(na) => na
                .into_iter()
                .map(|num| {
                    Ok(dir::DirValue::$key(types::NumValue {
                        number: num,
                        refinement: $comp.clone().into(),
                    }))
                })
                .collect(),

            ast::ValueType::NumberComparisonArray(nca) => nca
                .into_iter()
                .map(|nc| {
                    Ok(dir::DirValue::$key(types::NumValue {
                        number: nc.number,
                        refinement: nc.comparison_type.into(),
                    }))
                })
                .collect(),

            _ => Err(AnalysisErrorType::InvalidType {
                key: dir::DirKeyKind::$key.to_string(),
                expected: DataType::Number,
                got: $value.get_type(),
            }),
        }
    };
}

/// lowers the provided key & value to the respective DirValue
///
/// For example
/// ```notrust
/// card_bin = "123456"
/// ```notrust
///
/// This serves for the purpose were we have the DirKey as Card_bin and value as an arbitrary string
/// So particularly it lowers an arbitrary value to a predefined key.
macro_rules! lower_str {
    ($key:ident, $value:ident $(, $validation_closure:expr)?) => {
        match $value {
            ast::ValueType::StrValue(st) => {
                $($validation_closure(&st)?;)?
                Ok(vec![dir::DirValue::$key(types::StrValue { value: st })])
            }
            _ => Err(AnalysisErrorType::InvalidType {
                key: dir::DirKeyKind::$key.to_string(),
                expected: DataType::StrValue,
                got: $value.get_type(),
            }),
        }
    };
}

macro_rules! lower_metadata {
    ($key:ident, $value:ident) => {
        match $value {
            ast::ValueType::MetadataVariant(md) => {
                Ok(vec![dir::DirValue::$key(types::MetadataValue {
                    key: md.key,
                    value: md.value,
                })])
            }
            _ => Err(AnalysisErrorType::InvalidType {
                key: dir::DirKeyKind::$key.to_string(),
                expected: DataType::MetadataValue,
                got: $value.get_type(),
            }),
        }
    };
}
/// lowers the comparison operators for different subtle value types present
/// by throwing required errors for comparisons that can't be performed for a certain value type
/// for example
/// can't have greater/less than operations on enum types
fn lower_comparison_inner<O: EuclidDirFilter>(
    comp: ast::Comparison,
) -> Result<Vec<dir::DirValue>, AnalysisErrorType> {
    let key_enum = dir::DirKeyKind::from_str(comp.lhs.as_str())
        .map_err(|_| AnalysisErrorType::InvalidKey(comp.lhs.clone()))?;

    if !O::is_key_allowed(&key_enum) {
        return Err(AnalysisErrorType::InvalidKey(key_enum.to_string()));
    }

    match (&comp.comparison, &comp.value) {
        (
            ast::ComparisonType::LessThan
            | ast::ComparisonType::GreaterThan
            | ast::ComparisonType::GreaterThanEqual
            | ast::ComparisonType::LessThanEqual,
            ast::ValueType::EnumVariant(_),
        ) => {
            Err(AnalysisErrorType::InvalidComparison {
                operator: comp.comparison.clone(),
                value_type: DataType::EnumVariant,
            })?;
        }

        (
            ast::ComparisonType::LessThan
            | ast::ComparisonType::GreaterThan
            | ast::ComparisonType::GreaterThanEqual
            | ast::ComparisonType::LessThanEqual,
            ast::ValueType::NumberArray(_),
        ) => {
            Err(AnalysisErrorType::InvalidComparison {
                operator: comp.comparison.clone(),
                value_type: DataType::Number,
            })?;
        }

        (
            ast::ComparisonType::LessThan
            | ast::ComparisonType::GreaterThan
            | ast::ComparisonType::GreaterThanEqual
            | ast::ComparisonType::LessThanEqual,
            ast::ValueType::EnumVariantArray(_),
        ) => {
            Err(AnalysisErrorType::InvalidComparison {
                operator: comp.comparison.clone(),
                value_type: DataType::EnumVariant,
            })?;
        }

        (
            ast::ComparisonType::LessThan
            | ast::ComparisonType::GreaterThan
            | ast::ComparisonType::GreaterThanEqual
            | ast::ComparisonType::LessThanEqual,
            ast::ValueType::NumberComparisonArray(_),
        ) => {
            Err(AnalysisErrorType::InvalidComparison {
                operator: comp.comparison.clone(),
                value_type: DataType::Number,
            })?;
        }

        _ => {}
    }

    let value = comp.value;
    let comparison = comp.comparison;

    match key_enum {
        dir::DirKeyKind::PaymentMethod => lower_enum!(PaymentMethod, value),
        dir::DirKeyKind::CardType => lower_enum!(CardType, value),
        dir::DirKeyKind::CardNetwork => lower_enum!(CardNetwork, value),
        dir::DirKeyKind::PayLaterType => lower_enum!(PayLaterType, value),
        dir::DirKeyKind::WalletType => lower_enum!(WalletType, value),
        dir::DirKeyKind::BankDebitType => lower_enum!(BankDebitType, value),
        dir::DirKeyKind::BankRedirectType => lower_enum!(BankRedirectType, value),
        dir::DirKeyKind::CryptoType => lower_enum!(CryptoType, value),
        dir::DirKeyKind::PaymentType => lower_enum!(PaymentType, value),
        dir::DirKeyKind::MandateType => lower_enum!(MandateType, value),
        dir::DirKeyKind::MandateAcceptanceType => lower_enum!(MandateAcceptanceType, value),
        dir::DirKeyKind::RewardType => lower_enum!(RewardType, value),
        dir::DirKeyKind::PaymentCurrency => lower_enum!(PaymentCurrency, value),
        dir::DirKeyKind::AuthenticationType => lower_enum!(AuthenticationType, value),
        dir::DirKeyKind::CaptureMethod => lower_enum!(CaptureMethod, value),
        dir::DirKeyKind::BusinessCountry => lower_enum!(BusinessCountry, value),
        dir::DirKeyKind::BillingCountry => lower_enum!(BillingCountry, value),
        dir::DirKeyKind::SetupFutureUsage => lower_enum!(SetupFutureUsage, value),
        dir::DirKeyKind::UpiType => lower_enum!(UpiType, value),
        dir::DirKeyKind::OpenBankingType => lower_enum!(OpenBankingType, value),
        dir::DirKeyKind::VoucherType => lower_enum!(VoucherType, value),
        dir::DirKeyKind::GiftCardType => lower_enum!(GiftCardType, value),
        dir::DirKeyKind::BankTransferType => lower_enum!(BankTransferType, value),
        dir::DirKeyKind::CardRedirectType => lower_enum!(CardRedirectType, value),
        dir::DirKeyKind::MobilePaymentType => lower_enum!(MobilePaymentType, value),
        dir::DirKeyKind::RealTimePaymentType => lower_enum!(RealTimePaymentType, value),
        dir::DirKeyKind::CardBin => {
            let validation_closure = |st: &String| -> Result<(), AnalysisErrorType> {
                if st.len() == 6 && st.chars().all(|x| x.is_ascii_digit()) {
                    Ok(())
                } else {
                    Err(AnalysisErrorType::InvalidValue {
                        key: dir::DirKeyKind::CardBin,
                        value: st.clone(),
                        message: Some("Expected 6 digits".to_string()),
                    })
                }
            };
            lower_str!(CardBin, value, validation_closure)
        }
        dir::DirKeyKind::BusinessLabel => lower_str!(BusinessLabel, value),
        dir::DirKeyKind::MetaData => lower_metadata!(MetaData, value),
        dir::DirKeyKind::PaymentAmount => lower_number!(PaymentAmount, value, comparison),
        dir::DirKeyKind::Connector => Err(AnalysisErrorType::InvalidKey(
            dir::DirKeyKind::Connector.to_string(),
        )),
        dir::DirKeyKind::IssuerName => lower_str!(IssuerName, value),
        dir::DirKeyKind::IssuerCountry => lower_enum!(IssuerCountry, value),
        dir::DirKeyKind::CustomerDevicePlatform => lower_enum!(CustomerDevicePlatform, value),
        dir::DirKeyKind::CustomerDeviceType => lower_enum!(CustomerDeviceType, value),
        dir::DirKeyKind::CustomerDeviceDisplaySize => lower_enum!(CustomerDeviceDisplaySize, value),
        dir::DirKeyKind::AcquirerCountry => lower_enum!(AcquirerCountry, value),
        dir::DirKeyKind::AcquirerFraudRate => lower_number!(AcquirerFraudRate, value, comparison),
    }
}

/// returns all the comparison values by matching them appropriately to ComparisonTypes and in turn
/// calls the lower_comparison_inner function
fn lower_comparison<O: EuclidDirFilter>(
    comp: ast::Comparison,
) -> Result<dir::DirComparison, AnalysisError> {
    let metadata = comp.metadata.clone();
    let logic = match &comp.comparison {
        ast::ComparisonType::Equal => dir::DirComparisonLogic::PositiveDisjunction,
        ast::ComparisonType::NotEqual => dir::DirComparisonLogic::NegativeConjunction,
        ast::ComparisonType::LessThan => dir::DirComparisonLogic::PositiveDisjunction,
        ast::ComparisonType::LessThanEqual => dir::DirComparisonLogic::PositiveDisjunction,
        ast::ComparisonType::GreaterThanEqual => dir::DirComparisonLogic::PositiveDisjunction,
        ast::ComparisonType::GreaterThan => dir::DirComparisonLogic::PositiveDisjunction,
    };
    let values = lower_comparison_inner::<O>(comp).map_err(|etype| AnalysisError {
        error_type: etype,
        metadata: metadata.clone(),
    })?;

    Ok(dir::DirComparison {
        values,
        logic,
        metadata,
    })
}

/// lowers the if statement accordingly with a condition and following nested if statements (if
/// present)
fn lower_if_statement<O: EuclidDirFilter>(
    stmt: ast::IfStatement,
) -> Result<dir::DirIfStatement, AnalysisError> {
    Ok(dir::DirIfStatement {
        condition: stmt
            .condition
            .into_iter()
            .map(lower_comparison::<O>)
            .collect::<Result<_, _>>()?,
        nested: stmt
            .nested
            .map(|n| n.into_iter().map(lower_if_statement::<O>).collect())
            .transpose()?,
    })
}

/// lowers the rules supplied accordingly to DirRule struct by specifying the rule_name,
/// connector_selection and statements that are a bunch of if statements
pub fn lower_rule<O: EuclidDirFilter>(
    rule: ast::Rule<O>,
) -> Result<dir::DirRule<O>, AnalysisError> {
    Ok(dir::DirRule {
        name: rule.name,
        connector_selection: rule.connector_selection,
        statements: rule
            .statements
            .into_iter()
            .map(lower_if_statement::<O>)
            .collect::<Result<_, _>>()?,
    })
}

/// uses the above rules and lowers the whole ast Program into DirProgram by specifying
/// default_selection that is ast ConnectorSelection, a vector of DirRules and clones the metadata
/// whatever comes in the ast_program
pub fn lower_program<O: EuclidDirFilter>(
    program: ast::Program<O>,
) -> Result<dir::DirProgram<O>, AnalysisError> {
    Ok(dir::DirProgram {
        default_selection: program.default_selection,
        rules: program
            .rules
            .into_iter()
            .map(lower_rule)
            .collect::<Result<_, _>>()?,
        metadata: program.metadata,
    })
}


File: crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs
 Public functions: 3
 Public structs: 33

use common_enums::{enums, CaptureMethod, CardNetwork, FutureUsage, PaymentChannel};
use common_types::payments::{ApplePayPaymentData, GpayTokenizationData};
use common_utils::{
    crypto::{self, GenerateDigest},
    date_time,
    ext_traits::Encode,
    fp_utils,
    id_type::CustomerId,
    pii::{self, Email, IpAddress},
    request::Method,
    types::{MinorUnit, StringMajorUnit, StringMajorUnitForConnector},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    address::Address,
    payment_method_data::{
        self, ApplePayWalletData, BankRedirectData, CardDetailsForNetworkTransactionId,
        GooglePayWalletData, PayLaterData, PaymentMethodData, WalletData,
    },
    router_data::{
        AdditionalPaymentMethodConnectorResponse, ConnectorAuthType, ConnectorResponseData,
        ErrorResponse, RouterData,
    },
    router_flow_types::{
        refunds::{Execute, RSync},
        Authorize, Capture, CompleteAuthorize, PSync, PostCaptureVoid, SetupMandate, Void,
    },
    router_request_types::{
        authentication::MessageExtensionAttribute, BrowserInformation, PaymentsAuthorizeData,
        PaymentsPreProcessingData, ResponseId, SetupMandateRequestData,
    },
    router_response_types::{
        MandateReference, PaymentsResponseData, RedirectForm, RefundsResponseData,
    },
    types,
};
use hyperswitch_interfaces::{
    consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
    errors::{self},
};
use masking::{ExposeInterface, PeekInterface, Secret};
use serde::{Deserialize, Serialize};
use url::Url;

use crate::{
    types::{
        PaymentsPreprocessingResponseRouterData, RefundsResponseRouterData, ResponseRouterData,
    },
    utils::{
        self, convert_amount, missing_field_err, AddressData, AddressDetailsData,
        BrowserInformationData, ForeignTryFrom, PaymentsAuthorizeRequestData,
        PaymentsCancelRequestData, PaymentsPreProcessingRequestData, RouterData as _,
    },
};

pub static NUVEI_AMOUNT_CONVERTOR: &StringMajorUnitForConnector = &StringMajorUnitForConnector;

fn to_boolean(string: String) -> bool {
    let str = string.as_str();
    match str {
        "true" => true,
        "false" => false,
        "yes" => true,
        "no" => false,
        _ => false,
    }
}

// The dimensions of the challenge window for full screen.
const CHALLENGE_WINDOW_SIZE: &str = "05";
// The challenge preference for the challenge flow.
const CHALLENGE_PREFERENCE: &str = "01";

trait NuveiAuthorizePreprocessingCommon {
    fn get_browser_info(&self) -> Option<BrowserInformation>;
    fn get_related_transaction_id(&self) -> Option<String>;
    fn get_complete_authorize_url(&self) -> Option<String>;
    fn get_is_moto(&self) -> Option<bool>;
    fn get_ntid(&self) -> Option<String>;
    fn get_connector_mandate_id(&self) -> Option<String>;
    fn get_return_url_required(
        &self,
    ) -> Result<String, error_stack::Report<errors::ConnectorError>>;
    fn get_capture_method(&self) -> Option<CaptureMethod>;
    fn get_minor_amount_required(
        &self,
    ) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>>;
    fn get_customer_id_required(&self) -> Option<CustomerId>;
    fn get_email_required(&self) -> Result<Email, error_stack::Report<errors::ConnectorError>>;
    fn get_currency_required(
        &self,
    ) -> Result<enums::Currency, error_stack::Report<errors::ConnectorError>>;
    fn get_payment_method_data_required(
        &self,
    ) -> Result<PaymentMethodData, error_stack::Report<errors::ConnectorError>>;
    fn get_is_partial_approval(&self) -> Option<PartialApprovalFlag>;
    fn get_order_tax_amount(
        &self,
    ) -> Result<Option<MinorUnit>, error_stack::Report<errors::ConnectorError>>;
    fn is_customer_initiated_mandate_payment(&self) -> bool;
}

impl NuveiAuthorizePreprocessingCommon for SetupMandateRequestData {
    fn get_browser_info(&self) -> Option<BrowserInformation> {
        self.browser_info.clone()
    }

    fn get_related_transaction_id(&self) -> Option<String> {
        self.related_transaction_id.clone()
    }
    fn get_is_moto(&self) -> Option<bool> {
        match self.payment_channel {
            Some(PaymentChannel::MailOrder) | Some(PaymentChannel::TelephoneOrder) => Some(true),
            _ => None,
        }
    }

    fn get_customer_id_required(&self) -> Option<CustomerId> {
        self.customer_id.clone()
    }

    fn get_complete_authorize_url(&self) -> Option<String> {
        self.complete_authorize_url.clone()
    }

    fn get_connector_mandate_id(&self) -> Option<String> {
        self.mandate_id.as_ref().and_then(|mandate_ids| {
            mandate_ids.mandate_reference_id.as_ref().and_then(
                |mandate_ref_id| match mandate_ref_id {
                    api_models::payments::MandateReferenceId::ConnectorMandateId(id) => {
                        id.get_connector_mandate_id()
                    }
                    _ => None,
                },
            )
        })
    }

    fn get_return_url_required(
        &self,
    ) -> Result<String, error_stack::Report<errors::ConnectorError>> {
        self.router_return_url
            .clone()
            .ok_or_else(missing_field_err("return_url"))
    }

    fn get_capture_method(&self) -> Option<CaptureMethod> {
        self.capture_method
    }

    fn get_currency_required(
        &self,
    ) -> Result<enums::Currency, error_stack::Report<errors::ConnectorError>> {
        Ok(self.currency)
    }
    fn get_payment_method_data_required(
        &self,
    ) -> Result<PaymentMethodData, error_stack::Report<errors::ConnectorError>> {
        Ok(self.payment_method_data.clone())
    }
    fn get_order_tax_amount(
        &self,
    ) -> Result<Option<MinorUnit>, error_stack::Report<errors::ConnectorError>> {
        Ok(None)
    }

    fn get_minor_amount_required(
        &self,
    ) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>> {
        self.minor_amount
            .ok_or_else(missing_field_err("minor_amount"))
    }

    fn get_is_partial_approval(&self) -> Option<PartialApprovalFlag> {
        self.enable_partial_authorization
            .map(PartialApprovalFlag::from)
    }

    fn get_email_required(&self) -> Result<Email, error_stack::Report<errors::ConnectorError>> {
        self.email.clone().ok_or_else(missing_field_err("email"))
    }
    fn is_customer_initiated_mandate_payment(&self) -> bool {
        (self.customer_acceptance.is_some() || self.setup_mandate_details.is_some())
            && self.setup_future_usage == Some(FutureUsage::OffSession)
    }
    fn get_ntid(&self) -> Option<String> {
        None
    }
}

impl NuveiAuthorizePreprocessingCommon for PaymentsAuthorizeData {
    fn get_browser_info(&self) -> Option<BrowserInformation> {
        self.browser_info.clone()
    }
    fn get_ntid(&self) -> Option<String> {
        self.get_optional_network_transaction_id()
    }
    fn get_related_transaction_id(&self) -> Option<String> {
        self.related_transaction_id.clone()
    }
    fn get_is_moto(&self) -> Option<bool> {
        match self.payment_channel {
            Some(PaymentChannel::MailOrder) | Some(PaymentChannel::TelephoneOrder) => Some(true),
            _ => None,
        }
    }

    fn get_customer_id_required(&self) -> Option<CustomerId> {
        self.customer_id.clone()
    }

    fn get_complete_authorize_url(&self) -> Option<String> {
        self.complete_authorize_url.clone()
    }

    fn get_connector_mandate_id(&self) -> Option<String> {
        self.connector_mandate_id().clone()
    }

    fn get_return_url_required(
        &self,
    ) -> Result<String, error_stack::Report<errors::ConnectorError>> {
        self.get_router_return_url()
    }

    fn get_capture_method(&self) -> Option<CaptureMethod> {
        self.capture_method
    }

    fn get_minor_amount_required(
        &self,
    ) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>> {
        Ok(self.minor_amount)
    }

    fn get_currency_required(
        &self,
    ) -> Result<enums::Currency, error_stack::Report<errors::ConnectorError>> {
        Ok(self.currency)
    }
    fn get_payment_method_data_required(
        &self,
    ) -> Result<PaymentMethodData, error_stack::Report<errors::ConnectorError>> {
        Ok(self.payment_method_data.clone())
    }
    fn get_order_tax_amount(
        &self,
    ) -> Result<Option<MinorUnit>, error_stack::Report<errors::ConnectorError>> {
        Ok(self.order_tax_amount)
    }

    fn get_email_required(&self) -> Result<Email, error_stack::Report<errors::ConnectorError>> {
        self.get_email()
    }
    fn is_customer_initiated_mandate_payment(&self) -> bool {
        (self.customer_acceptance.is_some() || self.setup_mandate_details.is_some())
            && self.setup_future_usage == Some(FutureUsage::OffSession)
    }
    fn get_is_partial_approval(&self) -> Option<PartialApprovalFlag> {
        self.enable_partial_authorization
            .map(PartialApprovalFlag::from)
    }
}

impl NuveiAuthorizePreprocessingCommon for PaymentsPreProcessingData {
    fn get_browser_info(&self) -> Option<BrowserInformation> {
        self.browser_info.clone()
    }

    fn get_related_transaction_id(&self) -> Option<String> {
        self.related_transaction_id.clone()
    }

    fn get_is_moto(&self) -> Option<bool> {
        None
    }

    fn get_customer_id_required(&self) -> Option<CustomerId> {
        None
    }
    fn get_email_required(&self) -> Result<Email, error_stack::Report<errors::ConnectorError>> {
        self.get_email()
    }
    fn is_customer_initiated_mandate_payment(&self) -> bool {
        (self.customer_acceptance.is_some() || self.setup_mandate_details.is_some())
            && self.setup_future_usage == Some(FutureUsage::OffSession)
    }

    fn get_complete_authorize_url(&self) -> Option<String> {
        self.complete_authorize_url.clone()
    }

    fn get_connector_mandate_id(&self) -> Option<String> {
        self.connector_mandate_id()
    }

    fn get_return_url_required(
        &self,
    ) -> Result<String, error_stack::Report<errors::ConnectorError>> {
        self.get_router_return_url()
    }

    fn get_capture_method(&self) -> Option<CaptureMethod> {
        self.capture_method
    }

    fn get_minor_amount_required(
        &self,
    ) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>> {
        self.get_minor_amount()
    }

    fn get_currency_required(
        &self,
    ) -> Result<enums::Currency, error_stack::Report<errors::ConnectorError>> {
        self.get_currency()
    }
    fn get_payment_method_data_required(
        &self,
    ) -> Result<PaymentMethodData, error_stack::Report<errors::ConnectorError>> {
        self.payment_method_data.clone().ok_or(
            errors::ConnectorError::MissingRequiredField {
                field_name: "payment_method_data",
            }
            .into(),
        )
    }
    fn get_order_tax_amount(
        &self,
    ) -> Result<Option<MinorUnit>, error_stack::Report<errors::ConnectorError>> {
        Ok(None)
    }

    fn get_is_partial_approval(&self) -> Option<PartialApprovalFlag> {
        None
    }

    fn get_ntid(&self) -> Option<String> {
        None
    }
}

#[derive(Debug, Serialize, Default, Deserialize)]
pub struct NuveiMeta {
    pub session_token: Secret<String>,
}

#[derive(Debug, Serialize, Default, Deserialize)]
pub struct NuveiMandateMeta {
    pub frequency: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NuveiSessionRequest {
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub client_request_id: String,
    pub time_stamp: date_time::DateTime<date_time::YYYYMMDDHHmmss>,
    pub checksum: Secret<String>,
}

#[derive(Debug, Serialize, Default, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NuveiSessionResponse {
    pub session_token: Secret<String>,
    pub internal_request_id: i64,
    pub status: String,
    pub err_code: i64,
    pub reason: String,
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub version: String,
    pub client_request_id: String,
}

#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct NuvieAmountDetails {
    total_tax: Option<StringMajorUnit>,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct NuveiPaymentsRequest {
    pub time_stamp: String,
    pub session_token: Secret<String>,
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub client_request_id: Secret<String>,
    pub amount: StringMajorUnit,
    pub currency: enums::Currency,
    /// This ID uniquely identifies your consumer/user in your system.
    pub user_token_id: Option<CustomerId>,
    //unique transaction id
    pub client_unique_id: String,
    pub transaction_type: TransactionType,
    pub is_rebilling: Option<String>,
    pub payment_option: PaymentOption,
    pub is_moto: Option<bool>,
    pub device_details: DeviceDetails,
    pub checksum: Secret<String>,
    pub billing_address: Option<BillingAddress>,
    pub shipping_address: Option<ShippingAddress>,
    pub related_transaction_id: Option<String>,
    pub url_details: Option<UrlDetails>,
    pub amount_details: Option<NuvieAmountDetails>,
    pub is_partial_approval: Option<PartialApprovalFlag>,
    pub external_scheme_details: Option<ExternalSchemeDetails>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum PartialApprovalFlag {
    #[serde(rename = "1")]
    Enabled,
    #[serde(rename = "0")]
    Disabled,
}

impl From<bool> for PartialApprovalFlag {
    fn from(value: bool) -> Self {
        if value {
            Self::Enabled
        } else {
            Self::Disabled
        }
    }
}

#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct UrlDetails {
    pub success_url: String,
    pub failure_url: String,
    pub pending_url: String,
}

#[derive(Debug, Serialize, Default)]
pub struct NuveiInitPaymentRequest {
    pub session_token: Secret<String>,
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub client_request_id: String,
    pub amount: StringMajorUnit,
    pub currency: String,
    pub payment_option: PaymentOption,
    pub checksum: Secret<String>,
}

/// Handles payment request for capture, void and refund flows
#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct NuveiPaymentFlowRequest {
    pub time_stamp: String,
    pub merchant_id: Secret<String>,
    pub merchant_site_id: Secret<String>,
    pub client_request_id: String,
    pub amount: StringMajorUnit,
    pub currency: enums::Currency,
    pub related_transaction_id: Option<String>,
    pub checksum: Secret<String>,
}

#[derive(Debug, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct NuveiPaymentSyncRequest {
    pub session_token: Secret<String>,
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub enum TransactionType {
    Auth,
    #[default]
    Sale,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentOption {
    pub card: Option<Card>,
    pub redirect_url: Option<Url>,
    pub user_payment_option_id: Option<String>,
    pub alternative_payment_method: Option<AlternativePaymentMethod>,
    pub billing_address: Option<BillingAddress>,
    pub shipping_address: Option<BillingAddress>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NuveiBIC {
    #[serde(rename = "ABNANL2A")]
    Abnamro,
    #[serde(rename = "ASNBNL21")]
    ASNBank,
    #[serde(rename = "BUNQNL2A")]
    Bunq,
    #[serde(rename = "INGBNL2A")]
    Ing,
    #[serde(rename = "KNABNL2H")]
    Knab,
    #[serde(rename = "RABONL2U")]
    Rabobank,
    #[serde(rename = "RBRBNL21")]
    RegioBank,
    #[serde(rename = "SNSBNL2A")]
    SNSBank,
    #[serde(rename = "TRIONL2U")]
    TriodosBank,
    #[serde(rename = "FVLBNL22")]
    VanLanschotBankiers,
    #[serde(rename = "MOYONL21")]
    Moneyou,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AlternativePaymentMethod {
    pub payment_method: AlternativePaymentMethodType,
    #[serde(rename = "BIC")]
    pub bank_id: Option<NuveiBIC>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AlternativePaymentMethodType {
    #[default]
    #[serde(rename = "apmgw_expresscheckout")]
    Expresscheckout,
    #[serde(rename = "apmgw_Giropay")]
    Giropay,
    #[serde(rename = "apmgw_Sofort")]
    Sofort,
    #[serde(rename = "apmgw_iDeal")]
    Ideal,
    #[serde(rename = "apmgw_EPS")]
    Eps,
    #[serde(rename = "apmgw_Afterpay")]
    AfterPay,
    #[serde(rename = "apmgw_Klarna")]
    Klarna,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BillingAddress {
    pub email: Email,
    pub first_name: Option<Secret<String>>,
    pub last_name: Option<Secret<String>>,
    pub country: api_models::enums::CountryAlpha2,
    pub phone: Option<Secret<String>>,
    pub city: Option<Secret<String>>,
    pub address: Option<Secret<String>>,
    pub street_number: Option<Secret<String>>,
    pub zip: Option<Secret<String>>,
    pub state: Option<Secret<String>>,
    pub cell: Option<Secret<String>>,
    pub address_match: Option<Secret<String>>,
    pub address_line2: Option<Secret<String>>,
    pub address_line3: Option<Secret<String>>,
    pub home_phone: Option<Secret<String>>,
    pub work_phone: Option<Secret<String>>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ShippingAddress {
    pub salutation: Option<Secret<String>>,
    pub first_name: Option<Secret<String>>,
    pub last_name: Option<Secret<String>>,
    pub address: Option<Secret<String>>,
    pub cell: Option<Secret<String>>,
    pub phone: Option<Secret<String>>,
    pub zip: Option<Secret<String>>,
    pub city: Option<Secret<String>>,
    pub country: api_models::enums::CountryAlpha2,
    pub state: Option<Secret<String>>,
    pub email: Email,
    pub county: Option<Secret<String>>,
    pub address_line2: Option<Secret<String>>,
    pub address_line3: Option<Secret<String>>,
    pub street_number: Option<Secret<String>>,
    pub company_name: Option<Secret<String>>,
    pub care_of: Option<Secret<String>>,
}

impl From<&Address> for BillingAddress {
    fn from(address: &Address) -> Self {
        let address_details = address.address.as_ref();
        Self {
            email: address.email.clone().unwrap_or_default(),
            first_name: address.get_optional_first_name(),
            last_name: address_details.and_then(|address| address.get_optional_last_name()),
            country: address_details
                .and_then(|address| address.get_optional_country())
                .unwrap_or_default(),
            phone: address
                .phone
                .as_ref()
                .and_then(|phone| phone.number.clone()),
            city: address_details
                .and_then(|address| address.get_optional_city().map(|city| city.into())),
            address: address_details.and_then(|address| address.get_optional_line1()),
            street_number: None,
            zip: address_details.and_then(|details| details.get_optional_zip()),
            state: None,
            cell: None,
            address_match: None,
            address_line2: address_details.and_then(|address| address.get_optional_line2()),
            address_line3: address_details.and_then(|address| address.get_optional_line3()),
            home_phone: None,
            work_phone: None,
        }
    }
}

impl From<&Address> for ShippingAddress {
    fn from(address: &Address) -> Self {
        let address_details = address.address.as_ref();

        Self {
            email: address.email.clone().unwrap_or_default(),
            first_name: address_details.and_then(|details| details.get_optional_first_name()),
            last_name: address_details.and_then(|details| details.get_optional_last_name()),
            country: address_details
                .and_then(|details| details.get_optional_country())
                .unwrap_or_default(),
            phone: address
                .phone
                .as_ref()
                .and_then(|phone| phone.number.clone()),
            city: address_details
                .and_then(|details| details.get_optional_city().map(|city| city.into())),
            address: address_details.and_then(|details| details.get_optional_line1()),
            street_number: None,
            zip: address_details.and_then(|details| details.get_optional_zip()),
            state: None,
            cell: None,
            address_line2: address_details.and_then(|details| details.get_optional_line2()),
            address_line3: address_details.and_then(|details| details.get_optional_line3()),
            county: None,
            company_name: None,
            care_of: None,
            salutation: None,
        }
    }
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Card {
    pub card_number: Option<cards::CardNumber>,
    pub card_holder_name: Option<Secret<String>>,
    pub expiration_month: Option<Secret<String>>,
    pub expiration_year: Option<Secret<String>>,
    #[serde(rename = "CVV")]
    pub cvv: Option<Secret<String>>,
    pub three_d: Option<ThreeD>,
    pub cc_card_number: Option<Secret<String>>,
    pub bin: Option<Secret<String>>,
    pub last4_digits: Option<Secret<String>>,
    pub cc_exp_month: Option<Secret<String>>,
    pub cc_exp_year: Option<Secret<String>>,
    pub acquirer_id: Option<Secret<String>>,
    pub cvv2_reply: Option<String>,
    pub avs_code: Option<String>,
    pub card_type: Option<String>,
    pub brand: Option<String>,
    pub issuer_bank_name: Option<String>,
    pub issuer_country: Option<String>,
    pub is_prepaid: Option<String>,
    pub external_token: Option<ExternalToken>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalToken {
    pub external_token_provider: ExternalTokenProvider,
    pub mobile_token: Option<Secret<String>>,
    pub cryptogram: Option<Secret<String>>,
    pub eci_provider: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum ExternalTokenProvider {
    #[default]
    GooglePay,
    ApplePay,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreeD {
    pub method_completion_ind: Option<MethodCompletion>,
    pub browser_details: Option<BrowserDetails>,
    pub version: Option<String>,
    #[serde(rename = "notificationURL")]
    pub notification_url: Option<String>,
    #[serde(rename = "merchantURL")]
    pub merchant_url: Option<String>,
    pub acs_url: Option<String>,
    pub acs_challenge_mandate: Option<String>,
    pub c_req: Option<Secret<String>>,
    pub three_d_flow: Option<String>,
    pub external_transaction_id: Option<String>,
    pub transaction_id: Option<String>,
    pub three_d_reason_id: Option<String>,
    pub three_d_reason: Option<String>,
    pub challenge_preference_reason: Option<String>,
    pub challenge_cancel_reason_id: Option<String>,
    pub challenge_cancel_reason: Option<String>,
    pub is_liability_on_issuer: Option<String>,
    pub is_exemption_request_in_authentication: Option<String>,
    pub flow: Option<String>,
    pub acquirer_decision: Option<String>,
    pub decision_reason: Option<String>,
    pub platform_type: Option<PlatformType>,
    pub v2supported: Option<String>,
    pub v2_additional_params: Option<V2AdditionalParams>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub enum MethodCompletion {
    #[serde(rename = "Y")]
    Success,
    #[serde(rename = "N")]
    Failure,
    #[serde(rename = "U")]
    #[default]
    Unavailable,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub enum PlatformType {
    #[serde(rename = "01")]
    App,
    #[serde(rename = "02")]
    #[default]
    Browser,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserDetails {
    pub accept_header: String,
    pub ip: Secret<String, IpAddress>,
    pub java_enabled: String,
    pub java_script_enabled: String,
    pub language: String,
    pub color_depth: u8,
    pub screen_height: u32,
    pub screen_width: u32,
    pub time_zone: i32,
    pub user_agent: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct V2AdditionalParams {
    pub challenge_window_size: Option<String>,
    /// Recurring Expiry in format YYYYMMDD. REQUIRED if isRebilling = 0, We recommend setting rebillExpiry to a value of no more than 5 years from the date of the initial transaction processing date.
    pub rebill_expiry: Option<String>,
    /// Recurring Frequency in days
    pub rebill_frequency: Option<String>,
    pub challenge_preference: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DeviceDetails {
    pub ip_address: Secret<String, IpAddress>,
}

impl TransactionType {
    fn get_from_capture_method_and_amount_string(
        capture_method: CaptureMethod,
        amount: &str,
    ) -> Self {
        let amount_value = amount.parse::<f64>();
        if capture_method == CaptureMethod::Manual || amount_value == Ok(0.0) {
            Self::Auth
        } else {
            Self::Sale
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct NuveiRedirectionResponse {
    pub cres: Secret<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NuveiACSResponse {
    #[serde(rename = "threeDSServerTransID")]
    pub three_ds_server_trans_id: Secret<String>,
    #[serde(rename = "acsTransID")]
    pub acs_trans_id: Secret<String>,
    pub message_type: String,
    pub message_version: String,
    pub trans_status: Option<LiabilityShift>,
    pub message_extension: Option<Vec<MessageExtensionAttribute>>,
    pub acs_signed_content: Option<serde_json::Value>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum LiabilityShift {
    #[serde(rename = "Y", alias = "1")]
    Success,
    #[serde(rename = "N", alias = "0")]
    Failed,
}

pub fn encode_payload(
    payload: &[&str],
) -> Result<String, error_stack::Report<errors::ConnectorError>> {
    let data = payload.join("");
    let digest = crypto::Sha256
        .generate_digest(data.as_bytes())
        .change_context(errors::ConnectorError::RequestEncodingFailed)
        .attach_printable("error encoding nuvie payload")?;
    Ok(hex::encode(digest))
}

impl TryFrom<&types::PaymentsAuthorizeSessionTokenRouterData> for NuveiSessionRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &types::PaymentsAuthorizeSessionTokenRouterData,
    ) -> Result<Self, Self::Error> {
        let connector_meta: NuveiAuthType = NuveiAuthType::try_from(&item.connector_auth_type)?;
        let merchant_id = connector_meta.merchant_id;
        let merchant_site_id = connector_meta.merchant_site_id;
        let client_request_id = item.connector_request_reference_id.clone();
        let time_stamp = date_time::DateTime::<date_time::YYYYMMDDHHmmss>::from(date_time::now());
        let merchant_secret = connector_meta.merchant_secret;
        Ok(Self {
            merchant_id: merchant_id.clone(),
            merchant_site_id: merchant_site_id.clone(),
            client_request_id: client_request_id.clone(),
            time_stamp: time_stamp.clone(),
            checksum: Secret::new(encode_payload(&[
                merchant_id.peek(),
                merchant_site_id.peek(),
                &client_request_id,
                &time_stamp.to_string(),
                merchant_secret.peek(),
            ])?),
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, NuveiSessionResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, NuveiSessionResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: enums::AttemptStatus::Pending,
            session_token: Some(item.response.session_token.clone().expose()),
            response: Ok(PaymentsResponseData::SessionTokenResponse {
                session_token: item.response.session_token.expose(),
            }),
            ..item.data
        })
    }
}

#[derive(Debug)]
pub struct NuveiCardDetails {
    card: payment_method_data::Card,
    three_d: Option<ThreeD>,
    card_holder_name: Option<Secret<String>>,
}

// Define new structs with camelCase serialization
#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct GooglePayCamelCase {
    pm_type: Secret<String>,
    description: Secret<String>,
    info: GooglePayInfoCamelCase,
    tokenization_data: GooglePayTokenizationDataCamelCase,
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct GooglePayInfoCamelCase {
    card_network: Secret<String>,
    card_details: Secret<String>,
    assurance_details: Option<GooglePayAssuranceDetailsCamelCase>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalSchemeDetails {
    transaction_id: Secret<String>, // This is sensitive information
    brand: Option<CardNetwork>,
}
#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct GooglePayAssuranceDetailsCamelCase {
    card_holder_authenticated: bool,
    account_verified: bool,
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct GooglePayTokenizationDataCamelCase {
    #[serde(rename = "type")]
    token_type: Secret<String>,
    token: Secret<String>,
}

// Define ApplePay structs with camelCase serialization
#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct ApplePayCamelCase {
    payment_data: Secret<String>,
    payment_method: ApplePayPaymentMethodCamelCase,
    transaction_identifier: Secret<String>,
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct ApplePayPaymentMethodCamelCase {
    display_name: Secret<String>,
    network: Secret<String>,
    #[serde(rename = "type")]
    pm_type: Secret<String>,
}
fn get_googlepay_info<F, Req>(
    item: &RouterData<F, Req, PaymentsResponseData>,
    gpay_data: &GooglePayWalletData,
) -> Result<NuveiPaymentsRequest, error_stack::Report<errors::ConnectorError>>
where
    Req: NuveiAuthorizePreprocessingCommon,
{
    let is_rebilling = if item.request.is_customer_initiated_mandate_payment() {
        Some("0".to_string())
    } else {
        None
    };
    match gpay_data.tokenization_data {
        GpayTokenizationData::Decrypted(ref gpay_predecrypt_data) => Ok(NuveiPaymentsRequest {
            is_rebilling,
            payment_option: PaymentOption {
                card: Some(Card {
                    brand: Some(gpay_data.info.card_network.clone()),
                    card_number: Some(
                        gpay_predecrypt_data
                            .application_primary_account_number
                            .clone(),
                    ),
                    last4_digits: Some(Secret::new(
                        gpay_predecrypt_data
                            .application_primary_account_number
                            .clone()
                            .get_last4(),
                    )),
                    expiration_month: Some(gpay_predecrypt_data.card_exp_month.clone()),
                    expiration_year: Some(gpay_predecrypt_data.card_exp_year.clone()),
                    external_token: Some(ExternalToken {
                        external_token_provider: ExternalTokenProvider::GooglePay,
                        mobile_token: None,
                        cryptogram: gpay_predecrypt_data.cryptogram.clone(),
                        eci_provider: gpay_predecrypt_data.eci_indicator.clone(),
                    }),
                    ..Default::default()
                }),
                ..Default::default()
            },
            ..Default::default()
        }),
        GpayTokenizationData::Encrypted(ref encrypted_data) => Ok(NuveiPaymentsRequest {
            is_rebilling,
            payment_option: PaymentOption {
                card: Some(Card {
                    external_token: Some(ExternalToken {
                        external_token_provider: ExternalTokenProvider::GooglePay,

                        mobile_token: {
                            let (token_type, token) = (
                                encrypted_data.token_type.clone(),
                                encrypted_data.token.clone(),
                            );

                            let google_pay: GooglePayCamelCase = GooglePayCamelCase {
                                pm_type: Secret::new(gpay_data.pm_type.clone()),
                                description: Secret::new(gpay_data.description.clone()),
                                info: GooglePayInfoCamelCase {
                                    card_network: Secret::new(gpay_data.info.card_network.clone()),
                                    card_details: Secret::new(gpay_data.info.card_details.clone()),
                                    assurance_details: gpay_data
                                        .info
                                        .assurance_details
                                        .as_ref()
                                        .map(|details| GooglePayAssuranceDetailsCamelCase {
                                            card_holder_authenticated: details
                                                .card_holder_authenticated,
                                            account_verified: details.account_verified,
                                        }),
                                },
                                tokenization_data: GooglePayTokenizationDataCamelCase {
                                    token_type: token_type.into(),
                                    token: token.into(),
                                },
                            };
                            Some(Secret::new(
                                google_pay.encode_to_string_of_json().change_context(
                                    errors::ConnectorError::RequestEncodingFailed,
                                )?,
                            ))
                        },
                        cryptogram: None,
                        eci_provider: None,
                    }),
                    ..Default::default()
                }),
                ..Default::default()
            },
            ..Default::default()
        }),
    }
}

fn get_applepay_info<F, Req>(
    item: &RouterData<F, Req, PaymentsResponseData>,
    apple_pay_data: &ApplePayWalletData,
) -> Result<NuveiPaymentsRequest, error_stack::Report<errors::ConnectorError>>
where
    Req: NuveiAuthorizePreprocessingCommon,
{
    let is_rebilling = if item.request.is_customer_initiated_mandate_payment() {
        Some("0".to_string())
    } else {
        None
    };
    match apple_pay_data.payment_data {
        ApplePayPaymentData::Decrypted(ref apple_pay_predecrypt_data) => Ok(NuveiPaymentsRequest {
           is_rebilling,
            payment_option: PaymentOption {
                card: Some(Card {
                    brand: Some(apple_pay_data.payment_method.network.clone()),
                    card_number: Some(
                        apple_pay_predecrypt_data
                            .application_primary_account_number
                            .clone(),
                    ),
                    last4_digits: Some(Secret::new(
                        apple_pay_predecrypt_data
                            .application_primary_account_number
                            .get_last4(),
                    )),
                    expiration_month: Some(
                        apple_pay_predecrypt_data
                            .application_expiration_month
                            .clone(),
                    ),
                    expiration_year: Some(
                        apple_pay_predecrypt_data
                            .application_expiration_year
                            .clone(),
                    ),
                    external_token: Some(ExternalToken {


/// Panics if `json_payload` array does not contain one `GetPaymentMetricRequest` element.
    pub async fn get_org_payment_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetPaymentMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetPaymentMetricRequest");
        let flow = AnalyticsFlow::GetPaymentMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let org_id = auth.merchant_account.get_org_id();
                let auth: AuthInfo = AuthInfo::OrgLevel {
                    org_id: org_id.clone(),
                };

                let validator_response = request_validator(
                    AnalyticsRequest {
                        payment_attempt: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub struct ApiEventMetricsBucketValue {
    pub latency: Option<u64>,
    pub api_count: Option<u64>,
    pub status_code_count: Option<u64>,
}


pub fn is_operation_complete_authorize<Op: Debug>(operation: &Op) -> bool {
    matches!(format!("{operation:?}").as_str(), "CompleteAuthorize")
}