// File: crates/hyperswitch_connectors/src/connectors/dummyconnector.rs
// Module: hyperswitch_connectors
// Public functions: 1
// Public structs: 1

pub mod transformers;

use api_models::webhooks::{IncomingWebhookEvent, ObjectReferenceId};
use common_enums::{CaptureMethod, PaymentMethod, PaymentMethodType};
use common_utils::{
    consts as common_consts,
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        AccessTokenAuth, Authorize, Capture, Execute, PSync, PaymentMethodToken, RSync, Session,
        SetupMandate, Void,
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        ConnectorAccessToken, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration,
        ConnectorSpecifications, ConnectorValidation, MandateSetup, Payment, PaymentAuthorize,
        PaymentCapture, PaymentSession, PaymentSync, PaymentToken, PaymentVoid, Refund,
        RefundExecute, RefundSync,
    },
    configs::Connectors,
    errors::ConnectorError,
    events::connector_api_logs::ConnectorEvent,
    types::{
        PaymentsAuthorizeType, PaymentsCaptureType, PaymentsSyncType, RefundExecuteType,
        RefundSyncType, Response,
    },
    webhooks::{IncomingWebhook, IncomingWebhookRequestDetails},
};
use masking::{Mask as _, Maskable};
use transformers as dummyconnector;

use crate::{
    constants::headers,
    types::ResponseRouterData,
    utils::{construct_not_supported_error_report, convert_amount, RefundsRequestData as _},
};

#[derive(Clone)]
pub struct DummyConnector<const T: u8> {
    amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
}

impl<const T: u8> DummyConnector<T> {
    pub fn new() -> &'static Self {
        &Self {
            amount_converter: &MinorUnitForConnector,
        }
    }
}

impl<const T: u8> Payment for DummyConnector<T> {}
impl<const T: u8> PaymentSession for DummyConnector<T> {}
impl<const T: u8> ConnectorAccessToken for DummyConnector<T> {}
impl<const T: u8> MandateSetup for DummyConnector<T> {}
impl<const T: u8> PaymentAuthorize for DummyConnector<T> {}
impl<const T: u8> PaymentSync for DummyConnector<T> {}
impl<const T: u8> PaymentCapture for DummyConnector<T> {}
impl<const T: u8> PaymentVoid for DummyConnector<T> {}
impl<const T: u8> Refund for DummyConnector<T> {}
impl<const T: u8> RefundExecute for DummyConnector<T> {}
impl<const T: u8> RefundSync for DummyConnector<T> {}
impl<const T: u8> PaymentToken for DummyConnector<T> {}

impl<const T: u8>
    ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for DummyConnector<T>
{
    // Not Implemented (R)
}

impl<const T: u8, Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response>
    for DummyConnector<T>
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let mut header = vec![
            (
                headers::CONTENT_TYPE.to_string(),
                PaymentsAuthorizeType::get_content_type(self)
                    .to_string()
                    .into(),
            ),
            (
                common_consts::TENANT_HEADER.to_string(),
                req.tenant_id.get_string_repr().to_string().into(),
            ),
        ];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }
}

impl<const T: u8> ConnectorCommon for DummyConnector<T> {
    fn id(&self) -> &'static str {
        Into::<transformers::DummyConnectors>::into(T).get_dummy_connector_id()
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let auth = transformers::DummyConnectorAuthType::try_from(auth_type)
            .change_context(ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::AUTHORIZATION.to_string(),
            auth.api_key.into_masked(),
        )])
    }

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

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.error.code,
            message: response.error.message,
            reason: response.error.reason,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

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

impl<const T: u8> ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData>
    for DummyConnector<T>
{
    //TODO: implement sessions flow
}

impl<const T: u8> ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken>
    for DummyConnector<T>
{
}

impl<const T: u8> ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
    for DummyConnector<T>
{
    fn build_request(
        &self,
        _req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Err(
            ConnectorError::NotImplemented("Setup Mandate flow for DummyConnector".to_string())
                .into(),
        )
    }
}

impl<const T: u8> ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData>
    for DummyConnector<T>
{
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        match req.payment_method {
            PaymentMethod::Card
            | PaymentMethod::Upi
            | PaymentMethod::Wallet
            | PaymentMethod::PayLater => Ok(format!("{}/payment", self.base_url(connectors))),
            _ => Err(error_stack::report!(ConnectorError::NotSupported {
                message: format!("The payment method {} is not supported", req.payment_method),
                connector: Into::<transformers::DummyConnectors>::into(T).get_dummy_connector_id(),
            })),
        }
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;

        let connector_router_data = dummyconnector::DummyConnectorRouterData::from((amount, req));
        let connector_req =
            transformers::DummyConnectorPaymentsRequest::<T>::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&PaymentsAuthorizeType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(PaymentsAuthorizeType::get_headers(self, req, connectors)?)
                .set_body(PaymentsAuthorizeType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, ConnectorError> {
        let response: transformers::PaymentsResponse = res
            .response
            .parse_struct("DummyConnector PaymentsResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(ConnectorError::ResponseHandlingFailed)
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl<const T: u8> ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData>
    for DummyConnector<T>
{
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        match req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
        {
            Ok(transaction_id) => Ok(format!(
                "{}/payments/{}",
                self.base_url(connectors),
                transaction_id
            )),
            Err(_) => Err(error_stack::report!(
                ConnectorError::MissingConnectorTransactionID
            )),
        }
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(PaymentsSyncType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, ConnectorError> {
        let response: transformers::PaymentsResponse = res
            .response
            .parse_struct("dummyconnector PaymentsSyncResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(ConnectorError::ResponseHandlingFailed)
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl<const T: u8> ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData>
    for DummyConnector<T>
{
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Err(ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        Err(ConnectorError::NotImplemented("get_request_body method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&PaymentsCaptureType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(PaymentsCaptureType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, ConnectorError> {
        let response: transformers::PaymentsResponse = res
            .response
            .parse_struct("transformers PaymentsCaptureResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(ConnectorError::ResponseHandlingFailed)
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl<const T: u8> ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData>
    for DummyConnector<T>
{
}

impl<const T: u8> ConnectorIntegration<Execute, RefundsData, RefundsResponseData>
    for DummyConnector<T>
{
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!(
            "{}/{}/refund",
            self.base_url(connectors),
            req.request.connector_transaction_id
        ))
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_refund_amount,
            req.request.currency,
        )?;

        let connector_router_data = dummyconnector::DummyConnectorRouterData::from((amount, req));
        let connector_req =
            transformers::DummyConnectorRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        let request = RequestBuilder::new()
            .method(Method::Post)
            .url(&RefundExecuteType::get_url(self, req, connectors)?)
            .attach_default_headers()
            .headers(RefundExecuteType::get_headers(self, req, connectors)?)
            .set_body(RefundExecuteType::get_request_body(self, req, connectors)?)
            .build();
        Ok(Some(request))
    }

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, ConnectorError> {
        let response: transformers::RefundResponse = res
            .response
            .parse_struct("transformers RefundResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(ConnectorError::ResponseHandlingFailed)
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl<const T: u8> ConnectorIntegration<RSync, RefundsData, RefundsResponseData>
    for DummyConnector<T>
{
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        let refund_id = req.request.get_connector_refund_id()?;
        Ok(format!(
            "{}/refunds/{}",
            self.base_url(connectors),
            refund_id
        ))
    }

    fn build_request(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&RefundSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(RefundSyncType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, ConnectorError> {
        let response: transformers::RefundResponse = res
            .response
            .parse_struct("transformers RefundSyncResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(ConnectorError::ResponseHandlingFailed)
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

#[async_trait::async_trait]
impl<const T: u8> IncomingWebhook for DummyConnector<T> {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ObjectReferenceId, ConnectorError> {
        Err(report!(ConnectorError::WebhooksNotImplemented))
    }

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

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

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


// Struct: TrustpayAuthUpdateResponse
// File: crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TrustpayAuthUpdateResponse


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/katapult.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// Struct: FiservemeaPaymentMethodDetails
// File: crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FiservemeaPaymentMethodDetails


// Struct: GetFrmFilterRequest
// File: crates/api_models/src/analytics.rs
// Module: api_models
// Implementations: 1
// Traits: ApiEventMetric

pub struct GetFrmFilterRequest


// Implementation: impl webhooks::IncomingWebhook for for Coinbase
// File: crates/hyperswitch_connectors/src/connectors/coinbase.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl webhooks::IncomingWebhook for for Coinbase


// Function: get_applepay_encrypt_account_id
// File: crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
// Module: hyperswitch_connectors

pub fn get_applepay_encrypt_account_id(
        &self,
        currency: Currency,
    ) -> Result<Secret<String>, errors::ConnectorError>


// Function: accept_invitations_pre_auth
// File: crates/router/src/core/user_role.rs
// Module: router

pub fn accept_invitations_pre_auth(
    state: SessionState,
    user_token: auth::UserFromSinglePurposeToken,
    req: user_role_api::AcceptInvitationsPreAuthRequest,
) -> UserResponse<user_api::TokenResponse>


// Implementation: impl ConnectorCommon for for Tsys
// File: crates/hyperswitch_connectors/src/connectors/tsys.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl ConnectorCommon for for Tsys


// Function: get_surcharge_amount
// File: crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
// Module: hyperswitch_domain_models

pub fn get_surcharge_amount(&self) -> Option<MinorUnit>


// Struct: NexinetsRefundResponse
// File: crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NexinetsRefundResponse


// Struct: IncomingWebhookDetails
// File: crates/api_models/src/webhooks.rs
// Module: api_models
// Implementations: 0

pub struct IncomingWebhookDetails


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/recurly.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// Struct: LocalPrice
// File: crates/hyperswitch_connectors/src/connectors/coinbase/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct LocalPrice


// File: crates/api_models/src/events/gsm.rs
// Module: api_models

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

use crate::gsm;

impl ApiEventMetric for gsm::GsmCreateRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Gsm)
    }
}

impl ApiEventMetric for gsm::GsmUpdateRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Gsm)
    }
}

impl ApiEventMetric for gsm::GsmRetrieveRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Gsm)
    }
}

impl ApiEventMetric for gsm::GsmDeleteRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Gsm)
    }
}

impl ApiEventMetric for gsm::GsmDeleteResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Gsm)
    }
}

impl ApiEventMetric for gsm::GsmResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Gsm)
    }
}


// Struct: ApiKeyExpiryWorkflow
// File: crates/router/src/workflows/api_key_expiry.rs
// Module: router
// Implementations: 0

pub struct ApiKeyExpiryWorkflow


// Function: track_database_call
// File: crates/diesel_models/src/query/generics.rs
// Module: diesel_models

pub fn track_database_call<T, Fut, U>(future: Fut, operation: DatabaseOperation) -> U
    where
        Fut: std::future::Future<Output = U>,


// Function: update_payment_method_status_internal
// File: crates/router/src/core/payment_methods.rs
// Module: router

pub fn update_payment_method_status_internal(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: enums::MerchantStorageScheme,
    status: enums::PaymentMethodStatus,
    payment_method_id: &id_type::GlobalPaymentMethodId,
) -> RouterResult<domain::PaymentMethod>


        db.delete_payment_method_by_merchant_id_payment_method_id(
            &(self.state.into()),
            self.merchant_context.get_merchant_key_store(),
            self.merchant_context.get_merchant_account().get_id(),
            pm_id.payment_method_id.as_str(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

        if customer.default_payment_method_id.as_ref() == Some(&pm_id.payment_method_id) {
            let customer_update = CustomerUpdate::UpdateDefaultPaymentMethod {
                default_payment_method_id: Some(None),
            };
            db.update_customer_by_customer_id_merchant_id(
                key_manager_state,
                key.customer_id,
                key.merchant_id,
                customer,
                customer_update,
                self.merchant_context.get_merchant_key_store(),
                self.merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to update the default payment method id for the customer")?;
        };

        Ok(services::ApplicationResponse::Json(
            api::PaymentMethodDeleteResponse {
                payment_method_id: key.payment_method_id.clone(),
                deleted: true,
            },
        ))
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn add_payment_method(
        &self,
        req: &api::PaymentMethodCreate,
    ) -> errors::RouterResponse<api::PaymentMethodResponse> {
        req.validate()?;
        let db = &*self.state.store;
        let merchant_id = self.merchant_context.get_merchant_account().get_id();
        let customer_id = req.customer_id.clone().get_required_value("customer_id")?;
        let payment_method = req.payment_method.get_required_value("payment_method")?;
        let key_manager_state = self.state.into();
        let payment_method_billing_address: Option<Encryptable<Secret<serde_json::Value>>> = req
            .billing
            .clone()
            .async_map(|billing| {
                create_encrypted_data(
                    &key_manager_state,
                    self.merchant_context.get_merchant_key_store(),
                    billing,
                )
            })
            .await
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt Payment method billing address")?;

        let connector_mandate_details = req
            .connector_mandate_details
            .clone()
            .map(serde_json::to_value)
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)?;
        let response = match payment_method {
            #[cfg(feature = "payouts")]
            api_enums::PaymentMethod::BankTransfer => match req.bank_transfer.clone() {
                Some(bank) => self
                    .add_bank_to_locker(
                        req.clone(),
                        self.merchant_context.get_merchant_key_store(),
                        &bank,
                        &customer_id,
                    )
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Add PaymentMethod Failed"),
                _ => Ok(self.store_default_payment_method(req, &customer_id, merchant_id)),
            },
            api_enums::PaymentMethod::Card => match req.card.clone() {
                Some(card) => {
                    let mut card_details = card;
                    card_details = helpers::populate_bin_details_for_payment_method_create(
                        card_details.clone(),
                        db.get_payment_methods_store(),
                    )
                    .await;
                    helpers::validate_card_expiry(
                        &card_details.card_exp_month,
                        &card_details.card_exp_year,
                    )?;
                    Box::pin(self.add_card_to_locker(
                        req.clone(),
                        &card_details,
                        &customer_id,
                        None,
                    ))
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Add Card Failed")
                }
                _ => Ok(self.store_default_payment_method(req, &customer_id, merchant_id)),
            },
            _ => Ok(self.store_default_payment_method(req, &customer_id, merchant_id)),
        };

        let (mut resp, duplication_check) = response?;

        match duplication_check {
            Some(duplication_check) => match duplication_check {
                payment_methods::DataDuplicationCheck::Duplicated => {
                    let existing_pm = self
                        .get_or_insert_payment_method(
                            req.clone(),
                            &mut resp,
                            &customer_id,
                            self.merchant_context.get_merchant_key_store(),
                        )
                        .await?;

                    resp.client_secret = existing_pm.client_secret;
                }
                payment_methods::DataDuplicationCheck::MetaDataChanged => {
                    if let Some(card) = req.card.clone() {
                        let existing_pm = self
                            .get_or_insert_payment_method(
                                req.clone(),
                                &mut resp,
                                &customer_id,
                                self.merchant_context.get_merchant_key_store(),
                            )
                            .await?;

                        let client_secret = existing_pm.client_secret.clone();

                        self.delete_card_from_locker(
                            &customer_id,
                            merchant_id,
                            existing_pm
                                .locker_id
                                .as_ref()
                                .unwrap_or(&existing_pm.payment_method_id),
                        )
                        .await?;

                        let add_card_resp = self
                            .add_card_hs(
                                req.clone(),
                                &card,
                                &customer_id,
                                api::enums::LockerChoice::HyperswitchCardVault,
                                Some(
                                    existing_pm
                                        .locker_id
                                        .as_ref()
                                        .unwrap_or(&existing_pm.payment_method_id),
                                ),
                            )
                            .await;

                        if let Err(err) = add_card_resp {
                            logger::error!(vault_err=?err);
                            db.delete_payment_method_by_merchant_id_payment_method_id(
                                &(self.state.into()),
                                self.merchant_context.get_merchant_key_store(),
                                merchant_id,
                                &resp.payment_method_id,
                            )
                            .await
                            .to_not_found_response(
                                errors::ApiErrorResponse::PaymentMethodNotFound,
                            )?;

                            Err(report!(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable("Failed while updating card metadata changes"))?
                        };

                        let existing_pm_data = self
                            .get_card_details_without_locker_fallback(&existing_pm)
                            .await?;

                        let updated_card = Some(api::CardDetailFromLocker {
                            scheme: existing_pm.scheme.clone(),
                            last4_digits: Some(card.card_number.get_last4()),
                            issuer_country: card
                                .card_issuing_country
                                .or(existing_pm_data.issuer_country),
                            card_isin: Some(card.card_number.get_card_isin()),
                            card_number: Some(card.card_number),
                            expiry_month: Some(card.card_exp_month),
                            expiry_year: Some(card.card_exp_year),
                            card_token: None,
                            card_fingerprint: None,
                            card_holder_name: card
                                .card_holder_name
                                .or(existing_pm_data.card_holder_name),
                            nick_name: card.nick_name.or(existing_pm_data.nick_name),
                            card_network: card.card_network.or(existing_pm_data.card_network),
                            card_issuer: card.card_issuer.or(existing_pm_data.card_issuer),
                            card_type: card.card_type.or(existing_pm_data.card_type),
                            saved_to_locker: true,
                        });

                        let updated_pmd = updated_card.as_ref().map(|card| {
                            PaymentMethodsData::Card(CardDetailsPaymentMethod::from((
                                card.clone(),
                                None,
                            )))
                        });
                        let pm_data_encrypted: Option<Encryptable<Secret<serde_json::Value>>> =
                            updated_pmd
                                .async_map(|updated_pmd| {
                                    create_encrypted_data(
                                        &key_manager_state,
                                        self.merchant_context.get_merchant_key_store(),
                                        updated_pmd,
                                    )
                                })
                                .await
                                .transpose()
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable("Unable to encrypt payment method data")?;

                        let pm_update = storage::PaymentMethodUpdate::PaymentMethodDataUpdate {
                            payment_method_data: pm_data_encrypted.map(Into::into),
                        };

                        db.update_payment_method(
                            &(self.state.into()),
                            self.merchant_context.get_merchant_key_store(),
                            existing_pm,
                            pm_update,
                            self.merchant_context.get_merchant_account().storage_scheme,
                        )
                        .await
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed to add payment method in db")?;

                        resp.client_secret = client_secret;
                    }
                }
            },
            None => {
                let pm_metadata = resp.metadata.as_ref().map(|data| data.peek());

                let locker_id = if resp.payment_method == Some(api_enums::PaymentMethod::Card)
                    || resp.payment_method == Some(api_enums::PaymentMethod::BankTransfer)
                {
                    Some(resp.payment_method_id)
                } else {
                    None
                };
                resp.payment_method_id = generate_id(consts::ID_LENGTH, "pm");
                let pm = self
                    .insert_payment_method(
                        &resp,
                        req,
                        self.merchant_context.get_merchant_key_store(),
                        merchant_id,
                        &customer_id,
                        pm_metadata.cloned(),
                        None,
                        locker_id,
                        connector_mandate_details,
                        req.network_transaction_id.clone(),
                        payment_method_billing_address,
                        None,
                        None,
                        None,
                        Default::default(), //Currently this method is used for adding payment method via PaymentMethodCreate API which doesn't support external vault. hence Default i.e. InternalVault is passed for vault source and type
                    )
                    .await?;

                resp.client_secret = pm.client_secret;
            }
        }

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

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn get_client_secret_or_add_payment_method(
    state: &routes::SessionState,
    req: api::PaymentMethodCreate,
    merchant_context: &domain::MerchantContext,
) -> errors::RouterResponse<api::PaymentMethodResponse> {
    let merchant_id = merchant_context.get_merchant_account().get_id();
    let customer_id = req.customer_id.clone().get_required_value("customer_id")?;
    let cards = PmCards {
        state,
        merchant_context,
    };
    #[cfg(not(feature = "payouts"))]
    let condition = req.card.is_some();
    #[cfg(feature = "payouts")]
    let condition = req.card.is_some() || req.bank_transfer.is_some() || req.wallet.is_some();
    let key_manager_state = state.into();
    let payment_method_billing_address: Option<Encryptable<Secret<serde_json::Value>>> = req
        .billing
        .clone()
        .async_map(|billing| {
            create_encrypted_data(
                &key_manager_state,
                merchant_context.get_merchant_key_store(),
                billing,
            )
        })
        .await
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to encrypt Payment method billing address")?;

    let connector_mandate_details = req
        .connector_mandate_details
        .clone()
        .map(serde_json::to_value)
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

    if condition {
        Box::pin(cards.add_payment_method(&req)).await
    } else {
        let payment_method_id = generate_id(consts::ID_LENGTH, "pm");

        let res = cards
            .create_payment_method(
                &req,
                &customer_id,
                payment_method_id.as_str(),
                None,
                merchant_id,
                None,
                None,
                None,
                connector_mandate_details,
                Some(enums::PaymentMethodStatus::AwaitingData),
                None,
                payment_method_billing_address,
                None,
                None,
                None,
                None,
                Default::default(), //Currently this method is used for adding payment method via PaymentMethodCreate API which doesn't support external vault. hence Default i.e. InternalVault is passed for vault type
            )
            .await?;

        if res.status == enums::PaymentMethodStatus::AwaitingData {
            add_payment_method_status_update_task(
                &*state.store,
                &res,
                enums::PaymentMethodStatus::AwaitingData,
                enums::PaymentMethodStatus::Inactive,
                merchant_id,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable(
                "Failed to add payment method status update task in process tracker",
            )?;
        }

        Ok(services::api::ApplicationResponse::Json(
            api::PaymentMethodResponse::foreign_from((None, res)),
        ))
    }
}

#[instrument(skip_all)]
pub fn authenticate_pm_client_secret_and_check_expiry(
    req_client_secret: &String,
    payment_method: &domain::PaymentMethod,
) -> errors::CustomResult<bool, errors::ApiErrorResponse> {
    let stored_client_secret = payment_method
        .client_secret
        .clone()
        .get_required_value("client_secret")
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "client_secret",
        })
        .attach_printable("client secret not found in db")?;

    if req_client_secret != &stored_client_secret {
        Err((errors::ApiErrorResponse::ClientSecretInvalid).into())
    } else {
        let current_timestamp = common_utils::date_time::now();
        let session_expiry = payment_method
            .created_at
            .saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY));

        let expired = current_timestamp > session_expiry;

        Ok(expired)
    }
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn add_payment_method_data(
    state: routes::SessionState,
    req: api::PaymentMethodCreate,
    merchant_context: domain::MerchantContext,
    pm_id: String,
) -> errors::RouterResponse<api::PaymentMethodResponse> {
    let db = &*state.store;
    let cards = PmCards {
        state: &state,
        merchant_context: &merchant_context,
    };

    let pmd = req
        .payment_method_data
        .clone()
        .get_required_value("payment_method_data")?;
    req.payment_method.get_required_value("payment_method")?;
    let client_secret = req
        .client_secret
        .clone()
        .get_required_value("client_secret")?;
    let payment_method = db
        .find_payment_method(
            &((&state).into()),
            merchant_context.get_merchant_key_store(),
            pm_id.as_str(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::PaymentMethodNotFound)
        .attach_printable("Unable to find payment method")?;

    if payment_method.status != enums::PaymentMethodStatus::AwaitingData {
        return Err((errors::ApiErrorResponse::ClientSecretExpired).into());
    }

    let customer_id = payment_method.customer_id.clone();

    let customer = db
        .find_customer_by_customer_id_merchant_id(
            &(&state).into(),
            &customer_id,
            merchant_context.get_merchant_account().get_id(),
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?;

    let client_secret_expired =
        authenticate_pm_client_secret_and_check_expiry(&client_secret, &payment_method)?;

    if client_secret_expired {
        return Err((errors::ApiErrorResponse::ClientSecretExpired).into());
    };
    let key_manager_state = (&state).into();
    match pmd {
        api_models::payment_methods::PaymentMethodCreateData::Card(card) => {
            helpers::validate_card_expiry(&card.card_exp_month, &card.card_exp_year)?;
            let resp = Box::pin(cards.add_card_to_locker(req.clone(), &card, &customer_id, None))
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError);

            match resp {
                Ok((mut pm_resp, duplication_check)) => {
                    if duplication_check.is_some() {
                        let pm_update = storage::PaymentMethodUpdate::StatusUpdate {
                            status: Some(enums::PaymentMethodStatus::Inactive),
                        };

                        db.update_payment_method(
                            &((&state).into()),
                            merchant_context.get_merchant_key_store(),
                            payment_method,
                            pm_update,
                            merchant_context.get_merchant_account().storage_scheme,
                        )
                        .await
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed to add payment method in db")?;

                        cards
                            .get_or_insert_payment_method(
                                req.clone(),
                                &mut pm_resp,
                                &customer_id,
                                merchant_context.get_merchant_key_store(),
                            )
                            .await?;

                        return Ok(services::ApplicationResponse::Json(pm_resp));
                    } else {
                        let locker_id = pm_resp.payment_method_id.clone();
                        pm_resp.payment_method_id.clone_from(&pm_id);
                        pm_resp.client_secret = Some(client_secret.clone());

                        let card_isin = card.card_number.get_card_isin();

                        let card_info = db
                            .get_card_info(card_isin.as_str())
                            .await
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable("Failed to get card info")?;

                        let updated_card = CardDetailsPaymentMethod {
                            issuer_country: card_info
                                .as_ref()
                                .and_then(|ci| ci.card_issuing_country.clone()),
                            last4_digits: Some(card.card_number.get_last4()),
                            expiry_month: Some(card.card_exp_month),
                            expiry_year: Some(card.card_exp_year),
                            nick_name: card.nick_name,
                            card_holder_name: card.card_holder_name,
                            card_network: card_info.as_ref().and_then(|ci| ci.card_network.clone()),
                            card_isin: Some(card_isin),
                            card_issuer: card_info.as_ref().and_then(|ci| ci.card_issuer.clone()),
                            card_type: card_info.as_ref().and_then(|ci| ci.card_type.clone()),
                            saved_to_locker: true,
                            co_badged_card_data: None,
                        };
                        let pm_data_encrypted: Encryptable<Secret<serde_json::Value>> =
                            create_encrypted_data(
                                &key_manager_state,
                                merchant_context.get_merchant_key_store(),
                                PaymentMethodsData::Card(updated_card),
                            )
                            .await
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable("Unable to encrypt payment method data")?;

                        let pm_update = storage::PaymentMethodUpdate::AdditionalDataUpdate {
                            payment_method_data: Some(pm_data_encrypted.into()),
                            status: Some(enums::PaymentMethodStatus::Active),
                            locker_id: Some(locker_id),
                            network_token_requestor_reference_id: None,
                            payment_method: req.payment_method,
                            payment_method_issuer: req.payment_method_issuer,
                            payment_method_type: req.payment_method_type,
                            network_token_locker_id: None,
                            network_token_payment_method_data: None,
                        };

                        db.update_payment_method(
                            &((&state).into()),
                            merchant_context.get_merchant_key_store(),
                            payment_method,
                            pm_update,
                            merchant_context.get_merchant_account().storage_scheme,
                        )
                        .await
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed to add payment method in db")?;

                        if customer.default_payment_method_id.is_none() {
                            let _ = cards
                                .set_default_payment_method(
                                    merchant_context.get_merchant_account().get_id(),
                                    &customer_id,
                                    pm_id,
                                )
                                .await
                                .map_err(|error| {
                                    logger::error!(
                                        ?error,
                                        "Failed to set the payment method as default"
                                    )
                                });
                        }

                        return Ok(services::ApplicationResponse::Json(pm_resp));
                    }
                }
                Err(e) => {
                    let pm_update = storage::PaymentMethodUpdate::StatusUpdate {
                        status: Some(enums::PaymentMethodStatus::Inactive),
                    };

                    db.update_payment_method(
                        &((&state).into()),
                        merchant_context.get_merchant_key_store(),
                        payment_method,
                        pm_update,
                        merchant_context.get_merchant_account().storage_scheme,
                    )
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to update payment method in db")?;

                    return Err(e.attach_printable("Failed to add card to locker"));
                }
            }
        }
    }
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn update_customer_payment_method(
    state: routes::SessionState,
    merchant_context: domain::MerchantContext,
    req: api::PaymentMethodUpdate,
    payment_method_id: &str,
) -> errors::RouterResponse<api::PaymentMethodResponse> {
    // Currently update is supported only for cards
    if let Some(card_update) = req.card.clone() {
        let db = state.store.as_ref();

        let pm = db
            .find_payment_method(
                &((&state).into()),
                merchant_context.get_merchant_key_store(),
                payment_method_id,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

        if let Some(cs) = &req.client_secret {
            let is_client_secret_expired = authenticate_pm_client_secret_and_check_expiry(cs, &pm)?;

            if is_client_secret_expired {
                return Err((errors::ApiErrorResponse::ClientSecretExpired).into());
            };
        };

        if pm.status == enums::PaymentMethodStatus::AwaitingData {
            return Err(report!(errors::ApiErrorResponse::NotSupported {
                message: "Payment method is awaiting data so it cannot be updated".into()
            }));
        }

        if pm.payment_method_data.is_none() {
            return Err(report!(errors::ApiErrorResponse::GenericNotFoundError {
                message: "payment_method_data not found".to_string()
            }));
        }

        // Fetch the existing payment method data from db
        let existing_card_data =
            pm.payment_method_data
                .clone()
                .map(|x| x.into_inner().expose())
                .map(
                    |value| -> Result<
                        PaymentMethodsData,
                        error_stack::Report<errors::ApiErrorResponse>,
                    > {
                        value
                            .parse_value::<PaymentMethodsData>("PaymentMethodsData")
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable("Failed to deserialize payment methods data")
                    },
                )
                .transpose()?
                .and_then(|pmd| match pmd {
                    PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)),
                    _ => None,
                })
                .ok_or(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to obtain decrypted card object from db")?;

        let is_card_updation_required =
            validate_payment_method_update(card_update.clone(), existing_card_data.clone());

        let response = if is_card_updation_required {
            // Fetch the existing card data from locker for getting card number
            let card_data_from_locker = get_card_from_locker(
                &state,
                &pm.customer_id,
                &pm.merchant_id,
                pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error getting card from locker")?;

            if card_update.card_exp_month.is_some() || card_update.card_exp_year.is_some() {
                helpers::validate_card_expiry(
                    card_update
                        .card_exp_month
                        .as_ref()
                        .unwrap_or(&card_data_from_locker.card_exp_month),
                    card_update
                        .card_exp_year
                        .as_ref()
                        .unwrap_or(&card_data_from_locker.card_exp_year),
                )?;
            }

            let updated_card_details = card_update.apply(card_data_from_locker.clone());

            // Construct new payment method object from request
            let new_pm = api::PaymentMethodCreate {
                payment_method: pm.get_payment_method_type(),
                payment_method_type: pm.get_payment_method_subtype(),
                payment_method_issuer: pm.payment_method_issuer.clone(),
                payment_method_issuer_code: pm.payment_method_issuer_code,
                #[cfg(feature = "payouts")]
                bank_transfer: None,
                card: Some(updated_card_details.clone()),
                #[cfg(feature = "payouts")]
                wallet: None,
                metadata: None,
                customer_id: Some(pm.customer_id.clone()),
                client_secret: pm.client_secret.clone(),
                payment_method_data: None,
                card_network: None,
                billing: None,
                connector_mandate_details: None,
                network_transaction_id: None,
            };
            new_pm.validate()?;
            let cards = PmCards {
                state: &state,
                merchant_context: &merchant_context,
            };
            // Delete old payment method from locker
            cards
                .delete_card_from_locker(
                    &pm.customer_id,
                    &pm.merchant_id,
                    pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
                )
                .await?;

            // Add the updated payment method data to locker
            let (mut add_card_resp, _) = Box::pin(cards.add_card_to_locker(
                new_pm.clone(),
                &updated_card_details,
                &pm.customer_id,
                Some(pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id)),
            ))
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to add updated payment method to locker")?;

            // Construct new updated card object. Consider a field if passed in request or else populate it with the existing value from existing_card_data
            let updated_card = Some(api::CardDetailFromLocker {
                scheme: existing_card_data.scheme,
                last4_digits: Some(card_data_from_locker.card_number.get_last4()),
                issuer_country: existing_card_data.issuer_country,
                card_number: existing_card_data.card_number,
                expiry_month: card_update
                    .card_exp_month
                    .or(existing_card_data.expiry_month),
                expiry_year: card_update.card_exp_year.or(existing_card_data.expiry_year),
                card_token: existing_card_data.card_token,
                card_fingerprint: existing_card_data.card_fingerprint,
                card_holder_name: card_update
                    .card_holder_name
                    .or(existing_card_data.card_holder_name),
                nick_name: card_update.nick_name.or(existing_card_data.nick_name),
                card_network: existing_card_data.card_network,
                card_isin: existing_card_data.card_isin,
                card_issuer: existing_card_data.card_issuer,
                card_type: existing_card_data.card_type,
                saved_to_locker: true,
            });

            let updated_pmd = updated_card.as_ref().map(|card| {
                PaymentMethodsData::Card(CardDetailsPaymentMethod::from((card.clone(), None)))
            });
            let key_manager_state = (&state).into();
            let pm_data_encrypted: Option<Encryptable<Secret<serde_json::Value>>> = updated_pmd
                .async_map(|updated_pmd| {
                    create_encrypted_data(
                        &key_manager_state,
                        merchant_context.get_merchant_key_store(),
                        updated_pmd,
                    )
                })
                .await
                .transpose()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Unable to encrypt payment method data")?;

            let pm_update = storage::PaymentMethodUpdate::PaymentMethodDataUpdate {
                payment_method_data: pm_data_encrypted.map(Into::into),
            };

            add_card_resp
                .payment_method_id
                .clone_from(&pm.payment_method_id);

            db.update_payment_method(
                &((&state).into()),
                merchant_context.get_merchant_key_store(),
                pm,
                pm_update,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to update payment method in db")?;

            add_card_resp
        } else {
            // Return existing payment method data as response without any changes
            api::PaymentMethodResponse {
                merchant_id: pm.merchant_id.to_owned(),
                customer_id: Some(pm.customer_id.clone()),
                payment_method_id: pm.payment_method_id.clone(),
                payment_method: pm.get_payment_method_type(),
                payment_method_type: pm.get_payment_method_subtype(),
                #[cfg(feature = "payouts")]
                bank_transfer: None,
                card: Some(existing_card_data),
                metadata: pm.metadata,
                created: Some(pm.created_at),
                recurring_enabled: Some(false),
                installment_payment_enabled: Some(false),
                payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]),
                last_used_at: Some(common_utils::date_time::now()),
                client_secret: pm.client_secret.clone(),
            }
        };

        Ok(services::ApplicationResponse::Json(response))
    } else {
        Err(report!(errors::ApiErrorResponse::NotSupported {
            message: "Payment method update for the given payment method is not supported".into()
        }))
    }
}

#[cfg(feature = "v1")]
pub fn validate_payment_method_update(
    card_updation_obj: CardDetailUpdate,
    existing_card_data: api::CardDetailFromLocker,
) -> bool {
    // Return true If any one of the below condition returns true,
    // If a field is not passed in the update request, return false.
    // If the field is present, it depends on the existing field data:
    // - If existing field data is not present, or if it is present and doesn't match
    //   the update request data, then return true.
    // - Or else return false
    card_updation_obj
        .card_exp_month
        .map(|exp_month| exp_month.expose())
        .is_some_and(|new_exp_month| {
            existing_card_data
                .expiry_month
                .map(|exp_month| exp_month.expose())
                != Some(new_exp_month)
        })
        || card_updation_obj
            .card_exp_year
            .map(|exp_year| exp_year.expose())
            .is_some_and(|new_exp_year| {
                existing_card_data
                    .expiry_year
                    .map(|exp_year| exp_year.expose())
                    != Some(new_exp_year)
            })
        || card_updation_obj
            .card_holder_name
            .map(|name| name.expose())
            .is_some_and(|new_card_holder_name| {
                existing_card_data
                    .card_holder_name
                    .map(|name| name.expose())
                    != Some(new_card_holder_name)
            })
        || card_updation_obj
            .nick_name
            .map(|nick_name| nick_name.expose())
            .is_some_and(|new_nick_name| {
                existing_card_data
                    .nick_name
                    .map(|nick_name| nick_name.expose())
                    != Some(new_nick_name)
            })
}

#[cfg(feature = "v2")]
pub fn validate_payment_method_update(
    _card_updation_obj: CardDetailUpdate,
    _existing_card_data: api::CardDetailFromLocker,
) -> bool {
    todo!()
}

// Wrapper function to switch lockers

pub async fn get_card_from_locker(
    state: &routes::SessionState,
    customer_id: &id_type::CustomerId,
    merchant_id: &id_type::MerchantId,
    card_reference: &str,
) -> errors::RouterResult<Card> {
    metrics::GET_FROM_LOCKER.add(1, &[]);

    let get_card_from_rs_locker_resp = common_utils::metrics::utils::record_operation_time(
        async {
            get_card_from_hs_locker(
                state,
                customer_id,
                merchant_id,
                card_reference,
                api_enums::LockerChoice::HyperswitchCardVault,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed while getting card from hyperswitch card vault")
            .inspect_err(|_| {
                metrics::CARD_LOCKER_FAILURES.add(
                    1,
                    router_env::metric_attributes!(("locker", "rust"), ("operation", "get")),
                );
            })
        },
        &metrics::CARD_GET_TIME,
        router_env::metric_attributes!(("locker", "rust")),
    )
    .await?;

    logger::debug!("card retrieved from rust locker");
    Ok(get_card_from_rs_locker_resp)
}

#[cfg(feature = "v2")]
pub async fn delete_card_by_locker_id(
    state: &routes::SessionState,
    id: &id_type::GlobalCustomerId,
    merchant_id: &id_type::MerchantId,
) -> errors::RouterResult<payment_methods::DeleteCardResp> {
    todo!()
}

#[instrument(skip_all)]
pub async fn decode_and_decrypt_locker_data(
    state: &routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    enc_card_data: String,
) -> errors::CustomResult<Secret<String>, errors::VaultError> {
    let key = key_store.key.get_inner().peek();
    let decoded_bytes = hex::decode(&enc_card_data)
        .change_context(errors::VaultError::ResponseDeserializationFailed)
        .attach_printable("Failed to decode hex string into bytes")?;
    // Decrypt
    domain::types::crypto_operation(
        &state.into(),
        type_name!(payment_method::PaymentMethod),
        domain::types::CryptoOperation::DecryptOptional(Some(Encryption::new(
            decoded_bytes.into(),
        ))),
        Identifier::Merchant(key_store.merchant_id.clone()),
        key,
    )
    .await
    .and_then(|val| val.try_into_optionaloperation())
    .change_context(errors::VaultError::FetchPaymentMethodFailed)?
    .map_or(
        Err(report!(errors::VaultError::FetchPaymentMethodFailed)),
        |d| Ok(d.into_inner()),
    )
}

#[instrument(skip_all)]
pub async fn get_payment_method_from_hs_locker<'a>(
    state: &'a routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    customer_id: &id_type::CustomerId,
    merchant_id: &id_type::MerchantId,
    payment_method_reference: &'a str,
    locker_choice: Option<api_enums::LockerChoice>,
) -> errors::CustomResult<Secret<String>, errors::VaultError> {
    let locker = &state.conf.locker;
    let jwekey = state.conf.jwekey.get_inner();

    let payment_method_data = if !locker.mock_locker {
        let request = payment_methods::mk_get_card_request_hs(
            jwekey,
            locker,
            customer_id,
            merchant_id,
            payment_method_reference,
            locker_choice,
            state.tenant.tenant_id.clone(),
            state.request_id,
        )
        .await
        .change_context(errors::VaultError::FetchPaymentMethodFailed)
        .attach_printable("Making get payment method request failed")?;

        let get_card_resp = call_locker_api::<payment_methods::RetrieveCardResp>(
            state,
            request,
            "get_pm_from_locker",
            locker_choice,
        )
        .await
        .change_context(errors::VaultError::FetchPaymentMethodFailed)?;

        let retrieve_card_resp = get_card_resp
            .payload
            .get_required_value("RetrieveCardRespPayload")
            .change_context(errors::VaultError::FetchPaymentMethodFailed)
            .attach_printable("Failed to retrieve field - payload from RetrieveCardResp")?;
        let enc_card_data = retrieve_card_resp
            .enc_card_data
            .get_required_value("enc_card_data")
            .change_context(errors::VaultError::FetchPaymentMethodFailed)
            .attach_printable(
                "Failed to retrieve field - enc_card_data from RetrieveCardRespPayload",
            )?;
        decode_and_decrypt_locker_data(state, key_store, enc_card_data.peek().to_string()).await?
    } else {
        mock_get_payment_method(state, key_store, payment_method_reference)
            .await?
            .payment_method
            .payment_method_data
    };
    Ok(payment_method_data)
}

#[instrument(skip_all)]
pub async fn add_card_to_hs_locker(
    state: &routes::SessionState,
    payload: &payment_methods::StoreLockerReq,
    customer_id: &id_type::CustomerId,
    locker_choice: api_enums::LockerChoice,
) -> errors::CustomResult<payment_methods::StoreCardRespPayload, errors::VaultError> {
    let locker = &state.conf.locker;
    let jwekey = state.conf.jwekey.get_inner();
    let db = &*state.store;
    let stored_card_response = if !locker.mock_locker {
        let request = payment_methods::mk_add_locker_request_hs(
            jwekey,
            locker,
            payload,
            locker_choice,
            state.tenant.tenant_id.clone(),
            state.request_id,
        )
        .await?;
        call_locker_api::<payment_methods::StoreCardResp>(
            state,
            request,
            "add_card_to_hs_locker",
            Some(locker_choice),
        )
        .await
        .change_context(errors::VaultError::SaveCardFailed)?
    } else {
        let card_id = generate_id(consts::ID_LENGTH, "card");
        mock_call_to_locker_hs(db, &card_id, payload, None, None, Some(customer_id)).await?
    };

    let stored_card = stored_card_response
        .payload
        .get_required_value("StoreCardRespPayload")
        .change_context(errors::VaultError::SaveCardFailed)?;
    Ok(stored_card)
}

#[instrument(skip_all)]
pub async fn call_locker_api<T>(
    state: &routes::SessionState,
    request: Request,
    flow_name: &str,
    locker_choice: Option<api_enums::LockerChoice>,
) -> errors::CustomResult<T, errors::VaultError>
where
    T: serde::de::DeserializeOwned,
{
    let locker = &state.conf.locker;
    let jwekey = state.conf.jwekey.get_inner();
    let response_type_name = type_name!(T);

    let response = services::call_connector_api(state, request, flow_name)
        .await
        .change_context(errors::VaultError::ApiError)?;

    let is_locker_call_succeeded = response.is_ok();

    let jwe_body = response
        .unwrap_or_else(|err| err)
        .response
        .parse_struct::<services::JweBody>("JweBody")


// Struct: DummyConnectorRefundRequest
// File: crates/hyperswitch_connectors/src/connectors/dummyconnector/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DummyConnectorRefundRequest


// Struct: RefundListConstraints
// File: crates/hyperswitch_domain_models/src/refunds.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct RefundListConstraints


// Struct: GigadatCpiRequest
// File: crates/hyperswitch_connectors/src/connectors/gigadat/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GigadatCpiRequest


// File: crates/hyperswitch_connectors/src/connectors/ctp_mastercard.rs
// Module: hyperswitch_connectors
// Public structs: 1

use common_utils::errors::CustomResult;
use error_stack::report;
use hyperswitch_domain_models::{
    router_data::{AccessToken, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{PaymentsResponseData, RefundsResponseData},
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors, webhooks,
};

use crate::constants::headers;

#[derive(Clone)]
pub struct CtpMastercard;

impl api::Payment for CtpMastercard {}
impl api::PaymentSession for CtpMastercard {}
impl api::ConnectorAccessToken for CtpMastercard {}
impl api::MandateSetup for CtpMastercard {}
impl api::PaymentAuthorize for CtpMastercard {}
impl api::PaymentSync for CtpMastercard {}
impl api::PaymentCapture for CtpMastercard {}
impl api::PaymentVoid for CtpMastercard {}
impl api::Refund for CtpMastercard {}
impl api::RefundExecute for CtpMastercard {}
impl api::RefundSync for CtpMastercard {}
impl api::PaymentToken for CtpMastercard {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for CtpMastercard
{
    // Not Implemented (R)
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for CtpMastercard
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.get_content_type().to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }
}

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

    fn base_url<'a>(&self, _connectors: &'a Connectors) -> &'a str {
        ""
    }
}

impl ConnectorValidation for CtpMastercard {}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for CtpMastercard {}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for CtpMastercard {}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
    for CtpMastercard
{
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData>
    for CtpMastercard
{
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for CtpMastercard {}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for CtpMastercard {}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for CtpMastercard {}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for CtpMastercard {}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for CtpMastercard {}

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

    fn get_webhook_event_type(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

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

impl ConnectorSpecifications for CtpMastercard {}


// Implementation: impl RetryLimitsConfig
// File: crates/router/src/types/storage/revenue_recovery.rs
// Module: router
// Methods: 1 total (1 public)

impl RetryLimitsConfig


// Module Structure
// File: crates/router/src/core/revenue_recovery.rs
// Module: router

// Public submodules:
pub mod api;
pub mod transformers;
pub mod types;


// File: crates/router/src/bin/scheduler.rs
// Module: router
// 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
}


// Function: relay
// File: crates/router/src/routes/relay.rs
// Module: router

pub fn relay(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Json<api_models::relay::RelayRequest>,
) -> impl Responder


// Struct: SignifydErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/signifyd/transformers/api.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SignifydErrorResponse


// Function: server
// File: crates/router/src/routes/app.rs
// Module: router

pub fn server(state: AppState) -> Scope


// Struct: BillwerkErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/billwerk/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BillwerkErrorResponse


// Function: get_request_id
// File: crates/hyperswitch_interfaces/src/events/routing_api_logs.rs
// Module: hyperswitch_interfaces

// Documentation: Returns the request ID of the event.

pub fn get_request_id(&self) -> &str


// Implementation: impl api::PaymentAuthorize for for Adyen
// File: crates/hyperswitch_connectors/src/connectors/adyen.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Adyen


// Implementation: impl ConnectorTransactionId for for Helcim
// File: crates/hyperswitch_connectors/src/connectors/helcim.rs
// Module: hyperswitch_connectors
// Methods: 2 total (0 public)

impl ConnectorTransactionId for for Helcim


// Implementation: impl api::RefundExecute for for Mollie
// File: crates/hyperswitch_connectors/src/connectors/mollie.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundExecute for for Mollie


// Implementation: impl api::MandateSetup for for Authipay
// File: crates/hyperswitch_connectors/src/connectors/authipay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::MandateSetup for for Authipay


// Function: get_three_ds_decision_rule_output
// File: crates/router/src/core/three_ds_decision_rule.rs
// Module: router

pub fn get_three_ds_decision_rule_output(
    state: &SessionState,
    merchant_id: &common_utils::id_type::MerchantId,
    request: api_models::three_ds_decision_rule::ThreeDsDecisionRuleExecuteRequest,
) -> errors::RouterResult<common_types::three_ds_decision_rule_engine::ThreeDSDecision>


// Trait: Operation
// File: crates/router/src/core/payments/operations.rs
// Module: router

pub trait Operation<F: Clone, T>: Send + std::fmt::Debug


// Implementation: impl Responder
// File: crates/router/src/analytics.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Struct: RSyncSearchData
// File: crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RSyncSearchData


// Implementation: impl RefundMetricAccumulator for for RefundProcessedAmountAccumulator
// File: crates/analytics/src/refunds/accumulator.rs
// Module: analytics
// Methods: 2 total (0 public)

impl RefundMetricAccumulator for for RefundProcessedAmountAccumulator