    user_from_token: auth::UserFromToken,
) -> UserResponse<user_api::TokenResponse> {
    if user_from_token.merchant_id == request.merchant_id {
        return Err(UserErrors::InvalidRoleOperationWithMessage(
            "User switching to same merchant".to_string(),
        )
        .into());
    }

    let key_manager_state = &(&state).into();
    let role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
        &state,
        &user_from_token.role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .change_context(UserErrors::InternalServerError)
    .attach_printable("Failed to retrieve role information")?;

    // Check if the role is internal and handle separately
    let (org_id, merchant_id, profile_id, role_id) = if role_info.is_internal() {
        let merchant_key_store = state
            .store
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &request.merchant_id,
                &state.store.get_master_key().to_vec().into(),
            )
            .await
            .to_not_found_response(UserErrors::MerchantIdNotFound)?;

        let merchant_account = state
            .store
            .find_merchant_account_by_merchant_id(
                key_manager_state,
                &request.merchant_id,
                &merchant_key_store,
            )
            .await
            .to_not_found_response(UserErrors::MerchantIdNotFound)?;

        let profile_id = state
            .store
            .list_profile_by_merchant_id(
                key_manager_state,
                &merchant_key_store,
                &request.merchant_id,
            )
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Failed to list business profiles by merchant_id")?
            .pop()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("No business profile found for the given merchant_id")?
            .get_id()
            .to_owned();

        (
            merchant_account.organization_id,
            request.merchant_id,
            profile_id,
            user_from_token.role_id.clone(),
        )
    } else {
        // Match based on the other entity types
        match role_info.get_entity_type() {
            EntityType::Tenant | EntityType::Organization => {
                let merchant_key_store = state
                    .store
                    .get_merchant_key_store_by_merchant_id(
                        key_manager_state,
                        &request.merchant_id,
                        &state.store.get_master_key().to_vec().into(),
                    )
                    .await
                    .to_not_found_response(UserErrors::MerchantIdNotFound)?;

                let merchant_id = state
                    .store
                    .find_merchant_account_by_merchant_id(
                        key_manager_state,
                        &request.merchant_id,
                        &merchant_key_store,
                    )
                    .await
                    .change_context(UserErrors::MerchantIdNotFound)?
                    .organization_id
                    .eq(&user_from_token.org_id)
                    .then(|| request.merchant_id.clone())
                    .ok_or_else(|| {
                        UserErrors::InvalidRoleOperationWithMessage(
                            "No such merchant_id found for the user in the org".to_string(),
                        )
                    })?;

                let profile_id = state
                    .store
                    .list_profile_by_merchant_id(
                        key_manager_state,
                        &merchant_key_store,
                        &merchant_id,
                    )
                    .await
                    .change_context(UserErrors::InternalServerError)
                    .attach_printable("Failed to list business profiles by merchant_id")?
                    .pop()
                    .ok_or(UserErrors::InternalServerError)
                    .attach_printable("No business profile found for the merchant_id")?
                    .get_id()
                    .to_owned();
                (
                    user_from_token.org_id.clone(),
                    merchant_id,
                    profile_id,
                    user_from_token.role_id.clone(),
                )
            }

            EntityType::Merchant | EntityType::Profile => {
                let user_role = state
                    .global_store
                    .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                        user_id: &user_from_token.user_id,
                        tenant_id: user_from_token
                            .tenant_id
                            .as_ref()
                            .unwrap_or(&state.tenant.tenant_id),
                        org_id: Some(&user_from_token.org_id),
                        merchant_id: Some(&request.merchant_id),
                        profile_id: None,
                        entity_id: None,
                        version: None,
                        status: Some(UserStatus::Active),
                        limit: Some(1),
                    })
                    .await
                    .change_context(UserErrors::InternalServerError)
                    .attach_printable(
                        "Failed to list user roles for the given user_id, org_id and merchant_id",
                    )?
                    .pop()
                    .ok_or(UserErrors::InvalidRoleOperationWithMessage(
                        "No user role associated with the requested merchant_id".to_string(),
                    ))?;

                let (merchant_id, profile_id) =
                    utils::user_role::get_single_merchant_id_and_profile_id(&state, &user_role)
                        .await?;
                (
                    user_from_token.org_id,
                    merchant_id,
                    profile_id,
                    user_role.role_id,
                )
            }
        }
    };

    let lineage_context = LineageContext {
        user_id: user_from_token.user_id.clone(),
        merchant_id: merchant_id.clone(),
        role_id: role_id.clone(),
        org_id: org_id.clone(),
        profile_id: profile_id.clone(),
        tenant_id: user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id)
            .clone(),
    };

    utils::user::spawn_async_lineage_context_update_to_db(
        &state,
        &user_from_token.user_id,
        lineage_context,
    );

    let token = utils::user::generate_jwt_auth_token_with_attributes(
        &state,
        user_from_token.user_id,
        merchant_id.clone(),
        org_id.clone(),
        role_id.clone(),
        profile_id,
        user_from_token.tenant_id.clone(),
    )
    .await?;

    utils::user_role::set_role_info_in_cache_by_role_id_org_id(
        &state,
        &role_id,
        &org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await;

    let response = user_api::TokenResponse {
        token: token.clone(),
        token_type: common_enums::TokenPurpose::UserInfo,
    };

    auth::cookies::set_cookie_response(response, token)
}

pub async fn switch_profile_for_user_in_org_and_merchant(
    state: SessionState,
    request: user_api::SwitchProfileRequest,
    user_from_token: auth::UserFromToken,
) -> UserResponse<user_api::TokenResponse> {
    if user_from_token.profile_id == request.profile_id {
        return Err(UserErrors::InvalidRoleOperationWithMessage(
            "User switching to same profile".to_string(),
        )
        .into());
    }

    let key_manager_state = &(&state).into();
    let role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
        &state,
        &user_from_token.role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .change_context(UserErrors::InternalServerError)
    .attach_printable("Failed to retrieve role information")?;

    let (profile_id, role_id) = match role_info.get_entity_type() {
        EntityType::Tenant | EntityType::Organization | EntityType::Merchant => {
            let merchant_key_store = state
                .store
                .get_merchant_key_store_by_merchant_id(
                    key_manager_state,
                    &user_from_token.merchant_id,
                    &state.store.get_master_key().to_vec().into(),
                )
                .await
                .change_context(UserErrors::InternalServerError)
                .attach_printable("Failed to retrieve merchant key store by merchant_id")?;

            let profile_id = state
                .store
                .find_business_profile_by_merchant_id_profile_id(
                    key_manager_state,
                    &merchant_key_store,
                    &user_from_token.merchant_id,
                    &request.profile_id,
                )
                .await
                .change_context(UserErrors::InvalidRoleOperationWithMessage(
                    "No such profile found for the merchant".to_string(),
                ))?
                .get_id()
                .to_owned();
            (profile_id, user_from_token.role_id)
        }

        EntityType::Profile => {
            let user_role = state
                .global_store
                .list_user_roles_by_user_id(ListUserRolesByUserIdPayload{
                    user_id:&user_from_token.user_id,
                    tenant_id: user_from_token
                        .tenant_id
                        .as_ref()
                        .unwrap_or(&state.tenant.tenant_id),
                    org_id: Some(&user_from_token.org_id),
                    merchant_id: Some(&user_from_token.merchant_id),
                    profile_id:Some(&request.profile_id),
                    entity_id: None,
                    version:None,
                    status: Some(UserStatus::Active),
                    limit: Some(1)
                }
                )
                .await
                .change_context(UserErrors::InternalServerError)
                .attach_printable("Failed to list user roles for the given user_id, org_id, merchant_id and profile_id")?
                .pop()
                .ok_or(UserErrors::InvalidRoleOperationWithMessage(
                    "No user role associated with the profile".to_string(),
                ))?;

            (request.profile_id, user_role.role_id)
        }
    };

    let lineage_context = LineageContext {
        user_id: user_from_token.user_id.clone(),
        merchant_id: user_from_token.merchant_id.clone(),
        role_id: role_id.clone(),
        org_id: user_from_token.org_id.clone(),
        profile_id: profile_id.clone(),
        tenant_id: user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id)
            .clone(),
    };

    utils::user::spawn_async_lineage_context_update_to_db(
        &state,
        &user_from_token.user_id,
        lineage_context,
    );

    let token = utils::user::generate_jwt_auth_token_with_attributes(
        &state,
        user_from_token.user_id,
        user_from_token.merchant_id.clone(),
        user_from_token.org_id.clone(),
        role_id.clone(),
        profile_id,
        user_from_token.tenant_id.clone(),
    )
    .await?;

    utils::user_role::set_role_info_in_cache_by_role_id_org_id(
        &state,
        &role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await;

    let response = user_api::TokenResponse {
        token: token.clone(),
        token_type: common_enums::TokenPurpose::UserInfo,
    };

    auth::cookies::set_cookie_response(response, token)
}

#[cfg(feature = "v1")]
pub async fn clone_connector(
    state: SessionState,
    request: user_api::CloneConnectorRequest,
) -> UserResponse<api_models::admin::MerchantConnectorResponse> {
    let Some(allowlist) = &state.conf.clone_connector_allowlist else {
        return Err(UserErrors::InvalidCloneConnectorOperation(
            "Cloning is not allowed".to_string(),
        )
        .into());
    };

    fp_utils::when(
        allowlist
            .merchant_ids
            .contains(&request.source.merchant_id)
            .not(),
        || {
            Err(UserErrors::InvalidCloneConnectorOperation(
                "Cloning is not allowed from this merchant".to_string(),
            ))
        },
    )?;

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

    let source_key_store = state
        .store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &request.source.merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(UserErrors::InvalidCloneConnectorOperation(
            "Source merchant account not found".to_string(),
        ))?;

    let source_mca = state
        .store
        .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
            key_manager_state,
            &request.source.merchant_id,
            &request.source.mca_id,
            &source_key_store,
        )
        .await
        .to_not_found_response(UserErrors::InvalidCloneConnectorOperation(
            "Source merchant connector account not found".to_string(),
        ))?;

    let source_mca_name = source_mca
        .connector_name
        .parse::<connector_enums::Connector>()
        .change_context(UserErrors::InternalServerError)
        .attach_printable("Invalid connector name received")?;

    fp_utils::when(
        allowlist.connector_names.contains(&source_mca_name).not(),
        || {
            Err(UserErrors::InvalidCloneConnectorOperation(
                "Cloning is not allowed for this connector".to_string(),
            ))
        },
    )?;

    let merchant_connector_create = utils::user::build_cloned_connector_create_request(
        source_mca,
        Some(request.destination.profile_id.clone()),
        request.destination.connector_label,
    )
    .await?;

    let destination_key_store = state
        .store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &request.destination.merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(UserErrors::InvalidCloneConnectorOperation(
            "Destination merchant account not found".to_string(),
        ))?;

    let destination_merchant_account = state
        .store
        .find_merchant_account_by_merchant_id(
            key_manager_state,
            &request.destination.merchant_id,
            &destination_key_store,
        )
        .await
        .to_not_found_response(UserErrors::InvalidCloneConnectorOperation(
            "Destination merchant account not found".to_string(),
        ))?;

    let destination_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
        destination_merchant_account,
        destination_key_store,
    )));

    admin::create_connector(
        state,
        merchant_connector_create,
        destination_context,
        Some(request.destination.profile_id),
    )
    .await
    .map_err(|e| {
        let message = e.current_context().error_message();
        e.change_context(UserErrors::ErrorCloningConnector(message))
    })
    .attach_printable("Failed to create cloned connector")
}


pub struct TokenizedWalletValue2 {
    pub customer_id: Option<id_type::CustomerId>,
}


File: crates/external_services/src/email/no_email.rs
 Public functions: 1
 Public structs: 1

use common_utils::{errors::CustomResult, pii};
use router_env::logger;

use crate::email::{EmailClient, EmailError, EmailResult, IntermediateString};

/// Client when email support is disabled
#[derive(Debug, Clone, Default, serde::Deserialize)]
pub struct NoEmailClient {}

impl NoEmailClient {
    /// Constructs a new client when email is disabled
    pub async fn create() -> Self {
        Self {}
    }
}

#[async_trait::async_trait]
impl EmailClient for NoEmailClient {
    type RichText = String;
    fn convert_to_rich_text(
        &self,
        intermediate_string: IntermediateString,
    ) -> CustomResult<Self::RichText, EmailError> {
        Ok(intermediate_string.into_inner())
    }

    async fn send_email(
        &self,
        _recipient: pii::Email,
        _subject: String,
        _body: Self::RichText,
        _proxy_url: Option<&String>,
    ) -> EmailResult<()> {
        logger::info!("Email not sent as email support is disabled, please enable any of the supported email clients to send emails");
        Ok(())
    }
}


pub struct AirwallexPaymentsRequest {
    // Unique ID to be sent for each transaction/operation request to the connector
    request_id: String,
    payment_method: AirwallexPaymentMethod,
    payment_method_options: Option<AirwallexPaymentOptions>,
    return_url: Option<String>,
    device_data: DeviceData,
}


pub async fn make_stream_available(&self, stream_name_flag: &str) -> errors::DrainerResult<()> {
        match self.redis_conn.delete_key(&stream_name_flag.into()).await {
            Ok(redis::DelReply::KeyDeleted) => Ok(()),
            Ok(redis::DelReply::KeyNotDeleted) => {
                logger::error!("Tried to unlock a stream which is already unlocked");
                Ok(())
            }
            Err(error) => Err(errors::DrainerError::from(error).into()),
        }
    }


impl api::PaymentToken for Bamboraapac {}

impl api::PaymentToken for for Bamboraapac


File: crates/router/src/core/webhooks/webhook_events.rs
 Public functions: 3

use std::collections::HashSet;

use common_utils::{self, errors::CustomResult, fp_utils};
use error_stack::ResultExt;
use masking::PeekInterface;
use router_env::{instrument, tracing};

use crate::{
    core::errors::{self, RouterResponse, StorageErrorExt},
    routes::SessionState,
    services::ApplicationResponse,
    types::{api, domain, storage, transformers::ForeignTryFrom},
    utils::{OptionExt, StringExt},
};

const INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT: i64 = 100;
const INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS: i64 = 90;

#[derive(Debug)]
enum MerchantAccountOrProfile {
    MerchantAccount(Box<domain::MerchantAccount>),
    Profile(Box<domain::Profile>),
}

#[instrument(skip(state))]
pub async fn list_initial_delivery_attempts(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    api_constraints: api::webhook_events::EventListConstraints,
) -> RouterResponse<api::webhook_events::TotalEventsResponse> {
    let profile_id = api_constraints.profile_id.clone();
    let constraints = api::webhook_events::EventListConstraintsInternal::foreign_try_from(
        api_constraints.clone(),
    )?;

    let store = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let (account, key_store) =
        get_account_and_key_store(state.clone(), merchant_id.clone(), profile_id.clone()).await?;

    let now = common_utils::date_time::now();
    let events_list_begin_time =
        (now.date() - time::Duration::days(INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS)).midnight();

    let (events, total_count) = match constraints {
        api_models::webhook_events::EventListConstraintsInternal::ObjectIdFilter { object_id } => {
            let events = match account {
                MerchantAccountOrProfile::MerchantAccount(merchant_account) => {
                    store
                        .list_initial_events_by_merchant_id_primary_object_id(
                            key_manager_state,
                            merchant_account.get_id(),
                            &object_id,
                            &key_store,
                        )
                        .await
                }
                MerchantAccountOrProfile::Profile(business_profile) => {
                    store
                        .list_initial_events_by_profile_id_primary_object_id(
                            key_manager_state,
                            business_profile.get_id(),
                            &object_id,
                            &key_store,
                        )
                        .await
                }
            }
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to list events with specified constraints")?;

            let total_count = i64::try_from(events.len())
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error while converting from usize to i64")?;
            (events, total_count)
        }
        api_models::webhook_events::EventListConstraintsInternal::GenericFilter {
            created_after,
            created_before,
            limit,
            offset,
            event_classes,
            event_types,
            is_delivered,
        } => {
            let limit = match limit {
                Some(limit) if  limit <= INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT => Ok(Some(limit)),
                Some(limit) if limit > INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT => Err(
                    errors::ApiErrorResponse::InvalidRequestData{
                        message: format!("`limit` must be a number less than {INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT}")
                    }
                ),
                _  => Ok(Some(INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT)),
            }?;
            let offset = match offset {
                Some(offset) if offset > 0 => Some(offset),
                _ => None,
            };

            let event_classes = event_classes.unwrap_or(HashSet::new());
            let mut event_types = event_types.unwrap_or(HashSet::new());
            if !event_classes.is_empty() {
                event_types = finalize_event_types(event_classes, event_types).await?;
            }

            fp_utils::when(
                !created_after
                    .zip(created_before)
                    .map(|(created_after, created_before)| created_after <= created_before)
                    .unwrap_or(true),
                || {
                    Err(errors::ApiErrorResponse::InvalidRequestData { message: "The `created_after` timestamp must be an earlier timestamp compared to the `created_before` timestamp".to_string() })
                },
            )?;

            let created_after = match created_after {
                Some(created_after) => {
                    if created_after < events_list_begin_time {
                        Err(errors::ApiErrorResponse::InvalidRequestData { message: format!("`created_after` must be a timestamp within the past {INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS} days.") })
                    } else {
                        Ok(created_after)
                    }
                }
                None => Ok(events_list_begin_time),
            }?;

            let created_before = match created_before {
                Some(created_before) => {
                    if created_before < events_list_begin_time {
                        Err(errors::ApiErrorResponse::InvalidRequestData { message: format!("`created_before` must be a timestamp within the past {INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS} days.") })
                    } else {
                        Ok(created_before)
                    }
                }
                None => Ok(now),
            }?;

            let events = match account {
                MerchantAccountOrProfile::MerchantAccount(merchant_account) => {
                    store
                        .list_initial_events_by_merchant_id_constraints(
                            key_manager_state,
                            merchant_account.get_id(),
                            created_after,
                            created_before,
                            limit,
                            offset,
                            event_types.clone(),
                            is_delivered,
                            &key_store,
                        )
                        .await
                }
                MerchantAccountOrProfile::Profile(business_profile) => {
                    store
                        .list_initial_events_by_profile_id_constraints(
                            key_manager_state,
                            business_profile.get_id(),
                            created_after,
                            created_before,
                            limit,
                            offset,
                            event_types.clone(),
                            is_delivered,
                            &key_store,
                        )
                        .await
                }
            }
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to list events with specified constraints")?;

            let total_count = store
                .count_initial_events_by_constraints(
                    &merchant_id,
                    profile_id,
                    created_after,
                    created_before,
                    event_types,
                    is_delivered,
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to get total events count")?;

            (events, total_count)
        }
    };

    let events = events
        .into_iter()
        .map(api::webhook_events::EventListItemResponse::try_from)
        .collect::<Result<Vec<_>, _>>()?;

    Ok(ApplicationResponse::Json(
        api::webhook_events::TotalEventsResponse::new(total_count, events),
    ))
}

#[instrument(skip(state))]
pub async fn list_delivery_attempts(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    initial_attempt_id: String,
) -> RouterResponse<Vec<api::webhook_events::EventRetrieveResponse>> {
    let store = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let key_store = store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let events = store
        .list_events_by_merchant_id_initial_attempt_id(
            key_manager_state,
            &merchant_id,
            &initial_attempt_id,
            &key_store,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to list delivery attempts for initial event")?;

    if events.is_empty() {
        Err(error_stack::report!(
            errors::ApiErrorResponse::EventNotFound
        ))
        .attach_printable("No delivery attempts found with the specified `initial_attempt_id`")
    } else {
        Ok(ApplicationResponse::Json(
            events
                .into_iter()
                .map(api::webhook_events::EventRetrieveResponse::try_from)
                .collect::<Result<Vec<_>, _>>()?,
        ))
    }
}

#[instrument(skip(state))]
#[cfg(feature = "v1")]
pub async fn retry_delivery_attempt(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    event_id: String,
) -> RouterResponse<api::webhook_events::EventRetrieveResponse> {
    let store = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let key_store = store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let event_to_retry = store
        .find_event_by_merchant_id_event_id(
            key_manager_state,
            &key_store.merchant_id,
            &event_id,
            &key_store,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::EventNotFound)?;

    let business_profile_id = event_to_retry
        .business_profile_id
        .get_required_value("business_profile_id")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to read business profile ID from event to retry")?;
    let business_profile = store
        .find_business_profile_by_profile_id(key_manager_state, &key_store, &business_profile_id)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to find business profile")?;

    let delivery_attempt = storage::enums::WebhookDeliveryAttempt::ManualRetry;
    let new_event_id = super::utils::generate_event_id();
    let idempotent_event_id = super::utils::get_idempotent_event_id(
        &event_to_retry.primary_object_id,
        event_to_retry.event_type,
        delivery_attempt,
    );

    let now = common_utils::date_time::now();
    let new_event = domain::Event {
        event_id: new_event_id.clone(),
        event_type: event_to_retry.event_type,
        event_class: event_to_retry.event_class,
        is_webhook_notified: false,
        primary_object_id: event_to_retry.primary_object_id,
        primary_object_type: event_to_retry.primary_object_type,
        created_at: now,
        merchant_id: Some(business_profile.merchant_id.clone()),
        business_profile_id: Some(business_profile.get_id().to_owned()),
        primary_object_created_at: event_to_retry.primary_object_created_at,
        idempotent_event_id: Some(idempotent_event_id),
        initial_attempt_id: event_to_retry.initial_attempt_id,
        request: event_to_retry.request,
        response: None,
        delivery_attempt: Some(delivery_attempt),
        metadata: event_to_retry.metadata,
        is_overall_delivery_successful: Some(false),
    };

    let event = store
        .insert_event(key_manager_state, new_event, &key_store)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to insert event")?;

    // We only allow retrying deliveries for events with `request` populated.
    let request_content = event
        .request
        .as_ref()
        .get_required_value("request")
        .change_context(errors::ApiErrorResponse::InternalServerError)?
        .peek()
        .parse_struct("OutgoingWebhookRequestContent")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to parse webhook event request information")?;

    Box::pin(super::outgoing::trigger_webhook_and_raise_event(
        state.clone(),
        business_profile,
        &key_store,
        event,
        request_content,
        delivery_attempt,
        None,
        None,
    ))
    .await;

    let updated_event = store
        .find_event_by_merchant_id_event_id(
            key_manager_state,
            &key_store.merchant_id,
            &new_event_id,
            &key_store,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::EventNotFound)?;

    Ok(ApplicationResponse::Json(
        api::webhook_events::EventRetrieveResponse::try_from(updated_event)?,
    ))
}

async fn get_account_and_key_store(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    profile_id: Option<common_utils::id_type::ProfileId>,
) -> errors::RouterResult<(MerchantAccountOrProfile, domain::MerchantKeyStore)> {
    let store = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let merchant_key_store = store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    match profile_id {
        // If profile ID is specified, return business profile, since a business profile is more
        // specific than a merchant account.
        Some(profile_id) => {
            let business_profile = store
                .find_business_profile_by_merchant_id_profile_id(
                    key_manager_state,
                    &merchant_key_store,
                    &merchant_id,
                    &profile_id,
                )
                .await
                .attach_printable_lazy(|| {
                    format!(
                        "Failed to find business profile by merchant_id `{merchant_id:?}` and profile_id `{profile_id:?}`. \
                        The merchant_id associated with the business profile `{profile_id:?}` may be \
                        different than the merchant_id specified (`{merchant_id:?}`)."
                    )
                })
                .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
                    id: profile_id.get_string_repr().to_owned(),
                })?;

            Ok((
                MerchantAccountOrProfile::Profile(Box::new(business_profile)),
                merchant_key_store,
            ))
        }

        None => {
            let merchant_account = store
                .find_merchant_account_by_merchant_id(
                    key_manager_state,
                    &merchant_id,
                    &merchant_key_store,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

            Ok((
                MerchantAccountOrProfile::MerchantAccount(Box::new(merchant_account)),
                merchant_key_store,
            ))
        }
    }
}

async fn finalize_event_types(
    event_classes: HashSet<common_enums::EventClass>,
    mut event_types: HashSet<common_enums::EventType>,
) -> CustomResult<HashSet<common_enums::EventType>, errors::ApiErrorResponse> {
    // Examples:
    // 1. event_classes = ["payments", "refunds"], event_types = ["payment_succeeded"]
    // 2. event_classes = ["refunds"], event_types = ["payment_succeeded"]

    // Create possible_event_types based on event_classes
    // Example 1: possible_event_types = ["payment_*", "refund_*"]
    // Example 2: possible_event_types = ["refund_*"]
    let possible_event_types = event_classes
        .clone()
        .into_iter()
        .flat_map(common_enums::EventClass::event_types)
        .collect::<HashSet<_>>();

    if event_types.is_empty() {
        return Ok(possible_event_types);
    }

    // Extend event_types if disjoint with event_classes
    // Example 1: event_types = ["payment_succeeded", "refund_*"], is_disjoint is used to extend "refund_*" and ignore "payment_*".
    // Example 2: event_types = ["payment_succeeded", "refund_*"], is_disjoint is only used to extend "refund_*".
    event_classes.into_iter().for_each(|class| {
        let valid_event_types = class.event_types();
        if event_types.is_disjoint(&valid_event_types) {
            event_types.extend(valid_event_types);
        }
    });

    // Validate event_types is a subset of possible_event_types
    // Example 1: event_types is a subset of possible_event_types (valid)
    // Example 2: event_types is not a subset of possible_event_types (error due to "payment_succeeded")
    if !event_types.is_subset(&possible_event_types) {
        return Err(error_stack::report!(
            errors::ApiErrorResponse::InvalidRequestData {
                message: "`event_types` must be a subset of `event_classes`".to_string(),
            }
        ));
    }

    Ok(event_types.clone())
}


impl api::PaymentCapture for Katapult {}

impl api::PaymentCapture for for Katapult


impl Headers {
    pub fn as_map(&self) -> &HashMap<String, String> {
        &self.0
    }

    pub fn from_header_map(headers: Option<&HeaderMap>) -> Self {
        headers
            .map(|h| {
                let map = h
                    .iter()
                    .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
                    .collect();
                Self(map)
            })
            .unwrap_or_else(|| Self(HashMap::new()))
    }
}


/// Convert the amount to its base denomination based on Currency and return String
    pub fn to_currency_base_unit(self, amount: i64) -> Result<String, TryFromIntError> {
        let amount_f64 = self.to_currency_base_unit_asf64(amount)?;
        Ok(format!("{amount_f64:.2}"))
    }


pub struct PlacetopayAuthType {
    login: Secret<String>,
    tran_key: Secret<String>,
}


pub struct ErrorResponse {
    pub error_id: Option<String>,
    pub errors: Vec<Error>,
}


/// Macro to implement From trait for types wrapped in RequiredFromNullableWithDefault
    #[macro_export]
    macro_rules! impl_from_required_from_nullable_with_default {
        ($($type:ty),* $(,)?) => {
            $(
                impl From<$crate::RequiredFromNullableWithDefault<$type>> for $type {
                    fn from(wrapper: $crate::RequiredFromNullableWithDefault<$type>) -> Self {
                        wrapper.into_inner()
                    }
                }
            )*
        };
    }


pub struct DummyConnectorErrorResponse {
    pub error: ErrorData,
}


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


impl UnifiedConnectorServiceClient {
    /// Builds the connection to the gRPC service
    pub async fn build_connections(config: &GrpcClientSettings) -> Option<Self> {
        match &config.unified_connector_service {
            Some(unified_connector_service_client_config) => {
                let uri: Uri = match unified_connector_service_client_config
                    .base_url
                    .get_string_repr()
                    .parse()
                {
                    Ok(parsed_uri) => parsed_uri,
                    Err(err) => {
                        logger::error!(error = ?err, "Failed to parse URI for Unified Connector Service");
                        return None;
                    }
                };

                let connect_result = timeout(
                    Duration::from_secs(unified_connector_service_client_config.connection_timeout),
                    PaymentServiceClient::connect(uri),
                )
                .await;

                match connect_result {
                    Ok(Ok(client)) => {
                        logger::info!("Successfully connected to Unified Connector Service");
                        Some(Self { client })
                    }
                    Ok(Err(err)) => {
                        logger::error!(error = ?err, "Failed to connect to Unified Connector Service");
                        None
                    }
                    Err(err) => {
                        logger::error!(error = ?err, "Connection to Unified Connector Service timed out");
                        None
                    }
                }
            }
            None => {
                router_env::logger::error!(?config.unified_connector_service, "Unified Connector Service config is missing");
                None
            }
        }
    }

    /// Performs Payment Authorize
    pub async fn payment_authorize(
        &self,
        payment_authorize_request: payments_grpc::PaymentServiceAuthorizeRequest,
        connector_auth_metadata: ConnectorAuthMetadata,
        external_vault_proxy_metadata: Option<String>,
        grpc_headers: GrpcHeaders,
    ) -> UnifiedConnectorServiceResult<tonic::Response<PaymentServiceAuthorizeResponse>> {
        let mut request = tonic::Request::new(payment_authorize_request);

        let connector_name = connector_auth_metadata.connector_name.clone();
        let metadata = build_unified_connector_service_grpc_headers(
            connector_auth_metadata,
            external_vault_proxy_metadata,
            grpc_headers,
        )?;

        *request.metadata_mut() = metadata;

        self.client
            .clone()
            .authorize(request)
            .await
            .change_context(UnifiedConnectorServiceError::PaymentAuthorizeFailure)
            .inspect_err(|error| {
                logger::error!(
                    grpc_error=?error,
                    method="payment_authorize",
                    connector_name=?connector_name,
                    "UCS payment authorize gRPC call failed"
                )
            })
    }

    /// Performs Payment Sync/Get
    pub async fn payment_get(
        &self,
        payment_get_request: payments_grpc::PaymentServiceGetRequest,
        connector_auth_metadata: ConnectorAuthMetadata,
        grpc_headers: GrpcHeaders,
    ) -> UnifiedConnectorServiceResult<tonic::Response<payments_grpc::PaymentServiceGetResponse>>
    {
        let mut request = tonic::Request::new(payment_get_request);

        let connector_name = connector_auth_metadata.connector_name.clone();
        let metadata = build_unified_connector_service_grpc_headers(
            connector_auth_metadata,
            None,
            grpc_headers,
        )?;
        *request.metadata_mut() = metadata;

        self.client
            .clone()
            .get(request)
            .await
            .change_context(UnifiedConnectorServiceError::PaymentGetFailure)
            .inspect_err(|error| {
                logger::error!(
                    grpc_error=?error,
                    method="payment_get",
                    connector_name=?connector_name,
                    "UCS payment get/sync gRPC call failed"
                )
            })
    }

    /// Performs Payment Setup Mandate
    pub async fn payment_setup_mandate(
        &self,
        payment_register_request: payments_grpc::PaymentServiceRegisterRequest,
        connector_auth_metadata: ConnectorAuthMetadata,
        grpc_headers: GrpcHeaders,
    ) -> UnifiedConnectorServiceResult<tonic::Response<payments_grpc::PaymentServiceRegisterResponse>>
    {
        let mut request = tonic::Request::new(payment_register_request);

        let connector_name = connector_auth_metadata.connector_name.clone();
        let metadata = build_unified_connector_service_grpc_headers(
            connector_auth_metadata,
            None,
            grpc_headers,
        )?;
        *request.metadata_mut() = metadata;

        self.client
            .clone()
            .register(request)
            .await
            .change_context(UnifiedConnectorServiceError::PaymentRegisterFailure)
            .inspect_err(|error| {
                logger::error!(
                    grpc_error=?error,
                    method="payment_setup_mandate",
                    connector_name=?connector_name,
                    "UCS payment setup mandate gRPC call failed"
                )
            })
    }

    /// Performs Payment repeat (MIT - Merchant Initiated Transaction).
    pub async fn payment_repeat(
        &self,
        payment_repeat_request: payments_grpc::PaymentServiceRepeatEverythingRequest,
        connector_auth_metadata: ConnectorAuthMetadata,
        grpc_headers: GrpcHeaders,
    ) -> UnifiedConnectorServiceResult<
        tonic::Response<payments_grpc::PaymentServiceRepeatEverythingResponse>,
    > {
        let mut request = tonic::Request::new(payment_repeat_request);

        let connector_name = connector_auth_metadata.connector_name.clone();
        let metadata = build_unified_connector_service_grpc_headers(
            connector_auth_metadata,
            None,
            grpc_headers,
        )?;
        *request.metadata_mut() = metadata;

        self.client
            .clone()
            .repeat_everything(request)
            .await
            .change_context(UnifiedConnectorServiceError::PaymentRepeatEverythingFailure)
            .inspect_err(|error| {
                logger::error!(
                    grpc_error=?error,
                    method="payment_repeat",
                    connector_name=?connector_name,
                    "UCS payment repeat gRPC call failed"
                )
            })
    }

    /// Transforms incoming webhook through UCS
    pub async fn transform_incoming_webhook(
        &self,
        webhook_transform_request: PaymentServiceTransformRequest,
        connector_auth_metadata: ConnectorAuthMetadata,
        grpc_headers: GrpcHeaders,
    ) -> UnifiedConnectorServiceResult<tonic::Response<PaymentServiceTransformResponse>> {
        let mut request = tonic::Request::new(webhook_transform_request);

        let connector_name = connector_auth_metadata.connector_name.clone();
        let metadata = build_unified_connector_service_grpc_headers(
            connector_auth_metadata,
            None,
            grpc_headers,
        )?;
        *request.metadata_mut() = metadata;

        self.client
            .clone()
            .transform(request)
            .await
            .change_context(UnifiedConnectorServiceError::WebhookTransformFailure)
            .inspect_err(|error| {
                logger::error!(
                    grpc_error=?error,
                    method="transform_incoming_webhook",
                    connector_name=?connector_name,
                    "UCS webhook transform gRPC call failed"
                )
            })
    }
}


pub trait AuthServiceBankAccountCredentials:
    super::ConnectorIntegration<
    BankAccountCredentials,
    BankAccountCredentialsRequest,
    BankAccountCredentialsResponse,
>
{
}


impl Shift4AuthorizePreprocessingCommon for PaymentsPreProcessingData {
    fn get_email_optional(&self) -> Option<pii::Email> {
        self.email.clone()
    }

    fn get_complete_authorize_url(&self) -> Option<String> {
        self.complete_authorize_url.clone()
    }

    fn get_currency_required(&self) -> Result<enums::Currency, Error> {
        self.get_currency()
    }
    fn get_payment_method_data_required(&self) -> Result<PaymentMethodData, Error> {
        self.payment_method_data.clone().ok_or(
            errors::ConnectorError::MissingRequiredField {
                field_name: "payment_method_data",
            }
            .into(),
        )
    }
    fn is_automatic_capture(&self) -> Result<bool, Error> {
        self.is_auto_capture()
    }

    fn get_router_return_url(&self) -> Option<String> {
        self.router_return_url.clone()
    }
    fn get_metadata(
        &self,
    ) -> Result<Option<serde_json::Value>, error_stack::Report<errors::ConnectorError>> {
        Ok(None)
    }
}

impl Shift4AuthorizePreprocessingCommon for for PaymentsPreProcessingData


OpenAPI Block Path: components.schemas.PayseraData
{
  "type": "object"
}


pub struct TokenizedBankSensitiveValues {
    pub bank_account_number: Option<masking::Secret<String>>,
    pub bank_routing_number: Option<masking::Secret<String>>,
    pub bic: Option<masking::Secret<String>>,
    pub bank_sort_code: Option<masking::Secret<String>>,
    pub iban: Option<masking::Secret<String>>,
    pub pix_key: Option<masking::Secret<String>>,
    pub tax_id: Option<masking::Secret<String>>,
}


pub async fn perform_session_token_routing<F, D>(
    state: SessionState,
    business_profile: &domain::Profile,
    merchant_context: domain::MerchantContext,
    payment_data: &D,
    connectors: api::SessionConnectorDatas,
) -> RouterResult<SessionTokenRoutingResult>
where
    F: Clone,
    D: OperationSessionGetters<F>,
{
    let chosen = connectors.apply_filter_for_session_routing();
    let sfr = SessionFlowRoutingInput {
        country: payment_data
            .get_payment_intent()
            .billing_address
            .as_ref()
            .and_then(|address| address.get_inner().address.as_ref())
            .and_then(|details| details.country),
        payment_intent: payment_data.get_payment_intent(),

        chosen,
    };
    let result = self_routing::perform_session_flow_routing(
        &state,
        merchant_context.get_merchant_key_store(),
        sfr,
        business_profile,
        &enums::TransactionType::Payment,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("error performing session flow routing")?;

    let final_list = connectors.filter_and_validate_for_session_flow(&result)?;
    Ok(SessionTokenRoutingResult {
        final_result: final_list,
        routing_result: result,
    })
}


pub fn get_role_name(&self) -> &str {
        &self.role_name
    }


pub struct BankOfAmericaPaymentsRequest {
    processing_information: ProcessingInformation,
    payment_information: PaymentInformation,
    order_information: OrderInformationWithBill,
    client_reference_information: ClientReferenceInformation,
    #[serde(skip_serializing_if = "Option::is_none")]
    consumer_authentication_information: Option<BankOfAmericaConsumerAuthInformation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    merchant_defined_information: Option<Vec<MerchantDefinedInformation>>,
}


pub struct PayuProductData {
    name: String,
    unit_price: String,
    quantity: String,
    #[serde(rename = "virtual")]
    virtually: Option<bool>,
    listing_date: Option<String>,
}


File: crates/router_env/tests/env.rs

#![allow(clippy::print_stdout)]

#[cfg(feature = "vergen")]
use router_env as env;

#[cfg(feature = "vergen")]
#[tokio::test]
async fn basic() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    println!("CARGO_PKG_VERSION : {:?}", env!("CARGO_PKG_VERSION"));
    println!("CARGO_PROFILE : {:?}", env!("CARGO_PROFILE"));

    println!(
        "GIT_COMMIT_TIMESTAMP : {:?}",
        env!("VERGEN_GIT_COMMIT_TIMESTAMP")
    );
    println!("GIT_SHA : {:?}", env!("VERGEN_GIT_SHA"));
    println!("RUSTC_SEMVER : {:?}", env!("VERGEN_RUSTC_SEMVER"));
    println!(
        "CARGO_TARGET_TRIPLE : {:?}",
        env!("VERGEN_CARGO_TARGET_TRIPLE")
    );

    Ok(())
}

#[cfg(feature = "vergen")]
#[tokio::test]
async fn env_macro() {
    println!("version : {:?}", env::version!());
    println!("build : {:?}", env::build!());
    println!("commit : {:?}", env::commit!());
    // println!("platform : {:?}", env::platform!());

    assert!(!env::version!().is_empty());
    assert!(!env::build!().is_empty());
    assert!(!env::commit!().is_empty());
    // assert!(env::platform!().len() > 0);
}


pub async fn incoming_webhooks_wrapper<W: types::OutgoingWebhookType>(
    flow: &impl router_env::types::FlowMetric,
    state: SessionState,
    req_state: ReqState,
    req: &actix_web::HttpRequest,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    connector_id: &common_utils::id_type::MerchantConnectorAccountId,
    body: actix_web::web::Bytes,
    is_relay_webhook: bool,
) -> RouterResponse<serde_json::Value> {
    let start_instant = Instant::now();
    let (application_response, webhooks_response_tracker, serialized_req) =
        Box::pin(incoming_webhooks_core::<W>(
            state.clone(),
            req_state,
            req,
            merchant_context.clone(),
            profile,
            connector_id,
            body.clone(),
            is_relay_webhook,
        ))
        .await?;

    logger::info!(incoming_webhook_payload = ?serialized_req);

    let request_duration = Instant::now()
        .saturating_duration_since(start_instant)
        .as_millis();

    let request_id = RequestId::extract(req)
        .await
        .attach_printable("Unable to extract request id from request")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    let auth_type = auth::AuthenticationType::WebhookAuth {
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
    };
    let status_code = 200;
    let api_event = ApiEventsType::Webhooks {
        connector: connector_id.clone(),
        payment_id: webhooks_response_tracker.get_payment_id(),
    };
    let response_value = serde_json::to_value(&webhooks_response_tracker)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Could not convert webhook effect to string")?;

    let infra = state.infra_components.clone();

    let api_event = ApiEvent::new(
        state.tenant.tenant_id.clone(),
        Some(merchant_context.get_merchant_account().get_id().clone()),
        flow,
        &request_id,
        request_duration,
        status_code,
        serialized_req,
        Some(response_value),
        None,
        auth_type,
        None,
        api_event,
        req,
        req.method(),
        infra,
    );
    state.event_handler().log_event(&api_event);
    Ok(application_response)
}


pub struct GetnetCancelResponse {
    payment: CancelResponseData,
}


pub struct FrmMerchantAccount {
    pub merchant_id: common_utils::id_type::MerchantId,
}


}

pub async fn retry_delete_tokenize(
    db: &dyn db::StorageInterface,
    pm: enums::PaymentMethod,
    pt: storage::ProcessTracker,
) -> Result<(), errors::ProcessTrackerError> {
    let schedule_time = get_delete_tokenize_schedule_time(db, pm, pt.retry_count).await;

    match schedule_time {
        Some(s_time) => {
            let retry_schedule = db
                .as_scheduler()
                .retry_process(pt, s_time)
                .await
                .map_err(Into::into);
            metrics::TASKS_RESET_COUNT.add(
                1,
                router_env::metric_attributes!(("flow", "DeleteTokenizeData")),
            );
            retry_schedule
        }
        None => db
            .as_scheduler()
            .finish_process_with_business_status(
                pt,
                diesel_models::process_tracker::business_status::RETRIES_EXCEEDED,
            )
            .await
            .map_err(Into::into),
    }
}

// Fallback logic of old temp locker needs to be removed later


pub fn get_shipping_address(&self) -> Option<Address> {
        self.shipping.clone()
    }


pub struct FiuuRefundRequest {
    pub refund_type: RefundType,
    #[serde(rename = "MerchantID")]
    pub merchant_id: Secret<String>,
    #[serde(rename = "RefID")]
    pub ref_id: String,
    #[serde(rename = "TxnID")]
    pub txn_id: String,
    pub amount: StringMajorUnit,
    pub signature: Secret<String>,
    #[serde(rename = "notify_url")]
    pub notify_url: Option<Url>,
}


OpenAPI Block Path: components.schemas.OrderDetailsWithAmount
{
  "type": "object",
  "required": [
    "product_name",
    "quantity",
    "amount"
  ],
  "properties": {
    "product_name": {
      "type": "string",
      "description": "Name of the product that is being purchased",
      "example": "shirt",
      "maxLength": 255
    },
    "quantity": {
      "type": "integer",
      "format": "int32",
      "description": "The quantity of the product to be purchased",
      "example": 1,
      "minimum": 0
    },
    "amount": {
      "type": "integer",
      "format": "int64",
      "description": "the amount per quantity of product"
    },
    "tax_rate": {
      "type": "number",
      "format": "double",
      "description": "tax rate applicable to the product",
      "nullable": true
    },
    "total_tax_amount": {
      "type": "integer",
      "format": "int64",
      "description": "total tax amount applicable to the product",
      "nullable": true
    },
    "requires_shipping": {
      "type": "boolean",
      "nullable": true
    },
    "product_img_link": {
      "type": "string",
      "description": "The image URL of the product",
      "nullable": true
    },
    "product_id": {
      "type": "string",
      "description": "ID of the product that is being purchased",
      "nullable": true
    },
    "category": {
      "type": "string",
      "description": "Category of the product that is being purchased",
      "nullable": true
    },
    "sub_category": {
      "type": "string",
      "description": "Sub category of the product that is being purchased",
      "nullable": true
    },
    "brand": {
      "type": "string",
      "description": "Brand of the product that is being purchased",
      "nullable": true
    },
    "product_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/ProductType"
        }
      ],
      "nullable": true
    },
    "product_tax_code": {
      "type": "string",
      "description": "The tax code for the product",
      "nullable": true
    },
    "description": {
      "type": "string",
      "description": "Description for the item",
      "nullable": true
    },
    "sku": {
      "type": "string",
      "description": "Stock Keeping Unit (SKU) or the item identifier for this item.",
      "nullable": true
    },
    "upc": {
      "type": "string",
      "description": "Universal Product Code for the item.",
      "nullable": true
    },
    "commodity_code": {
      "type": "string",
      "description": "Code describing a commodity or a group of commodities pertaining to goods classification.",
      "nullable": true
    },
    "unit_of_measure": {
      "type": "string",
      "description": "Unit of measure used for the item quantity.",
      "nullable": true
    },
    "total_amount": {
      "type": "integer",
      "format": "int64",
      "description": "Total amount for the item.",
      "nullable": true
    },
    "unit_discount_amount": {
      "type": "integer",
      "format": "int64",
      "description": "Discount amount applied to this item.",
      "nullable": true
    }
  }
}


pub struct CardToken {
    #[serde(rename = "type")]
    pub payment_type: PaymentType,
    pub href: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cvc: Option<Secret<String>>,
}


pub struct MockTokenizeDBValue {
    pub value1: String,
    pub value2: String,
}


pub trait GetLockingInput {
    fn get_locking_input<F>(&self, flow: F) -> LockAction
    where
        F: router_env::types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>;
}


impl ApiEventMetric for GenerateFingerprintResponsePayload {}

impl ApiEventMetric for for GenerateFingerprintResponsePayload


OpenAPI Block Path: components.schemas.PaymentsCaptureRequest
{
  "type": "object",
  "properties": {
    "merchant_id": {
      "type": "string",
      "description": "The unique identifier for the merchant. This is usually inferred from the API key.",
      "nullable": true
    },
    "amount_to_capture": {
      "type": "integer",
      "format": "int64",
      "description": "The amount to capture, in the lowest denomination of the currency. If omitted, the entire `amount_capturable` of the payment will be captured. Must be less than or equal to the current `amount_capturable`.",
      "example": 6540,
      "nullable": true
    },
    "refund_uncaptured_amount": {
      "type": "boolean",
      "description": "Decider to refund the uncaptured amount. (Currently not fully supported or behavior may vary by connector).",
      "nullable": true
    },
    "statement_descriptor_suffix": {
      "type": "string",
      "description": "A dynamic suffix that appears on your customer's credit card statement. This is concatenated with the (shortened) descriptor prefix set on your account to form the complete statement descriptor. The combined length should not exceed connector-specific limits (typically 22 characters).",
      "nullable": true
    },
    "statement_descriptor_prefix": {
      "type": "string",
      "description": "An optional prefix for the statement descriptor that appears on your customer's credit card statement. This can override the default prefix set on your merchant account. The combined length of prefix and suffix should not exceed connector-specific limits (typically 22 characters).",
      "nullable": true
    },
    "merchant_connector_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MerchantConnectorDetailsWrap"
        }
      ],
      "nullable": true
    }
  }
}


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


pub struct UnifiedAuthenticationServiceAuthenticateConfirmationRequest {
    pub authenticate_by: String,
    pub source_authentication_id: common_utils::id_type::AuthenticationId,
    pub auth_creds: UnifiedAuthenticationServiceAuthType,
    pub x_src_flow_id: Option<String>,
    pub transaction_amount: Option<FloatMajorUnit>,
    pub transaction_currency: Option<enums::Currency>,
    pub checkout_event_type: Option<String>,
    pub checkout_event_status: Option<String>,
    pub confirmation_status: Option<String>,
    pub confirmation_reason: Option<String>,
    pub confirmation_timestamp: Option<String>,
    pub network_authorization_code: Option<String>,
    pub network_transaction_identifier: Option<String>,
    pub correlation_id: Option<String>,
    pub merchant_transaction_id: Option<String>,
}


            | PaymentAction::PaymentAuthenticateCompleteAuthorize => {
                Ok(CallConnectorAction::Trigger)
            }
        }
    }
}

static GLOBALPAY_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(|| {
        let supported_capture_methods = vec![
            enums::CaptureMethod::Automatic,
            enums::CaptureMethod::Manual,
            enums::CaptureMethod::SequentialAutomatic,
        ];

        let supported_card_network = vec![
            common_enums::CardNetwork::Visa,
            common_enums::CardNetwork::Mastercard,
            common_enums::CardNetwork::AmericanExpress,
            common_enums::CardNetwork::DinersClub,
            common_enums::CardNetwork::Discover,
            common_enums::CardNetwork::Interac,
            common_enums::CardNetwork::JCB,
            common_enums::CardNetwork::CartesBancaires,
            common_enums::CardNetwork::UnionPay,
        ];

        let mut globalpay_supported_payment_methods = SupportedPaymentMethods::new();

        globalpay_supported_payment_methods.add(
            enums::PaymentMethod::Card,
            enums::PaymentMethodType::Credit,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::Supported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::NotSupported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network.clone(),
                        }
                    }),
                ),
            },
        );

        globalpay_supported_payment_methods.add(
            enums::PaymentMethod::Card,
            enums::PaymentMethodType::Debit,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::Supported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::NotSupported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network.clone(),
                        }
                    }),
                ),
            },
        );

        globalpay_supported_payment_methods.add(
            enums::PaymentMethod::BankRedirect,
            enums::PaymentMethodType::Ideal,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::Supported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        globalpay_supported_payment_methods.add(
            enums::PaymentMethod::BankRedirect,
            enums::PaymentMethodType::Giropay,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::Supported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );
        globalpay_supported_payment_methods.add(
            enums::PaymentMethod::BankRedirect,
            enums::PaymentMethodType::Sofort,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::Supported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );
        globalpay_supported_payment_methods.add(
            enums::PaymentMethod::BankRedirect,
            enums::PaymentMethodType::Eps,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::Supported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        globalpay_supported_payment_methods.add(
            enums::PaymentMethod::Wallet,
            enums::PaymentMethodType::GooglePay,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::Supported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );
        globalpay_supported_payment_methods.add(
            enums::PaymentMethod::Wallet,
            enums::PaymentMethodType::Paypal,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::Supported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        globalpay_supported_payment_methods
    });

static GLOBALPAY_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "Globalpay",
        description: "Global Payments is an American multinational financial technology company that provides payment technology and services to merchants, issuers and consumers.",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Sandbox,
    };

static GLOBALPAY_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 1] = [enums::EventClass::Payments];

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

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

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


pub struct BillingData {
    bill_addr_city: Option<String>,
    bill_addr_country: Option<String>,
    bill_addr_line1: Option<Secret<String>>,
    bill_addr_line2: Option<Secret<String>>,
    bill_addr_line3: Option<Secret<String>>,
    bill_addr_postal_code: Option<Secret<String>>,
    bill_addr_state: Option<Secret<String>>,
}


pub struct Timeline {
    status: CoinbasePaymentStatus,
    context: Option<UnResolvedContext>,
    time: String,
    pub payment: Option<TimelinePayment>,
}


impl ApiEventMetric for DisputeId {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Dispute {
            dispute_id: self.dispute_id.clone(),
        })
    }
}


pub async fn proxy_for_payments_core<F, Res, Req, Op, FData, D>(
    state: SessionState,
    req_state: ReqState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    operation: Op,
    req: Req,
    payment_id: id_type::GlobalPaymentId,
    call_connector_action: CallConnectorAction,
    header_payload: HeaderPayload,
    return_raw_connector_response: Option<bool>,
) -> RouterResponse<Res>
where
    F: Send + Clone + Sync,
    Req: Send + Sync + Authenticate,
    FData: Send + Sync + Clone,
    Op: Operation<F, Req, Data = D> + ValidateStatusForOperation + Send + Sync + Clone,
    Req: Debug,
    D: OperationSessionGetters<F>
        + OperationSessionSetters<F>
        + transformers::GenerateResponse<Res>
        + Send
        + Sync
        + Clone,
    D: ConstructFlowSpecificData<F, FData, router_types::PaymentsResponseData>,
    RouterData<F, FData, router_types::PaymentsResponseData>: Feature<F, FData>,

    dyn api::Connector:
        services::api::ConnectorIntegration<F, FData, router_types::PaymentsResponseData>,

    PaymentResponse: Operation<F, FData, Data = D>,

    RouterData<F, FData, router_types::PaymentsResponseData>:
        hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<F, FData, D>,
{
    operation
        .to_validate_request()?
        .validate_request(&req, &merchant_context)?;

    let get_tracker_response = operation
        .to_get_tracker()?
        .get_trackers(
            &state,
            &payment_id,
            &req,
            &merchant_context,
            &profile,
            &header_payload,
        )
        .await?;

    let (payment_data, _req, connector_http_status_code, external_latency) =
        proxy_for_payments_operation_core::<_, _, _, _, _>(
            &state,
            req_state,
            merchant_context.clone(),
            profile.clone(),
            operation.clone(),
            req,
            get_tracker_response,
            call_connector_action,
            header_payload.clone(),
            return_raw_connector_response,
        )
        .await?;

    payment_data.generate_response(
        &state,
        connector_http_status_code,
        external_latency,
        header_payload.x_hs_latency,
        &merchant_context,
        &profile,
        None,
    )
}


impl ApiClientError {
    pub fn is_upstream_timeout(&self) -> bool {
        self == &Self::RequestTimeoutReceived
    }
    pub fn is_connection_closed_before_message_could_complete(&self) -> bool {
        self == &Self::ConnectionClosedIncompleteMessage
    }
}


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 struct ShouldCollectCvvDuringPayment(bool);


pub fn two_digits(&self) -> String {
        let year = self.peek() % 100;
        year.to_string()
    }


impl ConnectorValidation for Katapult {
    fn validate_mandate_payment(
        &self,
        _pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        match pm_data {
            PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented(
                "validate_mandate_payment does not support cards".to_string(),
            )
            .into()),
            _ => Ok(()),
        }
    }

    fn validate_psync_reference_id(
        &self,
        _data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        Ok(())
    }
}

impl ConnectorValidation for for Katapult


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

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

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

struct StripeSeleniumTest;

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

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

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