File: crates/storage_impl/src/mock_db/payment_attempt.rs

use common_utils::errors::CustomResult;
#[cfg(feature = "v2")]
use common_utils::{id_type, types::keymanager::KeyManagerState};
use diesel_models::enums as storage_enums;
#[cfg(feature = "v2")]
use hyperswitch_domain_models::merchant_key_store::MerchantKeyStore;
#[cfg(feature = "v1")]
use hyperswitch_domain_models::payments::payment_attempt::PaymentAttemptNew;
use hyperswitch_domain_models::payments::payment_attempt::{
    PaymentAttempt, PaymentAttemptInterface, PaymentAttemptUpdate,
};

use super::MockDb;
use crate::errors::StorageError;
#[cfg(feature = "v1")]
use crate::DataModelExt;

#[async_trait::async_trait]
impl PaymentAttemptInterface for MockDb {
    type Error = StorageError;
    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id(
        &self,
        _payment_id: &common_utils::id_type::PaymentId,
        _merchant_id: &common_utils::id_type::MerchantId,
        _attempt_id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn get_filters_for_payments(
        &self,
        _pi: &[hyperswitch_domain_models::payments::PaymentIntent],
        _merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<
        hyperswitch_domain_models::payments::payment_attempt::PaymentListFilters,
        StorageError,
    > {
        Err(StorageError::MockDbError)?
    }

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn get_total_count_of_filtered_payment_attempts(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _active_attempt_ids: &[String],
        _connector: Option<Vec<api_models::enums::Connector>>,
        _payment_method: Option<Vec<common_enums::PaymentMethod>>,
        _payment_method_type: Option<Vec<common_enums::PaymentMethodType>>,
        _authentication_type: Option<Vec<common_enums::AuthenticationType>>,
        _merchanat_connector_id: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
        _card_network: Option<Vec<storage_enums::CardNetwork>>,
        _card_discovery: Option<Vec<storage_enums::CardDiscovery>>,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<i64, StorageError> {
        Err(StorageError::MockDbError)?
    }

    #[cfg(all(feature = "v2", feature = "olap"))]
    async fn get_total_count_of_filtered_payment_attempts(
        &self,
        _merchant_id: &id_type::MerchantId,
        _active_attempt_ids: &[String],
        _connector: Option<Vec<api_models::enums::Connector>>,
        _payment_method_type: Option<Vec<common_enums::PaymentMethod>>,
        _payment_method_subtype: Option<Vec<common_enums::PaymentMethodType>>,
        _authentication_type: Option<Vec<common_enums::AuthenticationType>>,
        _merchant_connector_id: Option<Vec<id_type::MerchantConnectorAccountId>>,
        _card_network: Option<Vec<storage_enums::CardNetwork>>,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<i64, StorageError> {
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_attempt_id_merchant_id(
        &self,
        _attempt_id: &str,
        _merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v2")]
    async fn find_payment_attempt_by_id(
        &self,
        _key_manager_state: &KeyManagerState,
        _merchant_key_store: &MerchantKeyStore,
        _attempt_id: &id_type::GlobalAttemptId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v2")]
    async fn find_payment_attempts_by_payment_intent_id(
        &self,
        _key_manager_state: &KeyManagerState,
        _id: &id_type::GlobalPaymentId,
        _merchant_key_store: &MerchantKeyStore,
        _storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<Vec<PaymentAttempt>, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_preprocessing_id_merchant_id(
        &self,
        _preprocessing_id: &str,
        _merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_merchant_id_connector_txn_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_txn_id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v2")]
    async fn find_payment_attempt_by_profile_id_connector_transaction_id(
        &self,
        _key_manager_state: &KeyManagerState,
        _merchant_key_store: &MerchantKeyStore,
        _profile_id: &id_type::ProfileId,
        _connector_transaction_id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    async fn find_attempts_by_merchant_id_payment_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _payment_id: &common_utils::id_type::PaymentId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<Vec<PaymentAttempt>, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    #[allow(clippy::panic)]
    async fn insert_payment_attempt(
        &self,
        payment_attempt: PaymentAttemptNew,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        let mut payment_attempts = self.payment_attempts.lock().await;
        let time = common_utils::date_time::now();
        let payment_attempt = PaymentAttempt {
            payment_id: payment_attempt.payment_id,
            merchant_id: payment_attempt.merchant_id,
            attempt_id: payment_attempt.attempt_id,
            status: payment_attempt.status,
            net_amount: payment_attempt.net_amount,
            currency: payment_attempt.currency,
            save_to_locker: payment_attempt.save_to_locker,
            connector: payment_attempt.connector,
            error_message: payment_attempt.error_message,
            offer_amount: payment_attempt.offer_amount,
            payment_method_id: payment_attempt.payment_method_id,
            payment_method: payment_attempt.payment_method,
            connector_transaction_id: None,
            capture_method: payment_attempt.capture_method,
            capture_on: payment_attempt.capture_on,
            confirm: payment_attempt.confirm,
            authentication_type: payment_attempt.authentication_type,
            created_at: payment_attempt.created_at.unwrap_or(time),
            modified_at: payment_attempt.modified_at.unwrap_or(time),
            last_synced: payment_attempt.last_synced,
            cancellation_reason: payment_attempt.cancellation_reason,
            amount_to_capture: payment_attempt.amount_to_capture,
            mandate_id: None,
            browser_info: None,
            payment_token: None,
            error_code: payment_attempt.error_code,
            connector_metadata: None,
            charge_id: None,
            payment_experience: payment_attempt.payment_experience,
            payment_method_type: payment_attempt.payment_method_type,
            payment_method_data: payment_attempt.payment_method_data,
            business_sub_label: payment_attempt.business_sub_label,
            straight_through_algorithm: payment_attempt.straight_through_algorithm,
            mandate_details: payment_attempt.mandate_details,
            preprocessing_step_id: payment_attempt.preprocessing_step_id,
            error_reason: payment_attempt.error_reason,
            multiple_capture_count: payment_attempt.multiple_capture_count,
            connector_response_reference_id: None,
            amount_capturable: payment_attempt.amount_capturable,
            updated_by: storage_scheme.to_string(),
            authentication_data: payment_attempt.authentication_data,
            encoded_data: payment_attempt.encoded_data,
            merchant_connector_id: payment_attempt.merchant_connector_id,
            unified_code: payment_attempt.unified_code,
            unified_message: payment_attempt.unified_message,
            external_three_ds_authentication_attempted: payment_attempt
                .external_three_ds_authentication_attempted,
            authentication_connector: payment_attempt.authentication_connector,
            authentication_id: payment_attempt.authentication_id,
            mandate_data: payment_attempt.mandate_data,
            payment_method_billing_address_id: payment_attempt.payment_method_billing_address_id,
            fingerprint_id: payment_attempt.fingerprint_id,
            client_source: payment_attempt.client_source,
            client_version: payment_attempt.client_version,
            customer_acceptance: payment_attempt.customer_acceptance,
            organization_id: payment_attempt.organization_id,
            profile_id: payment_attempt.profile_id,
            connector_mandate_detail: payment_attempt.connector_mandate_detail,
            request_extended_authorization: payment_attempt.request_extended_authorization,
            extended_authorization_applied: payment_attempt.extended_authorization_applied,
            capture_before: payment_attempt.capture_before,
            card_discovery: payment_attempt.card_discovery,
            charges: None,
            issuer_error_code: None,
            issuer_error_message: None,
            processor_merchant_id: payment_attempt.processor_merchant_id,
            created_by: payment_attempt.created_by,
            setup_future_usage_applied: payment_attempt.setup_future_usage_applied,
            routing_approach: payment_attempt.routing_approach,
            connector_request_reference_id: payment_attempt.connector_request_reference_id,
            debit_routing_savings: None,
            network_transaction_id: payment_attempt.network_transaction_id,
        };
        payment_attempts.push(payment_attempt.clone());
        Ok(payment_attempt)
    }

    #[cfg(feature = "v2")]
    #[allow(clippy::panic)]
    async fn insert_payment_attempt(
        &self,
        _key_manager_state: &KeyManagerState,
        _merchant_key_store: &MerchantKeyStore,
        _payment_attempt: PaymentAttempt,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    // safety: only used for testing
    #[allow(clippy::unwrap_used)]
    async fn update_payment_attempt_with_attempt_id(
        &self,
        this: PaymentAttempt,
        payment_attempt: PaymentAttemptUpdate,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        let mut payment_attempts = self.payment_attempts.lock().await;

        let item = payment_attempts
            .iter_mut()
            .find(|item| item.attempt_id == this.attempt_id)
            .unwrap();

        *item = PaymentAttempt::from_storage_model(
            payment_attempt
                .to_storage_model()
                .apply_changeset(this.to_storage_model()),
        );

        Ok(item.clone())
    }

    #[cfg(feature = "v2")]
    async fn update_payment_attempt(
        &self,
        _key_manager_state: &KeyManagerState,
        _merchant_key_store: &MerchantKeyStore,
        _this: PaymentAttempt,
        _payment_attempt: PaymentAttemptUpdate,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
        &self,
        _connector_transaction_id: &common_utils::types::ConnectorTransactionId,
        _payment_id: &common_utils::id_type::PaymentId,
        _merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        // [#172]: Implement function for `MockDb`
        Err(StorageError::MockDbError)?
    }

    #[cfg(feature = "v1")]
    // safety: only used for testing
    #[allow(clippy::unwrap_used)]
    async fn find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id(
        &self,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        let payment_attempts = self.payment_attempts.lock().await;

        Ok(payment_attempts
            .iter()
            .find(|payment_attempt| {
                payment_attempt.payment_id == *payment_id
                    && payment_attempt.merchant_id.eq(merchant_id)
            })
            .cloned()
            .unwrap())
    }

    #[cfg(feature = "v1")]
    #[allow(clippy::unwrap_used)]
    async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id(
        &self,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        let payment_attempts = self.payment_attempts.lock().await;

        Ok(payment_attempts
            .iter()
            .find(|payment_attempt| {
                payment_attempt.payment_id == *payment_id
                    && payment_attempt.merchant_id.eq(merchant_id)
                    && (payment_attempt.status == storage_enums::AttemptStatus::PartialCharged
                        || payment_attempt.status == storage_enums::AttemptStatus::Charged)
            })
            .cloned()
            .unwrap())
    }

    #[cfg(feature = "v2")]
    #[allow(clippy::unwrap_used)]
    async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id(
        &self,
        _key_manager_state: &KeyManagerState,
        _merchant_key_store: &MerchantKeyStore,
        payment_id: &id_type::GlobalPaymentId,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, StorageError> {
        let payment_attempts = self.payment_attempts.lock().await;

        Ok(payment_attempts
            .iter()
            .find(|payment_attempt| {
                payment_attempt.payment_id == *payment_id
                    && (payment_attempt.status == storage_enums::AttemptStatus::PartialCharged
                        || payment_attempt.status == storage_enums::AttemptStatus::Charged)
            })
            .cloned()
            .unwrap())
    }
}


impl api::PaymentToken for Iatapay {}


pub fn search_index_to_opensearch_index(&self, index: SearchIndex) -> String {
        match index {
            SearchIndex::PaymentAttempts => self.indexes.payment_attempts.clone(),
            SearchIndex::PaymentIntents => self.indexes.payment_intents.clone(),
            SearchIndex::Refunds => self.indexes.refunds.clone(),
            SearchIndex::Disputes => self.indexes.disputes.clone(),
            SearchIndex::SessionizerPaymentAttempts => {
                self.indexes.sessionizer_payment_attempts.clone()
            }
            SearchIndex::SessionizerPaymentIntents => {
                self.indexes.sessionizer_payment_intents.clone()
            }
            SearchIndex::SessionizerRefunds => self.indexes.sessionizer_refunds.clone(),
            SearchIndex::SessionizerDisputes => self.indexes.sessionizer_disputes.clone(),
        }
    }


pub fn get_checkout_event_status_and_reason(
    attempt_status: common_enums::AttemptStatus,
) -> (Option<String>, Option<String>) {
    match attempt_status {
        common_enums::AttemptStatus::Charged | common_enums::AttemptStatus::Authorized => (
            Some("01".to_string()),
            Some("The payment was successful".to_string()),
        ),
        _ => (
            Some("03".to_string()),
            Some("The payment was not successful".to_string()),
        ),
    }
}


impl api::PaymentCapture for Bankofamerica {}

impl api::PaymentCapture for for Bankofamerica


impl RoleNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Role> {
        generics::generic_insert(conn, self).await
    }
}


impl DisputeMetricAccumulator for DisputedAmountAccumulator {
    type MetricOutput = Option<u64>;
    #[inline]
    fn add_metrics_bucket(&mut self, metrics: &DisputeMetricRow) {
        self.total = match (
            self.total,
            metrics
                .total
                .as_ref()
                .and_then(bigdecimal::ToPrimitive::to_i64),
        ) {
            (None, None) => None,
            (None, i @ Some(_)) | (i @ Some(_), None) => i,
            (Some(a), Some(b)) => Some(a + b),
        }
    }
    #[inline]
    fn collect(self) -> Self::MetricOutput {
        self.total.and_then(|i| u64::try_from(i).ok())
    }
}

impl DisputeMetricAccumulator for for DisputedAmountAccumulator


File: crates/common_utils/src/id_type/payout.rs

crate::id_type!(
    PayoutId,
    "A domain type for payout_id that can be used for payout ids"
);
crate::impl_id_type_methods!(PayoutId, "payout_id");
crate::impl_debug_id_type!(PayoutId);
crate::impl_try_from_cow_str_id_type!(PayoutId, "payout_id");
crate::impl_generate_id_id_type!(PayoutId, "payout");
crate::impl_queryable_id_type!(PayoutId);
crate::impl_to_sql_from_sql_id_type!(PayoutId);


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


pub struct PaymentIntentUpdateInternal {
    pub amount: Option<MinorUnit>,
    pub currency: Option<common_enums::Currency>,
    pub status: Option<common_enums::IntentStatus>,
    pub amount_captured: Option<MinorUnit>,
    pub customer_id: Option<id_type::CustomerId>,
    pub return_url: Option<String>,
    pub setup_future_usage: Option<common_enums::FutureUsage>,
    pub off_session: Option<bool>,
    pub metadata: Option<serde_json::Value>,
    pub billing_address_id: Option<String>,
    pub shipping_address_id: Option<String>,
    pub modified_at: Option<PrimitiveDateTime>,
    pub active_attempt_id: Option<String>,
    pub business_country: Option<common_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub description: Option<String>,
    pub statement_descriptor_name: Option<String>,
    pub statement_descriptor_suffix: Option<String>,
    pub order_details: Option<Vec<pii::SecretSerdeValue>>,
    pub attempt_count: Option<i16>,
    // Denotes the action(approve or reject) taken by merchant in case of manual review.
    // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment
    pub merchant_decision: Option<String>,
    pub payment_confirm_source: Option<common_enums::PaymentSource>,

    pub updated_by: String,
    pub surcharge_applicable: Option<bool>,
    pub incremental_authorization_allowed: Option<bool>,
    pub authorization_count: Option<i32>,
    pub fingerprint_id: Option<String>,
    pub session_expiry: Option<PrimitiveDateTime>,
    pub request_external_three_ds_authentication: Option<bool>,
    pub frm_metadata: Option<pii::SecretSerdeValue>,
    pub customer_details: Option<Encryptable<Secret<serde_json::Value>>>,
    pub billing_details: Option<Encryptable<Secret<serde_json::Value>>>,
    pub merchant_order_reference_id: Option<String>,
    pub shipping_details: Option<Encryptable<Secret<serde_json::Value>>>,
    pub is_payment_processor_token_flow: Option<bool>,
    pub tax_details: Option<diesel_models::TaxDetails>,
    pub force_3ds_challenge: Option<bool>,
    pub is_iframe_redirection_enabled: Option<bool>,
    pub payment_channel: Option<common_enums::PaymentChannel>,
    pub feature_metadata: Option<Secret<serde_json::Value>>,
    pub tax_status: Option<common_enums::TaxStatus>,
    pub discount_amount: Option<MinorUnit>,
    pub order_date: Option<PrimitiveDateTime>,
    pub shipping_amount_tax: Option<MinorUnit>,
    pub duty_amount: Option<MinorUnit>,
    pub enable_partial_authorization: Option<bool>,
}


pub fn server(config: AppState) -> Scope {
        use api_models::webhooks as webhook_type;

        #[allow(unused_mut)]
        let mut route = web::scope("/webhooks")
            .app_data(web::Data::new(config))
            .service(
                web::resource("/network_token_requestor/ref")
                    .route(
                        web::post().to(receive_network_token_requestor_incoming_webhook::<
                            webhook_type::OutgoingWebhook,
                        >),
                    )
                    .route(
                        web::get().to(receive_network_token_requestor_incoming_webhook::<
                            webhook_type::OutgoingWebhook,
                        >),
                    )
                    .route(
                        web::put().to(receive_network_token_requestor_incoming_webhook::<
                            webhook_type::OutgoingWebhook,
                        >),
                    ),
            )
            .service(
                web::resource("/{merchant_id}/{connector_id_or_name}")
                    .route(
                        web::post().to(receive_incoming_webhook::<webhook_type::OutgoingWebhook>),
                    )
                    .route(web::get().to(receive_incoming_webhook::<webhook_type::OutgoingWebhook>))
                    .route(
                        web::put().to(receive_incoming_webhook::<webhook_type::OutgoingWebhook>),
                    ),
            );

        #[cfg(feature = "frm")]
        {
            route = route.service(
                web::resource("/frm_fulfillment")
                    .route(web::post().to(frm_routes::frm_fulfillment)),
            );
        }

        route
    }


pub fn get_error_message(&self) -> String {
        match self {
            Self::InternalServerError => "Something went wrong".to_string(),
            Self::InvalidCredentials => "Incorrect email or password".to_string(),
            Self::UserNotFound => "Email doesn’t exist. Register".to_string(),
            Self::UserExists => "An account already exists with this email".to_string(),
            Self::LinkInvalid => "Invalid or expired link".to_string(),
            Self::UnverifiedUser => "Kindly verify your account".to_string(),
            Self::InvalidOldPassword => {
                "Old password incorrect. Please enter the correct password".to_string()
            }
            Self::EmailParsingError => "Invalid Email".to_string(),
            Self::NameParsingError => "Invalid Name".to_string(),
            Self::PasswordParsingError => "Invalid Password".to_string(),
            Self::UserAlreadyVerified => "User already verified".to_string(),
            Self::CompanyNameParsingError => "Invalid Company Name".to_string(),
            Self::MerchantAccountCreationError(error_message) => error_message.to_string(),
            Self::InvalidEmailError => "Invalid Email".to_string(),
            Self::MerchantIdNotFound => "Invalid Merchant ID".to_string(),
            Self::MetadataAlreadySet => "Metadata already set".to_string(),
            Self::DuplicateOrganizationId => {
                "An Organization with the id already exists".to_string()
            }
            Self::InvalidRoleId => "Invalid Role ID".to_string(),
            Self::InvalidRoleOperation => "User Role Operation Not Supported".to_string(),
            Self::IpAddressParsingFailed => "Something went wrong".to_string(),
            Self::InvalidMetadataRequest => "Invalid Metadata Request".to_string(),
            Self::MerchantIdParsingError => "Invalid Merchant Id".to_string(),
            Self::ChangePasswordError => "Old and new password cannot be same".to_string(),
            Self::InvalidDeleteOperation => "Delete Operation Not Supported".to_string(),
            Self::MaxInvitationsError => "Maximum invite count per request exceeded".to_string(),
            Self::RoleNotFound => "Role Not Found".to_string(),
            Self::InvalidRoleOperationWithMessage(error_message) => error_message.to_string(),
            Self::RoleNameParsingError => "Invalid Role Name".to_string(),
            Self::RoleNameAlreadyExists => "Role name already exists".to_string(),
            Self::TotpNotSetup => "TOTP not setup".to_string(),
            Self::InvalidTotp => "Invalid TOTP".to_string(),
            Self::TotpRequired => "TOTP required".to_string(),
            Self::InvalidRecoveryCode => "Invalid Recovery Code".to_string(),
            Self::MaxTotpAttemptsReached => "Maximum attempts reached for TOTP".to_string(),
            Self::MaxRecoveryCodeAttemptsReached => {
                "Maximum attempts reached for Recovery Code".to_string()
            }
            Self::TwoFactorAuthRequired => "Two factor auth required".to_string(),
            Self::TwoFactorAuthNotSetup => "Two factor auth not setup".to_string(),
            Self::TotpSecretNotFound => "TOTP secret not found".to_string(),
            Self::UserAuthMethodAlreadyExists => "User auth method already exists".to_string(),
            Self::InvalidUserAuthMethodOperation => {
                "Invalid user auth method operation".to_string()
            }
            Self::AuthConfigParsingError => "Auth config parsing error".to_string(),
            Self::SSOFailed => "Invalid SSO request".to_string(),
            Self::JwtProfileIdMissing => "profile_id missing in JWT".to_string(),
            Self::ForbiddenTenantId => "Forbidden tenant id".to_string(),
            Self::ErrorUploadingFile => "Error Uploading file to Theme Storage".to_string(),
            Self::ErrorRetrievingFile => "Error Retrieving file from Theme Storage".to_string(),
            Self::ThemeNotFound => "Theme not found".to_string(),
            Self::ThemeAlreadyExists => "Theme with lineage already exists".to_string(),
            Self::InvalidThemeLineage(field_name) => {
                format!("Invalid field: {field_name} in lineage")
            }
            Self::MissingEmailConfig => "Missing required field: email_config".to_string(),
            Self::InvalidAuthMethodOperationWithMessage(operation) => {
                format!("Invalid Auth Method Operation: {operation}")
            }
            Self::InvalidCloneConnectorOperation(operation) => {
                format!("Invalid Clone Connector Operation: {operation}")
            }
            Self::ErrorCloningConnector(error_message) => {
                format!("Error cloning connector: {error_message}")
            }
        }
    }


pub struct ArchipelCardHolder {
    billing_address: Option<ArchipelBillingAddress>,
}


pub struct Beneficiary {
    pub name: Option<Secret<String>>,
    pub document_type: Option<enums::DocumentKind>,
    pub document_number: Option<String>,
}


impl MerchantConnectorAccount {
    pub fn get_id(&self) -> id_type::MerchantConnectorAccountId {
        self.merchant_connector_id.clone()
    }
}


pub struct AccessTokenAuthenticationRequestData {
    pub auth_creds: router_data::ConnectorAuthType,
}


/// Insert a new payment token reference for the given connector_id
    pub fn insert_payment_token_reference_record(
        &mut self,
        connector_id: &common_utils::id_type::MerchantConnectorAccountId,
        record: ConnectorTokenReferenceRecord,
    ) {
        match self.payments {
            Some(ref mut payments_reference) => {
                payments_reference.insert(connector_id.clone(), record);
            }
            None => {
                let mut payments_reference = HashMap::new();
                payments_reference.insert(connector_id.clone(), record);
                self.payments = Some(PaymentsTokenReference(payments_reference));
            }
        }
    }


pub struct NoonCheckoutData {
    post_url: url::Url,
}


pub struct DiscountCodes {
    amount: StringMajorUnit,
    code: Option<String>,
}


pub async fn default_payment_method_set_api() {}


pub async fn invite_multiple_user(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<Vec<user_api::InviteUserRequest>>,
    auth_id_query_param: web::Query<user_api::AuthIdAndThemeIdQueryParam>,
) -> HttpResponse {
    let flow = Flow::InviteMultipleUser;
    let auth_id = auth_id_query_param.into_inner().auth_id;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload.into_inner(),
        |state, user, payload, req_state| {
            user_core::invite_multiple_user(state, user, payload, req_state, auth_id.clone())
        },
        &auth::JWTAuth {
            permission: Permission::ProfileUserWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub fn build(self) -> ConstraintGraph<V> {
        ConstraintGraph {
            domain: self.domain,
            domain_identifier_map: self.domain_identifier_map,
            nodes: self.nodes,
            edges: self.edges,
            value_map: self.value_map,
            node_info: self.node_info,
            node_metadata: self.node_metadata,
        }
    }


pub async fn recon_merchant_account_update(
    state: SessionState,
    auth: authentication::AuthenticationData,
    req: recon_api::ReconUpdateMerchantRequest,
) -> RouterResponse<api_types::MerchantAccountResponse> {
    let db = &*state.store;
    let key_manager_state = &(&state).into();

    let updated_merchant_account = storage::MerchantAccountUpdate::ReconUpdate {
        recon_status: req.recon_status,
    };
    let merchant_id = auth.merchant_account.get_id().clone();

    let updated_merchant_account = db
        .update_merchant(
            key_manager_state,
            auth.merchant_account.clone(),
            updated_merchant_account,
            &auth.key_store,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!("Failed while updating merchant's recon status: {merchant_id:?}")
        })?;

    #[cfg(feature = "email")]
    {
        let user_email = &req.user_email.clone();

        let theme = theme_utils::get_most_specific_theme_using_lineage(
            &state.clone(),
            ThemeLineage::Merchant {
                tenant_id: state.tenant.tenant_id.clone(),
                org_id: auth.merchant_account.get_org_id().clone(),
                merchant_id: merchant_id.clone(),
            },
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(format!(
            "Failed to fetch theme for merchant_id = {merchant_id:?}",
        ))?;

        let email_contents = email_types::ReconActivation {
            recipient_email: domain::UserEmail::from_pii_email(user_email.clone())
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable(
                    "Failed to convert recipient's email to UserEmail from pii::Email",
                )?,
            user_name: domain::UserName::new(Secret::new("HyperSwitch User".to_string()))
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to form username")?,
            subject: consts::EMAIL_SUBJECT_APPROVAL_RECON_REQUEST,
            theme_id: theme.as_ref().map(|theme| theme.theme_id.clone()),
            theme_config: theme
                .map(|theme| theme.email_config())
                .unwrap_or(state.conf.theme.email_config.clone()),
        };
        if req.recon_status == enums::ReconStatus::Active {
            let _ = state
                .email_client
                .compose_and_send_email(
                    user_utils::get_base_url(&state),
                    Box::new(email_contents),
                    state.conf.proxy.https_url.as_ref(),
                )
                .await
                .inspect_err(|err| {
                    router_env::logger::error!(
                        "Failed to compose and send email notifying them of recon activation: {}",
                        err
                    )
                })
                .change_context(UserErrors::InternalServerError)
                .attach_printable("Failed to compose and send email for ReconActivation");
        }
    }

    Ok(service_api::ApplicationResponse::Json(
        api_types::MerchantAccountResponse::foreign_try_from(updated_merchant_account)
            .change_context(errors::ApiErrorResponse::InvalidDataValue {
                field_name: "merchant_account",
            })?,
    ))
}


pub(crate) struct SinglePurposeJWTAuth(pub TokenPurpose);


File: crates/router/tests/connectors/bitpay.rs

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

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

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

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .bitpay
                .expect("Missing connector authentication configuration")
                .into(),
        )
    }

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

static CONNECTOR: BitpayTest = BitpayTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    Some(utils::PaymentInfo {
        address: Some(PaymentAddress::new(
            None,
            Some(Address {
                address: Some(AddressDetails {
                    first_name: Some(Secret::new("first".to_string())),
                    last_name: Some(Secret::new("last".to_string())),
                    line1: Some(Secret::new("line1".to_string())),
                    line2: Some(Secret::new("line2".to_string())),
                    city: Some("city".to_string()),
                    zip: Some(Secret::new("zip".to_string())),
                    country: Some(api_models::enums::CountryAlpha2::IN),
                    ..Default::default()
                }),
                phone: Some(PhoneDetails {
                    number: Some(Secret::new("9123456789".to_string())),
                    country_code: Some("+91".to_string()),
                }),
                email: None,
            }),
            None,
            None,
        )),
        ..Default::default()
    })
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    Some(types::PaymentsAuthorizeData {
        amount: 1,
        currency: enums::Currency::USD,
        payment_method_data: domain::PaymentMethodData::Crypto(domain::CryptoData {
            pay_currency: None,
            network: None,
        }),
        confirm: true,
        statement_descriptor_suffix: None,
        statement_descriptor: None,
        setup_future_usage: None,
        mandate_id: None,
        off_session: None,
        setup_mandate_details: None,
        // capture_method: Some(capture_method),
        browser_info: None,
        order_details: None,
        order_category: None,
        email: None,
        customer_name: None,
        payment_experience: None,
        payment_method_type: None,
        session_token: None,
        enrolled_for_3ds: false,
        related_transaction_id: None,
        router_return_url: Some(String::from("https://google.com/")),
        webhook_url: Some(String::from("https://google.com/")),
        complete_authorize_url: None,
        capture_method: None,
        customer_id: None,
        surcharge_details: None,
        request_incremental_authorization: false,
        metadata: None,
        authentication_data: None,
        customer_acceptance: None,
        ..utils::PaymentAuthorizeType::default().0
    })
}

// Creates a payment using the manual capture flow
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let resp = response.clone().response.ok().unwrap();
    assert_eq!(response.status, enums::AttemptStatus::AuthenticationPending);
    let endpoint = match resp {
        types::PaymentsResponseData::TransactionResponse {
            redirection_data, ..
        } => Some(redirection_data),
        _ => None,
    };
    assert!(endpoint.is_some())
}

// Synchronizes a successful transaction.
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    "NPf27TDfyU5mhcTCw2oaq4".to_string(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a expired transaction.
#[actix_web::test]
async fn should_sync_expired_payment() {
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    "bUsFf4RjQEahjbjGcETRS".to_string(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Failure);
}


pub struct VerifyEmail {
    pub recipient_email: domain::UserEmail,
    pub settings: std::sync::Arc<configs::Settings>,
    pub auth_id: Option<String>,
    pub theme_id: Option<String>,
    pub theme_config: EmailThemeConfig,
}


/// get_dsk_key
    pub fn get_dsl_config(&self) -> String {
        format!("dsl_{}", self.get_string_repr())
    }


impl api::PaymentToken for Nuvei {}


pub struct AdminApiAuthWithApiKeyFallbackAndMerchantIdFromRoute(pub id_type::MerchantId);


pub struct CardExpirationMonth(StrongSecret<u8>);


pub async fn contract_based_dynamic_routing_setup(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: common_utils::id_type::ProfileId,
    feature_to_enable: routing_types::DynamicRoutingFeatures,
    config: Option<routing_types::ContractBasedRoutingConfig>,
) -> RouterResult<service_api::ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let business_profile: domain::Profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")
    .change_context(errors::ApiErrorResponse::ProfileNotFound {
        id: profile_id.get_string_repr().to_owned(),
    })?;

    let mut dynamic_routing_algo_ref: Option<routing_types::DynamicRoutingAlgorithmRef> =
        business_profile
            .dynamic_routing_algorithm
            .clone()
            .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable(
                "unable to deserialize dynamic routing algorithm ref from business profile",
            )
            .ok()
            .flatten();

    utils::when(
        dynamic_routing_algo_ref
            .as_mut()
            .and_then(|algo| {
                algo.contract_based_routing.as_mut().map(|contract_algo| {
                    *contract_algo.get_enabled_features() == feature_to_enable
                        && contract_algo
                            .clone()
                            .get_algorithm_id_with_timestamp()
                            .algorithm_id
                            .is_some()
                })
            })
            .unwrap_or(false),
        || {
            Err(errors::ApiErrorResponse::PreconditionFailed {
                message: "Contract Routing with specified features is already enabled".to_string(),
            })
        },
    )?;

    if feature_to_enable == routing::DynamicRoutingFeatures::None {
        let algorithm = dynamic_routing_algo_ref
            .clone()
            .get_required_value("dynamic_routing_algo_ref")
            .attach_printable("Failed to get dynamic_routing_algo_ref")?;
        return helpers::disable_dynamic_routing_algorithm(
            &state,
            merchant_context.get_merchant_key_store().clone(),
            business_profile,
            algorithm,
            routing_types::DynamicRoutingType::ContractBasedRouting,
        )
        .await;
    }

    let config = config
        .get_required_value("ContractBasedRoutingConfig")
        .attach_printable("Failed to get ContractBasedRoutingConfig from request")?;

    let merchant_id = business_profile.merchant_id.clone();
    let algorithm_id = common_utils::generate_routing_id_of_default_length();
    let timestamp = common_utils::date_time::now();

    let algo = RoutingAlgorithm {
        algorithm_id: algorithm_id.clone(),
        profile_id: profile_id.clone(),
        merchant_id,
        name: helpers::CONTRACT_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(),
        description: None,
        kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic,
        algorithm_data: serde_json::json!(config),
        created_at: timestamp,
        modified_at: timestamp,
        algorithm_for: common_enums::TransactionType::Payment,
        decision_engine_routing_id: None,
    };

    // 1. if dynamic_routing_algo_ref already present, insert contract based algo and disable success based
    // 2. if dynamic_routing_algo_ref is not present, create a new dynamic_routing_algo_ref with contract algo set up
    let final_algorithm = if let Some(mut algo) = dynamic_routing_algo_ref {
        algo.update_algorithm_id(
            algorithm_id,
            feature_to_enable,
            routing_types::DynamicRoutingType::ContractBasedRouting,
        );
        if feature_to_enable == routing::DynamicRoutingFeatures::DynamicConnectorSelection {
            algo.disable_algorithm_id(routing_types::DynamicRoutingType::SuccessRateBasedRouting);
        }
        algo
    } else {
        let contract_algo = routing_types::ContractRoutingAlgorithm {
            algorithm_id_with_timestamp: routing_types::DynamicAlgorithmWithTimestamp::new(Some(
                algorithm_id.clone(),
            )),
            enabled_feature: feature_to_enable,
        };
        routing_types::DynamicRoutingAlgorithmRef {
            success_based_algorithm: None,
            elimination_routing_algorithm: None,
            dynamic_routing_volume_split: None,
            contract_based_routing: Some(contract_algo),
            is_merchant_created_in_decision_engine: dynamic_routing_algo_ref
                .as_ref()
                .is_some_and(|algo| algo.is_merchant_created_in_decision_engine),
        }
    };

    // validate the contained mca_ids
    let mut contained_mca = Vec::new();
    if let Some(info_vec) = &config.label_info {
        for info in info_vec {
            utils::when(
                contained_mca.iter().any(|mca_id| mca_id == &info.mca_id),
                || {
                    Err(error_stack::Report::new(
                        errors::ApiErrorResponse::InvalidRequestData {
                            message: "Duplicate mca configuration received".to_string(),
                        },
                    ))
                },
            )?;

            contained_mca.push(info.mca_id.to_owned());
        }

        let validation_futures: Vec<_> = info_vec
            .iter()
            .map(|info| async {
                let mca_id = info.mca_id.clone();
                let label = info.label.clone();
                let mca = db
                    .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
                        key_manager_state,
                        merchant_context.get_merchant_account().get_id(),
                        &mca_id,
                        merchant_context.get_merchant_key_store(),
                    )
                    .await
                    .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                        id: mca_id.get_string_repr().to_owned(),
                    })?;

                utils::when(mca.connector_name != label, || {
                    Err(error_stack::Report::new(
                        errors::ApiErrorResponse::InvalidRequestData {
                            message: "Incorrect mca configuration received".to_string(),
                        },
                    ))
                })?;

                Ok::<_, error_stack::Report<errors::ApiErrorResponse>>(())
            })
            .collect();

        futures::future::try_join_all(validation_futures).await?;
    }

    let record = db
        .insert_routing_algorithm(algo)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to insert record in routing algorithm table")?;

    helpers::update_business_profile_active_dynamic_algorithm_ref(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        business_profile,
        final_algorithm,
    )
    .await?;

    let new_record = record.foreign_into();

    metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(
        1,
        router_env::metric_attributes!(("profile_id", profile_id.get_string_repr().to_string())),
    );
    Ok(service_api::ApplicationResponse::Json(new_record))
}


pub async fn get_totp_secret_from_redis(
    state: &SessionState,
    user_id: &str,
) -> UserResult<Option<masking::Secret<String>>> {
    let redis_conn = super::get_redis_connection_for_global_tenant(state)?;
    redis_conn
        .get_key::<Option<String>>(&get_totp_secret_key(user_id).into())
        .await
        .change_context(UserErrors::InternalServerError)
        .map(|secret| secret.map(Into::into))
}


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


pub async fn perform_execute_payment(
    state: &SessionState,
    execute_task_process: &storage::ProcessTracker,
    profile: &domain::Profile,
    merchant_context: domain::MerchantContext,
    tracking_data: &pcr::RevenueRecoveryWorkflowTrackingData,
    revenue_recovery_payment_data: &pcr::RevenueRecoveryPaymentData,
    payment_intent: &PaymentIntent,
) -> Result<(), sch_errors::ProcessTrackerError> {
    let db = &*state.store;

    let mut revenue_recovery_metadata = payment_intent
        .feature_metadata
        .as_ref()
        .and_then(|feature_metadata| feature_metadata.payment_revenue_recovery_metadata.clone())
        .get_required_value("Payment Revenue Recovery Metadata")?
        .convert_back();

    let decision = types::Decision::get_decision_based_on_params(
        state,
        payment_intent.status,
        revenue_recovery_metadata
            .payment_connector_transmission
            .unwrap_or_default(),
        payment_intent.active_attempt_id.clone(),
        revenue_recovery_payment_data,
        &tracking_data.global_payment_id,
    )
    .await?;

    // TODO decide if its a global failure or is it requeueable error
    match decision {
        types::Decision::Execute => {
            let connector_customer_id = revenue_recovery_metadata.get_connector_customer_id();

            let last_token_used = payment_intent
                .feature_metadata
                .as_ref()
                .and_then(|fm| fm.payment_revenue_recovery_metadata.as_ref())
                .map(|rr| {
                    rr.billing_connector_payment_details
                        .payment_processor_token
                        .clone()
                });

            let processor_token = storage::revenue_recovery_redis_operation::RedisTokenManager::get_token_based_on_retry_type(
                state,
                &connector_customer_id,
                tracking_data.revenue_recovery_retry,
                last_token_used.as_deref(),
                )
                .await
                .change_context(errors::ApiErrorResponse::GenericNotFoundError {
                    message: "Failed to fetch token details from redis".to_string(),
                })?
                .ok_or(errors::ApiErrorResponse::GenericNotFoundError {
                    message: "Failed to fetch token details from redis".to_string(),
            })?;
            logger::info!("Token fetched from redis success");
            let card_info =
                api_models::payments::AdditionalCardInfo::foreign_from(&processor_token);
            // record attempt call
            let record_attempt = api::record_internal_attempt_api(
                state,
                payment_intent,
                revenue_recovery_payment_data,
                &revenue_recovery_metadata,
                card_info,
                &processor_token
                    .payment_processor_token_details
                    .payment_processor_token,
            )
            .await;

            match record_attempt {
                Ok(_) => {
                    let action = Box::pin(types::Action::execute_payment(
                        state,
                        revenue_recovery_payment_data.merchant_account.get_id(),
                        payment_intent,
                        execute_task_process,
                        profile,
                        merchant_context,
                        revenue_recovery_payment_data,
                        &revenue_recovery_metadata,
                    ))
                    .await?;
                    Box::pin(action.execute_payment_task_response_handler(
                        state,
                        payment_intent,
                        execute_task_process,
                        revenue_recovery_payment_data,
                        &mut revenue_recovery_metadata,
                    ))
                    .await?;
                }
                Err(err) => {
                    logger::error!("Error while recording attempt: {:?}", err);
                    let pt_update = storage::ProcessTrackerUpdate::StatusUpdate {
                        status: enums::ProcessTrackerStatus::Pending,
                        business_status: Some(String::from(
                            business_status::EXECUTE_WORKFLOW_REQUEUE,
                        )),
                    };
                    db.as_scheduler()
                        .update_process(execute_task_process.clone(), pt_update)
                        .await?;
                }
            }
        }

        types::Decision::Psync(attempt_status, attempt_id) => {
            // find if a psync task is already present
            let task = PSYNC_WORKFLOW;
            let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;
            let process_tracker_id = attempt_id.get_psync_revenue_recovery_id(task, runner);
            let psync_process = db.find_process_by_id(&process_tracker_id).await?;

            match psync_process {
                Some(_) => {
                    let pcr_status: types::RevenueRecoveryPaymentsAttemptStatus =
                        attempt_status.foreign_into();

                    pcr_status
                        .update_pt_status_based_on_attempt_status_for_execute_payment(
                            db,
                            execute_task_process,
                        )
                        .await?;
                }

                None => {
                    // insert new psync task
                    insert_psync_pcr_task_to_pt(
                        revenue_recovery_payment_data.billing_mca.get_id().clone(),
                        db,
                        revenue_recovery_payment_data
                            .merchant_account
                            .get_id()
                            .clone(),
                        payment_intent.get_id().clone(),
                        revenue_recovery_payment_data.profile.get_id().clone(),
                        attempt_id.clone(),
                        storage::ProcessTrackerRunner::PassiveRecoveryWorkflow,
                        tracking_data.revenue_recovery_retry,
                    )
                    .await?;

                    // finish the current task
                    db.as_scheduler()
                        .finish_process_with_business_status(
                            execute_task_process.clone(),
                            business_status::EXECUTE_WORKFLOW_COMPLETE_FOR_PSYNC,
                        )
                        .await?;
                }
            };
        }
        types::Decision::ReviewForSuccessfulPayment => {
            // Finish the current task since the payment was a success
            // And mark it as review as it might have happened through the external system
            db.finish_process_with_business_status(
                execute_task_process.clone(),
                business_status::EXECUTE_WORKFLOW_COMPLETE_FOR_REVIEW,
            )
            .await?;
        }
        types::Decision::ReviewForFailedPayment(triggered_by) => {
            match triggered_by {
                enums::TriggeredBy::Internal => {
                    // requeue the current tasks to update the fields for rescheduling a payment
                    let pt_update = storage::ProcessTrackerUpdate::StatusUpdate {
                        status: enums::ProcessTrackerStatus::Pending,
                        business_status: Some(String::from(
                            business_status::EXECUTE_WORKFLOW_REQUEUE,
                        )),
                    };
                    db.as_scheduler()
                        .update_process(execute_task_process.clone(), pt_update)
                        .await?;
                }
                enums::TriggeredBy::External => {
                    logger::debug!("Failed Payment Attempt Triggered By External");
                    // Finish the current task since the payment was a failure by an external source
                    db.as_scheduler()
                        .finish_process_with_business_status(
                            execute_task_process.clone(),
                            business_status::EXECUTE_WORKFLOW_COMPLETE_FOR_REVIEW,
                        )
                        .await?;
                }
            };
        }
        types::Decision::InvalidDecision => {
            db.finish_process_with_business_status(
                execute_task_process.clone(),
                business_status::EXECUTE_WORKFLOW_COMPLETE,
            )
            .await?;
            logger::warn!("Abnormal State Identified")
        }
    }

    Ok(())
}


impl api::Refund for Nuvei {}


pub fn server(state: AppState) -> Scope {
        web::scope("/v2/api-keys")
            .app_data(web::Data::new(state))
            .service(web::resource("").route(web::post().to(api_keys::api_key_create)))
            .service(web::resource("/list").route(web::get().to(api_keys::api_key_list)))
            .service(
                web::resource("/{key_id}")
                    .route(web::get().to(api_keys::api_key_retrieve))
                    .route(web::put().to(api_keys::api_key_update))
                    .route(web::delete().to(api_keys::api_key_revoke)),
            )
    }


OpenAPI Block Path: components.schemas.RuleThreeDsDecisionRule
{
  "type": "object",
  "required": [
    "name",
    "connectorSelection",
    "statements"
  ],
  "properties": {
    "name": {
      "type": "string"
    },
    "connectorSelection": {
      "$ref": "#/components/schemas/ThreeDSDecision"
    },
    "statements": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/IfStatement"
      }
    }
  }
}


pub struct PaymentAccountInformation {
    card: Option<PaymentAccountCardInformation>,
    features: Option<PaymentAccountFeatureInformation>,
}


OpenAPI Block Path: paths."/three_ds_decision/execute"
{
  "post": {
    "tags": [
      "3DS Decision Rule"
    ],
    "summary": "3DS Decision - Execute",
    "operationId": "Execute 3DS Decision Rule",
    "requestBody": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/ThreeDsDecisionRuleExecuteRequest"
          }
        }
      },
      "required": true
    },
    "responses": {
      "200": {
        "description": "3DS Decision Rule Executed Successfully",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ThreeDsDecisionRuleExecuteResponse"
            }
          }
        }
      },
      "400": {
        "description": "Bad Request"
      }
    },
    "security": [
      {
        "api_key": []
      }
    ]
  }
}


/// Response of the retrieved routing configs for a merchant account
pub struct RoutingRetrieveResponse {
    pub algorithm: Option<MerchantRoutingAlgorithm>,
}


File: crates/api_models/src/events/customer.rs

use common_utils::events::{ApiEventMetric, ApiEventsType};

use crate::customers::{
    CustomerDeleteResponse, CustomerRequest, CustomerResponse, CustomerUpdateRequestInternal,
};

#[cfg(feature = "v1")]
impl ApiEventMetric for CustomerDeleteResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Customer {
            customer_id: self.customer_id.clone(),
        })
    }
}

#[cfg(feature = "v2")]
impl ApiEventMetric for CustomerDeleteResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Customer {
            customer_id: Some(self.id.clone()),
        })
    }
}

#[cfg(feature = "v1")]
impl ApiEventMetric for CustomerRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        self.get_merchant_reference_id()
            .clone()
            .map(|cid| ApiEventsType::Customer { customer_id: cid })
    }
}

#[cfg(feature = "v2")]
impl ApiEventMetric for CustomerRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Customer { customer_id: None })
    }
}

#[cfg(feature = "v1")]
impl ApiEventMetric for CustomerResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        self.get_merchant_reference_id()
            .clone()
            .map(|cid| ApiEventsType::Customer { customer_id: cid })
    }
}

#[cfg(feature = "v2")]
impl ApiEventMetric for CustomerResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Customer {
            customer_id: Some(self.id.clone()),
        })
    }
}

#[cfg(feature = "v1")]
impl ApiEventMetric for CustomerUpdateRequestInternal {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Customer {
            customer_id: self.customer_id.clone(),
        })
    }
}

#[cfg(feature = "v2")]
impl ApiEventMetric for CustomerUpdateRequestInternal {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Customer {
            customer_id: Some(self.id.clone()),
        })
    }
}


File: crates/analytics/src/connector_events/events.rs
 Public functions: 1
 Public structs: 1

use api_models::analytics::{connector_events::ConnectorEventsRequest, Granularity};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use crate::{
    query::{Aggregate, GroupByClause, QueryBuilder, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, FiltersError, FiltersResult, LoadRow},
};
pub trait ConnectorEventLogAnalytics: LoadRow<ConnectorEventsResult> {}

pub async fn get_connector_events<T>(
    merchant_id: &common_utils::id_type::MerchantId,
    query_param: ConnectorEventsRequest,
    pool: &T,
) -> FiltersResult<Vec<ConnectorEventsResult>>
where
    T: AnalyticsDataSource + ConnectorEventLogAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    let mut query_builder: QueryBuilder<T> =
        QueryBuilder::new(AnalyticsCollection::ConnectorEvents);
    query_builder.add_select_column("*").switch()?;

    query_builder
        .add_filter_clause("merchant_id", merchant_id)
        .switch()?;

    query_builder
        .add_filter_clause("payment_id", &query_param.payment_id)
        .switch()?;

    if let Some(refund_id) = query_param.refund_id {
        query_builder
            .add_filter_clause("refund_id", &refund_id)
            .switch()?;
    }

    if let Some(dispute_id) = query_param.dispute_id {
        query_builder
            .add_filter_clause("dispute_id", &dispute_id)
            .switch()?;
    }

    //TODO!: update the execute_query function to return reports instead of plain errors...
    query_builder
        .execute_query::<ConnectorEventsResult, _>(pool)
        .await
        .change_context(FiltersError::QueryBuildingError)?
        .change_context(FiltersError::QueryExecutionFailure)
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct ConnectorEventsResult {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: String,
    pub connector_name: Option<String>,
    pub request_id: Option<String>,
    pub flow: String,
    pub request: String,
    #[serde(rename = "masked_response")]
    pub response: Option<String>,
    pub error: Option<String>,
    pub status_code: u16,
    pub latency: Option<u128>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    pub method: Option<String>,
}


pub struct MerchantLevel {
    pub tenant_id: id_type::TenantId,
    pub org_id: id_type::OrganizationId,
    pub merchant_id: id_type::MerchantId,
}


File: crates/analytics/src/frm/filters.rs
 Public functions: 1
 Public structs: 1

use api_models::analytics::{
    frm::{FrmDimensions, FrmTransactionType},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use diesel_models::enums::FraudCheckStatus;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use crate::{
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
    types::{
        AnalyticsCollection, AnalyticsDataSource, DBEnumWrapper, FiltersError, FiltersResult,
        LoadRow,
    },
};
pub trait FrmFilterAnalytics: LoadRow<FrmFilterRow> {}

pub async fn get_frm_filter_for_dimension<T>(
    dimension: FrmDimensions,
    merchant_id: &common_utils::id_type::MerchantId,
    time_range: &TimeRange,
    pool: &T,
) -> FiltersResult<Vec<FrmFilterRow>>
where
    T: AnalyticsDataSource + FrmFilterAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::FraudCheck);

    query_builder.add_select_column(dimension).switch()?;
    time_range
        .set_filter_clause(&mut query_builder)
        .attach_printable("Error filtering time range")
        .switch()?;

    query_builder
        .add_filter_clause("merchant_id", merchant_id)
        .switch()?;

    query_builder.set_distinct();

    query_builder
        .execute_query::<FrmFilterRow, _>(pool)
        .await
        .change_context(FiltersError::QueryBuildingError)?
        .change_context(FiltersError::QueryExecutionFailure)
}

#[derive(Debug, serde::Serialize, Eq, PartialEq, serde::Deserialize)]
pub struct FrmFilterRow {
    pub frm_status: Option<DBEnumWrapper<FraudCheckStatus>>,
    pub frm_transaction_type: Option<DBEnumWrapper<FrmTransactionType>>,
    pub frm_name: Option<String>,
}