File: crates/hyperswitch_connectors/src/connectors/nordea/transformers.rs
 Public functions: 1
 Public structs: 2

use std::collections::HashMap;

use common_utils::{pii, request::Method, types::StringMajorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{BankDebitData, PaymentMethodData},
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{Authorize, PreProcessing},
    router_request_types::{PaymentsAuthorizeData, PaymentsPreProcessingData, ResponseId},
    router_response_types::{PaymentsResponseData, RedirectForm},
    types::{
        self, AccessTokenAuthenticationRouterData, PaymentsAuthorizeRouterData,
        PaymentsPreProcessingRouterData, PaymentsSyncRouterData,
    },
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use rand::distributions::DistString;
use serde::{Deserialize, Deserializer, Serialize};

use crate::{
    connectors::nordea::{
        requests::{
            AccessScope, AccountNumber, AccountType, CreditorAccount, CreditorBank, DebitorAccount,
            GrantType, NordeaOAuthExchangeRequest, NordeaOAuthRequest,
            NordeaPaymentsConfirmRequest, NordeaPaymentsRequest, NordeaRouterData, PaymentsUrgency,
        },
        responses::{
            NordeaErrorBody, NordeaFailures, NordeaOAuthExchangeResponse, NordeaPaymentStatus,
            NordeaPaymentsConfirmResponse, NordeaPaymentsInitiateResponse,
        },
    },
    types::{PaymentsSyncResponseRouterData, ResponseRouterData},
    utils::{self, get_unimplemented_payment_method_error_message, RouterData as _},
};

type Error = error_stack::Report<errors::ConnectorError>;

impl<T> From<(StringMajorUnit, T)> for NordeaRouterData<T> {
    fn from((amount, item): (StringMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}

// Auth Struct
#[derive(Debug)]
pub struct NordeaAuthType {
    pub(super) client_id: Secret<String>,
    pub(super) client_secret: Secret<String>,
    /// PEM format private key for eIDAS signing
    /// Should be base64 encoded
    pub(super) eidas_private_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for NordeaAuthType {
    type Error = Error;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => Ok(Self {
                client_id: key1.to_owned(),
                client_secret: api_key.to_owned(),
                eidas_private_key: api_secret.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct NordeaConnectorMetadataObject {
    /// Account number of the beneficiary (merchant)
    pub destination_account_number: Secret<String>,
    /// Account type (example: IBAN, BBAN_SE, BBAN_DK, BBAN_NO, BGNR, PGNR, GIRO_DK, BBAN_OTHER)
    pub account_type: String,
    /// Name of the beneficiary (merchant)
    pub merchant_name: Secret<String>,
}

impl TryFrom<&Option<pii::SecretSerdeValue>> for NordeaConnectorMetadataObject {
    type Error = Error;
    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: "merchant_connector_account.metadata",
            })?;
        Ok(metadata)
    }
}

impl TryFrom<&AccessTokenAuthenticationRouterData> for NordeaOAuthRequest {
    type Error = Error;
    fn try_from(item: &AccessTokenAuthenticationRouterData) -> Result<Self, Self::Error> {
        let country = item.get_billing_country()?;

        // Set refresh_token maximum expiry duration to 180 days (259200 / 60 = 180)
        // Minimum is 1 minute
        let duration = Some(259200);
        let maximum_transaction_history = Some(18);
        let redirect_uri = "https://hyperswitch.io".to_string();
        let scope = [
            AccessScope::AccountsBasic,
            AccessScope::AccountsDetails,
            AccessScope::AccountsBalances,
            AccessScope::AccountsTransactions,
            AccessScope::PaymentsMultiple,
        ]
        .to_vec();
        let state = rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 15);

        Ok(Self {
            country,
            duration,
            maximum_transaction_history,
            redirect_uri,
            scope,
            state: state.into(),
        })
    }
}

impl TryFrom<&types::RefreshTokenRouterData> for NordeaOAuthExchangeRequest {
    type Error = Error;
    fn try_from(item: &types::RefreshTokenRouterData) -> Result<Self, Self::Error> {
        let code = item
            .request
            .authentication_token
            .as_ref()
            .ok_or(errors::ConnectorError::MissingRequiredField {
                field_name: "authorization_code",
            })?
            .code
            .clone();
        let grant_type = GrantType::AuthorizationCode;
        let redirect_uri = Some("https://hyperswitch.io".to_string());

        Ok(Self {
            code: Some(code),
            grant_type,
            redirect_uri,
            refresh_token: None, // We're not using refresh_token to generate new access_token
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, NordeaOAuthExchangeResponse, T, AccessToken>>
    for RouterData<F, T, AccessToken>
{
    type Error = Error;
    fn try_from(
        item: ResponseRouterData<F, NordeaOAuthExchangeResponse, T, AccessToken>,
    ) -> Result<Self, Self::Error> {
        let access_token =
            item.response
                .access_token
                .ok_or(errors::ConnectorError::MissingRequiredField {
                    field_name: "access_token",
                })?;

        let expires_in = item.response.expires_in.unwrap_or(3600); // Default to 1 hour if not provided

        Ok(Self {
            status: common_enums::AttemptStatus::AuthenticationSuccessful,
            response: Ok(AccessToken {
                token: access_token.clone(),
                expires: expires_in,
            }),
            ..item.data
        })
    }
}

impl TryFrom<&str> for AccountType {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value.to_uppercase().as_str() {
            "IBAN" => Ok(Self::Iban),
            "BBAN_SE" => Ok(Self::BbanSe),
            "BBAN_DK" => Ok(Self::BbanDk),
            "BBAN_NO" => Ok(Self::BbanNo),
            "BGNR" => Ok(Self::Bgnr),
            "PGNR" => Ok(Self::Pgnr),
            "GIRO_DK" => Ok(Self::GiroDk),
            "BBAN_OTHER" => Ok(Self::BbanOther),
            _ => Err(errors::ConnectorError::InvalidConnectorConfig {
                config: "account_type",
            }
            .into()),
        }
    }
}

impl<'de> Deserialize<'de> for PaymentsUrgency {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?.to_lowercase();
        match s.as_str() {
            "standard" => Ok(Self::Standard),
            "express" => Ok(Self::Express),
            "sameday" => Ok(Self::Sameday),
            _ => Err(serde::de::Error::unknown_variant(
                &s,
                &["standard", "express", "sameday"],
            )),
        }
    }
}

fn get_creditor_account_from_metadata(
    router_data: &PaymentsPreProcessingRouterData,
) -> Result<CreditorAccount, Error> {
    let metadata: NordeaConnectorMetadataObject =
        utils::to_connector_meta_from_secret(router_data.connector_meta_data.clone())
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "merchant_connector_account.metadata",
            })?;
    let creditor_account = CreditorAccount {
        account: AccountNumber {
            account_type: AccountType::try_from(metadata.account_type.as_str())
                .unwrap_or(AccountType::Iban),
            currency: router_data.request.currency,
            value: metadata.destination_account_number,
        },
        country: router_data.get_optional_billing_country(),
        // Merchant is the beneficiary in this case
        name: Some(metadata.merchant_name),
        message: router_data
            .description
            .as_ref()
            .map(|desc| desc.chars().take(20).collect::<String>()),
        bank: Some(CreditorBank {
            address: None,
            bank_code: None,
            bank_name: None,
            business_identifier_code: None,
            country: router_data.get_billing_country()?,
        }),
        creditor_address: None,
        // Either Reference or Message must be supplied in the request
        reference: None,
    };
    Ok(creditor_account)
}

impl TryFrom<&NordeaRouterData<&PaymentsPreProcessingRouterData>> for NordeaPaymentsRequest {
    type Error = Error;
    fn try_from(
        item: &NordeaRouterData<&PaymentsPreProcessingRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            Some(PaymentMethodData::BankDebit(bank_debit_data)) => match bank_debit_data {
                BankDebitData::SepaBankDebit { iban, .. } => {
                    let creditor_account = get_creditor_account_from_metadata(item.router_data)?;
                    let debitor_account = DebitorAccount {
                        account: AccountNumber {
                            account_type: AccountType::Iban,
                            currency: item.router_data.request.currency,
                            value: iban,
                        },
                        message: item
                            .router_data
                            .description
                            .as_ref()
                            .map(|desc| desc.chars().take(20).collect::<String>()),
                    };

                    let instructed_amount = super::requests::InstructedAmount {
                        amount: item.amount.clone(),
                        currency: item.router_data.request.currency.ok_or(
                            errors::ConnectorError::MissingRequiredField {
                                field_name: "amount",
                            },
                        )?,
                    };

                    Ok(Self {
                        creditor_account,
                        debitor_account,
                        end_to_end_identification: None,
                        external_id: Some(item.router_data.connector_request_reference_id.clone()),
                        instructed_amount,
                        recurring: None,
                        request_availability_of_funds: None,
                        requested_execution_date: None,
                        tpp_messages: None,
                        urgency: None,
                    })
                }
                BankDebitData::AchBankDebit { .. }
                | BankDebitData::BacsBankDebit { .. }
                | BankDebitData::BecsBankDebit { .. } => {
                    Err(errors::ConnectorError::NotImplemented(
                        get_unimplemented_payment_method_error_message("Nordea"),
                    )
                    .into())
                }
            },
            Some(PaymentMethodData::CardRedirect(_))
            | Some(PaymentMethodData::CardDetailsForNetworkTransactionId(_))
            | Some(PaymentMethodData::Wallet(_))
            | Some(PaymentMethodData::PayLater(_))
            | Some(PaymentMethodData::BankRedirect(_))
            | Some(PaymentMethodData::BankTransfer(_))
            | Some(PaymentMethodData::Crypto(_))
            | Some(PaymentMethodData::MandatePayment)
            | Some(PaymentMethodData::Reward)
            | Some(PaymentMethodData::RealTimePayment(_))
            | Some(PaymentMethodData::MobilePayment(_))
            | Some(PaymentMethodData::Upi(_))
            | Some(PaymentMethodData::Voucher(_))
            | Some(PaymentMethodData::GiftCard(_))
            | Some(PaymentMethodData::OpenBanking(_))
            | Some(PaymentMethodData::CardToken(_))
            | Some(PaymentMethodData::NetworkToken(_))
            | Some(PaymentMethodData::Card(_))
            | None => {
                Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into())
            }
        }
    }
}

impl TryFrom<&NordeaRouterData<&PaymentsAuthorizeRouterData>> for NordeaPaymentsConfirmRequest {
    type Error = Error;
    fn try_from(
        item: &NordeaRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let payment_ids = match &item.router_data.response {
            Ok(response_data) => response_data
                .get_connector_transaction_id()
                .map_err(|_| errors::ConnectorError::MissingConnectorTransactionID)?,
            Err(_) => return Err(errors::ConnectorError::ResponseDeserializationFailed.into()),
        };

        Ok(Self {
            authentication_method: None,
            language: None,
            payments_ids: vec![payment_ids],
            redirect_url: None,
            state: None,
        })
    }
}

impl From<NordeaPaymentStatus> for common_enums::AttemptStatus {
    fn from(item: NordeaPaymentStatus) -> Self {
        match item {
            NordeaPaymentStatus::Confirmed | NordeaPaymentStatus::Paid => Self::Charged,

            NordeaPaymentStatus::PendingConfirmation
            | NordeaPaymentStatus::PendingSecondConfirmation => Self::ConfirmationAwaited,
            NordeaPaymentStatus::PendingUserApproval => Self::AuthenticationPending,

            NordeaPaymentStatus::OnHold | NordeaPaymentStatus::Unknown => Self::Pending,

            NordeaPaymentStatus::Rejected
            | NordeaPaymentStatus::InsufficientFunds
            | NordeaPaymentStatus::LimitExceeded
            | NordeaPaymentStatus::UserApprovalFailed
            | NordeaPaymentStatus::UserApprovalTimeout
            | NordeaPaymentStatus::UserApprovalCancelled => Self::Failure,
        }
    }
}

pub fn get_error_data(error_response: Option<&NordeaErrorBody>) -> Option<&NordeaFailures> {
    error_response
        .and_then(|error| error.nordea_failures.as_ref())
        .and_then(|failures| failures.first())
}

// Helper function to convert NordeaPaymentsInitiateResponse to common response data
fn convert_nordea_payment_response(
    response: &NordeaPaymentsInitiateResponse,
) -> Result<(PaymentsResponseData, common_enums::AttemptStatus), Error> {
    let payment_response = response
        .payments_response
        .as_ref()
        .ok_or(errors::ConnectorError::ResponseHandlingFailed)?;

    let resource_id = ResponseId::ConnectorTransactionId(payment_response.payment_id.clone());

    let response_data = PaymentsResponseData::TransactionResponse {
        resource_id,
        redirection_data: Box::new(None),
        mandate_reference: Box::new(None),
        connector_metadata: None,
        network_txn_id: None,
        connector_response_reference_id: payment_response.external_id.clone(),
        incremental_authorization_allowed: None,
        charges: None,
    };

    let status = common_enums::AttemptStatus::from(payment_response.payment_status.clone());

    Ok((response_data, status))
}

impl
    TryFrom<
        ResponseRouterData<
            PreProcessing,
            NordeaPaymentsInitiateResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
    > for RouterData<PreProcessing, PaymentsPreProcessingData, PaymentsResponseData>
{
    type Error = Error;
    fn try_from(
        item: ResponseRouterData<
            PreProcessing,
            NordeaPaymentsInitiateResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let (response, status) = convert_nordea_payment_response(&item.response)?;
        Ok(Self {
            status,
            response: Ok(response),
            ..item.data
        })
    }
}

impl
    TryFrom<
        ResponseRouterData<
            Authorize,
            NordeaPaymentsConfirmResponse,
            PaymentsAuthorizeData,
            PaymentsResponseData,
        >,
    > for RouterData<Authorize, PaymentsAuthorizeData, PaymentsResponseData>
{
    type Error = Error;
    fn try_from(
        item: ResponseRouterData<
            Authorize,
            NordeaPaymentsConfirmResponse,
            PaymentsAuthorizeData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        // First check if there are any errors in the response
        if let Some(errors) = &item.response.errors {
            if !errors.is_empty() {
                // Get the first error for the error response
                let first_error = errors
                    .first()
                    .ok_or(errors::ConnectorError::ResponseHandlingFailed)?;

                return Ok(Self {
                    status: common_enums::AttemptStatus::Failure,
                    response: Err(ErrorResponse {
                        code: first_error
                            .error
                            .clone()
                            .unwrap_or_else(|| "UNKNOWN_ERROR".to_string()),
                        message: first_error
                            .error_description
                            .clone()
                            .unwrap_or_else(|| "Payment confirmation failed".to_string()),
                        reason: first_error.error_description.clone(),
                        status_code: item.http_code,
                        attempt_status: Some(common_enums::AttemptStatus::Failure),
                        connector_transaction_id: first_error.payment_id.clone(),
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    }),
                    ..item.data
                });
            }
        }

        // If no errors, proceed with normal response handling
        // Check if there's a redirect link at the top level only
        let redirection_data = item
            .response
            .links
            .as_ref()
            .and_then(|links| {
                links.iter().find(|link| {
                    link.rel
                        .as_ref()
                        .map(|rel| rel == "signing")
                        .unwrap_or(false)
                })
            })
            .and_then(|link| link.href.clone())
            .map(|redirect_url| RedirectForm::Form {
                endpoint: redirect_url,
                method: Method::Get,
                form_fields: HashMap::new(),
            });

        let (response, status) = match &item.response.nordea_payments_response {
            Some(payment_response_wrapper) => {
                // Get the first payment from the payments array
                let payment = payment_response_wrapper
                    .payments
                    .first()
                    .ok_or(errors::ConnectorError::ResponseHandlingFailed)?;

                let resource_id = ResponseId::ConnectorTransactionId(payment.payment_id.clone());

                let response = Ok(PaymentsResponseData::TransactionResponse {
                    resource_id,
                    redirection_data: Box::new(redirection_data),
                    mandate_reference: Box::new(None),
                    connector_metadata: None,
                    network_txn_id: None,
                    connector_response_reference_id: payment.external_id.clone(),
                    incremental_authorization_allowed: None,
                    charges: None,
                });

                let status = common_enums::AttemptStatus::from(payment.payment_status.clone());

                (response, status)
            }
            None => {
                // No payment response, but we might still have a redirect link
                if let Some(redirect) = redirection_data {
                    let response = Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::NoResponseId,
                        redirection_data: Box::new(Some(redirect)),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: None,
                        incremental_authorization_allowed: None,
                        charges: None,
                    });
                    (response, common_enums::AttemptStatus::AuthenticationPending)
                } else {
                    return Err(errors::ConnectorError::ResponseHandlingFailed.into());
                }
            }
        };

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

impl TryFrom<PaymentsSyncResponseRouterData<NordeaPaymentsInitiateResponse>>
    for PaymentsSyncRouterData
{
    type Error = Error;
    fn try_from(
        item: PaymentsSyncResponseRouterData<NordeaPaymentsInitiateResponse>,
    ) -> Result<Self, Self::Error> {
        let (response, status) = convert_nordea_payment_response(&item.response)?;
        Ok(Self {
            status,
            response: Ok(response),
            ..item.data
        })
    }
}


pub struct PayloadRefundRequest {
    #[serde(rename = "type")]
    pub transaction_type: TransactionTypes,
    pub amount: StringMajorUnit,
    #[serde(rename = "ledger[0][assoc_transaction_id]")]
    pub ledger_assoc_transaction_id: String,
}


pub async fn get_or_update_dispute_object(
    state: SessionState,
    option_dispute: Option<diesel_models::dispute::Dispute>,
    dispute_details: api::disputes::DisputePayload,
    merchant_id: &common_utils::id_type::MerchantId,
    organization_id: &common_utils::id_type::OrganizationId,
    payment_attempt: &PaymentAttempt,
    dispute_status: common_enums::enums::DisputeStatus,
    business_profile: &domain::Profile,
    connector_name: &str,
) -> CustomResult<diesel_models::dispute::Dispute, errors::ApiErrorResponse> {
    let db = &*state.store;
    match option_dispute {
        None => {
            metrics::INCOMING_DISPUTE_WEBHOOK_NEW_RECORD_METRIC.add(1, &[]);
            let dispute_id = generate_id(consts::ID_LENGTH, "dp");
            let new_dispute = diesel_models::dispute::DisputeNew {
                dispute_id,
                amount: dispute_details.amount.clone(),
                currency: dispute_details.currency.to_string(),
                dispute_stage: dispute_details.dispute_stage,
                dispute_status,
                payment_id: payment_attempt.payment_id.to_owned(),
                connector: connector_name.to_owned(),
                attempt_id: payment_attempt.attempt_id.to_owned(),
                merchant_id: merchant_id.to_owned(),
                connector_status: dispute_details.connector_status,
                connector_dispute_id: dispute_details.connector_dispute_id,
                connector_reason: dispute_details.connector_reason,
                connector_reason_code: dispute_details.connector_reason_code,
                challenge_required_by: dispute_details.challenge_required_by,
                connector_created_at: dispute_details.created_at,
                connector_updated_at: dispute_details.updated_at,
                profile_id: Some(business_profile.get_id().to_owned()),
                evidence: None,
                merchant_connector_id: payment_attempt.merchant_connector_id.clone(),
                dispute_amount: StringMinorUnitForConnector::convert_back(
                    &StringMinorUnitForConnector,
                    dispute_details.amount,
                    dispute_details.currency,
                )
                .change_context(
                    errors::ApiErrorResponse::AmountConversionFailed {
                        amount_type: "MinorUnit",
                    },
                )?,
                organization_id: organization_id.clone(),
                dispute_currency: Some(dispute_details.currency),
            };
            state
                .store
                .insert_dispute(new_dispute.clone())
                .await
                .to_not_found_response(errors::ApiErrorResponse::WebhookResourceNotFound)
        }
        Some(dispute) => {
            logger::info!("Dispute Already exists, Updating the dispute details");
            metrics::INCOMING_DISPUTE_WEBHOOK_UPDATE_RECORD_METRIC.add(1, &[]);
            core_utils::validate_dispute_stage_and_dispute_status(
                dispute.dispute_stage,
                dispute.dispute_status,
                dispute_details.dispute_stage,
                dispute_status,
            )
            .change_context(errors::ApiErrorResponse::WebhookProcessingFailure)
            .attach_printable("dispute stage and status validation failed")?;
            let update_dispute = diesel_models::dispute::DisputeUpdate::Update {
                dispute_stage: dispute_details.dispute_stage,
                dispute_status,
                connector_status: dispute_details.connector_status,
                connector_reason: dispute_details.connector_reason,
                connector_reason_code: dispute_details.connector_reason_code,
                challenge_required_by: dispute_details.challenge_required_by,
                connector_updated_at: dispute_details.updated_at,
            };
            db.update_dispute(dispute, update_dispute)
                .await
                .to_not_found_response(errors::ApiErrorResponse::WebhookResourceNotFound)
        }
    }
}


impl ConnectorValidation for Wellsfargopayout {
    //TODO: implement functions when support enabled
}

impl ConnectorValidation for for Wellsfargopayout


pub struct BarclaycardRedirectionAuthResponse {
    pub transaction_id: String,
}


File: crates/hyperswitch_interfaces/src/lib.rs

//! Hyperswitch interface
#![warn(missing_docs, missing_debug_implementations)]

pub mod api;
pub mod authentication;
/// Configuration related functionalities
pub mod configs;
/// Connector integration interface module
pub mod connector_integration_interface;
/// definition of the new connector integration trait
pub mod connector_integration_v2;
/// Constants used throughout the application
pub mod consts;
/// Conversion implementations
pub mod conversion_impls;
pub mod disputes;
pub mod encryption_interface;
pub mod errors;
pub mod events;
/// connector integrity check interface
pub mod integrity;
pub mod metrics;
pub mod secrets_interface;
pub mod types;
pub mod webhooks;

/// Crm interface
pub mod crm;


pub struct RedirectErrorMessage {
    pub errors: Option<Vec<String>>,
}


impl api::PaymentVoid for Moneris {}


impl api::PaymentToken for Rapyd {}


pub struct Shift4AuthType {
    pub(super) api_key: Secret<String>,
}


pub struct AuthEventFilterRow {
    pub authentication_status: Option<DBEnumWrapper<AuthenticationStatus>>,
    pub trans_status: Option<DBEnumWrapper<TransactionStatus>>,
    pub authentication_type: Option<DBEnumWrapper<DecoupledAuthenticationType>>,
    pub error_message: Option<String>,
    pub authentication_connector: Option<DBEnumWrapper<AuthenticationConnectors>>,
    pub message_version: Option<String>,
    pub acs_reference_number: Option<String>,
    pub platform: Option<String>,
    pub mcc: Option<String>,
    pub currency: Option<DBEnumWrapper<Currency>>,
    pub merchant_country: Option<String>,
    pub billing_country: Option<String>,
    pub shipping_country: Option<String>,
    pub issuer_country: Option<String>,
    pub earliest_supported_version: Option<String>,
    pub latest_supported_version: Option<String>,
    pub whitelist_decision: Option<bool>,
    pub device_manufacturer: Option<String>,
    pub device_type: Option<String>,
    pub device_brand: Option<String>,
    pub device_os: Option<String>,
    pub device_display: Option<String>,
    pub browser_name: Option<String>,
    pub browser_version: Option<String>,
    pub issuer_id: Option<String>,
    pub scheme_name: Option<String>,
    pub exemption_requested: Option<bool>,
    pub exemption_accepted: Option<bool>,
}


pub async fn find_by_payment_id_merchant_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned())),
        )
        .await
    }


impl api::MandateSetup for Custombilling {}

impl api::MandateSetup for for Custombilling


pub fn remove_quotes_for_integer_values(
    connector_name: &str,
) -> Result<(&str, Option<String>), io::Error> {
    let collection_path = get_collection_path(connector_name);
    let collection_dir_path = get_dir_path(connector_name);

    let values_to_replace = [
        "amount",
        "another_random_number",
        "capture_amount",
        "random_number",
        "refund_amount",
    ];

    export_collection(connector_name, collection_dir_path);

    let mut contents = fs::read_to_string(&collection_path)?;
    for value_to_replace in values_to_replace {
        if let Ok(re) = Regex::new(&format!(
            r#"\\"(?P<field>\{{\{{{value_to_replace}\}}\}})\\""#,
        )) {
            contents = re.replace_all(&contents, "$field").to_string();
        } else {
            eprintln!("Regex validation failed.");
        }

        let mut file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .open(&collection_path)?;

        file.write_all(contents.as_bytes())?;
    }

    Ok((connector_name, Some(collection_path)))
}


pub async fn perform_session_token_routing<F, D>(
    state: SessionState,
    merchant_context: &domain::MerchantContext,
    business_profile: &domain::Profile,
    payment_data: &mut D,
    connectors: api::SessionConnectorDatas,
) -> RouterResult<api::SessionConnectorDatas>
where
    F: Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F>,
{
    let chosen = connectors.apply_filter_for_session_routing();
    let sfr = SessionFlowRoutingInput {
        state: &state,
        country: payment_data
            .get_address()
            .get_payment_method_billing()
            .and_then(|address| address.address.as_ref())
            .and_then(|details| details.country),
        key_store: merchant_context.get_merchant_key_store(),
        merchant_account: merchant_context.get_merchant_account(),
        payment_attempt: payment_data.get_payment_attempt(),
        payment_intent: payment_data.get_payment_intent(),
        chosen,
    };
    let (result, routing_approach) = self_routing::perform_session_flow_routing(
        sfr,
        business_profile,
        &enums::TransactionType::Payment,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("error performing session flow routing")?;

    payment_data.set_routing_approach_in_attempt(routing_approach);

    let final_list = connectors.filter_and_validate_for_session_flow(&result)?;

    Ok(final_list)
}


impl MerchantConnectorUpdate {
    pub fn get_frm_config_as_secret(&self) -> Option<Vec<Secret<serde_json::Value>>> {
        match self.frm_configs.as_ref() {
            Some(frm_value) => {
                let configs_for_frm_value: Vec<Secret<serde_json::Value>> = frm_value
                    .iter()
                    .map(|config| config.encode_to_value().map(Secret::new))
                    .collect::<Result<Vec<_>, _>>()
                    .ok()?;
                Some(configs_for_frm_value)
            }
            None => None,
        }
    }
}


impl super::api_event::filters::ApiEventFilterAnalytics for ClickhouseClient {}

impl super::api_event::filters::ApiEventFilterAnalytics for for ClickhouseClient


impl PayoutLink {
    pub fn server(state: AppState) -> Scope {
        let mut route = web::scope("/payout_link").app_data(web::Data::new(state));
        route = route.service(
            web::resource("/{merchant_id}/{payout_id}").route(web::get().to(render_payout_link)),
        );
        route
    }
}


File: crates/scheduler/src/configs/defaults.rs

impl Default for super::settings::SchedulerSettings {
    fn default() -> Self {
        Self {
            stream: "SCHEDULER_STREAM".into(),
            producer: super::settings::ProducerSettings::default(),
            consumer: super::settings::ConsumerSettings::default(),
            graceful_shutdown_interval: 60000,
            loop_interval: 5000,
            server: super::settings::Server::default(),
        }
    }
}

impl Default for super::settings::ProducerSettings {
    fn default() -> Self {
        Self {
            upper_fetch_limit: 0,
            lower_fetch_limit: 1800,
            lock_key: "PRODUCER_LOCKING_KEY".into(),
            lock_ttl: 160,
            batch_size: 200,
        }
    }
}

impl Default for super::settings::ConsumerSettings {
    fn default() -> Self {
        Self {
            disabled: false,
            consumer_group: "SCHEDULER_GROUP".into(),
        }
    }
}

impl Default for super::settings::Server {
    fn default() -> Self {
        Self {
            port: 8080,
            workers: num_cpus::get_physical(),
            host: "localhost".into(),
        }
    }
}


impl UnifiedAuthenticationService for ClickToPay {
    fn get_pre_authentication_request_data(
        _payment_method_data: Option<&domain::PaymentMethodData>,
        service_details: Option<payments::CtpServiceDetails>,
        amount: common_utils::types::MinorUnit,
        currency: Option<common_enums::Currency>,
        merchant_details: Option<&hyperswitch_domain_models::router_request_types::unified_authentication_service::MerchantDetails>,
        billing_address: Option<&hyperswitch_domain_models::address::Address>,
        acquirer_bin: Option<String>,
        acquirer_merchant_id: Option<String>,
    ) -> RouterResult<UasPreAuthenticationRequestData> {
        let domain_service_details = hyperswitch_domain_models::router_request_types::unified_authentication_service::CtpServiceDetails {
            service_session_ids: Some(ServiceSessionIds {
                merchant_transaction_id: service_details
                    .as_ref()
                    .and_then(|details| details.merchant_transaction_id.clone()),
                correlation_id: service_details
                    .as_ref()
                    .and_then(|details| details.correlation_id.clone()),
                x_src_flow_id: service_details
                    .as_ref()
                    .and_then(|details| details.x_src_flow_id.clone()),
            }),
            payment_details: None,
        };

        let transaction_details = TransactionDetails {
            amount: Some(amount),
            currency,
            device_channel: None,
            message_category: None,
        };

        let authentication_info = Some(AuthenticationInfo {
            authentication_type: None,
            authentication_reasons: None,
            consent_received: false, // This is not relevant in this flow so keeping it as false
            is_authenticated: false, // This is not relevant in this flow so keeping it as false
            locale: None,
            supported_card_brands: None,
            encrypted_payload: service_details
                .as_ref()
                .and_then(|details| details.encrypted_payload.clone()),
        });
        Ok(UasPreAuthenticationRequestData {
            service_details: Some(domain_service_details),
            transaction_details: Some(transaction_details),
            payment_details: None,
            authentication_info,
            merchant_details: merchant_details.cloned(),
            billing_address: billing_address.cloned(),
            acquirer_bin,
            acquirer_merchant_id,
        })
    }

    async fn pre_authentication(
        state: &SessionState,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: Option<&common_utils::id_type::PaymentId>,
        payment_method_data: Option<&domain::PaymentMethodData>,
        merchant_connector_account: &MerchantConnectorAccountType,
        connector_name: &str,
        authentication_id: &common_utils::id_type::AuthenticationId,
        payment_method: common_enums::PaymentMethod,
        amount: common_utils::types::MinorUnit,
        currency: Option<common_enums::Currency>,
        service_details: Option<payments::CtpServiceDetails>,
        merchant_details: Option<&hyperswitch_domain_models::router_request_types::unified_authentication_service::MerchantDetails>,
        billing_address: Option<&hyperswitch_domain_models::address::Address>,
        acquirer_bin: Option<String>,
        acquirer_merchant_id: Option<String>,
    ) -> RouterResult<UasPreAuthenticationRouterData> {
        let pre_authentication_data = Self::get_pre_authentication_request_data(
            payment_method_data,
            service_details,
            amount,
            currency,
            merchant_details,
            billing_address,
            acquirer_bin,
            acquirer_merchant_id,
        )?;

        let pre_auth_router_data: UasPreAuthenticationRouterData =
            utils::construct_uas_router_data(
                state,
                connector_name.to_string(),
                payment_method,
                merchant_id.clone(),
                None,
                pre_authentication_data,
                merchant_connector_account,
                Some(authentication_id.to_owned()),
                payment_id.cloned(),
            )?;

        Box::pin(utils::do_auth_connector_call(
            state,
            UNIFIED_AUTHENTICATION_SERVICE.to_string(),
            pre_auth_router_data,
        ))
        .await
    }

    async fn post_authentication(
        state: &SessionState,
        _business_profile: &domain::Profile,
        payment_id: Option<&common_utils::id_type::PaymentId>,
        merchant_connector_account: &MerchantConnectorAccountType,
        connector_name: &str,
        authentication_id: &common_utils::id_type::AuthenticationId,
        payment_method: common_enums::PaymentMethod,
        merchant_id: &common_utils::id_type::MerchantId,
        _authentication: Option<&Authentication>,
    ) -> RouterResult<UasPostAuthenticationRouterData> {
        let post_authentication_data = UasPostAuthenticationRequestData {
            threeds_server_transaction_id: None,
        };

        let post_auth_router_data: UasPostAuthenticationRouterData =
            utils::construct_uas_router_data(
                state,
                connector_name.to_string(),
                payment_method,
                merchant_id.clone(),
                None,
                post_authentication_data,
                merchant_connector_account,
                Some(authentication_id.to_owned()),
                payment_id.cloned(),
            )?;

        utils::do_auth_connector_call(
            state,
            UNIFIED_AUTHENTICATION_SERVICE.to_string(),
            post_auth_router_data,
        )
        .await
    }

    async fn confirmation(
        state: &SessionState,
        authentication_id: Option<&common_utils::id_type::AuthenticationId>,
        currency: Option<common_enums::Currency>,
        status: common_enums::AttemptStatus,
        service_details: Option<payments::CtpServiceDetails>,
        merchant_connector_account: &MerchantConnectorAccountType,
        connector_name: &str,
        payment_method: common_enums::PaymentMethod,
        net_amount: common_utils::types::MinorUnit,
        payment_id: Option<&common_utils::id_type::PaymentId>,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> RouterResult<()> {
        let authentication_id = authentication_id
            .ok_or(ApiErrorResponse::InternalServerError)
            .attach_printable("Missing authentication id in tracker")?;

        let currency = currency.ok_or(ApiErrorResponse::MissingRequiredField {
            field_name: "currency",
        })?;

        let current_time = common_utils::date_time::date_as_yyyymmddthhmmssmmmz()
            .change_context(ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to get current time")?;

        let payment_attempt_status = status;

        let (checkout_event_status, confirmation_reason) =
            utils::get_checkout_event_status_and_reason(payment_attempt_status);

        let click_to_pay_details = service_details.clone();

        let authentication_confirmation_data = UasConfirmationRequestData {
            x_src_flow_id: click_to_pay_details
                .as_ref()
                .and_then(|details| details.x_src_flow_id.clone()),
            transaction_amount: net_amount,
            transaction_currency: currency,
            checkout_event_type: Some("01".to_string()), // hardcoded to '01' since only authorise flow is implemented
            checkout_event_status: checkout_event_status.clone(),
            confirmation_status: checkout_event_status.clone(),
            confirmation_reason,
            confirmation_timestamp: Some(current_time),
            network_authorization_code: Some("01".to_string()), // hardcoded to '01' since only authorise flow is implemented
            network_transaction_identifier: Some("mastercard".to_string()), // hardcoded to 'mastercard' since only mastercard has confirmation flow requirement
            correlation_id: click_to_pay_details
                .clone()
                .and_then(|details| details.correlation_id),
            merchant_transaction_id: click_to_pay_details
                .and_then(|details| details.merchant_transaction_id),
        };

        let authentication_confirmation_router_data : hyperswitch_domain_models::types::UasAuthenticationConfirmationRouterData = utils::construct_uas_router_data(
            state,
            connector_name.to_string(),
            payment_method,
            merchant_id.clone(),
            None,
            authentication_confirmation_data,
            merchant_connector_account,
            Some(authentication_id.to_owned()),
            payment_id.cloned(),
        )?;

        utils::do_auth_connector_call(
            state,
            UNIFIED_AUTHENTICATION_SERVICE.to_string(),
            authentication_confirmation_router_data,
        )
        .await
        .ok(); // marking this as .ok() since this is not a required step at our end for completing the transaction

        Ok(())
    }
}

impl UnifiedAuthenticationService for for ClickToPay


pub struct BamboraBrowserInfo {
    accept_header: String,
    java_enabled: bool,
    language: String,
    color_depth: u8,
    screen_height: u32,
    screen_width: u32,
    time_zone: i32,
    user_agent: String,
    javascript_enabled: bool,
}


pub struct SplitRefundInput {
    pub refund_request: Option<common_types::refunds::SplitRefund>,
    pub payment_charges: Option<common_types::payments::ConnectorChargeResponseData>,
    pub split_payment_request: Option<common_types::payments::SplitPaymentsRequest>,
    pub charge_id: Option<String>,
}


pub struct CardDetails {
    pub country: Option<String>,
    pub last4: Option<String>,
    pub exp_month: Option<masking::Secret<String>>,
    pub exp_year: Option<masking::Secret<String>>,
    pub fingerprint: Option<masking::Secret<String>>,
}


impl api::MandateSetup for Zen {}


impl api::PaymentSession for Dlocal {}


pub(crate) async fn update_pt_status_based_on_attempt_status_for_payments_sync(
        &self,
        state: &SessionState,
        payment_intent: &PaymentIntent,
        process_tracker: storage::ProcessTracker,
        profile: &domain::Profile,
        merchant_context: domain::MerchantContext,
        revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
        payment_attempt: PaymentAttempt,
        revenue_recovery_metadata: &mut PaymentRevenueRecoveryMetadata,
    ) -> Result<(), errors::ProcessTrackerError> {
        let connector_customer_id = payment_intent
            .extract_connector_customer_id_from_payment_intent()
            .change_context(errors::RecoveryError::ValueNotFound)
            .attach_printable("Failed to extract customer ID from payment intent")?;

        let db = &*state.store;

        let recovery_payment_intent =
            hyperswitch_domain_models::revenue_recovery::RecoveryPaymentIntent::from(
                payment_intent,
            );

        let recovery_payment_attempt =
            hyperswitch_domain_models::revenue_recovery::RecoveryPaymentAttempt::from(
                &payment_attempt,
            );

        let recovery_payment_tuple = recovery_incoming_flow::RecoveryPaymentTuple::new(
            &recovery_payment_intent,
            &recovery_payment_attempt,
        );

        let used_token = get_payment_processor_token_id_from_payment_attempt(&payment_attempt);

        let retry_count = process_tracker.retry_count;

        match self {
            Self::Succeeded => {
                // finish psync task as the payment was a success
                db.as_scheduler()
                    .finish_process_with_business_status(
                        process_tracker,
                        business_status::PSYNC_WORKFLOW_COMPLETE,
                    )
                    .await?;
                // publish events to kafka
                if let Err(e) = recovery_incoming_flow::RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
                    state,
                    &recovery_payment_tuple,
                    Some(retry_count+1)
                )
                .await{
                    router_env::logger::error!(
                        "Failed to publish revenue recovery event to kafka: {:?}",
                        e
                    );
                };

                // update the status of token in redis
                let _update_error_code = storage::revenue_recovery_redis_operation::RedisTokenManager::update_payment_processor_token_error_code_from_process_tracker(
                    state,
                    &connector_customer_id,
                    &None,
                    // Since this is succeeded payment attempt, 'is_hard_decine' will be false.
                    &Some(false),
                    used_token.as_deref(),
                )
                .await;

                // unlocking the token
                let _unlock_the_connector_customer_id = storage::revenue_recovery_redis_operation::RedisTokenManager::unlock_connector_customer_status(
                    state,
                    &connector_customer_id,
                )
                .await;

                // Record a successful transaction back to Billing Connector
                // TODO: Add support for retrying failed outgoing recordback webhooks
                record_back_to_billing_connector(
                    state,
                    &payment_attempt,
                    payment_intent,
                    &revenue_recovery_payment_data.billing_mca,
                )
                .await
                .change_context(errors::RecoveryError::RecordBackToBillingConnectorFailed)
                .attach_printable("Failed to update the process tracker")?;
            }
            Self::Failed => {
                // finish psync task
                db.as_scheduler()
                    .finish_process_with_business_status(
                        process_tracker.clone(),
                        business_status::PSYNC_WORKFLOW_COMPLETE,
                    )
                    .await?;
                // publish events to kafka
                if let Err(e) = recovery_incoming_flow::RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
                    state,
                    &recovery_payment_tuple,
                    Some(retry_count+1)
                )
                .await{
                    router_env::logger::error!(
                        "Failed to publish revenue recovery event to kafka : {:?}", e
                    );
                };

                let error_code = recovery_payment_attempt.error_code;

                let is_hard_decline = revenue_recovery::check_hard_decline(state, &payment_attempt)
                    .await
                    .ok();

                // update the status of token in redis
                let _update_error_code = storage::revenue_recovery_redis_operation::RedisTokenManager::update_payment_processor_token_error_code_from_process_tracker(
                    state,
                    &connector_customer_id,
                    &error_code,
                    &is_hard_decline,
                    used_token.as_deref(),
                )
                .await;

                // unlocking the token
                let _unlock_the_connector_customer_id = storage::revenue_recovery_redis_operation::RedisTokenManager::unlock_connector_customer_status(
                    state,
                    &connector_customer_id,
                )
                .await;

                // Reopen calculate workflow on payment failure
                reopen_calculate_workflow_on_payment_failure(
                    state,
                    &process_tracker,
                    profile,
                    merchant_context,
                    payment_intent,
                    revenue_recovery_payment_data,
                )
                .await?;
            }
            Self::Processing => {
                // do a psync payment
                let action = Box::pin(Action::payment_sync_call(
                    state,
                    revenue_recovery_payment_data,
                    payment_intent,
                    &process_tracker,
                    profile,
                    merchant_context,
                    payment_attempt,
                ))
                .await?;

                //handle the response
                Box::pin(action.psync_response_handler(
                    state,
                    payment_intent,
                    &process_tracker,
                    revenue_recovery_metadata,
                    revenue_recovery_payment_data,
                ))
                .await?;
            }
            Self::InvalidStatus(status) => logger::debug!(
                "Invalid Attempt Status for the Recovery Payment : {}",
                status
            ),
        }
        Ok(())
    }


pub async fn merchant_account_list(
    state: web::Data<AppState>,
    req: HttpRequest,
    query_params: web::Query<api_models::admin::MerchantAccountListRequest>,
) -> HttpResponse {
    let flow = Flow::MerchantAccountList;

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        query_params.into_inner(),
        |state, auth, request, _| list_merchant_account(state, request, auth),
        auth::auth_type(
            &auth::PlatformOrgAdminAuth {
                is_admin_auth_allowed: true,
                organization_id: None,
            },
            &auth::JWTAuthMerchantFromHeader {
                required_permission: Permission::MerchantAccountRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


File: crates/hyperswitch_connectors/src/connectors/mifinity.rs
 Public functions: 1
 Public structs: 1

pub mod transformers;

use api_models::webhooks::{IncomingWebhookEvent, ObjectReferenceId};
use common_enums::enums;
use common_utils::{
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
};
use error_stack::{report, Report, ResultExt};
use hyperswitch_domain_models::{
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks::{IncomingWebhook, IncomingWebhookRequestDetails},
};
use lazy_static::lazy_static;
use masking::{ExposeInterface, Mask};
use router_env::logger;
use transformers::{self as mifinity, auth_headers};

use crate::{
    constants::{headers, CONNECTOR_UNAUTHORIZED_ERROR},
    types::ResponseRouterData,
    utils::convert_amount,
};

#[derive(Clone)]
pub struct Mifinity {
    amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}

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

impl api::Payment for Mifinity {}
impl api::PaymentSession for Mifinity {}
impl api::ConnectorAccessToken for Mifinity {}
impl api::MandateSetup for Mifinity {}
impl api::PaymentAuthorize for Mifinity {}
impl api::PaymentSync for Mifinity {}
impl api::PaymentCapture for Mifinity {}
impl api::PaymentVoid for Mifinity {}
impl api::Refund for Mifinity {}
impl api::RefundExecute for Mifinity {}
impl api::RefundSync for Mifinity {}
impl api::PaymentToken for Mifinity {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Mifinity
{
    // Not Implemented (R)
}

const API_VERSION: &str = "1";

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Mifinity
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![
            (
                headers::CONTENT_TYPE.to_string(),
                self.get_content_type().to_string().into(),
            ),
            (
                auth_headers::API_VERSION.to_string(),
                API_VERSION.to_string().into(),
            ),
        ];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }
}

impl ConnectorCommon for Mifinity {
    fn id(&self) -> &'static str {
        "mifinity"
    }

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Base
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.mifinity.base_url.as_ref()
    }

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = mifinity::MifinityAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::KEY.to_string(),
            auth.key.expose().into_masked(),
        )])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        if res.response.is_empty() {
            Ok(ErrorResponse {
                status_code: res.status_code,
                code: NO_ERROR_CODE.to_string(),
                message: NO_ERROR_MESSAGE.to_string(),
                reason: Some(CONNECTOR_UNAUTHORIZED_ERROR.to_string()),
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        } else {
            let response: Result<
                mifinity::MifinityErrorResponse,
                Report<common_utils::errors::ParsingError>,
            > = res.response.parse_struct("MifinityErrorResponse");

            match response {
                Ok(response) => {
                    event_builder.map(|i| i.set_response_body(&response));
                    router_env::logger::info!(connector_response=?response);
                    Ok(ErrorResponse {
                        status_code: res.status_code,
                        code: response
                            .errors
                            .iter()
                            .map(|error| error.error_code.clone())
                            .collect::<Vec<String>>()
                            .join(" & "),
                        message: response
                            .errors
                            .iter()
                            .map(|error| error.message.clone())
                            .collect::<Vec<String>>()
                            .join(" & "),
                        reason: Some(
                            response
                                .errors
                                .iter()
                                .map(|error| error.message.clone())
                                .collect::<Vec<String>>()
                                .join(" & "),
                        ),
                        attempt_status: None,
                        connector_transaction_id: None,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                }

                Err(error_msg) => {
                    event_builder.map(|event| event.set_error(serde_json::json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
                    logger::error!(deserialization_error =? error_msg);
                    crate::utils::handle_json_response_deserialization_failure(res, "mifinity")
                }
            }
        }
    }
}

impl ConnectorValidation for Mifinity {
    //TODO: implement functions when support enabled
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Mifinity {
    //TODO: implement sessions flow
}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Mifinity {}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
    for Mifinity
{
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Mifinity {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}pegasus-ci/api/gateway/init-iframe",
            self.base_url(connectors)
        ))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;

        let connector_router_data = mifinity::MifinityRouterData::from((amount, req));
        let connector_req = mifinity::MifinityPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsAuthorizeType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::PaymentsAuthorizeType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsAuthorizeType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: mifinity::MifinityPaymentsResponse = res
            .response
            .parse_struct("Mifinity PaymentsAuthorizeResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
    fn get_5xx_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Mifinity {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let merchant_id = &req.merchant_id;
        let payment_id = &req.connector_request_reference_id;
        Ok(format!(
            "{}api/gateway/payment-status/payment_validation_key_{}_{}",
            self.base_url(connectors),
            merchant_id.get_string_repr(),
            payment_id
        ))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: mifinity::MifinityPsyncResponse = res
            .response
            .parse_struct("mifinity PaymentsSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Mifinity {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsCaptureType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsCaptureType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, errors::ConnectorError> {
        let response: mifinity::MifinityPaymentsResponse = res
            .response
            .parse_struct("Mifinity PaymentsCaptureResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Mifinity {}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Mifinity {
    fn build_request(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Refunds".to_string(),
            connector: "Mifinity".to_string(),
        }
        .into())
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Mifinity {}

#[async_trait::async_trait]
impl IncomingWebhook for Mifinity {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_resource_object(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }
}

lazy_static! {
    static ref MIFINITY_SUPPORTED_PAYMENT_METHODS: SupportedPaymentMethods = {
        let supported_capture_methods = vec![enums::CaptureMethod::Automatic];

        let mut mifinity_supported_payment_methods = SupportedPaymentMethods::new();
        mifinity_supported_payment_methods.add(
            enums::PaymentMethod::Wallet,
            enums::PaymentMethodType::Mifinity,
            PaymentMethodDetails{
                mandates: common_enums::FeatureStatus::NotSupported,
                refunds: common_enums::FeatureStatus::NotSupported,
                supported_capture_methods,
                specific_features: None,
            },
        );

        mifinity_supported_payment_methods
    };

    static ref MIFINITY_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "MIFINITY",
        description:
            "Mifinity is a payment gateway empowering you to pay online, receive funds, and send money globally, the MiFinity eWallet supports super-low fees, offering infinite possibilities to do more of the things you love.",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Live,
    };

    static ref MIFINITY_SUPPORTED_WEBHOOK_FLOWS: Vec<enums::EventClass> = Vec::new();

}

impl ConnectorSpecifications for Mifinity {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&*MIFINITY_CONNECTOR_INFO)
    }

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

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


impl FlattenedPaymentMethodsEnabled {
    async fn perform_filtering(
        self,
        state: &routes::SessionState,
        merchant_context: &domain::MerchantContext,
        profile_id: &id_type::ProfileId,
        req: &api_models::payments::ListMethodsForPaymentsRequest,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
    ) -> errors::RouterResult<FilteredPaymentMethodsEnabled> {
        let billing_address = payment_intent
            .billing_address
            .clone()
            .and_then(|address| address.into_inner().address);

        let mut response: Vec<hyperswitch_domain_models::merchant_connector_account::PaymentMethodsEnabledForConnector> = vec![];

        for payment_method_enabled_details in self.0.payment_methods_enabled {
            filter_payment_methods(
                payment_method_enabled_details,
                req,
                &mut response,
                Some(payment_intent),
                billing_address.as_ref(),
                &state.conf,
            )
            .await?;
        }

        Ok(FilteredPaymentMethodsEnabled(response))
    }
}


pub struct NoonRevokeMandateResponse {
    result: NoonRevokeMandateResult,
}


pub struct TemporaryVaultCvc {
    card_cvc: masking::Secret<String>,
}


pub struct PaymentMethodResponse {
    /// The unique identifier of the Payment method
    #[schema(value_type = String, example = "12345_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub id: id_type::GlobalPaymentMethodId,

    /// Unique identifier for a merchant
    #[schema(value_type = String, example = "merchant_1671528864")]
    pub merchant_id: id_type::MerchantId,

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

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod, example = "card")]
    pub payment_method_type: Option<api_enums::PaymentMethod>,

    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>, example = "credit")]
    pub payment_method_subtype: Option<api_enums::PaymentMethodType>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2023-01-18T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<time::PrimitiveDateTime>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was last used
    #[schema(value_type = Option<PrimitiveDateTime>, example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_used_at: Option<time::PrimitiveDateTime>,

    /// The payment method details related to the payment method
    pub payment_method_data: Option<PaymentMethodResponseData>,

    /// The connector token details if available
    pub connector_tokens: Option<Vec<ConnectorTokenDetails>>,

    pub network_token: Option<NetworkTokenResponse>,
}


pub async fn customers_delete(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<id_type::CustomerId>,
) -> impl Responder {
    let flow = Flow::CustomersDelete;
    let customer_id = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        customer_id,
        |state, auth: auth::AuthenticationData, customer_id, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            delete_customer(state, merchant_context, customer_id)
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::MerchantCustomerWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub fn requires_forex_functionality(&self) -> bool {
        self.payment_attempt
            .as_ref()
            .map(|req| req.metrics.iter().any(|metric| metric.is_forex_metric()))
            .unwrap_or_default()
            || self
                .payment_intent
                .as_ref()
                .map(|req| req.metrics.iter().any(|metric| metric.is_forex_metric()))
                .unwrap_or_default()
            || self
                .refund
                .as_ref()
                .map(|req| req.metrics.iter().any(|metric| metric.is_forex_metric()))
                .unwrap_or_default()
            || self
                .dispute
                .as_ref()
                .map(|req| req.metrics.iter().any(|metric| metric.is_forex_metric()))
                .unwrap_or_default()
    }


impl ConnectorCommon for Paystack {
    fn id(&self) -> &'static str {
        "paystack"
    }

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Minor
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.paystack.base_url.as_ref()
    }

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = paystack::PaystackAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::AUTHORIZATION.to_string(),
            format!("Bearer {}", auth.api_key.expose()).into_masked(),
        )])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: paystack::PaystackErrorResponse = res
            .response
            .parse_struct("PaystackErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        let error_message = paystack::get_error_message(response.clone());

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.code,
            message: error_message,
            reason: Some(response.message),
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


pub fn get_frm_config_as_secret(
    frm_configs: Option<Vec<api_models::admin::FrmConfigs>>,
) -> Option<Vec<Secret<serde_json::Value>>> {
    match frm_configs.as_ref() {
        Some(frm_value) => {
            let configs_for_frm_value: Vec<Secret<serde_json::Value>> = frm_value
                .iter()
                .map(|config| {
                    config
                        .encode_to_value()
                        .change_context(errors::ApiErrorResponse::ConfigNotFound)
                        .map(Secret::new)
                })
                .collect::<Result<Vec<_>, _>>()
                .ok()?;
            Some(configs_for_frm_value)
        }
        None => None,
    }
}


impl CardExpirationMonth {
    pub fn two_digits(&self) -> String {
        format!("{:02}", self.peek())
    }
}


pub fn server(state: AppState) -> Scope {
        web::scope("/authentication")
            .app_data(web::Data::new(state))
            .service(web::resource("").route(web::post().to(authentication::authentication_create)))
            .service(
                web::resource("/{authentication_id}/eligibility")
                    .route(web::post().to(authentication::authentication_eligibility)),
            )
            .service(
                web::resource("/{authentication_id}/authenticate")
                    .route(web::post().to(authentication::authentication_authenticate)),
            )
            .service(
                web::resource("{merchant_id}/{authentication_id}/sync")
                    .route(web::post().to(authentication::authentication_sync))
                    .route(web::get().to(authentication::authentication_sync_post_update)),
            )
    }


impl Default for RevenueRecoveryPaymentProcessTrackerMapping {
    fn default() -> Self {
        Self {
            default_mapping: RetryMapping {
                // 1st attempt happens after 1 minute of it being
                start_after: 60,

                frequencies: vec![
                    // 2nd and 3rd attempts happen at intervals of 3 hours each
                    (60 * 60 * 3, 2),
                    // 4th, 5th, 6th attempts happen at intervals of 6 hours each
                    (60 * 60 * 6, 3),
                    // 7th, 8th, 9th attempts happen at intervals of 9 hour each
                    (60 * 60 * 9, 3),
                    // 10th, 11th and 12th attempts happen at intervals of 12 hours each
                    (60 * 60 * 12, 3),
                    // 13th, 14th and 15th attempts happen at intervals of 18 hours each
                    (60 * 60 * 18, 3),
                ],
            },
            custom_merchant_mapping: HashMap::new(),
        }
    }
}

impl Default for for RevenueRecoveryPaymentProcessTrackerMapping


File: crates/router/src/core/fraud_check/operation/fraud_check_pre.rs
 Public structs: 1

use async_trait::async_trait;
use common_enums::FrmSuggestion;
use common_utils::ext_traits::Encode;
use diesel_models::enums::FraudCheckLastStep;
use router_env::{instrument, tracing};
use uuid::Uuid;

use super::{Domain, FraudCheckOperation, GetTracker, UpdateTracker};
use crate::{
    core::{
        errors::RouterResult,
        fraud_check::{
            self as frm_core,
            types::{FrmData, PaymentDetails, PaymentToFrmData},
            ConnectorDetailsCore,
        },
        payments,
    },
    errors,
    routes::app::ReqState,
    types::{
        api::fraud_check as frm_api,
        domain,
        fraud_check::{
            FraudCheckCheckoutData, FraudCheckResponseData, FraudCheckTransactionData, FrmRequest,
            FrmResponse, FrmRouterData,
        },
        storage::{
            enums::{FraudCheckStatus, FraudCheckType},
            fraud_check::{FraudCheckNew, FraudCheckUpdate},
        },
        ResponseId,
    },
    SessionState,
};

#[derive(Debug, Clone, Copy)]
pub struct FraudCheckPre;

impl<F, D> FraudCheckOperation<F, D> for &FraudCheckPre
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<F> + Send + Sync + Clone,
{
    fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<PaymentToFrmData> + Send + Sync)> {
        Ok(*self)
    }
    fn to_domain(&self) -> RouterResult<&(dyn Domain<F, D>)> {
        Ok(*self)
    }
    fn to_update_tracker(&self) -> RouterResult<&(dyn UpdateTracker<FrmData, F, D> + Send + Sync)> {
        Ok(*self)
    }
}

impl<F, D> FraudCheckOperation<F, D> for FraudCheckPre
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<F> + Send + Sync + Clone,
{
    fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<PaymentToFrmData> + Send + Sync)> {
        Ok(self)
    }
    fn to_domain(&self) -> RouterResult<&(dyn Domain<F, D>)> {
        Ok(self)
    }
    fn to_update_tracker(&self) -> RouterResult<&(dyn UpdateTracker<FrmData, F, D> + Send + Sync)> {
        Ok(self)
    }
}

#[async_trait]
impl GetTracker<PaymentToFrmData> for FraudCheckPre {
    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: PaymentToFrmData,
        frm_connector_details: ConnectorDetailsCore,
    ) -> RouterResult<Option<FrmData>> {
        todo!()
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: PaymentToFrmData,
        frm_connector_details: ConnectorDetailsCore,
    ) -> RouterResult<Option<FrmData>> {
        let db = &*state.store;

        let payment_details: Option<serde_json::Value> = PaymentDetails::from(payment_data.clone())
            .encode_to_value()
            .ok();

        let existing_fraud_check = db
            .find_fraud_check_by_payment_id_if_present(
                payment_data.payment_intent.get_id().to_owned(),
                payment_data.merchant_account.get_id().clone(),
            )
            .await
            .ok();

        let fraud_check = match existing_fraud_check {
            Some(Some(fraud_check)) => Ok(fraud_check),
            _ => {
                db.insert_fraud_check_response(FraudCheckNew {
                    frm_id: Uuid::new_v4().simple().to_string(),
                    payment_id: payment_data.payment_intent.get_id().to_owned(),
                    merchant_id: payment_data.merchant_account.get_id().clone(),
                    attempt_id: payment_data.payment_attempt.attempt_id.clone(),
                    created_at: common_utils::date_time::now(),
                    frm_name: frm_connector_details.connector_name,
                    frm_transaction_id: None,
                    frm_transaction_type: FraudCheckType::PreFrm,
                    frm_status: FraudCheckStatus::Pending,
                    frm_score: None,
                    frm_reason: None,
                    frm_error: None,
                    payment_details,
                    metadata: None,
                    modified_at: common_utils::date_time::now(),
                    last_step: FraudCheckLastStep::Processing,
                    payment_capture_method: payment_data.payment_attempt.capture_method,
                })
                .await
            }
        };

        match fraud_check {
            Ok(fraud_check_value) => {
                let frm_data = FrmData {
                    payment_intent: payment_data.payment_intent,
                    payment_attempt: payment_data.payment_attempt,
                    merchant_account: payment_data.merchant_account,
                    address: payment_data.address,
                    fraud_check: fraud_check_value,
                    connector_details: payment_data.connector_details,
                    order_details: payment_data.order_details,
                    refund: None,
                    frm_metadata: payment_data.frm_metadata,
                };
                Ok(Some(frm_data))
            }
            Err(error) => {
                router_env::logger::error!("inserting into fraud_check table failed {error:?}");
                Ok(None)
            }
        }
    }
}

#[async_trait]
impl<F, D> Domain<F, D> for FraudCheckPre
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<F> + Send + Sync + Clone,
{
    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn post_payment_frm<'a>(
        &'a self,
        _state: &'a SessionState,
        _req_state: ReqState,
        _payment_data: &mut D,
        _frm_data: &mut FrmData,
        _merchant_context: &domain::MerchantContext,
        _customer: &Option<domain::Customer>,
    ) -> RouterResult<Option<FrmRouterData>> {
        todo!()
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn post_payment_frm<'a>(
        &'a self,
        state: &'a SessionState,
        _req_state: ReqState,
        payment_data: &mut D,
        frm_data: &mut FrmData,
        merchant_context: &domain::MerchantContext,
        customer: &Option<domain::Customer>,
    ) -> RouterResult<Option<FrmRouterData>> {
        let router_data = frm_core::call_frm_service::<F, frm_api::Transaction, _, D>(
            state,
            payment_data,
            &mut frm_data.to_owned(),
            merchant_context,
            customer,
        )
        .await?;
        frm_data.fraud_check.last_step = FraudCheckLastStep::TransactionOrRecordRefund;
        Ok(Some(FrmRouterData {
            merchant_id: router_data.merchant_id,
            connector: router_data.connector,
            payment_id: router_data.payment_id.clone(),
            attempt_id: router_data.attempt_id,
            request: FrmRequest::Transaction(FraudCheckTransactionData {
                amount: router_data.request.amount,
                order_details: router_data.request.order_details,
                currency: router_data.request.currency,
                payment_method: Some(router_data.payment_method),
                error_code: router_data.request.error_code,
                error_message: router_data.request.error_message,
                connector_transaction_id: router_data.request.connector_transaction_id,
                connector: router_data.request.connector,
            }),
            response: FrmResponse::Transaction(router_data.response),
        }))
    }

    async fn pre_payment_frm<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: &mut D,
        frm_data: &mut FrmData,
        merchant_context: &domain::MerchantContext,
        customer: &Option<domain::Customer>,
    ) -> RouterResult<FrmRouterData> {
        let router_data = frm_core::call_frm_service::<F, frm_api::Checkout, _, D>(
            state,
            payment_data,
            &mut frm_data.to_owned(),
            merchant_context,
            customer,
        )
        .await?;
        frm_data.fraud_check.last_step = FraudCheckLastStep::CheckoutOrSale;
        Ok(FrmRouterData {
            merchant_id: router_data.merchant_id,
            connector: router_data.connector,
            payment_id: router_data.payment_id.clone(),
            attempt_id: router_data.attempt_id,
            request: FrmRequest::Checkout(Box::new(FraudCheckCheckoutData {
                amount: router_data.request.amount,
                order_details: router_data.request.order_details,
                currency: router_data.request.currency,
                browser_info: router_data.request.browser_info,
                payment_method_data: router_data.request.payment_method_data,
                email: router_data.request.email,
                gateway: router_data.request.gateway,
            })),
            response: FrmResponse::Checkout(router_data.response),
        })
    }
}

#[async_trait]
impl<F, D> UpdateTracker<FrmData, F, D> for FraudCheckPre
where
    F: Clone + Send,
    D: payments::OperationSessionGetters<F> + Send + Sync + Clone,
{
    async fn update_tracker<'b>(
        &'b self,
        state: &SessionState,
        _key_store: &domain::MerchantKeyStore,
        mut frm_data: FrmData,
        payment_data: &mut D,
        _frm_suggestion: Option<FrmSuggestion>,
        frm_router_data: FrmRouterData,
    ) -> RouterResult<FrmData> {
        let frm_check_update = match frm_router_data.response {
            FrmResponse::Checkout(response) => match response {
                Err(err) => Some(FraudCheckUpdate::ErrorUpdate {
                    status: FraudCheckStatus::TransactionFailure,
                    error_message: Some(Some(err.message)),
                }),
                Ok(payments_response) => match payments_response {
                    FraudCheckResponseData::TransactionResponse {
                        resource_id,
                        connector_metadata,
                        status,
                        reason,
                        score,
                    } => {
                        let connector_transaction_id = match resource_id {
                            ResponseId::NoResponseId => None,
                            ResponseId::ConnectorTransactionId(id) => Some(id),
                            ResponseId::EncodedData(id) => Some(id),
                        };

                        let fraud_check_update = FraudCheckUpdate::ResponseUpdate {
                            frm_status: status,
                            frm_transaction_id: connector_transaction_id,
                            frm_reason: reason,
                            frm_score: score,
                            metadata: connector_metadata,
                            modified_at: common_utils::date_time::now(),
                            last_step: frm_data.fraud_check.last_step,
                            payment_capture_method: frm_data.fraud_check.payment_capture_method,
                        };
                        Some(fraud_check_update)
                    }
                    FraudCheckResponseData::FulfillmentResponse {
                        order_id: _,
                        shipment_ids: _,
                    } => None,
                    FraudCheckResponseData::RecordReturnResponse {
                        resource_id: _,
                        connector_metadata: _,
                        return_id: _,
                    } => Some(FraudCheckUpdate::ErrorUpdate {
                        status: FraudCheckStatus::TransactionFailure,
                        error_message: Some(Some(
                            "Error: Got Record Return Response response in current Checkout flow"
                                .to_string(),
                        )),
                    }),
                },
            },
            FrmResponse::Transaction(response) => match response {
                Err(err) => Some(FraudCheckUpdate::ErrorUpdate {
                    status: FraudCheckStatus::TransactionFailure,
                    error_message: Some(Some(err.message)),
                }),
                Ok(payments_response) => match payments_response {
                    FraudCheckResponseData::TransactionResponse {
                        resource_id,
                        connector_metadata,
                        status,
                        reason,
                        score,
                    } => {
                        let connector_transaction_id = match resource_id {
                            ResponseId::NoResponseId => None,
                            ResponseId::ConnectorTransactionId(id) => Some(id),
                            ResponseId::EncodedData(id) => Some(id),
                        };

                        let frm_status = payment_data
                            .get_frm_message()
                            .as_ref()
                            .map_or(status, |frm_data| frm_data.frm_status);

                        let fraud_check_update = FraudCheckUpdate::ResponseUpdate {
                            frm_status,
                            frm_transaction_id: connector_transaction_id,
                            frm_reason: reason,
                            frm_score: score,
                            metadata: connector_metadata,
                            modified_at: common_utils::date_time::now(),
                            last_step: frm_data.fraud_check.last_step,
                            payment_capture_method: None,
                        };
                        Some(fraud_check_update)
                    }
                    FraudCheckResponseData::FulfillmentResponse {
                        order_id: _,
                        shipment_ids: _,
                    } => None,
                    FraudCheckResponseData::RecordReturnResponse {
                        resource_id: _,
                        connector_metadata: _,
                        return_id: _,
                    } => Some(FraudCheckUpdate::ErrorUpdate {
                        status: FraudCheckStatus::TransactionFailure,
                        error_message: Some(Some(
                            "Error: Got Record Return Response response in current Checkout flow"
                                .to_string(),
                        )),
                    }),
                },
            },
            FrmResponse::Sale(_response)
            | FrmResponse::Fulfillment(_response)
            | FrmResponse::RecordReturn(_response) => Some(FraudCheckUpdate::ErrorUpdate {
                status: FraudCheckStatus::TransactionFailure,
                error_message: Some(Some(
                    "Error: Got Pre(Sale) flow response in current post flow".to_string(),
                )),
            }),
        };

        let db = &*state.store;
        frm_data.fraud_check = match frm_check_update {
            Some(fraud_check_update) => db
                .update_fraud_check_response_with_attempt_id(
                    frm_data.clone().fraud_check,
                    fraud_check_update,
                )
                .await
                .map_err(|error| error.change_context(errors::ApiErrorResponse::PaymentNotFound))?,
            None => frm_data.clone().fraud_check,
        };

        Ok(frm_data)
    }
}


impl masking::SerializableSecret for RedirectResponse {}

impl masking::SerializableSecret for for RedirectResponse


pub struct ConsultaOperacionesReturn {
    messages: MessagesResponseData,
}