// File: crates/diesel_models/src/merchant_connector_account.rs
// Module: diesel_models
// Public functions: 4
// Public structs: 9

#[cfg(feature = "v2")]
use std::collections::HashMap;
use std::fmt::Debug;

use common_utils::{encryption::Encryption, id_type, pii};
#[cfg(feature = "v2")]
use diesel::{sql_types::Jsonb, AsExpression};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};

use crate::enums as storage_enums;
#[cfg(feature = "v1")]
use crate::schema::merchant_connector_account;
#[cfg(feature = "v2")]
use crate::schema_v2::merchant_connector_account;

#[cfg(feature = "v1")]
#[derive(
    Clone,
    Debug,
    serde::Serialize,
    serde::Deserialize,
    Identifiable,
    Queryable,
    Selectable,
    router_derive::DebugAsDisplay,
)]
#[diesel(table_name = merchant_connector_account, primary_key(merchant_connector_id), check_for_backend(diesel::pg::Pg))]
pub struct MerchantConnectorAccount {
    pub merchant_id: id_type::MerchantId,
    pub connector_name: String,
    pub connector_account_details: Encryption,
    pub test_mode: Option<bool>,
    pub disabled: Option<bool>,
    pub merchant_connector_id: id_type::MerchantConnectorAccountId,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub payment_methods_enabled: Option<Vec<pii::SecretSerdeValue>>,
    pub connector_type: storage_enums::ConnectorType,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_label: Option<String>,
    pub business_country: Option<storage_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub business_sub_label: Option<String>,
    pub frm_configs: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    pub profile_id: Option<id_type::ProfileId>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::ConnectorStatus,
    pub additional_merchant_data: Option<Encryption>,
    pub connector_wallets_details: Option<Encryption>,
    pub version: common_enums::ApiVersion,
    pub id: Option<id_type::MerchantConnectorAccountId>,
}

#[cfg(feature = "v1")]
impl MerchantConnectorAccount {
    pub fn get_id(&self) -> id_type::MerchantConnectorAccountId {
        self.merchant_connector_id.clone()
    }
}

#[cfg(feature = "v2")]
use crate::RequiredFromNullable;

#[cfg(feature = "v2")]
#[derive(
    Clone,
    Debug,
    serde::Serialize,
    serde::Deserialize,
    Identifiable,
    Queryable,
    Selectable,
    router_derive::DebugAsDisplay,
)]
#[diesel(table_name = merchant_connector_account, check_for_backend(diesel::pg::Pg))]
pub struct MerchantConnectorAccount {
    pub merchant_id: id_type::MerchantId,
    pub connector_name: common_enums::connector_enums::Connector,
    pub connector_account_details: Encryption,
    pub disabled: Option<bool>,
    #[diesel(deserialize_as = super::OptionalDieselArray<common_types::payment_methods::PaymentMethodsEnabled>)]
    pub payment_methods_enabled: Option<Vec<common_types::payment_methods::PaymentMethodsEnabled>>,
    pub connector_type: storage_enums::ConnectorType,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_label: Option<String>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    #[diesel(deserialize_as = RequiredFromNullable<id_type::ProfileId>)]
    pub profile_id: id_type::ProfileId,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::ConnectorStatus,
    pub additional_merchant_data: Option<Encryption>,
    pub connector_wallets_details: Option<Encryption>,
    pub version: common_enums::ApiVersion,
    pub id: id_type::MerchantConnectorAccountId,
    pub feature_metadata: Option<MerchantConnectorAccountFeatureMetadata>,
}

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

#[cfg(feature = "v1")]
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_connector_account)]
pub struct MerchantConnectorAccountNew {
    pub merchant_id: Option<id_type::MerchantId>,
    pub connector_type: Option<storage_enums::ConnectorType>,
    pub connector_name: Option<String>,
    pub connector_account_details: Option<Encryption>,
    pub test_mode: Option<bool>,
    pub disabled: Option<bool>,
    pub merchant_connector_id: id_type::MerchantConnectorAccountId,
    pub payment_methods_enabled: Option<Vec<pii::SecretSerdeValue>>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_label: Option<String>,
    pub business_country: Option<storage_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub business_sub_label: Option<String>,
    pub frm_configs: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    pub profile_id: Option<id_type::ProfileId>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::ConnectorStatus,
    pub additional_merchant_data: Option<Encryption>,
    pub connector_wallets_details: Option<Encryption>,
    pub version: common_enums::ApiVersion,
    pub id: Option<id_type::MerchantConnectorAccountId>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_connector_account)]
pub struct MerchantConnectorAccountNew {
    pub merchant_id: Option<id_type::MerchantId>,
    pub connector_type: Option<storage_enums::ConnectorType>,
    pub connector_name: Option<common_enums::connector_enums::Connector>,
    pub connector_account_details: Option<Encryption>,
    pub disabled: Option<bool>,
    #[diesel(deserialize_as = super::OptionalDieselArray<common_types::payment_methods::PaymentMethodsEnabled>)]
    pub payment_methods_enabled: Option<Vec<common_types::payment_methods::PaymentMethodsEnabled>>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_label: Option<String>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    pub profile_id: id_type::ProfileId,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::ConnectorStatus,
    pub additional_merchant_data: Option<Encryption>,
    pub connector_wallets_details: Option<Encryption>,
    pub version: common_enums::ApiVersion,
    pub id: id_type::MerchantConnectorAccountId,
    pub feature_metadata: Option<MerchantConnectorAccountFeatureMetadata>,
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_connector_account)]
pub struct MerchantConnectorAccountUpdateInternal {
    pub connector_type: Option<storage_enums::ConnectorType>,
    pub connector_name: Option<String>,
    pub connector_account_details: Option<Encryption>,
    pub connector_label: Option<String>,
    pub test_mode: Option<bool>,
    pub disabled: Option<bool>,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub payment_methods_enabled: Option<Vec<pii::SecretSerdeValue>>,
    pub frm_configs: Option<pii::SecretSerdeValue>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub modified_at: Option<time::PrimitiveDateTime>,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: Option<storage_enums::ConnectorStatus>,
    pub connector_wallets_details: Option<Encryption>,
    pub additional_merchant_data: Option<Encryption>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = merchant_connector_account)]
pub struct MerchantConnectorAccountUpdateInternal {
    pub connector_type: Option<storage_enums::ConnectorType>,
    pub connector_account_details: Option<Encryption>,
    pub connector_label: Option<String>,
    pub disabled: Option<bool>,
    #[diesel(deserialize_as = super::OptionalDieselArray<common_types::payment_methods::PaymentMethodsEnabled>)]
    pub payment_methods_enabled: Option<Vec<common_types::payment_methods::PaymentMethodsEnabled>>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub modified_at: Option<time::PrimitiveDateTime>,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: Option<storage_enums::ConnectorStatus>,
    pub connector_wallets_details: Option<Encryption>,
    pub additional_merchant_data: Option<Encryption>,
    pub feature_metadata: Option<MerchantConnectorAccountFeatureMetadata>,
}

#[cfg(feature = "v1")]
impl MerchantConnectorAccountUpdateInternal {
    pub fn create_merchant_connector_account(
        self,
        source: MerchantConnectorAccount,
    ) -> MerchantConnectorAccount {
        MerchantConnectorAccount {
            merchant_id: source.merchant_id,
            connector_type: self.connector_type.unwrap_or(source.connector_type),
            connector_account_details: self
                .connector_account_details
                .unwrap_or(source.connector_account_details),
            test_mode: self.test_mode,
            disabled: self.disabled,
            merchant_connector_id: self
                .merchant_connector_id
                .unwrap_or(source.merchant_connector_id),
            payment_methods_enabled: self.payment_methods_enabled,
            frm_config: self.frm_config,
            modified_at: self.modified_at.unwrap_or(source.modified_at),
            pm_auth_config: self.pm_auth_config,
            status: self.status.unwrap_or(source.status),

            ..source
        }
    }
}

#[cfg(feature = "v2")]
impl MerchantConnectorAccountUpdateInternal {
    pub fn create_merchant_connector_account(
        self,
        source: MerchantConnectorAccount,
    ) -> MerchantConnectorAccount {
        MerchantConnectorAccount {
            connector_type: self.connector_type.unwrap_or(source.connector_type),
            connector_account_details: self
                .connector_account_details
                .unwrap_or(source.connector_account_details),
            disabled: self.disabled,
            payment_methods_enabled: self.payment_methods_enabled,
            frm_config: self.frm_config,
            modified_at: self.modified_at.unwrap_or(source.modified_at),
            pm_auth_config: self.pm_auth_config,
            status: self.status.unwrap_or(source.status),
            feature_metadata: self.feature_metadata,

            ..source
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, AsExpression)]
#[diesel(sql_type = Jsonb)]
pub struct MerchantConnectorAccountFeatureMetadata {
    pub revenue_recovery: Option<RevenueRecoveryMetadata>,
}

#[cfg(feature = "v2")]
common_utils::impl_to_sql_from_sql_json!(MerchantConnectorAccountFeatureMetadata);

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct RevenueRecoveryMetadata {
    /// The maximum number of retries allowed for an invoice. This limit is set by the merchant for each `billing connector`.Once this limit is reached, no further retries will be attempted.
    pub max_retry_count: u16,
    /// Maximum number of `billing connector` retries before revenue recovery can start executing retries.
    pub billing_connector_retry_threshold: u16,
    /// Billing account reference id is payment gateway id at billing connector end.
    /// Merchants need to provide a mapping between these merchant connector account and the corresponding
    /// account reference IDs for each `billing connector`.
    pub billing_account_reference: BillingAccountReference,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct BillingAccountReference(pub HashMap<id_type::MerchantConnectorAccountId, String>);


// Trait: ActivePaymentsMetric
// File: crates/analytics/src/active_payments/metrics.rs
// Module: analytics

pub trait ActivePaymentsMetric<T>


    pub customer: Option<Secret<String>>,
    pub payment_method: Option<String>,
    pub statement_descriptor: Option<String>,
    pub statement_descriptor_suffix: Option<String>,
    pub metadata: StripeMetadata,
    pub next_action: Option<StripeNextActionResponse>,
    pub payment_method_options: Option<StripePaymentMethodOptions>,
    pub latest_attempt: Option<LatestAttempt>,
    pub last_setup_error: Option<ErrorDetails>,
}

fn extract_payment_method_connector_response_from_latest_charge(
    stripe_charge_enum: &StripeChargeEnum,
) -> Option<ConnectorResponseData> {
    let is_overcapture_enabled = stripe_charge_enum.get_overcapture_status();
    let additional_payment_method_details =
        if let StripeChargeEnum::ChargeObject(charge_object) = stripe_charge_enum {
            charge_object
                .payment_method_details
                .as_ref()
                .and_then(StripePaymentMethodDetailsResponse::get_additional_payment_method_data)
        } else {
            None
        };

    let additional_payment_method_data = additional_payment_method_details
        .as_ref()
        .map(AdditionalPaymentMethodConnectorResponse::from);
    let extended_authorization_data = additional_payment_method_details
        .as_ref()
        .map(ExtendedAuthorizationResponseData::from);

    if additional_payment_method_data.is_some()
        || extended_authorization_data.is_some()
        || is_overcapture_enabled.is_some()
    {
        Some(ConnectorResponseData::new(
            additional_payment_method_data,
            is_overcapture_enabled,
            extended_authorization_data,
        ))
    } else {
        None
    }
}

fn extract_payment_method_connector_response_from_latest_attempt(
    stripe_latest_attempt: &LatestAttempt,
) -> Option<ConnectorResponseData> {
    if let LatestAttempt::PaymentIntentAttempt(intent_attempt) = stripe_latest_attempt {
        intent_attempt
            .payment_method_details
            .as_ref()
            .and_then(StripePaymentMethodDetailsResponse::get_additional_payment_method_data)
    } else {
        None
    }
    .as_ref()
    .map(AdditionalPaymentMethodConnectorResponse::from)
    .map(ConnectorResponseData::with_additional_payment_method_data)
}

impl<F, T> TryFrom<ResponseRouterData<F, PaymentIntentResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
where
    T: SplitPaymentData + GetRequestIncrementalAuthorization,
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        //
        item: ResponseRouterData<F, PaymentIntentResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let redirect_data = item.response.next_action.clone();
        let redirection_data = redirect_data
            .and_then(|redirection_data| redirection_data.get_url())
            .map(|redirection_url| RedirectForm::from((redirection_url, Method::Get)));

        let mandate_reference = item.response.payment_method.map(|payment_method_id| {
            // Implemented Save and re-use payment information for recurring charges
            // For more info: https://docs.stripe.com/recurring-payments#accept-recurring-payments
            // For backward compatibility payment_method_id & connector_mandate_id is being populated with the same value
            let connector_mandate_id = Some(payment_method_id.clone().expose());
            let payment_method_id = Some(payment_method_id.expose());

            let mandate_metadata: Option<Secret<Value>> =
                match item.data.request.get_split_payment_data() {
                    Some(SplitPaymentsRequest::StripeSplitPayment(stripe_split_data)) => {
                        Some(Secret::new(serde_json::json!({
                            "transfer_account_id": stripe_split_data.transfer_account_id,
                            "charge_type": stripe_split_data.charge_type,
                            "application_fees": stripe_split_data.application_fees,
                        })))
                    }
                    _ => None,
                };

            MandateReference {
                connector_mandate_id,
                payment_method_id,
                mandate_metadata,
                connector_mandate_request_reference_id: None,
            }
        });

        //Note: we might have to call retrieve_setup_intent to get the network_transaction_id in case its not sent in PaymentIntentResponse
        // Or we identify the mandate txns before hand and always call SetupIntent in case of mandate payment call
        let network_txn_id = match item.response.latest_charge.as_ref() {
            Some(StripeChargeEnum::ChargeObject(charge_object)) => charge_object
                .payment_method_details
                .as_ref()
                .and_then(|payment_method_details| match payment_method_details {
                    StripePaymentMethodDetailsResponse::Card { card } => {
                        card.network_transaction_id.clone()
                    }
                    _ => None,
                }),
            _ => None,
        };

        let connector_metadata =
            get_connector_metadata(item.response.next_action.as_ref(), item.response.amount)?;

        let status = AttemptStatus::from(item.response.status);

        let response = if is_payment_failure(status) {
            *get_stripe_payments_response_data(
                &item.response.last_payment_error,
                item.http_code,
                item.response.id.clone(),
            )
        } else {
            let charges = item
                .response
                .latest_charge
                .as_ref()
                .map(|charge| match charge {
                    StripeChargeEnum::ChargeId(charges) => charges.clone(),
                    StripeChargeEnum::ChargeObject(charge) => charge.id.clone(),
                })
                .and_then(|charge_id| construct_charge_response(charge_id, &item.data.request));

            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()),
                redirection_data: Box::new(redirection_data),
                mandate_reference: Box::new(mandate_reference),
                connector_metadata,
                network_txn_id,
                connector_response_reference_id: Some(item.response.id),
                incremental_authorization_allowed: item
                    .data
                    .request
                    .get_request_incremental_authorization(),
                charges,
            })
        };

        let connector_response_data = item
            .response
            .latest_charge
            .as_ref()
            .and_then(extract_payment_method_connector_response_from_latest_charge);

        let minor_amount_capturable = item
            .response
            .latest_charge
            .as_ref()
            .and_then(StripeChargeEnum::get_maximum_capturable_amount);

        Ok(Self {
            status,
            /* Commented out fields:
            client_secret: Some(item.response.client_secret.clone().as_str()),
            description: item.response.description.map(|x| x.as_str()),
            statement_descriptor_suffix: item.response.statement_descriptor_suffix.map(|x| x.as_str()),
            three_ds_form,
            */
            response,
            amount_captured: item
                .response
                .amount_received
                .map(|amount| amount.get_amount_as_i64()),
            minor_amount_captured: item.response.amount_received,
            connector_response: connector_response_data,
            minor_amount_capturable,
            ..item.data
        })
    }
}

impl From<StripePaymentStatus> for common_enums::AuthorizationStatus {
    fn from(item: StripePaymentStatus) -> Self {
        match item {
            StripePaymentStatus::Succeeded
            | StripePaymentStatus::RequiresCapture
            | StripePaymentStatus::Chargeable
            | StripePaymentStatus::RequiresCustomerAction
            | StripePaymentStatus::RequiresConfirmation
            | StripePaymentStatus::Consumed => Self::Success,
            StripePaymentStatus::Processing | StripePaymentStatus::Pending => Self::Processing,
            StripePaymentStatus::Failed
            | StripePaymentStatus::Canceled
            | StripePaymentStatus::RequiresPaymentMethod => Self::Failure,
        }
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            PaymentIntentResponse,
            PaymentsIncrementalAuthorizationData,
            PaymentsResponseData,
        >,
    > for RouterData<F, PaymentsIncrementalAuthorizationData, PaymentsResponseData>
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            PaymentIntentResponse,
            PaymentsIncrementalAuthorizationData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let status = common_enums::AuthorizationStatus::from(item.response.status);
        Ok(Self {
            response: Ok(PaymentsResponseData::IncrementalAuthorizationResponse {
                status,
                error_code: None,
                error_message: None,
                connector_authorization_id: Some(item.response.id),
            }),
            ..item.data
        })
    }
}

pub fn get_connector_metadata(
    next_action: Option<&StripeNextActionResponse>,
    amount: MinorUnit,
) -> CustomResult<Option<Value>, ConnectorError> {
    let next_action_response = next_action
        .and_then(|next_action_response| match next_action_response {
            StripeNextActionResponse::DisplayBankTransferInstructions(response) => {
                match response.financial_addresses.clone() {
                    FinancialInformation::StripeFinancialInformation(financial_addresses) => {
                        let bank_instructions = financial_addresses.first();
                        let (sepa_bank_instructions, bacs_bank_instructions) = bank_instructions
                            .map_or((None, None), |financial_address| {
                                (
                                    financial_address.iban.to_owned().map(
                                        |sepa_financial_details| SepaFinancialDetails {
                                            account_holder_name: sepa_financial_details
                                                .account_holder_name,
                                            bic: sepa_financial_details.bic,
                                            country: sepa_financial_details.country,
                                            iban: sepa_financial_details.iban,
                                            reference: response.reference.to_owned(),
                                        },
                                    ),
                                    financial_address.sort_code.to_owned(),
                                )
                            });
                        let bank_transfer_instructions = SepaAndBacsBankTransferInstructions {
                            sepa_bank_instructions,
                            bacs_bank_instructions,
                            receiver: SepaAndBacsReceiver {
                                amount_received: amount - response.amount_remaining,
                                amount_remaining: response.amount_remaining,
                            },
                        };

                        Some(bank_transfer_instructions.encode_to_value())
                    }
                    FinancialInformation::AchFinancialInformation(financial_addresses) => {
                        let mut ach_financial_information = HashMap::new();
                        for address in financial_addresses {
                            match address.financial_details {
                                AchFinancialDetails::Aba(aba_details) => {
                                    ach_financial_information
                                        .insert("account_number", aba_details.account_number);
                                    ach_financial_information
                                        .insert("bank_name", aba_details.bank_name);
                                    ach_financial_information
                                        .insert("routing_number", aba_details.routing_number);
                                }
                                AchFinancialDetails::Swift(swift_details) => {
                                    ach_financial_information
                                        .insert("swift_code", swift_details.swift_code);
                                }
                            }
                        }

                        let ach_financial_information_value =
                            serde_json::to_value(ach_financial_information).ok()?;

                        let ach_transfer_instruction =
                            serde_json::from_value::<payments::AchTransfer>(
                                ach_financial_information_value,
                            )
                            .ok()?;

                        let bank_transfer_instructions = payments::BankTransferNextStepsData {
                            bank_transfer_instructions:
                                payments::BankTransferInstructions::AchCreditTransfer(Box::new(
                                    ach_transfer_instruction,
                                )),
                            receiver: None,
                        };

                        Some(bank_transfer_instructions.encode_to_value())
                    }
                }
            }
            StripeNextActionResponse::WechatPayDisplayQrCode(response) => {
                let wechat_pay_instructions = QrCodeNextInstructions {
                    image_data_url: response.image_data_url.to_owned(),
                    display_to_timestamp: None,
                };

                Some(wechat_pay_instructions.encode_to_value())
            }
            StripeNextActionResponse::CashappHandleRedirectOrDisplayQrCode(response) => {
                let cashapp_qr_instructions: QrCodeNextInstructions = QrCodeNextInstructions {
                    image_data_url: response.qr_code.image_url_png.to_owned(),
                    display_to_timestamp: response.qr_code.expires_at.to_owned(),
                };
                Some(cashapp_qr_instructions.encode_to_value())
            }
            StripeNextActionResponse::MultibancoDisplayDetails(response) => {
                let multibanco_bank_transfer_instructions = payments::BankTransferNextStepsData {
                    bank_transfer_instructions: payments::BankTransferInstructions::Multibanco(
                        Box::new(payments::MultibancoTransferInstructions {
                            reference: response.clone().reference,
                            entity: response.clone().entity.expose(),
                        }),
                    ),
                    receiver: None,
                };
                Some(multibanco_bank_transfer_instructions.encode_to_value())
            }
            _ => None,
        })
        .transpose()
        .change_context(ConnectorError::ResponseHandlingFailed)?;
    Ok(next_action_response)
}

pub fn get_payment_method_id(
    latest_charge: Option<StripeChargeEnum>,
    payment_method_id_from_intent_root: Secret<String>,
) -> String {
    match latest_charge {
        Some(StripeChargeEnum::ChargeObject(charge)) => match charge.payment_method_details {
            Some(StripePaymentMethodDetailsResponse::Bancontact { bancontact }) => bancontact
                .attached_payment_method
                .map(|attached_payment_method| attached_payment_method.expose())
                .unwrap_or(payment_method_id_from_intent_root.expose()),
            Some(StripePaymentMethodDetailsResponse::Ideal { ideal }) => ideal
                .attached_payment_method
                .map(|attached_payment_method| attached_payment_method.expose())
                .unwrap_or(payment_method_id_from_intent_root.expose()),
            Some(StripePaymentMethodDetailsResponse::Blik)
            | Some(StripePaymentMethodDetailsResponse::Eps)
            | Some(StripePaymentMethodDetailsResponse::Fpx)
            | Some(StripePaymentMethodDetailsResponse::Giropay)
            | Some(StripePaymentMethodDetailsResponse::Przelewy24)
            | Some(StripePaymentMethodDetailsResponse::Card { .. })
            | Some(StripePaymentMethodDetailsResponse::Klarna)
            | Some(StripePaymentMethodDetailsResponse::Affirm)
            | Some(StripePaymentMethodDetailsResponse::AfterpayClearpay)
            | Some(StripePaymentMethodDetailsResponse::AmazonPay)
            | Some(StripePaymentMethodDetailsResponse::ApplePay)
            | Some(StripePaymentMethodDetailsResponse::Ach)
            | Some(StripePaymentMethodDetailsResponse::Sepa)
            | Some(StripePaymentMethodDetailsResponse::Becs)
            | Some(StripePaymentMethodDetailsResponse::Bacs)
            | Some(StripePaymentMethodDetailsResponse::Wechatpay)
            | Some(StripePaymentMethodDetailsResponse::Alipay)
            | Some(StripePaymentMethodDetailsResponse::CustomerBalance)
            | Some(StripePaymentMethodDetailsResponse::Cashapp { .. })
            | Some(StripePaymentMethodDetailsResponse::RevolutPay)
            | None => payment_method_id_from_intent_root.expose(),
        },
        Some(StripeChargeEnum::ChargeId(_)) | None => payment_method_id_from_intent_root.expose(),
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, PaymentIntentSyncResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
where
    T: SplitPaymentData,
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, PaymentIntentSyncResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let redirect_data = item.response.next_action.clone();
        let redirection_data = redirect_data
            .and_then(|redirection_data| redirection_data.get_url())
            .map(|redirection_url| RedirectForm::from((redirection_url, Method::Get)));

        let mandate_reference = item
            .response
            .payment_method
            .clone()
            .map(|payment_method_id| {
                // Implemented Save and re-use payment information for recurring charges
                // For more info: https://docs.stripe.com/recurring-payments#accept-recurring-payments
                // For backward compatibility payment_method_id & connector_mandate_id is being populated with the same value
                let payment_method_id =
                    get_payment_method_id(item.response.latest_charge.clone(), payment_method_id);

                MandateReference {
                    connector_mandate_id: Some(payment_method_id.clone()),
                    payment_method_id: Some(payment_method_id),
                    mandate_metadata: None,
                    connector_mandate_request_reference_id: None,
                }
            });

        let connector_metadata =
            get_connector_metadata(item.response.next_action.as_ref(), item.response.amount)?;

        let status = AttemptStatus::from(item.response.status.to_owned());

        let connector_response_data = item
            .response
            .latest_charge
            .as_ref()
            .and_then(extract_payment_method_connector_response_from_latest_charge);

        let response = if is_payment_failure(status) {
            *get_stripe_payments_response_data(
                &item.response.payment_intent_fields.last_payment_error,
                item.http_code,
                item.response.id.clone(),
            )
        } else {
            let network_transaction_id = match item.response.latest_charge.clone() {
                Some(StripeChargeEnum::ChargeObject(charge_object)) => charge_object
                    .payment_method_details
                    .and_then(|payment_method_details| match payment_method_details {
                        StripePaymentMethodDetailsResponse::Card { card } => {
                            card.network_transaction_id
                        }
                        _ => None,
                    }),
                _ => None,
            };
            let charges = item
                .response
                .latest_charge
                .as_ref()
                .map(|charge| match charge {
                    StripeChargeEnum::ChargeId(charges) => charges.clone(),
                    StripeChargeEnum::ChargeObject(charge) => charge.id.clone(),
                })
                .and_then(|charge_id| construct_charge_response(charge_id, &item.data.request));

            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()),
                redirection_data: Box::new(redirection_data),
                mandate_reference: Box::new(mandate_reference),
                connector_metadata,
                network_txn_id: network_transaction_id,
                connector_response_reference_id: Some(item.response.id.clone()),
                incremental_authorization_allowed: None,
                charges,
            })
        };

        Ok(Self {
            status: AttemptStatus::from(item.response.status.to_owned()),
            response,
            amount_captured: item
                .response
                .amount_received
                .map(|amount| amount.get_amount_as_i64()),
            minor_amount_captured: item.response.amount_received,
            connector_response: connector_response_data,
            ..item.data
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, SetupIntentResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, SetupIntentResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let redirect_data = item.response.next_action.clone();
        let redirection_data = redirect_data
            .and_then(|redirection_data| redirection_data.get_url())
            .map(|redirection_url| RedirectForm::from((redirection_url, Method::Get)));

        let mandate_reference = item.response.payment_method.map(|payment_method_id| {
            // Implemented Save and re-use payment information for recurring charges
            // For more info: https://docs.stripe.com/recurring-payments#accept-recurring-payments
            // For backward compatibility payment_method_id & connector_mandate_id is being populated with the same value
            let connector_mandate_id = Some(payment_method_id.clone());
            let payment_method_id = Some(payment_method_id);
            MandateReference {
                connector_mandate_id,
                payment_method_id,
                mandate_metadata: None,
                connector_mandate_request_reference_id: None,
            }
        });
        let status = AttemptStatus::from(item.response.status);
        let connector_response_data = item
            .response
            .latest_attempt
            .as_ref()
            .and_then(extract_payment_method_connector_response_from_latest_attempt);

        let response = if is_payment_failure(status) {
            *get_stripe_payments_response_data(
                &item.response.last_setup_error,
                item.http_code,
                item.response.id.clone(),
            )
        } else {
            let network_transaction_id = match item.response.latest_attempt {
                Some(LatestAttempt::PaymentIntentAttempt(attempt)) => attempt
                    .payment_method_details
                    .and_then(|payment_method_details| match payment_method_details {
                        StripePaymentMethodDetailsResponse::Card { card } => {
                            card.network_transaction_id
                        }
                        _ => None,
                    }),
                _ => None,
            };

            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()),
                redirection_data: Box::new(redirection_data),
                mandate_reference: Box::new(mandate_reference),
                connector_metadata: None,
                network_txn_id: network_transaction_id,
                connector_response_reference_id: Some(item.response.id),
                incremental_authorization_allowed: None,
                charges: None,
            })
        };

        Ok(Self {
            status,
            response,
            connector_response: connector_response_data,
            ..item.data
        })
    }
}

pub fn stripe_opt_latest_attempt_to_opt_string(
    latest_attempt: Option<LatestAttempt>,
) -> Option<String> {
    match latest_attempt {
        Some(LatestAttempt::PaymentIntentAttempt(attempt)) => attempt
            .payment_method_options
            .and_then(|payment_method_options| match payment_method_options {
                StripePaymentMethodOptions::Card {
                    network_transaction_id,
                    ..
                } => network_transaction_id.map(|network_id| network_id.expose()),
                _ => None,
            }),
        _ => None,
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case", remote = "Self")]
pub enum StripeNextActionResponse {
    CashappHandleRedirectOrDisplayQrCode(StripeCashappQrResponse),
    RedirectToUrl(StripeRedirectToUrlResponse),
    AlipayHandleRedirect(StripeRedirectToUrlResponse),
    VerifyWithMicrodeposits(StripeVerifyWithMicroDepositsResponse),
    WechatPayDisplayQrCode(WechatPayRedirectToQr),
    DisplayBankTransferInstructions(StripeBankTransferDetails),
    MultibancoDisplayDetails(MultibancoCreditTansferResponse),
    NoNextActionBody,
}

impl StripeNextActionResponse {
    fn get_url(&self) -> Option<Url> {
        match self {
            Self::RedirectToUrl(redirect_to_url) | Self::AlipayHandleRedirect(redirect_to_url) => {
                Some(redirect_to_url.url.to_owned())
            }
            Self::WechatPayDisplayQrCode(_) => None,
            Self::VerifyWithMicrodeposits(verify_with_microdeposits) => {
                Some(verify_with_microdeposits.hosted_verification_url.to_owned())
            }
            Self::CashappHandleRedirectOrDisplayQrCode(_) => None,
            Self::DisplayBankTransferInstructions(_) => None,
            Self::MultibancoDisplayDetails(_) => None,
            Self::NoNextActionBody => None,
        }
    }
}

// This impl is required because Stripe's response is of the below format, which is externally
// tagged, but also with an extra 'type' field specifying the enum variant name:
// "next_action": {
//   "redirect_to_url": { "return_url": "...", "url": "..." },
//   "type": "redirect_to_url"
// },
// Reference: https://github.com/serde-rs/serde/issues/1343#issuecomment-409698470
impl<'de> Deserialize<'de> for StripeNextActionResponse {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        struct Wrapper {
            #[serde(rename = "type")]
            _ignore: String,
            #[serde(flatten, with = "StripeNextActionResponse")]
            inner: StripeNextActionResponse,
        }

        // There is some exception in the stripe next action, it usually sends :
        // "next_action": {
        //   "redirect_to_url": { "return_url": "...", "url": "..." },
        //   "type": "redirect_to_url"
        // },
        // But there is a case where it only sends the type and not other field named as it's type
        let stripe_next_action_response =
            Wrapper::deserialize(deserializer).map_or(Self::NoNextActionBody, |w| w.inner);

        Ok(stripe_next_action_response)
    }
}

impl Serialize for StripeNextActionResponse {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        match *self {
            Self::CashappHandleRedirectOrDisplayQrCode(ref i) => {
                Serialize::serialize(i, serializer)
            }
            Self::RedirectToUrl(ref i) => Serialize::serialize(i, serializer),
            Self::AlipayHandleRedirect(ref i) => Serialize::serialize(i, serializer),
            Self::VerifyWithMicrodeposits(ref i) => Serialize::serialize(i, serializer),
            Self::WechatPayDisplayQrCode(ref i) => Serialize::serialize(i, serializer),
            Self::DisplayBankTransferInstructions(ref i) => Serialize::serialize(i, serializer),
            Self::MultibancoDisplayDetails(ref i) => Serialize::serialize(i, serializer),
            Self::NoNextActionBody => Serialize::serialize("NoNextActionBody", serializer),
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct StripeRedirectToUrlResponse {
    return_url: String,
    url: Url,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct WechatPayRedirectToQr {
    // This data contains url, it should be converted to QR code.
    // Note: The url in this data is not redirection url
    data: Url,
    // This is the image source, this image_data_url can directly be used by sdk to show the QR code
    image_data_url: Url,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct StripeVerifyWithMicroDepositsResponse {
    hosted_verification_url: Url,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum FinancialInformation {
    AchFinancialInformation(Vec<AchFinancialInformation>),
    StripeFinancialInformation(Vec<StripeFinancialInformation>),
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct StripeBankTransferDetails {
    pub amount_remaining: MinorUnit,
    pub currency: String,
    pub financial_addresses: FinancialInformation,
    pub hosted_instructions_url: Option<String>,
    pub reference: Option<String>,
    #[serde(rename = "type")]
    pub bank_transfer_type: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct StripeCashappQrResponse {
    pub mobile_auth_url: Url,
    pub qr_code: QrCodeResponse,
    pub hosted_instructions_url: Url,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct QrCodeResponse {
    pub expires_at: Option<i64>,
    pub image_url_png: Url,
    pub image_url_svg: Url,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct AbaDetails {
    pub account_number: Secret<String>,
    pub bank_name: Secret<String>,
    pub routing_number: Secret<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct SwiftDetails {
    pub account_number: Secret<String>,
    pub bank_name: Secret<String>,
    pub swift_code: Secret<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum AchFinancialDetails {
    Aba(AbaDetails),
    Swift(SwiftDetails),
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct StripeFinancialInformation {
    pub iban: Option<SepaFinancialDetails>,
    pub sort_code: Option<BacsFinancialDetails>,
    pub supported_networks: Vec<String>,
    #[serde(rename = "type")]
    pub financial_info_type: String,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct AchFinancialInformation {
    #[serde(flatten)]
    pub financial_details: AchFinancialDetails,
    pub supported_networks: Vec<String>,
    #[serde(rename = "type")]
    pub financial_info_type: String,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct SepaFinancialDetails {
    pub account_holder_name: Secret<String>,
    pub bic: Secret<String>,
    pub country: Secret<String>,
    pub iban: Secret<String>,
    pub reference: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct BacsFinancialDetails {
    pub account_holder_name: Secret<String>,
    pub account_number: Secret<String>,
    pub sort_code: Secret<String>,
}

// REFUND :
// Type definition for Stripe RefundRequest

#[derive(Debug, Serialize)]
pub struct RefundRequest {
    pub amount: Option<MinorUnit>, //amount in cents, hence passed as integer
    pub payment_intent: String,
    #[serde(flatten)]
    pub meta_data: StripeMetadata,
}

impl<F> TryFrom<(&RefundsRouterData<F>, MinorUnit)> for RefundRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        (item, refund_amount): (&RefundsRouterData<F>, MinorUnit),
    ) -> Result<Self, Self::Error> {
        let payment_intent = item.request.connector_transaction_id.clone();
        Ok(Self {
            amount: Some(refund_amount),
            payment_intent,
            meta_data: StripeMetadata {
                order_id: Some(item.request.refund_id.clone()),
                is_refund_id_as_reference: Some("true".to_string()),
            },
        })
    }
}

#[derive(Debug, Serialize)]
pub struct ChargeRefundRequest {
    pub charge: String,
    pub refund_application_fee: Option<bool>,
    pub reverse_transfer: Option<bool>,
    pub amount: Option<MinorUnit>, //amount in cents, hence passed as integer
    #[serde(flatten)]
    pub meta_data: StripeMetadata,
}

impl<F> TryFrom<&RefundsRouterData<F>> for ChargeRefundRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(item: &RefundsRouterData<F>) -> Result<Self, Self::Error> {
        let amount = item.request.minor_refund_amount;
        match item.request.split_refunds.as_ref() {
            None => Err(ConnectorError::MissingRequiredField {
                field_name: "split_refunds",
            }
            .into()),

            Some(split_refunds) => match split_refunds {
                SplitRefundsRequest::StripeSplitRefund(stripe_refund) => {
                    let (refund_application_fee, reverse_transfer) = match &stripe_refund.options {
                        ChargeRefundsOptions::Direct(DirectChargeRefund {
                            revert_platform_fee,
                        }) => (Some(*revert_platform_fee), None),
                        ChargeRefundsOptions::Destination(DestinationChargeRefund {
                            revert_platform_fee,
                            revert_transfer,
                        }) => (Some(*revert_platform_fee), Some(*revert_transfer)),
                    };

                    Ok(Self {
                        charge: stripe_refund.charge_id.clone(),
                        refund_application_fee,
                        reverse_transfer,
                        amount: Some(amount),
                        meta_data: StripeMetadata {
                            order_id: Some(item.request.refund_id.clone()),
                            is_refund_id_as_reference: Some("true".to_string()),
                        },
                    })
                }
                _ => Err(ConnectorError::MissingRequiredField {
                    field_name: "stripe_split_refund",
                })?,
            },
        }
    }
}

// Type definition for Stripe Refund Response

#[derive(Default, Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum RefundStatus {
    Succeeded,
    Failed,
    #[default]
    Pending,
    RequiresAction,
}

impl From<RefundStatus> for enums::RefundStatus {
    fn from(item: RefundStatus) -> Self {
        match item {
            RefundStatus::Succeeded => Self::Success,
            RefundStatus::Failed => Self::Failure,
            RefundStatus::Pending => Self::Pending,
            RefundStatus::RequiresAction => Self::ManualReview,
        }
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RefundResponse {
    pub id: String,
    pub object: String,
    pub amount: MinorUnit,
    pub currency: String,
    pub metadata: StripeMetadata,
    pub payment_intent: String,
    pub status: RefundStatus,
    pub failure_reason: Option<String>,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = enums::RefundStatus::from(item.response.status);
        let response = if is_refund_failure(refund_status) {
            Err(hyperswitch_domain_models::router_data::ErrorResponse {
                code: consts::NO_ERROR_CODE.to_string(),
                message: item
                    .response
                    .failure_reason
                    .clone()
                    .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
                reason: item.response.failure_reason,
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: Some(item.response.id),
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        } else {
            Ok(RefundsResponseData {
                connector_refund_id: item.response.id,
                refund_status,
            })
        };

        Ok(Self {
            response,
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = enums::RefundStatus::from(item.response.status);
        let response = if is_refund_failure(refund_status) {
            Err(hyperswitch_domain_models::router_data::ErrorResponse {
                code: consts::NO_ERROR_CODE.to_string(),
                message: item
                    .response
                    .failure_reason
                    .clone()
                    .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
                reason: item.response.failure_reason,
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: Some(item.response.id),
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        } else {
            Ok(RefundsResponseData {
                connector_refund_id: item.response.id,
                refund_status,
            })
        };

        Ok(Self {
            response,
            ..item.data
        })
    }
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct ErrorDetails {
    pub code: Option<String>,
    #[serde(rename = "type")]
    pub error_type: Option<String>,
    pub message: Option<String>,
    pub param: Option<String>,
    pub decline_code: Option<String>,
    pub payment_intent: Option<PaymentIntentErrorResponse>,
    pub network_advice_code: Option<String>,
    pub network_decline_code: Option<String>,
    pub advice_code: Option<String>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct PaymentIntentErrorResponse {
    pub id: String,
}

#[derive(Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct ErrorResponse {
    pub error: ErrorDetails,
}

#[derive(Debug, Default, Eq, PartialEq, Serialize)]
pub struct StripeShippingAddress {
    #[serde(rename = "shipping[address][city]")]
    pub city: Option<String>,
    #[serde(rename = "shipping[address][country]")]
    pub country: Option<api_enums::CountryAlpha2>,
    #[serde(rename = "shipping[address][line1]")]
    pub line1: Option<Secret<String>>,
    #[serde(rename = "shipping[address][line2]")]
    pub line2: Option<Secret<String>>,
    #[serde(rename = "shipping[address][postal_code]")]
    pub zip: Option<Secret<String>>,
    #[serde(rename = "shipping[address][state]")]
    pub state: Option<Secret<String>>,
    #[serde(rename = "shipping[name]")]
    pub name: Option<Secret<String>>,
    #[serde(rename = "shipping[phone]")]
    pub phone: Option<Secret<String>>,
}

#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize)]
pub struct StripeBillingAddress {
    #[serde(rename = "payment_method_data[billing_details][email]")]
    pub email: Option<Email>,
    #[serde(rename = "payment_method_data[billing_details][address][country]")]
    pub country: Option<api_enums::CountryAlpha2>,
    #[serde(rename = "payment_method_data[billing_details][name]")]
    pub name: Option<Secret<String>>,
    #[serde(rename = "payment_method_data[billing_details][address][city]")]
    pub city: Option<String>,
    #[serde(rename = "payment_method_data[billing_details][address][line1]")]
    pub address_line1: Option<Secret<String>>,
    #[serde(rename = "payment_method_data[billing_details][address][line2]")]
    pub address_line2: Option<Secret<String>>,
    #[serde(rename = "payment_method_data[billing_details][address][postal_code]")]
    pub zip_code: Option<Secret<String>>,
    #[serde(rename = "payment_method_data[billing_details][address][state]")]
    pub state: Option<Secret<String>>,
    #[serde(rename = "payment_method_data[billing_details][phone]")]
    pub phone: Option<Secret<String>>,
}

#[derive(Debug, Clone, serde::Deserialize, Eq, PartialEq)]
pub struct StripeRedirectResponse {
    pub payment_intent: Option<String>,
    pub payment_intent_client_secret: Option<Secret<String>>,
    pub source_redirect_slug: Option<String>,


// Function: set_stored_card_response
// File: crates/router/src/core/payment_methods/tokenize/card_executor.rs
// Module: router

pub fn set_stored_card_response(
        self,
        store_card_response: &'a StoreLockerResponse,
    ) -> NetworkTokenizationBuilder<'a, CardStored>


// Struct: AuthorizedotnetPaymentCancelOrCaptureRequest
// File: crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AuthorizedotnetPaymentCancelOrCaptureRequest


// Function: get_net_amount
// File: crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
// Module: hyperswitch_domain_models

pub fn get_net_amount(&self) -> MinorUnit


// Implementation: impl api::PaymentCapture for for Paybox
// File: crates/hyperswitch_connectors/src/connectors/paybox.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentCapture for for Paybox


// File: crates/router/tests/connectors/checkbook.rs
// Module: router

use std::str::FromStr;

use hyperswitch_domain_models::address::{Address, AddressDetails};
use masking::Secret;
use router::types::{self, api, storage::enums, Email};

use crate::utils::{self, ConnectorActions};

#[derive(Clone, Copy)]
struct CheckbookTest;
impl ConnectorActions for CheckbookTest {}
impl utils::Connector for CheckbookTest {
    fn get_data(&self) -> api::ConnectorData {
        use router::connector::Checkbook;
        utils::construct_connector_data_old(
            Box::new(Checkbook::new()),
            types::Connector::Checkbook,
            api::GetToken::Connector,
            None,
        )
    }

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        types::ConnectorAuthType::BodyKey {
            key1: Secret::new("dummy_publishable_key".to_string()),
            api_key: Secret::new("dummy_secret_key".to_string()),
        }
    }

    fn get_name(&self) -> String {
        "checkbook".to_string()
    }
}

static CONNECTOR: CheckbookTest = CheckbookTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    Some(utils::PaymentInfo {
        address: Some(types::PaymentAddress::new(
            None,
            None,
            Some(Address {
                address: Some(AddressDetails {
                    first_name: Some(Secret::new("John".to_string())),
                    last_name: Some(Secret::new("Doe".to_string())),
                    ..Default::default()
                }),
                phone: None,
                email: Some(Email::from_str("abc@gmail.com").unwrap()),
            }),
            None,
        )),
        ..Default::default()
    })
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Creates a payment.
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::AuthenticationPending);
}

// Synchronizes a payment.
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::AuthenticationPending,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::AuthenticationPending);
}

// Voids a payment.
#[actix_web::test]
async fn should_void_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}


// Struct: GooglePayPaymentMethodDetails
// File: crates/hyperswitch_domain_models/src/router_data.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct GooglePayPaymentMethodDetails


// Struct: NexinetsErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NexinetsErrorResponse


// Implementation: impl ConnectorValidation for for Dwolla
// File: crates/hyperswitch_connectors/src/connectors/dwolla.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ConnectorValidation for for Dwolla


// Struct: PazeToken
// File: crates/hyperswitch_domain_models/src/router_data.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct PazeToken


// Function: get_frm_metrics
// File: crates/router/src/analytics.rs
// Module: router

// Documentation: Panics if `json_payload` array does not contain one `GetFrmMetricRequest` element.

pub fn get_frm_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetFrmMetricRequest; 1]>,
    ) -> impl Responder