        merchant_context.get_merchant_account().get_id(),
    )
    .await?;

    match business_profile {
        Some(business_profile) => {
            core_utils::validate_profile_id_from_auth_layer(
                authentication_profile_id,
                &business_profile,
            )?;
            let routing_algo_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
                enums::TransactionType::Payment => business_profile.routing_algorithm.clone(),
                #[cfg(feature = "payouts")]
                enums::TransactionType::Payout => business_profile.payout_routing_algorithm.clone(),
                enums::TransactionType::ThreeDsAuthentication => {
                    business_profile.three_ds_decision_rule_algorithm.clone()
                }
            }
            .map(|val| val.parse_value("RoutingAlgorithmRef"))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("unable to deserialize routing algorithm ref from merchant account")?
            .unwrap_or_default();

            let timestamp = common_utils::date_time::now_unix_timestamp();

            match routing_algo_ref.algorithm_id {
                Some(algorithm_id) => {
                    let routing_algorithm: routing_types::RoutingAlgorithmRef =
                        routing_types::RoutingAlgorithmRef {
                            algorithm_id: None,
                            timestamp,
                            config_algo_id: routing_algo_ref.config_algo_id.clone(),
                            surcharge_config_algo_id: routing_algo_ref.surcharge_config_algo_id,
                        };

                    let record = db
                        .find_routing_algorithm_by_profile_id_algorithm_id(
                            &profile_id,
                            &algorithm_id,
                        )
                        .await
                        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
                    let response = record.foreign_into();
                    helpers::update_profile_active_algorithm_ref(
                        db,
                        key_manager_state,
                        merchant_context.get_merchant_key_store(),
                        business_profile,
                        routing_algorithm,
                        &transaction_type,
                    )
                    .await?;

                    metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
                    Ok(service_api::ApplicationResponse::Json(response))
                }
                None => Err(errors::ApiErrorResponse::PreconditionFailed {
                    message: "Algorithm is already inactive".to_string(),
                })?,
            }
        }
        None => Err(errors::ApiErrorResponse::InvalidRequestData {
            message: "The business_profile is not present".to_string(),
        }
        .into()),
    }
}

#[cfg(feature = "v2")]
pub async fn update_default_fallback_routing(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: common_utils::id_type::ProfileId,
    updated_list_of_connectors: Vec<routing_types::RoutableConnectorChoice>,
) -> RouterResponse<Vec<routing_types::RoutableConnectorChoice>> {
    metrics::ROUTING_UPDATE_CONFIG.add(1, &[]);
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")?;
    let profile_wrapper = admin::ProfileWrapper::new(profile);
    let default_list_of_connectors =
        profile_wrapper.get_default_fallback_list_of_connector_under_profile()?;

    utils::when(
        default_list_of_connectors.len() != updated_list_of_connectors.len(),
        || {
            Err(errors::ApiErrorResponse::PreconditionFailed {
                message: "current config and updated config have different lengths".to_string(),
            })
        },
    )?;

    let existing_set_of_default_connectors: FxHashSet<String> = FxHashSet::from_iter(
        default_list_of_connectors
            .iter()
            .map(|conn_choice| conn_choice.to_string()),
    );
    let updated_set_of_default_connectors: FxHashSet<String> = FxHashSet::from_iter(
        updated_list_of_connectors
            .iter()
            .map(|conn_choice| conn_choice.to_string()),
    );

    let symmetric_diff_between_existing_and_updated_connectors: Vec<String> =
        existing_set_of_default_connectors
            .symmetric_difference(&updated_set_of_default_connectors)
            .cloned()
            .collect();

    utils::when(
        !symmetric_diff_between_existing_and_updated_connectors.is_empty(),
        || {
            Err(errors::ApiErrorResponse::InvalidRequestData {
                message: format!(
                    "connector mismatch between old and new configs ({})",
                    symmetric_diff_between_existing_and_updated_connectors.join(", ")
                ),
            })
        },
    )?;
    profile_wrapper
        .update_default_fallback_routing_of_connectors_under_profile(
            db,
            &updated_list_of_connectors,
            key_manager_state,
            merchant_context.get_merchant_key_store(),
        )
        .await?;

    metrics::ROUTING_UPDATE_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(
        updated_list_of_connectors,
    ))
}

#[cfg(feature = "v1")]
pub async fn update_default_routing_config(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    updated_config: Vec<routing_types::RoutableConnectorChoice>,
    transaction_type: &enums::TransactionType,
) -> RouterResponse<Vec<routing_types::RoutableConnectorChoice>> {
    metrics::ROUTING_UPDATE_CONFIG.add(1, &[]);
    let db = state.store.as_ref();
    let default_config = helpers::get_merchant_default_config(
        db,
        merchant_context
            .get_merchant_account()
            .get_id()
            .get_string_repr(),
        transaction_type,
    )
    .await?;

    utils::when(default_config.len() != updated_config.len(), || {
        Err(errors::ApiErrorResponse::PreconditionFailed {
            message: "current config and updated config have different lengths".to_string(),
        })
    })?;

    let existing_set: FxHashSet<String> =
        FxHashSet::from_iter(default_config.iter().map(|c| c.to_string()));
    let updated_set: FxHashSet<String> =
        FxHashSet::from_iter(updated_config.iter().map(|c| c.to_string()));

    let symmetric_diff: Vec<String> = existing_set
        .symmetric_difference(&updated_set)
        .cloned()
        .collect();

    utils::when(!symmetric_diff.is_empty(), || {
        Err(errors::ApiErrorResponse::InvalidRequestData {
            message: format!(
                "connector mismatch between old and new configs ({})",
                symmetric_diff.join(", ")
            ),
        })
    })?;

    helpers::update_merchant_default_config(
        db,
        merchant_context
            .get_merchant_account()
            .get_id()
            .get_string_repr(),
        updated_config.clone(),
        transaction_type,
    )
    .await?;

    metrics::ROUTING_UPDATE_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(updated_config))
}

#[cfg(feature = "v2")]
pub async fn retrieve_default_fallback_algorithm_for_profile(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: common_utils::id_type::ProfileId,
) -> RouterResponse<Vec<routing_types::RoutableConnectorChoice>> {
    metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG.add(1, &[]);
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")?;

    let connectors_choice = admin::ProfileWrapper::new(profile)
        .get_default_fallback_list_of_connector_under_profile()?;

    metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(connectors_choice))
}

#[cfg(feature = "v1")]
pub async fn retrieve_default_routing_config(
    state: SessionState,
    profile_id: Option<common_utils::id_type::ProfileId>,
    merchant_context: domain::MerchantContext,
    transaction_type: &enums::TransactionType,
) -> RouterResponse<Vec<routing_types::RoutableConnectorChoice>> {
    metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG.add(1, &[]);
    let db = state.store.as_ref();
    let id = profile_id
        .map(|profile_id| profile_id.get_string_repr().to_owned())
        .unwrap_or_else(|| {
            merchant_context
                .get_merchant_account()
                .get_id()
                .get_string_repr()
                .to_string()
        });

    helpers::get_merchant_default_config(db, &id, transaction_type)
        .await
        .map(|conn_choice| {
            metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
            service_api::ApplicationResponse::Json(conn_choice)
        })
}

#[cfg(feature = "v2")]
pub async fn retrieve_routing_config_under_profile(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    query_params: RoutingRetrieveQuery,
    profile_id: common_utils::id_type::ProfileId,
    transaction_type: &enums::TransactionType,
) -> RouterResponse<routing_types::LinkedRoutingConfigRetrieveResponse> {
    metrics::ROUTING_RETRIEVE_LINK_CONFIG.add(1, &[]);
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")?;

    let record = db
        .list_routing_algorithm_metadata_by_profile_id(
            business_profile.get_id(),
            i64::from(query_params.limit.unwrap_or_default()),
            i64::from(query_params.offset.unwrap_or_default()),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;

    let active_algorithms = record
        .into_iter()
        .filter(|routing_rec| &routing_rec.algorithm_for == transaction_type)
        .map(|routing_algo| routing_algo.foreign_into())
        .collect::<Vec<_>>();

    metrics::ROUTING_RETRIEVE_LINK_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(
        routing_types::LinkedRoutingConfigRetrieveResponse::ProfileBased(active_algorithms),
    ))
}

#[cfg(feature = "v1")]
pub async fn retrieve_linked_routing_config(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    authentication_profile_id: Option<common_utils::id_type::ProfileId>,
    query_params: routing_types::RoutingRetrieveLinkQuery,
    transaction_type: enums::TransactionType,
) -> RouterResponse<routing_types::LinkedRoutingConfigRetrieveResponse> {
    metrics::ROUTING_RETRIEVE_LINK_CONFIG.add(1, &[]);

    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let merchant_key_store = merchant_context.get_merchant_key_store();
    let merchant_id = merchant_context.get_merchant_account().get_id();

    // Get business profiles
    let business_profiles = if let Some(profile_id) = query_params.profile_id {
        core_utils::validate_and_get_business_profile(
            db,
            key_manager_state,
            merchant_key_store,
            Some(&profile_id),
            merchant_id,
        )
        .await?
        .map(|profile| vec![profile])
        .get_required_value("Profile")
        .change_context(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?
    } else {
        let business_profile = db
            .list_profile_by_merchant_id(key_manager_state, merchant_key_store, merchant_id)
            .await
            .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
        core_utils::filter_objects_based_on_profile_id_list(
            authentication_profile_id.map(|profile_id| vec![profile_id]),
            business_profile,
        )
    };

    let mut active_algorithms = Vec::new();

    for business_profile in business_profiles {
        let profile_id = business_profile.get_id();

        // Handle static routing algorithm
        let routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
            enums::TransactionType::Payment => &business_profile.routing_algorithm,
            #[cfg(feature = "payouts")]
            enums::TransactionType::Payout => &business_profile.payout_routing_algorithm,
            enums::TransactionType::ThreeDsAuthentication => {
                &business_profile.three_ds_decision_rule_algorithm
            }
        }
        .clone()
        .map(|val| val.parse_value("RoutingAlgorithmRef"))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("unable to deserialize routing algorithm ref from merchant account")?
        .unwrap_or_default();

        if let Some(algorithm_id) = routing_ref.algorithm_id {
            let record = db
                .find_routing_algorithm_metadata_by_algorithm_id_profile_id(
                    &algorithm_id,
                    profile_id,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
            let hs_records: Vec<routing_types::RoutingDictionaryRecord> =
                vec![record.foreign_into()];
            let de_records = retrieve_decision_engine_active_rules(
                &state,
                &transaction_type,
                profile_id.clone(),
                hs_records.clone(),
            )
            .await;
            compare_and_log_result(
                de_records.clone(),
                hs_records.clone(),
                "list_active_routing".to_string(),
            );
            active_algorithms.append(
                &mut select_routing_result(&state, &business_profile, hs_records, de_records).await,
            );
        }

        // Handle dynamic routing algorithms
        let dynamic_routing_ref: routing_types::DynamicRoutingAlgorithmRef = business_profile
            .dynamic_routing_algorithm
            .clone()
            .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable(
                "unable to deserialize dynamic routing algorithm ref from business profile",
            )?
            .unwrap_or_default();

        // Collect all dynamic algorithm IDs
        let mut dynamic_algorithm_ids = Vec::new();

        if let Some(sba) = &dynamic_routing_ref.success_based_algorithm {
            if let Some(id) = &sba.algorithm_id_with_timestamp.algorithm_id {
                dynamic_algorithm_ids.push(id.clone());
            }
        }
        if let Some(era) = &dynamic_routing_ref.elimination_routing_algorithm {
            if let Some(id) = &era.algorithm_id_with_timestamp.algorithm_id {
                dynamic_algorithm_ids.push(id.clone());
            }
        }
        if let Some(cbr) = &dynamic_routing_ref.contract_based_routing {
            if let Some(id) = &cbr.algorithm_id_with_timestamp.algorithm_id {
                dynamic_algorithm_ids.push(id.clone());
            }
        }

        // Fetch all dynamic algorithms
        for algorithm_id in dynamic_algorithm_ids {
            let record = db
                .find_routing_algorithm_metadata_by_algorithm_id_profile_id(
                    &algorithm_id,
                    profile_id,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
            if record.algorithm_for == transaction_type {
                active_algorithms.push(record.foreign_into());
            }
        }
    }

    metrics::ROUTING_RETRIEVE_LINK_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(
        routing_types::LinkedRoutingConfigRetrieveResponse::ProfileBased(active_algorithms),
    ))
}

pub async fn retrieve_decision_engine_active_rules(
    state: &SessionState,
    transaction_type: &enums::TransactionType,
    profile_id: common_utils::id_type::ProfileId,
    hs_records: Vec<routing_types::RoutingDictionaryRecord>,
) -> Vec<routing_types::RoutingDictionaryRecord> {
    let mut de_records =
        list_de_euclid_active_routing_algorithm(state, profile_id.get_string_repr().to_owned())
            .await
            .map_err(|e| {
                router_env::logger::error!(?e, "Failed to list DE Euclid active routing algorithm");
            })
            .ok() // Avoid throwing error if Decision Engine is not available or other errors thrown
            .unwrap_or_default();
    // Use Hs records to list the dynamic algorithms as DE is not supporting dynamic algorithms in HS standard
    let mut dynamic_algos = hs_records
        .into_iter()
        .filter(|record| record.kind == routing_types::RoutingAlgorithmKind::Dynamic)
        .collect::<Vec<_>>();
    de_records.append(&mut dynamic_algos);
    de_records
        .into_iter()
        .filter(|r| r.algorithm_for == Some(*transaction_type))
        .collect::<Vec<_>>()
}
// List all the default fallback algorithms under all the profile under a merchant
pub async fn retrieve_default_routing_config_for_profiles(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    transaction_type: &enums::TransactionType,
) -> RouterResponse<Vec<routing_types::ProfileDefaultRoutingConfig>> {
    metrics::ROUTING_RETRIEVE_CONFIG_FOR_PROFILE.add(1, &[]);
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let all_profiles = db
        .list_profile_by_merchant_id(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().get_id(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)
        .attach_printable("error retrieving all business profiles for merchant")?;

    let retrieve_config_futures = all_profiles
        .iter()
        .map(|prof| {
            helpers::get_merchant_default_config(
                db,
                prof.get_id().get_string_repr(),
                transaction_type,
            )
        })
        .collect::<Vec<_>>();

    let configs = futures::future::join_all(retrieve_config_futures)
        .await
        .into_iter()
        .collect::<Result<Vec<_>, _>>()?;

    let default_configs = configs
        .into_iter()
        .zip(all_profiles.iter().map(|prof| prof.get_id().to_owned()))
        .map(
            |(config, profile_id)| routing_types::ProfileDefaultRoutingConfig {
                profile_id,
                connectors: config,
            },
        )
        .collect::<Vec<_>>();

    metrics::ROUTING_RETRIEVE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(default_configs))
}

pub async fn update_default_routing_config_for_profile(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    updated_config: Vec<routing_types::RoutableConnectorChoice>,
    profile_id: common_utils::id_type::ProfileId,
    transaction_type: &enums::TransactionType,
) -> RouterResponse<routing_types::ProfileDefaultRoutingConfig> {
    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE.add(1, &[]);

    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

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

    utils::when(default_config.len() != updated_config.len(), || {
        Err(errors::ApiErrorResponse::PreconditionFailed {
            message: "current config and updated config have different lengths".to_string(),
        })
    })?;

    let existing_set = FxHashSet::from_iter(
        default_config
            .iter()
            .map(|c| (c.connector.to_string(), c.merchant_connector_id.as_ref())),
    );

    let updated_set = FxHashSet::from_iter(
        updated_config
            .iter()
            .map(|c| (c.connector.to_string(), c.merchant_connector_id.as_ref())),
    );

    let symmetric_diff = existing_set
        .symmetric_difference(&updated_set)
        .cloned()
        .collect::<Vec<_>>();

    utils::when(!symmetric_diff.is_empty(), || {
        let error_str = symmetric_diff
            .into_iter()
            .map(|(connector, ident)| format!("'{connector}:{ident:?}'"))
            .collect::<Vec<_>>()
            .join(", ");

        Err(errors::ApiErrorResponse::InvalidRequestData {
            message: format!("connector mismatch between old and new configs ({error_str})"),
        })
    })?;

    helpers::update_merchant_default_config(
        db,
        business_profile.get_id().get_string_repr(),
        updated_config.clone(),
        transaction_type,
    )
    .await?;

    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(
        routing_types::ProfileDefaultRoutingConfig {
            profile_id: business_profile.get_id().to_owned(),
            connectors: updated_config,
        },
    ))
}

// Toggle the specific routing type as well as add the default configs in RoutingAlgorithm table
// and update the same in business profile table.

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn toggle_specific_dynamic_routing(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    feature_to_enable: routing::DynamicRoutingFeatures,
    profile_id: common_utils::id_type::ProfileId,
    dynamic_routing_type: routing::DynamicRoutingType,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(
        1,
        router_env::metric_attributes!(
            ("profile_id", profile_id.clone()),
            ("algorithm_type", dynamic_routing_type.to_string())
        ),
    );
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

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

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

    match feature_to_enable {
        routing::DynamicRoutingFeatures::Metrics
        | routing::DynamicRoutingFeatures::DynamicConnectorSelection => {
            // occurs when algorithm is already present in the db
            // 1. If present with same feature then return response as already enabled
            // 2. Else update the feature and persist the same on db
            // 3. If not present in db then create a new default entry
            Box::pin(helpers::enable_dynamic_routing_algorithm(
                &state,
                merchant_context.get_merchant_key_store().clone(),
                business_profile,
                feature_to_enable,
                dynamic_routing_algo_ref,
                dynamic_routing_type,
                None,
            ))
            .await
        }
        routing::DynamicRoutingFeatures::None => {
            // disable specific dynamic routing for the requested profile
            helpers::disable_dynamic_routing_algorithm(
                &state,
                merchant_context.get_merchant_key_store().clone(),
                business_profile,
                dynamic_routing_algo_ref,
                dynamic_routing_type,
            )
            .await
        }
    }
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn create_specific_dynamic_routing(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    feature_to_enable: routing::DynamicRoutingFeatures,
    profile_id: common_utils::id_type::ProfileId,
    dynamic_routing_type: routing::DynamicRoutingType,
    payload: routing_types::DynamicRoutingPayload,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(
        1,
        router_env::metric_attributes!(
            ("profile_id", profile_id.clone()),
            ("algorithm_type", dynamic_routing_type.to_string())
        ),
    );
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

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

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

    match feature_to_enable {
        routing::DynamicRoutingFeatures::Metrics
        | routing::DynamicRoutingFeatures::DynamicConnectorSelection => {
            Box::pin(helpers::enable_dynamic_routing_algorithm(
                &state,
                merchant_context.get_merchant_key_store().clone(),
                business_profile,
                feature_to_enable,
                dynamic_routing_algo_ref,
                dynamic_routing_type,
                Some(payload),
            ))
            .await
        }
        routing::DynamicRoutingFeatures::None => {
            // disable specific dynamic routing for the requested profile
            helpers::disable_dynamic_routing_algorithm(
                &state,
                merchant_context.get_merchant_key_store().clone(),
                business_profile,
                dynamic_routing_algo_ref,
                dynamic_routing_type,
            )
            .await
        }
    }
}

#[cfg(feature = "v1")]
pub async fn configure_dynamic_routing_volume_split(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: common_utils::id_type::ProfileId,
    routing_info: routing::RoutingVolumeSplit,
) -> RouterResponse<routing::RoutingVolumeSplit> {
    metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(
        1,
        router_env::metric_attributes!(("profile_id", profile_id.clone())),
    );
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    utils::when(
        routing_info.split > crate::consts::DYNAMIC_ROUTING_MAX_VOLUME,
        || {
            Err(errors::ApiErrorResponse::InvalidRequestData {
                message: "Dynamic routing volume split should be less than 100".to_string(),
            })
        },
    )?;

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

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

    dynamic_routing_algo_ref.update_volume_split(Some(routing_info.split));

    helpers::update_business_profile_active_dynamic_algorithm_ref(
        db,
        &((&state).into()),
        merchant_context.get_merchant_key_store(),
        business_profile.clone(),
        dynamic_routing_algo_ref.clone(),
    )
    .await?;

    Ok(service_api::ApplicationResponse::Json(routing_info))
}

#[cfg(feature = "v1")]
pub async fn retrieve_dynamic_routing_volume_split(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: common_utils::id_type::ProfileId,
) -> RouterResponse<routing_types::RoutingVolumeSplitResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

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

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

    let resp = routing_types::RoutingVolumeSplitResponse {
        split: dynamic_routing_algo_ref
            .dynamic_routing_volume_split
            .unwrap_or_default(),
    };

    Ok(service_api::ApplicationResponse::Json(resp))
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn success_based_routing_update_configs(
    state: SessionState,
    request: routing_types::SuccessBasedRoutingConfig,
    algorithm_id: common_utils::id_type::RoutingId,
    profile_id: common_utils::id_type::ProfileId,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE.add(
        1,
        router_env::metric_attributes!(
            ("profile_id", profile_id.clone()),
            (
                "algorithm_type",
                routing::DynamicRoutingType::SuccessRateBasedRouting.to_string()
            )
        ),
    );
    let db = state.store.as_ref();

    let dynamic_routing_algo_to_update = db
        .find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, &algorithm_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;

    let mut config_to_update: routing::SuccessBasedRoutingConfig = dynamic_routing_algo_to_update
        .algorithm_data
        .parse_value::<routing::SuccessBasedRoutingConfig>("SuccessBasedRoutingConfig")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("unable to deserialize algorithm data from routing table into SuccessBasedRoutingConfig")?;

    config_to_update.update(request);

    let updated_algorithm_id = common_utils::generate_routing_id_of_default_length();
    let timestamp = common_utils::date_time::now();
    let algo = RoutingAlgorithm {
        algorithm_id: updated_algorithm_id,
        profile_id: dynamic_routing_algo_to_update.profile_id,
        merchant_id: dynamic_routing_algo_to_update.merchant_id,
        name: dynamic_routing_algo_to_update.name,
        description: dynamic_routing_algo_to_update.description,
        kind: dynamic_routing_algo_to_update.kind,
        algorithm_data: serde_json::json!(config_to_update.clone()),
        created_at: timestamp,
        modified_at: timestamp,
        algorithm_for: dynamic_routing_algo_to_update.algorithm_for,
        decision_engine_routing_id: None,
    };
    let record = db
        .insert_routing_algorithm(algo)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to insert record in routing algorithm table")?;

    // redact cache for success based routing configs
    let cache_key = format!(
        "{}_{}",
        profile_id.get_string_repr(),
        algorithm_id.get_string_repr()
    );
    let cache_entries_to_redact = vec![cache::CacheKind::SuccessBasedDynamicRoutingCache(
        cache_key.into(),
    )];
    let _ = cache::redact_from_redis_and_publish(
        state.store.get_cache_store().as_ref(),
        cache_entries_to_redact,
    )
    .await
    .map_err(|e| router_env::logger::error!("unable to publish into the redact channel for evicting the success based routing config cache {e:?}"));

    let new_record = record.foreign_into();

    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE.add(
        1,
        router_env::metric_attributes!(("profile_id", profile_id.clone())),
    );

    if !state.conf.open_router.dynamic_routing_enabled {
        state
            .grpc_client
            .dynamic_routing
            .as_ref()
            .async_map(|dr_client| async {
                dr_client
                    .success_rate_client
                    .invalidate_success_rate_routing_keys(
                        profile_id.get_string_repr().into(),
                        state.get_grpc_headers(),
                    )
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to invalidate the routing keys")
            })
            .await
            .transpose()?;
    }

    Ok(service_api::ApplicationResponse::Json(new_record))
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn elimination_routing_update_configs(
    state: SessionState,
    request: routing_types::EliminationRoutingConfig,
    algorithm_id: common_utils::id_type::RoutingId,
    profile_id: common_utils::id_type::ProfileId,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE.add(
        1,
        router_env::metric_attributes!(
            ("profile_id", profile_id.clone()),
            (
                "algorithm_type",
                routing::DynamicRoutingType::EliminationRouting.to_string()
            )
        ),
    );

    let db = state.store.as_ref();

    let dynamic_routing_algo_to_update = db
        .find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, &algorithm_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;

    let mut config_to_update: routing::EliminationRoutingConfig = dynamic_routing_algo_to_update
        .algorithm_data
        .parse_value::<routing::EliminationRoutingConfig>("EliminationRoutingConfig")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "unable to deserialize algorithm data from routing table into EliminationRoutingConfig",
        )?;

    config_to_update.update(request);

    let updated_algorithm_id = common_utils::generate_routing_id_of_default_length();
    let timestamp = common_utils::date_time::now();
    let algo = RoutingAlgorithm {
        algorithm_id: updated_algorithm_id,
        profile_id: dynamic_routing_algo_to_update.profile_id,
        merchant_id: dynamic_routing_algo_to_update.merchant_id,
        name: dynamic_routing_algo_to_update.name,
        description: dynamic_routing_algo_to_update.description,
        kind: dynamic_routing_algo_to_update.kind,
        algorithm_data: serde_json::json!(config_to_update),
        created_at: timestamp,
        modified_at: timestamp,
        algorithm_for: dynamic_routing_algo_to_update.algorithm_for,
        decision_engine_routing_id: None,
    };

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

    // redact cache for elimination routing configs
    let cache_key = format!(
        "{}_{}",
        profile_id.get_string_repr(),
        algorithm_id.get_string_repr()
    );
    let cache_entries_to_redact = vec![cache::CacheKind::EliminationBasedDynamicRoutingCache(
        cache_key.into(),
    )];

    cache::redact_from_redis_and_publish(
        state.store.get_cache_store().as_ref(),
        cache_entries_to_redact,
    )
    .await
    .map_err(|e| router_env::logger::error!("unable to publish into the redact channel for evicting the elimination routing config cache {e:?}")).ok();

    let new_record = record.foreign_into();

    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE.add(
        1,
        router_env::metric_attributes!(("profile_id", profile_id.clone())),
    );

    if !state.conf.open_router.dynamic_routing_enabled {
        state
            .grpc_client
            .dynamic_routing
            .as_ref()
            .async_map(|dr_client| async {
                dr_client
                    .elimination_based_client
                    .invalidate_elimination_bucket(
                        profile_id.get_string_repr().into(),
                        state.get_grpc_headers(),
                    )
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to invalidate the elimination routing keys")
            })
            .await
            .transpose()?;
    }

    Ok(service_api::ApplicationResponse::Json(new_record))
}

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

    let business_profile: domain::Profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")


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


impl api::Payment for Katapult {}


impl ApiEventMetric for ToggleBlocklistQuery {}

impl ApiEventMetric for for ToggleBlocklistQuery


pub fn get_valid_webhook_status(key: &str) -> JsResult {
    let event_class = EventClass::from_str(key)
        .map_err(|_| "Invalid webhook event type received".to_string())
        .err_to_js()?;

    match event_class {
        EventClass::Payments => {
            let statuses: Vec<IntentStatus> = IntentStatus::iter()
                .filter(|intent_status| Into::<Option<EventType>>::into(*intent_status).is_some())
                .collect();
            Ok(serde_wasm_bindgen::to_value(&statuses)?)
        }
        EventClass::Refunds => {
            let statuses: Vec<RefundStatus> = RefundStatus::iter()
                .filter(|status| Into::<Option<EventType>>::into(*status).is_some())
                .collect();
            Ok(serde_wasm_bindgen::to_value(&statuses)?)
        }
        EventClass::Disputes => {
            let statuses: Vec<DisputeStatus> = DisputeStatus::iter().collect();
            Ok(serde_wasm_bindgen::to_value(&statuses)?)
        }
        EventClass::Mandates => {
            let statuses: Vec<MandateStatus> = MandateStatus::iter()
                .filter(|status| Into::<Option<EventType>>::into(*status).is_some())
                .collect();
            Ok(serde_wasm_bindgen::to_value(&statuses)?)
        }
        #[cfg(feature = "payouts")]
        EventClass::Payouts => {
            let statuses: Vec<PayoutStatus> = PayoutStatus::iter()
                .filter(|status| Into::<Option<EventType>>::into(*status).is_some())
                .collect();
            Ok(serde_wasm_bindgen::to_value(&statuses)?)
        }
    }
}


impl FraudCheckConnectorData {
    pub fn get_connector_by_name(name: &str) -> CustomResult<Self, errors::ApiErrorResponse> {
        let connector_name = enums::FrmConnectors::from_str(name)
            .change_context(errors::ApiErrorResponse::IncorrectConnectorNameGiven)
            .attach_printable_lazy(|| {
                format!("unable to parse connector: {:?}", name.to_string())
            })?;
        let connector = Self::convert_connector(connector_name)?;
        Ok(Self {
            connector,
            connector_name,
        })
    }

    fn convert_connector(
        connector_name: enums::FrmConnectors,
    ) -> CustomResult<ConnectorEnum, errors::ApiErrorResponse> {
        match connector_name {
            enums::FrmConnectors::Signifyd => {
                Ok(ConnectorEnum::Old(Box::new(&connector::Signifyd)))
            }
            enums::FrmConnectors::Riskified => {
                Ok(ConnectorEnum::Old(Box::new(connector::Riskified::new())))
            }
        }
    }
}


impl api::PaymentSession for Fiuu {}


pub struct SquareTokenResponse {
    card_nonce: Secret<String>,
}


File: crates/router/src/core/payment_link/validator.rs
 Public functions: 1

use actix_http::header;
use api_models::admin::PaymentLinkConfig;
use common_utils::validation::validate_domain_against_allowed_domains;
use error_stack::{report, ResultExt};
use url::Url;

use crate::{
    core::errors::{self, RouterResult},
    types::storage::PaymentLink,
};

pub fn validate_secure_payment_link_render_request(
    request_headers: &header::HeaderMap,
    payment_link: &PaymentLink,
    payment_link_config: &PaymentLinkConfig,
) -> RouterResult<()> {
    let link_id = payment_link.payment_link_id.clone();
    let allowed_domains = payment_link_config
        .allowed_domains
        .clone()
        .ok_or(report!(errors::ApiErrorResponse::InvalidRequestUrl))
        .attach_printable_lazy(|| {
            format!("Secure payment link was not generated for {link_id}\nmissing allowed_domains")
        })?;

    // Validate secure_link was generated
    if payment_link.secure_link.clone().is_none() {
        return Err(report!(errors::ApiErrorResponse::InvalidRequestUrl)).attach_printable_lazy(
            || format!("Secure payment link was not generated for {link_id}\nmissing secure_link"),
        );
    }

    // Fetch destination is "iframe"
    match request_headers.get("sec-fetch-dest").and_then(|v| v.to_str().ok()) {
        Some("iframe") => Ok(()),
        Some(requestor) => Err(report!(errors::ApiErrorResponse::AccessForbidden {
            resource: "payment_link".to_string(),
        }))
        .attach_printable_lazy(|| {
            format!(
                "Access to payment_link [{link_id}] is forbidden when requested through {requestor}",

            )
        }),
        None => Err(report!(errors::ApiErrorResponse::AccessForbidden {
            resource: "payment_link".to_string(),
        }))
        .attach_printable_lazy(|| {
            format!(
                "Access to payment_link [{link_id}] is forbidden when sec-fetch-dest is not present in request headers",

            )
        }),
    }?;

    // Validate origin / referer
    let domain_in_req = {
        let origin_or_referer = request_headers
            .get("origin")
            .or_else(|| request_headers.get("referer"))
            .and_then(|v| v.to_str().ok())
            .ok_or_else(|| {
                report!(errors::ApiErrorResponse::AccessForbidden {
                    resource: "payment_link".to_string(),
                })
            })
            .attach_printable_lazy(|| {
                format!(
                    "Access to payment_link [{link_id}] is forbidden when origin or referer is not present in request headers",

                )
            })?;

        let url = Url::parse(origin_or_referer)
            .map_err(|_| {
                report!(errors::ApiErrorResponse::AccessForbidden {
                    resource: "payment_link".to_string(),
                })
            })
            .attach_printable_lazy(|| {
                format!("Invalid URL found in request headers {origin_or_referer}")
            })?;

        url.host_str()
            .and_then(|host| url.port().map(|port| format!("{host}:{port}")))
            .or_else(|| url.host_str().map(String::from))
            .ok_or_else(|| {
                report!(errors::ApiErrorResponse::AccessForbidden {
                    resource: "payment_link".to_string(),
                })
            })
            .attach_printable_lazy(|| {
                format!("host or port not found in request headers {url:?}")
            })?
    };

    if validate_domain_against_allowed_domains(&domain_in_req, allowed_domains) {
        Ok(())
    } else {
        Err(report!(errors::ApiErrorResponse::AccessForbidden {
            resource: "payment_link".to_string(),
        }))
        .attach_printable_lazy(|| {
            format!(
                "Access to payment_link [{link_id}] is forbidden from requestor - {domain_in_req}",
            )
        })
    }
}


pub struct JpmorganRouterData<T> {
    pub amount: MinorUnit,
    pub router_data: T,
}


pub async fn render_payout_link(
    state: web::Data<AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::PayoutId,
    )


pub struct SilverflowTokenizationResponse {
    pub key: String,
    #[serde(rename = "agentKey")]
    pub agent_key: String,
    pub last4: String,
    pub status: String,
    pub reference: String,
    #[serde(rename = "cardInfo")]
    pub card_info: Vec<CardInfo>,
    pub created: String,
    #[serde(rename = "cvcPresent")]
    pub cvc_present: bool,
    pub version: i32,
}


pub struct BamboraVoidRequest {
    amount: FloatMajorUnit,
}


impl api::PaymentSession for Trustpayments {}

impl api::PaymentSession for for Trustpayments


impl fmt::Display for ApiMethod {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Grpc => write!(f, "Grpc"),
            Self::Rest(method) => write!(f, "Rest ({method})"),
        }
    }
}


pub struct GocardlessBankAccountResponse {
    customer_bank_accounts: CustomerBankAccountResponse,
}


pub struct NexixpayRefundRequest {
    pub amount: StringMinorUnit,
    pub currency: Currency,
}


pub async fn health() -> impl actix_web::Responder {
    metrics::HEALTH_METRIC.add(1, &[]);
    logger::info!("Health was called");

    actix_web::HttpResponse::Ok().body("health is good")
}


impl api::Refund for Adyenplatform {}


pub struct MerchantCategoryCodeWithName {
    pub code: MerchantCategoryCode,
    pub name: MerchantCategory,
}


File: crates/diesel_models/src/payment_attempt.rs
 Public functions: 6
 Public structs: 11

#[cfg(feature = "v2")]
use common_types::payments as common_payments_types;
use common_types::primitive_wrappers::{
    ExtendedAuthorizationAppliedBool, RequestExtendedAuthorizationBool,
};
use common_utils::{
    id_type, pii,
    types::{ConnectorTransactionId, ConnectorTransactionIdTrait, MinorUnit},
};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::enums as storage_enums;
#[cfg(feature = "v1")]
use crate::schema::payment_attempt;
#[cfg(feature = "v2")]
use crate::{schema_v2::payment_attempt, RequiredFromNullable};

common_utils::impl_to_sql_from_sql_json!(ConnectorMandateReferenceId);
#[derive(
    Clone, Debug, serde::Deserialize, serde::Serialize, Eq, PartialEq, diesel::AsExpression,
)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct ConnectorMandateReferenceId {
    pub connector_mandate_id: Option<String>,
    pub payment_method_id: Option<String>,
    pub mandate_metadata: Option<pii::SecretSerdeValue>,
    pub connector_mandate_request_reference_id: Option<String>,
}

impl ConnectorMandateReferenceId {
    pub fn get_connector_mandate_request_reference_id(&self) -> Option<String> {
        self.connector_mandate_request_reference_id.clone()
    }
}

#[cfg(feature = "v2")]
#[derive(
    Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Serialize, Deserialize, Selectable,
)]
#[diesel(table_name = payment_attempt, primary_key(id), check_for_backend(diesel::pg::Pg))]
pub struct PaymentAttempt {
    pub payment_id: id_type::GlobalPaymentId,
    pub merchant_id: id_type::MerchantId,
    pub status: storage_enums::AttemptStatus,
    pub connector: Option<String>,
    pub error_message: Option<String>,
    pub surcharge_amount: Option<MinorUnit>,
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,
    #[diesel(deserialize_as = RequiredFromNullable<storage_enums::AuthenticationType>)]
    pub authentication_type: storage_enums::AuthenticationType,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_synced: Option<PrimitiveDateTime>,
    pub cancellation_reason: Option<String>,
    pub amount_to_capture: Option<MinorUnit>,
    pub browser_info: Option<common_utils::types::BrowserInformation>,
    pub error_code: Option<String>,
    pub payment_token: Option<String>,
    pub connector_metadata: Option<pii::SecretSerdeValue>,
    pub payment_experience: Option<storage_enums::PaymentExperience>,
    pub payment_method_data: Option<pii::SecretSerdeValue>,
    pub preprocessing_step_id: Option<String>,
    pub error_reason: Option<String>,
    pub multiple_capture_count: Option<i16>,
    pub connector_response_reference_id: Option<String>,
    pub amount_capturable: MinorUnit,
    pub updated_by: String,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub encoded_data: Option<masking::Secret<String>>,
    pub unified_code: Option<String>,
    pub unified_message: Option<String>,
    #[diesel(deserialize_as = RequiredFromNullable<MinorUnit>)]
    pub net_amount: MinorUnit,
    pub external_three_ds_authentication_attempted: Option<bool>,
    pub authentication_connector: Option<String>,
    pub authentication_id: Option<id_type::AuthenticationId>,
    pub fingerprint_id: Option<String>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub customer_acceptance: Option<masking::Secret<common_payments_types::CustomerAcceptance>>,
    pub profile_id: id_type::ProfileId,
    pub organization_id: id_type::OrganizationId,
    pub card_network: Option<String>,
    pub shipping_cost: Option<MinorUnit>,
    pub order_tax_amount: Option<MinorUnit>,
    pub request_extended_authorization: Option<RequestExtendedAuthorizationBool>,
    pub extended_authorization_applied: Option<ExtendedAuthorizationAppliedBool>,
    pub capture_before: Option<PrimitiveDateTime>,
    pub card_discovery: Option<storage_enums::CardDiscovery>,
    pub charges: Option<common_types::payments::ConnectorChargeResponseData>,
    pub processor_merchant_id: Option<id_type::MerchantId>,
    pub created_by: Option<String>,
    pub connector_request_reference_id: Option<String>,
    pub network_transaction_id: Option<String>,
    #[diesel(deserialize_as = RequiredFromNullable<storage_enums::PaymentMethod>)]
    pub payment_method_type_v2: storage_enums::PaymentMethod,
    pub connector_payment_id: Option<ConnectorTransactionId>,
    pub payment_method_subtype: storage_enums::PaymentMethodType,
    pub routing_result: Option<serde_json::Value>,
    pub authentication_applied: Option<common_enums::AuthenticationType>,
    pub external_reference_id: Option<String>,
    pub tax_on_surcharge: Option<MinorUnit>,
    pub payment_method_billing_address: Option<common_utils::encryption::Encryption>,
    pub redirection_data: Option<RedirectForm>,
    pub connector_payment_data: Option<String>,
    pub connector_token_details: Option<ConnectorTokenDetails>,
    pub id: id_type::GlobalAttemptId,
    pub feature_metadata: Option<PaymentAttemptFeatureMetadata>,
    /// This field can be returned for both approved and refused Mastercard payments.
    /// This code provides additional information about the type of transaction or the reason why the payment failed.
    /// If the payment failed, the network advice code gives guidance on if and when you can retry the payment.
    pub network_advice_code: Option<String>,
    /// For card errors resulting from a card issuer decline, a brand specific 2, 3, or 4 digit code which indicates the reason the authorization failed.
    pub network_decline_code: Option<String>,
    /// A string indicating how to proceed with an network error if payment gateway provide one. This is used to understand the network error code better.
    pub network_error_message: Option<String>,
}

#[cfg(feature = "v1")]
#[derive(
    Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Serialize, Deserialize, Selectable,
)]
#[diesel(table_name = payment_attempt, primary_key(attempt_id, merchant_id), check_for_backend(diesel::pg::Pg))]
pub struct PaymentAttempt {
    pub payment_id: id_type::PaymentId,
    pub merchant_id: id_type::MerchantId,
    pub attempt_id: String,
    pub status: storage_enums::AttemptStatus,
    pub amount: MinorUnit,
    pub currency: Option<storage_enums::Currency>,
    pub save_to_locker: Option<bool>,
    pub connector: Option<String>,
    pub error_message: Option<String>,
    pub offer_amount: Option<MinorUnit>,
    pub surcharge_amount: Option<MinorUnit>,
    pub tax_amount: Option<MinorUnit>,
    pub payment_method_id: Option<String>,
    pub payment_method: Option<storage_enums::PaymentMethod>,
    pub connector_transaction_id: Option<ConnectorTransactionId>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub capture_on: Option<PrimitiveDateTime>,
    pub confirm: bool,
    pub authentication_type: Option<storage_enums::AuthenticationType>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_synced: Option<PrimitiveDateTime>,
    pub cancellation_reason: Option<String>,
    pub amount_to_capture: Option<MinorUnit>,
    pub mandate_id: Option<String>,
    pub browser_info: Option<serde_json::Value>,
    pub error_code: Option<String>,
    pub payment_token: Option<String>,
    pub connector_metadata: Option<serde_json::Value>,
    pub payment_experience: Option<storage_enums::PaymentExperience>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub payment_method_data: Option<serde_json::Value>,
    pub business_sub_label: Option<String>,
    pub straight_through_algorithm: Option<serde_json::Value>,
    pub preprocessing_step_id: Option<String>,
    // providing a location to store mandate details intermediately for transaction
    pub mandate_details: Option<storage_enums::MandateDataType>,
    pub error_reason: Option<String>,
    pub multiple_capture_count: Option<i16>,
    // reference to the payment at connector side
    pub connector_response_reference_id: Option<String>,
    pub amount_capturable: MinorUnit,
    pub updated_by: String,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub authentication_data: Option<serde_json::Value>,
    pub encoded_data: Option<String>,
    pub unified_code: Option<String>,
    pub unified_message: Option<String>,
    pub net_amount: Option<MinorUnit>,
    pub external_three_ds_authentication_attempted: Option<bool>,
    pub authentication_connector: Option<String>,
    pub authentication_id: Option<id_type::AuthenticationId>,
    pub mandate_data: Option<storage_enums::MandateDetails>,
    pub fingerprint_id: Option<String>,
    pub payment_method_billing_address_id: Option<String>,
    pub charge_id: Option<String>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub profile_id: id_type::ProfileId,
    pub organization_id: id_type::OrganizationId,
    pub card_network: Option<String>,
    pub shipping_cost: Option<MinorUnit>,
    pub order_tax_amount: Option<MinorUnit>,
    /// INFO: This field is deprecated and replaced by processor_transaction_data
    pub connector_transaction_data: Option<String>,
    pub connector_mandate_detail: Option<ConnectorMandateReferenceId>,
    pub request_extended_authorization: Option<RequestExtendedAuthorizationBool>,
    pub extended_authorization_applied: Option<ExtendedAuthorizationAppliedBool>,
    pub capture_before: Option<PrimitiveDateTime>,
    pub processor_transaction_data: Option<String>,
    pub card_discovery: Option<storage_enums::CardDiscovery>,
    pub charges: Option<common_types::payments::ConnectorChargeResponseData>,
    pub issuer_error_code: Option<String>,
    pub issuer_error_message: Option<String>,
    pub processor_merchant_id: Option<id_type::MerchantId>,
    pub created_by: Option<String>,
    pub setup_future_usage_applied: Option<storage_enums::FutureUsage>,
    pub routing_approach: Option<storage_enums::RoutingApproach>,
    pub connector_request_reference_id: Option<String>,
    pub network_transaction_id: Option<String>,
}

#[cfg(feature = "v1")]
impl ConnectorTransactionIdTrait for PaymentAttempt {
    fn get_optional_connector_transaction_id(&self) -> Option<&String> {
        match self
            .connector_transaction_id
            .as_ref()
            .map(|txn_id| txn_id.get_txn_id(self.processor_transaction_data.as_ref()))
            .transpose()
        {
            Ok(txn_id) => txn_id,

            // In case hashed data is missing from DB, use the hashed ID as connector transaction ID
            Err(_) => self
                .connector_transaction_id
                .as_ref()
                .map(|txn_id| txn_id.get_id()),
        }
    }
}

#[cfg(feature = "v2")]
impl ConnectorTransactionIdTrait for PaymentAttempt {
    fn get_optional_connector_transaction_id(&self) -> Option<&String> {
        match self
            .connector_payment_id
            .as_ref()
            .map(|txn_id| txn_id.get_txn_id(self.connector_payment_data.as_ref()))
            .transpose()
        {
            Ok(txn_id) => txn_id,

            // In case hashed data is missing from DB, use the hashed ID as connector payment ID
            Err(_) => self
                .connector_payment_id
                .as_ref()
                .map(|txn_id| txn_id.get_id()),
        }
    }
}

#[cfg(feature = "v2")]
#[derive(
    Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize, diesel::AsExpression,
)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct ConnectorTokenDetails {
    pub connector_mandate_id: Option<String>,
    pub connector_token_request_reference_id: Option<String>,
}

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

#[cfg(feature = "v2")]
impl ConnectorTokenDetails {
    pub fn get_connector_token_request_reference_id(&self) -> Option<String> {
        self.connector_token_request_reference_id.clone()
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Queryable, Serialize, Deserialize)]
pub struct PaymentListFilters {
    pub connector: Vec<String>,
    pub currency: Vec<storage_enums::Currency>,
    pub status: Vec<storage_enums::IntentStatus>,
    pub payment_method: Vec<storage_enums::PaymentMethod>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = payment_attempt)]
pub struct PaymentAttemptNew {
    pub payment_id: id_type::GlobalPaymentId,
    pub merchant_id: id_type::MerchantId,
    pub status: storage_enums::AttemptStatus,
    pub error_message: Option<String>,
    pub surcharge_amount: Option<MinorUnit>,
    pub tax_on_surcharge: Option<MinorUnit>,
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,
    pub authentication_type: storage_enums::AuthenticationType,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_synced: Option<PrimitiveDateTime>,
    pub cancellation_reason: Option<String>,
    pub amount_to_capture: Option<MinorUnit>,
    pub browser_info: Option<common_utils::types::BrowserInformation>,
    pub payment_token: Option<String>,
    pub error_code: Option<String>,
    pub connector_metadata: Option<pii::SecretSerdeValue>,
    pub payment_experience: Option<storage_enums::PaymentExperience>,
    pub payment_method_data: Option<pii::SecretSerdeValue>,
    pub preprocessing_step_id: Option<String>,
    pub error_reason: Option<String>,
    pub connector_response_reference_id: Option<String>,
    pub network_transaction_id: Option<String>,
    pub multiple_capture_count: Option<i16>,
    pub amount_capturable: MinorUnit,
    pub updated_by: String,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub redirection_data: Option<RedirectForm>,
    pub encoded_data: Option<masking::Secret<String>>,
    pub unified_code: Option<String>,
    pub unified_message: Option<String>,
    pub net_amount: MinorUnit,
    pub external_three_ds_authentication_attempted: Option<bool>,
    pub authentication_connector: Option<String>,
    pub authentication_id: Option<id_type::AuthenticationId>,
    pub fingerprint_id: Option<String>,
    pub payment_method_billing_address: Option<common_utils::encryption::Encryption>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub customer_acceptance: Option<masking::Secret<common_payments_types::CustomerAcceptance>>,
    pub profile_id: id_type::ProfileId,
    pub organization_id: id_type::OrganizationId,
    pub card_network: Option<String>,
    pub shipping_cost: Option<MinorUnit>,
    pub order_tax_amount: Option<MinorUnit>,
    pub charges: Option<common_types::payments::ConnectorChargeResponseData>,
    pub feature_metadata: Option<PaymentAttemptFeatureMetadata>,
    pub payment_method_type_v2: storage_enums::PaymentMethod,
    pub connector_payment_id: Option<ConnectorTransactionId>,
    pub payment_method_subtype: storage_enums::PaymentMethodType,
    pub id: id_type::GlobalAttemptId,
    pub connector_token_details: Option<ConnectorTokenDetails>,
    pub card_discovery: Option<storage_enums::CardDiscovery>,
    pub request_extended_authorization: Option<RequestExtendedAuthorizationBool>,
    pub extended_authorization_applied: Option<ExtendedAuthorizationAppliedBool>,
    pub capture_before: Option<PrimitiveDateTime>,
    pub connector: Option<String>,
    pub network_decline_code: Option<String>,
    pub network_advice_code: Option<String>,
    pub network_error_message: Option<String>,
    pub processor_merchant_id: Option<id_type::MerchantId>,
    pub created_by: Option<String>,
    pub connector_request_reference_id: Option<String>,
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = payment_attempt)]
pub struct PaymentAttemptNew {
    pub payment_id: id_type::PaymentId,
    pub merchant_id: id_type::MerchantId,
    pub attempt_id: String,
    pub status: storage_enums::AttemptStatus,
    pub amount: MinorUnit,
    pub currency: Option<storage_enums::Currency>,
    // pub auto_capture: Option<bool>,
    pub save_to_locker: Option<bool>,
    pub connector: Option<String>,
    pub error_message: Option<String>,
    pub offer_amount: Option<MinorUnit>,
    pub surcharge_amount: Option<MinorUnit>,
    pub tax_amount: Option<MinorUnit>,
    pub payment_method_id: Option<String>,
    pub payment_method: Option<storage_enums::PaymentMethod>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub capture_on: Option<PrimitiveDateTime>,
    pub confirm: bool,
    pub authentication_type: Option<storage_enums::AuthenticationType>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_synced: Option<PrimitiveDateTime>,
    pub cancellation_reason: Option<String>,
    pub amount_to_capture: Option<MinorUnit>,
    pub mandate_id: Option<String>,
    pub browser_info: Option<serde_json::Value>,
    pub payment_token: Option<String>,
    pub error_code: Option<String>,
    pub connector_metadata: Option<serde_json::Value>,
    pub payment_experience: Option<storage_enums::PaymentExperience>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub payment_method_data: Option<serde_json::Value>,
    pub business_sub_label: Option<String>,
    pub straight_through_algorithm: Option<serde_json::Value>,
    pub preprocessing_step_id: Option<String>,
    pub mandate_details: Option<storage_enums::MandateDataType>,
    pub error_reason: Option<String>,
    pub connector_response_reference_id: Option<String>,
    pub multiple_capture_count: Option<i16>,
    pub amount_capturable: MinorUnit,
    pub updated_by: String,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub authentication_data: Option<serde_json::Value>,
    pub encoded_data: Option<String>,
    pub unified_code: Option<String>,
    pub unified_message: Option<String>,
    pub net_amount: Option<MinorUnit>,
    pub external_three_ds_authentication_attempted: Option<bool>,
    pub authentication_connector: Option<String>,
    pub authentication_id: Option<id_type::AuthenticationId>,
    pub mandate_data: Option<storage_enums::MandateDetails>,
    pub fingerprint_id: Option<String>,
    pub payment_method_billing_address_id: Option<String>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub profile_id: id_type::ProfileId,
    pub organization_id: id_type::OrganizationId,
    pub card_network: Option<String>,
    pub shipping_cost: Option<MinorUnit>,
    pub order_tax_amount: Option<MinorUnit>,
    pub connector_mandate_detail: Option<ConnectorMandateReferenceId>,
    pub request_extended_authorization: Option<RequestExtendedAuthorizationBool>,
    pub extended_authorization_applied: Option<ExtendedAuthorizationAppliedBool>,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub capture_before: Option<PrimitiveDateTime>,
    pub card_discovery: Option<storage_enums::CardDiscovery>,
    pub processor_merchant_id: Option<id_type::MerchantId>,
    pub created_by: Option<String>,
    pub setup_future_usage_applied: Option<storage_enums::FutureUsage>,
    pub routing_approach: Option<storage_enums::RoutingApproach>,
    pub connector_request_reference_id: Option<String>,
    pub network_transaction_id: Option<String>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PaymentAttemptUpdate {
    Update {
        amount: MinorUnit,
        currency: storage_enums::Currency,
        status: storage_enums::AttemptStatus,
        authentication_type: Option<storage_enums::AuthenticationType>,
        payment_method: Option<storage_enums::PaymentMethod>,
        payment_token: Option<String>,
        payment_method_data: Option<serde_json::Value>,
        payment_method_type: Option<storage_enums::PaymentMethodType>,
        payment_experience: Option<storage_enums::PaymentExperience>,
        business_sub_label: Option<String>,
        amount_to_capture: Option<MinorUnit>,
        capture_method: Option<storage_enums::CaptureMethod>,
        surcharge_amount: Option<MinorUnit>,
        tax_amount: Option<MinorUnit>,
        fingerprint_id: Option<String>,
        payment_method_billing_address_id: Option<String>,
        network_transaction_id: Option<String>,
        updated_by: String,
    },
    UpdateTrackers {
        payment_token: Option<String>,
        connector: Option<String>,
        straight_through_algorithm: Option<serde_json::Value>,
        amount_capturable: Option<MinorUnit>,
        surcharge_amount: Option<MinorUnit>,
        tax_amount: Option<MinorUnit>,
        updated_by: String,
        merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
        routing_approach: Option<storage_enums::RoutingApproach>,
    },
    AuthenticationTypeUpdate {
        authentication_type: storage_enums::AuthenticationType,
        updated_by: String,
    },
    ConfirmUpdate {
        amount: MinorUnit,
        currency: storage_enums::Currency,
        status: storage_enums::AttemptStatus,
        authentication_type: Option<storage_enums::AuthenticationType>,
        capture_method: Option<storage_enums::CaptureMethod>,
        payment_method: Option<storage_enums::PaymentMethod>,
        browser_info: Option<serde_json::Value>,
        connector: Option<String>,
        payment_token: Option<String>,
        payment_method_data: Option<serde_json::Value>,
        payment_method_type: Option<storage_enums::PaymentMethodType>,
        payment_experience: Option<storage_enums::PaymentExperience>,
        business_sub_label: Option<String>,
        straight_through_algorithm: Option<serde_json::Value>,
        error_code: Option<Option<String>>,
        error_message: Option<Option<String>>,
        surcharge_amount: Option<MinorUnit>,
        tax_amount: Option<MinorUnit>,
        fingerprint_id: Option<String>,
        updated_by: String,
        merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
        payment_method_id: Option<String>,
        external_three_ds_authentication_attempted: Option<bool>,
        authentication_connector: Option<String>,
        authentication_id: Option<id_type::AuthenticationId>,
        payment_method_billing_address_id: Option<String>,
        client_source: Option<String>,
        client_version: Option<String>,
        customer_acceptance: Option<pii::SecretSerdeValue>,
        shipping_cost: Option<MinorUnit>,
        order_tax_amount: Option<MinorUnit>,
        connector_mandate_detail: Option<ConnectorMandateReferenceId>,
        card_discovery: Option<storage_enums::CardDiscovery>,
        routing_approach: Option<storage_enums::RoutingApproach>,
        connector_request_reference_id: Option<String>,
        network_transaction_id: Option<String>,
    },
    VoidUpdate {
        status: storage_enums::AttemptStatus,
        cancellation_reason: Option<String>,
        updated_by: String,
    },
    PaymentMethodDetailsUpdate {
        payment_method_id: Option<String>,
        updated_by: String,
    },
    ConnectorMandateDetailUpdate {
        connector_mandate_detail: Option<ConnectorMandateReferenceId>,
        updated_by: String,
    },
    BlocklistUpdate {
        status: storage_enums::AttemptStatus,
        error_code: Option<Option<String>>,
        error_message: Option<Option<String>>,
        updated_by: String,
    },
    RejectUpdate {
        status: storage_enums::AttemptStatus,
        error_code: Option<Option<String>>,
        error_message: Option<Option<String>>,
        updated_by: String,
    },
    ResponseUpdate {
        status: storage_enums::AttemptStatus,
        connector: Option<String>,
        connector_transaction_id: Option<String>,
        authentication_type: Option<storage_enums::AuthenticationType>,
        network_transaction_id: Option<String>,
        payment_method_id: Option<String>,
        mandate_id: Option<String>,
        connector_metadata: Option<serde_json::Value>,
        payment_token: Option<String>,
        error_code: Option<Option<String>>,
        error_message: Option<Option<String>>,
        error_reason: Option<Option<String>>,
        connector_response_reference_id: Option<String>,
        amount_capturable: Option<MinorUnit>,
        updated_by: String,
        authentication_data: Option<serde_json::Value>,
        encoded_data: Option<String>,
        unified_code: Option<Option<String>>,
        unified_message: Option<Option<String>>,
        capture_before: Option<PrimitiveDateTime>,
        extended_authorization_applied: Option<ExtendedAuthorizationAppliedBool>,
        payment_method_data: Option<serde_json::Value>,
        connector_mandate_detail: Option<ConnectorMandateReferenceId>,
        charges: Option<common_types::payments::ConnectorChargeResponseData>,
        setup_future_usage_applied: Option<storage_enums::FutureUsage>,
    },
    UnresolvedResponseUpdate {
        status: storage_enums::AttemptStatus,
        connector: Option<String>,
        connector_transaction_id: Option<String>,
        payment_method_id: Option<String>,
        error_code: Option<Option<String>>,
        error_message: Option<Option<String>>,
        error_reason: Option<Option<String>>,
        connector_response_reference_id: Option<String>,
        updated_by: String,
    },
    StatusUpdate {
        status: storage_enums::AttemptStatus,
        updated_by: String,
    },
    ErrorUpdate {
        connector: Option<String>,
        status: storage_enums::AttemptStatus,
        error_code: Option<Option<String>>,
        error_message: Option<Option<String>>,
        error_reason: Option<Option<String>>,
        amount_capturable: Option<MinorUnit>,
        updated_by: String,
        unified_code: Option<Option<String>>,
        unified_message: Option<Option<String>>,
        connector_transaction_id: Option<String>,
        payment_method_data: Option<serde_json::Value>,
        authentication_type: Option<storage_enums::AuthenticationType>,
        issuer_error_code: Option<String>,
        issuer_error_message: Option<String>,
    },
    CaptureUpdate {
        amount_to_capture: Option<MinorUnit>,
        multiple_capture_count: Option<i16>,
        updated_by: String,
    },
    AmountToCaptureUpdate {
        status: storage_enums::AttemptStatus,
        amount_capturable: MinorUnit,
        updated_by: String,
    },
    PreprocessingUpdate {
        status: storage_enums::AttemptStatus,
        payment_method_id: Option<String>,
        connector_metadata: Option<serde_json::Value>,
        preprocessing_step_id: Option<String>,
        connector_transaction_id: Option<String>,
        connector_response_reference_id: Option<String>,
        updated_by: String,
    },
    ConnectorResponse {
        authentication_data: Option<serde_json::Value>,
        encoded_data: Option<String>,
        connector_transaction_id: Option<String>,
        connector: Option<String>,
        charges: Option<common_types::payments::ConnectorChargeResponseData>,
        updated_by: String,
    },
    IncrementalAuthorizationAmountUpdate {
        amount: MinorUnit,
        amount_capturable: MinorUnit,
    },
    AuthenticationUpdate {
        status: storage_enums::AttemptStatus,
        external_three_ds_authentication_attempted: Option<bool>,
        authentication_connector: Option<String>,
        authentication_id: Option<id_type::AuthenticationId>,
        updated_by: String,
    },
    ManualUpdate {
        status: Option<storage_enums::AttemptStatus>,
        error_code: Option<String>,
        error_message: Option<String>,
        error_reason: Option<String>,
        updated_by: String,
        unified_code: Option<String>,
        unified_message: Option<String>,
        connector_transaction_id: Option<String>,
    },
    PostSessionTokensUpdate {
        updated_by: String,
        connector_metadata: Option<serde_json::Value>,
    },
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PaymentAttemptUpdate {
    // Update {
    //     amount: MinorUnit,
    //     currency: storage_enums::Currency,
    //     status: storage_enums::AttemptStatus,
    //     authentication_type: Option<storage_enums::AuthenticationType>,
    //     payment_method: Option<storage_enums::PaymentMethod>,
    //     payment_token: Option<String>,
    //     payment_method_data: Option<serde_json::Value>,
    //     payment_method_type: Option<storage_enums::PaymentMethodType>,
    //     payment_experience: Option<storage_enums::PaymentExperience>,
    //     business_sub_label: Option<String>,
    //     amount_to_capture: Option<MinorUnit>,
    //     capture_method: Option<storage_enums::CaptureMethod>,
    //     surcharge_amount: Option<MinorUnit>,
    //     tax_amount: Option<MinorUnit>,
    //     fingerprint_id: Option<String>,
    //     payment_method_billing_address_id: Option<String>,
    //     updated_by: String,
    // },
    // UpdateTrackers {
    //     payment_token: Option<String>,
    //     connector: Option<String>,
    //     straight_through_algorithm: Option<serde_json::Value>,
    //     amount_capturable: Option<MinorUnit>,
    //     surcharge_amount: Option<MinorUnit>,
    //     tax_amount: Option<MinorUnit>,
    //     updated_by: String,
    //     merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    // },
    // AuthenticationTypeUpdate {
    //     authentication_type: storage_enums::AuthenticationType,
    //     updated_by: String,
    // },
    // ConfirmUpdate {
    //     amount: MinorUnit,
    //     currency: storage_enums::Currency,
    //     status: storage_enums::AttemptStatus,
    //     authentication_type: Option<storage_enums::AuthenticationType>,
    //     capture_method: Option<storage_enums::CaptureMethod>,
    //     payment_method: Option<storage_enums::PaymentMethod>,
    //     browser_info: Option<serde_json::Value>,
    //     connector: Option<String>,
    //     payment_token: Option<String>,
    //     payment_method_data: Option<serde_json::Value>,
    //     payment_method_type: Option<storage_enums::PaymentMethodType>,
    //     payment_experience: Option<storage_enums::PaymentExperience>,
    //     business_sub_label: Option<String>,
    //     straight_through_algorithm: Option<serde_json::Value>,
    //     error_code: Option<Option<String>>,
    //     error_message: Option<Option<String>>,
    //     amount_capturable: Option<MinorUnit>,
    //     surcharge_amount: Option<MinorUnit>,
    //     tax_amount: Option<MinorUnit>,
    //     fingerprint_id: Option<String>,
    //     updated_by: String,
    //     merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    //     payment_method_id: Option<String>,
    //     external_three_ds_authentication_attempted: Option<bool>,
    //     authentication_connector: Option<String>,
    //     authentication_id: Option<String>,
    //     payment_method_billing_address_id: Option<String>,
    //     client_source: Option<String>,
    //     client_version: Option<String>,
    //     customer_acceptance: Option<pii::SecretSerdeValue>,
    // },
    // VoidUpdate {
    //     status: storage_enums::AttemptStatus,
    //     cancellation_reason: Option<String>,
    //     updated_by: String,
    // },
    // PaymentMethodDetailsUpdate {
    //     payment_method_id: Option<String>,
    //     updated_by: String,
    // },
    // ConnectorMandateDetailUpdate {
    //     connector_mandate_detail: Option<ConnectorMandateReferenceId>,
    //     updated_by: String,
    // }
    // BlocklistUpdate {
    //     status: storage_enums::AttemptStatus,
    //     error_code: Option<Option<String>>,
    //     error_message: Option<Option<String>>,
    //     updated_by: String,
    // },
    // RejectUpdate {
    //     status: storage_enums::AttemptStatus,
    //     error_code: Option<Option<String>>,
    //     error_message: Option<Option<String>>,
    //     updated_by: String,
    // },
    ResponseUpdate {
        status: storage_enums::AttemptStatus,
        connector: Option<String>,
        connector_payment_id: Option<String>,
        authentication_type: Option<storage_enums::AuthenticationType>,
        payment_method_id: Option<id_type::GlobalPaymentMethodId>,
        connector_metadata: Option<pii::SecretSerdeValue>,
        payment_token: Option<String>,
        error_code: Option<Option<String>>,
        error_message: Option<Option<String>>,
        error_reason: Option<Option<String>>,
        connector_response_reference_id: Option<String>,
        amount_capturable: Option<MinorUnit>,
        updated_by: String,
        unified_code: Option<Option<String>>,
        unified_message: Option<Option<String>>,
    },
    UnresolvedResponseUpdate {
        status: storage_enums::AttemptStatus,
        connector: Option<String>,
        connector_payment_id: Option<String>,
        payment_method_id: Option<id_type::GlobalPaymentMethodId>,
        error_code: Option<Option<String>>,
        error_message: Option<Option<String>>,
        error_reason: Option<Option<String>>,
        connector_response_reference_id: Option<String>,
        updated_by: String,
    },
    // StatusUpdate {
    //     status: storage_enums::AttemptStatus,
    //     updated_by: String,
    // },
    ErrorUpdate {
        connector: Option<String>,
        status: storage_enums::AttemptStatus,
        error_code: Option<Option<String>>,
        error_message: Option<Option<String>>,
        error_reason: Option<Option<String>>,
        amount_capturable: Option<MinorUnit>,
        updated_by: String,
        unified_code: Option<Option<String>>,
        unified_message: Option<Option<String>>,
        connector_payment_id: Option<String>,
        authentication_type: Option<storage_enums::AuthenticationType>,
    },
    // CaptureUpdate {
    //     amount_to_capture: Option<MinorUnit>,
    //     multiple_capture_count: Option<MinorUnit>,
    //     updated_by: String,
    // },
    // AmountToCaptureUpdate {
    //     status: storage_enums::AttemptStatus,
    //     amount_capturable: MinorUnit,
    //     updated_by: String,
    // },
    PreprocessingUpdate {
        status: storage_enums::AttemptStatus,
        payment_method_id: Option<id_type::GlobalPaymentMethodId>,
        connector_metadata: Option<pii::SecretSerdeValue>,
        preprocessing_step_id: Option<String>,
        connector_payment_id: Option<String>,
        connector_response_reference_id: Option<String>,
        updated_by: String,
    },
    ConnectorResponse {
        connector_payment_id: Option<String>,
        connector: Option<String>,
        updated_by: String,
    },
    // IncrementalAuthorizationAmountUpdate {
    //     amount: MinorUnit,
    //     amount_capturable: MinorUnit,
    // },
    // AuthenticationUpdate {
    //     status: storage_enums::AttemptStatus,
    //     external_three_ds_authentication_attempted: Option<bool>,
    //     authentication_connector: Option<String>,
    //     authentication_id: Option<String>,
    //     updated_by: String,
    // },
    ManualUpdate {
        status: Option<storage_enums::AttemptStatus>,
        error_code: Option<String>,
        error_message: Option<String>,
        error_reason: Option<String>,
        updated_by: String,
        unified_code: Option<String>,
        unified_message: Option<String>,
        connector_payment_id: Option<String>,
    },
}

// TODO: uncomment fields as and when required
#[cfg(feature = "v2")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = payment_attempt)]
pub struct PaymentAttemptUpdateInternal {
    pub status: Option<storage_enums::AttemptStatus>,
    pub authentication_type: Option<storage_enums::AuthenticationType>,
    pub error_message: Option<String>,
    pub connector_payment_id: Option<ConnectorTransactionId>,
    pub connector_payment_data: Option<String>,
    pub payment_method_id: Option<id_type::GlobalPaymentMethodId>,
    // cancellation_reason: Option<String>,
    pub modified_at: PrimitiveDateTime,
    pub browser_info: Option<serde_json::Value>,
    // payment_token: Option<String>,
    pub error_code: Option<String>,
    pub connector_metadata: Option<pii::SecretSerdeValue>,
    // payment_method_data: Option<serde_json::Value>,
    // payment_experience: Option<storage_enums::PaymentExperience>,
    // preprocessing_step_id: Option<String>,


pub struct PSync;


impl Authorization {
    pub fn new(header: Option<&HeaderValue>) -> Self {
        Self {
            header: header.cloned(),
        }
    }

    pub async fn verify_webhook_source(
        self,
        nt_service: &settings::NetworkTokenizationService,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        let secret = nt_service.webhook_source_verification_key.clone();

        let source_verified = match self.header {
            Some(authorization_header) => match authorization_header.to_str() {
                Ok(header_value) => Ok(header_value == secret.expose()),
                Err(err) => {
                    logger::error!(?err, "Failed to parse authorization header");
                    Err(errors::ApiErrorResponse::WebhookAuthenticationFailed)
                }
            },
            None => Ok(false),
        }?;
        logger::info!(source_verified=?source_verified);

        helper_utils::when(!source_verified, || {
            Err(report!(
                errors::ApiErrorResponse::WebhookAuthenticationFailed
            ))
        })?;

        Ok(())
    }
}


impl api::RefundExecute for Flexiti {}


pub struct CybersourceConsumerAuthInformationRequest {
    return_url: String,
    reference_id: String,
}


pub async fn get_filters_for_payments(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Json<common_utils::types::TimeRange>,
) -> impl Responder {
    let flow = Flow::PaymentsList;
    let payload = payload.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::get_filters_for_payments(state, merchant_context, req)
        },
        &auth::JWTAuth {
            permission: Permission::MerchantPaymentRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct NovalnetCancelRequest {
    pub transaction: NovalnetCancelTransaction,
    pub custom: NovalnetCustom,
}


pub async fn refund_update(
    state: web::Data<routes::AppState>,
    req: HttpRequest,
    path: web::Path<String>,
    form_payload: web::Form<types::StripeUpdateRefundRequest>,
) -> HttpResponse {
    let mut payload = form_payload.into_inner();
    payload.refund_id = path.into_inner();
    let create_refund_update_req: refund_types::RefundUpdateRequest = payload.into();
    let flow = Flow::RefundsUpdate;

    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripeRefundResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        create_refund_update_req,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            refunds::refund_update_core(state, merchant_context, req)
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn get_payment_filters_profile(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
) -> impl Responder {
    let flow = Flow::PaymentsFilters;
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        (),
        |state, auth: auth::AuthenticationData, _, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::get_payment_filters(
                state,
                merchant_context,
                Some(vec![auth.profile.get_id().clone()]),
            )
        },
        &auth::JWTAuth {
            permission: Permission::ProfilePaymentRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl api::PaymentCapture for Boku {}


pub struct LabelWithScoreEventResponse {
    pub score: f64,
    pub label: String,
}


pub struct PaymentsSessionData {
    pub amount: i64,
    pub currency: common_enums::Currency,
    pub country: Option<common_enums::CountryAlpha2>,
    pub surcharge_details: Option<SurchargeDetails>,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub email: Option<pii::Email>,
    // Minor Unit amount for amount frame work
    pub minor_amount: MinorUnit,
    pub apple_pay_recurring_details: Option<api_models::payments::ApplePayRecurringPaymentRequest>,
    pub customer_name: Option<Secret<String>>,
    pub order_tax_amount: Option<MinorUnit>,
    pub shipping_cost: Option<MinorUnit>,
    pub metadata: Option<Secret<serde_json::Value>>,
}


pub struct PaymentMethodContainer {
    #[serde(rename = "payment-method")]
    pub payment_method: Vec<PaymentMethod>,
}


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


pub struct NewUserOrganization(diesel_org::OrganizationNew);


pub fn get_entity(&self) -> Option<&Entity> {
        self.entity.as_ref()
    }


pub struct WebhookEventDataResourceEvent {
    pub notify_type: NotifyType,
}


impl Clone for PIISerializer {
        fn clone(&self) -> Self {
            Self {
                inner: JsonValueSerializer,
            }
        }
    }


File: crates/test_utils/src/main.rs

#![allow(clippy::print_stdout, clippy::print_stderr)]

use std::process::{exit, Command};

use anyhow::Result;
use test_utils::newman_runner;

fn main() -> Result<()> {
    let mut runner = newman_runner::generate_runner()?;

    // Execute the newman command
    let output = runner.newman_command.spawn();
    let mut child = match output {
        Ok(child) => child,
        Err(err) => {
            eprintln!("Failed to execute command: {err}");
            exit(1);
        }
    };
    let status = child.wait();

    // Filter out None values leaving behind Some(Path)
    let paths: Vec<String> = runner.modified_file_paths.into_iter().flatten().collect();

    if !paths.is_empty() {
        let git_status = Command::new("git").arg("restore").args(&paths).output();

        match git_status {
            Ok(output) if !output.status.success() => {
                let stderr_str = String::from_utf8_lossy(&output.stderr);
                eprintln!("Git command failed with error: {stderr_str}");
            }
            Ok(_) => {
                println!("Git restore successful!");
            }
            Err(e) => {
                eprintln!("Error running Git: {e}");
            }
        }
    }

    let exit_code = match status {
        Ok(exit_status) => {
            if exit_status.success() {
                println!("Command executed successfully!");
                exit_status.code().unwrap_or(0)
            } else {
                eprintln!("Command failed with exit code: {:?}", exit_status.code());
                exit_status.code().unwrap_or(1)
            }
        }
        Err(err) => {
            eprintln!("Failed to wait for command execution: {err}");
            exit(1);
        }
    };

    exit(exit_code);
}


pub trait FrmFilterAnalytics: LoadRow<FrmFilterRow> {}


impl SurchargeMetadata {
    pub fn new(payment_attempt_id: String) -> Self {
        Self {
            surcharge_results: HashMap::new(),
            payment_attempt_id,
        }
    }
    pub fn is_empty_result(&self) -> bool {
        self.surcharge_results.is_empty()
    }
    pub fn get_surcharge_results_size(&self) -> usize {
        self.surcharge_results.len()
    }
    pub fn insert_surcharge_details(
        &mut self,
        surcharge_key: SurchargeKey,
        surcharge_details: SurchargeDetails,
    ) {
        self.surcharge_results
            .insert(surcharge_key, surcharge_details);
    }
    pub fn get_surcharge_details(&self, surcharge_key: SurchargeKey) -> Option<&SurchargeDetails> {
        self.surcharge_results.get(&surcharge_key)
    }
    pub fn get_surcharge_metadata_redis_key(payment_attempt_id: &str) -> String {
        format!("surcharge_metadata_{payment_attempt_id}")
    }
    pub fn get_individual_surcharge_key_value_pairs(&self) -> Vec<(String, SurchargeDetails)> {
        self.surcharge_results
            .iter()
            .map(|(surcharge_key, surcharge_details)| {
                let key = Self::get_surcharge_details_redis_hashset_key(surcharge_key);
                (key, surcharge_details.to_owned())
            })
            .collect()
    }
    pub fn get_surcharge_details_redis_hashset_key(surcharge_key: &SurchargeKey) -> String {
        match surcharge_key {
            SurchargeKey::Token(token) => {
                format!("token_{token}")
            }
            SurchargeKey::PaymentMethodData(payment_method, payment_method_type, card_network) => {
                if let Some(card_network) = card_network {
                    format!("{payment_method}_{payment_method_type}_{card_network}")
                } else {
                    format!("{payment_method}_{payment_method_type}")
                }
            }
        }
    }
    #[instrument(skip_all)]
    pub async fn persist_individual_surcharge_details_in_redis(
        &self,
        state: &SessionState,
        business_profile: &Profile,
    ) -> RouterResult<()> {
        if !self.is_empty_result() {
            let redis_conn = state
                .store
                .get_redis_conn()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to get redis connection")?;
            let redis_key = Self::get_surcharge_metadata_redis_key(&self.payment_attempt_id);

            let mut value_list = Vec::with_capacity(self.get_surcharge_results_size());
            for (key, value) in self.get_individual_surcharge_key_value_pairs().into_iter() {
                value_list.push((
                    key,
                    value
                        .encode_to_string_of_json()
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed to encode to string of json")?,
                ));
            }
            let intent_fulfillment_time = business_profile
                .get_order_fulfillment_time()
                .unwrap_or(router_consts::DEFAULT_FULFILLMENT_TIME);
            redis_conn
                .set_hash_fields(
                    &redis_key.as_str().into(),
                    value_list,
                    Some(intent_fulfillment_time),
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to write to redis")?;
            logger::debug!("Surcharge results stored in redis with key = {}", redis_key);
        }
        Ok(())
    }

    #[instrument(skip_all)]
    pub async fn get_individual_surcharge_detail_from_redis(
        state: &SessionState,
        surcharge_key: SurchargeKey,
        payment_attempt_id: &str,
    ) -> CustomResult<SurchargeDetails, RedisError> {
        let redis_conn = state
            .store
            .get_redis_conn()
            .attach_printable("Failed to get redis connection")?;
        let redis_key = Self::get_surcharge_metadata_redis_key(payment_attempt_id);
        let value_key = Self::get_surcharge_details_redis_hashset_key(&surcharge_key);
        let result = redis_conn
            .get_hash_field_and_deserialize(
                &redis_key.as_str().into(),
                &value_key,
                "SurchargeDetails",
            )
            .await;
        logger::debug!(
            "Surcharge result fetched from redis with key = {} and {}",
            redis_key,
            value_key
        );
        result
    }
}


impl api::PaymentVoid for Nuvei {}


impl api::MandateSetup for Celero {}


pub struct TrustpayCreateIntentRequest {
    pub amount: StringMajorUnit,
    pub currency: String,
    // If true, Apple Pay will be initialized
    pub init_apple_pay: Option<bool>,
    // If true, Google pay will be initialized
    pub init_google_pay: Option<bool>,
    pub reference: String,
}