impl AuthenticationType {
    pub fn get_merchant_id(&self) -> Option<&id_type::MerchantId> {
        match self {
            Self::ApiKey {
                merchant_id,
                key_id: _,
            }
            | Self::AdminApiAuthWithMerchantId { merchant_id }
            | Self::MerchantId { merchant_id }
            | Self::PublishableKey { merchant_id }
            | Self::MerchantJwt {
                merchant_id,
                user_id: _,
            }
            | Self::MerchantJwtWithProfileId { merchant_id, .. }
            | Self::WebhookAuth { merchant_id } => Some(merchant_id),
            Self::AdminApiKey
            | Self::OrganizationJwt { .. }
            | Self::UserJwt { .. }
            | Self::SinglePurposeJwt { .. }
            | Self::SinglePurposeOrLoginJwt { .. }
            | Self::NoAuth => None,
        }
    }
}


File: crates/diesel_models/src/query/customers.rs
 Public functions: 10
 Public structs: 1

use common_utils::id_type;
use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};

use super::generics;
#[cfg(feature = "v1")]
use crate::schema::customers::dsl;
#[cfg(feature = "v2")]
use crate::schema_v2::customers::dsl;
use crate::{
    customers::{Customer, CustomerNew, CustomerUpdateInternal},
    errors, PgPooledConn, StorageResult,
};

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

pub struct CustomerListConstraints {
    pub limit: i64,
    pub offset: Option<i64>,
}

impl Customer {
    #[cfg(feature = "v2")]
    pub async fn update_by_id(
        conn: &PgPooledConn,
        id: id_type::GlobalCustomerId,
        customer: CustomerUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
            conn,
            id.clone(),
            customer,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => {
                    generics::generic_find_by_id::<<Self as HasTable>::Table, _, _>(conn, id).await
                }
                _ => Err(error),
            },
            result => result,
        }
    }

    #[cfg(feature = "v2")]
    pub async fn find_by_global_id(
        conn: &PgPooledConn,
        id: &id_type::GlobalCustomerId,
    ) -> StorageResult<Self> {
        generics::generic_find_by_id::<<Self as HasTable>::Table, _, _>(conn, id.to_owned()).await
    }

    pub async fn list_by_merchant_id(
        conn: &PgPooledConn,
        merchant_id: &id_type::MerchantId,
        constraints: CustomerListConstraints,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id.eq(merchant_id.to_owned()),
            Some(constraints.limit),
            constraints.offset,
            Some(dsl::created_at),
        )
        .await
    }

    #[cfg(feature = "v2")]
    pub async fn find_optional_by_merchant_id_merchant_reference_id(
        conn: &PgPooledConn,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::merchant_reference_id.eq(customer_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_optional_by_customer_id_merchant_id(
        conn: &PgPooledConn,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_by_id_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            (customer_id.to_owned(), merchant_id.to_owned()),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn update_by_customer_id_merchant_id(
        conn: &PgPooledConn,
        customer_id: id_type::CustomerId,
        merchant_id: id_type::MerchantId,
        customer: CustomerUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
            conn,
            (customer_id.clone(), merchant_id.clone()),
            customer,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => {
                    generics::generic_find_by_id::<<Self as HasTable>::Table, _, _>(
                        conn,
                        (customer_id, merchant_id),
                    )
                    .await
                }
                _ => Err(error),
            },
            result => result,
        }
    }

    #[cfg(feature = "v1")]
    pub async fn delete_by_customer_id_merchant_id(
        conn: &PgPooledConn,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
    ) -> StorageResult<bool> {
        generics::generic_delete::<<Self as HasTable>::Table, _>(
            conn,
            dsl::customer_id
                .eq(customer_id.to_owned())
                .and(dsl::merchant_id.eq(merchant_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v2")]
    pub async fn find_by_merchant_reference_id_merchant_id(
        conn: &PgPooledConn,
        merchant_reference_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::merchant_reference_id.eq(merchant_reference_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_customer_id_merchant_id(
        conn: &PgPooledConn,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
    ) -> StorageResult<Self> {
        generics::generic_find_by_id::<<Self as HasTable>::Table, _, _>(
            conn,
            (customer_id.to_owned(), merchant_id.to_owned()),
        )
        .await
    }
}


pub fn set_order_amount(&mut self, order_amount: MinorUnit) {
        self.order_amount = order_amount;
    }


pub struct UpdateNexixpayConnectorMetaData {
    pub three_d_s_auth_result: Option<ThreeDSAuthResult>,
    pub three_d_s_auth_response: Option<Secret<String>>,
    pub authorization_operation_id: Option<String>,
    pub capture_operation_id: Option<String>,
    pub cancel_operation_id: Option<String>,
    pub psync_flow: Option<NexixpayPaymentIntent>,
    pub meta_data: serde_json::Value,
    pub is_auto_capture: bool,
}


pub struct GlobalpayRefreshTokenRequest {
    pub app_id: Secret<String>,
    pub nonce: Secret<String>,
    pub secret: Secret<String>,
    pub grant_type: String,
}


pub struct WorkflowRunner;


impl<F> PaymentAttemptRecordData<F>
where
    F: Clone,
{
    pub fn get_updated_feature_metadata(
        &self,
    ) -> CustomResult<Option<FeatureMetadata>, errors::api_error_response::ApiErrorResponse> {
        let payment_intent_feature_metadata = self.payment_intent.get_feature_metadata();
        let revenue_recovery = self.payment_intent.get_revenue_recovery_metadata();
        let payment_attempt_connector = self.payment_attempt.connector.clone();

        let feature_metadata_first_pg_error_code = revenue_recovery
            .as_ref()
            .and_then(|data| data.first_payment_attempt_pg_error_code.clone());

        let (first_pg_error_code, first_network_advice_code, first_network_decline_code) =
            feature_metadata_first_pg_error_code.map_or_else(
                || {
                    let first_pg_error_code = self
                        .payment_attempt
                        .error
                        .as_ref()
                        .map(|error| error.code.clone());
                    let first_network_advice_code = self
                        .payment_attempt
                        .error
                        .as_ref()
                        .and_then(|error| error.network_advice_code.clone());
                    let first_network_decline_code = self
                        .payment_attempt
                        .error
                        .as_ref()
                        .and_then(|error| error.network_decline_code.clone());
                    (
                        first_pg_error_code,
                        first_network_advice_code,
                        first_network_decline_code,
                    )
                },
                |pg_code| {
                    let advice_code = revenue_recovery
                        .as_ref()
                        .and_then(|data| data.first_payment_attempt_network_advice_code.clone());
                    let decline_code = revenue_recovery
                        .as_ref()
                        .and_then(|data| data.first_payment_attempt_network_decline_code.clone());
                    (Some(pg_code), advice_code, decline_code)
                },
            );

        let billing_connector_payment_method_details = Some(
            diesel_models::types::BillingConnectorPaymentMethodDetails::Card(
                diesel_models::types::BillingConnectorAdditionalCardInfo {
                    card_network: self.revenue_recovery_data.card_network.clone(),
                    card_issuer: self.revenue_recovery_data.card_issuer.clone(),
                },
            ),
        );

        let payment_revenue_recovery_metadata = match payment_attempt_connector {
            Some(connector) => Some(diesel_models::types::PaymentRevenueRecoveryMetadata {
                // Update retry count by one.
                total_retry_count: revenue_recovery.as_ref().map_or(
                    self.revenue_recovery_data
                        .retry_count
                        .map_or_else(|| 1, |retry_count| retry_count),
                    |data| (data.total_retry_count + 1),
                ),
                // Since this is an external system call, marking this payment_connector_transmission to ConnectorCallSucceeded.
                payment_connector_transmission:
                    common_enums::PaymentConnectorTransmission::ConnectorCallUnsuccessful,
                billing_connector_id: self.revenue_recovery_data.billing_connector_id.clone(),
                active_attempt_payment_connector_id: self
                    .payment_attempt
                    .get_attempt_merchant_connector_account_id()?,
                billing_connector_payment_details:
                    diesel_models::types::BillingConnectorPaymentDetails {
                        payment_processor_token: self
                            .revenue_recovery_data
                            .processor_payment_method_token
                            .clone(),
                        connector_customer_id: self
                            .revenue_recovery_data
                            .connector_customer_id
                            .clone(),
                    },
                payment_method_type: self.payment_attempt.payment_method_type,
                payment_method_subtype: self.payment_attempt.payment_method_subtype,
                connector: connector.parse().map_err(|err| {
                    router_env::logger::error!(?err, "Failed to parse connector string to enum");
                    errors::api_error_response::ApiErrorResponse::InternalServerError
                })?,
                invoice_next_billing_time: self.revenue_recovery_data.invoice_next_billing_time,
                invoice_billing_started_at_time: self
                    .revenue_recovery_data
                    .invoice_next_billing_time,
                billing_connector_payment_method_details,
                first_payment_attempt_network_advice_code: first_network_advice_code,
                first_payment_attempt_network_decline_code: first_network_decline_code,
                first_payment_attempt_pg_error_code: first_pg_error_code,
            }),
            None => Err(errors::api_error_response::ApiErrorResponse::InternalServerError)
                .attach_printable("Connector not found in payment attempt")?,
        };
        Ok(Some(FeatureMetadata {
            redirect_response: payment_intent_feature_metadata
                .as_ref()
                .and_then(|data| data.redirect_response.clone()),
            search_tags: payment_intent_feature_metadata
                .as_ref()
                .and_then(|data| data.search_tags.clone()),
            apple_pay_recurring_details: payment_intent_feature_metadata
                .as_ref()
                .and_then(|data| data.apple_pay_recurring_details.clone()),
            payment_revenue_recovery_metadata,
        }))
    }
}


impl ConnectorSpecifications for Ebanx {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&EBANX_CONNECTOR_INFO)
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        None
    }

    fn get_supported_webhook_flows(&self) -> Option<&'static [common_enums::enums::EventClass]> {
        None
    }
}

impl ConnectorSpecifications for for Ebanx


pub struct ExternalVaultDeleteFlow;


impl api::RefundSync for Shift4 {}


pub struct DetailsType {
    pub loc: Vec<String>,
    #[serde(rename = "type")]
    pub error_type: String,
}


pub fn number_comparison_array_value(input: &str) -> ParseResult<&str, ast::ValueType> {
    let many_with_comma = multi::many0(sequence::preceded(
        skip_ws(complete::tag(",")),
        skip_ws(number_comparison),
    ));

    let full_sequence = sequence::pair(skip_ws(number_comparison), many_with_comma);

    error::context(
        "number_comparison_array_value",
        combinator::map(
            sequence::delimited(
                skip_ws(complete::tag("(")),
                full_sequence,
                skip_ws(complete::tag(")")),
            ),
            |tup: (ast::NumberComparison, Vec<ast::NumberComparison>)| {
                let mut rest = tup.1;
                rest.insert(0, tup.0);
                ast::ValueType::NumberComparisonArray(rest)
            },
        ),
    )(input)
}


pub struct PaymeErrorResponse {
    pub status_code: u16,
    pub status_error_details: String,
    pub status_additional_info: serde_json::Value,
    pub status_error_code: u32,
}


pub struct DummyConnectorRefundRequest {
    pub amount: i64,
    pub payment_id: Option<common_utils::id_type::PaymentId>,
}


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


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

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

    fn common_get_content_type(&self) -> &'static str {
        mime::APPLICATION_JSON.essence_str()
    }

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

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

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

        event_builder.map(|i| i.set_error_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response
                .error
                .clone()
                .map_or(NO_ERROR_CODE.to_string(), |error| error.code),
            message: response.error.map_or_else(
                || response.message.unwrap_or(NO_ERROR_MESSAGE.to_string()),
                |error| error.message,
            ),
            reason: None,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


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


pub struct GlobalPayPaymentMethodsRequest {
    pub reference: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub usage_mode: Option<UsageMode>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub card: Option<Card>,
}


Documentation: api-reference/v1/payouts/payouts--fulfill.mdx
# Type: Doc File

---
openapi: post /payouts/{payout_id}/fulfill
---


pub struct ValuedComparison {
    pub values: Vec<EuclidValue>,
    pub logic: ValuedComparisonLogic,
    pub metadata: Metadata,
}


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


pub struct TaxjarPaymentsResponse {
    tax: Tax,
}


impl api::ConnectorCustomer for Facilitapay {}

impl api::ConnectorCustomer for for Facilitapay


pub struct BamboraapacErrorResponse {
    pub declined_code: Option<String>,
    pub declined_message: Option<String>,
}


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

    fn common_get_content_type(&self) -> &'static str {
        "application/json;charset=utf-8"
    }

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

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

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: Result<
            wellsfargo::WellsfargoErrorResponse,
            Report<common_utils::errors::ParsingError>,
        > = res.response.parse_struct("Wellsfargo ErrorResponse");

        let error_message = if res.status_code == 401 {
            constants::CONNECTOR_UNAUTHORIZED_ERROR
        } else {
            hyperswitch_interfaces::consts::NO_ERROR_MESSAGE
        };
        match response {
            Ok(transformers::WellsfargoErrorResponse::StandardError(response)) => {
                event_builder.map(|i| i.set_error_response_body(&response));
                router_env::logger::info!(connector_response=?response);

                let (code, message, reason) = match response.error_information {
                    Some(ref error_info) => {
                        let detailed_error_info = error_info.details.as_ref().map(|details| {
                            details
                                .iter()
                                .map(|det| format!("{} : {}", det.field, det.reason))
                                .collect::<Vec<_>>()
                                .join(", ")
                        });
                        (
                            error_info.reason.clone(),
                            error_info.reason.clone(),
                            transformers::get_error_reason(
                                Some(error_info.message.clone()),
                                detailed_error_info,
                                None,
                            ),
                        )
                    }
                    None => {
                        let detailed_error_info = response.details.map(|details| {
                            details
                                .iter()
                                .map(|det| format!("{} : {}", det.field, det.reason))
                                .collect::<Vec<_>>()
                                .join(", ")
                        });
                        (
                            response.reason.clone().map_or(
                                hyperswitch_interfaces::consts::NO_ERROR_CODE.to_string(),
                                |reason| reason.to_string(),
                            ),
                            response
                                .reason
                                .map_or(error_message.to_string(), |reason| reason.to_string()),
                            transformers::get_error_reason(
                                response.message,
                                detailed_error_info,
                                None,
                            ),
                        )
                    }
                };

                Ok(ErrorResponse {
                    status_code: res.status_code,
                    code,
                    message,
                    reason,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                })
            }
            Ok(transformers::WellsfargoErrorResponse::AuthenticationError(response)) => {
                event_builder.map(|i| i.set_error_response_body(&response));
                router_env::logger::info!(connector_response=?response);
                Ok(ErrorResponse {
                    status_code: res.status_code,
                    code: hyperswitch_interfaces::consts::NO_ERROR_CODE.to_string(),
                    message: response.response.rmsg.clone(),
                    reason: Some(response.response.rmsg),
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                })
            }
            Ok(transformers::WellsfargoErrorResponse::NotAvailableError(response)) => {
                event_builder.map(|i| i.set_error_response_body(&response));
                router_env::logger::info!(connector_response=?response);
                let error_response = response
                    .errors
                    .iter()
                    .map(|error_info| {
                        format!(
                            "{}: {}",
                            error_info.error_type.clone().unwrap_or("".to_string()),
                            error_info.message.clone().unwrap_or("".to_string())
                        )
                    })
                    .collect::<Vec<String>>()
                    .join(" & ");
                Ok(ErrorResponse {
                    status_code: res.status_code,
                    code: hyperswitch_interfaces::consts::NO_ERROR_CODE.to_string(),
                    message: error_response.clone(),
                    reason: Some(error_response),
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                })
            }
            Err(error_msg) => {
                event_builder.map(|event| event.set_error(serde_json::json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
                router_env::logger::error!(deserialization_error =? error_msg);
                utils::handle_json_response_deserialization_failure(res, "wellsfargo")
            }
        }
    }
}


pub async fn refresh_pm_filters_cache(
    state: &SessionState,
    key: &str,
    graph: cgraph::ConstraintGraph<dir::DirValue>,
) -> Arc<hyperswitch_constraint_graph::ConstraintGraph<dir::DirValue>> {
    let pm_filter_graph = Arc::new(graph);
    PM_FILTERS_CGRAPH_CACHE
        .push(
            CacheKey {
                key: key.to_string(),
                prefix: state.tenant.redis_key_prefix.clone(),
            },
            pm_filter_graph.clone(),
        )
        .await;
    pm_filter_graph
}


pub fn generate_signature(
        &self,
        auth: cybersource::CybersourceAuthType,
        host: String,
        resource: &str,
        payload: &String,
        date: OffsetDateTime,
        http_method: Method,
    ) -> CustomResult<String, errors::ConnectorError> {
        let cybersource::CybersourceAuthType {
            api_key,
            merchant_account,
            api_secret,
        } = auth;
        let is_post_method = matches!(http_method, Method::Post);
        let is_patch_method = matches!(http_method, Method::Patch);
        let is_delete_method = matches!(http_method, Method::Delete);
        let digest_str = if is_post_method || is_patch_method {
            "digest "
        } else {
            ""
        };
        let headers = format!("host date (request-target) {digest_str}v-c-merchant-id");
        let request_target = if is_post_method {
            format!("(request-target): post {resource}\ndigest: SHA-256={payload}\n")
        } else if is_patch_method {
            format!("(request-target): patch {resource}\ndigest: SHA-256={payload}\n")
        } else if is_delete_method {
            format!("(request-target): delete {resource}\n")
        } else {
            format!("(request-target): get {resource}\n")
        };
        let signature_string = format!(
            "host: {host}\ndate: {date}\n{request_target}v-c-merchant-id: {}",
            merchant_account.peek()
        );
        let key_value = consts::BASE64_ENGINE
            .decode(api_secret.expose())
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "connector_account_details.api_secret",
            })?;
        let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value);
        let signature_value =
            consts::BASE64_ENGINE.encode(hmac::sign(&key, signature_string.as_bytes()).as_ref());
        let signature_header = format!(
            r#"keyid="{}", algorithm="HmacSHA256", headers="{headers}", signature="{signature_value}""#,
            api_key.peek()
        );

        Ok(signature_header)
    }


impl api::PaymentCapture for Nmi {}


pub async fn update_by_customer_id_merchant_id(
        conn: &PgPooledConn,
        customer_id: id_type::CustomerId,
        merchant_id: id_type::MerchantId,
        customer: CustomerUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
            conn,
            (customer_id.clone(), merchant_id.clone()),
            customer,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => {
                    generics::generic_find_by_id::<<Self as HasTable>::Table, _, _>(
                        conn,
                        (customer_id, merchant_id),
                    )
                    .await
                }
                _ => Err(error),
            },
            result => result,
        }
    }


/// Getter for the `module_name` field
    pub fn get_module_name(&self) -> Option<Module> {
        self.module_name
    }


pub async fn payouts_list_profile() {}


impl<T, I> Serialize for Secret<T, I>
where
    T: SerializableSecret + Serialize + Sized,
    I: Strategy<T>,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        pii_serializer::pii_serialize(self, serializer)
    }
}


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


pub struct IndomaretVoucherData {}


impl Configs {
    pub fn server(config: AppState) -> Scope {
        web::scope("/configs")
            .app_data(web::Data::new(config))
            .service(web::resource("/").route(web::post().to(config_key_create)))
            .service(
                web::resource("/{key}")
                    .route(web::get().to(config_key_retrieve))
                    .route(web::post().to(config_key_update))
                    .route(web::delete().to(config_key_delete)),
            )
    }
}


pub struct ConnectorAgnosticMitChoice {
    pub enabled: bool,
}


pub struct PreProcessing;


pub async fn start_web_server(
    state: routes::AppState,
    service: String,
) -> errors::ApplicationResult<Server> {
    let server = state
        .conf
        .scheduler
        .as_ref()
        .ok_or(ApplicationError::InvalidConfigurationValueError(
            "Scheduler server is invalidly configured".into(),
        ))?
        .server
        .clone();

    let web_server = actix_web::HttpServer::new(move || {
        actix_web::App::new().service(Health::server(state.clone(), service.clone()))
    })
    .bind((server.host.as_str(), server.port))
    .change_context(ApplicationError::ConfigurationError)?
    .workers(server.workers)
    .run();
    let _ = web_server.handle();

    Ok(web_server)
}


pub struct RecurlyInvoiceTransactionsStatus {
    pub status: String,
}


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::apply_filters_on_payments(
                state,
                merchant_context,
                auth.profile_id.map(|profile_id| vec![profile_id]),
                req,
            )
        },
        &auth::JWTAuth {
            permission: Permission::ProfilePaymentRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct PaymentIntents;


pub struct FiuuWebhookStatus {
    pub capture_method: Option<CaptureMethod>,
    pub status: FiuuPaymentWebhookStatus,
}


        for key in DirKeyKind::iter() {
            if matches!(key, DirKeyKind::Connector) {
                continue;
            }

            let json_str = if let DirKeyKind::MetaData = key {
                r#""metadata""#.to_string()
            } else {
                serde_json::to_string(&key).expect("JSON Serialization")
            };
            let display_str = key.to_string();

            assert_eq!(
                json_str.get(1..json_str.len() - 1).expect("Value metadata"),
                display_str
            );
            key_names.insert(key, display_str);
        }

        let values = vec![
            dirval!(PaymentMethod = Card),
            dirval!(CardBin s= "123456"),
            dirval!(CardType = Credit),
            dirval!(CardNetwork = Visa),
            dirval!(PayLaterType = Klarna),
            dirval!(WalletType = Paypal),
            dirval!(BankRedirectType = Sofort),
            dirval!(BankDebitType = Bacs),
            dirval!(CryptoType = CryptoCurrency),
            dirval!("" = "metadata"),
            dirval!(PaymentAmount = 100),
            dirval!(PaymentCurrency = USD),
            dirval!(CardRedirectType = Benefit),
            dirval!(AuthenticationType = ThreeDs),
            dirval!(CaptureMethod = Manual),
            dirval!(BillingCountry = UnitedStatesOfAmerica),
            dirval!(BusinessCountry = France),
        ];

        for val in values {
            let json_val = serde_json::to_value(&val).expect("JSON Value Serialization");

            let json_key = json_val
                .as_object()
                .expect("Serialized Object")
                .get("key")
                .expect("Object Key");

            let value_str = json_key.as_str().expect("Value string");
            let dir_key = val.get_key();

            let key_name = key_names.get(&dir_key.kind).expect("Key name");

            assert_eq!(key_name, value_str);
        }
    }

    #[cfg(feature = "ast_parser")]
    #[test]
    fn test_allowed_dir_keys() {
        use crate::types::DummyOutput;

        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           payment_method = card
        }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");

        let out = ast::lowering::lower_program::<DummyOutput>(program);
        assert!(out.is_ok())
    }
    #[cfg(feature = "ast_parser")]
    #[test]
    fn test_not_allowed_dir_keys() {
        use crate::types::DummyOutput;

        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           bank_debit = ach
        }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");

        let out = ast::lowering::lower_program::<DummyOutput>(program);
        assert!(out.is_err())
    }
}


        backend_input,
        eligible_connectors,
        profile_id,
        &api_enums::TransactionType::from(transaction_data),
    )
    .await
}

pub async fn perform_fallback_routing(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    transaction_data: &routing::TransactionData<'_>,
    eligible_connectors: Option<&Vec<api_enums::RoutableConnectors>>,
    business_profile: &domain::Profile,
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>> {
    #[cfg(feature = "v1")]
    let fallback_config = routing::helpers::get_merchant_default_config(
        &*state.store,
        match transaction_data {
            routing::TransactionData::Payment(payment_data) => payment_data
                .payment_intent
                .profile_id
                .as_ref()
                .get_required_value("profile_id")
                .change_context(errors::RoutingError::ProfileIdMissing)?
                .get_string_repr(),
            #[cfg(feature = "payouts")]
            routing::TransactionData::Payout(payout_data) => {
                payout_data.payout_attempt.profile_id.get_string_repr()
            }
        },
        &api_enums::TransactionType::from(transaction_data),
    )
    .await
    .change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
    #[cfg(feature = "v2")]
    let fallback_config = admin::ProfileWrapper::new(business_profile.clone())
        .get_default_fallback_list_of_connector_under_profile()
        .change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
    let backend_input = match transaction_data {
        routing::TransactionData::Payment(payment_data) => make_dsl_input(payment_data)?,
        #[cfg(feature = "payouts")]
        routing::TransactionData::Payout(payout_data) => make_dsl_input_for_payouts(payout_data)?,
    };

    perform_cgraph_filtering(
        state,
        key_store,
        fallback_config,
        backend_input,
        eligible_connectors,
        business_profile.get_id(),
        &api_enums::TransactionType::from(transaction_data),
    )
    .await
}

pub async fn perform_eligibility_analysis_with_fallback(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    chosen: Vec<routing_types::RoutableConnectorChoice>,
    transaction_data: &routing::TransactionData<'_>,
    eligible_connectors: Option<Vec<api_enums::RoutableConnectors>>,
    business_profile: &domain::Profile,
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>> {
    logger::debug!("euclid_routing: performing eligibility");
    let mut final_selection = perform_eligibility_analysis(
        state,
        key_store,
        chosen,
        transaction_data,
        eligible_connectors.as_ref(),
        business_profile.get_id(),
    )
    .await?;

    let fallback_selection = perform_fallback_routing(
        state,
        key_store,
        transaction_data,
        eligible_connectors.as_ref(),
        business_profile,
    )
    .await;

    final_selection.append(
        &mut fallback_selection
            .unwrap_or_default()
            .iter()
            .filter(|&routable_connector_choice| {
                !final_selection.contains(routable_connector_choice)
            })
            .cloned()
            .collect::<Vec<_>>(),
    );

    let final_selected_connectors = final_selection
        .iter()
        .map(|item| item.connector)
        .collect::<Vec<_>>();
    logger::debug!(final_selected_connectors_for_routing=?final_selected_connectors, "euclid_routing: List of final selected connectors for routing");

    Ok(final_selection)
}

#[cfg(feature = "v2")]
pub async fn perform_session_flow_routing<'a>(
    state: &'a SessionState,
    key_store: &'a domain::MerchantKeyStore,
    session_input: SessionFlowRoutingInput<'_>,
    business_profile: &domain::Profile,
    transaction_type: &api_enums::TransactionType,
) -> RoutingResult<FxHashMap<api_enums::PaymentMethodType, Vec<routing_types::SessionRoutingChoice>>>
{
    let mut pm_type_map: FxHashMap<api_enums::PaymentMethodType, FxHashMap<String, api::GetToken>> =
        FxHashMap::default();

    let profile_id = business_profile.get_id().clone();

    let routing_algorithm =
        MerchantAccountRoutingAlgorithm::V1(business_profile.routing_algorithm_id.clone());

    let payment_method_input = dsl_inputs::PaymentMethodInput {
        payment_method: None,
        payment_method_type: None,
        card_network: None,
    };

    let payment_input = dsl_inputs::PaymentInput {
        amount: session_input
            .payment_intent
            .amount_details
            .calculate_net_amount(),
        currency: session_input.payment_intent.amount_details.currency,
        authentication_type: session_input.payment_intent.authentication_type,
        card_bin: None,
        capture_method: Option::<euclid_enums::CaptureMethod>::foreign_from(
            session_input.payment_intent.capture_method,
        ),
        // business_country not available in payment_intent anymore
        business_country: None,
        billing_country: session_input
            .country
            .map(storage_enums::Country::from_alpha2),
        // business_label not available in payment_intent anymore
        business_label: None,
        setup_future_usage: Some(session_input.payment_intent.setup_future_usage),
    };

    let metadata = session_input
        .payment_intent
        .parse_and_get_metadata("routing_parameters")
        .change_context(errors::RoutingError::MetadataParsingError)
        .attach_printable("Unable to parse routing_parameters from metadata of payment_intent")
        .unwrap_or(None);

    let mut backend_input = dsl_inputs::BackendInput {
        metadata,
        payment: payment_input,
        payment_method: payment_method_input,
        mandate: dsl_inputs::MandateData {
            mandate_acceptance_type: None,
            mandate_type: None,
            payment_type: None,
        },
        acquirer_data: None,
        customer_device_data: None,
        issuer_data: None,
    };

    for connector_data in session_input.chosen.iter() {
        pm_type_map
            .entry(connector_data.payment_method_sub_type)
            .or_default()
            .insert(
                connector_data.connector.connector_name.to_string(),
                connector_data.connector.get_token.clone(),
            );
    }

    let mut result: FxHashMap<
        api_enums::PaymentMethodType,
        Vec<routing_types::SessionRoutingChoice>,
    > = FxHashMap::default();

    for (pm_type, allowed_connectors) in pm_type_map {
        let euclid_pmt: euclid_enums::PaymentMethodType = pm_type;
        let euclid_pm: euclid_enums::PaymentMethod = euclid_pmt.into();

        backend_input.payment_method.payment_method = Some(euclid_pm);
        backend_input.payment_method.payment_method_type = Some(euclid_pmt);

        let session_pm_input = SessionRoutingPmTypeInput {
            routing_algorithm: &routing_algorithm,
            backend_input: backend_input.clone(),
            allowed_connectors,
            profile_id: &profile_id,
        };

        let routable_connector_choice_option = perform_session_routing_for_pm_type(
            state,
            key_store,
            &session_pm_input,
            transaction_type,
            business_profile,
        )
        .await?;

        if let Some(routable_connector_choice) = routable_connector_choice_option {
            let mut session_routing_choice: Vec<routing_types::SessionRoutingChoice> = Vec::new();

            for selection in routable_connector_choice {
                let connector_name = selection.connector.to_string();
                if let Some(get_token) = session_pm_input.allowed_connectors.get(&connector_name) {
                    let connector_data = api::ConnectorData::get_connector_by_name(
                        &state.clone().conf.connectors,
                        &connector_name,
                        get_token.clone(),
                        selection.merchant_connector_id,
                    )
                    .change_context(errors::RoutingError::InvalidConnectorName(connector_name))?;

                    session_routing_choice.push(routing_types::SessionRoutingChoice {
                        connector: connector_data,
                        payment_method_type: pm_type,
                    });
                }
            }
            if !session_routing_choice.is_empty() {
                result.insert(pm_type, session_routing_choice);
            }
        }
    }

    Ok(result)
}

#[cfg(feature = "v1")]
pub async fn perform_session_flow_routing(
    session_input: SessionFlowRoutingInput<'_>,
    business_profile: &domain::Profile,
    transaction_type: &api_enums::TransactionType,
) -> RoutingResult<(
    FxHashMap<api_enums::PaymentMethodType, Vec<routing_types::SessionRoutingChoice>>,
    Option<common_enums::RoutingApproach>,
)> {
    let mut pm_type_map: FxHashMap<api_enums::PaymentMethodType, FxHashMap<String, api::GetToken>> =
        FxHashMap::default();

    let profile_id = session_input
        .payment_intent
        .profile_id
        .clone()
        .get_required_value("profile_id")
        .change_context(errors::RoutingError::ProfileIdMissing)?;

    let routing_algorithm: MerchantAccountRoutingAlgorithm = {
        business_profile
            .routing_algorithm
            .clone()
            .map(|val| val.parse_value("MerchantAccountRoutingAlgorithm"))
            .transpose()
            .change_context(errors::RoutingError::InvalidRoutingAlgorithmStructure)?
            .unwrap_or_default()
    };

    let payment_method_input = dsl_inputs::PaymentMethodInput {
        payment_method: None,
        payment_method_type: None,
        card_network: None,
    };

    let payment_input = dsl_inputs::PaymentInput {
        amount: session_input.payment_attempt.get_total_amount(),
        currency: session_input
            .payment_intent
            .currency
            .get_required_value("Currency")
            .change_context(errors::RoutingError::DslMissingRequiredField {
                field_name: "currency".to_string(),
            })?,
        authentication_type: session_input.payment_attempt.authentication_type,
        card_bin: None,
        capture_method: session_input
            .payment_attempt
            .capture_method
            .and_then(Option::<euclid_enums::CaptureMethod>::foreign_from),
        business_country: session_input
            .payment_intent
            .business_country
            .map(api_enums::Country::from_alpha2),
        billing_country: session_input
            .country
            .map(storage_enums::Country::from_alpha2),
        business_label: session_input.payment_intent.business_label.clone(),
        setup_future_usage: session_input.payment_intent.setup_future_usage,
    };

    let metadata = session_input
        .payment_intent
        .parse_and_get_metadata("routing_parameters")
        .change_context(errors::RoutingError::MetadataParsingError)
        .attach_printable("Unable to parse routing_parameters from metadata of payment_intent")
        .unwrap_or(None);

    let mut backend_input = dsl_inputs::BackendInput {
        metadata,
        payment: payment_input,
        payment_method: payment_method_input,
        mandate: dsl_inputs::MandateData {
            mandate_acceptance_type: None,
            mandate_type: None,
            payment_type: None,
        },
        acquirer_data: None,
        customer_device_data: None,
        issuer_data: None,
    };

    for connector_data in session_input.chosen.iter() {
        pm_type_map
            .entry(connector_data.payment_method_sub_type)
            .or_default()
            .insert(
                connector_data.connector.connector_name.to_string(),
                connector_data.connector.get_token.clone(),
            );
    }

    let mut result: FxHashMap<
        api_enums::PaymentMethodType,
        Vec<routing_types::SessionRoutingChoice>,
    > = FxHashMap::default();
    let mut final_routing_approach = None;

    for (pm_type, allowed_connectors) in pm_type_map {
        let euclid_pmt: euclid_enums::PaymentMethodType = pm_type;
        let euclid_pm: euclid_enums::PaymentMethod = euclid_pmt.into();

        backend_input.payment_method.payment_method = Some(euclid_pm);
        backend_input.payment_method.payment_method_type = Some(euclid_pmt);

        let session_pm_input = SessionRoutingPmTypeInput {
            state: session_input.state,
            key_store: session_input.key_store,
            attempt_id: session_input.payment_attempt.get_id(),
            routing_algorithm: &routing_algorithm,
            backend_input: backend_input.clone(),
            allowed_connectors,
            profile_id: &profile_id,
        };

        let (routable_connector_choice_option, routing_approach) =
            perform_session_routing_for_pm_type(
                &session_pm_input,
                transaction_type,
                business_profile,
            )
            .await?;

        final_routing_approach = routing_approach;

        if let Some(routable_connector_choice) = routable_connector_choice_option {
            let mut session_routing_choice: Vec<routing_types::SessionRoutingChoice> = Vec::new();

            for selection in routable_connector_choice {
                let connector_name = selection.connector.to_string();
                if let Some(get_token) = session_pm_input.allowed_connectors.get(&connector_name) {
                    let connector_data = api::ConnectorData::get_connector_by_name(
                        &session_pm_input.state.clone().conf.connectors,
                        &connector_name,
                        get_token.clone(),
                        selection.merchant_connector_id,
                    )
                    .change_context(errors::RoutingError::InvalidConnectorName(connector_name))?;

                    session_routing_choice.push(routing_types::SessionRoutingChoice {
                        connector: connector_data,
                        payment_method_type: pm_type,
                    });
                }
            }
            if !session_routing_choice.is_empty() {
                result.insert(pm_type, session_routing_choice);
            }
        }
    }

    Ok((result, final_routing_approach))
}

#[cfg(feature = "v1")]
async fn perform_session_routing_for_pm_type(
    session_pm_input: &SessionRoutingPmTypeInput<'_>,
    transaction_type: &api_enums::TransactionType,
    _business_profile: &domain::Profile,
) -> RoutingResult<(
    Option<Vec<api_models::routing::RoutableConnectorChoice>>,
    Option<common_enums::RoutingApproach>,
)> {
    let merchant_id = &session_pm_input.key_store.merchant_id;

    let algorithm_id = match session_pm_input.routing_algorithm {
        MerchantAccountRoutingAlgorithm::V1(algorithm_ref) => &algorithm_ref.algorithm_id,
    };

    let (chosen_connectors, routing_approach) = if let Some(ref algorithm_id) = algorithm_id {
        let cached_algorithm = ensure_algorithm_cached_v1(
            &session_pm_input.state.clone(),
            merchant_id,
            algorithm_id,
            session_pm_input.profile_id,
            transaction_type,
        )
        .await?;

        match cached_algorithm.as_ref() {
            CachedAlgorithm::Single(conn) => (
                vec![(**conn).clone()],
                Some(common_enums::RoutingApproach::StraightThroughRouting),
            ),
            CachedAlgorithm::Priority(plist) => (plist.clone(), None),
            CachedAlgorithm::VolumeSplit(splits) => (
                perform_volume_split(splits.to_vec())
                    .change_context(errors::RoutingError::ConnectorSelectionFailed)?,
                Some(common_enums::RoutingApproach::VolumeBasedRouting),
            ),
            CachedAlgorithm::Advanced(interpreter) => (
                execute_dsl_and_get_connector_v1(
                    session_pm_input.backend_input.clone(),
                    interpreter,
                )?,
                Some(common_enums::RoutingApproach::RuleBasedRouting),
            ),
        }
    } else {
        (
            routing::helpers::get_merchant_default_config(
                &*session_pm_input.state.clone().store,
                session_pm_input.profile_id.get_string_repr(),
                transaction_type,
            )
            .await
            .change_context(errors::RoutingError::FallbackConfigFetchFailed)?,
            None,
        )
    };

    let mut final_selection = perform_cgraph_filtering(
        &session_pm_input.state.clone(),
        session_pm_input.key_store,
        chosen_connectors,
        session_pm_input.backend_input.clone(),
        None,
        session_pm_input.profile_id,
        transaction_type,
    )
    .await?;

    if final_selection.is_empty() {
        let fallback = routing::helpers::get_merchant_default_config(
            &*session_pm_input.state.clone().store,
            session_pm_input.profile_id.get_string_repr(),
            transaction_type,
        )
        .await
        .change_context(errors::RoutingError::FallbackConfigFetchFailed)?;

        final_selection = perform_cgraph_filtering(
            &session_pm_input.state.clone(),
            session_pm_input.key_store,
            fallback,
            session_pm_input.backend_input.clone(),
            None,
            session_pm_input.profile_id,
            transaction_type,
        )
        .await?;
    }

    if final_selection.is_empty() {
        Ok((None, routing_approach))
    } else {
        Ok((Some(final_selection), routing_approach))
    }
}

#[cfg(feature = "v2")]
async fn get_chosen_connectors<'a>(
    state: &'a SessionState,
    key_store: &'a domain::MerchantKeyStore,
    session_pm_input: &SessionRoutingPmTypeInput<'_>,
    transaction_type: &api_enums::TransactionType,
    profile_wrapper: &admin::ProfileWrapper,
) -> RoutingResult<Vec<api_models::routing::RoutableConnectorChoice>> {
    let merchant_id = &key_store.merchant_id;

    let MerchantAccountRoutingAlgorithm::V1(algorithm_id) = session_pm_input.routing_algorithm;

    let chosen_connectors = if let Some(ref algorithm_id) = algorithm_id {
        let cached_algorithm = ensure_algorithm_cached_v1(
            state,
            merchant_id,
            algorithm_id,
            session_pm_input.profile_id,
            transaction_type,
        )
        .await?;

        match cached_algorithm.as_ref() {
            CachedAlgorithm::Single(conn) => vec![(**conn).clone()],
            CachedAlgorithm::Priority(plist) => plist.clone(),
            CachedAlgorithm::VolumeSplit(splits) => perform_volume_split(splits.to_vec())
                .change_context(errors::RoutingError::ConnectorSelectionFailed)?,
            CachedAlgorithm::Advanced(interpreter) => execute_dsl_and_get_connector_v1(
                session_pm_input.backend_input.clone(),
                interpreter,
            )?,
        }
    } else {
        profile_wrapper
            .get_default_fallback_list_of_connector_under_profile()
            .change_context(errors::RoutingError::FallbackConfigFetchFailed)?
    };
    Ok(chosen_connectors)
}

#[cfg(feature = "v2")]
async fn perform_session_routing_for_pm_type<'a>(
    state: &'a SessionState,
    key_store: &'a domain::MerchantKeyStore,
    session_pm_input: &SessionRoutingPmTypeInput<'_>,
    transaction_type: &api_enums::TransactionType,
    business_profile: &domain::Profile,
) -> RoutingResult<Option<Vec<api_models::routing::RoutableConnectorChoice>>> {
    let profile_wrapper = admin::ProfileWrapper::new(business_profile.clone());
    let chosen_connectors = get_chosen_connectors(
        state,
        key_store,
        session_pm_input,
        transaction_type,
        &profile_wrapper,
    )
    .await?;

    let mut final_selection = perform_cgraph_filtering(
        state,
        key_store,
        chosen_connectors,
        session_pm_input.backend_input.clone(),
        None,
        session_pm_input.profile_id,
        transaction_type,
    )
    .await?;

    if final_selection.is_empty() {
        let fallback = profile_wrapper
            .get_default_fallback_list_of_connector_under_profile()
            .change_context(errors::RoutingError::FallbackConfigFetchFailed)?;

        final_selection = perform_cgraph_filtering(
            state,
            key_store,
            fallback,
            session_pm_input.backend_input.clone(),
            None,
            session_pm_input.profile_id,
            transaction_type,
        )
        .await?;
    }

    if final_selection.is_empty() {
        Ok(None)
    } else {
        Ok(Some(final_selection))
    }
}
#[cfg(feature = "v2")]
pub fn make_dsl_input_for_surcharge(
    _payment_attempt: &oss_storage::PaymentAttempt,
    _payment_intent: &oss_storage::PaymentIntent,
    _billing_address: Option<Address>,
) -> RoutingResult<dsl_inputs::BackendInput> {
    todo!()
}

#[cfg(feature = "v1")]
pub fn make_dsl_input_for_surcharge(
    payment_attempt: &oss_storage::PaymentAttempt,
    payment_intent: &oss_storage::PaymentIntent,
    billing_address: Option<Address>,
) -> RoutingResult<dsl_inputs::BackendInput> {
    let mandate_data = dsl_inputs::MandateData {
        mandate_acceptance_type: None,
        mandate_type: None,
        payment_type: None,
    };

    let payment_input = dsl_inputs::PaymentInput {
        amount: payment_attempt.get_total_amount(),
        // currency is always populated in payment_attempt during payment create
        currency: payment_attempt
            .currency
            .get_required_value("currency")
            .change_context(errors::RoutingError::DslMissingRequiredField {
                field_name: "currency".to_string(),
            })?,
        authentication_type: payment_attempt.authentication_type,
        card_bin: None,
        capture_method: payment_attempt.capture_method,
        business_country: payment_intent
            .business_country
            .map(api_enums::Country::from_alpha2),
        billing_country: billing_address
            .and_then(|bic| bic.address)
            .and_then(|add| add.country)
            .map(api_enums::Country::from_alpha2),
        business_label: payment_intent.business_label.clone(),
        setup_future_usage: payment_intent.setup_future_usage,
    };

    let metadata = payment_intent
        .parse_and_get_metadata("routing_parameters")
        .change_context(errors::RoutingError::MetadataParsingError)
        .attach_printable("Unable to parse routing_parameters from metadata of payment_intent")
        .unwrap_or(None);
    let payment_method_input = dsl_inputs::PaymentMethodInput {
        payment_method: None,
        payment_method_type: None,
        card_network: None,
    };
    let backend_input = dsl_inputs::BackendInput {
        metadata,
        payment: payment_input,
        payment_method: payment_method_input,
        mandate: mandate_data,
        acquirer_data: None,
        customer_device_data: None,
        issuer_data: None,
    };
    Ok(backend_input)
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn perform_dynamic_routing_with_open_router<F, D>(
    state: &SessionState,
    routable_connectors: Vec<api_routing::RoutableConnectorChoice>,
    profile: &domain::Profile,
    payment_data: oss_storage::PaymentAttempt,
    old_payment_data: &mut D,
) -> RoutingResult<Vec<api_routing::RoutableConnectorChoice>>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let dynamic_routing_algo_ref: api_routing::DynamicRoutingAlgorithmRef = profile
        .dynamic_routing_algorithm
        .clone()
        .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
        .transpose()
        .change_context(errors::RoutingError::DeserializationError {
            from: "JSON".to_string(),
            to: "DynamicRoutingAlgorithmRef".to_string(),
        })
        .attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?
        .ok_or(errors::RoutingError::GenericNotFoundError {
            field: "dynamic_routing_algorithm".to_string(),
        })?;

    logger::debug!(
        "performing dynamic_routing with open_router for profile {}",
        profile.get_id().get_string_repr()
    );

    let is_success_rate_routing_enabled =
        dynamic_routing_algo_ref.is_success_rate_routing_enabled();
    let is_elimination_enabled = dynamic_routing_algo_ref.is_elimination_enabled();

    // Since success_based and elimination routing is being done in 1 api call, we call decide_gateway when either of it enabled
    let connectors = if is_success_rate_routing_enabled || is_elimination_enabled {
        let connectors = perform_decide_gateway_call_with_open_router(
            state,
            routable_connectors.clone(),
            profile.get_id(),
            &payment_data,
            is_elimination_enabled,
            old_payment_data,
        )
        .await?;

        if is_elimination_enabled {
            // This will initiate the elimination process for the connector.
            // Penalize the elimination score of the connector before making a payment.
            // Once the payment is made, we will update the score based on the payment status
            if let Some(connector) = connectors.first() {
                logger::debug!(
                "penalizing the elimination score of the gateway with id {} in open_router for profile {}",
                connector, profile.get_id().get_string_repr()
            );
                update_gateway_score_with_open_router(
                    state,
                    connector.clone(),
                    profile.get_id(),
                    &payment_data.merchant_id,
                    &payment_data.payment_id,
                    common_enums::AttemptStatus::AuthenticationPending,
                )
                .await?
            }
        }
        connectors
    } else {
        routable_connectors
    };

    Ok(connectors)
}

#[cfg(feature = "v1")]
pub async fn perform_open_routing_for_debit_routing<F, D>(
    state: &SessionState,
    co_badged_card_request: or_types::CoBadgedCardRequest,
    card_isin: Option<Secret<String>>,
    old_payment_data: &mut D,
) -> RoutingResult<or_types::DebitRoutingOutput>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let payment_attempt = old_payment_data.get_payment_attempt().clone();

    logger::debug!(
        "performing debit routing with open_router for profile {}",
        payment_attempt.profile_id.get_string_repr()
    );

    let metadata = Some(
        serde_json::to_string(&co_badged_card_request)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to encode Vaulting data to string")
            .change_context(errors::RoutingError::MetadataParsingError)?,
    );

    let open_router_req_body = OpenRouterDecideGatewayRequest::construct_debit_request(
        &payment_attempt,
        metadata,
        card_isin,
        Some(or_types::RankingAlgorithm::NtwBasedRouting),
    );

    let routing_events_wrapper = utils::RoutingEventsWrapper::new(
        state.tenant.tenant_id.clone(),
        state.request_id,
        payment_attempt.payment_id.get_string_repr().to_string(),
        payment_attempt.profile_id.to_owned(),
        payment_attempt.merchant_id.to_owned(),
        "DecisionEngine: Debit Routing".to_string(),
        Some(open_router_req_body.clone()),
        true,
        true,
    );

    let response: RoutingResult<utils::RoutingEventsResponse<DecidedGateway>> =
        utils::EuclidApiClient::send_decision_engine_request(
            state,
            services::Method::Post,
            "decide-gateway",
            Some(open_router_req_body),
            None,
            Some(routing_events_wrapper),
        )
        .await;

    let output = match response {
        Ok(events_response) => {
            let response =
                events_response
                    .response
                    .ok_or(errors::RoutingError::OpenRouterError(
                        "Response from decision engine API is empty".to_string(),
                    ))?;

            let debit_routing_output = response
                .debit_routing_output
                .get_required_value("debit_routing_output")
                .change_context(errors::RoutingError::OpenRouterError(
                    "Failed to parse the response from open_router".into(),
                ))
                .attach_printable("debit_routing_output is missing in the open routing response")?;

            old_payment_data.set_routing_approach_in_attempt(Some(
                common_enums::RoutingApproach::from_decision_engine_approach(
                    &response.routing_approach,
                ),
            ));

            Ok(debit_routing_output)
        }
        Err(error_response) => {
            logger::error!("open_router_error_response: {:?}", error_response);
            Err(errors::RoutingError::OpenRouterError(
                "Failed to perform debit routing in open router".into(),
            ))
        }
    }?;

    Ok(output)
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn perform_dynamic_routing_with_intelligent_router<F, D>(
    state: &SessionState,
    routable_connectors: Vec<api_routing::RoutableConnectorChoice>,
    profile: &domain::Profile,
    dynamic_routing_config_params_interpolator: routing::helpers::DynamicRoutingConfigParamsInterpolator,
    payment_data: &mut D,
) -> RoutingResult<Vec<api_routing::RoutableConnectorChoice>>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let dynamic_routing_algo_ref: api_routing::DynamicRoutingAlgorithmRef = profile
        .dynamic_routing_algorithm
        .clone()
        .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
        .transpose()
        .change_context(errors::RoutingError::DeserializationError {
            from: "JSON".to_string(),
            to: "DynamicRoutingAlgorithmRef".to_string(),
        })
        .attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?
        .ok_or(errors::RoutingError::GenericNotFoundError {
            field: "dynamic_routing_algorithm".to_string(),
        })?;

    logger::debug!(
        "performing dynamic_routing for profile {}",
        profile.get_id().get_string_repr()
    );

    let payment_attempt = payment_data.get_payment_attempt().clone();

    let mut connector_list = match dynamic_routing_algo_ref
        .success_based_algorithm
        .as_ref()
        .async_map(|algorithm| {
            perform_success_based_routing(
                state,
                routable_connectors.clone(),
                profile.get_id(),
                &payment_attempt.merchant_id,
                &payment_attempt.payment_id,
                dynamic_routing_config_params_interpolator.clone(),
                algorithm.clone(),
                payment_data,
            )
        })
        .await
        .transpose()
        .inspect_err(|e| logger::error!(dynamic_routing_error=?e))
        .ok()
        .flatten()
    {
        Some(success_based_list) => success_based_list,
        None => {
            // Only run contract based if success based returns None
            dynamic_routing_algo_ref
                .contract_based_routing
                .as_ref()
                .async_map(|algorithm| {
                    perform_contract_based_routing(
                        state,
                        routable_connectors.clone(),
                        profile.get_id(),
                        &payment_attempt.merchant_id,
                        &payment_attempt.payment_id,
                        dynamic_routing_config_params_interpolator.clone(),
                        algorithm.clone(),
                        payment_data,
                    )
                })
                .await
                .transpose()
                .inspect_err(|e| logger::error!(dynamic_routing_error=?e))
                .ok()
                .flatten()
                .unwrap_or(routable_connectors.clone())
        }
    };

    connector_list = dynamic_routing_algo_ref
        .elimination_routing_algorithm
        .as_ref()
        .async_map(|algorithm| {
            perform_elimination_routing(
                state,
                connector_list.clone(),
                profile.get_id(),
                &payment_attempt.merchant_id,
                &payment_attempt.payment_id,
                dynamic_routing_config_params_interpolator.clone(),
                algorithm.clone(),
            )
        })
        .await
        .transpose()
        .inspect_err(|e| logger::error!(dynamic_routing_error=?e))
        .ok()
        .flatten()
        .unwrap_or(connector_list);

    Ok(connector_list)
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
#[instrument(skip_all)]
pub async fn perform_decide_gateway_call_with_open_router<F, D>(
    state: &SessionState,
    mut routable_connectors: Vec<api_routing::RoutableConnectorChoice>,
    profile_id: &common_utils::id_type::ProfileId,
    payment_attempt: &oss_storage::PaymentAttempt,
    is_elimination_enabled: bool,
    old_payment_data: &mut D,
) -> RoutingResult<Vec<api_routing::RoutableConnectorChoice>>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    logger::debug!(
        "performing decide_gateway call with open_router for profile {}",
        profile_id.get_string_repr()
    );

    let open_router_req_body = OpenRouterDecideGatewayRequest::construct_sr_request(
        payment_attempt,
        routable_connectors.clone(),
        Some(or_types::RankingAlgorithm::SrBasedRouting),
        is_elimination_enabled,
    );

    let routing_events_wrapper = utils::RoutingEventsWrapper::new(
        state.tenant.tenant_id.clone(),
        state.request_id,
        payment_attempt.payment_id.get_string_repr().to_string(),
        payment_attempt.profile_id.to_owned(),
        payment_attempt.merchant_id.to_owned(),
        "DecisionEngine: SuccessRate decide_gateway".to_string(),
        Some(open_router_req_body.clone()),
        true,
        false,
    );

    let response: RoutingResult<utils::RoutingEventsResponse<DecidedGateway>> =
        utils::SRApiClient::send_decision_engine_request(
            state,
            services::Method::Post,
            "decide-gateway",
            Some(open_router_req_body),
            None,
            Some(routing_events_wrapper),
        )
        .await;

    let sr_sorted_connectors = match response {
        Ok(resp) => {
            let decided_gateway: DecidedGateway =
                resp.response.ok_or(errors::RoutingError::OpenRouterError(
                    "Empty response received from open_router".into(),
                ))?;

            let mut routing_event = resp.event.ok_or(errors::RoutingError::RoutingEventsError {
                message: "Decision-Engine: RoutingEvent not found in RoutingEventsResponse"
                    .to_string(),
                status_code: 500,
            })?;

            routing_event.set_response_body(&decided_gateway);
            routing_event.set_routing_approach(
                utils::RoutingApproach::from_decision_engine_approach(
                    &decided_gateway.routing_approach,
                )
                .to_string(),
            );

            old_payment_data.set_routing_approach_in_attempt(Some(
                common_enums::RoutingApproach::from_decision_engine_approach(
                    &decided_gateway.routing_approach,
                ),
            ));

            if let Some(gateway_priority_map) = decided_gateway.gateway_priority_map {
                logger::debug!(gateway_priority_map=?gateway_priority_map, routing_approach=decided_gateway.routing_approach, "open_router decide_gateway call response");
                routable_connectors.sort_by(|connector_choice_a, connector_choice_b| {
                    let connector_choice_a_score = gateway_priority_map
                        .get(&connector_choice_a.to_string())
                        .copied()
                        .unwrap_or(0.0);
                    let connector_choice_b_score = gateway_priority_map
                        .get(&connector_choice_b.to_string())
                        .copied()
                        .unwrap_or(0.0);
                    connector_choice_b_score
                        .partial_cmp(&connector_choice_a_score)
                        .unwrap_or(std::cmp::Ordering::Equal)
                });
            }

            routing_event.set_routable_connectors(routable_connectors.clone());
            state.event_handler().log_event(&routing_event);

            Ok(routable_connectors)
        }
        Err(err) => {
            logger::error!("open_router_error_response: {:?}", err);

            Err(errors::RoutingError::OpenRouterError(
                "Failed to perform decide_gateway call in open_router".into(),
            ))
        }
    }?;

    Ok(sr_sorted_connectors)
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
#[instrument(skip_all)]
pub async fn update_gateway_score_with_open_router(
    state: &SessionState,
    payment_connector: api_routing::RoutableConnectorChoice,
    profile_id: &common_utils::id_type::ProfileId,
    merchant_id: &common_utils::id_type::MerchantId,
    payment_id: &common_utils::id_type::PaymentId,
    payment_status: common_enums::AttemptStatus,
) -> RoutingResult<()> {
    let open_router_req_body = or_types::UpdateScorePayload {
        merchant_id: profile_id.clone(),
        gateway: payment_connector.to_string(),
        status: payment_status.foreign_into(),
        payment_id: payment_id.clone(),
    };

    let routing_events_wrapper = utils::RoutingEventsWrapper::new(
        state.tenant.tenant_id.clone(),
        state.request_id,
        payment_id.get_string_repr().to_string(),
        profile_id.to_owned(),
        merchant_id.to_owned(),
        "DecisionEngine: SuccessRate update_gateway_score".to_string(),
        Some(open_router_req_body.clone()),
        true,
        false,
    );

    let response: RoutingResult<utils::RoutingEventsResponse<or_types::UpdateScoreResponse>> =
        utils::SRApiClient::send_decision_engine_request(
            state,
            services::Method::Post,
            "update-gateway-score",
            Some(open_router_req_body),
            None,
            Some(routing_events_wrapper),
        )
        .await;

    match response {
        Ok(resp) => {
            let update_score_resp = resp.response.ok_or(errors::RoutingError::OpenRouterError(
                "Failed to parse the response from open_router".into(),
            ))?;

            let mut routing_event = resp.event.ok_or(errors::RoutingError::RoutingEventsError {
                message: "Decision-Engine: RoutingEvent not found in RoutingEventsResponse"
                    .to_string(),
                status_code: 500,
            })?;

            logger::debug!(
                "open_router update_gateway_score response for gateway with id {}: {:?}",
                payment_connector,
                update_score_resp.message
            );

            routing_event.set_response_body(&update_score_resp);
            routing_event.set_payment_connector(payment_connector.clone()); // check this in review
            state.event_handler().log_event(&routing_event);

            Ok(())
        }
        Err(err) => {
            logger::error!("open_router_update_gateway_score_error: {:?}", err);

            Err(errors::RoutingError::OpenRouterError(
                "Failed to update gateway score in open_router".into(),
            ))
        }
    }?;

    Ok(())
}

/// success based dynamic routing
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn perform_success_based_routing<F, D>(
    state: &SessionState,


pub struct PaypalSyncBatchResponse {
    pub payout_batch_id: Option<String>,
    pub sender_batch_id: Option<String>,
    pub batch_status: PaypalFulfillStatus,
}


pub fn get_resolved_relation(prev_relation: Self, curr_relation: Self) -> Self {
        if prev_relation != curr_relation {
            Self::Contradiction
        } else {
            curr_relation
        }
    }


pub struct Program<O> {
    pub default_selection: O,
    #[schema(value_type=RuleConnectorSelection)]
    pub rules: Vec<Rule<O>>,
    #[schema(value_type=HashMap<String, serde_json::Value>)]
    pub metadata: Metadata,
}


pub async fn payouts_list_by_filter() {}


pub struct PaymentStatus;


pub fn check_presence(&self, value: &EuclidValue) -> bool {
        let key = value.get_key();

        match key.key_type() {
            types::DataType::MetadataValue => self.atomic_values.contains(value),
            types::DataType::StrValue => self.atomic_values.contains(value),
            types::DataType::EnumVariant => self.atomic_values.contains(value),
            types::DataType::Number => {
                let ctx_num_value = self
                    .numeric_values
                    .get(&key)
                    .and_then(|value| value.get_num_value());

                value.get_num_value().zip(ctx_num_value).is_some_and(
                    |(program_value, ctx_value)| {
                        let program_num = program_value.number;
                        let ctx_num = ctx_value.number;

                        match &program_value.refinement {
                            None => program_num == ctx_num,
                            Some(NumValueRefinement::NotEqual) => ctx_num != program_num,
                            Some(NumValueRefinement::GreaterThan) => ctx_num > program_num,
                            Some(NumValueRefinement::GreaterThanEqual) => ctx_num >= program_num,
                            Some(NumValueRefinement::LessThanEqual) => ctx_num <= program_num,
                            Some(NumValueRefinement::LessThan) => ctx_num < program_num,
                        }
                    },
                )
            }
        }
    }


File: crates/diesel_models/src/dynamic_routing_stats.rs
 Public structs: 3

use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};

use crate::schema::dynamic_routing_stats;

#[derive(Clone, Debug, Eq, Insertable, PartialEq)]
#[diesel(table_name = dynamic_routing_stats)]
pub struct DynamicRoutingStatsNew {
    pub payment_id: common_utils::id_type::PaymentId,
    pub attempt_id: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub profile_id: common_utils::id_type::ProfileId,
    pub amount: common_utils::types::MinorUnit,
    pub success_based_routing_connector: String,
    pub payment_connector: String,
    pub currency: Option<common_enums::Currency>,
    pub payment_method: Option<common_enums::PaymentMethod>,
    pub capture_method: Option<common_enums::CaptureMethod>,
    pub authentication_type: Option<common_enums::AuthenticationType>,
    pub payment_status: common_enums::AttemptStatus,
    pub conclusive_classification: common_enums::SuccessBasedRoutingConclusiveState,
    pub created_at: time::PrimitiveDateTime,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub global_success_based_connector: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Insertable)]
#[diesel(table_name = dynamic_routing_stats, primary_key(attempt_id, merchant_id), check_for_backend(diesel::pg::Pg))]
pub struct DynamicRoutingStats {
    pub payment_id: common_utils::id_type::PaymentId,
    pub attempt_id: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub profile_id: common_utils::id_type::ProfileId,
    pub amount: common_utils::types::MinorUnit,
    pub success_based_routing_connector: String,
    pub payment_connector: String,
    pub currency: Option<common_enums::Currency>,
    pub payment_method: Option<common_enums::PaymentMethod>,
    pub capture_method: Option<common_enums::CaptureMethod>,
    pub authentication_type: Option<common_enums::AuthenticationType>,
    pub payment_status: common_enums::AttemptStatus,
    pub conclusive_classification: common_enums::SuccessBasedRoutingConclusiveState,
    pub created_at: time::PrimitiveDateTime,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub global_success_based_connector: Option<String>,
}

#[derive(
    Clone, Debug, Eq, PartialEq, AsChangeset, router_derive::DebugAsDisplay, serde::Deserialize,
)]
#[diesel(table_name = dynamic_routing_stats)]
pub struct DynamicRoutingStatsUpdate {
    pub amount: common_utils::types::MinorUnit,
    pub success_based_routing_connector: String,
    pub payment_connector: String,
    pub currency: Option<common_enums::Currency>,
    pub payment_method: Option<common_enums::PaymentMethod>,
    pub capture_method: Option<common_enums::CaptureMethod>,
    pub authentication_type: Option<common_enums::AuthenticationType>,
    pub payment_status: common_enums::AttemptStatus,
    pub conclusive_classification: common_enums::SuccessBasedRoutingConclusiveState,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub global_success_based_connector: Option<String>,
}


impl Display for CustomJsonError {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            f.write_str(
                serde_json::to_string(&serde_json::json!({
                    "error": {
                        "error_type": "invalid_request",
                        "message": self.err.to_string(),
                        "code": "IR_06",
                    }
                }))
                .as_deref()
                .unwrap_or("Invalid Json Error"),
            )
        }
    }


pub fn perform_integrity_check<F>(
    mut router_data: RouterData<F, types::RefundsData, types::RefundsResponseData>,
) -> RouterData<F, types::RefundsData, types::RefundsResponseData>
where
    F: Debug + Clone + 'static,
{
    // Initiating Integrity check
    let integrity_result = check_refund_integrity(&router_data.request, &router_data.response);
    router_data.integrity_check = integrity_result;
    router_data
}


pub async fn routing_unlink_config(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::ProfileId>,
    transaction_type: &enums::TransactionType,
) -> impl Responder {
    let flow = Flow::RoutingUnlinkConfig;
    let path = path.into_inner();
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        path.clone(),
        |state, auth: auth::AuthenticationData, path, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            routing::unlink_routing_config_under_profile(
                state,
                merchant_context,
                path,
                transaction_type,
            )
        },
        #[cfg(not(feature = "release"))]
        auth::auth_type(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::JWTAuthProfileFromRoute {
                profile_id: path,
                required_permission: Permission::MerchantRoutingWrite,
            },
            req.headers(),
        ),
        #[cfg(feature = "release")]
        &auth::JWTAuthProfileFromRoute {
            profile_id: path,
            required_permission: Permission::MerchantRoutingWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct StripebillingErrorResponse {
    pub status_code: u16,
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
}


pub struct AirwallexAuthType {
    pub x_api_key: Secret<String>,
    pub x_client_id: Secret<String>,
}


pub struct InitPayment;


pub struct MandateRevokeRequestData {
    pub mandate_id: String,
    pub connector_mandate_id: Option<String>,
}


pub struct FlexitiPaymentsResponse {
    redirection_url: url::Url,
    online_order_id: String,
}


pub struct DlocalPaymentsCancelResponse {
    status: DlocalPaymentStatus,
    order_id: String,
}


pub struct SCAResult {
    pub version: String,
    pub directory_server_trans_id: String,
}


pub async fn retrieve_surcharge_decision_manager_config(
    state: web::Data<AppState>,
    req: HttpRequest,
) -> impl Responder {
    let flow = Flow::DecisionManagerRetrieveConfig;
    Box::pin(oss_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),
            ));
            surcharge_decision_config::retrieve_surcharge_decision_config(state, merchant_context)
        },
        #[cfg(not(feature = "release"))]
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::MerchantSurchargeDecisionManagerRead,
            },
            req.headers(),
        ),
        #[cfg(feature = "release")]
        &auth::JWTAuth {
            permission: Permission::MerchantSurchargeDecisionManagerRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct FacilitapayCardDetails {
    #[serde(rename = "card_number")]
    pub number: cards::CardNumber,
    #[serde(rename = "card_expiration_date")]
    pub expiry_date: Secret<String>, // Format: "MM/YYYY"
    #[serde(rename = "card_security_code")]
    pub cvc: Secret<String>,
    #[serde(rename = "card_brand")]
    pub brand: String,
    pub fullname: Secret<String>,
}


File: crates/test_utils/tests/connectors/bluesnap_wh_ui.rs

use thirtyfour::{prelude::*, WebDriver};

use crate::{selenium::*, tester};

struct BluesnapSeleniumTest;

impl SeleniumTest for BluesnapSeleniumTest {
    fn get_connector_name(&self) -> String {
        "bluesnap".to_string()
    }
}

async fn should_make_webhook(web_driver: WebDriver) -> Result<(), WebDriverError> {
    let conn = BluesnapSeleniumTest {};
    conn.make_webhook_test(
        web_driver,
        &format!("{CHECKOUT_BASE_URL}/saved/199"),
        vec![
            Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
            Event::Assert(Assert::IsPresent("succeeded")),
        ],
        10,
        "succeeded",
    )
    .await?;
    Ok(())
}

#[test]
fn should_make_webhook_test() {
    tester!(should_make_webhook);
}