impl MerchantConnectorAccount {
    pub async fn update(
        self,
        conn: &PgPooledConn,
        merchant_connector_account: MerchantConnectorAccountUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
            conn,
            self.merchant_connector_id.to_owned(),
            merchant_connector_account,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn delete_by_merchant_id_merchant_connector_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_connector_id: &common_utils::id_type::MerchantConnectorAccountId,
    ) -> StorageResult<bool> {
        generics::generic_delete::<<Self as HasTable>::Table, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::merchant_connector_id.eq(merchant_connector_id.to_owned())),
        )
        .await
    }

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

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

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

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

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

    pub async fn list_enabled_by_profile_id(
        conn: &PgPooledConn,
        profile_id: &common_utils::id_type::ProfileId,
        connector_type: common_enums::ConnectorType,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::profile_id
                .eq(profile_id.to_owned())
                .and(dsl::disabled.eq(false))
                .and(dsl::connector_type.eq(connector_type)),
            None,
            None,
            Some(dsl::created_at.asc()),
        )
        .await
    }
}


pub struct ExternalVaultProxyFlowData {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub customer_id: Option<common_utils::id_type::CustomerId>,
    pub connector_customer: Option<String>,
    pub payment_id: String,
    pub attempt_id: String,
    pub status: common_enums::AttemptStatus,
    pub payment_method: common_enums::PaymentMethod,
    pub description: Option<String>,
    pub address: PaymentAddress,
    pub auth_type: common_enums::AuthenticationType,
    pub connector_meta_data: Option<pii::SecretSerdeValue>,
    pub amount_captured: Option<i64>,
    // minor amount for amount framework
    pub minor_amount_captured: Option<MinorUnit>,
    pub access_token: Option<AccessToken>,
    pub session_token: Option<String>,
    pub reference_id: Option<String>,
    pub payment_method_token: Option<PaymentMethodToken>,
    pub recurring_mandate_payment_data: Option<RecurringMandatePaymentData>,
    pub preprocessing_id: Option<String>,
    /// This is the balance amount for gift cards or voucher
    pub payment_method_balance: Option<PaymentMethodBalance>,

    ///for switching between two different versions of the same connector
    pub connector_api_version: Option<String>,
    /// Contains a reference ID that should be sent in the connector request
    pub connector_request_reference_id: String,

    pub test_mode: Option<bool>,
    pub connector_http_status_code: Option<u16>,
    pub external_latency: Option<u128>,
    /// Contains apple pay flow type simplified or manual
    pub apple_pay_flow: Option<ApplePayFlow>,

    /// This field is used to store various data regarding the response from connector
    pub connector_response: Option<ConnectorResponseData>,
    pub payment_method_status: Option<common_enums::PaymentMethodStatus>,
}


impl<'a> PaymentsDslInput<'a> {
    pub fn new(
        setup_mandate: Option<&'a mandates::MandateData>,
        payment_attempt: &'a storage::PaymentAttempt,
        payment_intent: &'a storage::PaymentIntent,
        payment_method_data: Option<&'a domain::PaymentMethodData>,
        address: &'a payment_address::PaymentAddress,
        recurring_details: Option<&'a mandates_api::RecurringDetails>,
        currency: storage_enums::Currency,
    ) -> Self {
        Self {
            setup_mandate,
            payment_attempt,
            payment_intent,
            payment_method_data,
            address,
            recurring_details,
            currency,
        }
    }
}


File: crates/router/src/bin/scheduler.rs
 Public functions: 5
 Public structs: 2

use std::{collections::HashMap, str::FromStr, sync::Arc};

use actix_web::{dev::Server, web, Scope};
use api_models::health_check::SchedulerHealthCheckResponse;
use common_utils::ext_traits::{OptionExt, StringExt};
use diesel_models::process_tracker::{self as storage, business_status};
use error_stack::ResultExt;
use router::{
    configs::settings::{CmdLineConf, Settings},
    core::{
        errors::{self, CustomResult},
        health_check::HealthCheckInterface,
    },
    logger, routes,
    services::{self, api},
    workflows,
};
use router_env::{
    instrument,
    tracing::{self, Instrument},
};
use scheduler::{
    consumer::workflows::ProcessTrackerWorkflow, errors::ProcessTrackerError,
    workflows::ProcessTrackerWorkflows, SchedulerSessionState,
};
use storage_impl::errors::ApplicationError;
use tokio::sync::{mpsc, oneshot};

const SCHEDULER_FLOW: &str = "SCHEDULER_FLOW";
#[tokio::main]
async fn main() -> CustomResult<(), ProcessTrackerError> {
    let cmd_line = <CmdLineConf as clap::Parser>::parse();

    #[allow(clippy::expect_used)]
    let conf = Settings::with_config_path(cmd_line.config_path)
        .expect("Unable to construct application configuration");
    let api_client = Box::new(
        services::ProxyClient::new(&conf.proxy)
            .change_context(ProcessTrackerError::ConfigurationError)?,
    );
    // channel for listening to redis disconnect events
    let (redis_shutdown_signal_tx, redis_shutdown_signal_rx) = oneshot::channel();
    let state = Box::pin(routes::AppState::new(
        conf,
        redis_shutdown_signal_tx,
        api_client,
    ))
    .await;
    // channel to shutdown scheduler gracefully
    let (tx, rx) = mpsc::channel(1);
    let _task_handle = tokio::spawn(
        router::receiver_for_error(redis_shutdown_signal_rx, tx.clone()).in_current_span(),
    );

    #[allow(clippy::expect_used)]
    let scheduler_flow_str =
        std::env::var(SCHEDULER_FLOW).expect("SCHEDULER_FLOW environment variable not set");
    #[allow(clippy::expect_used)]
    let scheduler_flow = scheduler::SchedulerFlow::from_str(&scheduler_flow_str)
        .expect("Unable to parse SchedulerFlow from environment variable");

    #[allow(clippy::print_stdout)] // The logger has not yet been initialized
    #[cfg(feature = "vergen")]
    {
        println!(
            "Starting {scheduler_flow} (Version: {})",
            router_env::git_tag!()
        );
    }

    let _guard = router_env::setup(
        &state.conf.log,
        &scheduler_flow_str,
        [router_env::service_name!()],
    );

    #[allow(clippy::expect_used)]
    let web_server = Box::pin(start_web_server(
        state.clone(),
        scheduler_flow_str.to_string(),
    ))
    .await
    .expect("Failed to create the server");

    let _task_handle = tokio::spawn(
        async move {
            let _ = web_server.await;
            logger::error!("The health check probe stopped working!");
        }
        .in_current_span(),
    );

    logger::debug!(startup_config=?state.conf);

    start_scheduler(&state, scheduler_flow, (tx, rx)).await?;

    logger::error!("Scheduler shut down");
    Ok(())
}

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

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

    Ok(web_server)
}

pub struct Health;

impl Health {
    pub fn server(state: routes::AppState, service: String) -> Scope {
        web::scope("health")
            .app_data(web::Data::new(state))
            .app_data(web::Data::new(service))
            .service(web::resource("").route(web::get().to(health)))
            .service(web::resource("/ready").route(web::get().to(deep_health_check)))
    }
}

#[instrument(skip_all)]
pub async fn health() -> impl actix_web::Responder {
    logger::info!("Scheduler health was called");
    actix_web::HttpResponse::Ok().body("Scheduler health is good")
}
#[instrument(skip_all)]
pub async fn deep_health_check(
    state: web::Data<routes::AppState>,
    service: web::Data<String>,
) -> impl actix_web::Responder {
    let mut checks = HashMap::new();
    let stores = state.stores.clone();
    let app_state = Arc::clone(&state.into_inner());
    let service_name = service.into_inner();
    for (tenant, _) in stores {
        let session_state_res = app_state.clone().get_session_state(&tenant, None, || {
            errors::ApiErrorResponse::MissingRequiredField {
                field_name: "tenant_id",
            }
            .into()
        });
        let session_state = match session_state_res {
            Ok(state) => state,
            Err(err) => {
                return api::log_and_return_error_response(err);
            }
        };
        let report = deep_health_check_func(session_state, &service_name).await;
        match report {
            Ok(response) => {
                checks.insert(
                    tenant,
                    serde_json::to_string(&response)
                        .map_err(|err| {
                            logger::error!(serialization_error=?err);
                        })
                        .unwrap_or_default(),
                );
            }
            Err(err) => {
                return api::log_and_return_error_response(err);
            }
        }
    }
    services::http_response_json(
        serde_json::to_string(&checks)
            .map_err(|err| {
                logger::error!(serialization_error=?err);
            })
            .unwrap_or_default(),
    )
}
#[instrument(skip_all)]
pub async fn deep_health_check_func(
    state: routes::SessionState,
    service: &str,
) -> errors::RouterResult<SchedulerHealthCheckResponse> {
    logger::info!("{} deep health check was called", service);

    logger::debug!("Database health check begin");

    let db_status = state
        .health_check_db()
        .await
        .map(|_| true)
        .map_err(|error| {
            let message = error.to_string();
            error.change_context(errors::ApiErrorResponse::HealthCheckError {
                component: "Database",
                message,
            })
        })?;

    logger::debug!("Database health check end");

    logger::debug!("Redis health check begin");

    let redis_status = state
        .health_check_redis()
        .await
        .map(|_| true)
        .map_err(|error| {
            let message = error.to_string();
            error.change_context(errors::ApiErrorResponse::HealthCheckError {
                component: "Redis",
                message,
            })
        })?;

    let outgoing_req_check =
        state
            .health_check_outgoing()
            .await
            .map(|_| true)
            .map_err(|error| {
                let message = error.to_string();
                error.change_context(errors::ApiErrorResponse::HealthCheckError {
                    component: "Outgoing Request",
                    message,
                })
            })?;

    logger::debug!("Redis health check end");

    let response = SchedulerHealthCheckResponse {
        database: db_status,
        redis: redis_status,
        outgoing_request: outgoing_req_check,
    };

    Ok(response)
}

#[derive(Debug, Copy, Clone)]
pub struct WorkflowRunner;

#[async_trait::async_trait]
impl ProcessTrackerWorkflows<routes::SessionState> for WorkflowRunner {
    async fn trigger_workflow<'a>(
        &'a self,
        state: &'a routes::SessionState,
        process: storage::ProcessTracker,
    ) -> CustomResult<(), ProcessTrackerError> {
        let runner = process
            .runner
            .clone()
            .get_required_value("runner")
            .change_context(ProcessTrackerError::MissingRequiredField)
            .attach_printable("Missing runner field in process information")?;
        let runner: storage::ProcessTrackerRunner = runner
            .parse_enum("ProcessTrackerRunner")
            .change_context(ProcessTrackerError::UnexpectedFlow)
            .attach_printable("Failed to parse workflow runner name")?;

        let get_operation = |runner: storage::ProcessTrackerRunner| -> CustomResult<
            Box<dyn ProcessTrackerWorkflow<routes::SessionState>>,
            ProcessTrackerError,
        > {
            match runner {
                storage::ProcessTrackerRunner::PaymentsSyncWorkflow => {
                    Ok(Box::new(workflows::payment_sync::PaymentsSyncWorkflow))
                }
                storage::ProcessTrackerRunner::RefundWorkflowRouter => {
                    Ok(Box::new(workflows::refund_router::RefundWorkflowRouter))
                }
                storage::ProcessTrackerRunner::ProcessDisputeWorkflow => {
                    Ok(Box::new(workflows::process_dispute::ProcessDisputeWorkflow))
                }
                storage::ProcessTrackerRunner::DisputeListWorkflow => {
                    Ok(Box::new(workflows::dispute_list::DisputeListWorkflow))
                }
                storage::ProcessTrackerRunner::DeleteTokenizeDataWorkflow => Ok(Box::new(
                    workflows::tokenized_data::DeleteTokenizeDataWorkflow,
                )),
                storage::ProcessTrackerRunner::ApiKeyExpiryWorkflow => {
                    #[cfg(feature = "email")]
                    {
                        Ok(Box::new(workflows::api_key_expiry::ApiKeyExpiryWorkflow))
                    }

                    #[cfg(not(feature = "email"))]
                    {
                        Err(error_stack::report!(ProcessTrackerError::UnexpectedFlow))
                            .attach_printable(
                                "Cannot run API key expiry workflow when email feature is disabled",
                            )
                    }
                }
                storage::ProcessTrackerRunner::OutgoingWebhookRetryWorkflow => Ok(Box::new(
                    workflows::outgoing_webhook_retry::OutgoingWebhookRetryWorkflow,
                )),
                storage::ProcessTrackerRunner::AttachPayoutAccountWorkflow => {
                    #[cfg(feature = "payouts")]
                    {
                        Ok(Box::new(
                            workflows::attach_payout_account_workflow::AttachPayoutAccountWorkflow,
                        ))
                    }
                    #[cfg(not(feature = "payouts"))]
                    {
                        Err(
                            error_stack::report!(ProcessTrackerError::UnexpectedFlow),
                        )
                        .attach_printable(
                            "Cannot run Stripe external account workflow when payouts feature is disabled",
                        )
                    }
                }
                storage::ProcessTrackerRunner::PaymentMethodStatusUpdateWorkflow => Ok(Box::new(
                    workflows::payment_method_status_update::PaymentMethodStatusUpdateWorkflow,
                )),
                storage::ProcessTrackerRunner::PassiveRecoveryWorkflow => {
                    Ok(Box::new(workflows::revenue_recovery::ExecutePcrWorkflow))
                }
            }
        };

        let operation = get_operation(runner)?;

        let app_state = &state.clone();
        let output = operation.execute_workflow(state, process.clone()).await;
        match output {
            Ok(_) => operation.success_handler(app_state, process).await,
            Err(error) => match operation
                .error_handler(app_state, process.clone(), error)
                .await
            {
                Ok(_) => (),
                Err(error) => {
                    logger::error!(?error, "Failed while handling error");
                    let status = state
                        .get_db()
                        .as_scheduler()
                        .finish_process_with_business_status(
                            process,
                            business_status::GLOBAL_FAILURE,
                        )
                        .await;
                    if let Err(error) = status {
                        logger::error!(
                            ?error,
                            "Failed while performing database operation: {}",
                            business_status::GLOBAL_FAILURE
                        );
                    }
                }
            },
        };
        Ok(())
    }
}

async fn start_scheduler(
    state: &routes::AppState,
    scheduler_flow: scheduler::SchedulerFlow,
    channel: (mpsc::Sender<()>, mpsc::Receiver<()>),
) -> CustomResult<(), ProcessTrackerError> {
    let scheduler_settings = state
        .conf
        .scheduler
        .clone()
        .ok_or(ProcessTrackerError::ConfigurationError)?;
    scheduler::start_process_tracker(
        state,
        scheduler_flow,
        Arc::new(scheduler_settings),
        channel,
        WorkflowRunner {},
        |state, tenant| {
            Arc::new(state.clone())
                .get_session_state(tenant, None, || ProcessTrackerError::TenantNotFound.into())
        },
    )
    .await
}


File: crates/api_models/src/events/chat.rs

use common_utils::events::{ApiEventMetric, ApiEventsType};

use crate::chat::{ChatRequest, ChatResponse};

common_utils::impl_api_event_type!(Chat, (ChatRequest, ChatResponse));


File: crates/router/src/routes/health.rs
 Public functions: 2

use actix_web::{web, HttpRequest};
use api_models::health_check::RouterHealthCheckResponse;
use router_env::{instrument, logger, tracing, Flow};

use super::app;
use crate::{
    core::{api_locking, health_check::HealthCheckInterface},
    errors::{self, RouterResponse},
    routes::metrics,
    services::{api, authentication as auth},
};
/// .
// #[logger::instrument(skip_all, name = "name1", level = "warn", fields( key1 = "val1" ))]
#[instrument(skip_all, fields(flow = ?Flow::HealthCheck))]
// #[actix_web::get("/health")]
pub async fn health() -> impl actix_web::Responder {
    metrics::HEALTH_METRIC.add(1, &[]);
    logger::info!("Health was called");

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

#[instrument(skip_all, fields(flow = ?Flow::DeepHealthCheck))]
pub async fn deep_health_check(
    state: web::Data<app::AppState>,
    request: HttpRequest,
) -> impl actix_web::Responder {
    metrics::HEALTH_METRIC.add(1, &[]);

    let flow = Flow::DeepHealthCheck;

    Box::pin(api::server_wrap(
        flow,
        state,
        &request,
        (),
        |state, _: (), _, _| deep_health_check_func(state),
        &auth::NoAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

async fn deep_health_check_func(
    state: app::SessionState,
) -> RouterResponse<RouterHealthCheckResponse> {
    logger::info!("Deep health check was called");

    logger::debug!("Database health check begin");

    let db_status = state.health_check_db().await.map_err(|error| {
        let message = error.to_string();
        error.change_context(errors::ApiErrorResponse::HealthCheckError {
            component: "Database",
            message,
        })
    })?;

    logger::debug!("Database health check end");

    logger::debug!("Redis health check begin");

    let redis_status = state.health_check_redis().await.map_err(|error| {
        let message = error.to_string();
        error.change_context(errors::ApiErrorResponse::HealthCheckError {
            component: "Redis",
            message,
        })
    })?;

    logger::debug!("Redis health check end");

    logger::debug!("Locker health check begin");

    let locker_status = state.health_check_locker().await.map_err(|error| {
        let message = error.to_string();
        error.change_context(errors::ApiErrorResponse::HealthCheckError {
            component: "Locker",
            message,
        })
    })?;

    logger::debug!("Locker health check end");

    logger::debug!("Analytics health check begin");

    #[cfg(feature = "olap")]
    let analytics_status = state.health_check_analytics().await.map_err(|error| {
        let message = error.to_string();
        error.change_context(errors::ApiErrorResponse::HealthCheckError {
            component: "Analytics",
            message,
        })
    })?;

    logger::debug!("Analytics health check end");

    logger::debug!("gRPC health check begin");

    #[cfg(feature = "dynamic_routing")]
    let grpc_health_check = state.health_check_grpc().await.map_err(|error| {
        let message = error.to_string();
        error.change_context(errors::ApiErrorResponse::HealthCheckError {
            component: "gRPC services",
            message,
        })
    })?;

    logger::debug!("gRPC health check end");

    logger::debug!("Decision Engine health check begin");

    #[cfg(feature = "dynamic_routing")]
    let decision_engine_health_check =
        state
            .health_check_decision_engine()
            .await
            .map_err(|error| {
                let message = error.to_string();
                error.change_context(errors::ApiErrorResponse::HealthCheckError {
                    component: "Decision Engine service",
                    message,
                })
            })?;

    logger::debug!("Decision Engine health check end");

    logger::debug!("Opensearch health check begin");

    #[cfg(feature = "olap")]
    let opensearch_status = state.health_check_opensearch().await.map_err(|error| {
        let message = error.to_string();
        error.change_context(errors::ApiErrorResponse::HealthCheckError {
            component: "Opensearch",
            message,
        })
    })?;

    logger::debug!("Opensearch health check end");

    logger::debug!("Outgoing Request health check begin");

    let outgoing_check = state.health_check_outgoing().await.map_err(|error| {
        let message = error.to_string();
        error.change_context(errors::ApiErrorResponse::HealthCheckError {
            component: "Outgoing Request",
            message,
        })
    })?;

    logger::debug!("Outgoing Request health check end");

    logger::debug!("Unified Connector Service health check begin");

    let unified_connector_service_status = state
        .health_check_unified_connector_service()
        .await
        .map_err(|error| {
            let message = error.to_string();
            error.change_context(errors::ApiErrorResponse::HealthCheckError {
                component: "Unified Connector Service",
                message,
            })
        })?;

    logger::debug!("Unified Connector Service health check end");

    let response = RouterHealthCheckResponse {
        database: db_status.into(),
        redis: redis_status.into(),
        vault: locker_status.into(),
        #[cfg(feature = "olap")]
        analytics: analytics_status.into(),
        #[cfg(feature = "olap")]
        opensearch: opensearch_status.into(),
        outgoing_request: outgoing_check.into(),
        #[cfg(feature = "dynamic_routing")]
        grpc_health_check,
        #[cfg(feature = "dynamic_routing")]
        decision_engine: decision_engine_health_check.into(),
        unified_connector_service: unified_connector_service_status.into(),
    };

    Ok(api::ApplicationResponse::Json(response))
}


pub struct MifinityClient {
    first_name: Secret<String>,
    last_name: Secret<String>,
    phone: Secret<String>,
    dialing_code: String,
    nationality: api_models::enums::CountryAlpha2,
    email_address: Email,
    dob: Secret<Date>,
}


pub fn with_source(payment_confirm_source: common_enums::PaymentSource) -> Self {
        Self {
            payment_confirm_source: Some(payment_confirm_source),
            ..Default::default()
        }
    }


pub(super) struct AwsFileStorageClient {
    /// AWS S3 client
    inner_client: Client,
    /// The name of the AWS S3 bucket.
    bucket_name: String,
}


impl api::ConnectorAccessToken for Taxjar {}

impl api::ConnectorAccessToken for for Taxjar


pub struct NovalnetPaymentsRequestCustomer {
    first_name: Option<Secret<String>>,
    last_name: Option<Secret<String>>,
    email: Email,
    mobile: Option<Secret<String>>,
    billing: Option<NovalnetPaymentsRequestBilling>,
    no_nc: i64,
}


pub struct SchemaMeta {
    struct_name: syn::Ident,
    type_ident: syn::Ident,
}


pub struct Parameters {
    name: String,
    value: String,
}


OpenAPI Block Path: paths."/payments/{payment_id}/3ds/authentication"
{
  "post": {
    "tags": [
      "Payments"
    ],
    "summary": "Payments - External 3DS Authentication",
    "description": "External 3DS Authentication is performed and returns the AuthenticationResponse",
    "operationId": "Initiate external authentication for a Payment",
    "parameters": [
      {
        "name": "payment_id",
        "in": "path",
        "description": "The identifier for payment",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    ],
    "requestBody": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/PaymentsExternalAuthenticationRequest"
          }
        }
      },
      "required": true
    },
    "responses": {
      "200": {
        "description": "Authentication created",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PaymentsExternalAuthenticationResponse"
            }
          }
        }
      },
      "400": {
        "description": "Missing mandatory fields",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GenericErrorResponseOpenApi"
            }
          }
        }
      }
    },
    "security": [
      {
        "publishable_key": []
      }
    ]
  }
}


File: crates/analytics/src/api_event/metrics/latency.rs
 Public structs: 1

use std::collections::HashSet;

use api_models::analytics::{
    api_event::{ApiEventDimensions, ApiEventFilters, ApiEventMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

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

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

#[async_trait::async_trait]
impl<T> super::ApiEventMetric<T> for MaxLatency
where
    T: AnalyticsDataSource + super::ApiEventMetricAnalytics,
    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: &[ApiEventDimensions],
        merchant_id: &common_utils::id_type::MerchantId,
        filters: &ApiEventFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(ApiEventMetricsBucketIdentifier, ApiEventMetricRow)>> {
        let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::ApiEvents);

        query_builder
            .add_select_column(Aggregate::Sum {
                field: "latency",
                alias: Some("latency_sum"),
            })
            .switch()?;

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

        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;
        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

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

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

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

        query_builder
            .add_custom_filter_clause("request", "10.63.134.6", FilterTypes::NotLike)
            .attach_printable("Error filtering out locker IP")
            .switch()?;

        query_builder
            .execute_query::<LatencyAvg, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    ApiEventMetricsBucketIdentifier::new(TimeRange {
                        start_time: match (granularity, i.start_bucket) {
                            (Some(g), Some(st)) => g.clip_to_start(st)?,
                            _ => time_range.start_time,
                        },
                        end_time: granularity.as_ref().map_or_else(
                            || Ok(time_range.end_time),
                            |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                        )?,
                    }),
                    ApiEventMetricRow {
                        latency: if i.latency_count != 0 {
                            Some(i.latency_sum.unwrap_or(0) / i.latency_count)
                        } else {
                            None
                        },
                        api_count: None,
                        status_code_count: None,
                        start_bucket: i.start_bucket,
                        end_bucket: i.end_bucket,
                    },
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(ApiEventMetricsBucketIdentifier, ApiEventMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}

#[derive(Debug, PartialEq, Eq, serde::Deserialize)]
pub struct LatencyAvg {
    latency_sum: Option<u64>,
    latency_count: u64,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub start_bucket: Option<PrimitiveDateTime>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub end_bucket: Option<PrimitiveDateTime>,
}


pub async fn get_connector_choice<F, Req, D>(
    operation: &BoxedOperation<'_, F, Req, D>,
    state: &SessionState,
    req: &Req,
    merchant_context: &domain::MerchantContext,
    business_profile: &domain::Profile,
    payment_data: &mut D,
    eligible_connectors: Option<Vec<enums::RoutableConnectors>>,
    mandate_type: Option<api::MandateTransactionType>,
) -> RouterResult<Option<ConnectorCallType>>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let connector_choice = operation
        .to_domain()?
        .get_connector(
            merchant_context,
            &state.clone(),
            req,
            payment_data.get_payment_intent(),
        )
        .await?;

    let connector = if should_call_connector(operation, payment_data) {
        Some(match connector_choice {
            api::ConnectorChoice::SessionMultiple(connectors) => {
                let routing_output = perform_session_token_routing(
                    state.clone(),
                    merchant_context,
                    business_profile,
                    payment_data,
                    connectors,
                )
                .await?;
                ConnectorCallType::SessionMultiple(routing_output)
            }

            api::ConnectorChoice::StraightThrough(straight_through) => {
                connector_selection(
                    state,
                    merchant_context,
                    business_profile,
                    payment_data,
                    Some(straight_through),
                    eligible_connectors,
                    mandate_type,
                )
                .await?
            }

            api::ConnectorChoice::Decide => {
                connector_selection(
                    state,
                    merchant_context,
                    business_profile,
                    payment_data,
                    None,
                    eligible_connectors,
                    mandate_type,
                )
                .await?
            }
        })
    } else if let api::ConnectorChoice::StraightThrough(algorithm) = connector_choice {
        update_straight_through_routing(payment_data, algorithm)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to update straight through routing algorithm")?;

        None
    } else {
        None
    };
    Ok(connector)
}


pub struct SearchFilters {
    pub payment_method: Option<Vec<String>>,
    pub currency: Option<Vec<String>>,
    pub status: Option<Vec<String>>,
    pub customer_email: Option<Vec<HashedString<common_utils::pii::EmailStrategy>>>,
    pub search_tags: Option<Vec<HashedString<WithType>>>,
    pub connector: Option<Vec<String>>,
    pub payment_method_type: Option<Vec<String>>,
    pub card_network: Option<Vec<String>>,
    pub card_last_4: Option<Vec<String>>,
    pub payment_id: Option<Vec<String>>,
    pub amount: Option<Vec<u64>>,
    pub customer_id: Option<Vec<String>>,
}


impl<const T: u8> RefundSync for DummyConnector<T> {}


File: crates/router/src/core/blocklist/transformers.rs
 Public functions: 1

use api_models::{blocklist, enums as api_enums};
use common_utils::{
    ext_traits::{Encode, StringExt},
    request::RequestContent,
};
use error_stack::ResultExt;
use josekit::jwe;
use masking::{PeekInterface, StrongSecret};
use router_env::{instrument, tracing};

use crate::{
    configs::settings,
    core::{
        errors::{self, CustomResult},
        payment_methods::transformers as payment_methods,
    },
    headers, routes,
    services::{api as services, encryption, EncryptionAlgorithm},
    types::{storage, transformers::ForeignFrom},
    utils::ConnectorResponseExt,
};

const LOCKER_FINGERPRINT_PATH: &str = "/cards/fingerprint";

impl ForeignFrom<storage::Blocklist> for blocklist::AddToBlocklistResponse {
    fn foreign_from(from: storage::Blocklist) -> Self {
        Self {
            fingerprint_id: from.fingerprint_id,
            data_kind: from.data_kind,
            created_at: from.created_at,
        }
    }
}

async fn generate_fingerprint_request(
    jwekey: &settings::Jwekey,
    locker: &settings::Locker,
    payload: &blocklist::GenerateFingerprintRequest,
    locker_choice: api_enums::LockerChoice,
) -> CustomResult<services::Request, errors::VaultError> {
    let payload = payload
        .encode_to_vec()
        .change_context(errors::VaultError::RequestEncodingFailed)?;

    let private_key = jwekey.vault_private_key.peek().as_bytes();

    let jws = encryption::jws_sign_payload(&payload, &locker.locker_signing_key_id, private_key)
        .await
        .change_context(errors::VaultError::RequestEncodingFailed)?;

    let jwe_payload = generate_jwe_payload_for_request(jwekey, &jws, locker_choice).await?;
    let mut url = match locker_choice {
        api_enums::LockerChoice::HyperswitchCardVault => locker.host.to_owned(),
    };
    url.push_str(LOCKER_FINGERPRINT_PATH);
    let mut request = services::Request::new(services::Method::Post, &url);
    request.add_header(headers::CONTENT_TYPE, "application/json".into());
    request.set_body(RequestContent::Json(Box::new(jwe_payload)));
    Ok(request)
}

async fn generate_jwe_payload_for_request(
    jwekey: &settings::Jwekey,
    jws: &str,
    locker_choice: api_enums::LockerChoice,
) -> CustomResult<encryption::JweBody, errors::VaultError> {
    let jws_payload: Vec<&str> = jws.split('.').collect();

    let generate_jws_body = |payload: Vec<&str>| -> Option<encryption::JwsBody> {
        Some(encryption::JwsBody {
            header: payload.first()?.to_string(),
            payload: payload.get(1)?.to_string(),
            signature: payload.get(2)?.to_string(),
        })
    };

    let jws_body =
        generate_jws_body(jws_payload).ok_or(errors::VaultError::GenerateFingerprintFailed)?;

    let payload = jws_body
        .encode_to_vec()
        .change_context(errors::VaultError::GenerateFingerprintFailed)?;

    let public_key = match locker_choice {
        api_enums::LockerChoice::HyperswitchCardVault => {
            jwekey.vault_encryption_key.peek().as_bytes()
        }
    };

    let jwe_encrypted =
        encryption::encrypt_jwe(&payload, public_key, EncryptionAlgorithm::A256GCM, None)
            .await
            .change_context(errors::VaultError::SaveCardFailed)
            .attach_printable("Error on jwe encrypt")?;
    let jwe_payload: Vec<&str> = jwe_encrypted.split('.').collect();

    let generate_jwe_body = |payload: Vec<&str>| -> Option<encryption::JweBody> {
        Some(encryption::JweBody {
            header: payload.first()?.to_string(),
            iv: payload.get(2)?.to_string(),
            encrypted_payload: payload.get(3)?.to_string(),
            tag: payload.get(4)?.to_string(),
            encrypted_key: payload.get(1)?.to_string(),
        })
    };

    let jwe_body =
        generate_jwe_body(jwe_payload).ok_or(errors::VaultError::GenerateFingerprintFailed)?;

    Ok(jwe_body)
}

#[instrument(skip_all)]
pub async fn generate_fingerprint(
    state: &routes::SessionState,
    card_number: StrongSecret<String>,
    hash_key: StrongSecret<String>,
    locker_choice: api_enums::LockerChoice,
) -> CustomResult<blocklist::GenerateFingerprintResponsePayload, errors::VaultError> {
    let payload = blocklist::GenerateFingerprintRequest {
        card: blocklist::Card { card_number },
        hash_key,
    };

    let generate_fingerprint_resp =
        call_to_locker_for_fingerprint(state, &payload, locker_choice).await?;

    Ok(generate_fingerprint_resp)
}

#[instrument(skip_all)]
async fn call_to_locker_for_fingerprint(
    state: &routes::SessionState,
    payload: &blocklist::GenerateFingerprintRequest,
    locker_choice: api_enums::LockerChoice,
) -> CustomResult<blocklist::GenerateFingerprintResponsePayload, errors::VaultError> {
    let locker = &state.conf.locker;
    let jwekey = state.conf.jwekey.get_inner();

    let request = generate_fingerprint_request(jwekey, locker, payload, locker_choice).await?;
    let response = services::call_connector_api(state, request, "call_locker_to_get_fingerprint")
        .await
        .change_context(errors::VaultError::GenerateFingerprintFailed);
    let jwe_body: encryption::JweBody = response
        .get_response_inner("JweBody")
        .change_context(errors::VaultError::GenerateFingerprintFailed)?;

    let decrypted_payload = decrypt_generate_fingerprint_response_payload(
        jwekey,
        jwe_body,
        Some(locker_choice),
        locker.decryption_scheme.clone(),
    )
    .await
    .change_context(errors::VaultError::GenerateFingerprintFailed)
    .attach_printable("Error getting decrypted fingerprint response payload")?;
    let generate_fingerprint_response: blocklist::GenerateFingerprintResponsePayload =
        decrypted_payload
            .parse_struct("GenerateFingerprintResponse")
            .change_context(errors::VaultError::ResponseDeserializationFailed)?;

    Ok(generate_fingerprint_response)
}

async fn decrypt_generate_fingerprint_response_payload(
    jwekey: &settings::Jwekey,
    jwe_body: encryption::JweBody,
    locker_choice: Option<api_enums::LockerChoice>,
    decryption_scheme: settings::DecryptionScheme,
) -> CustomResult<String, errors::VaultError> {
    let target_locker = locker_choice.unwrap_or(api_enums::LockerChoice::HyperswitchCardVault);

    let public_key = match target_locker {
        api_enums::LockerChoice::HyperswitchCardVault => {
            jwekey.vault_encryption_key.peek().as_bytes()
        }
    };

    let private_key = jwekey.vault_private_key.peek().as_bytes();

    let jwt = payment_methods::get_dotted_jwe(jwe_body);
    let alg = match decryption_scheme {
        settings::DecryptionScheme::RsaOaep => jwe::RSA_OAEP,
        settings::DecryptionScheme::RsaOaep256 => jwe::RSA_OAEP_256,
    };

    let jwe_decrypted = encryption::decrypt_jwe(
        &jwt,
        encryption::KeyIdCheck::SkipKeyIdCheck,
        private_key,
        alg,
    )
    .await
    .change_context(errors::VaultError::SaveCardFailed)
    .attach_printable("Jwe Decryption failed for JweBody for vault")?;

    let jws = jwe_decrypted
        .parse_struct("JwsBody")
        .change_context(errors::VaultError::ResponseDeserializationFailed)?;
    let jws_body = payment_methods::get_dotted_jws(jws);

    encryption::verify_sign(jws_body, public_key)
        .change_context(errors::VaultError::SaveCardFailed)
        .attach_printable("Jws Decryption failed for JwsBody for vault")
}


OpenAPI Block Path: components.schemas.PaymentMethodDeleteResponse
{
  "type": "object",
  "required": [
    "payment_method_id",
    "deleted"
  ],
  "properties": {
    "payment_method_id": {
      "type": "string",
      "description": "The unique identifier of the Payment method",
      "example": "card_rGK4Vi5iSW70MY7J2mIg"
    },
    "deleted": {
      "type": "boolean",
      "description": "Whether payment method was deleted or not",
      "example": true
    }
  }
}


File: crates/hyperswitch_domain_models/src/ext_traits.rs

use common_utils::{
    errors::{self, CustomResult},
    ext_traits::ValueExt,
    fp_utils::when,
};
use error_stack::ResultExt;

use crate::errors::api_error_response;

pub type DomainResult<T> = CustomResult<T, api_error_response::ApiErrorResponse>;
pub trait OptionExt<T> {
    fn check_value_present(&self, field_name: &'static str) -> DomainResult<()>;

    fn get_required_value(self, field_name: &'static str) -> DomainResult<T>;

    fn parse_enum<E>(self, enum_name: &'static str) -> CustomResult<E, errors::ParsingError>
    where
        T: AsRef<str>,
        E: std::str::FromStr,
        // Requirement for converting the `Err` variant of `FromStr` to `Report<Err>`
        <E as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static;

    fn parse_value<U>(self, type_name: &'static str) -> CustomResult<U, errors::ParsingError>
    where
        T: ValueExt,
        U: serde::de::DeserializeOwned;

    fn update_value(&mut self, value: Option<T>);
}

impl<T> OptionExt<T> for Option<T> {
    fn check_value_present(&self, field_name: &'static str) -> DomainResult<()> {
        when(self.is_none(), || {
            Err(error_stack::Report::new(
                api_error_response::ApiErrorResponse::MissingRequiredField { field_name },
            )
            .attach_printable(format!("Missing required field {field_name}")))
        })
    }

    // This will allow the error message that was generated in this function to point to the call site
    #[track_caller]
    fn get_required_value(self, field_name: &'static str) -> DomainResult<T> {
        match self {
            Some(v) => Ok(v),
            None => Err(error_stack::Report::new(
                api_error_response::ApiErrorResponse::MissingRequiredField { field_name },
            )
            .attach_printable(format!("Missing required field {field_name}"))),
        }
    }

    fn parse_enum<E>(self, enum_name: &'static str) -> CustomResult<E, errors::ParsingError>
    where
        T: AsRef<str>,
        E: std::str::FromStr,
        <E as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static,
    {
        let value = self
            .get_required_value(enum_name)
            .change_context(errors::ParsingError::UnknownError)?;

        E::from_str(value.as_ref())
            .change_context(errors::ParsingError::UnknownError)
            .attach_printable_lazy(|| format!("Invalid {{ {enum_name} }} "))
    }

    fn parse_value<U>(self, type_name: &'static str) -> CustomResult<U, errors::ParsingError>
    where
        T: ValueExt,
        U: serde::de::DeserializeOwned,
    {
        let value = self
            .get_required_value(type_name)
            .change_context(errors::ParsingError::UnknownError)?;
        value.parse_value(type_name)
    }

    fn update_value(&mut self, value: Self) {
        if let Some(a) = value {
            *self = Some(a)
        }
    }
}


pub struct SingleRefundResponseData {
    refund_status: String, // quick-xml only parse string as a field
    refund_id: String,
}


pub struct Authorization {
    #[serde(rename = "@id")]
    pub id: String,
    #[serde(rename = "@reportGroup")]
    pub report_group: String,
    #[serde(rename = "@customerId", skip_serializing_if = "Option::is_none")]
    pub customer_id: Option<String>,
    pub order_id: String,
    pub amount: MinorUnit,
    pub order_source: OrderSource,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bill_to_address: Option<VantivAddressData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ship_to_address: Option<VantivAddressData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub card: Option<WorldpayvantivCardData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub token: Option<TokenizationData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub enhanced_data: Option<EnhancedData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub processing_type: Option<VantivProcessingType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub original_network_transaction_id: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub allow_partial_auth: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cardholder_authentication: Option<CardholderAuthentication>,
}


impl MandateUpdateInternal {
    pub fn apply_changeset(self, source: Mandate) -> Mandate {
        let Self {
            mandate_status,
            amount_captured,
            connector_mandate_ids,
            connector_mandate_id,
            payment_method_id,
            original_payment_id,
            updated_by,
        } = self;

        Mandate {
            mandate_status: mandate_status.unwrap_or(source.mandate_status),
            amount_captured: amount_captured.map_or(source.amount_captured, Some),
            connector_mandate_ids: connector_mandate_ids.map_or(source.connector_mandate_ids, Some),
            connector_mandate_id: connector_mandate_id.map_or(source.connector_mandate_id, Some),
            payment_method_id: payment_method_id.unwrap_or(source.payment_method_id),
            original_payment_id: original_payment_id.map_or(source.original_payment_id, Some),
            updated_by: updated_by.map_or(source.updated_by, Some),
            ..source
        }
    }
}


impl api::PaymentSession for Bankofamerica {}

impl api::PaymentSession for for Bankofamerica


File: crates/hyperswitch_interfaces/src/authentication.rs
 Public structs: 1

//! Authentication interface

/// struct ExternalAuthenticationPayload
#[derive(Clone, serde::Deserialize, Debug, serde::Serialize, PartialEq, Eq)]
pub struct ExternalAuthenticationPayload {
    /// trans_status
    pub trans_status: common_enums::TransactionStatus,
    /// authentication_value
    pub authentication_value: Option<masking::Secret<String>>,
    /// eci
    pub eci: Option<String>,
    /// Indicates whether the challenge was canceled by the user or system.
    pub challenge_cancel: Option<String>,
    /// Reason for the challenge code, if applicable.
    pub challenge_code_reason: Option<String>,
}


pub struct DummyConnectorAuthType {
    pub(super) api_key: Secret<String>,
}


pub(super) fn log_event<T: KafkaMessage>(&self, event: &T) {
        logger::info!(event = ?event.masked_serialize().unwrap_or_else(|e| serde_json::json!({"error": e.to_string()})), event_type =? event.event_type(), event_id =? event.key(), log_type =? "event");
    }


impl ConnectorValidation for Noon {
    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<enums::CaptureMethod>,
        _payment_method: enums::PaymentMethod,
        _pmt: Option<enums::PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        let capture_method = capture_method.unwrap_or_default();
        match capture_method {
            enums::CaptureMethod::Automatic
            | enums::CaptureMethod::Manual
            | enums::CaptureMethod::SequentialAutomatic => Ok(()),
            enums::CaptureMethod::ManualMultiple | enums::CaptureMethod::Scheduled => Err(
                connector_utils::construct_not_implemented_error_report(capture_method, self.id()),
            ),
        }
    }

    fn validate_mandate_payment(
        &self,
        pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        let mandate_supported_pmd = std::collections::HashSet::from([
            PaymentMethodDataType::Card,
            PaymentMethodDataType::ApplePay,
            PaymentMethodDataType::GooglePay,
        ]);
        connector_utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id())
    }

    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> {
        // since we can make psync call with our reference_id, having connector_transaction_id is not an mandatory criteria
        Ok(())
    }
}


pub fn build_paypal_post_request<T>(
    url: String,
    body: T,
    access_token: String,
) -> RouterResult<Request>
where
    T: serde::Serialize + Send + 'static,
{
    Ok(RequestBuilder::new()
        .method(Method::Post)
        .url(&url)
        .attach_default_headers()
        .header(
            header::AUTHORIZATION.to_string().as_str(),
            format!("Bearer {access_token}").as_str(),
        )
        .header(
            header::CONTENT_TYPE.to_string().as_str(),
            "application/json",
        )
        .set_body(RequestContent::Json(Box::new(body)))
        .build())
}


pub struct AddVault;


File: crates/common_types/src/callback_mapper.rs
 Public functions: 1

use common_utils::id_type;
use diesel::{AsExpression, FromSqlRow};

#[derive(
    Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, AsExpression, FromSqlRow,
)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
/// Represents the data associated with a callback mapper.
pub enum CallbackMapperData {
    /// data variant used while processing the network token webhook
    NetworkTokenWebhook {
        /// Merchant id associated with the network token requestor reference id
        merchant_id: id_type::MerchantId,
        /// Payment Method id associated with the network token requestor reference id
        payment_method_id: String,
        /// Customer id associated with the network token requestor reference id
        customer_id: id_type::CustomerId,
    },
}

impl CallbackMapperData {
    /// Retrieves the details of the network token webhook type from callback mapper data.
    pub fn get_network_token_webhook_details(
        &self,
    ) -> (id_type::MerchantId, String, id_type::CustomerId) {
        match self {
            Self::NetworkTokenWebhook {
                merchant_id,
                payment_method_id,
                customer_id,
            } => (
                merchant_id.clone(),
                payment_method_id.clone(),
                customer_id.clone(),
            ),
        }
    }
}

common_utils::impl_to_sql_from_sql_json!(CallbackMapperData);


pub struct NetworkTokenErrorInfo {
    pub code: String,
    pub developer_message: String,
}


pub struct DisputeFilterValue {
    pub dimension: DisputeDimensions,
    pub values: Vec<String>,
}


pub fn server(state: AppState) -> Scope {
        web::scope("/v2/profiles")
            .app_data(web::Data::new(state))
            .service(web::resource("").route(web::post().to(profiles::profile_create)))
            .service(
                web::scope("/{profile_id}")
                    .service(
                        web::resource("")
                            .route(web::get().to(profiles::profile_retrieve))
                            .route(web::put().to(profiles::profile_update)),
                    )
                    .service(
                        web::resource("/connector-accounts")
                            .route(web::get().to(admin::connector_list)),
                    )
                    .service(
                        web::resource("/fallback-routing")
                            .route(web::get().to(routing::routing_retrieve_default_config))
                            .route(web::patch().to(routing::routing_update_default_config)),
                    )
                    .service(
                        web::resource("/activate-routing-algorithm").route(web::patch().to(
                            |state, req, path, payload| {
                                routing::routing_link_config(
                                    state,
                                    req,
                                    path,
                                    payload,
                                    &TransactionType::Payment,
                                )
                            },
                        )),
                    )
                    .service(
                        web::resource("/deactivate-routing-algorithm").route(web::patch().to(
                            |state, req, path| {
                                routing::routing_unlink_config(
                                    state,
                                    req,
                                    path,
                                    &TransactionType::Payment,
                                )
                            },
                        )),
                    )
                    .service(web::resource("/routing-algorithm").route(web::get().to(
                        |state, req, query_params, path| {
                            routing::routing_retrieve_linked_config(
                                state,
                                req,
                                query_params,
                                path,
                                &TransactionType::Payment,
                            )
                        },
                    )))
                    .service(
                        web::resource("/decision")
                            .route(web::put().to(routing::upsert_decision_manager_config))
                            .route(web::get().to(routing::retrieve_decision_manager_config)),
                    ),
            )
    }


impl CustomerUpdateInternal {
    pub fn apply_changeset(self, source: Customer) -> Customer {
        let Self {
            name,
            email,
            phone,
            description,
            phone_country_code,
            metadata,
            connector_customer,
            default_payment_method_id,
            default_billing_address,
            default_shipping_address,
            status,
            tax_registration_id,
            ..
        } = self;

        Customer {
            name: name.map_or(source.name, Some),
            email: email.map_or(source.email, Some),
            phone: phone.map_or(source.phone, Some),
            description: description.map_or(source.description, Some),
            phone_country_code: phone_country_code.map_or(source.phone_country_code, Some),
            metadata: metadata.map_or(source.metadata, Some),
            modified_at: common_utils::date_time::now(),
            connector_customer: connector_customer.map_or(source.connector_customer, Some),
            default_payment_method_id: default_payment_method_id
                .flatten()
                .map_or(source.default_payment_method_id, Some),
            default_billing_address: default_billing_address
                .map_or(source.default_billing_address, Some),
            default_shipping_address: default_shipping_address
                .map_or(source.default_shipping_address, Some),
            status: status.unwrap_or(source.status),
            tax_registration_id: tax_registration_id.map_or(source.tax_registration_id, Some),
            ..source
        }
    }
}


                    ),
                    ("merchant_id", merchant_id.clone()),
                ),
            ),
        }
    }
}

pub fn add_apple_pay_payment_status_metrics(
    payment_attempt_status: enums::AttemptStatus,
    apple_pay_flow: Option<domain::ApplePayFlow>,
    connector: Option<String>,
    merchant_id: id_type::MerchantId,
) {
    if payment_attempt_status == enums::AttemptStatus::Charged {
        if let Some(flow) = apple_pay_flow {
            match flow {
                domain::ApplePayFlow::Simplified(_) => {
                    metrics::APPLE_PAY_SIMPLIFIED_FLOW_SUCCESSFUL_PAYMENT.add(
                        1,
                        router_env::metric_attributes!(
                            (
                                "connector",
                                connector.to_owned().unwrap_or("null".to_string()),
                            ),
                            ("merchant_id", merchant_id.clone()),
                        ),
                    )
                }
                domain::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW_SUCCESSFUL_PAYMENT
                    .add(
                        1,
                        router_env::metric_attributes!(
                            (
                                "connector",
                                connector.to_owned().unwrap_or("null".to_string()),
                            ),
                            ("merchant_id", merchant_id.clone()),
                        ),
                    ),
            }
        }
    } else if payment_attempt_status == enums::AttemptStatus::Failure {
        if let Some(flow) = apple_pay_flow {
            match flow {
                domain::ApplePayFlow::Simplified(_) => {
                    metrics::APPLE_PAY_SIMPLIFIED_FLOW_FAILED_PAYMENT.add(
                        1,
                        router_env::metric_attributes!(
                            (
                                "connector",
                                connector.to_owned().unwrap_or("null".to_string()),
                            ),
                            ("merchant_id", merchant_id.clone()),
                        ),
                    )
                }
                domain::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW_FAILED_PAYMENT.add(
                    1,
                    router_env::metric_attributes!(
                        (
                            "connector",
                            connector.to_owned().unwrap_or("null".to_string()),
                        ),
                        ("merchant_id", merchant_id.clone()),
                    ),
                ),
            }
        }
    }
}

pub fn check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata(
    metadata: Option<Value>,
) -> bool {
    let external_three_ds_connector_metadata: Option<ExternalThreeDSConnectorMetadata> = metadata
            .parse_value("ExternalThreeDSConnectorMetadata")
            .map_err(|err| logger::warn!(parsing_error=?err,"Error while parsing ExternalThreeDSConnectorMetadata"))
            .ok();
    external_three_ds_connector_metadata
        .and_then(|metadata| metadata.pull_mechanism_for_external_3ds_enabled)
        .unwrap_or(true)
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
pub async fn trigger_payments_webhook<F, Op, D>(
    merchant_account: domain::MerchantAccount,
    business_profile: domain::Profile,
    key_store: &domain::MerchantKeyStore,
    payment_data: D,
    customer: Option<domain::Customer>,
    state: &SessionState,
    operation: Op,
) -> RouterResult<()>
where
    F: Send + Clone + Sync,
    Op: Debug,
    D: payments_core::OperationSessionGetters<F>,
{
    todo!()
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
pub async fn trigger_payments_webhook<F, Op, D>(
    merchant_context: domain::MerchantContext,
    business_profile: domain::Profile,
    payment_data: D,
    customer: Option<domain::Customer>,
    state: &SessionState,
    operation: Op,
) -> RouterResult<()>
where
    F: Send + Clone + Sync,
    Op: Debug,
    D: payments_core::OperationSessionGetters<F>,
{
    let status = payment_data.get_payment_intent().status;
    let should_trigger_webhook = business_profile
        .get_payment_webhook_statuses()
        .contains(&status);

    if should_trigger_webhook {
        let captures = payment_data
            .get_multiple_capture_data()
            .map(|multiple_capture_data| {
                multiple_capture_data
                    .get_all_captures()
                    .into_iter()
                    .cloned()
                    .collect()
            });
        let payment_id = payment_data.get_payment_intent().get_id().to_owned();
        let payments_response = crate::core::payments::transformers::payments_to_payments_response(
            payment_data,
            captures,
            customer,
            services::AuthFlow::Merchant,
            &state.base_url,
            &operation,
            &state.conf.connector_request_reference_id_config,
            None,
            None,
            None,
        )?;

        let event_type = status.into();

        if let services::ApplicationResponse::JsonWithHeaders((payments_response_json, _)) =
            payments_response
        {
            let cloned_state = state.clone();
            // This spawns this futures in a background thread, the exception inside this future won't affect
            // the current thread and the lifecycle of spawn thread is not handled by runtime.
            // So when server shutdown won't wait for this thread's completion.

            if let Some(event_type) = event_type {
                tokio::spawn(
                    async move {
                        let primary_object_created_at = payments_response_json.created;
                        Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook(
                            cloned_state,
                            merchant_context.clone(),
                            business_profile,
                            event_type,
                            diesel_models::enums::EventClass::Payments,
                            payment_id.get_string_repr().to_owned(),
                            diesel_models::enums::EventObjectType::PaymentDetails,
                            webhooks::OutgoingWebhookContent::PaymentDetails(Box::new(
                                payments_response_json,
                            )),
                            primary_object_created_at,
                        ))
                        .await
                    }
                    .in_current_span(),
                );
            } else {
                logger::warn!(
                    "Outgoing webhook not sent because of missing event type status mapping"
                );
            }
        }
    }

    Ok(())
}

type Handle<T> = tokio::task::JoinHandle<RouterResult<T>>;

pub async fn flatten_join_error<T>(handle: Handle<T>) -> RouterResult<T> {
    match handle.await {
        Ok(Ok(t)) => Ok(t),
        Ok(Err(err)) => Err(err),
        Err(err) => Err(err)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Join Error"),
    }
}

#[cfg(feature = "v1")]
pub async fn trigger_refund_outgoing_webhook(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    refund: &diesel_models::Refund,
    profile_id: id_type::ProfileId,
) -> RouterResult<()> {
    let refund_status = refund.refund_status;

    let key_manager_state = &(state).into();
    let business_profile = state
        .store
        .find_business_profile_by_profile_id(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &profile_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    let should_trigger_webhook = business_profile
        .get_refund_webhook_statuses()
        .contains(&refund_status);

    if should_trigger_webhook {
        let event_type = refund_status.into();
        let refund_response: api_models::refunds::RefundResponse = refund.clone().foreign_into();
        let refund_id = refund_response.refund_id.clone();
        let cloned_state = state.clone();
        let cloned_merchant_context = merchant_context.clone();
        let primary_object_created_at = refund_response.created_at;
        if let Some(outgoing_event_type) = event_type {
            tokio::spawn(
                async move {
                    Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook(
                        cloned_state,
                        cloned_merchant_context,
                        business_profile,
                        outgoing_event_type,
                        diesel_models::enums::EventClass::Refunds,
                        refund_id.to_string(),
                        diesel_models::enums::EventObjectType::RefundDetails,
                        webhooks::OutgoingWebhookContent::RefundDetails(Box::new(refund_response)),
                        primary_object_created_at,
                    ))
                    .await
                }
                .in_current_span(),
            );
        } else {
            logger::warn!("Outgoing webhook not sent because of missing event type status mapping");
        };
    }
    Ok(())
}

#[cfg(feature = "v2")]
pub async fn trigger_refund_outgoing_webhook(
    state: &SessionState,
    merchant_account: &domain::MerchantAccount,
    refund: &diesel_models::Refund,
    profile_id: id_type::ProfileId,
    key_store: &domain::MerchantKeyStore,
) -> RouterResult<()> {
    todo!()
}

pub fn get_locale_from_header(headers: &actix_web::http::header::HeaderMap) -> String {
    get_header_value_by_key(ACCEPT_LANGUAGE.into(), headers)
        .ok()
        .flatten()
        .map(|val| val.to_string())
        .unwrap_or(common_utils::consts::DEFAULT_LOCALE.to_string())
}

#[cfg(all(feature = "payouts", feature = "v1"))]
pub async fn trigger_payouts_webhook(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payout_response: &api_models::payouts::PayoutCreateResponse,
) -> RouterResult<()> {
    let key_manager_state = &(state).into();
    let profile_id = &payout_response.profile_id;
    let business_profile = state
        .store
        .find_business_profile_by_profile_id(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            profile_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    let status = &payout_response.status;
    let should_trigger_webhook = business_profile
        .get_payout_webhook_statuses()
        .contains(status);

    if should_trigger_webhook {
        let event_type = (*status).into();
        if let Some(event_type) = event_type {
            let cloned_merchant_context = merchant_context.clone();
            let cloned_state = state.clone();
            let cloned_response = payout_response.clone();

            // This spawns this futures in a background thread, the exception inside this future won't affect
            // the current thread and the lifecycle of spawn thread is not handled by runtime.
            // So when server shutdown won't wait for this thread's completion.
            tokio::spawn(
                async move {
                    let primary_object_created_at = cloned_response.created;
                    Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook(
                        cloned_state,
                        cloned_merchant_context,
                        business_profile,
                        event_type,
                        diesel_models::enums::EventClass::Payouts,
                        cloned_response.payout_id.get_string_repr().to_owned(),
                        diesel_models::enums::EventObjectType::PayoutDetails,
                        webhooks::OutgoingWebhookContent::PayoutDetails(Box::new(cloned_response)),
                        primary_object_created_at,
                    ))
                    .await
                }
                .in_current_span(),
            );
        } else {
            logger::warn!("Outgoing webhook not sent because of missing event type status mapping");
        }
    }
    Ok(())
}

#[cfg(all(feature = "payouts", feature = "v2"))]
pub async fn trigger_payouts_webhook(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payout_response: &api_models::payouts::PayoutCreateResponse,
) -> RouterResult<()> {
    todo!()
}


## 2024.04.12.0

### Features

- **connector:** [ZSL] add connector template code ([#4285](https://github.com/juspay/hyperswitch/pull/4285)) ([`086516b`](https://github.com/juspay/hyperswitch/commit/086516b7b307e074b4301bd14a4c65595b6e142c))
- **events:** Add events framework for registering events ([#4115](https://github.com/juspay/hyperswitch/pull/4115)) ([`3963219`](https://github.com/juspay/hyperswitch/commit/3963219e44bd771353d754aa356097e2d78a1392))
- **payment_methods:** Added kv support for payment_methods table ([#4311](https://github.com/juspay/hyperswitch/pull/4311)) ([`eb3cecd`](https://github.com/juspay/hyperswitch/commit/eb3cecdd74b4c758948f9de82727af76b9ba9fb0))
- **payouts:** Add kafka events ([#4264](https://github.com/juspay/hyperswitch/pull/4264)) ([`a2958c3`](https://github.com/juspay/hyperswitch/commit/a2958c33b5c4ed627c97e97e791ca2cfbfcecd5e))
- **router:**
  - Add `ApiKeyAuth` support for `upsert_connector_agnostic_mandate_config` ([#4335](https://github.com/juspay/hyperswitch/pull/4335)) ([`963a10c`](https://github.com/juspay/hyperswitch/commit/963a10c877cf7e63cef2c05093cc2c3d4eab66ec))
  - Add support for accepting an existing `payment_method_id` as the `payment_method_data` in `/payments` request ([#4328](https://github.com/juspay/hyperswitch/pull/4328)) ([`92e19af`](https://github.com/juspay/hyperswitch/commit/92e19af275c615db77e3ae398bfd487529210ba4))
- **users:** Add role specific fields for list merchants API ([#4326](https://github.com/juspay/hyperswitch/pull/4326)) ([`018c5b1`](https://github.com/juspay/hyperswitch/commit/018c5b10646a68a9898c47ade4874d52250231a8))

### Bug Fixes

- **compatibility:** Generate payment_id if not sent ([#4125](https://github.com/juspay/hyperswitch/pull/4125)) ([`9448673`](https://github.com/juspay/hyperswitch/commit/9448673c1c49fe1419f47c28f59e30268b9691c5))
- **connectors:** Amount received should be zero for `pending` and `failed` status ([#4331](https://github.com/juspay/hyperswitch/pull/4331)) ([`6aa66c4`](https://github.com/juspay/hyperswitch/commit/6aa66c4243fd2a55c9df5420fd2dc85ef156561b))
- **mandate:** Add validation for currency in MIT recurring payments ([#4308](https://github.com/juspay/hyperswitch/pull/4308)) ([`07c917c`](https://github.com/juspay/hyperswitch/commit/07c917c0559da1774848d0deb95a2725fc0d6503))

### Refactors

- **card:** Use `billing.first_name` instead of `card_holder_name` ([#4239](https://github.com/juspay/hyperswitch/pull/4239)) ([`8b66cda`](https://github.com/juspay/hyperswitch/commit/8b66cdaaf384bb0d5ce986334a7b32bb3cb13581))
- **connector:** [Ebanx] Add base_url to Integ Environment ([#4332](https://github.com/juspay/hyperswitch/pull/4332)) ([`13ba3cb`](https://github.com/juspay/hyperswitch/commit/13ba3cbd9627ff53b701084d8d4c0b800793a3e3))
- **connectors:** [ZSL] add Local bank Transfer ([#4337](https://github.com/juspay/hyperswitch/pull/4337)) ([`266a075`](https://github.com/juspay/hyperswitch/commit/266a075ab653b96505a4f8f26688153ced952c8f))
- **payment_methods:**
  - Add some payment method data to new domain type to be used in connector module ([#4234](https://github.com/juspay/hyperswitch/pull/4234)) ([`ce1e165`](https://github.com/juspay/hyperswitch/commit/ce1e165cecade481ce6002795049d6a9ffec96e2))
  - Add BankDebit payment method data to new domain type to be used in connector module ([#4238](https://github.com/juspay/hyperswitch/pull/4238)) ([`2bf775a`](https://github.com/juspay/hyperswitch/commit/2bf775a97e331cde2cad3e3d2a325850d969add9))
- **router:** Add `updated` field to `PaymentsResponse` ([#4292](https://github.com/juspay/hyperswitch/pull/4292)) ([`c99e038`](https://github.com/juspay/hyperswitch/commit/c99e038a4813aa68b7a0a6ad3458c93a0e3c27ba))

### Miscellaneous Tasks

- **deps:** Update time crate to 0.3.35 ([#4338](https://github.com/juspay/hyperswitch/pull/4338)) ([`44e7456`](https://github.com/juspay/hyperswitch/commit/44e7456a1088f8c413ff3694357822328bbc29bb))

**Full Changelog:** [`2024.04.10.0...2024.04.12.0`](https://github.com/juspay/hyperswitch/compare/2024.04.10.0...2024.04.12.0)

- - -

## 2024.04.10.0

### Features

- **connector:** [Ebanx] Template for ebanx payout ([#4141](https://github.com/juspay/hyperswitch/pull/4141)) ([`ed186a5`](https://github.com/juspay/hyperswitch/commit/ed186a5a9343c1d735749eb9ec90cb0d0f6094cd))
- **router:** Add local bank transfer payment method ([#4294](https://github.com/juspay/hyperswitch/pull/4294)) ([`06440eb`](https://github.com/juspay/hyperswitch/commit/06440eb6400adf166b203d7e4f587c1e2d5fe4f8))

### Bug Fixes

- **psync:** Log the error if payment method retrieve fails in the `psync flow` ([#4321](https://github.com/juspay/hyperswitch/pull/4321)) ([`5b89209`](https://github.com/juspay/hyperswitch/commit/5b89209b6f48691ee5ae2f9ede0d913abc9105f9))

### Refactors

- **payment_methods:** Add BankRedirect payment method data to new domain type to be used in connector module ([#4175](https://github.com/juspay/hyperswitch/pull/4175)) ([`e0e8437`](https://github.com/juspay/hyperswitch/commit/e0e843715cd02ac8b2eff2f645fe8471551ee914))

**Full Changelog:** [`2024.04.08.0...2024.04.10.0`](https://github.com/juspay/hyperswitch/compare/2024.04.08.0...2024.04.10.0)

- - -

## 2024.04.08.0

### Features

- **users:** Implemented cookie parsing for auth ([#4298](https://github.com/juspay/hyperswitch/pull/4298)) ([`2d394f9`](https://github.com/juspay/hyperswitch/commit/2d394f98e96d0beafca24abe2ac9f10a05460993))

### Bug Fixes

- **locker:** Handle card duplication in payouts flow ([#4013](https://github.com/juspay/hyperswitch/pull/4013)) ([`2fac436`](https://github.com/juspay/hyperswitch/commit/2fac436683060b8e7c81b210dfdf468f5194f24c))
- **mandates:** Store network transaction id only when `pg_agnostic` config is enabled in the `authorize_flow` ([#4318](https://github.com/juspay/hyperswitch/pull/4318)) ([`7b4c4fe`](https://github.com/juspay/hyperswitch/commit/7b4c4fea332d56f81a73b496fa0fefdbb64b3648))
- **redis_interface:** Remove mget function from redis interface ([#4303](https://github.com/juspay/hyperswitch/pull/4303)) ([`14035d2`](https://github.com/juspay/hyperswitch/commit/14035d2f838d88c56fe37f78caab6c88bc8b33e4))

### Refactors

- **payment_methods:** Add PayLater payment method data to new domain type to be used in connector module ([#4165](https://github.com/juspay/hyperswitch/pull/4165)) ([`6694852`](https://github.com/juspay/hyperswitch/commit/669485275db192b0e8e30f3528c0d61150d91847))

**Full Changelog:** [`2024.04.05.0...2024.04.08.0`](https://github.com/juspay/hyperswitch/compare/2024.04.05.0...2024.04.08.0)

- - -

## 2024.04.05.0

### Features

- **payout-events:** Add kafka events for payout analytics ([#4211](https://github.com/juspay/hyperswitch/pull/4211)) ([`bc25f3f`](https://github.com/juspay/hyperswitch/commit/bc25f3fa40e807cc92d2d53a2287b92eff727d3c))
- **router:**
  - Store `network_reference_id` against the `payment_method_id` in the `payment_method_table` ([#4041](https://github.com/juspay/hyperswitch/pull/4041)) ([`21e2d78`](https://github.com/juspay/hyperswitch/commit/21e2d78117a9e25708b8c6a2280f6a836ee86072))
  - Use `NTID` in `MIT` payments if the `pg_agnostic_mit` config is enabled ([#4113](https://github.com/juspay/hyperswitch/pull/4113)) ([`b58d7a8`](https://github.com/juspay/hyperswitch/commit/b58d7a8e62eef9880f717731063101bf92af3f34))
  - Add NTID flow for cybersource ([#4193](https://github.com/juspay/hyperswitch/pull/4193)) ([`071462f`](https://github.com/juspay/hyperswitch/commit/071462f2af8efeb16e48d351bbae68fd2fd64179))
- **webhooks:** Allow manually retrying delivery of outgoing webhooks ([#4176](https://github.com/juspay/hyperswitch/pull/4176)) ([`63d2b68`](https://github.com/juspay/hyperswitch/commit/63d2b6855acee1adeae2efff10f424e056af0bcb))

### Bug Fixes

- **payouts:** Persist status updates in payouts table ([#4280](https://github.com/juspay/hyperswitch/pull/4280)) ([`02ffe7e`](https://github.com/juspay/hyperswitch/commit/02ffe7e48068a43d319d67e0e976420d201776db))

### Refactors

- **connector:**
  - [Multisafepay] handle authorize and psync 2xx failure error response ([#4124](https://github.com/juspay/hyperswitch/pull/4124)) ([`9ebe0f4`](https://github.com/juspay/hyperswitch/commit/9ebe0f4371f13c7527972242424af2d926c84b5e))
  - Add support for GooglePay recurring payments ([#4300](https://github.com/juspay/hyperswitch/pull/4300)) ([`622aac3`](https://github.com/juspay/hyperswitch/commit/622aac3015e95de55e83abd047b5c680ecd8d662))
- **core:** Log the appropriate error message if the card fails to get saved in locker ([#4296](https://github.com/juspay/hyperswitch/pull/4296)) ([`9de3cdb`](https://github.com/juspay/hyperswitch/commit/9de3cdb7d37dd1d18c6a84368e70ceb52b7ae53a))
- **payment_link:** Decouple shimmer css from main payment_link css for better performance ([#4286](https://github.com/juspay/hyperswitch/pull/4286)) ([`9453e8f`](https://github.com/juspay/hyperswitch/commit/9453e8fcfac49fc399343ee7c4c1598412b370c7))

**Full Changelog:** [`2024.04.04.0...2024.04.05.0`](https://github.com/juspay/hyperswitch/compare/2024.04.04.0...2024.04.05.0)

- - -

## 2024.04.04.0

### Features

- **api:** Add browser information in payments response ([#3963](https://github.com/juspay/hyperswitch/pull/3963)) ([`4051cbb`](https://github.com/juspay/hyperswitch/commit/4051cbb4e7f708267b26439061e001bb00342cad))
- **core:** Update connector_mandate_details in payment_method ([#4155](https://github.com/juspay/hyperswitch/pull/4155)) ([`d8028ce`](https://github.com/juspay/hyperswitch/commit/d8028cefd53219ce15ba31ff3ea5ada3c0e217e7))
- **cypress:** Add cypress test cases ([#4271](https://github.com/juspay/hyperswitch/pull/4271)) ([`06e30e0`](https://github.com/juspay/hyperswitch/commit/06e30e04b06779862dd493ecaa7285875ffb402b))
- **router:** Create a merchant config for enable processor agnostic MIT ([#4025](https://github.com/juspay/hyperswitch/pull/4025)) ([`2a691a5`](https://github.com/juspay/hyperswitch/commit/2a691a5c05573b0a9caa8b2d7e57bc90c49280fe))

### Refactors

- **connector:** [Stripe] fix mandate flow ([#4281](https://github.com/juspay/hyperswitch/pull/4281)) ([`ea706f8`](https://github.com/juspay/hyperswitch/commit/ea706f81debd13a44a49ac3d1d3ef7f1882b683b))
- **core:** Locker call made synchronous for updation of pm_id ([#4289](https://github.com/juspay/hyperswitch/pull/4289)) ([`6e94a56`](https://github.com/juspay/hyperswitch/commit/6e94a5636462a8071e69f072ec058c6068e5d1f7))
- **mandates:** Add validations for recurring mandates using payment_method_id ([#4263](https://github.com/juspay/hyperswitch/pull/4263)) ([`49cfe72`](https://github.com/juspay/hyperswitch/commit/49cfe72cd2a20ba25c3323fca81bba7ea48b591b))
- **payment_methods:**
  - Add Wallets payment method data to new domain type to be used in connector module ([#4160](https://github.com/juspay/hyperswitch/pull/4160)) ([`8efd468`](https://github.com/juspay/hyperswitch/commit/8efd468ac150ff8d28f5b44b25701ba1837f243d))
  - Add `network_transaction_id` column in the `payment_methods` table ([#4005](https://github.com/juspay/hyperswitch/pull/4005)) ([`179f5ff`](https://github.com/juspay/hyperswitch/commit/179f5ff052aa530f2b429aaf20ea326bdc7f7ce0))
- **payout:** Handle saving wallet in temp locker ([#4230](https://github.com/juspay/hyperswitch/pull/4230)) ([`ae37b05`](https://github.com/juspay/hyperswitch/commit/ae37b059e09d9e6b597914536359fbdd5dd777d2))
- Fix typos ([#4277](https://github.com/juspay/hyperswitch/pull/4277)) ([`36f4112`](https://github.com/juspay/hyperswitch/commit/36f4112a6fa07c53a0b0c101539e4bf36d18893f))
- Fix typos in stripe transformers ([#4287](https://github.com/juspay/hyperswitch/pull/4287)) ([`4445a86`](https://github.com/juspay/hyperswitch/commit/4445a86207a31fd84553c70959a1341143759bc3))

### Miscellaneous Tasks

- **postman:** Update Postman collection files ([`70eb294`](https://github.com/juspay/hyperswitch/commit/70eb2940ecef503cbf9a898d0f8382f9abe83057))

**Full Changelog:** [`2024.04.03.0...2024.04.04.0`](https://github.com/juspay/hyperswitch/compare/2024.04.03.0...2024.04.04.0)

- - -

## 2024.04.03.0

### Features

- **analytics:** Three_ds and authentication events in sdkevents ([#4251](https://github.com/juspay/hyperswitch/pull/4251)) ([`88b53b0`](https://github.com/juspay/hyperswitch/commit/88b53b0d5ccfb16b03fc17c453f2c7afa26ec92e))
- **payment_link:** Add payment info metadata to payment link ([#4270](https://github.com/juspay/hyperswitch/pull/4270)) ([`97fbc89`](https://github.com/juspay/hyperswitch/commit/97fbc899c12a0c66ac89a7feaa6d45d39239a746))
- **router:** [BOA] implement mandates for cards and wallets ([#4232](https://github.com/juspay/hyperswitch/pull/4232)) ([`2f304e6`](https://github.com/juspay/hyperswitch/commit/2f304e601607980e7e536d94411ddf0f9023c605))

### Bug Fixes

- **connector:** [Cryptopay]fix redirection for cryptopay ([#4272](https://github.com/juspay/hyperswitch/pull/4272)) ([`1023f46`](https://github.com/juspay/hyperswitch/commit/1023f46c885dc2b70ccbb3931e667740695f448e))

### Refactors

- **payment_methods:** Add a new domain type for payment method data to be used in connector module ([#4140](https://github.com/juspay/hyperswitch/pull/4140)) ([`9cce152`](https://github.com/juspay/hyperswitch/commit/9cce1520e3b0c7c1d1ae70ca8cc30787ff96dded))
- **postman:** Paypal test cases for Capture ([#4265](https://github.com/juspay/hyperswitch/pull/4265)) ([`a071463`](https://github.com/juspay/hyperswitch/commit/a071463b29f9794e7069d57057d3bcc3a238f89b))

### Build System / Dependencies

- **deps:** Update dependencies ([#4268](https://github.com/juspay/hyperswitch/pull/4268)) ([`1f0d60e`](https://github.com/juspay/hyperswitch/commit/1f0d60e64fc9379d8a07a0c72970afc7b491dafa))

**Full Changelog:** [`2024.04.02.0...2024.04.03.0`](https://github.com/juspay/hyperswitch/compare/2024.04.02.0...2024.04.03.0)

- - -

## 2024.04.02.0

### Features

- **connector:** [billwerk] implement payment and refund flows ([#4245](https://github.com/juspay/hyperswitch/pull/4245)) ([`aecf4ae`](https://github.com/juspay/hyperswitch/commit/aecf4aeacce33c3dc03e089ef6d62af93e29ca9a))
- Return customer details in payments response body ([#4237](https://github.com/juspay/hyperswitch/pull/4237)) ([`740749e`](https://github.com/juspay/hyperswitch/commit/740749e18ae4458726cdf2501f3d3b789c819f7a))

### Refactors

- **core:** Removed the processing status for payment_method_status ([#4213](https://github.com/juspay/hyperswitch/pull/4213)) ([`a843713`](https://github.com/juspay/hyperswitch/commit/a843713126cea102064b342b6fc82429d89d758a))

### Documentation

- **README:** Remove link to outdated early access form ([`78befb4`](https://github.com/juspay/hyperswitch/commit/78befb42a35b1f98b1bd47b253d3c06e50bb2ee4))

### Build System / Dependencies

- **deps:** Bump `error-stack` from version `0.3.1` to `0.4.1` ([#4188](https://github.com/juspay/hyperswitch/pull/4188)) ([`ea730d4`](https://github.com/juspay/hyperswitch/commit/ea730d4ffc712cdf264492db109836fcde9b2b03))

**Full Changelog:** [`2024.04.01.0...2024.04.02.0`](https://github.com/juspay/hyperswitch/compare/2024.04.01.0...2024.04.02.0)

- - -

## 2024.04.01.0

### Features

- **mandates:** Allow off-session payments using `payment_method_id` ([#4132](https://github.com/juspay/hyperswitch/pull/4132)) ([`7b337ac`](https://github.com/juspay/hyperswitch/commit/7b337ac39d72f90dd0ebe58133218896ff279313))
- **payment_method:** API to list countries and currencies supported by a country and payment method type ([#4126](https://github.com/juspay/hyperswitch/pull/4126)) ([`74cd4a7`](https://github.com/juspay/hyperswitch/commit/74cd4a79526eb1a2dead87855e6a39248ec5ccb7))

### Miscellaneous Tasks

- **config:** Add billwerk base URL in deployment configs ([#4243](https://github.com/juspay/hyperswitch/pull/4243)) ([`e8289f0`](https://github.com/juspay/hyperswitch/commit/e8289f061d4735478cb1521de50f696d2412ad33))

**Full Changelog:** [`2024.03.28.0...2024.04.01.0`](https://github.com/juspay/hyperswitch/compare/2024.03.28.0...2024.04.01.0)

- - -

## 2024.03.28.0

### Features

- **connector:** [billwerk] add connector template code ([#4123](https://github.com/juspay/hyperswitch/pull/4123)) ([`37be05d`](https://github.com/juspay/hyperswitch/commit/37be05d31d97651ddaa2c67b828d24563b35d37e))

### Bug Fixes

- **connectors:** Fix wallet token deserialization error ([#4133](https://github.com/juspay/hyperswitch/pull/4133)) ([`929848f`](https://github.com/juspay/hyperswitch/commit/929848f8713b45daf479ba24fb0a49b8e327b6fd))
- **core:** Amount capturable remain same for `processing` status in capture ([#4229](https://github.com/juspay/hyperswitch/pull/4229)) ([`9523cf4`](https://github.com/juspay/hyperswitch/commit/9523cf4bbac488503c31640cade326095937e33c))
- **euclid_wasm:** Checkout wasm metadata issue ([#4198](https://github.com/juspay/hyperswitch/pull/4198)) ([`246898f`](https://github.com/juspay/hyperswitch/commit/246898fbb00a67d5791827527ce45e01b01b232c))
- **log:** Adding span metadata to `tokio` spawned futures ([#4118](https://github.com/juspay/hyperswitch/pull/4118)) ([`0706221`](https://github.com/juspay/hyperswitch/commit/070622125f49c4cc9c35f5ba9c634f1fef6b26d2))
- **trustpay:** [Trustpay] Add error code mapping '800.100.100' ([#4224](https://github.com/juspay/hyperswitch/pull/4224)) ([`9798db4`](https://github.com/juspay/hyperswitch/commit/9798db4558d926a218a0ca6f7f7c4e24a187b3da))

### Refactors

- **config:** Allow wildcard origin for development and Docker Compose setups ([#4231](https://github.com/juspay/hyperswitch/pull/4231)) ([`6587472`](https://github.com/juspay/hyperswitch/commit/65874728094bb550d6c311965fbb5f1577091bbb))

**Full Changelog:** [`2024.03.27.0...2024.03.28.0`](https://github.com/juspay/hyperswitch/compare/2024.03.27.0...2024.03.28.0)

- - -

## 2024.03.27.0

### Bug Fixes

- **connector:**


pub async fn complete_payout_eligibility(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector_data: &api::ConnectorData,
    payout_data: &mut PayoutData,
) -> RouterResult<()> {
    let payout_attempt = &payout_data.payout_attempt.to_owned();

    if !payout_data.should_terminate
        && payout_attempt.is_eligible.is_none()
        && connector_data
            .connector_name
            .supports_payout_eligibility(payout_data.payouts.payout_type)
    {
        Box::pin(check_payout_eligibility(
            state,
            merchant_context,
            connector_data,
            payout_data,
        ))
        .await
        .attach_printable("Eligibility failed for given Payout request")?;
    }

    utils::when(
        !payout_attempt
            .is_eligible
            .unwrap_or(state.conf.payouts.payout_eligibility),
        || {
            Err(report!(errors::ApiErrorResponse::PayoutFailed {
                data: Some(serde_json::json!({
                    "message": "Payout method data is invalid"
                }))
            })
            .attach_printable("Payout data provided is invalid"))
        },
    )?;

    Ok(())
}


/// from base_currency -> to_currency
    pub fn backward_conversion(
        &self,
        amt: Decimal,
        to_currency: Currency,
    ) -> Result<Decimal, CurrencyConversionError> {
        let to_factor = self
            .conversion
            .get(&to_currency)
            .ok_or_else(|| {
                CurrencyConversionError::ConversionNotSupported(to_currency.to_string())
            })?
            .to_factor;
        amt.checked_mul(to_factor)
            .ok_or(CurrencyConversionError::DecimalMultiplicationFailed)
    }


impl super::payments::metrics::PaymentMetricAnalytics for ClickhouseClient {}

impl super::payments::metrics::PaymentMetricAnalytics for for ClickhouseClient


impl ConnectorValidation for Facilitapay {}

impl ConnectorValidation for for Facilitapay