// File: crates/router/src/core/payments/operations/payment_create.rs
// Module: router
// Public functions: 4
// Public structs: 1

use std::marker::PhantomData;

use api_models::{
    enums::FrmSuggestion, mandates::RecurringDetails, payment_methods::PaymentMethodsData,
    payments::GetAddressFromPaymentMethodData,
};
use async_trait::async_trait;
use common_types::payments as common_payments_types;
use common_utils::{
    ext_traits::{AsyncExt, Encode, ValueExt},
    type_name,
    types::{
        keymanager::{Identifier, KeyManagerState, ToEncryptable},
        MinorUnit,
    },
};
use diesel_models::{
    ephemeral_key,
    payment_attempt::ConnectorMandateReferenceId as DieselConnectorMandateReferenceId,
};
use error_stack::{self, ResultExt};
use hyperswitch_domain_models::{
    mandates::MandateDetails,
    payments::{
        payment_attempt::PaymentAttempt, payment_intent::CustomerData,
        FromRequestEncryptablePaymentIntent,
    },
};
use masking::{ExposeInterface, PeekInterface, Secret};
use router_derive::PaymentOperation;
use router_env::{instrument, logger, tracing};
use time::PrimitiveDateTime;

use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
use crate::{
    consts,
    core::{
        errors::{self, CustomResult, RouterResult, StorageErrorExt},
        mandate::helpers as m_helpers,
        payment_link,
        payment_methods::cards::create_encrypted_data,
        payments::{self, helpers, operations, CustomerDetails, PaymentAddress, PaymentData},
        utils as core_utils,
    },
    db::StorageInterface,
    events::audit_events::{AuditEvent, AuditEventType},
    routes::{app::ReqState, SessionState},
    services,
    types::{
        self,
        api::{self, ConnectorCallType, PaymentIdTypeExt},
        domain,
        storage::{
            self,
            enums::{self, IntentStatus},
        },
        transformers::{ForeignFrom, ForeignTryFrom},
    },
    utils::{self, OptionExt},
};

#[derive(Debug, Clone, Copy, PaymentOperation)]
#[operation(operations = "all", flow = "authorize")]
pub struct PaymentCreate;

type PaymentCreateOperation<'a, F> = BoxedOperation<'a, F, api::PaymentsRequest, PaymentData<F>>;

/// The `get_trackers` function for `PaymentsCreate` is an entrypoint for new payments
/// This will create all the entities required for a new payment from the request
#[async_trait]
impl<F: Send + Clone + Sync> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for PaymentCreate {
    #[instrument(skip_all)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &api::PaymentIdType,
        request: &api::PaymentsRequest,
        merchant_context: &domain::MerchantContext,
        _auth_flow: services::AuthFlow,
        header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<operations::GetTrackerResponse<'a, F, api::PaymentsRequest, PaymentData<F>>>
    {
        let db = &*state.store;
        let key_manager_state = &state.into();
        let ephemeral_key = Self::get_ephemeral_key(request, state, merchant_context).await;
        let merchant_id = merchant_context.get_merchant_account().get_id();
        let storage_scheme = merchant_context.get_merchant_account().storage_scheme;

        let money @ (amount, currency) = payments_create_request_validation(request)?;

        let payment_id = payment_id
            .get_payment_intent_id()
            .change_context(errors::ApiErrorResponse::PaymentNotFound)?;

        #[cfg(feature = "v1")]
        helpers::validate_business_details(
            request.business_country,
            request.business_label.as_ref(),
            merchant_context,
        )?;

        // If profile id is not passed, get it from the business_country and business_label
        #[cfg(feature = "v1")]
        let profile_id = core_utils::get_profile_id_from_business_details(
            key_manager_state,
            request.business_country,
            request.business_label.as_ref(),
            merchant_context,
            request.profile_id.as_ref(),
            &*state.store,
            true,
        )
        .await?;

        // Profile id will be mandatory in v2 in the request / headers
        #[cfg(feature = "v2")]
        let profile_id = request
            .profile_id
            .clone()
            .get_required_value("profile_id")
            .attach_printable("Profile id is a mandatory parameter")?;

        // TODO: eliminate a redundant db call to fetch the business profile
        // Validate whether profile_id passed in request is valid and is linked to the merchant
        let business_profile = if let Some(business_profile) =
            core_utils::validate_and_get_business_profile(
                db,
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                Some(&profile_id),
                merchant_id,
            )
            .await?
        {
            business_profile
        } else {
            db.find_business_profile_by_profile_id(
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                &profile_id,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
                id: profile_id.get_string_repr().to_owned(),
            })?
        };
        let customer_acceptance = request.customer_acceptance.clone();

        let recurring_details = request.recurring_details.clone();

        let mandate_type = m_helpers::get_mandate_type(
            request.mandate_data.clone(),
            request.off_session,
            request.setup_future_usage,
            request.customer_acceptance.clone(),
            request.payment_token.clone(),
            request.payment_method,
        )
        .change_context(errors::ApiErrorResponse::MandateValidationFailed {
            reason: "Expected one out of recurring_details and mandate_data but got both".into(),
        })?;

        let m_helpers::MandateGenericData {
            token,
            payment_method,
            payment_method_type,
            mandate_data,
            recurring_mandate_payment_data,
            mandate_connector,
            payment_method_info,
        } = helpers::get_token_pm_type_mandate_details(
            state,
            request,
            mandate_type,
            merchant_context,
            None,
            None,
        )
        .await?;

        helpers::validate_allowed_payment_method_types_request(
            state,
            &profile_id,
            merchant_context,
            request.allowed_payment_method_types.clone(),
        )
        .await?;

        let customer_details = helpers::get_customer_details_from_request(request);

        let shipping_address = helpers::create_or_find_address_for_payment_by_request(
            state,
            request.shipping.as_ref(),
            None,
            merchant_id,
            customer_details.customer_id.as_ref(),
            merchant_context.get_merchant_key_store(),
            &payment_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;

        let billing_address = helpers::create_or_find_address_for_payment_by_request(
            state,
            request.billing.as_ref(),
            None,
            merchant_id,
            customer_details.customer_id.as_ref(),
            merchant_context.get_merchant_key_store(),
            &payment_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;

        let payment_method_billing_address =
            helpers::create_or_find_address_for_payment_by_request(
                state,
                request
                    .payment_method_data
                    .as_ref()
                    .and_then(|pmd| pmd.billing.as_ref()),
                None,
                merchant_id,
                customer_details.customer_id.as_ref(),
                merchant_context.get_merchant_key_store(),
                &payment_id,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await?;

        let browser_info = request
            .browser_info
            .clone()
            .as_ref()
            .map(Encode::encode_to_value)
            .transpose()
            .change_context(errors::ApiErrorResponse::InvalidDataValue {
                field_name: "browser_info",
            })?;

        let attempt_id = if core_utils::is_merchant_enabled_for_payment_id_as_connector_request_id(
            &state.conf,
            merchant_id,
        ) {
            payment_id.get_string_repr().to_string()
        } else {
            payment_id.get_attempt_id(1)
        };

        let session_expiry =
            common_utils::date_time::now().saturating_add(time::Duration::seconds(
                request.session_expiry.map(i64::from).unwrap_or(
                    business_profile
                        .session_expiry
                        .unwrap_or(consts::DEFAULT_SESSION_EXPIRY),
                ),
            ));

        let payment_link_data = match request.payment_link {
            Some(true) => {
                let merchant_name = merchant_context
                    .get_merchant_account()
                    .merchant_name
                    .clone()
                    .map(|name| name.into_inner().peek().to_owned())
                    .unwrap_or_default();

                let default_domain_name = state.base_url.clone();

                let (payment_link_config, domain_name) =
                    payment_link::get_payment_link_config_based_on_priority(
                        request.payment_link_config.clone(),
                        business_profile.payment_link_config.clone(),
                        merchant_name,
                        default_domain_name,
                        request.payment_link_config_id.clone(),
                    )?;

                create_payment_link(
                    request,
                    payment_link_config,
                    merchant_id,
                    payment_id.clone(),
                    db,
                    amount,
                    request.description.clone(),
                    profile_id.clone(),
                    domain_name,
                    session_expiry,
                    header_payload.locale.clone(),
                )
                .await?
            }
            _ => None,
        };

        let payment_intent_new = Self::make_payment_intent(
            state,
            &payment_id,
            merchant_context,
            money,
            request,
            shipping_address
                .as_ref()
                .map(|address| address.address_id.clone()),
            payment_link_data.clone(),
            billing_address
                .as_ref()
                .map(|address| address.address_id.clone()),
            attempt_id,
            profile_id.clone(),
            session_expiry,
            &business_profile,
            request.is_payment_id_from_merchant,
        )
        .await?;

        let (payment_attempt_new, additional_payment_data) = Self::make_payment_attempt(
            &payment_id,
            merchant_id,
            &merchant_context.get_merchant_account().organization_id,
            money,
            payment_method,
            payment_method_type,
            request,
            browser_info,
            state,
            payment_method_billing_address
                .as_ref()
                .map(|address| address.address_id.clone()),
            &payment_method_info,
            merchant_context.get_merchant_key_store(),
            profile_id,
            &customer_acceptance,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;

        let payment_intent = db
            .insert_payment_intent(
                key_manager_state,
                payment_intent_new,
                merchant_context.get_merchant_key_store(),
                storage_scheme,
            )
            .await
            .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment {
                payment_id: payment_id.clone(),
            })?;

        if let Some(order_details) = &request.order_details {
            helpers::validate_order_details_amount(
                order_details.to_owned(),
                payment_intent.amount,
                false,
            )?;
        }

        #[cfg(feature = "v1")]
        let mut payment_attempt = db
            .insert_payment_attempt(payment_attempt_new, storage_scheme)
            .await
            .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment {
                payment_id: payment_id.clone(),
            })?;

        #[cfg(feature = "v2")]
        let payment_attempt = db
            .insert_payment_attempt(
                key_manager_state,
                merchant_key_store,
                payment_attempt_new,
                storage_scheme,
            )
            .await
            .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment {
                payment_id: payment_id.clone(),
            })?;

        let mandate_details_present = payment_attempt.mandate_details.is_some();

        helpers::validate_mandate_data_and_future_usage(
            request.setup_future_usage,
            mandate_details_present,
        )?;
        // connector mandate reference update history
        let mandate_id = request
            .mandate_id
            .as_ref()
            .or_else(|| {
            request.recurring_details
                .as_ref()
                .and_then(|recurring_details| match recurring_details {
                    RecurringDetails::MandateId(id) => Some(id),
                    _ => None,
                })
        })
            .async_and_then(|mandate_id| async {
                let mandate = db
                    .find_mandate_by_merchant_id_mandate_id(merchant_id, mandate_id, storage_scheme)
                    .await
                    .to_not_found_response(errors::ApiErrorResponse::MandateNotFound);
                Some(mandate.and_then(|mandate_obj| {
                    match (
                        mandate_obj.network_transaction_id,
                        mandate_obj.connector_mandate_ids,
                    ) {
                        (_, Some(connector_mandate_id)) => connector_mandate_id
                        .parse_value("ConnectorMandateId")
                        .change_context(errors::ApiErrorResponse::MandateNotFound)
                        .map(|connector_id: api_models::payments::ConnectorMandateReferenceId| {
                            api_models::payments::MandateIds {
                                mandate_id: Some(mandate_obj.mandate_id),
                                mandate_reference_id: Some(api_models::payments::MandateReferenceId::ConnectorMandateId(
                                api_models::payments::ConnectorMandateReferenceId::new(
                                    connector_id.get_connector_mandate_id(),
                                    connector_id.get_payment_method_id(),
                                    None,
                                    None,
                                    connector_id.get_connector_mandate_request_reference_id(),
                                )
                                ))
                            }
                         }),
                        (Some(network_tx_id), _) => Ok(api_models::payments::MandateIds {
                            mandate_id: Some(mandate_obj.mandate_id),
                            mandate_reference_id: Some(
                                api_models::payments::MandateReferenceId::NetworkMandateId(
                                    network_tx_id,
                                ),
                            ),
                        }),
                        (_, _) => Ok(api_models::payments::MandateIds {
                            mandate_id: Some(mandate_obj.mandate_id),
                            mandate_reference_id: None,
                        }),
                    }
                }))
            })
            .await
            .transpose()?;

        let mandate_id = if mandate_id.is_none() {
            request
                .recurring_details
                .as_ref()
                .and_then(|recurring_details| match recurring_details {
                    RecurringDetails::ProcessorPaymentToken(token) => {
                        Some(api_models::payments::MandateIds {
                            mandate_id: None,
                            mandate_reference_id: Some(
                                api_models::payments::MandateReferenceId::ConnectorMandateId(
                                    api_models::payments::ConnectorMandateReferenceId::new(
                                        Some(token.processor_payment_token.clone()),
                                        None,
                                        None,
                                        None,
                                        None,
                                    ),
                                ),
                            ),
                        })
                    }
                    _ => None,
                })
        } else {
            mandate_id
        };
        let operation = payments::if_not_create_change_operation::<_, F>(
            payment_intent.status,
            request.confirm,
            self,
        );

        let creds_identifier = request
            .merchant_connector_details
            .as_ref()
            .map(|mcd| mcd.creds_identifier.to_owned());
        request
            .merchant_connector_details
            .to_owned()
            .async_map(|mcd| async {
                helpers::insert_merchant_connector_creds_to_config(
                    db,
                    merchant_context.get_merchant_account().get_id(),
                    mcd,
                )
                .await
            })
            .await
            .transpose()?;

        // The operation merges mandate data from both request and payment_attempt
        let setup_mandate = mandate_data;

        let surcharge_details = request.surcharge_details.map(|request_surcharge_details| {
            payments::types::SurchargeDetails::from((&request_surcharge_details, &payment_attempt))
        });

        let payment_method_data_after_card_bin_call = request
            .payment_method_data
            .as_ref()
            .and_then(|payment_method_data_from_request| {
                payment_method_data_from_request
                    .payment_method_data
                    .as_ref()
            })
            .zip(additional_payment_data)
            .map(|(payment_method_data, additional_payment_data)| {
                payment_method_data.apply_additional_payment_data(additional_payment_data)
            })
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Card cobadge check failed due to an invalid card network regex")?;

        let additional_pm_data_from_locker = if let Some(ref pm) = payment_method_info {
            let card_detail_from_locker: Option<api::CardDetailFromLocker> = pm
                .payment_method_data
                .clone()
                .map(|x| x.into_inner().expose())
                .and_then(|v| {
                    v.parse_value("PaymentMethodsData")
                        .map_err(|err| {
                            router_env::logger::info!(
                                "PaymentMethodsData deserialization failed: {:?}",
                                err
                            )
                        })
                        .ok()
                })
                .and_then(|pmd| match pmd {
                    PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)),
                    _ => None,
                });

            card_detail_from_locker.map(|card_details| {
                let additional_data = card_details.into();
                api_models::payments::AdditionalPaymentData::Card(Box::new(additional_data))
            })
        } else {
            None
        };
        // Only set `payment_attempt.payment_method_data` if `additional_pm_data_from_locker` is not None
        if let Some(additional_pm_data) = additional_pm_data_from_locker.as_ref() {
            payment_attempt.payment_method_data = Some(
                Encode::encode_to_value(additional_pm_data)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to encode additional pm data")?,
            );
        }
        let amount = payment_attempt.get_total_amount().into();

        payment_attempt.connector_mandate_detail =
            Some(DieselConnectorMandateReferenceId::foreign_from(
                api_models::payments::ConnectorMandateReferenceId::new(
                    None,
                    None,
                    None, // update_history
                    None, // mandate_metadata
                    Some(common_utils::generate_id_with_len(
                        consts::CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH,
                    )), // connector_mandate_request_reference_id
                ),
            ));

        let address = PaymentAddress::new(
            shipping_address.as_ref().map(From::from),
            billing_address.as_ref().map(From::from),
            payment_method_billing_address.as_ref().map(From::from),
            business_profile.use_billing_as_payment_method_billing,
        );

        let payment_method_data_billing = request
            .payment_method_data
            .as_ref()
            .and_then(|pmd| pmd.payment_method_data.as_ref())
            .and_then(|payment_method_data_billing| {
                payment_method_data_billing.get_billing_address()
            })
            .map(From::from);

        let unified_address =
            address.unify_with_payment_method_data_billing(payment_method_data_billing);

        let payment_data = PaymentData {
            flow: PhantomData,
            payment_intent,
            payment_attempt,
            currency,
            amount,
            email: request.email.clone(),
            mandate_id: mandate_id.clone(),
            mandate_connector,
            setup_mandate,
            customer_acceptance,
            token,
            address: unified_address,
            token_data: None,
            confirm: request.confirm,
            payment_method_data: payment_method_data_after_card_bin_call.map(Into::into),
            payment_method_token: None,
            payment_method_info,
            refunds: vec![],
            disputes: vec![],
            attempts: None,
            force_sync: None,
            all_keys_required: None,
            sessions_token: vec![],
            card_cvc: request.card_cvc.clone(),
            creds_identifier,
            pm_token: None,
            connector_customer_id: None,
            recurring_mandate_payment_data,
            ephemeral_key,
            multiple_capture_data: None,
            redirect_response: None,
            surcharge_details,
            frm_message: None,
            payment_link_data,
            incremental_authorization_details: None,
            authorizations: vec![],
            authentication: None,
            recurring_details,
            poll_config: None,
            tax_data: None,
            session_id: None,
            service_details: None,
            card_testing_guard_data: None,
            vault_operation: None,
            threeds_method_comp_ind: None,
            whole_connector_response: None,
            is_manual_retry_enabled: None,
        };

        let get_trackers_response = operations::GetTrackerResponse {
            operation,
            customer_details: Some(customer_details),
            payment_data,
            business_profile,
            mandate_type,
        };

        Ok(get_trackers_response)
    }
}

#[async_trait]
impl<F: Clone + Send + Sync> Domain<F, api::PaymentsRequest, PaymentData<F>> for PaymentCreate {
    #[instrument(skip_all)]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut PaymentData<F>,
        request: Option<CustomerDetails>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<(PaymentCreateOperation<'a, F>, Option<domain::Customer>), errors::StorageError>
    {
        helpers::create_customer_if_not_exist(
            state,
            Box::new(self),
            payment_data,
            request,
            &key_store.merchant_id,
            key_store,
            storage_scheme,
        )
        .await
    }

    async fn payments_dynamic_tax_calculation<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut PaymentData<F>,
        _connector_call_type: &ConnectorCallType,
        business_profile: &domain::Profile,
        merchant_context: &domain::MerchantContext,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        let is_tax_connector_enabled = business_profile.get_is_tax_connector_enabled();
        let skip_external_tax_calculation = payment_data
            .payment_intent
            .skip_external_tax_calculation
            .unwrap_or(false);
        if is_tax_connector_enabled && !skip_external_tax_calculation {
            let db = state.store.as_ref();

            let key_manager_state: &KeyManagerState = &state.into();

            let merchant_connector_id = business_profile
                .tax_connector_id
                .as_ref()
                .get_required_value("business_profile.tax_connector_id")?;

            #[cfg(feature = "v1")]
            let mca = db
                .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
                    key_manager_state,
                    &business_profile.merchant_id,
                    merchant_connector_id,
                    merchant_context.get_merchant_key_store(),
                )
                .await
                .to_not_found_response(
                    errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                        id: merchant_connector_id.get_string_repr().to_string(),
                    },
                )?;

            #[cfg(feature = "v2")]
            let mca = db
                .find_merchant_connector_account_by_id(
                    key_manager_state,
                    merchant_connector_id,
                    key_store,
                )
                .await
                .to_not_found_response(
                    errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                        id: merchant_connector_id.get_string_repr().to_string(),
                    },
                )?;

            let connector_data =
                api::TaxCalculateConnectorData::get_connector_by_name(&mca.connector_name)?;

            let router_data = core_utils::construct_payments_dynamic_tax_calculation_router_data(
                state,
                merchant_context,
                payment_data,
                &mca,
            )
            .await?;
            let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
                api::CalculateTax,
                types::PaymentsTaxCalculationData,
                types::TaxCalculationResponseData,
            > = connector_data.connector.get_connector_integration();

            let response = services::execute_connector_processing_step(
                state,
                connector_integration,
                &router_data,
                payments::CallConnectorAction::Trigger,
                None,
                None,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Tax connector Response Failed")?;

            let tax_response = response.response.map_err(|err| {
                errors::ApiErrorResponse::ExternalConnectorError {
                    code: err.code,
                    message: err.message,
                    connector: connector_data.connector_name.clone().to_string(),
                    status_code: err.status_code,
                    reason: err.reason,
                }
            })?;

            payment_data.payment_intent.tax_details = Some(diesel_models::TaxDetails {
                default: Some(diesel_models::DefaultTax {
                    order_tax_amount: tax_response.order_tax_amount,
                }),
                payment_method_type: None,
            });

            Ok(())
        } else {
            Ok(())
        }
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: &mut PaymentData<F>,
        storage_scheme: enums::MerchantStorageScheme,
        merchant_key_store: &domain::MerchantKeyStore,
        customer: &Option<domain::Customer>,
        business_profile: &domain::Profile,
        should_retry_with_pan: bool,
    ) -> RouterResult<(
        PaymentCreateOperation<'a, F>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Box::pin(helpers::make_pm_data(
            Box::new(self),
            state,
            payment_data,
            merchant_key_store,
            customer,
            storage_scheme,
            business_profile,
            should_retry_with_pan,
        ))
        .await
    }

    #[instrument(skip_all)]
    async fn add_task_to_process_tracker<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_attempt: &PaymentAttempt,
        _requeue: bool,
        _schedule_time: Option<PrimitiveDateTime>,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    async fn get_connector<'a>(
        &'a self,
        _merchant_context: &domain::MerchantContext,
        state: &SessionState,
        request: &api::PaymentsRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, request.routing.clone()).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut PaymentData<F>,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[async_trait]
impl<F: Clone + Sync> UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> for PaymentCreate {
    #[instrument(skip_all)]
    async fn update_trackers<'b>(
        &'b self,
        state: &'b SessionState,
        req_state: ReqState,
        mut payment_data: PaymentData<F>,
        customer: Option<domain::Customer>,
        storage_scheme: enums::MerchantStorageScheme,
        _updated_customer: Option<storage::CustomerUpdate>,
        key_store: &domain::MerchantKeyStore,
        _frm_suggestion: Option<FrmSuggestion>,
        _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(PaymentCreateOperation<'b, F>, PaymentData<F>)>
    where
        F: 'b + Send,
    {
        let status = match payment_data.payment_intent.status {
            IntentStatus::RequiresPaymentMethod => match payment_data.payment_method_data {
                Some(_) => Some(IntentStatus::RequiresConfirmation),
                _ => None,
            },
            IntentStatus::RequiresConfirmation => {
                if let Some(true) = payment_data.confirm {
                    //TODO: do this later, request validation should happen before
                    Some(IntentStatus::Processing)
                } else {
                    None
                }
            }
            _ => None,
        };

        let payment_token = payment_data.token.clone();
        let connector = payment_data.payment_attempt.connector.clone();
        let straight_through_algorithm = payment_data
            .payment_attempt
            .straight_through_algorithm
            .clone();
        let authorized_amount = payment_data.payment_attempt.get_total_amount();
        let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone();

        let surcharge_amount = payment_data
            .surcharge_details
            .as_ref()
            .map(|surcharge_details| surcharge_details.surcharge_amount);
        let tax_amount = payment_data
            .surcharge_details
            .as_ref()
            .map(|surcharge_details| surcharge_details.tax_on_surcharge_amount);

        let routing_approach = payment_data.payment_attempt.routing_approach.clone();
        let is_stored_credential = helpers::is_stored_credential(
            &payment_data.recurring_details,
            &payment_data.pm_token,
            payment_data.mandate_id.is_some(),
            payment_data.payment_attempt.is_stored_credential,
        );
        payment_data.payment_attempt = state
            .store
            .update_payment_attempt_with_attempt_id(
                payment_data.payment_attempt,
                storage::PaymentAttemptUpdate::UpdateTrackers {
                    payment_token,
                    connector,
                    straight_through_algorithm,
                    amount_capturable: match payment_data.confirm.unwrap_or(true) {
                        true => Some(authorized_amount),
                        false => None,
                    },
                    surcharge_amount,
                    tax_amount,
                    updated_by: storage_scheme.to_string(),
                    merchant_connector_id,
                    routing_approach,
                    is_stored_credential,
                },
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

        let customer_id = payment_data.payment_intent.customer_id.clone();

        let raw_customer_details = customer
            .map(|customer| CustomerData::foreign_try_from(customer.clone()))
            .transpose()?;
        let key_manager_state = state.into();
        // Updation of Customer Details for the cases where both customer_id and specific customer
        // details are provided in Payment Create Request
        let customer_details = raw_customer_details
            .clone()
            .async_map(|customer_details| {
                create_encrypted_data(&key_manager_state, key_store, customer_details)
            })
            .await
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt customer details")?;

        payment_data.payment_intent = state
            .store
            .update_payment_intent(
                &state.into(),
                payment_data.payment_intent,
                storage::PaymentIntentUpdate::PaymentCreateUpdate {
                    return_url: None,
                    status,
                    customer_id,
                    shipping_address_id: None,
                    billing_address_id: None,
                    customer_details,
                    updated_by: storage_scheme.to_string(),
                },
                key_store,
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
        req_state
            .event_context
            .event(AuditEvent::new(AuditEventType::PaymentCreate))
            .with(payment_data.to_event())
            .emit();

        // payment_data.mandate_id = response.and_then(|router_data| router_data.request.mandate_id);
        Ok((
            payments::is_confirm(self, payment_data.confirm),
            payment_data,
        ))
    }
}

impl<F: Send + Clone + Sync> ValidateRequest<F, api::PaymentsRequest, PaymentData<F>>
    for PaymentCreate
{
    #[instrument(skip_all)]
    fn validate_request<'a, 'b>(
        &'b self,
        request: &api::PaymentsRequest,
        merchant_context: &'a domain::MerchantContext,
    ) -> RouterResult<(PaymentCreateOperation<'b, F>, operations::ValidateResult)> {
        helpers::validate_customer_information(request)?;

        if let Some(amount) = request.amount {
            helpers::validate_max_amount(amount)?;
        }
        if let Some(session_expiry) = &request.session_expiry {
            helpers::validate_session_expiry(session_expiry.to_owned())?;
        }

        if let Some(payment_link) = &request.payment_link {
            if *payment_link {
                helpers::validate_payment_link_request(request)?;
            }
        };

        let payment_id = request.payment_id.clone().ok_or(error_stack::report!(
            errors::ApiErrorResponse::PaymentNotFound
        ))?;

        let request_merchant_id = request.merchant_id.as_ref();
        helpers::validate_merchant_id(
            merchant_context.get_merchant_account().get_id(),
            request_merchant_id,
        )
        .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)?;

        helpers::validate_request_amount_and_amount_to_capture(
            request.amount,
            request.amount_to_capture,
            request.surcharge_details,
        )
        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "amount_to_capture".to_string(),
            expected_format: "amount_to_capture lesser than amount".to_string(),
        })?;

        helpers::validate_amount_to_capture_and_capture_method(None, request)?;
        helpers::validate_card_data(
            request
                .payment_method_data
                .as_ref()
                .and_then(|pmd| pmd.payment_method_data.clone()),
        )?;

        helpers::validate_payment_method_fields_present(request)?;

        let mandate_type =
            helpers::validate_mandate(request, payments::is_operation_confirm(self))?;

        helpers::validate_recurring_details_and_token(
            &request.recurring_details,
            &request.payment_token,
            &request.mandate_id,
        )?;

        request.validate_stored_credential().change_context(
            errors::ApiErrorResponse::InvalidRequestData {
                message:
                    "is_stored_credential should be true when reusing stored payment method data"
                        .to_string(),
            },
        )?;

        helpers::validate_overcapture_request(
            &request.enable_overcapture,
            &request.capture_method,
        )?;

        if request.confirm.unwrap_or(false) {
            helpers::validate_pm_or_token_given(
                &request.payment_method,
                &request
                    .payment_method_data
                    .as_ref()
                    .and_then(|pmd| pmd.payment_method_data.clone()),
                &request.payment_method_type,
                &mandate_type,
                &request.payment_token,
                &request.ctp_service_details,
            )?;

            helpers::validate_customer_id_mandatory_cases(
                request.setup_future_usage.is_some(),
                request.customer_id.as_ref().or(request
                    .customer
                    .as_ref()
                    .map(|customer| customer.id.clone())
                    .as_ref()),
            )?;
        }

        if request.split_payments.is_some() {
            let amount = request.amount.get_required_value("amount")?;
            helpers::validate_platform_request_for_marketplace(
                amount,
                request.split_payments.clone(),
            )?;
        };

        let _request_straight_through: Option<api::routing::StraightThroughAlgorithm> = request
            .routing
            .clone()
            .map(|val| val.parse_value("RoutingAlgorithm"))
            .transpose()
            .change_context(errors::ApiErrorResponse::InvalidRequestData {
                message: "Invalid straight through routing rules format".to_string(),
            })
            .attach_printable("Invalid straight through routing rules format")?;

        Ok((
            Box::new(self),
            operations::ValidateResult {
                merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
                payment_id,
                storage_scheme: merchant_context.get_merchant_account().storage_scheme,
                requeue: matches!(
                    request.retry_action,
                    Some(api_models::enums::RetryAction::Requeue)
                ),
            },
        ))
    }
}

impl PaymentCreate {
    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    #[allow(clippy::too_many_arguments)]
    pub async fn make_payment_attempt(
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        organization_id: &common_utils::id_type::OrganizationId,
        money: (api::Amount, enums::Currency),
        payment_method: Option<enums::PaymentMethod>,
        payment_method_type: Option<enums::PaymentMethodType>,
        request: &api::PaymentsRequest,
        browser_info: Option<serde_json::Value>,
        state: &SessionState,
        payment_method_billing_address_id: Option<String>,
        payment_method_info: &Option<domain::PaymentMethod>,
        _key_store: &domain::MerchantKeyStore,
        profile_id: common_utils::id_type::ProfileId,
        customer_acceptance: &Option<payments::CustomerAcceptance>,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<(
        storage::PaymentAttemptNew,
        Option<api_models::payments::AdditionalPaymentData>,
    )> {
        todo!()
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    #[allow(clippy::too_many_arguments)]
    pub async fn make_payment_attempt(
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        organization_id: &common_utils::id_type::OrganizationId,
        money: (api::Amount, enums::Currency),
        payment_method: Option<enums::PaymentMethod>,
        payment_method_type: Option<enums::PaymentMethodType>,
        request: &api::PaymentsRequest,
        browser_info: Option<serde_json::Value>,
        state: &SessionState,
        optional_payment_method_billing_address_id: Option<String>,
        payment_method_info: &Option<domain::PaymentMethod>,
        key_store: &domain::MerchantKeyStore,
        profile_id: common_utils::id_type::ProfileId,
        customer_acceptance: &Option<common_payments_types::CustomerAcceptance>,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<(
        storage::PaymentAttemptNew,
        Option<api_models::payments::AdditionalPaymentData>,
    )> {
        let payment_method_data =
            request
                .payment_method_data
                .as_ref()
                .and_then(|payment_method_data_request| {
                    payment_method_data_request.payment_method_data.as_ref()
                });

        let created_at @ modified_at @ last_synced = Some(common_utils::date_time::now());
        let status = helpers::payment_attempt_status_fsm(payment_method_data, request.confirm);
        let (amount, currency) = (money.0, Some(money.1));

        let mut additional_pm_data = request
            .payment_method_data


// Implementation: impl api::PaymentSync for for Bambora
// File: crates/hyperswitch_connectors/src/connectors/bambora.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Bambora


// Function: get_payment_method_type
// File: crates/api_models/src/admin.rs
// Module: api_models

pub fn get_payment_method_type(
        &self,
    ) -> Option<&Vec<payment_methods::RequestPaymentMethodTypes>>


// Struct: ClientReferenceInformation
// File: crates/hyperswitch_connectors/src/connectors/barclaycard/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ClientReferenceInformation


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/hyperwallet.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// Function: authentication_authenticate_core
// File: crates/router/src/core/unified_authentication_service.rs
// Module: router

pub fn authentication_authenticate_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: AuthenticationAuthenticateRequest,
    auth_flow: AuthFlow,
) -> RouterResponse<AuthenticationAuthenticateResponse>


// Implementation: impl FraudCheck for for Riskified
// File: crates/hyperswitch_connectors/src/connectors/riskified.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl FraudCheck for for Riskified


// Function: call_connector_payout
// File: crates/router/src/core/payouts.rs
// Module: router

pub fn call_connector_payout(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector_data: &api::ConnectorData,
    payout_data: &mut PayoutData,
) -> RouterResult<()>


// Function: get_delete_tokenize_schedule_time
// File: crates/router/src/core/payment_methods/vault.rs
// Module: router

pub fn get_delete_tokenize_schedule_time(
    db: &dyn db::StorageInterface,
    pm: enums::PaymentMethod,
    retry_count: i32,
) -> Option<time::PrimitiveDateTime>


// Struct: PaymentMethodResponse
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Implementations: 0

pub struct PaymentMethodResponse


// Implementation: impl webhooks::IncomingWebhook for for Hipay
// File: crates/hyperswitch_connectors/src/connectors/hipay.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl webhooks::IncomingWebhook for for Hipay


// Function: is_refund_event
// File: crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs
// Module: hyperswitch_connectors

pub fn is_refund_event(event_code: &AirwallexWebhookEventType) -> bool


// Implementation: impl std::fmt::Debug for for RdKafkaProducer
// File: crates/router/src/services/kafka.rs
// Module: router
// Methods: 1 total (0 public)

impl std::fmt::Debug for for RdKafkaProducer


// Struct: CustomerUpdateRequest
// File: crates/api_models/src/customers.rs
// Module: api_models
// Implementations: 2

pub struct CustomerUpdateRequest


// Implementation: impl RoutingAlgorithmRef
// File: crates/api_models/src/routing.rs
// Module: api_models
// Methods: 4 total (4 public)

impl RoutingAlgorithmRef


// Function: get_payment_billing
// File: crates/hyperswitch_domain_models/src/payment_address.rs
// Module: hyperswitch_domain_models

pub fn get_payment_billing(&self) -> Option<&Address>


// File: crates/common_utils/src/id_type/client_secret.rs
// Module: common_utils
// Public functions: 1

crate::id_type!(
    ClientSecretId,
    "A type for key_id that can be used for Ephemeral key IDs"
);
crate::impl_id_type_methods!(ClientSecretId, "key_id");

// This is to display the `ClientSecretId` as ClientSecretId(abcd)
crate::impl_debug_id_type!(ClientSecretId);
crate::impl_try_from_cow_str_id_type!(ClientSecretId, "key_id");

crate::impl_generate_id_id_type!(ClientSecretId, "csi");
crate::impl_serializable_secret_id_type!(ClientSecretId);
crate::impl_queryable_id_type!(ClientSecretId);
crate::impl_to_sql_from_sql_id_type!(ClientSecretId);

#[cfg(feature = "v2")]
impl crate::events::ApiEventMetric for ClientSecretId {
    fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
        Some(crate::events::ApiEventsType::ClientSecret {
            key_id: self.clone(),
        })
    }
}

crate::impl_default_id_type!(ClientSecretId, "key");

impl ClientSecretId {
    /// Generate a key for redis
    pub fn generate_redis_key(&self) -> String {
        format!("cs_{}", self.get_string_repr())
    }
}


// File: crates/router/src/core/fraud_check.rs
// Module: router
// Public functions: 12

use std::fmt::Debug;

use api_models::{self, enums as api_enums};
use common_enums::CaptureMethod;
use error_stack::ResultExt;
use masking::PeekInterface;
use router_env::{
    logger,
    tracing::{self, instrument},
};

use self::{
    flows::{self as frm_flows, FeatureFrm},
    types::{
        self as frm_core_types, ConnectorDetailsCore, FrmConfigsObject, FrmData, FrmInfo,
        PaymentDetails, PaymentToFrmData,
    },
};
use super::errors::{ConnectorErrorExt, RouterResponse};
use crate::{
    core::{
        errors::{self, RouterResult},
        payments::{self, flows::ConstructFlowSpecificData, operations::BoxedOperation},
    },
    db::StorageInterface,
    routes::{app::ReqState, SessionState},
    services,
    types::{
        self as oss_types,
        api::{
            fraud_check as frm_api, routing::FrmRoutingAlgorithm, Connector,
            FraudCheckConnectorData, Fulfillment,
        },
        domain, fraud_check as frm_types,
        storage::{
            enums::{
                AttemptStatus, FraudCheckLastStep, FraudCheckStatus, FraudCheckType, FrmSuggestion,
                IntentStatus,
            },
            fraud_check::{FraudCheck, FraudCheckUpdate},
            PaymentIntent,
        },
    },
    utils::ValueExt,
};
pub mod flows;
pub mod operation;
pub mod types;

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn call_frm_service<D: Clone, F, Req, OperationData>(
    state: &SessionState,
    payment_data: &OperationData,
    frm_data: &mut FrmData,
    merchant_context: &domain::MerchantContext,
    customer: &Option<domain::Customer>,
) -> RouterResult<oss_types::RouterData<F, Req, frm_types::FraudCheckResponseData>>
where
    F: Send + Clone,

    OperationData: payments::OperationSessionGetters<D> + Send + Sync + Clone,

    // To create connector flow specific interface data
    FrmData: ConstructFlowSpecificData<F, Req, frm_types::FraudCheckResponseData>,
    oss_types::RouterData<F, Req, frm_types::FraudCheckResponseData>: FeatureFrm<F, Req> + Send,

    // To construct connector flow specific api
    dyn Connector: services::api::ConnectorIntegration<F, Req, frm_types::FraudCheckResponseData>,
{
    todo!()
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn call_frm_service<D: Clone, F, Req, OperationData>(
    state: &SessionState,
    payment_data: &OperationData,
    frm_data: &mut FrmData,
    merchant_context: &domain::MerchantContext,
    customer: &Option<domain::Customer>,
) -> RouterResult<oss_types::RouterData<F, Req, frm_types::FraudCheckResponseData>>
where
    F: Send + Clone,

    OperationData: payments::OperationSessionGetters<D> + Send + Sync + Clone,

    // To create connector flow specific interface data
    FrmData: ConstructFlowSpecificData<F, Req, frm_types::FraudCheckResponseData>,
    oss_types::RouterData<F, Req, frm_types::FraudCheckResponseData>: FeatureFrm<F, Req> + Send,

    // To construct connector flow specific api
    dyn Connector: services::api::ConnectorIntegration<F, Req, frm_types::FraudCheckResponseData>,
{
    let merchant_connector_account = payments::construct_profile_id_and_get_mca(
        state,
        merchant_context,
        payment_data,
        &frm_data.connector_details.connector_name,
        None,
        false,
    )
    .await?;

    frm_data
        .payment_attempt
        .connector_transaction_id
        .clone_from(&payment_data.get_payment_attempt().connector_transaction_id);

    let mut router_data = frm_data
        .construct_router_data(
            state,
            &frm_data.connector_details.connector_name,
            merchant_context,
            customer,
            &merchant_connector_account,
            None,
            None,
        )
        .await?;

    router_data.status = payment_data.get_payment_attempt().status;
    if matches!(
        frm_data.fraud_check.frm_transaction_type,
        FraudCheckType::PreFrm
    ) && matches!(
        frm_data.fraud_check.last_step,
        FraudCheckLastStep::CheckoutOrSale
    ) {
        frm_data.fraud_check.last_step = FraudCheckLastStep::TransactionOrRecordRefund
    }

    let connector =
        FraudCheckConnectorData::get_connector_by_name(&frm_data.connector_details.connector_name)?;
    let router_data_res = router_data
        .decide_frm_flows(
            state,
            &connector,
            payments::CallConnectorAction::Trigger,
            merchant_context,
        )
        .await?;

    Ok(router_data_res)
}

#[cfg(feature = "v2")]
pub async fn should_call_frm<F, D>(
    _merchant_context: &domain::MerchantContext,
    _payment_data: &D,
    _state: &SessionState,
) -> RouterResult<(
    bool,
    Option<FrmRoutingAlgorithm>,
    Option<common_utils::id_type::ProfileId>,
    Option<FrmConfigsObject>,
)>
where
    F: Send + Clone,
    D: payments::OperationSessionGetters<F> + Send + Sync + Clone,
{
    // Frm routing algorithm is not present in the merchant account
    // it has to be fetched from the business profile
    todo!()
}

#[cfg(feature = "v1")]
pub async fn should_call_frm<F, D>(
    merchant_context: &domain::MerchantContext,
    payment_data: &D,
    state: &SessionState,
) -> RouterResult<(
    bool,
    Option<FrmRoutingAlgorithm>,
    Option<common_utils::id_type::ProfileId>,
    Option<FrmConfigsObject>,
)>
where
    F: Send + Clone,
    D: payments::OperationSessionGetters<F> + Send + Sync + Clone,
{
    use common_utils::ext_traits::OptionExt;
    use masking::ExposeInterface;

    let db = &*state.store;
    match merchant_context
        .get_merchant_account()
        .frm_routing_algorithm
        .clone()
    {
        Some(frm_routing_algorithm_value) => {
            let frm_routing_algorithm_struct: FrmRoutingAlgorithm = frm_routing_algorithm_value
                .clone()
                .parse_value("FrmRoutingAlgorithm")
                .change_context(errors::ApiErrorResponse::MissingRequiredField {
                    field_name: "frm_routing_algorithm",
                })
                .attach_printable("Data field not found in frm_routing_algorithm")?;

            let profile_id = payment_data
                .get_payment_intent()
                .profile_id
                .as_ref()
                .get_required_value("profile_id")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("profile_id is not set in payment_intent")?
                .clone();

            #[cfg(feature = "v1")]
            let merchant_connector_account_from_db_option = db
                .find_merchant_connector_account_by_profile_id_connector_name(
                    &state.into(),
                    &profile_id,
                    &frm_routing_algorithm_struct.data,
                    merchant_context.get_merchant_key_store(),
                )
                .await
                .map_err(|error| {
                    logger::error!(
                        "{:?}",
                        error.change_context(
                            errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                                id: merchant_context
                                    .get_merchant_account()
                                    .get_id()
                                    .get_string_repr()
                                    .to_owned(),
                            }
                        )
                    )
                })
                .ok();
            let enabled_merchant_connector_account_from_db_option =
                merchant_connector_account_from_db_option.and_then(|mca| {
                    if mca.disabled.unwrap_or(false) {
                        logger::info!("No eligible connector found for FRM");
                        None
                    } else {
                        Some(mca)
                    }
                });

            #[cfg(feature = "v2")]
            let merchant_connector_account_from_db_option: Option<
                domain::MerchantConnectorAccount,
            > = {
                let _ = key_store;
                let _ = frm_routing_algorithm_struct;
                let _ = profile_id;
                todo!()
            };

            match enabled_merchant_connector_account_from_db_option {
                Some(merchant_connector_account_from_db) => {
                    let frm_configs_option = merchant_connector_account_from_db
                        .frm_configs
                        .ok_or(errors::ApiErrorResponse::MissingRequiredField {
                            field_name: "frm_configs",
                        })
                        .ok();
                    match frm_configs_option {
                        Some(frm_configs_value) => {
                            let frm_configs_struct: Vec<api_models::admin::FrmConfigs> = frm_configs_value
                                .into_iter()
                                .map(|config| { config
                                    .expose()
                                    .parse_value("FrmConfigs")
                                    .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                                            field_name: "frm_configs".to_string(),
                                            expected_format: r#"[{ "gateway": "stripe", "payment_methods": [{ "payment_method": "card","flow": "post"}]}]"#.to_string(),
                                        })
                                })
                                .collect::<Result<Vec<_>, _>>()?;

                            let mut is_frm_connector_enabled = false;
                            let mut is_frm_pm_enabled = false;
                            let connector = payment_data.get_payment_attempt().connector.clone();
                            let filtered_frm_config = frm_configs_struct
                                .iter()
                                .filter(|frm_config| match (&connector, &frm_config.gateway) {
                                    (Some(current_connector), Some(configured_connector)) => {
                                        let is_enabled =
                                            *current_connector == configured_connector.to_string();
                                        if is_enabled {
                                            is_frm_connector_enabled = true;
                                        }
                                        is_enabled
                                    }
                                    (None, _) | (_, None) => true,
                                })
                                .collect::<Vec<_>>();
                            let filtered_payment_methods = filtered_frm_config
                                .iter()
                                .map(|frm_config| {
                                    let filtered_frm_config_by_pm = frm_config
                                        .payment_methods
                                        .iter()
                                        .filter(|frm_config_pm| {
                                            match (
                                                payment_data.get_payment_attempt().payment_method,
                                                frm_config_pm.payment_method,
                                            ) {
                                                (
                                                    Some(current_pm),
                                                    Some(configured_connector_pm),
                                                ) => {
                                                    let is_enabled = current_pm.to_string()
                                                        == configured_connector_pm.to_string();
                                                    if is_enabled {
                                                        is_frm_pm_enabled = true;
                                                    }
                                                    is_enabled
                                                }
                                                (None, _) | (_, None) => true,
                                            }
                                        })
                                        .collect::<Vec<_>>();
                                    filtered_frm_config_by_pm
                                })
                                .collect::<Vec<_>>()
                                .concat();
                            let is_frm_enabled = is_frm_connector_enabled && is_frm_pm_enabled;
                            logger::debug!(
                                "is_frm_connector_enabled {:?}, is_frm_pm_enabled:  {:?}, is_frm_enabled :{:?}",
                                is_frm_connector_enabled,
                                is_frm_pm_enabled,
                                is_frm_enabled
                            );
                            // filtered_frm_config...
                            // Panic Safety: we are first checking if the object is present... only if present, we try to fetch index 0
                            let frm_configs_object = FrmConfigsObject {
                                frm_enabled_gateway: filtered_frm_config
                                    .first()
                                    .and_then(|c| c.gateway),
                                frm_enabled_pm: filtered_payment_methods
                                    .first()
                                    .and_then(|pm| pm.payment_method),
                                // flow type should be consumed from payment_method.flow. To provide backward compatibility, if we don't find it there, we consume it from payment_method.payment_method_types[0].flow_type.
                                frm_preferred_flow_type: filtered_payment_methods
                                    .first()
                                    .and_then(|pm| pm.flow.clone())
                                    .or(filtered_payment_methods.first().and_then(|pm| {
                                        pm.payment_method_types.as_ref().and_then(|pmt| {
                                            pmt.first().map(|pmts| pmts.flow.clone())
                                        })
                                    }))
                                    .ok_or(errors::ApiErrorResponse::InvalidDataFormat {
                                            field_name: "frm_configs".to_string(),
                                            expected_format: r#"[{ "gateway": "stripe", "payment_methods": [{ "payment_method": "card","flow": "post"}]}]"#.to_string(),
                                    })?,
                            };
                            logger::debug!(
                                "frm_routing_configs: {:?} {:?} {:?} {:?}",
                                frm_routing_algorithm_struct,
                                profile_id,
                                frm_configs_object,
                                is_frm_enabled
                            );
                            Ok((
                                is_frm_enabled,
                                Some(frm_routing_algorithm_struct),
                                Some(profile_id),
                                Some(frm_configs_object),
                            ))
                        }
                        None => {
                            logger::error!("Cannot find frm_configs for FRM provider");
                            Ok((false, None, None, None))
                        }
                    }
                }
                None => {
                    logger::error!("Cannot find merchant connector account for FRM provider");
                    Ok((false, None, None, None))
                }
            }
        }
        _ => Ok((false, None, None, None)),
    }
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
pub async fn make_frm_data_and_fraud_check_operation<F, D>(
    _db: &dyn StorageInterface,
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payment_data: D,
    frm_routing_algorithm: FrmRoutingAlgorithm,
    profile_id: common_utils::id_type::ProfileId,
    frm_configs: FrmConfigsObject,
    _customer: &Option<domain::Customer>,
) -> RouterResult<FrmInfo<F, D>>
where
    F: Send + Clone,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
{
    todo!()
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
pub async fn make_frm_data_and_fraud_check_operation<F, D>(
    _db: &dyn StorageInterface,
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payment_data: D,
    frm_routing_algorithm: FrmRoutingAlgorithm,
    profile_id: common_utils::id_type::ProfileId,
    frm_configs: FrmConfigsObject,
    _customer: &Option<domain::Customer>,
) -> RouterResult<FrmInfo<F, D>>
where
    F: Send + Clone,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
{
    let order_details = payment_data
        .get_payment_intent()
        .order_details
        .clone()
        .or_else(||
            // when the order_details are present within the meta_data, we need to take those to support backward compatibility
            payment_data.get_payment_intent().metadata.clone().and_then(|meta| {
                let order_details = meta.get("order_details").to_owned();
                order_details.map(|order| vec![masking::Secret::new(order.to_owned())])
            }))
        .map(|order_details_value| {
            order_details_value
                .into_iter()
                .map(|data| {
                    data.peek()
                        .to_owned()
                        .parse_value("OrderDetailsWithAmount")
                        .attach_printable("unable to parse OrderDetailsWithAmount")
                })
                .collect::<Result<Vec<_>, _>>()
                .unwrap_or_default()
        });

    let frm_connector_details = ConnectorDetailsCore {
        connector_name: frm_routing_algorithm.data,
        profile_id,
    };

    let payment_to_frm_data = PaymentToFrmData {
        amount: payment_data.get_amount(),
        payment_intent: payment_data.get_payment_intent().to_owned(),
        payment_attempt: payment_data.get_payment_attempt().to_owned(),
        merchant_account: merchant_context.get_merchant_account().to_owned(),
        address: payment_data.get_address().clone(),
        connector_details: frm_connector_details.clone(),
        order_details,
        frm_metadata: payment_data.get_payment_intent().frm_metadata.clone(),
    };

    let fraud_check_operation: operation::BoxedFraudCheckOperation<F, D> =
        fraud_check_operation_by_frm_preferred_flow_type(frm_configs.frm_preferred_flow_type);
    let frm_data = fraud_check_operation
        .to_get_tracker()?
        .get_trackers(state, payment_to_frm_data, frm_connector_details)
        .await?;
    Ok(FrmInfo {
        fraud_check_operation,
        frm_data,
        suggested_action: None,
    })
}

fn fraud_check_operation_by_frm_preferred_flow_type<F, D>(
    frm_preferred_flow_type: api_enums::FrmPreferredFlowTypes,
) -> operation::BoxedFraudCheckOperation<F, D>
where
    operation::FraudCheckPost: operation::FraudCheckOperation<F, D>,
    operation::FraudCheckPre: operation::FraudCheckOperation<F, D>,
{
    match frm_preferred_flow_type {
        api_enums::FrmPreferredFlowTypes::Pre => Box::new(operation::FraudCheckPre),
        api_enums::FrmPreferredFlowTypes::Post => Box::new(operation::FraudCheckPost),
    }
}

#[allow(clippy::too_many_arguments)]
pub async fn pre_payment_frm_core<F, Req, D>(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payment_data: &mut D,
    frm_info: &mut FrmInfo<F, D>,
    frm_configs: FrmConfigsObject,
    customer: &Option<domain::Customer>,
    should_continue_transaction: &mut bool,
    should_continue_capture: &mut bool,
    operation: &BoxedOperation<'_, F, Req, D>,
) -> RouterResult<Option<FrmData>>
where
    F: Send + Clone,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
{
    let mut frm_data = None;
    if is_operation_allowed(operation) {
        frm_data = if let Some(frm_data) = &mut frm_info.frm_data {
            if matches!(
                frm_configs.frm_preferred_flow_type,
                api_enums::FrmPreferredFlowTypes::Pre
            ) {
                let fraud_check_operation = &mut frm_info.fraud_check_operation;

                let frm_router_data = fraud_check_operation
                    .to_domain()?
                    .pre_payment_frm(state, payment_data, frm_data, merchant_context, customer)
                    .await?;
                let _router_data = call_frm_service::<F, frm_api::Transaction, _, D>(
                    state,
                    payment_data,
                    frm_data,
                    merchant_context,
                    customer,
                )
                .await?;
                let frm_data_updated = fraud_check_operation
                    .to_update_tracker()?
                    .update_tracker(
                        state,
                        merchant_context.get_merchant_key_store(),
                        frm_data.clone(),
                        payment_data,
                        None,
                        frm_router_data,
                    )
                    .await?;
                let frm_fraud_check = frm_data_updated.fraud_check.clone();
                payment_data.set_frm_message(frm_fraud_check.clone());
                if matches!(frm_fraud_check.frm_status, FraudCheckStatus::Fraud) {
                    *should_continue_transaction = false;
                    frm_info.suggested_action = Some(FrmSuggestion::FrmCancelTransaction);
                }
                logger::debug!(
                    "frm_updated_data: {:?} {:?}",
                    frm_info.fraud_check_operation,
                    frm_info.suggested_action
                );
                Some(frm_data_updated)
            } else if matches!(
                frm_configs.frm_preferred_flow_type,
                api_enums::FrmPreferredFlowTypes::Post
            ) && !matches!(
                frm_data.fraud_check.frm_status,
                FraudCheckStatus::TransactionFailure // Incase of TransactionFailure frm status(No frm decision is taken by frm processor), if capture method is automatic we should not change it to manual.
            ) {
                *should_continue_capture = false;
                Some(frm_data.to_owned())
            } else {
                Some(frm_data.to_owned())
            }
        } else {
            None
        };
    }
    Ok(frm_data)
}

#[allow(clippy::too_many_arguments)]
pub async fn post_payment_frm_core<F, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    payment_data: &mut D,
    frm_info: &mut FrmInfo<F, D>,
    frm_configs: FrmConfigsObject,
    customer: &Option<domain::Customer>,
    should_continue_capture: &mut bool,
) -> RouterResult<Option<FrmData>>
where
    F: Send + Clone,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
{
    if let Some(frm_data) = &mut frm_info.frm_data {
        // Allow the Post flow only if the payment is authorized,
        // this logic has to be removed if we are going to call /sale or /transaction after failed transaction
        let fraud_check_operation = &mut frm_info.fraud_check_operation;
        if payment_data.get_payment_attempt().status == AttemptStatus::Authorized {
            let frm_router_data_opt = fraud_check_operation
                .to_domain()?
                .post_payment_frm(
                    state,
                    req_state.clone(),
                    payment_data,
                    frm_data,
                    merchant_context,
                    customer,
                )
                .await?;
            if let Some(frm_router_data) = frm_router_data_opt {
                let mut frm_data = fraud_check_operation
                    .to_update_tracker()?
                    .update_tracker(
                        state,
                        merchant_context.get_merchant_key_store(),
                        frm_data.to_owned(),
                        payment_data,
                        None,
                        frm_router_data.to_owned(),
                    )
                    .await?;
                let frm_fraud_check = frm_data.fraud_check.clone();
                let mut frm_suggestion = None;
                payment_data.set_frm_message(frm_fraud_check.clone());
                if matches!(frm_fraud_check.frm_status, FraudCheckStatus::Fraud) {
                    frm_info.suggested_action = Some(FrmSuggestion::FrmCancelTransaction);
                } else if matches!(frm_fraud_check.frm_status, FraudCheckStatus::ManualReview) {
                    frm_info.suggested_action = Some(FrmSuggestion::FrmManualReview);
                }
                fraud_check_operation
                    .to_domain()?
                    .execute_post_tasks(
                        state,
                        req_state,
                        &mut frm_data,
                        merchant_context,
                        frm_configs,
                        &mut frm_suggestion,
                        payment_data,
                        customer,
                        should_continue_capture,
                    )
                    .await?;
                logger::debug!("frm_post_tasks_data: {:?}", frm_data);
                let updated_frm_data = fraud_check_operation
                    .to_update_tracker()?
                    .update_tracker(
                        state,
                        merchant_context.get_merchant_key_store(),
                        frm_data.to_owned(),
                        payment_data,
                        frm_suggestion,
                        frm_router_data.to_owned(),
                    )
                    .await?;
                return Ok(Some(updated_frm_data));
            }
        }

        Ok(Some(frm_data.to_owned()))
    } else {
        Ok(None)
    }
}

#[allow(clippy::too_many_arguments)]
pub async fn call_frm_before_connector_call<F, Req, D>(
    operation: &BoxedOperation<'_, F, Req, D>,
    merchant_context: &domain::MerchantContext,
    payment_data: &mut D,
    state: &SessionState,
    frm_info: &mut Option<FrmInfo<F, D>>,
    customer: &Option<domain::Customer>,
    should_continue_transaction: &mut bool,
    should_continue_capture: &mut bool,
) -> RouterResult<Option<FrmConfigsObject>>
where
    F: Send + Clone,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
{
    let (is_frm_enabled, frm_routing_algorithm, frm_connector_label, frm_configs) =
        should_call_frm(merchant_context, payment_data, state).await?;
    if let Some((frm_routing_algorithm_val, profile_id)) =
        frm_routing_algorithm.zip(frm_connector_label)
    {
        if let Some(frm_configs) = frm_configs.clone() {
            let mut updated_frm_info = Box::pin(make_frm_data_and_fraud_check_operation(
                &*state.store,
                state,
                merchant_context,
                payment_data.to_owned(),
                frm_routing_algorithm_val,
                profile_id,
                frm_configs.clone(),
                customer,
            ))
            .await?;

            if is_frm_enabled {
                pre_payment_frm_core(
                    state,
                    merchant_context,
                    payment_data,
                    &mut updated_frm_info,
                    frm_configs,
                    customer,
                    should_continue_transaction,
                    should_continue_capture,
                    operation,
                )
                .await?;
            }
            *frm_info = Some(updated_frm_info);
        }
    }
    let fraud_capture_method = frm_info.as_ref().and_then(|frm_info| {
        frm_info
            .frm_data
            .as_ref()
            .map(|frm_data| frm_data.fraud_check.payment_capture_method)
    });
    if matches!(fraud_capture_method, Some(Some(CaptureMethod::Manual)))
        && matches!(
            payment_data.get_payment_attempt().status,
            AttemptStatus::Unresolved
        )
    {
        if let Some(info) = frm_info {
            info.suggested_action = Some(FrmSuggestion::FrmAuthorizeTransaction)
        };
        *should_continue_transaction = false;
        logger::debug!(
            "skipping connector call since payment_capture_method is already {:?}",
            fraud_capture_method
        );
    };
    logger::debug!("frm_configs: {:?} {:?}", frm_configs, is_frm_enabled);
    Ok(frm_configs)
}

pub fn is_operation_allowed<Op: Debug>(operation: &Op) -> bool {
    ![
        "PaymentSession",
        "PaymentApprove",
        "PaymentReject",
        "PaymentCapture",
        "PaymentsCancel",
    ]
    .contains(&format!("{operation:?}").as_str())
}

#[cfg(feature = "v1")]
impl From<PaymentToFrmData> for PaymentDetails {
    fn from(payment_data: PaymentToFrmData) -> Self {
        Self {
            amount: common_utils::types::MinorUnit::from(payment_data.amount).get_amount_as_i64(),
            currency: payment_data.payment_attempt.currency,
            payment_method: payment_data.payment_attempt.payment_method,
            payment_method_type: payment_data.payment_attempt.payment_method_type,
            refund_transaction_id: None,
        }
    }
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn frm_fulfillment_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: frm_core_types::FrmFulfillmentRequest,
) -> RouterResponse<frm_types::FraudCheckResponseData> {
    let db = &*state.clone().store;
    let payment_intent = db
        .find_payment_intent_by_payment_id_merchant_id(
            &(&state).into(),
            &req.payment_id.clone(),
            merchant_context.get_merchant_account().get_id(),
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::PaymentNotFound)?;
    match payment_intent.status {
        IntentStatus::Succeeded => {
            let invalid_request_error = errors::ApiErrorResponse::InvalidRequestData {
                message: "no fraud check entry found for this payment_id".to_string(),
            };
            let existing_fraud_check = db
                .find_fraud_check_by_payment_id_if_present(
                    req.payment_id.clone(),
                    merchant_context.get_merchant_account().get_id().clone(),
                )
                .await
                .change_context(invalid_request_error.to_owned())?;
            match existing_fraud_check {
                Some(fraud_check) => {
                    if (matches!(fraud_check.frm_transaction_type, FraudCheckType::PreFrm)
                        && fraud_check.last_step == FraudCheckLastStep::TransactionOrRecordRefund)
                        || (matches!(fraud_check.frm_transaction_type, FraudCheckType::PostFrm)
                            && fraud_check.last_step == FraudCheckLastStep::CheckoutOrSale)
                    {
                        Box::pin(make_fulfillment_api_call(
                            db,
                            fraud_check,
                            payment_intent,
                            state,
                            merchant_context,
                            req,
                        ))
                        .await
                    } else {
                        Err(errors::ApiErrorResponse::PreconditionFailed {message:"Frm pre/post flow hasn't terminated yet, so fulfillment cannot be called".to_string(),}.into())
                    }
                }
                None => Err(invalid_request_error.into()),
            }
        }
        _ => Err(errors::ApiErrorResponse::PreconditionFailed {
            message: "Fulfillment can be performed only for succeeded payment".to_string(),
        }
        .into()),
    }
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn make_fulfillment_api_call(
    db: &dyn StorageInterface,
    fraud_check: FraudCheck,
    payment_intent: PaymentIntent,
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: frm_core_types::FrmFulfillmentRequest,
) -> RouterResponse<frm_types::FraudCheckResponseData> {
    let payment_attempt = db
        .find_payment_attempt_by_attempt_id_merchant_id(
            &payment_intent.active_attempt.get_id(),
            merchant_context.get_merchant_account().get_id(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::PaymentNotFound)?;
    let connector_data = FraudCheckConnectorData::get_connector_by_name(&fraud_check.frm_name)?;
    let connector_integration: services::BoxedFrmConnectorIntegrationInterface<
        Fulfillment,
        frm_types::FraudCheckFulfillmentData,
        frm_types::FraudCheckResponseData,
    > = connector_data.connector.get_connector_integration();
    let router_data = frm_flows::fulfillment_flow::construct_fulfillment_router_data(
        &state,
        &payment_intent,
        &payment_attempt,
        &merchant_context,
        fraud_check.frm_name.clone(),
        req,
    )
    .await?;
    let response = services::execute_connector_processing_step(
        &state,
        connector_integration,
        &router_data,
        payments::CallConnectorAction::Trigger,
        None,
        None,
    )
    .await
    .to_payment_failed_response()?;
    let fraud_check_copy = fraud_check.clone();
    let fraud_check_update = FraudCheckUpdate::ResponseUpdate {
        frm_status: fraud_check.frm_status,
        frm_transaction_id: fraud_check.frm_transaction_id,
        frm_reason: fraud_check.frm_reason,
        frm_score: fraud_check.frm_score,
        metadata: fraud_check.metadata,
        modified_at: common_utils::date_time::now(),
        last_step: FraudCheckLastStep::Fulfillment,
        payment_capture_method: fraud_check.payment_capture_method,
    };
    let _updated = db
        .update_fraud_check_response_with_attempt_id(fraud_check_copy, fraud_check_update)
        .await
        .map_err(|error| error.change_context(errors::ApiErrorResponse::PaymentNotFound))?;
    let fulfillment_response =
        response
            .response
            .map_err(|err| errors::ApiErrorResponse::ExternalConnectorError {
                code: err.code,
                message: err.message,
                connector: connector_data.connector_name.clone().to_string(),
                status_code: err.status_code,
                reason: err.reason,
            })?;
    Ok(services::ApplicationResponse::Json(fulfillment_response))
}


// Struct: RefundMetricsBucketValue
// File: crates/api_models/src/analytics/refunds.rs
// Module: api_models
// Implementations: 0

pub struct RefundMetricsBucketValue


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/payone.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// Struct: PaymentLinkTransactionDetails
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 0

pub struct PaymentLinkTransactionDetails


// Function: user_org_create
// File: crates/router/src/routes/user.rs
// Module: router

pub fn user_org_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::UserOrgMerchantCreateRequest>,
) -> HttpResponse


// Implementation: impl ConnectorCommon for for Digitalvirgo
// File: crates/hyperswitch_connectors/src/connectors/digitalvirgo.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl ConnectorCommon for for Digitalvirgo


// Implementation: impl common_utils::events::ApiEventMetric for for FeatureMatrixListResponse
// File: crates/api_models/src/feature_matrix.rs
// Module: api_models
// Methods: 0 total (0 public)

impl common_utils::events::ApiEventMetric for for FeatureMatrixListResponse


// Function: make_dsl_input_for_surcharge
// File: crates/router/src/core/payments/routing.rs
// Module: router

pub fn make_dsl_input_for_surcharge(
    _payment_attempt: &oss_storage::PaymentAttempt,
    _payment_intent: &oss_storage::PaymentIntent,
    _billing_address: Option<Address>,
) -> RoutingResult<dsl_inputs::BackendInput>


// Implementation: impl MerchantConnectorAccountTypeDetails
// File: crates/hyperswitch_domain_models/src/merchant_connector_account.rs
// Module: hyperswitch_domain_models
// Methods: 8 total (8 public)

impl MerchantConnectorAccountTypeDetails


// Implementation: impl api::RefundExecute for for Bamboraapac
// File: crates/hyperswitch_connectors/src/connectors/bamboraapac.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundExecute for for Bamboraapac


// Struct: ContractRoutingAlgorithm
// File: crates/api_models/src/routing.rs
// Module: api_models
// Implementations: 1
// Traits: DynamicRoutingAlgoAccessor

pub struct ContractRoutingAlgorithm


// Struct: NTWithNTIRef
// File: crates/router/src/core/payments.rs
// Module: router
// Implementations: 0

pub struct NTWithNTIRef


// Struct: PayoutsMandateReference
// File: crates/diesel_models/src/payment_method.rs
// Module: diesel_models
// Implementations: 2
// Traits: std::ops::Deref, std::ops::DerefMut

pub struct PayoutsMandateReference


// Function: proxy_confirm_intent
// File: crates/router/src/routes/payments.rs
// Module: router

pub fn proxy_confirm_intent(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<api_models::payments::ProxyPaymentsRequest>,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder


// Struct: VaultDeleteResponse
// File: crates/router/src/types/payment_methods.rs
// Module: router
// Implementations: 0

pub struct VaultDeleteResponse


// Struct: Capture
// File: crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Capture


// Struct: CheckoutRouterData
// File: crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CheckoutRouterData<T>


// Function: strip_jwt_token
// File: crates/router/src/services/authentication.rs
// Module: router

pub fn strip_jwt_token(token: &str) -> RouterResult<&str>


// Implementation: impl Capturable for for PaymentsCancelPostCaptureData
// File: crates/router/src/types.rs
// Module: router
// Methods: 2 total (0 public)

impl Capturable for for PaymentsCancelPostCaptureData


// Function: get_flow_from_upi_collect
// File: crates/router/src/routes/dummy_connector/utils.rs
// Module: router

pub fn get_flow_from_upi_collect(
        self,
    ) -> types::DummyConnectorResult<types::DummyConnectorUpiFlow>


// Function: get_payout_description_category
// File: crates/euclid_wasm/src/lib.rs
// Module: euclid_wasm

pub fn get_payout_description_category() -> JsResult


// Function: update_last_used_at
// File: crates/router/src/core/payment_methods/cards.rs
// Module: router

pub fn update_last_used_at(
    payment_method: &domain::PaymentMethod,
    state: &routes::SessionState,
    storage_scheme: MerchantStorageScheme,
    key_store: &domain::MerchantKeyStore,
) -> errors::RouterResult<()>


// Function: update_gsm_rule
// File: crates/router/src/routes/gsm.rs
// Module: router

pub fn update_gsm_rule(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<gsm_api_types::GsmUpdateRequest>,
) -> impl Responder


// Struct: BrowserInfo
// File: crates/hyperswitch_connectors/src/connectors/powertranz/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BrowserInfo


// Struct: UnifiedAuthenticationServicePreAuthenticateResponse
// File: crates/hyperswitch_connectors/src/connectors/unified_authentication_service/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct UnifiedAuthenticationServicePreAuthenticateResponse


// Struct: FiservemeaExpiryDate
// File: crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FiservemeaExpiryDate


// Implementation: impl api::PaymentVoid for for Finix
// File: crates/hyperswitch_connectors/src/connectors/finix.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentVoid for for Finix


// Implementation: impl Responder
// File: crates/router/src/routes/routing.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// File: crates/analytics/src/frm/metrics/frm_triggered_attempts.rs
// Module: analytics

use api_models::analytics::{
    frm::{FrmDimensions, FrmFilters, FrmMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::FrmMetricRow;
use crate::{
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};

#[derive(Default)]
pub(super) struct FrmTriggeredAttempts {}

#[async_trait::async_trait]
impl<T> super::FrmMetric<T> for FrmTriggeredAttempts
where
    T: AnalyticsDataSource + super::FrmMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[FrmDimensions],
        merchant_id: &common_utils::id_type::MerchantId,
        filters: &FrmFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<Vec<(FrmMetricsBucketIdentifier, FrmMetricRow)>> {
        let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::FraudCheck);

        for dim in dimensions.iter() {
            query_builder.add_select_column(dim).switch()?;
        }

        query_builder
            .add_select_column(Aggregate::Count {
                field: None,
                alias: Some("count"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

        filters.set_filter_clause(&mut query_builder).switch()?;

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

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

        for dim in dimensions.iter() {
            query_builder
                .add_group_by_clause(dim)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }

        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .execute_query::<FrmMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    FrmMetricsBucketIdentifier::new(
                        i.frm_name.as_ref().map(|i| i.to_string()),
                        i.frm_status.as_ref().map(|i| i.0.to_string()),
                        i.frm_transaction_type.as_ref().map(|i| i.0.to_string()),
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<Vec<_>, crate::query::PostProcessingError>>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}