/// Constructor expecting config path set explicitly.
    pub fn new_with_config_path(
        explicit_config_path: Option<PathBuf>,
    ) -> Result<Self, config::ConfigError> {
        // Configuration values are picked up in the following priority order (1 being least
        // priority):
        // 1. Defaults from the implementation of the `Default` trait.
        // 2. Values from config file. The config file accessed depends on the environment
        //    specified by the `RUN_ENV` environment variable. `RUN_ENV` can be one of
        //    `development`, `sandbox` or `production`. If nothing is specified for `RUN_ENV`,
        //    `/config/development.toml` file is read.
        // 3. Environment variables prefixed with `ROUTER` and each level separated by double
        //    underscores.
        //
        // Values in config file override the defaults in `Default` trait, and the values set using
        // environment variables override both the defaults and the config file values.

        let environment = crate::env::which();
        let config_path = Self::config_path(&environment.to_string(), explicit_config_path);

        let config = Self::builder(&environment.to_string())?
            .add_source(config::File::from(config_path).required(false))
            .add_source(config::Environment::with_prefix("ROUTER").separator("__"))
            .build()?;

        // The logger may not yet be initialized when constructing the application configuration
        #[allow(clippy::print_stderr)]
        serde_path_to_error::deserialize(config).map_err(|error| {
            crate::error!(%error, "Unable to deserialize configuration");
            eprintln!("Unable to deserialize application configuration: {error}");
            error.into_inner()
        })
    }


pub struct ResponseIndicators {
    pub alternate_route_debit_indicator: Option<bool>,
    pub signature_line_indicator: Option<bool>,
    pub signature_debit_route_indicator: Option<bool>,
}


        self.build_error_response(res, event_builder)
    }
}

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Bankofamerica {
    fn get_webhook_object_reference_id(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported)
    }

    fn get_webhook_resource_object(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }
}

static BANKOFAMERICA_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::JCB,
            common_enums::CardNetwork::DinersClub,
            common_enums::CardNetwork::Discover,
            common_enums::CardNetwork::CartesBancaires,
            common_enums::CardNetwork::UnionPay,
            common_enums::CardNetwork::Maestro,
            common_enums::CardNetwork::Interac,
        ];

        let mut bankofamerica_supported_payment_methods = SupportedPaymentMethods::new();

        bankofamerica_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,
            },
        );

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

        bankofamerica_supported_payment_methods.add(
            enums::PaymentMethod::Wallet,
            enums::PaymentMethodType::SamsungPay,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::NotSupported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        bankofamerica_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(),
                        }
                    }),
                ),
            },
        );

        bankofamerica_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(),
                        }
                    }),
                ),
            },
        );

        bankofamerica_supported_payment_methods
    });

static BANKOFAMERICA_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "Bank Of America",
        description:
            "It is the second-largest banking institution in the United States and the second-largest bank in the world by market capitalization ",
        connector_type: enums::HyperswitchConnectorCategory::BankAcquirer,
        integration_status: enums::ConnectorIntegrationStatus::Live,
    };

static BANKOFAMERICA_SUPPORTED_WEBHOOK_FLOWS: [common_enums::EventClass; 0] = [];

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

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

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


pub async fn set_sso_id_in_redis(
    state: &SessionState,
    oidc_state: Secret<String>,
    sso_id: String,
) -> UserResult<()> {
    let connection = get_redis_connection_for_global_tenant(state)?;
    let key = get_oidc_key(&oidc_state.expose());
    connection
        .set_key_with_expiry(&key.into(), sso_id, REDIS_SSO_TTL)
        .await
        .change_context(UserErrors::InternalServerError)
        .attach_printable("Failed to set sso id in redis")
}


pub async fn send_recon_request(
    state: SessionState,
    auth_data: authentication::AuthenticationDataWithUser,
) -> RouterResponse<recon_api::ReconStatusResponse> {
    #[cfg(not(feature = "email"))]
    return Ok(service_api::ApplicationResponse::Json(
        recon_api::ReconStatusResponse {
            recon_status: enums::ReconStatus::NotRequested,
        },
    ));

    #[cfg(feature = "email")]
    {
        let user_in_db = &auth_data.user;
        let merchant_id = auth_data.merchant_account.get_id().clone();

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

        let user_email = user_in_db.email.clone();
        let email_contents = email_types::ProFeatureRequest {
            feature_name: consts::RECON_FEATURE_TAG.to_string(),
            merchant_id: merchant_id.clone(),
            user_name: domain::UserName::new(user_in_db.name.clone())
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to form username")?,
            user_email: domain::UserEmail::from_pii_email(user_email.clone())
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to convert recipient's email to UserEmail")?,
            recipient_email: domain::UserEmail::from_pii_email(
                state.conf.email.recon_recipient_email.clone(),
            )
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to convert recipient's email to UserEmail")?,
            subject: format!(
                "{} {}",
                consts::EMAIL_SUBJECT_DASHBOARD_FEATURE_REQUEST,
                user_email.expose().peek()
            ),
            theme_id: theme.as_ref().map(|theme| theme.theme_id.clone()),
            theme_config: theme
                .map(|theme| theme.email_config())
                .unwrap_or(state.conf.theme.email_config.clone()),
        };
        state
            .email_client
            .compose_and_send_email(
                user_utils::get_base_url(&state),
                Box::new(email_contents),
                state.conf.proxy.https_url.as_ref(),
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to compose and send email for ProFeatureRequest [Recon]")
            .async_and_then(|_| async {
                let updated_merchant_account = storage::MerchantAccountUpdate::ReconUpdate {
                    recon_status: enums::ReconStatus::Requested,
                };
                let db = &*state.store;
                let key_manager_state = &(&state).into();

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

                Ok(service_api::ApplicationResponse::Json(
                    recon_api::ReconStatusResponse {
                        recon_status: response.recon_status,
                    },
                ))
            })
            .await
    }
}


pub struct CardInfo {
    pub card_number: Option<cards::CardNumber>,
    pub card_holder_name: Option<Secret<String>>,
    pub card_expiry_date: Option<Secret<i32>>,
    pub card_cvc: Option<Secret<String>>,
    pub flexible_3d: Option<bool>,
    pub moto: Option<bool>,
    pub term_url: Option<String>,
}


pub struct CustomerPaymentMethodListResponse {
    pub object: &'static str,
    pub data: Vec<PaymentMethodData>,
}


pub struct USBankAccount {
    country_code: CountryAlpha2,
    account_number: Secret<String>,
    bank_code: Secret<String>,
    account_type: AccountType,
    account_holder_name: Secret<String>,
}


Documentation: api-reference/v1/business-profile/business-profile--list.mdx
# Type: Doc File

---
openapi: get /account/{account_id}/business_profile
---


pub async fn insert_disputes(
    conn: &PgPooledConn,
    batch: Vec<DisputeNew>,
) -> StorageResult<Vec<Dispute>> {
    let query = diesel::insert_into(<Dispute>::table()).values(batch);

    logger::debug!(query = %debug_query::<diesel::pg::Pg,_>(&query).to_string());

    query
        .get_results_async(conn)
        .await
        .change_context(errors::DatabaseError::Others)
        .attach_printable("Error while inserting disputes")
}


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


pub struct DataResponse {
    charge_credit_card: AuthChargeCreditCard,
}


pub async fn get_connector_tokenization_action_when_confirm_true<F, Req, D>(
    _state: &SessionState,
    _operation: &BoxedOperation<'_, F, Req, D>,
    payment_data: &mut D,
    _validate_result: &operations::ValidateResult,
    _merchant_key_store: &domain::MerchantKeyStore,
    _customer: &Option<domain::Customer>,
    _business_profile: &domain::Profile,
) -> RouterResult<(D, TokenizationAction)>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    // TODO: Implement this function
    let payment_data = payment_data.to_owned();
    Ok((payment_data, TokenizationAction::SkipConnectorTokenization))
}


pub async fn payouts_list_profile(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Query<payout_types::PayoutListConstraints>,
) -> HttpResponse {
    let flow = Flow::PayoutsList;
    let payload = json_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),
            ));
            payouts_list_core(
                state,
                merchant_context,
                auth.profile_id.map(|profile_id| vec![profile_id]),
                req,
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::ProfilePayoutRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl api::PaymentSync for Wise {}


pub struct PaystackRefundsResponseData {
    status: bool,
    message: String,
    data: PaystackRefundsData,
}


Documentation: api-reference/intelligent-router-api-reference/success-rate/fetch-entity-and-global-success-rates.mdx
# Type: Doc File

---
openapi: post /success_rate.SuccessRateCalculator/FetchEntityAndGlobalSuccessRate
---


pub struct AdminApiAuthWithMerchantIdFromHeader;


pub struct TokenioAuthType {
    pub(super) merchant_id: Secret<String>,
    pub(super) private_key: Secret<String>,
    pub(super) key_id: Secret<String>,
    pub(super) key_algorithm: CryptoAlgorithm,
}


pub struct GlobalpayCaptureRequest {
    pub amount: Option<StringMinorUnit>,
    pub capture_sequence: Option<Sequence>,
    pub reference: Option<String>,
}


pub fn new(org_new: OrganizationNew) -> Self {
        let OrganizationNew {
            id,
            organization_name,
            organization_details,
            metadata,
            created_at,
            modified_at,
            version,
            organization_type,
            platform_merchant_id,
        } = org_new;
        Self {
            id,
            organization_name,
            organization_details,
            metadata,
            created_at,
            modified_at,
            version,
            organization_type: Some(organization_type),
            platform_merchant_id,
        }
    }


impl IncomingWebhook for Nmi {
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, ConnectorError> {
        Ok(Box::new(crypto::HmacSha256))
    }

    fn get_webhook_source_verification_signature(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, ConnectorError> {
        let sig_header = get_header_key_value("webhook-signature", request.headers)?;

        let regex_pattern = r"t=(.*),s=(.*)";

        if let Some(captures) = Regex::new(regex_pattern)
            .change_context(ConnectorError::WebhookSignatureNotFound)?
            .captures(sig_header)
        {
            let signature = captures
                .get(2)
                .ok_or(ConnectorError::WebhookSignatureNotFound)?
                .as_str();

            // Decode hex signature to bytes
            hex::decode(signature).change_context(ConnectorError::WebhookSignatureNotFound)
        } else {
            Err(report!(ConnectorError::WebhookSignatureNotFound))
        }
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, ConnectorError> {
        let sig_header = get_header_key_value("webhook-signature", request.headers)?;

        let regex_pattern = r"t=(.*),s=(.*)";

        if let Some(captures) = Regex::new(regex_pattern)
            .change_context(ConnectorError::WebhookSignatureNotFound)?
            .captures(sig_header)
        {
            let nonce = captures
                .get(1)
                .ok_or(ConnectorError::WebhookSignatureNotFound)?
                .as_str();

            let message = format!("{}.{}", nonce, String::from_utf8_lossy(request.body));

            Ok(message.into_bytes())
        } else {
            Err(report!(ConnectorError::WebhookSignatureNotFound))
        }
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, ConnectorError> {
        let reference_body: nmi::NmiWebhookObjectReference = request
            .body
            .parse_struct("nmi NmiWebhookObjectReference")
            .change_context(ConnectorError::WebhookResourceObjectNotFound)?;

        let object_reference_id = match reference_body.event_body.action.action_type {
            nmi::NmiActionType::Sale => api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::PaymentAttemptId(
                    reference_body.event_body.order_id,
                ),
            ),
            nmi::NmiActionType::Auth => api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::PaymentAttemptId(
                    reference_body.event_body.order_id,
                ),
            ),
            nmi::NmiActionType::Capture => api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::PaymentAttemptId(
                    reference_body.event_body.order_id,
                ),
            ),
            nmi::NmiActionType::Void => api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::PaymentAttemptId(
                    reference_body.event_body.order_id,
                ),
            ),
            nmi::NmiActionType::Refund => api_models::webhooks::ObjectReferenceId::RefundId(
                api_models::webhooks::RefundIdType::RefundId(reference_body.event_body.order_id),
            ),
            _ => Err(ConnectorError::WebhooksNotImplemented)?,
        };

        Ok(object_reference_id)
    }

    fn get_webhook_event_type(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, ConnectorError> {
        let event_type_body: nmi::NmiWebhookEventBody = request
            .body
            .parse_struct("nmi NmiWebhookEventType")
            .change_context(ConnectorError::WebhookResourceObjectNotFound)?;

        Ok(transformers::get_nmi_webhook_event(
            event_type_body.event_type,
        ))
    }

    fn get_webhook_resource_object(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, ConnectorError> {
        let webhook_body: nmi::NmiWebhookBody = request
            .body
            .parse_struct("nmi NmiWebhookBody")
            .change_context(ConnectorError::WebhookResourceObjectNotFound)?;

        match webhook_body.event_body.action.action_type {
            nmi::NmiActionType::Sale
            | nmi::NmiActionType::Auth
            | nmi::NmiActionType::Capture
            | nmi::NmiActionType::Void
            | nmi::NmiActionType::Credit => {
                Ok(Box::new(nmi::SyncResponse::try_from(&webhook_body)?))
            }
            nmi::NmiActionType::Refund => Ok(Box::new(webhook_body)),
        }
    }
}


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

#![allow(
    clippy::expect_used,
    clippy::panic,
    clippy::unwrap_in_result,
    clippy::unwrap_used,
    clippy::print_stderr
)]
mod aci_ui;
mod adyen_uk_ui;
mod adyen_uk_wh_ui;
mod airwallex_ui;
mod authorizedotnet_ui;
mod authorizedotnet_wh_ui;
mod bambora_ui;
mod bluesnap_ui;
mod bluesnap_wh_ui;
mod checkout_ui;
mod checkout_wh_ui;
mod globalpay_ui;
mod mollie_ui;
mod multisafepay_ui;
mod nexinets_ui;
mod noon_ui;
mod nuvei_ui;
mod paypal_ui;
mod payu_ui;
mod selenium;
mod shift4_ui;
mod stripe_ui;
mod stripe_wh_ui;
mod trustpay_3ds_ui;
mod worldline_ui;
mod zen_ui;


impl api::PaymentSync for Helcim {}


File: crates/analytics/src/sdk_events/metrics/payment_data_filled_count.rs

use std::collections::HashSet;

use api_models::analytics::{
    sdk_events::{
        SdkEventDimensions, SdkEventFilters, SdkEventMetricsBucketIdentifier, SdkEventNames,
    },
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

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

#[derive(Default)]
pub(super) struct PaymentDataFilledCount;

#[async_trait::async_trait]
impl<T> super::SdkEventMetric<T> for PaymentDataFilledCount
where
    T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[SdkEventDimensions],
        publishable_key: &str,
        filters: &SdkEventFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>> {
        let mut query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::SdkEventsAnalytics);
        let dimensions = dimensions.to_vec();

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

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

        if let Some(granularity) = granularity {
            query_builder
                .add_granularity_in_mins(granularity)
                .switch()?;
        }

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

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

        query_builder
            .add_bool_filter_clause("first_event", 1)
            .switch()?;

        query_builder
            .add_filter_clause("event_name", SdkEventNames::PaymentDataFilled)
            .switch()?;

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

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

        if let Some(_granularity) = granularity.as_ref() {
            query_builder
                .add_group_by_clause("time_bucket")
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .execute_query::<SdkEventMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    SdkEventMetricsBucketIdentifier::new(
                        i.payment_method.clone(),
                        i.platform.clone(),
                        i.browser_name.clone(),
                        i.source.clone(),
                        i.component.clone(),
                        i.payment_experience.clone(),
                        i.time_bucket.clone(),
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


pub struct CustomerUpdateInternal {
    pub name: Option<Encryption>,
    pub email: Option<Encryption>,
    pub phone: Option<Encryption>,
    pub description: Option<Description>,
    pub phone_country_code: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub modified_at: PrimitiveDateTime,
    pub connector_customer: Option<common_types::customers::ConnectorCustomerMap>,
    pub default_payment_method_id: Option<Option<common_utils::id_type::GlobalPaymentMethodId>>,
    pub updated_by: Option<String>,
    pub default_billing_address: Option<Encryption>,
    pub default_shipping_address: Option<Encryption>,
    pub status: Option<DeleteStatus>,
    pub tax_registration_id: Option<Encryption>,
}


pub async fn create_theme(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<theme_api::CreateThemeRequest>,
) -> HttpResponse {
    let flow = Flow::CreateTheme;
    let payload = payload.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, payload, _| theme_core::create_theme(state, payload),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub fn set_tenancy(&mut self, tenant_config: &dyn TenantConfig) {
        self.ckh_database_name = Some(tenant_config.get_clickhouse_database().to_string());
    }


pub struct Refunds;


OpenAPI Block Path: components.schemas.RefundUpdateRequest
{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string",
      "description": "An arbitrary string attached to the object. Often useful for displaying to users and your customer support executive",
      "example": "Customer returned the product",
      "nullable": true,
      "maxLength": 255
    },
    "metadata": {
      "type": "object",
      "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.",
      "nullable": true
    }
  },
  "additionalProperties": false
}


pub struct User {
    pub client_user_id: id_type::CustomerId,
}


File: crates/router/src/middleware.rs
 Public functions: 1
 Public structs: 10

use common_utils::consts::TENANT_HEADER;
use futures::StreamExt;
use router_env::{
    logger,
    tracing::{field::Empty, Instrument},
};

use crate::{headers, routes::metrics};

/// Middleware to include request ID in response header.
pub struct RequestId;

impl<S, B> actix_web::dev::Transform<S, actix_web::dev::ServiceRequest> for RequestId
where
    S: actix_web::dev::Service<
        actix_web::dev::ServiceRequest,
        Response = actix_web::dev::ServiceResponse<B>,
        Error = actix_web::Error,
    >,
    S::Future: 'static,
    B: 'static,
{
    type Response = actix_web::dev::ServiceResponse<B>;
    type Error = actix_web::Error;
    type Transform = RequestIdMiddleware<S>;
    type InitError = ();
    type Future = std::future::Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        std::future::ready(Ok(RequestIdMiddleware { service }))
    }
}

pub struct RequestIdMiddleware<S> {
    service: S,
}

impl<S, B> actix_web::dev::Service<actix_web::dev::ServiceRequest> for RequestIdMiddleware<S>
where
    S: actix_web::dev::Service<
        actix_web::dev::ServiceRequest,
        Response = actix_web::dev::ServiceResponse<B>,
        Error = actix_web::Error,
    >,
    S::Future: 'static,
    B: 'static,
{
    type Response = actix_web::dev::ServiceResponse<B>;
    type Error = actix_web::Error;
    type Future = futures::future::LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    actix_web::dev::forward_ready!(service);

    fn call(&self, req: actix_web::dev::ServiceRequest) -> Self::Future {
        let old_x_request_id = req.headers().get("x-request-id").cloned();
        let mut req = req;
        let request_id_fut = req.extract::<router_env::tracing_actix_web::RequestId>();
        let response_fut = self.service.call(req);

        Box::pin(
            async move {
                let request_id = request_id_fut.await?;
                let request_id = request_id.as_hyphenated().to_string();
                if let Some(upstream_request_id) = old_x_request_id {
                    router_env::logger::info!(?upstream_request_id);
                }
                let mut response = response_fut.await?;
                response.headers_mut().append(
                    http::header::HeaderName::from_static("x-request-id"),
                    http::HeaderValue::from_str(&request_id)?,
                );

                Ok(response)
            }
            .in_current_span(),
        )
    }
}

/// Middleware for attaching default response headers. Headers with the same key already set in a
/// response will not be overwritten.
pub fn default_response_headers() -> actix_web::middleware::DefaultHeaders {
    use actix_web::http::header;

    let default_headers_middleware = actix_web::middleware::DefaultHeaders::new();

    #[cfg(feature = "vergen")]
    let default_headers_middleware =
        default_headers_middleware.add(("x-hyperswitch-version", router_env::git_tag!()));

    default_headers_middleware
        // Max age of 1 year in seconds, equal to `60 * 60 * 24 * 365` seconds.
        .add((header::STRICT_TRANSPORT_SECURITY, "max-age=31536000"))
        .add((header::VIA, "HyperSwitch"))
}

/// Middleware to build a TOP level domain span for each request.
pub struct LogSpanInitializer;

impl<S, B> actix_web::dev::Transform<S, actix_web::dev::ServiceRequest> for LogSpanInitializer
where
    S: actix_web::dev::Service<
        actix_web::dev::ServiceRequest,
        Response = actix_web::dev::ServiceResponse<B>,
        Error = actix_web::Error,
    >,
    S::Future: 'static,
    B: 'static,
{
    type Response = actix_web::dev::ServiceResponse<B>;
    type Error = actix_web::Error;
    type Transform = LogSpanInitializerMiddleware<S>;
    type InitError = ();
    type Future = std::future::Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        std::future::ready(Ok(LogSpanInitializerMiddleware { service }))
    }
}

pub struct LogSpanInitializerMiddleware<S> {
    service: S,
}

impl<S, B> actix_web::dev::Service<actix_web::dev::ServiceRequest>
    for LogSpanInitializerMiddleware<S>
where
    S: actix_web::dev::Service<
        actix_web::dev::ServiceRequest,
        Response = actix_web::dev::ServiceResponse<B>,
        Error = actix_web::Error,
    >,
    S::Future: 'static,
    B: 'static,
{
    type Response = actix_web::dev::ServiceResponse<B>;
    type Error = actix_web::Error;
    type Future = futures::future::LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    actix_web::dev::forward_ready!(service);

    // TODO: have a common source of truth for the list of top level fields
    // /crates/router_env/src/logger/storage.rs also has a list of fields  called PERSISTENT_KEYS
    fn call(&self, req: actix_web::dev::ServiceRequest) -> Self::Future {
        let tenant_id = req
            .headers()
            .get(TENANT_HEADER)
            .and_then(|i| i.to_str().ok())
            .map(|s| s.to_owned());
        let response_fut = self.service.call(req);
        let tenant_id_clone = tenant_id.clone();
        Box::pin(
            async move {
                if let Some(tenant) = tenant_id_clone {
                    router_env::tracing::Span::current().record("tenant_id", tenant);
                }
                let response = response_fut.await;
                router_env::tracing::Span::current().record("golden_log_line", true);
                response
            }
            .instrument(
                router_env::tracing::info_span!(
                    "ROOT_SPAN",
                    payment_id = Empty,
                    merchant_id = Empty,
                    connector_name = Empty,
                    payment_method = Empty,
                    status_code = Empty,
                    flow = "UNKNOWN",
                    golden_log_line = Empty,
                    tenant_id = &tenant_id
                )
                .or_current(),
            ),
        )
    }
}

fn get_request_details_from_value(json_value: &serde_json::Value, parent_key: &str) -> String {
    match json_value {
        serde_json::Value::Null => format!("{parent_key}: null"),
        serde_json::Value::Bool(b) => format!("{parent_key}: {b}"),
        serde_json::Value::Number(num) => format!("{}: {}", parent_key, num.to_string().len()),
        serde_json::Value::String(s) => format!("{}: {}", parent_key, s.len()),
        serde_json::Value::Array(arr) => {
            let mut result = String::new();
            for (index, value) in arr.iter().enumerate() {
                let child_key = format!("{parent_key}[{index}]");
                result.push_str(&get_request_details_from_value(value, &child_key));
                if index < arr.len() - 1 {
                    result.push_str(", ");
                }
            }
            result
        }
        serde_json::Value::Object(obj) => {
            let mut result = String::new();
            for (index, (key, value)) in obj.iter().enumerate() {
                let child_key = format!("{parent_key}[{key}]");
                result.push_str(&get_request_details_from_value(value, &child_key));
                if index < obj.len() - 1 {
                    result.push_str(", ");
                }
            }
            result
        }
    }
}

/// Middleware for Logging request_details of HTTP 400 Bad Requests
pub struct Http400RequestDetailsLogger;

impl<S: 'static, B> actix_web::dev::Transform<S, actix_web::dev::ServiceRequest>
    for Http400RequestDetailsLogger
where
    S: actix_web::dev::Service<
        actix_web::dev::ServiceRequest,
        Response = actix_web::dev::ServiceResponse<B>,
        Error = actix_web::Error,
    >,
    S::Future: 'static,
    B: 'static,
{
    type Response = actix_web::dev::ServiceResponse<B>;
    type Error = actix_web::Error;
    type Transform = Http400RequestDetailsLoggerMiddleware<S>;
    type InitError = ();
    type Future = std::future::Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        std::future::ready(Ok(Http400RequestDetailsLoggerMiddleware {
            service: std::rc::Rc::new(service),
        }))
    }
}

pub struct Http400RequestDetailsLoggerMiddleware<S> {
    service: std::rc::Rc<S>,
}

impl<S, B> actix_web::dev::Service<actix_web::dev::ServiceRequest>
    for Http400RequestDetailsLoggerMiddleware<S>
where
    S: actix_web::dev::Service<
            actix_web::dev::ServiceRequest,
            Response = actix_web::dev::ServiceResponse<B>,
            Error = actix_web::Error,
        > + 'static,
    S::Future: 'static,
    B: 'static,
{
    type Response = actix_web::dev::ServiceResponse<B>;
    type Error = actix_web::Error;
    type Future = futures::future::LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    actix_web::dev::forward_ready!(service);

    fn call(&self, mut req: actix_web::dev::ServiceRequest) -> Self::Future {
        let svc = self.service.clone();
        let request_id_fut = req.extract::<router_env::tracing_actix_web::RequestId>();
        Box::pin(async move {
            let (http_req, payload) = req.into_parts();
            let result_payload: Vec<Result<bytes::Bytes, actix_web::error::PayloadError>> =
                payload.collect().await;
            let payload = result_payload
                .into_iter()
                .collect::<Result<Vec<bytes::Bytes>, actix_web::error::PayloadError>>()?;
            let bytes = payload.clone().concat().to_vec();
            let bytes_length = bytes.len();
            // we are creating h1 payload manually from bytes, currently there's no way to create http2 payload with actix
            let (_, mut new_payload) = actix_http::h1::Payload::create(true);
            new_payload.unread_data(bytes.to_vec().clone().into());
            let new_req = actix_web::dev::ServiceRequest::from_parts(http_req, new_payload.into());

            let content_length_header = new_req
                .headers()
                .get(headers::CONTENT_LENGTH)
                .map(ToOwned::to_owned);
            let response_fut = svc.call(new_req);
            let response = response_fut.await?;
            // Log the request_details when we receive 400 status from the application
            if response.status() == 400 {
                let request_id = request_id_fut.await?.as_hyphenated().to_string();
                let content_length_header_string = content_length_header
                    .map(|content_length_header| {
                        content_length_header.to_str().map(ToOwned::to_owned)
                    })
                    .transpose()
                    .inspect_err(|error| {
                        logger::warn!("Could not convert content length to string {error:?}");
                    })
                    .ok()
                    .flatten();

                logger::info!("Content length from header: {content_length_header_string:?}, Bytes length: {bytes_length}");

                if !bytes.is_empty() {
                    let value_result: Result<serde_json::Value, serde_json::Error> =
                        serde_json::from_slice(&bytes);
                    match value_result {
                        Ok(value) => {
                            logger::info!(
                                "request_id: {request_id}, request_details: {}",
                                get_request_details_from_value(&value, "")
                            );
                        }
                        Err(err) => {
                            logger::warn!("error while parsing the request in json value: {err}");
                        }
                    }
                } else {
                    logger::info!("request_id: {request_id}, request_details: Empty Body");
                }
            }
            Ok(response)
        })
    }
}

/// Middleware for Adding Accept-Language header based on query params
pub struct AddAcceptLanguageHeader;

impl<S: 'static, B> actix_web::dev::Transform<S, actix_web::dev::ServiceRequest>
    for AddAcceptLanguageHeader
where
    S: actix_web::dev::Service<
        actix_web::dev::ServiceRequest,
        Response = actix_web::dev::ServiceResponse<B>,
        Error = actix_web::Error,
    >,
    S::Future: 'static,
    B: 'static,
{
    type Response = actix_web::dev::ServiceResponse<B>;
    type Error = actix_web::Error;
    type Transform = AddAcceptLanguageHeaderMiddleware<S>;
    type InitError = ();
    type Future = std::future::Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        std::future::ready(Ok(AddAcceptLanguageHeaderMiddleware {
            service: std::rc::Rc::new(service),
        }))
    }
}

pub struct AddAcceptLanguageHeaderMiddleware<S> {
    service: std::rc::Rc<S>,
}

impl<S, B> actix_web::dev::Service<actix_web::dev::ServiceRequest>
    for AddAcceptLanguageHeaderMiddleware<S>
where
    S: actix_web::dev::Service<
            actix_web::dev::ServiceRequest,
            Response = actix_web::dev::ServiceResponse<B>,
            Error = actix_web::Error,
        > + 'static,
    S::Future: 'static,
    B: 'static,
{
    type Response = actix_web::dev::ServiceResponse<B>;
    type Error = actix_web::Error;
    type Future = futures::future::LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    actix_web::dev::forward_ready!(service);

    fn call(&self, mut req: actix_web::dev::ServiceRequest) -> Self::Future {
        let svc = self.service.clone();
        Box::pin(async move {
            #[derive(serde::Deserialize)]
            struct LocaleQueryParam {
                locale: Option<String>,
            }
            let query_params = req.query_string();
            let locale_param =
                serde_qs::from_str::<LocaleQueryParam>(query_params).map_err(|error| {
                    actix_web::error::ErrorBadRequest(format!(
                        "Could not convert query params to locale query parmas: {error:?}",
                    ))
                })?;
            let accept_language_header = req.headers().get(http::header::ACCEPT_LANGUAGE);
            if let Some(locale) = locale_param.locale {
                req.headers_mut().insert(
                    http::header::ACCEPT_LANGUAGE,
                    http::HeaderValue::from_str(&locale)?,
                );
            } else if accept_language_header.is_none() {
                req.headers_mut().insert(
                    http::header::ACCEPT_LANGUAGE,
                    http::HeaderValue::from_static("en"),
                );
            }
            let response_fut = svc.call(req);
            let response = response_fut.await?;
            Ok(response)
        })
    }
}

/// Middleware for recording request-response metrics
pub struct RequestResponseMetrics;

impl<S: 'static, B> actix_web::dev::Transform<S, actix_web::dev::ServiceRequest>
    for RequestResponseMetrics
where
    S: actix_web::dev::Service<
        actix_web::dev::ServiceRequest,
        Response = actix_web::dev::ServiceResponse<B>,
        Error = actix_web::Error,
    >,
    S::Future: 'static,
    B: 'static,
{
    type Response = actix_web::dev::ServiceResponse<B>;
    type Error = actix_web::Error;
    type Transform = RequestResponseMetricsMiddleware<S>;
    type InitError = ();
    type Future = std::future::Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        std::future::ready(Ok(RequestResponseMetricsMiddleware {
            service: std::rc::Rc::new(service),
        }))
    }
}

pub struct RequestResponseMetricsMiddleware<S> {
    service: std::rc::Rc<S>,
}

impl<S, B> actix_web::dev::Service<actix_web::dev::ServiceRequest>
    for RequestResponseMetricsMiddleware<S>
where
    S: actix_web::dev::Service<
            actix_web::dev::ServiceRequest,
            Response = actix_web::dev::ServiceResponse<B>,
            Error = actix_web::Error,
        > + 'static,
    S::Future: 'static,
    B: 'static,
{
    type Response = actix_web::dev::ServiceResponse<B>;
    type Error = actix_web::Error;
    type Future = futures::future::LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    actix_web::dev::forward_ready!(service);

    fn call(&self, req: actix_web::dev::ServiceRequest) -> Self::Future {
        use std::borrow::Cow;

        let svc = self.service.clone();

        let request_path = req
            .match_pattern()
            .map(Cow::<'static, str>::from)
            .unwrap_or_else(|| "UNKNOWN".into());
        let request_method = Cow::<'static, str>::from(req.method().as_str().to_owned());

        Box::pin(async move {
            let mut attributes =
                router_env::metric_attributes!(("path", request_path), ("method", request_method))
                    .to_vec();

            let response_fut = svc.call(req);

            metrics::REQUESTS_RECEIVED.add(1, &attributes);

            let (response_result, request_duration) =
                common_utils::metrics::utils::time_future(response_fut).await;
            let response = response_result?;

            attributes.extend_from_slice(router_env::metric_attributes!((
                "status_code",
                i64::from(response.status().as_u16())
            )));

            metrics::REQUEST_TIME.record(request_duration.as_secs_f64(), &attributes);

            Ok(response)
        })
    }
}


impl api::RefundExecute for Checkbook {}

impl api::RefundExecute for for Checkbook


pub struct FraudDetails {
    pub final_decision: String,
}


pub struct CybersourceConsumerAuthInformationResponse {
    access_token: String,
    device_data_collection_url: String,
    reference_id: String,
}


File: crates/router/src/services/kafka/dispute.rs
 Public functions: 1
 Public structs: 1

use common_utils::{
    ext_traits::StringExt,
    id_type,
    types::{AmountConvertor, MinorUnit, StringMinorUnitForConnector},
};
use diesel_models::enums as storage_enums;
use masking::Secret;
use time::OffsetDateTime;

use crate::types::storage::dispute::Dispute;

#[derive(serde::Serialize, Debug)]
pub struct KafkaDispute<'a> {
    pub dispute_id: &'a String,
    pub dispute_amount: MinorUnit,
    pub currency: storage_enums::Currency,
    pub dispute_stage: &'a storage_enums::DisputeStage,
    pub dispute_status: &'a storage_enums::DisputeStatus,
    pub payment_id: &'a id_type::PaymentId,
    pub attempt_id: &'a String,
    pub merchant_id: &'a id_type::MerchantId,
    pub connector_status: &'a String,
    pub connector_dispute_id: &'a String,
    pub connector_reason: Option<&'a String>,
    pub connector_reason_code: Option<&'a String>,
    #[serde(default, with = "time::serde::timestamp::option")]
    pub challenge_required_by: Option<OffsetDateTime>,
    #[serde(default, with = "time::serde::timestamp::option")]
    pub connector_created_at: Option<OffsetDateTime>,
    #[serde(default, with = "time::serde::timestamp::option")]
    pub connector_updated_at: Option<OffsetDateTime>,
    #[serde(default, with = "time::serde::timestamp")]
    pub created_at: OffsetDateTime,
    #[serde(default, with = "time::serde::timestamp")]
    pub modified_at: OffsetDateTime,
    pub connector: &'a String,
    pub evidence: &'a Secret<serde_json::Value>,
    pub profile_id: Option<&'a id_type::ProfileId>,
    pub merchant_connector_id: Option<&'a id_type::MerchantConnectorAccountId>,
    pub organization_id: &'a id_type::OrganizationId,
}

impl<'a> KafkaDispute<'a> {
    pub fn from_storage(dispute: &'a Dispute) -> Self {
        let currency = dispute.dispute_currency.unwrap_or(
            dispute
                .currency
                .to_uppercase()
                .parse_enum("Currency")
                .unwrap_or_default(),
        );
        Self {
            dispute_id: &dispute.dispute_id,
            dispute_amount: StringMinorUnitForConnector::convert_back(
                &StringMinorUnitForConnector,
                dispute.amount.clone(),
                currency,
            )
            .unwrap_or_else(|e| {
                router_env::logger::error!("Failed to convert dispute amount: {e:?}");
                MinorUnit::new(0)
            }),
            currency,
            dispute_stage: &dispute.dispute_stage,
            dispute_status: &dispute.dispute_status,
            payment_id: &dispute.payment_id,
            attempt_id: &dispute.attempt_id,
            merchant_id: &dispute.merchant_id,
            connector_status: &dispute.connector_status,
            connector_dispute_id: &dispute.connector_dispute_id,
            connector_reason: dispute.connector_reason.as_ref(),
            connector_reason_code: dispute.connector_reason_code.as_ref(),
            challenge_required_by: dispute.challenge_required_by.map(|i| i.assume_utc()),
            connector_created_at: dispute.connector_created_at.map(|i| i.assume_utc()),
            connector_updated_at: dispute.connector_updated_at.map(|i| i.assume_utc()),
            created_at: dispute.created_at.assume_utc(),
            modified_at: dispute.modified_at.assume_utc(),
            connector: &dispute.connector,
            evidence: &dispute.evidence,
            profile_id: dispute.profile_id.as_ref(),
            merchant_connector_id: dispute.merchant_connector_id.as_ref(),
            organization_id: &dispute.organization_id,
        }
    }
}

impl super::KafkaMessage for KafkaDispute<'_> {
    fn key(&self) -> String {
        format!(
            "{}_{}_{}",
            self.merchant_id.get_string_repr(),
            self.payment_id.get_string_repr(),
            self.dispute_id
        )
    }

    fn event_type(&self) -> crate::events::EventType {
        crate::events::EventType::Dispute
    }
}


pub struct HeaderKey {
    pub api_key: Secret<String>,
}


impl api::RefundExecute for Opayo {}


impl Responder {
        let flow = AnalyticsFlow::GetPaymentFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let org_id = auth.merchant_account.get_org_id();
                let merchant_id = auth.merchant_account.get_id();
                let auth: AuthInfo = AuthInfo::MerchantLevel {
                    org_id: org_id.clone(),
                    merchant_ids: vec![merchant_id.clone()],
                };
                analytics::payments::get_filters(&state.pool, req, &auth)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


OpenAPI Block Path: components.schemas.PlatformAccountCreateRequest
{
  "type": "object",
  "required": [
    "organization_name"
  ],
  "properties": {
    "organization_name": {
      "type": "string",
      "example": "organization_abc",
      "maxLength": 64
    }
  }
}


OpenAPI Block Path: components.schemas.GpayEcryptedTokenizationData
{
  "type": "object",
  "description": "This struct represents the encrypted Gpay payment data",
  "required": [
    "type",
    "token"
  ],
  "properties": {
    "type": {
      "type": "string",
      "description": "The type of the token"
    },
    "token": {
      "type": "string",
      "description": "Token generated for the wallet"
    }
  }
}


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>,
}


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

use hyperswitch_domain_models::payment_method_data::{Card, PaymentMethodData};
use masking::Secret;
use router::types::{self, api, storage::enums};
use test_utils::connector_auth;

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

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

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

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

static CONNECTOR: ElavonTest = ElavonTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    None
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}

// Captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Partially captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(
            payment_method_details(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized,);
}

// Voids a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_void_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_void_payment(
            payment_method_details(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: String::from(""),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}

// Refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Synchronizes a refund using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_manually_captured_refund() {
    let refund_response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_make_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_auto_captured_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                capture_method: Some(enums::CaptureMethod::Automatic),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged,);
}

// Refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_auto_captured_payment() {
    let response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        refund_response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() {
    CONNECTOR
        .make_payment_and_multiple_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
}

// Synchronizes a refund using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_refund() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Cards Negative scenarios
// Creates a payment with incorrect CVC.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_cvc() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_cvc: Secret::new("12345".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's security code is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry month.
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_exp_month: Secret::new("20".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration month is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry year.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_expiry_year() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_exp_year: Secret::new("2000".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration year is invalid.".to_string(),
    );
}

// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let void_response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        void_response.response.unwrap_err().message,
        "You cannot cancel this PaymentIntent because it has a status of succeeded."
    );
}

// Captures a payment using invalid connector payment id.
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let capture_response = CONNECTOR
        .capture_payment("123456789".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        capture_response.response.unwrap_err().message,
        String::from("No such payment_intent: '123456789'")
    );
}

// Refunds a payment with refund amount higher than payment amount.
#[actix_web::test]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
    let response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 150,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Refund amount (₹1.50) is greater than charge amount (₹1.00)",
    );
}

// Connector dependent test cases goes here

// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests


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


pub struct SubError {
    pub code: String,
    pub message: String,
    pub http_status_code: u16,
}


pub struct NexixpayErrorResponse {
    pub errors: Vec<NexixpayErrorBody>,
}


pub async fn find_by_profile_name_merchant_id(
        conn: &PgPooledConn,
        profile_name: &str,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::profile_name
                .eq(profile_name.to_owned())
                .and(dsl::merchant_id.eq(merchant_id.to_owned())),
        )
        .await
    }


impl std::fmt::Display for RedisError {
///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
///         match self {
///             Self::UnknownResult => write!(f, "Unknown result")
///         }
///     }
/// }

impl std::fmt::Display for for RedisError


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


pub struct PaymentResponse {
    ssl_result: SslResult,
    ssl_txn_id: String,
    ssl_result_message: String,
    ssl_token: Option<Secret<String>>,
}


pub struct Notification {
    pub kind: String, // xml parse only string to fields
    pub timestamp: String,
    pub dispute: Option<BraintreeDisputeData>,
}


pub async fn generate_profile_authentication_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GenerateAuthenticationReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let user = UserInterface::find_user_by_id(&*state.global_store, &user_id)
                    .await
                    .change_context(AnalyticsError::UnknownError)?;

                let user_email = UserEmail::from_pii_email(user.email)
                    .change_context(AnalyticsError::UnknownError)?
                    .get_secret();
                let org_id = auth.merchant_account.get_org_id();
                let merchant_id = auth.merchant_account.get_id();
                let profile_id = auth
                    .profile_id
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?;
                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(merchant_id.clone()),
                    auth: AuthInfo::ProfileLevel {
                        org_id: org_id.clone(),
                        merchant_id: merchant_id.clone(),
                        profile_ids: vec![profile_id.clone()],
                    },
                    email: user_email,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.authentication_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileReportRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


File: crates/diesel_models/src/user_role.rs
 Public functions: 1
 Public structs: 3

use std::hash::Hash;

use common_enums::EntityType;
use common_utils::{consts, id_type};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use time::PrimitiveDateTime;

use crate::{enums, schema::user_roles};

#[derive(Clone, Debug, Identifiable, Queryable, Selectable, Eq)]
#[diesel(table_name = user_roles, check_for_backend(diesel::pg::Pg))]
pub struct UserRole {
    pub id: i32,
    pub user_id: String,
    pub merchant_id: Option<id_type::MerchantId>,
    pub role_id: String,
    pub org_id: Option<id_type::OrganizationId>,
    pub status: enums::UserStatus,
    pub created_by: String,
    pub last_modified_by: String,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub profile_id: Option<id_type::ProfileId>,
    pub entity_id: Option<String>,
    pub entity_type: Option<EntityType>,
    pub version: enums::UserRoleVersion,
    pub tenant_id: id_type::TenantId,
}

impl UserRole {
    pub fn get_entity_id_and_type(&self) -> Option<(String, EntityType)> {
        match (self.version, self.entity_type, self.role_id.as_str()) {
            (enums::UserRoleVersion::V1, None, consts::ROLE_ID_ORGANIZATION_ADMIN) => {
                let org_id = self.org_id.clone()?.get_string_repr().to_string();
                Some((org_id, EntityType::Organization))
            }
            (enums::UserRoleVersion::V1, None, _) => {
                let merchant_id = self.merchant_id.clone()?.get_string_repr().to_string();
                Some((merchant_id, EntityType::Merchant))
            }
            (enums::UserRoleVersion::V1, Some(_), _) => {
                self.entity_id.clone().zip(self.entity_type)
            }
            (enums::UserRoleVersion::V2, _, _) => self.entity_id.clone().zip(self.entity_type),
        }
    }
}

impl Hash for UserRole {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.user_id.hash(state);
        if let Some((entity_id, entity_type)) = self.get_entity_id_and_type() {
            entity_id.hash(state);
            entity_type.hash(state);
        }
    }
}

impl PartialEq for UserRole {
    fn eq(&self, other: &Self) -> bool {
        match (
            self.get_entity_id_and_type(),
            other.get_entity_id_and_type(),
        ) {
            (
                Some((self_entity_id, self_entity_type)),
                Some((other_entity_id, other_entity_type)),
            ) => {
                self.user_id == other.user_id
                    && self_entity_id == other_entity_id
                    && self_entity_type == other_entity_type
            }
            _ => self.user_id == other.user_id,
        }
    }
}

#[derive(router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = user_roles)]
pub struct UserRoleNew {
    pub user_id: String,
    pub merchant_id: Option<id_type::MerchantId>,
    pub role_id: String,
    pub org_id: Option<id_type::OrganizationId>,
    pub status: enums::UserStatus,
    pub created_by: String,
    pub last_modified_by: String,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub profile_id: Option<id_type::ProfileId>,
    pub entity_id: Option<String>,
    pub entity_type: Option<EntityType>,
    pub version: enums::UserRoleVersion,
    pub tenant_id: id_type::TenantId,
}

#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = user_roles)]
pub struct UserRoleUpdateInternal {
    role_id: Option<String>,
    status: Option<enums::UserStatus>,
    last_modified_by: Option<String>,
    last_modified: PrimitiveDateTime,
}

#[derive(Clone)]
pub enum UserRoleUpdate {
    UpdateStatus {
        status: enums::UserStatus,
        modified_by: String,
    },
    UpdateRole {
        role_id: String,
        modified_by: String,
    },
}

impl From<UserRoleUpdate> for UserRoleUpdateInternal {
    fn from(value: UserRoleUpdate) -> Self {
        let last_modified = common_utils::date_time::now();
        match value {
            UserRoleUpdate::UpdateRole {
                role_id,
                modified_by,
            } => Self {
                role_id: Some(role_id),
                last_modified_by: Some(modified_by),
                status: None,
                last_modified,
            },
            UserRoleUpdate::UpdateStatus {
                status,
                modified_by,
            } => Self {
                status: Some(status),
                last_modified,
                last_modified_by: Some(modified_by),
                role_id: None,
            },
        }
    }
}


pub struct ApiEvent {
    tenant_id: common_utils::id_type::TenantId,
    merchant_id: Option<common_utils::id_type::MerchantId>,
    api_flow: String,
    created_at_timestamp: i128,
    request_id: String,
    latency: u128,
    status_code: i64,
    #[serde(flatten)]
    auth_type: AuthenticationType,
    request: String,
    user_agent: Option<String>,
    ip_addr: Option<String>,
    url_path: String,
    response: Option<String>,
    error: Option<serde_json::Value>,
    #[serde(flatten)]
    event_type: ApiEventsType,
    hs_latency: Option<u128>,
    http_method: String,
    #[serde(flatten)]
    infra_components: Option<serde_json::Value>,
}


impl<V> ConstraintGraph<V>
    where
        V: ValueNode + NodeViz,
        <V as ValueNode>::Key: NodeViz,
    {
        fn get_node_label(node: &types::Node<V>) -> String {
            let label = match &node.node_type {
                types::NodeType::Value(types::NodeValue::Key(key)) => format!("any {}", key.viz()),
                types::NodeType::Value(types::NodeValue::Value(val)) => {
                    format!("{} = {}", val.get_key().viz(), val.viz())
                }
                types::NodeType::AllAggregator => "&&".to_string(),
                types::NodeType::AnyAggregator => "| |".to_string(),
                types::NodeType::InAggregator(agg) => {
                    let key = if let Some(val) = agg.iter().next() {
                        val.get_key().viz()
                    } else {
                        return "empty in".to_string();
                    };

                    let nodes = agg.iter().map(NodeViz::viz).collect::<Vec<_>>();
                    format!("{key} in [{}]", nodes.join(", "))
                }
            };

            format!("\"{label}\"")
        }

        fn build_node(cg_node_id: types::NodeId, cg_node: &types::Node<V>) -> Node {
            let viz_node_id = get_node_id(cg_node_id);
            let viz_node_label = Self::get_node_label(cg_node);

            node!(viz_node_id; attr!("label", viz_node_label))
        }

        fn build_edge(cg_edge: &types::Edge) -> Edge {
            let pred_vertex = get_node_id(cg_edge.pred);
            let succ_vertex = get_node_id(cg_edge.succ);
            let arrowhead = match cg_edge.strength {
                types::Strength::Weak => "onormal",
                types::Strength::Normal => "normal",
                types::Strength::Strong => "normalnormal",
            };
            let color = match cg_edge.relation {
                types::Relation::Positive => "blue",
                types::Relation::Negative => "red",
            };

            edge!(
                node_id!(pred_vertex) => node_id!(succ_vertex);
                attr!("arrowhead", arrowhead),
                attr!("color", color)
            )
        }

        pub fn get_viz_digraph(&self) -> Graph {
            graph!(
                strict di id!("constraint_graph"),
                self.nodes
                    .iter()
                    .map(|(node_id, node)| Self::build_node(node_id, node))
                    .map(Stmt::Node)
                    .chain(self.edges.values().map(Self::build_edge).map(Stmt::Edge))
                    .collect::<Vec<_>>()
            )
        }

        pub fn get_viz_digraph_string(&self) -> String {
            let mut ctx = PrinterContext::default();
            let digraph = self.get_viz_digraph();
            digraph.print(&mut ctx)
        }
    }


/// Here the `self` takes precedence
    pub fn unify_with_payment_data_billing(
        self,
        other_payment_method_billing: Option<Address>,
    ) -> Self {
        let unified_payment_method_billing = self
            .get_payment_method_billing()
            .map(|payment_method_billing| {
                payment_method_billing
                    .clone()
                    .unify_address(other_payment_method_billing.as_ref())
            })
            .or(other_payment_method_billing);

        Self {
            shipping: self.shipping,
            billing: self.billing,
            unified_payment_method_billing,
            payment_method_billing: self.payment_method_billing,
        }
    }


pub async fn find_by_merchant_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        get_disabled: bool,
    ) -> StorageResult<Vec<Self>> {
        if get_disabled {
            generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
                conn,
                dsl::merchant_id.eq(merchant_id.to_owned()),
                None,
                None,
                Some(dsl::created_at.asc()),
            )
            .await
        } else {
            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::disabled.eq(false)),
                None,
                None,
                None,
            )
            .await
        }
    }


File: crates/router/src/workflows/process_dispute.rs
 Public functions: 2
 Public structs: 1

use common_utils::ext_traits::{StringExt, ValueExt};
use diesel_models::process_tracker::business_status;
use error_stack::ResultExt;
use router_env::logger;
use scheduler::{
    consumer::{self, types::process_data, workflows::ProcessTrackerWorkflow},
    errors as sch_errors, utils as scheduler_utils,
};

#[cfg(feature = "v1")]
use crate::core::webhooks::incoming::get_payment_attempt_from_object_reference_id;
use crate::{
    core::disputes,
    db::StorageInterface,
    errors,
    routes::SessionState,
    types::{api, domain, storage},
};

pub struct ProcessDisputeWorkflow;

/// This workflow inserts only new dispute records into the dispute table and triggers related outgoing webhook
#[async_trait::async_trait]
impl ProcessTrackerWorkflow<SessionState> for ProcessDisputeWorkflow {
    #[cfg(feature = "v2")]
    async fn execute_workflow<'a>(
        &'a self,
        _state: &'a SessionState,
        _process: storage::ProcessTracker,
    ) -> Result<(), sch_errors::ProcessTrackerError> {
        todo!()
    }

    #[cfg(feature = "v1")]
    async fn execute_workflow<'a>(
        &'a self,
        state: &'a SessionState,
        process: storage::ProcessTracker,
    ) -> Result<(), sch_errors::ProcessTrackerError> {
        let db: &dyn StorageInterface = &*state.store;
        let tracking_data: api::ProcessDisputePTData = process
            .tracking_data
            .clone()
            .parse_value("ProcessDisputePTData")?;
        let key_manager_state = &state.into();
        let key_store = db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &tracking_data.merchant_id,
                &db.get_master_key().to_vec().into(),
            )
            .await?;

        let merchant_account = db
            .find_merchant_account_by_merchant_id(
                key_manager_state,
                &tracking_data.merchant_id,
                &key_store,
            )
            .await?;

        let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
            merchant_account.clone(),
            key_store.clone(),
        )));

        let payment_attempt = get_payment_attempt_from_object_reference_id(
            state,
            tracking_data.dispute_payload.object_reference_id.clone(),
            &merchant_context,
        )
        .await?;

        let business_profile = state
            .store
            .find_business_profile_by_profile_id(
                &(state).into(),
                merchant_context.get_merchant_key_store(),
                &payment_attempt.profile_id,
            )
            .await?;

        // Check if the dispute already exists
        let dispute = state
            .store
            .find_by_merchant_id_payment_id_connector_dispute_id(
                merchant_context.get_merchant_account().get_id(),
                &payment_attempt.payment_id,
                &tracking_data.dispute_payload.connector_dispute_id,
            )
            .await
            .ok()
            .flatten();

        if dispute.is_some() {
            // Dispute already exists — mark the process as complete
            state
                .store
                .as_scheduler()
                .finish_process_with_business_status(process, business_status::COMPLETED_BY_PT)
                .await?;
        } else {
            // Update dispute data
            let response = disputes::update_dispute_data(
                state,
                merchant_context,
                business_profile,
                dispute,
                tracking_data.dispute_payload,
                payment_attempt,
                tracking_data.connector_name.as_str(),
            )
            .await
            .map_err(|error| logger::error!("Dispute update failed: {error}"));

            match response {
                Ok(_) => {
                    state
                        .store
                        .as_scheduler()
                        .finish_process_with_business_status(
                            process,
                            business_status::COMPLETED_BY_PT,
                        )
                        .await?;
                }
                Err(_) => {
                    retry_sync_task(
                        db,
                        tracking_data.connector_name,
                        tracking_data.merchant_id,
                        process,
                    )
                    .await?;
                }
            }
        }
        Ok(())
    }

    async fn error_handler<'a>(
        &'a self,
        state: &'a SessionState,
        process: storage::ProcessTracker,
        error: sch_errors::ProcessTrackerError,
    ) -> errors::CustomResult<(), sch_errors::ProcessTrackerError> {
        consumer::consumer_error_handler(state.store.as_scheduler(), process, error).await
    }
}

pub async fn get_sync_process_schedule_time(
    db: &dyn StorageInterface,
    connector: &str,
    merchant_id: &common_utils::id_type::MerchantId,
    retry_count: i32,
) -> Result<Option<time::PrimitiveDateTime>, errors::ProcessTrackerError> {
    let mapping: common_utils::errors::CustomResult<
        process_data::ConnectorPTMapping,
        errors::StorageError,
    > = db
        .find_config_by_key(&format!("pt_mapping_{connector}"))
        .await
        .map(|value| value.config)
        .and_then(|config| {
            config
                .parse_struct("ConnectorPTMapping")
                .change_context(errors::StorageError::DeserializationFailed)
        });
    let mapping = match mapping {
        Ok(x) => x,
        Err(error) => {
            logger::info!(?error, "Redis Mapping Error");
            process_data::ConnectorPTMapping::default()
        }
    };
    let time_delta = scheduler_utils::get_schedule_time(mapping, merchant_id, retry_count);

    Ok(scheduler_utils::get_time_from_delta(time_delta))
}

/// Schedule the task for retry
///
/// Returns bool which indicates whether this was the last retry or not
pub async fn retry_sync_task(
    db: &dyn StorageInterface,
    connector: String,
    merchant_id: common_utils::id_type::MerchantId,
    pt: storage::ProcessTracker,
) -> Result<bool, sch_errors::ProcessTrackerError> {
    let schedule_time =
        get_sync_process_schedule_time(db, &connector, &merchant_id, pt.retry_count + 1).await?;

    match schedule_time {
        Some(s_time) => {
            db.as_scheduler().retry_process(pt, s_time).await?;
            Ok(false)
        }
        None => {
            db.as_scheduler()
                .finish_process_with_business_status(pt, business_status::RETRIES_EXCEEDED)
                .await?;
            Ok(true)
        }
    }
}