File: crates/hyperswitch_domain_models/src/router_request_types.rs
 Public functions: 2
 Public structs: 50

pub mod authentication;
pub mod fraud_check;
pub mod revenue_recovery;
pub mod unified_authentication_service;
use api_models::payments::{AdditionalPaymentData, RequestSurchargeDetails};
use common_types::payments as common_payments_types;
use common_utils::{consts, errors, ext_traits::OptionExt, id_type, pii, types::MinorUnit};
use diesel_models::{enums as storage_enums, types::OrderDetailsWithAmount};
use error_stack::ResultExt;
use masking::Secret;
use serde::Serialize;
use serde_with::serde_as;

use super::payment_method_data::PaymentMethodData;
use crate::{
    address,
    errors::api_error_response::{ApiErrorResponse, NotImplementedMessage},
    mandates,
    payment_method_data::ExternalVaultPaymentMethodData,
    payments,
    router_data::{self, AccessTokenAuthenticationResponse, RouterData},
    router_flow_types as flows, router_response_types as response_types,
    vault::PaymentMethodVaultingData,
};
#[derive(Debug, Clone)]
pub struct PaymentsAuthorizeData {
    pub payment_method_data: PaymentMethodData,
    /// total amount (original_amount + surcharge_amount + tax_on_surcharge_amount)
    /// If connector supports separate field for surcharge amount, consider using below functions defined on `PaymentsAuthorizeData` to fetch original amount and surcharge amount separately
    /// ```text
    /// get_original_amount()
    /// get_surcharge_amount()
    /// get_tax_on_surcharge_amount()
    /// get_total_surcharge_amount() // returns surcharge_amount + tax_on_surcharge_amount
    /// ```
    pub amount: i64,
    pub order_tax_amount: Option<MinorUnit>,
    pub email: Option<pii::Email>,
    pub customer_name: Option<Secret<String>>,
    pub currency: storage_enums::Currency,
    pub confirm: bool,
    pub statement_descriptor_suffix: Option<String>,
    pub statement_descriptor: Option<String>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    pub router_return_url: Option<String>,
    pub webhook_url: Option<String>,
    pub complete_authorize_url: Option<String>,
    // Mandates
    pub setup_future_usage: Option<storage_enums::FutureUsage>,
    pub mandate_id: Option<api_models::payments::MandateIds>,
    pub off_session: Option<bool>,
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
    pub setup_mandate_details: Option<mandates::MandateData>,
    pub browser_info: Option<BrowserInformation>,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub order_category: Option<String>,
    pub session_token: Option<String>,
    pub enrolled_for_3ds: bool,
    pub related_transaction_id: Option<String>,
    pub payment_experience: Option<storage_enums::PaymentExperience>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub surcharge_details: Option<SurchargeDetails>,
    pub customer_id: Option<id_type::CustomerId>,
    pub request_incremental_authorization: bool,
    pub metadata: Option<serde_json::Value>,
    pub authentication_data: Option<AuthenticationData>,
    pub request_extended_authorization:
        Option<common_types::primitive_wrappers::RequestExtendedAuthorizationBool>,
    pub split_payments: Option<common_types::payments::SplitPaymentsRequest>,

    // New amount for amount frame work
    pub minor_amount: MinorUnit,

    /// Merchant's identifier for the payment/invoice. This will be sent to the connector
    /// if the connector provides support to accept multiple reference ids.
    /// In case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.
    pub merchant_order_reference_id: Option<String>,
    pub integrity_object: Option<AuthoriseIntegrityObject>,
    pub shipping_cost: Option<MinorUnit>,
    pub additional_payment_method_data: Option<AdditionalPaymentData>,
    pub merchant_account_id: Option<Secret<String>>,
    pub merchant_config_currency: Option<storage_enums::Currency>,
    pub connector_testing_data: Option<pii::SecretSerdeValue>,
    pub order_id: Option<String>,
    pub locale: Option<String>,
    pub payment_channel: Option<common_enums::PaymentChannel>,
    pub enable_partial_authorization: Option<bool>,
}

#[derive(Debug, Clone)]
pub struct ExternalVaultProxyPaymentsData {
    pub payment_method_data: ExternalVaultPaymentMethodData,
    /// total amount (original_amount + surcharge_amount + tax_on_surcharge_amount)
    /// If connector supports separate field for surcharge amount, consider using below functions defined on `PaymentsAuthorizeData` to fetch original amount and surcharge amount separately
    /// ```text
    /// get_original_amount()
    /// get_surcharge_amount()
    /// get_tax_on_surcharge_amount()
    /// get_total_surcharge_amount() // returns surcharge_amount + tax_on_surcharge_amount
    /// ```
    pub amount: i64,
    pub order_tax_amount: Option<MinorUnit>,
    pub email: Option<pii::Email>,
    pub customer_name: Option<Secret<String>>,
    pub currency: storage_enums::Currency,
    pub confirm: bool,
    pub statement_descriptor_suffix: Option<String>,
    pub statement_descriptor: Option<String>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    pub router_return_url: Option<String>,
    pub webhook_url: Option<String>,
    pub complete_authorize_url: Option<String>,
    // Mandates
    pub setup_future_usage: Option<storage_enums::FutureUsage>,
    pub mandate_id: Option<api_models::payments::MandateIds>,
    pub off_session: Option<bool>,
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
    pub setup_mandate_details: Option<mandates::MandateData>,
    pub browser_info: Option<BrowserInformation>,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub order_category: Option<String>,
    pub session_token: Option<String>,
    pub enrolled_for_3ds: bool,
    pub related_transaction_id: Option<String>,
    pub payment_experience: Option<storage_enums::PaymentExperience>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub surcharge_details: Option<SurchargeDetails>,
    pub customer_id: Option<id_type::CustomerId>,
    pub request_incremental_authorization: bool,
    pub metadata: Option<serde_json::Value>,
    pub authentication_data: Option<AuthenticationData>,
    pub request_extended_authorization:
        Option<common_types::primitive_wrappers::RequestExtendedAuthorizationBool>,
    pub split_payments: Option<common_types::payments::SplitPaymentsRequest>,

    // New amount for amount frame work
    pub minor_amount: MinorUnit,
    /// Merchant's identifier for the payment/invoice. This will be sent to the connector
    /// if the connector provides support to accept multiple reference ids.
    /// In case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.
    pub merchant_order_reference_id: Option<String>,
    pub integrity_object: Option<AuthoriseIntegrityObject>,
    pub shipping_cost: Option<MinorUnit>,
    pub additional_payment_method_data: Option<AdditionalPaymentData>,
    pub merchant_account_id: Option<Secret<String>>,
    pub merchant_config_currency: Option<storage_enums::Currency>,
    pub connector_testing_data: Option<pii::SecretSerdeValue>,
    pub order_id: Option<String>,
}

// Note: Integrity traits for ExternalVaultProxyPaymentsData are not implemented
// as they are not mandatory for this flow. The integrity_check field in RouterData
// will use Ok(()) as default, similar to other flows.

// Implement ConnectorCustomerData conversion for ExternalVaultProxy RouterData
impl
    TryFrom<
        &RouterData<
            flows::ExternalVaultProxy,
            ExternalVaultProxyPaymentsData,
            response_types::PaymentsResponseData,
        >,
    > for ConnectorCustomerData
{
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(
        data: &RouterData<
            flows::ExternalVaultProxy,
            ExternalVaultProxyPaymentsData,
            response_types::PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            email: data.request.email.clone(),
            payment_method_data: None, // External vault proxy doesn't use regular payment method data
            description: None,
            phone: None,
            name: data.request.customer_name.clone(),
            preprocessing_id: data.preprocessing_id.clone(),
            split_payments: data.request.split_payments.clone(),
            setup_future_usage: data.request.setup_future_usage,
            customer_acceptance: data.request.customer_acceptance.clone(),
        })
    }
}
#[derive(Debug, Clone)]
pub struct PaymentsPostSessionTokensData {
    // amount here would include amount, surcharge_amount and shipping_cost
    pub amount: MinorUnit,
    /// original amount sent by the merchant
    pub order_amount: MinorUnit,
    pub currency: storage_enums::Currency,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    /// Merchant's identifier for the payment/invoice. This will be sent to the connector
    /// if the connector provides support to accept multiple reference ids.
    /// In case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.
    pub merchant_order_reference_id: Option<String>,
    pub shipping_cost: Option<MinorUnit>,
    pub setup_future_usage: Option<storage_enums::FutureUsage>,
    pub router_return_url: Option<String>,
}

#[derive(Debug, Clone)]
pub struct PaymentsUpdateMetadataData {
    pub metadata: pii::SecretSerdeValue,
    pub connector_transaction_id: String,
}

#[derive(Debug, Clone, PartialEq)]
pub struct AuthoriseIntegrityObject {
    /// Authorise amount
    pub amount: MinorUnit,
    /// Authorise currency
    pub currency: storage_enums::Currency,
}

#[derive(Debug, Clone, PartialEq)]
pub struct SyncIntegrityObject {
    /// Sync amount
    pub amount: Option<MinorUnit>,
    /// Sync currency
    pub currency: Option<storage_enums::Currency>,
}

#[derive(Debug, Clone, Default)]
pub struct PaymentsCaptureData {
    pub amount_to_capture: i64,
    pub currency: storage_enums::Currency,
    pub connector_transaction_id: String,
    pub payment_amount: i64,
    pub multiple_capture_data: Option<MultipleCaptureRequestData>,
    pub connector_meta: Option<serde_json::Value>,
    pub browser_info: Option<BrowserInformation>,
    pub metadata: Option<serde_json::Value>,
    // This metadata is used to store the metadata shared during the payment intent request.
    pub capture_method: Option<storage_enums::CaptureMethod>,
    pub split_payments: Option<common_types::payments::SplitPaymentsRequest>,
    // New amount for amount frame work
    pub minor_payment_amount: MinorUnit,
    pub minor_amount_to_capture: MinorUnit,
    pub integrity_object: Option<CaptureIntegrityObject>,
    pub webhook_url: Option<String>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct CaptureIntegrityObject {
    /// capture amount
    pub capture_amount: Option<MinorUnit>,
    /// capture currency
    pub currency: storage_enums::Currency,
}

#[derive(Debug, Clone, Default)]
pub struct PaymentsIncrementalAuthorizationData {
    pub total_amount: i64,
    pub additional_amount: i64,
    pub currency: storage_enums::Currency,
    pub reason: Option<String>,
    pub connector_transaction_id: String,
    pub connector_meta: Option<serde_json::Value>,
}

#[derive(Debug, Clone, Default)]
pub struct MultipleCaptureRequestData {
    pub capture_sequence: i16,
    pub capture_reference: String,
}

#[derive(Debug, Clone)]
pub struct AuthorizeSessionTokenData {
    pub amount_to_capture: Option<i64>,
    pub currency: storage_enums::Currency,
    pub connector_transaction_id: String,
    pub amount: Option<i64>,
}

#[derive(Debug, Clone)]
pub struct ConnectorCustomerData {
    pub description: Option<String>,
    pub email: Option<pii::Email>,
    pub phone: Option<Secret<String>>,
    pub name: Option<Secret<String>>,
    pub preprocessing_id: Option<String>,
    pub payment_method_data: Option<PaymentMethodData>,
    pub split_payments: Option<common_types::payments::SplitPaymentsRequest>,
    // Mandates
    pub setup_future_usage: Option<storage_enums::FutureUsage>,
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
}

impl TryFrom<SetupMandateRequestData> for ConnectorCustomerData {
    type Error = error_stack::Report<ApiErrorResponse>;
    fn try_from(data: SetupMandateRequestData) -> Result<Self, Self::Error> {
        Ok(Self {
            email: data.email,
            payment_method_data: Some(data.payment_method_data),
            description: None,
            phone: None,
            name: None,
            preprocessing_id: None,
            split_payments: None,
            setup_future_usage: data.setup_future_usage,
            customer_acceptance: data.customer_acceptance,
        })
    }
}

impl TryFrom<SetupMandateRequestData> for PaymentsPreProcessingData {
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(data: SetupMandateRequestData) -> Result<Self, Self::Error> {
        Ok(Self {
            payment_method_data: Some(data.payment_method_data),
            amount: data.amount,
            minor_amount: data.minor_amount,
            email: data.email,
            currency: Some(data.currency),
            payment_method_type: data.payment_method_type,
            setup_mandate_details: data.setup_mandate_details,
            capture_method: data.capture_method,
            order_details: None,
            router_return_url: data.router_return_url,
            webhook_url: data.webhook_url,
            complete_authorize_url: data.complete_authorize_url,
            browser_info: data.browser_info,
            surcharge_details: None,
            connector_transaction_id: None,
            mandate_id: data.mandate_id,
            related_transaction_id: None,
            redirect_response: None,
            enrolled_for_3ds: false,
            split_payments: None,
            metadata: data.metadata,
            customer_acceptance: data.customer_acceptance,
            setup_future_usage: data.setup_future_usage,
        })
    }
}
impl
    TryFrom<
        &RouterData<flows::Authorize, PaymentsAuthorizeData, response_types::PaymentsResponseData>,
    > for ConnectorCustomerData
{
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(
        data: &RouterData<
            flows::Authorize,
            PaymentsAuthorizeData,
            response_types::PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            email: data.request.email.clone(),
            payment_method_data: Some(data.request.payment_method_data.clone()),
            description: None,
            phone: None,
            name: data.request.customer_name.clone(),
            preprocessing_id: data.preprocessing_id.clone(),
            split_payments: data.request.split_payments.clone(),
            setup_future_usage: data.request.setup_future_usage,
            customer_acceptance: data.request.customer_acceptance.clone(),
        })
    }
}

impl TryFrom<&RouterData<flows::Session, PaymentsSessionData, response_types::PaymentsResponseData>>
    for ConnectorCustomerData
{
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(
        data: &RouterData<
            flows::Session,
            PaymentsSessionData,
            response_types::PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            email: data.request.email.clone(),
            payment_method_data: None,
            description: None,
            phone: None,
            name: data.request.customer_name.clone(),
            preprocessing_id: data.preprocessing_id.clone(),
            split_payments: None,
            setup_future_usage: None,
            customer_acceptance: None,
        })
    }
}

#[derive(Debug, Clone)]
pub struct PaymentMethodTokenizationData {
    pub payment_method_data: PaymentMethodData,
    pub browser_info: Option<BrowserInformation>,
    pub currency: storage_enums::Currency,
    pub amount: Option<i64>,
    pub split_payments: Option<common_types::payments::SplitPaymentsRequest>,
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
    pub setup_future_usage: Option<storage_enums::FutureUsage>,
    pub setup_mandate_details: Option<mandates::MandateData>,
    pub mandate_id: Option<api_models::payments::MandateIds>,
}

impl TryFrom<SetupMandateRequestData> for PaymentMethodTokenizationData {
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(data: SetupMandateRequestData) -> Result<Self, Self::Error> {
        Ok(Self {
            payment_method_data: data.payment_method_data,
            browser_info: None,
            currency: data.currency,
            amount: data.amount,
            split_payments: None,
            customer_acceptance: data.customer_acceptance,
            setup_future_usage: data.setup_future_usage,
            setup_mandate_details: data.setup_mandate_details,
            mandate_id: data.mandate_id,
        })
    }
}
impl<F> From<&RouterData<F, PaymentsAuthorizeData, response_types::PaymentsResponseData>>
    for PaymentMethodTokenizationData
{
    fn from(
        data: &RouterData<F, PaymentsAuthorizeData, response_types::PaymentsResponseData>,
    ) -> Self {
        Self {
            payment_method_data: data.request.payment_method_data.clone(),
            browser_info: None,
            currency: data.request.currency,
            amount: Some(data.request.amount),
            split_payments: data.request.split_payments.clone(),
            customer_acceptance: data.request.customer_acceptance.clone(),
            setup_future_usage: data.request.setup_future_usage,
            setup_mandate_details: data.request.setup_mandate_details.clone(),
            mandate_id: data.request.mandate_id.clone(),
        }
    }
}

impl TryFrom<PaymentsAuthorizeData> for PaymentMethodTokenizationData {
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(data: PaymentsAuthorizeData) -> Result<Self, Self::Error> {
        Ok(Self {
            payment_method_data: data.payment_method_data,
            browser_info: data.browser_info,
            currency: data.currency,
            amount: Some(data.amount),
            split_payments: data.split_payments.clone(),
            customer_acceptance: data.customer_acceptance,
            setup_future_usage: data.setup_future_usage,
            setup_mandate_details: data.setup_mandate_details,
            mandate_id: data.mandate_id,
        })
    }
}

impl TryFrom<CompleteAuthorizeData> for PaymentMethodTokenizationData {
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(data: CompleteAuthorizeData) -> Result<Self, Self::Error> {
        Ok(Self {
            payment_method_data: data
                .payment_method_data
                .get_required_value("payment_method_data")
                .change_context(ApiErrorResponse::MissingRequiredField {
                    field_name: "payment_method_data",
                })?,
            browser_info: data.browser_info,
            currency: data.currency,
            amount: Some(data.amount),
            split_payments: None,
            customer_acceptance: data.customer_acceptance,
            setup_future_usage: data.setup_future_usage,
            setup_mandate_details: data.setup_mandate_details,
            mandate_id: data.mandate_id,
        })
    }
}

impl TryFrom<ExternalVaultProxyPaymentsData> for PaymentMethodTokenizationData {
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(_data: ExternalVaultProxyPaymentsData) -> Result<Self, Self::Error> {
        // TODO: External vault proxy payments should not use regular payment method tokenization
        // This needs to be implemented separately for external vault flows
        Err(ApiErrorResponse::NotImplemented {
            message: NotImplementedMessage::Reason(
                "External vault proxy tokenization not implemented".to_string(),
            ),
        }
        .into())
    }
}

#[derive(Debug, Clone)]
pub struct CreateOrderRequestData {
    pub minor_amount: MinorUnit,
    pub currency: storage_enums::Currency,
}

impl TryFrom<PaymentsAuthorizeData> for CreateOrderRequestData {
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(data: PaymentsAuthorizeData) -> Result<Self, Self::Error> {
        Ok(Self {
            minor_amount: data.minor_amount,
            currency: data.currency,
        })
    }
}

impl TryFrom<ExternalVaultProxyPaymentsData> for CreateOrderRequestData {
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(data: ExternalVaultProxyPaymentsData) -> Result<Self, Self::Error> {
        Ok(Self {
            minor_amount: data.minor_amount,
            currency: data.currency,
        })
    }
}

#[derive(Debug, Clone)]
pub struct PaymentsPreProcessingData {
    pub payment_method_data: Option<PaymentMethodData>,
    pub amount: Option<i64>,
    pub email: Option<pii::Email>,
    pub currency: Option<storage_enums::Currency>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub setup_mandate_details: Option<mandates::MandateData>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub router_return_url: Option<String>,
    pub webhook_url: Option<String>,
    pub complete_authorize_url: Option<String>,
    pub surcharge_details: Option<SurchargeDetails>,
    pub browser_info: Option<BrowserInformation>,
    pub connector_transaction_id: Option<String>,
    pub enrolled_for_3ds: bool,
    pub mandate_id: Option<api_models::payments::MandateIds>,
    pub related_transaction_id: Option<String>,
    pub redirect_response: Option<CompleteAuthorizeRedirectResponse>,
    pub metadata: Option<Secret<serde_json::Value>>,
    pub split_payments: Option<common_types::payments::SplitPaymentsRequest>,
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
    pub setup_future_usage: Option<storage_enums::FutureUsage>,
    // New amount for amount frame work
    pub minor_amount: Option<MinorUnit>,
}

impl TryFrom<PaymentsAuthorizeData> for PaymentsPreProcessingData {
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(data: PaymentsAuthorizeData) -> Result<Self, Self::Error> {
        Ok(Self {
            payment_method_data: Some(data.payment_method_data),
            amount: Some(data.amount),
            minor_amount: Some(data.minor_amount),
            email: data.email,
            currency: Some(data.currency),
            payment_method_type: data.payment_method_type,
            setup_mandate_details: data.setup_mandate_details,
            capture_method: data.capture_method,
            order_details: data.order_details,
            router_return_url: data.router_return_url,
            webhook_url: data.webhook_url,
            complete_authorize_url: data.complete_authorize_url,
            browser_info: data.browser_info,
            surcharge_details: data.surcharge_details,
            connector_transaction_id: None,
            mandate_id: data.mandate_id,
            related_transaction_id: data.related_transaction_id,
            redirect_response: None,
            enrolled_for_3ds: data.enrolled_for_3ds,
            split_payments: data.split_payments,
            metadata: data.metadata.map(Secret::new),
            customer_acceptance: data.customer_acceptance,
            setup_future_usage: data.setup_future_usage,
        })
    }
}

impl TryFrom<CompleteAuthorizeData> for PaymentsPreProcessingData {
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(data: CompleteAuthorizeData) -> Result<Self, Self::Error> {
        Ok(Self {
            payment_method_data: data.payment_method_data,
            amount: Some(data.amount),
            minor_amount: Some(data.minor_amount),
            email: data.email,
            currency: Some(data.currency),
            payment_method_type: None,
            setup_mandate_details: data.setup_mandate_details,
            capture_method: data.capture_method,
            order_details: None,
            router_return_url: None,
            webhook_url: None,
            complete_authorize_url: data.complete_authorize_url,
            browser_info: data.browser_info,
            surcharge_details: None,
            connector_transaction_id: data.connector_transaction_id,
            mandate_id: data.mandate_id,
            related_transaction_id: None,
            redirect_response: data.redirect_response,
            split_payments: None,
            enrolled_for_3ds: true,
            metadata: data.connector_meta.map(Secret::new),
            customer_acceptance: data.customer_acceptance,
            setup_future_usage: data.setup_future_usage,
        })
    }
}

#[derive(Debug, Clone)]
pub struct PaymentsPostProcessingData {
    pub payment_method_data: PaymentMethodData,
    pub customer_id: Option<id_type::CustomerId>,
    pub connector_transaction_id: Option<String>,
    pub country: Option<common_enums::CountryAlpha2>,
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
    pub header_payload: Option<payments::HeaderPayload>,
}

impl<F> TryFrom<RouterData<F, PaymentsAuthorizeData, response_types::PaymentsResponseData>>
    for PaymentsPostProcessingData
{
    type Error = error_stack::Report<ApiErrorResponse>;

    fn try_from(
        data: RouterData<F, PaymentsAuthorizeData, response_types::PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            payment_method_data: data.request.payment_method_data,
            connector_transaction_id: match data.response {
                Ok(response_types::PaymentsResponseData::TransactionResponse {
                    resource_id: ResponseId::ConnectorTransactionId(id),
                    ..
                }) => Some(id.clone()),
                _ => None,
            },
            customer_id: data.request.customer_id,
            country: data
                .address
                .get_payment_billing()
                .and_then(|bl| bl.address.as_ref())
                .and_then(|address| address.country),
            connector_meta_data: data.connector_meta_data.clone(),
            header_payload: data.header_payload,
        })
    }
}
#[derive(Debug, Clone)]
pub struct CompleteAuthorizeData {
    pub payment_method_data: Option<PaymentMethodData>,
    pub amount: i64,
    pub email: Option<pii::Email>,
    pub currency: storage_enums::Currency,
    pub confirm: bool,
    pub statement_descriptor_suffix: Option<String>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    // Mandates
    pub setup_future_usage: Option<storage_enums::FutureUsage>,
    pub mandate_id: Option<api_models::payments::MandateIds>,
    pub off_session: Option<bool>,
    pub setup_mandate_details: Option<mandates::MandateData>,
    pub redirect_response: Option<CompleteAuthorizeRedirectResponse>,
    pub browser_info: Option<BrowserInformation>,
    pub connector_transaction_id: Option<String>,
    pub connector_meta: Option<serde_json::Value>,
    pub complete_authorize_url: Option<String>,
    pub metadata: Option<serde_json::Value>,
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
    // New amount for amount frame work
    pub minor_amount: MinorUnit,
    pub merchant_account_id: Option<Secret<String>>,
    pub merchant_config_currency: Option<storage_enums::Currency>,
    pub threeds_method_comp_ind: Option<api_models::payments::ThreeDsCompletionIndicator>,
}

#[derive(Debug, Clone)]
pub struct CompleteAuthorizeRedirectResponse {
    pub params: Option<Secret<String>>,
    pub payload: Option<pii::SecretSerdeValue>,
}

#[derive(Debug, Default, Clone)]
pub struct PaymentsSyncData {
    //TODO : add fields based on the connector requirements
    pub connector_transaction_id: ResponseId,
    pub encoded_data: Option<String>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    pub connector_meta: Option<serde_json::Value>,
    pub sync_type: SyncRequestType,
    pub mandate_id: Option<api_models::payments::MandateIds>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub currency: storage_enums::Currency,
    pub payment_experience: Option<common_enums::PaymentExperience>,
    pub split_payments: Option<common_types::payments::SplitPaymentsRequest>,
    pub amount: MinorUnit,
    pub integrity_object: Option<SyncIntegrityObject>,
    pub connector_reference_id: Option<String>,
}

#[derive(Debug, Default, Clone)]
pub enum SyncRequestType {
    MultipleCaptureSync(Vec<String>),
    #[default]
    SinglePaymentSync,
}

#[derive(Debug, Default, Clone)]
pub struct PaymentsCancelData {
    pub amount: Option<i64>,
    pub currency: Option<storage_enums::Currency>,
    pub connector_transaction_id: String,
    pub cancellation_reason: Option<String>,
    pub connector_meta: Option<serde_json::Value>,
    pub browser_info: Option<BrowserInformation>,
    pub metadata: Option<serde_json::Value>,
    // This metadata is used to store the metadata shared during the payment intent request.

    // minor amount data for amount framework
    pub minor_amount: Option<MinorUnit>,
    pub webhook_url: Option<String>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
}

#[derive(Debug, Default, Clone)]
pub struct PaymentsCancelPostCaptureData {
    pub currency: Option<storage_enums::Currency>,
    pub connector_transaction_id: String,
    pub cancellation_reason: Option<String>,
    pub connector_meta: Option<serde_json::Value>,
    // minor amount data for amount framework
    pub minor_amount: Option<MinorUnit>,
}

#[derive(Debug, Default, Clone)]
pub struct PaymentsRejectData {
    pub amount: Option<i64>,
    pub currency: Option<storage_enums::Currency>,
}

#[derive(Debug, Default, Clone)]
pub struct PaymentsApproveData {
    pub amount: Option<i64>,
    pub currency: Option<storage_enums::Currency>,
}

#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct BrowserInformation {
    pub color_depth: Option<u8>,
    pub java_enabled: Option<bool>,
    pub java_script_enabled: Option<bool>,
    pub language: Option<String>,
    pub screen_height: Option<u32>,
    pub screen_width: Option<u32>,
    pub time_zone: Option<i32>,
    pub ip_address: Option<std::net::IpAddr>,
    pub accept_header: Option<String>,
    pub user_agent: Option<String>,
    pub os_type: Option<String>,
    pub os_version: Option<String>,
    pub device_model: Option<String>,
    pub accept_language: Option<String>,
}

#[cfg(feature = "v2")]
impl From<common_utils::types::BrowserInformation> for BrowserInformation {
    fn from(value: common_utils::types::BrowserInformation) -> Self {
        Self {
            color_depth: value.color_depth,
            java_enabled: value.java_enabled,
            java_script_enabled: value.java_script_enabled,
            language: value.language,
            screen_height: value.screen_height,
            screen_width: value.screen_width,
            time_zone: value.time_zone,
            ip_address: value.ip_address,
            accept_header: value.accept_header,
            user_agent: value.user_agent,
            os_type: value.os_type,
            os_version: value.os_version,
            device_model: value.device_model,
            accept_language: value.accept_language,
        }
    }
}

#[derive(Debug, Clone, Default, Serialize)]
pub enum ResponseId {
    ConnectorTransactionId(String),
    EncodedData(String),
    #[default]
    NoResponseId,
}
impl ResponseId {
    pub fn get_connector_transaction_id(
        &self,
    ) -> errors::CustomResult<String, errors::ValidationError> {
        match self {
            Self::ConnectorTransactionId(txn_id) => Ok(txn_id.to_string()),
            _ => Err(errors::ValidationError::IncorrectValueProvided {
                field_name: "connector_transaction_id",
            })
            .attach_printable("Expected connector transaction ID not found"),
        }
    }
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct SurchargeDetails {
    /// original_amount
    pub original_amount: MinorUnit,
    /// surcharge value
    pub surcharge: common_utils::types::Surcharge,
    /// tax on surcharge value
    pub tax_on_surcharge:
        Option<common_utils::types::Percentage<{ consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH }>>,
    /// surcharge amount for this payment
    pub surcharge_amount: MinorUnit,
    /// tax on surcharge amount for this payment
    pub tax_on_surcharge_amount: MinorUnit,
}

impl SurchargeDetails {
    pub fn get_total_surcharge_amount(&self) -> MinorUnit {
        self.surcharge_amount + self.tax_on_surcharge_amount
    }
}

#[cfg(feature = "v1")]
impl
    From<(
        &RequestSurchargeDetails,
        &payments::payment_attempt::PaymentAttempt,
    )> for SurchargeDetails
{
    fn from(
        (request_surcharge_details, payment_attempt): (
            &RequestSurchargeDetails,
            &payments::payment_attempt::PaymentAttempt,
        ),
    ) -> Self {
        let surcharge_amount = request_surcharge_details.surcharge_amount;
        let tax_on_surcharge_amount = request_surcharge_details.tax_amount.unwrap_or_default();
        Self {
            original_amount: payment_attempt.net_amount.get_order_amount(),
            surcharge: common_utils::types::Surcharge::Fixed(
                request_surcharge_details.surcharge_amount,
            ),
            tax_on_surcharge: None,
            surcharge_amount,
            tax_on_surcharge_amount,
        }
    }
}

#[cfg(feature = "v2")]
impl
    From<(
        &RequestSurchargeDetails,
        &payments::payment_attempt::PaymentAttempt,
    )> for SurchargeDetails
{
    fn from(
        (_request_surcharge_details, _payment_attempt): (
            &RequestSurchargeDetails,
            &payments::payment_attempt::PaymentAttempt,
        ),
    ) -> Self {
        todo!()
    }
}

#[derive(Debug, Clone)]
pub struct AuthenticationData {
    pub eci: Option<String>,
    pub cavv: Secret<String>,
    pub threeds_server_transaction_id: Option<String>,
    pub message_version: Option<common_utils::types::SemanticVersion>,
    pub ds_trans_id: Option<String>,
    pub created_at: time::PrimitiveDateTime,
    pub challenge_code: Option<String>,
    pub challenge_cancel: Option<String>,
    pub challenge_code_reason: Option<String>,
    pub message_extension: Option<pii::SecretSerdeValue>,
    pub acs_trans_id: Option<String>,
    pub authentication_type: Option<common_enums::DecoupledAuthenticationType>,
}

#[derive(Debug, Clone)]
pub struct RefundsData {
    pub refund_id: String,
    pub connector_transaction_id: String,

    pub connector_refund_id: Option<String>,
    pub currency: storage_enums::Currency,
    /// Amount for the payment against which this refund is issued
    pub payment_amount: i64,

    pub reason: Option<String>,
    pub webhook_url: Option<String>,
    /// Amount to be refunded
    pub refund_amount: i64,
    /// Arbitrary metadata required for refund
    pub connector_metadata: Option<serde_json::Value>,
    /// refund method
    pub refund_connector_metadata: Option<pii::SecretSerdeValue>,
    pub browser_info: Option<BrowserInformation>,
    /// Charges associated with the payment
    pub split_refunds: Option<SplitRefundsRequest>,

    // New amount for amount frame work
    pub minor_payment_amount: MinorUnit,
    pub minor_refund_amount: MinorUnit,
    pub integrity_object: Option<RefundIntegrityObject>,
    pub refund_status: storage_enums::RefundStatus,
    pub merchant_account_id: Option<Secret<String>>,
    pub merchant_config_currency: Option<storage_enums::Currency>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    pub additional_payment_method_data: Option<AdditionalPaymentData>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct RefundIntegrityObject {
    /// refund currency
    pub currency: storage_enums::Currency,
    /// refund amount
    pub refund_amount: MinorUnit,
}

#[derive(Debug, serde::Deserialize, Clone)]
pub enum SplitRefundsRequest {
    StripeSplitRefund(StripeSplitRefund),
    AdyenSplitRefund(common_types::domain::AdyenSplitData),
    XenditSplitRefund(common_types::domain::XenditSplitSubMerchantData),
}

#[derive(Debug, serde::Deserialize, Clone)]
pub struct StripeSplitRefund {
    pub charge_id: String,
    pub transfer_account_id: String,
    pub charge_type: api_models::enums::PaymentChargeType,
    pub options: ChargeRefundsOptions,
}

#[derive(Debug, serde::Deserialize, Clone)]
pub struct ChargeRefunds {
    pub charge_id: String,
    pub transfer_account_id: String,
    pub charge_type: api_models::enums::PaymentChargeType,
    pub options: ChargeRefundsOptions,
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum ChargeRefundsOptions {
    Destination(DestinationChargeRefund),
    Direct(DirectChargeRefund),
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DirectChargeRefund {
    pub revert_platform_fee: bool,
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DestinationChargeRefund {
    pub revert_platform_fee: bool,
    pub revert_transfer: bool,
}

#[derive(Debug, Clone)]
pub struct AccessTokenAuthenticationRequestData {
    pub auth_creds: router_data::ConnectorAuthType,
}

impl TryFrom<router_data::ConnectorAuthType> for AccessTokenAuthenticationRequestData {
    type Error = ApiErrorResponse;
    fn try_from(connector_auth: router_data::ConnectorAuthType) -> Result<Self, Self::Error> {
        Ok(Self {
            auth_creds: connector_auth,
        })
    }
}

#[derive(Debug, Clone)]
pub struct AccessTokenRequestData {
    pub app_id: Secret<String>,
    pub id: Option<Secret<String>>,


impl PaymentSync for Netcetera {}


impl api::PaymentPostCaptureVoid for Worldpayvantiv {}

impl api::PaymentPostCaptureVoid for for Worldpayvantiv


File: crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs
 Public functions: 1
 Public structs: 51

use api_models::payments::{
    ApplePayCombinedMetadata, ApplepayCombinedSessionTokenData, ApplepaySessionTokenData,
    ApplepaySessionTokenMetadata,
};
use base64::Engine;
use common_enums::{enums, Currency};
use common_utils::{
    consts::BASE64_ENGINE, ext_traits::ValueExt, pii, request::Method, types::FloatMajorUnit,
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{PaymentMethodData, WalletData},
    router_data::{ConnectorAuthType, PaymentMethodToken, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types,
};
use hyperswitch_interfaces::errors;
use masking::{ExposeInterface, Secret};
use serde::{ser::Serializer, Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{
        self, CardData as _, PaymentsCancelRequestData, PaymentsSyncRequestData, RouterData as _,
    },
};

#[derive(Debug, Serialize)]
pub struct FiservRouterData<T> {
    pub amount: FloatMajorUnit,
    pub router_data: T,
}

impl<T> TryFrom<(FloatMajorUnit, T)> for FiservRouterData<T> {
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from((amount, router_data): (FloatMajorUnit, T)) -> Result<Self, Self::Error> {
        Ok(Self {
            amount,
            router_data,
        })
    }
}

impl Serialize for FiservCheckoutChargesRequest {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            Self::Checkout(inner) => inner.serialize(serializer),
            Self::Charges(inner) => inner.serialize(serializer),
        }
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservPaymentsRequest {
    amount: Amount,
    merchant_details: MerchantDetails,
    #[serde(flatten)]
    checkout_charges_request: FiservCheckoutChargesRequest,
}

#[derive(Debug)]
pub enum FiservCheckoutChargesRequest {
    Checkout(CheckoutPaymentsRequest),
    Charges(ChargesPaymentRequest),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckoutPaymentsRequest {
    order: FiservOrder,
    payment_method: FiservPaymentMethod,
    interactions: FiservInteractions,
    transaction_details: TransactionDetails,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum FiservChannel {
    Web,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum FiservPaymentInitiator {
    Merchant,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum FiservCustomerConfirmation {
    ReviewAndPay,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservInteractions {
    channel: FiservChannel,
    customer_confirmation: FiservCustomerConfirmation,
    payment_initiator: FiservPaymentInitiator,
    return_urls: FiservReturnUrls,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservReturnUrls {
    success_url: String,
    cancel_url: String,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservPaymentMethod {
    provider: FiservWallet,
    #[serde(rename = "type")]
    wallet_type: FiservWalletType,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservOrder {
    intent: FiservIntent,
}

#[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum FiservIntent {
    Authorize,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ChargesPaymentRequest {
    source: Source,
    transaction_interaction: Option<TransactionInteraction>,
    transaction_details: TransactionDetails,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum FiservWallet {
    ApplePay,
    GooglePay,
    PayPal,
}

#[derive(Debug, Serialize)]
#[serde(tag = "sourceType")]
pub enum Source {
    #[serde(rename = "GooglePay")]
    GooglePay(GooglePayData),
    #[serde(rename = "PaymentCard")]
    PaymentCard { card: CardData },
    #[serde(rename = "ApplePay")]
    ApplePay(ApplePayWalletDetails),
    #[serde(rename = "DecryptedWallet")]
    DecryptedWallet(DecryptedWalletDetails),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplePayWalletDetails {
    pub data: Secret<String>,
    pub header: ApplePayHeader,
    pub signature: Secret<String>,
    pub version: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub application_data: Option<Secret<String>>,
    pub apple_pay_merchant_id: Secret<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplePayHeader {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub application_data_hash: Option<Secret<String>>,
    pub ephemeral_public_key: Secret<String>,
    pub public_key_hash: Secret<String>,
    pub transaction_id: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DecryptedWalletDetails {
    pub card: WalletCardData,
    #[serde(rename = "cavv")]
    pub cryptogram: Secret<String>,
    #[serde(rename = "xid")]
    pub transaction_id: Secret<String>,
    pub wallet_type: FiservWalletType,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum FiservWalletType {
    ApplePay,
    GooglePay,
    PaypalWallet,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayData {
    data: Secret<String>,
    signature: Secret<String>,
    version: String,
    intermediate_signing_key: IntermediateSigningKey,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CardData {
    card_data: cards::CardNumber,
    expiration_month: Secret<String>,
    expiration_year: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    security_code: Option<Secret<String>>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WalletCardData {
    card_data: cards::CardNumber,
    expiration_month: Secret<String>,
    expiration_year: Secret<String>,
}

#[derive(Default, Debug, Serialize)]
pub struct Amount {
    total: FloatMajorUnit,
    currency: String,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionDetails {
    #[serde(skip_serializing_if = "Option::is_none")]
    capture_flag: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    reversal_reason_code: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    merchant_transaction_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    operation_type: Option<OperationType>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum OperationType {
    Create,
    Capture,
    Authorize,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MerchantDetails {
    merchant_id: Secret<String>,
    terminal_id: Option<Secret<String>>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionInteraction {
    origin: TransactionInteractionOrigin,
    eci_indicator: TransactionInteractionEciIndicator,
    pos_condition_code: TransactionInteractionPosConditionCode,
}

#[derive(Default, Debug, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum TransactionInteractionOrigin {
    #[default]
    Ecom,
}
#[derive(Default, Debug, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TransactionInteractionEciIndicator {
    #[default]
    ChannelEncrypted,
}
#[derive(Default, Debug, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TransactionInteractionPosConditionCode {
    #[default]
    CardNotPresentEcom,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct IntermediateSigningKey {
    signed_key: Secret<String>,
    signatures: Vec<Secret<String>>,
}

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

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignedMessage {
    encrypted_message: String,
    ephemeral_public_key: String,
    tag: String,
}

#[derive(Debug, Default)]
pub struct FullyParsedGooglePayToken {
    pub signature: Secret<String>,
    pub protocol_version: String,
    pub encrypted_message: String,
    pub ephemeral_public_key: String,
    pub tag: String,
    pub key_value: String,
    pub key_expiration: String,
    pub signatures: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RawGooglePayToken {
    pub signature: Secret<String>,
    pub protocol_version: String,
    pub signed_message: Secret<String>,
    pub intermediate_signing_key: IntermediateSigningKey,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ApplePayDecryptedData {
    pub data: Secret<String>,
    pub signature: Secret<String>,
    pub version: Secret<String>,
    pub header: ApplePayHeader,
}

pub fn parse_googlepay_token_safely(token_json_str: &str) -> FullyParsedGooglePayToken {
    let mut result = FullyParsedGooglePayToken::default();

    if let Ok(raw_token) = serde_json::from_str::<RawGooglePayToken>(token_json_str) {
        result.signature = raw_token.signature;
        result.protocol_version = raw_token.protocol_version;
        result.signatures = raw_token
            .intermediate_signing_key
            .signatures
            .into_iter()
            .map(|s| s.expose().to_owned())
            .collect();

        if let Ok(key) = serde_json::from_str::<SignedKey>(
            &raw_token.intermediate_signing_key.signed_key.expose(),
        ) {
            result.key_value = key.key_value;
            result.key_expiration = key.key_expiration;
        }

        if let Ok(message) =
            serde_json::from_str::<SignedMessage>(&raw_token.signed_message.expose())
        {
            result.encrypted_message = message.encrypted_message;
            result.ephemeral_public_key = message.ephemeral_public_key;
            result.tag = message.tag;
        }
    }

    result
}

impl TryFrom<&FiservRouterData<&types::PaymentsAuthorizeRouterData>> for FiservPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        item: &FiservRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let auth: FiservAuthType = FiservAuthType::try_from(&item.router_data.connector_auth_type)?;
        let amount = Amount {
            total: item.amount,
            currency: item.router_data.request.currency.to_string(),
        };
        let metadata = item.router_data.get_connector_meta()?.clone();
        let session: FiservSessionObject = metadata
            .expose()
            .parse_value("FiservSessionObject")
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "Merchant connector account metadata",
            })?;

        let merchant_details = MerchantDetails {
            merchant_id: auth.merchant_account,
            terminal_id: Some(session.terminal_id),
        };

        let checkout_charges_request = match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(ref ccard) => {
                Ok(FiservCheckoutChargesRequest::Charges(
                    ChargesPaymentRequest {
                        source: Source::PaymentCard {
                            card: CardData {
                                card_data: ccard.card_number.clone(),
                                expiration_month: ccard.card_exp_month.clone(),
                                expiration_year: ccard.get_expiry_year_4_digit(),
                                security_code: Some(ccard.card_cvc.clone()),
                            },
                        },
                        transaction_details: TransactionDetails {
                            capture_flag: Some(matches!(
                                item.router_data.request.capture_method,
                                Some(enums::CaptureMethod::Automatic)
                                    | Some(enums::CaptureMethod::SequentialAutomatic)
                                    | None
                            )),
                            reversal_reason_code: None,
                            merchant_transaction_id: Some(
                                item.router_data.connector_request_reference_id.clone(),
                            ),
                            operation_type: None,
                        },
                        transaction_interaction: Some(TransactionInteraction {
                            //Payment is being made in online mode, card not present
                            origin: TransactionInteractionOrigin::Ecom,
                            // transaction encryption such as SSL/TLS, but authentication was not performed
                            eci_indicator: TransactionInteractionEciIndicator::ChannelEncrypted,
                            //card not present in online transaction
                            pos_condition_code:
                                TransactionInteractionPosConditionCode::CardNotPresentEcom,
                        }),
                    },
                ))
            }
            PaymentMethodData::Wallet(wallet_data) => match wallet_data {
                WalletData::GooglePay(data) => {
                    let token_string = data
                        .tokenization_data
                        .get_encrypted_google_pay_token()
                        .change_context(errors::ConnectorError::MissingRequiredField {
                            field_name: "gpay wallet_token",
                        })?
                        .to_owned();

                    let parsed = parse_googlepay_token_safely(&token_string);

                    Ok(FiservCheckoutChargesRequest::Charges(
                        ChargesPaymentRequest {
                            source: Source::GooglePay(GooglePayData {
                                data: Secret::new(parsed.encrypted_message),
                                signature: Secret::new(parsed.signature.expose().to_owned()),
                                version: parsed.protocol_version,
                                intermediate_signing_key: IntermediateSigningKey {
                                    signed_key: Secret::new(
                                        serde_json::json!({
                                            "keyValue": parsed.key_value,
                                            "keyExpiration": parsed.key_expiration
                                        })
                                        .to_string(),
                                    ),
                                    signatures: parsed
                                        .signatures
                                        .into_iter()
                                        .map(|s| Secret::new(s.to_owned()))
                                        .collect(),
                                },
                            }),
                            transaction_details: TransactionDetails {
                                capture_flag: Some(matches!(
                                    item.router_data.request.capture_method,
                                    Some(enums::CaptureMethod::Automatic)
                                        | Some(enums::CaptureMethod::SequentialAutomatic)
                                        | None
                                )),
                                reversal_reason_code: None,
                                merchant_transaction_id: Some(
                                    item.router_data.connector_request_reference_id.clone(),
                                ),
                                operation_type: None,
                            },
                            transaction_interaction: None,
                        },
                    ))
                }
                WalletData::PaypalRedirect(_) => {
                    let return_url = item
                        .router_data
                        .request
                        .complete_authorize_url
                        .clone()
                        .ok_or(errors::ConnectorError::MissingRequiredField {
                            field_name: "return_url",
                        })?;
                    Ok(FiservCheckoutChargesRequest::Checkout(
                        CheckoutPaymentsRequest {
                            payment_method: FiservPaymentMethod {
                                provider: FiservWallet::PayPal,
                                wallet_type: FiservWalletType::PaypalWallet,
                            },
                            order: FiservOrder {
                                intent: FiservIntent::Authorize,
                            },
                            interactions: FiservInteractions {
                                channel: FiservChannel::Web,
                                customer_confirmation: FiservCustomerConfirmation::ReviewAndPay,
                                payment_initiator: FiservPaymentInitiator::Merchant,
                                return_urls: FiservReturnUrls {
                                    success_url: return_url.clone(),
                                    cancel_url: return_url,
                                },
                            },
                            transaction_details: TransactionDetails {
                                operation_type: Some(OperationType::Create),
                                capture_flag: Some(matches!(
                                    item.router_data.request.capture_method,
                                    Some(enums::CaptureMethod::Automatic)
                                        | Some(enums::CaptureMethod::SequentialAutomatic)
                                        | None
                                )),
                                reversal_reason_code: None,
                                merchant_transaction_id: Some(
                                    item.router_data.connector_request_reference_id.clone(),
                                ),
                            },
                        },
                    ))
                }
                WalletData::ApplePay(apple_pay_data) => match item
                    .router_data
                    .payment_method_token
                    .clone()
                {
                    Some(PaymentMethodToken::ApplePayDecrypt(pre_decrypt_data)) => Ok(
                        FiservCheckoutChargesRequest::Charges(ChargesPaymentRequest {
                            source: Source::DecryptedWallet(DecryptedWalletDetails {
                                wallet_type: FiservWalletType::ApplePay,
                                cryptogram: pre_decrypt_data
                                    .payment_data
                                    .online_payment_cryptogram
                                    .clone(),
                                transaction_id: Secret::new(apple_pay_data.transaction_identifier),
                                card: WalletCardData {
                                    card_data: pre_decrypt_data
                                        .application_primary_account_number
                                        .clone(),
                                    expiration_month: pre_decrypt_data
                                        .get_expiry_month()
                                        .change_context(
                                            errors::ConnectorError::MissingRequiredField {
                                                field_name: "apple_pay_expiry_month",
                                            },
                                        )?,
                                    expiration_year: pre_decrypt_data.get_four_digit_expiry_year(),
                                },
                            }),
                            transaction_details: TransactionDetails {
                                capture_flag: Some(matches!(
                                    item.router_data.request.capture_method,
                                    Some(enums::CaptureMethod::Automatic)
                                        | Some(enums::CaptureMethod::SequentialAutomatic)
                                        | None
                                )),
                                reversal_reason_code: None,
                                merchant_transaction_id: Some(
                                    item.router_data.connector_request_reference_id.clone(),
                                ),
                                operation_type: None,
                            },
                            transaction_interaction: None,
                        }),
                    ),
                    _ => {
                        let decoded_bytes = match apple_pay_data.payment_data {
                            common_types::payments::ApplePayPaymentData::Encrypted(
                                ref encrypted_str,
                            ) => BASE64_ENGINE
                                .decode(encrypted_str)
                                .change_context(errors::ConnectorError::ParsingFailed)?,
                            _ => {
                                return Err(errors::ConnectorError::ParsingFailed.into());
                            }
                        };

                        let payment_data_decoded: ApplePayDecryptedData =
                            serde_json::from_slice(&decoded_bytes)
                                .change_context(errors::ConnectorError::ParsingFailed)?;

                        let data = payment_data_decoded.data;
                        let signature = payment_data_decoded.signature;
                        let version = payment_data_decoded.version;

                        let header = ApplePayHeader {
                            ephemeral_public_key: payment_data_decoded.header.ephemeral_public_key,
                            public_key_hash: payment_data_decoded.header.public_key_hash,
                            transaction_id: payment_data_decoded.header.transaction_id,
                            application_data_hash: None,
                        };

                        let apple_pay_metadata = item.router_data.get_connector_meta()?.expose();
                        let applepay_metadata = apple_pay_metadata
                            .clone()
                            .parse_value::<ApplepayCombinedSessionTokenData>(
                                "ApplepayCombinedSessionTokenData",
                            )
                            .map(|combined_metadata| {
                                ApplepaySessionTokenMetadata::ApplePayCombined(
                                    combined_metadata.apple_pay_combined,
                                )
                            })
                            .or_else(|_| {
                                apple_pay_metadata
                                    .parse_value::<ApplepaySessionTokenData>(
                                        "ApplepaySessionTokenData",
                                    )
                                    .map(|old_metadata| {
                                        ApplepaySessionTokenMetadata::ApplePay(
                                            old_metadata.apple_pay,
                                        )
                                    })
                            })
                            .change_context(errors::ConnectorError::ParsingFailed)?;

                        let merchant_identifier = match applepay_metadata {
                            ApplepaySessionTokenMetadata::ApplePayCombined(ref combined) => {
                                match combined {
                                    ApplePayCombinedMetadata::Simplified { .. } => {
                                        return Err(
                                            errors::ConnectorError::MissingApplePayTokenData.into(),
                                        )
                                    }
                                    ApplePayCombinedMetadata::Manual {
                                        session_token_data, ..
                                    } => &session_token_data.merchant_identifier,
                                }
                            }
                            ApplepaySessionTokenMetadata::ApplePay(ref data) => {
                                &data.session_token_data.merchant_identifier
                            }
                        };

                        Ok(FiservCheckoutChargesRequest::Charges(
                            ChargesPaymentRequest {
                                source: Source::ApplePay(ApplePayWalletDetails {
                                    data,
                                    header,
                                    signature,
                                    version,
                                    application_data: None,
                                    apple_pay_merchant_id: Secret::new(
                                        merchant_identifier.to_owned(),
                                    ),
                                }),
                                transaction_details: TransactionDetails {
                                    capture_flag: Some(matches!(
                                        item.router_data.request.capture_method,
                                        Some(enums::CaptureMethod::Automatic)
                                            | Some(enums::CaptureMethod::SequentialAutomatic)
                                            | None
                                    )),
                                    reversal_reason_code: None,
                                    merchant_transaction_id: Some(
                                        item.router_data.connector_request_reference_id.clone(),
                                    ),
                                    operation_type: None,
                                },
                                transaction_interaction: None,
                            },
                        ))
                    }
                },
                _ => Err(error_stack::report!(
                    errors::ConnectorError::NotImplemented(
                        utils::get_unimplemented_payment_method_error_message("fiserv"),
                    )
                )),
            },
            PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => Err(
                error_stack::report!(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("fiserv"),
                )),
            ),
        }?;

        Ok(Self {
            amount,
            checkout_charges_request,
            merchant_details,
        })
    }
}

pub struct FiservAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) merchant_account: Secret<String>,
    pub(super) api_secret: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for FiservAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        if let ConnectorAuthType::SignatureKey {
            api_key,
            key1,
            api_secret,
        } = auth_type
        {
            Ok(Self {
                api_key: api_key.to_owned(),
                merchant_account: key1.to_owned(),
                api_secret: api_secret.to_owned(),
            })
        } else {
            Err(errors::ConnectorError::FailedToObtainAuthType)?
        }
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservCancelRequest {
    transaction_details: TransactionDetails,
    merchant_details: MerchantDetails,
    reference_transaction_details: ReferenceTransactionDetails,
}

impl TryFrom<&types::PaymentsCancelRouterData> for FiservCancelRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> {
        let auth: FiservAuthType = FiservAuthType::try_from(&item.connector_auth_type)?;
        let metadata = item.get_connector_meta()?.clone();
        let session: FiservSessionObject = metadata
            .expose()
            .parse_value("FiservSessionObject")
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "Merchant connector account metadata",
            })?;
        Ok(Self {
            merchant_details: MerchantDetails {
                merchant_id: auth.merchant_account,
                terminal_id: Some(session.terminal_id),
            },
            reference_transaction_details: ReferenceTransactionDetails {
                reference_transaction_id: item.request.connector_transaction_id.to_string(),
            },
            transaction_details: TransactionDetails {
                capture_flag: None,
                reversal_reason_code: Some(item.request.get_cancellation_reason()?),
                merchant_transaction_id: Some(item.connector_request_reference_id.clone()),
                operation_type: None,
            },
        })
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorResponse {
    pub error: Option<Vec<ErrorDetails>>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorDetails {
    #[serde(rename = "type")]
    pub error_type: Option<String>,
    pub code: Option<String>,
    pub field: Option<String>,
    pub message: Option<String>,
    pub additional_info: Option<String>,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum FiservPaymentStatus {
    Succeeded,
    Failed,
    Captured,
    Declined,
    Voided,
    Authorized,
    #[default]
    Processing,
    Created,
}

impl From<FiservPaymentStatus> for enums::AttemptStatus {
    fn from(item: FiservPaymentStatus) -> Self {
        match item {
            FiservPaymentStatus::Captured | FiservPaymentStatus::Succeeded => Self::Charged,
            FiservPaymentStatus::Declined | FiservPaymentStatus::Failed => Self::Failure,
            FiservPaymentStatus::Processing => Self::Authorizing,
            FiservPaymentStatus::Voided => Self::Voided,
            FiservPaymentStatus::Authorized => Self::Authorized,
            FiservPaymentStatus::Created => Self::AuthenticationPending,
        }
    }
}

impl From<FiservPaymentStatus> for enums::RefundStatus {
    fn from(item: FiservPaymentStatus) -> Self {
        match item {
            FiservPaymentStatus::Succeeded
            | FiservPaymentStatus::Authorized
            | FiservPaymentStatus::Captured => Self::Success,
            FiservPaymentStatus::Declined | FiservPaymentStatus::Failed => Self::Failure,
            FiservPaymentStatus::Voided
            | FiservPaymentStatus::Processing
            | FiservPaymentStatus::Created => Self::Pending,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProcessorResponseDetails {
    pub approval_status: Option<String>,
    pub approval_code: Option<String>,
    pub reference_number: Option<String>,
    pub processor: Option<String>,
    pub host: Option<String>,
    pub network_routed: Option<String>,
    pub network_international_id: Option<String>,
    pub response_code: Option<String>,
    pub response_message: Option<String>,
    pub host_response_code: Option<String>,
    pub host_response_message: Option<String>,
    pub additional_info: Option<Vec<AdditionalInfo>>,
    pub bank_association_details: Option<BankAssociationDetails>,
    pub response_indicators: Option<ResponseIndicators>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AdditionalInfo {
    pub name: Option<String>,
    pub value: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BankAssociationDetails {
    pub association_response_code: Option<String>,
    pub avs_security_code_response: Option<AvsSecurityCodeResponse>,
}

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

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Association {
    pub avs_code: Option<String>,
    pub security_code_response: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResponseIndicators {
    pub alternate_route_debit_indicator: Option<bool>,
    pub signature_line_indicator: Option<bool>,
    pub signature_debit_route_indicator: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservChargesResponse {
    pub gateway_response: GatewayResponse,
    pub payment_receipt: PaymentReceipt,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservCheckoutResponse {
    pub gateway_response: GatewayResponse,
    pub payment_receipt: PaymentReceipt,
    pub interactions: FiservResponseInteractions,
    pub order: Option<FiservResponseOrders>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservResponseInteractions {
    actions: FiservResponseActions,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservResponseActions {
    #[serde(rename = "type")]
    action_type: String,
    url: url::Url,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservResponseOrders {
    intent: FiservIntent,
    order_id: String,
    order_status: FiservOrderStatus,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum FiservOrderStatus {
    PayerActionRequired,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FiservPaymentsResponse {
    Charges(FiservChargesResponse),
    Checkout(FiservCheckoutResponse),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentReceipt {
    pub approved_amount: ApprovedAmount,
    pub processor_response_details: Option<ProcessorResponseDetails>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApprovedAmount {
    pub total: FloatMajorUnit,
    pub currency: Currency,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(transparent)]
pub struct FiservSyncResponse {
    pub sync_responses: Vec<FiservPaymentsResponse>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GatewayResponse {
    gateway_transaction_id: Option<String>,
    transaction_state: FiservPaymentStatus,
    transaction_processing_details: TransactionProcessingDetails,
}

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

impl<F, T> TryFrom<ResponseRouterData<F, FiservPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, FiservPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let (gateway_resp, redirect_url, order_id) = match &item.response {
            FiservPaymentsResponse::Charges(res) => (res.gateway_response.clone(), None, None),
            FiservPaymentsResponse::Checkout(res) => (
                res.gateway_response.clone(),
                Some(res.interactions.actions.url.clone()),
                res.order.as_ref().map(|o| o.order_id.clone()),
            ),
        };

        let redirection_data = redirect_url.map(|url| RedirectForm::from((url, Method::Get)));

        let connector_metadata: Option<serde_json::Value> = Some(serde_json::json!({
            "order_id": order_id,
        }));

        Ok(Self {
            status: enums::AttemptStatus::from(gateway_resp.transaction_state),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(
                    gateway_resp.transaction_processing_details.transaction_id,
                ),
                redirection_data: Box::new(redirection_data),
                mandate_reference: Box::new(None),
                connector_metadata,
                network_txn_id: None,
                connector_response_reference_id: Some(
                    gateway_resp.transaction_processing_details.order_id,
                ),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, FiservSyncResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, FiservSyncResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let gateway_resp = match item.response.sync_responses.first() {
            Some(gateway_response) => gateway_response,
            None => Err(errors::ConnectorError::ResponseHandlingFailed)?,
        };

        let connector_response_reference_id = match gateway_resp {
            FiservPaymentsResponse::Charges(res) => {
                &res.gateway_response.transaction_processing_details.order_id
            }
            FiservPaymentsResponse::Checkout(res) => {
                &res.gateway_response.transaction_processing_details.order_id
            }
        };

        let transaction_id = match gateway_resp {
            FiservPaymentsResponse::Charges(res) => {
                &res.gateway_response
                    .transaction_processing_details
                    .transaction_id
            }
            FiservPaymentsResponse::Checkout(res) => {
                &res.gateway_response
                    .transaction_processing_details
                    .transaction_id
            }
        };

        let transaction_state = match gateway_resp {
            FiservPaymentsResponse::Charges(res) => &res.gateway_response.transaction_state,
            FiservPaymentsResponse::Checkout(res) => &res.gateway_response.transaction_state,
        };

        Ok(Self {
            status: enums::AttemptStatus::from(transaction_state.clone()),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(transaction_id.to_string()),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(connector_response_reference_id.to_string()),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservCaptureRequest {
    amount: Amount,
    transaction_details: TransactionDetails,
    merchant_details: MerchantDetails,
    reference_transaction_details: ReferenceTransactionDetails,
    #[serde(skip_serializing_if = "Option::is_none")]
    order: Option<FiservOrderRequest>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservOrderRequest {
    order_id: Option<String>,
}

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

#[derive(Debug, Serialize, Deserialize)]
pub struct FiservSessionObject {
    pub terminal_id: Secret<String>,
}

impl TryFrom<&Option<pii::SecretSerdeValue>> for FiservSessionObject {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(meta_data: &Option<pii::SecretSerdeValue>) -> Result<Self, Self::Error> {
        let metadata: Self = utils::to_connector_meta_from_secret::<Self>(meta_data.clone())
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "metadata",
            })?;
        Ok(metadata)
    }
}

impl TryFrom<&FiservRouterData<&types::PaymentsCaptureRouterData>> for FiservCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &FiservRouterData<&types::PaymentsCaptureRouterData>,
    ) -> Result<Self, Self::Error> {
        let auth: FiservAuthType = FiservAuthType::try_from(&item.router_data.connector_auth_type)?;
        let metadata = item
            .router_data
            .connector_meta_data
            .clone()
            .ok_or(errors::ConnectorError::RequestEncodingFailed)?;
        let session: FiservSessionObject = metadata
            .expose()
            .parse_value("FiservSessionObject")
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "Merchant connector account metadata",
            })?;

        let order_id = item
            .router_data
            .request
            .connector_meta
            .as_ref()
            .and_then(|v| v.get("order_id"))
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        Ok(Self {
            amount: Amount {
                total: item.amount,
                currency: item.router_data.request.currency.to_string(),
            },
            order: Some(FiservOrderRequest { order_id }),
            transaction_details: TransactionDetails {
                capture_flag: Some(true),
                reversal_reason_code: None,
                merchant_transaction_id: Some(
                    item.router_data.connector_request_reference_id.clone(),
                ),
                operation_type: Some(OperationType::Capture),
            },
            merchant_details: MerchantDetails {
                merchant_id: auth.merchant_account,
                terminal_id: Some(session.terminal_id),
            },
            reference_transaction_details: ReferenceTransactionDetails {
                reference_transaction_id: item


pub struct Violations {
    pub razao: String,
    pub propriedade: String,
    pub valor: String,
}


/// Short hash of the current commit.
///
/// Example: `f5f383e`.
#[cfg(feature = "vergen")]
#[macro_export]
macro_rules! commit {
    () => {
        env!("VERGEN_GIT_SHA")
    };
}


pub struct PaymentMethodMigrationResponse {
    pub line_number: Option<i64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method: Option<api_enums::PaymentMethod>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,
    pub customer_id: Option<id_type::CustomerId>,
    pub migration_status: MigrationStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub migration_error: Option<String>,
    pub card_number_masked: Option<masking::Secret<String>>,
    pub card_migrated: Option<bool>,
    pub network_token_migrated: Option<bool>,
    pub connector_mandate_details_migrated: Option<bool>,
    pub network_transaction_id_migrated: Option<bool>,
}


pub struct Customer {
    id: String,
}


pub struct GenericLink {
    pub link_id: String,
    pub primary_reference: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub last_modified_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub expiry: PrimitiveDateTime,
    pub link_data: serde_json::Value,
    pub link_status: GenericLinkStatus,
    pub link_type: storage_enums::GenericLinkType,
    pub url: Secret<String>,
    pub return_url: Option<String>,
}


pub struct PaypalConfirmRequest {
    create_transaction_request: PaypalConfirmTransactionRequest,
}


pub async fn payment_intent_list(
    state: web::Data<routes::AppState>,
    req: HttpRequest,
    payload: web::Query<types::StripePaymentListConstraints>,
) -> HttpResponse {
    let payload = match payment_types::PaymentListConstraints::try_from(payload.into_inner()) {
        Ok(p) => p,
        Err(err) => return api::log_and_return_error_response(err),
    };
    use crate::core::api_locking;
    let flow = Flow::PaymentsList;
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentListResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::list_payments(state, merchant_context, None, req)
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}