pub async fn get_frm_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetFrmFilterRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetFrmFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req: GetFrmFilterRequest, _| async move {
                analytics::frm::get_filters(&state.pool, req, auth.merchant_account.get_id())
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


File: crates/hyperswitch_domain_models/src/callback_mapper.rs
 Public functions: 1
 Public structs: 1

use common_enums::enums as common_enums;
use common_types::callback_mapper::CallbackMapperData;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CallbackMapper {
    pub id: String,
    pub callback_mapper_id_type: common_enums::CallbackMapperIdType,
    pub data: CallbackMapperData,
    pub created_at: time::PrimitiveDateTime,
    pub last_modified_at: time::PrimitiveDateTime,
}

impl CallbackMapper {
    pub fn new(
        id: String,
        callback_mapper_id_type: common_enums::CallbackMapperIdType,
        data: CallbackMapperData,
        created_at: time::PrimitiveDateTime,
        last_modified_at: time::PrimitiveDateTime,
    ) -> Self {
        Self {
            id,
            callback_mapper_id_type,
            data,
            created_at,
            last_modified_at,
        }
    }
}


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 ConnectorCommon for for DummyConnector


pub struct NovalnetWebhookNotificationResponse {
    pub event: NovalnetWebhookEvent,
    pub result: ResultData,
    pub transaction: NovalnetWebhookTransactionData,
}


pub struct GetThemeResponse {
    pub theme_id: String,
    pub theme_name: String,
    pub entity_type: EntityType,
    pub tenant_id: id_type::TenantId,
    pub org_id: Option<id_type::OrganizationId>,
    pub merchant_id: Option<id_type::MerchantId>,
    pub profile_id: Option<id_type::ProfileId>,
    pub email_config: EmailThemeConfig,
    pub theme_data: ThemeData,
}


pub struct PaymentsMandateReferenceRecord {
    pub connector_mandate_id: String,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<i64>,
    pub original_payment_authorized_currency: Option<Currency>,
    pub mandate_metadata: Option<pii::SecretSerdeValue>,
    pub connector_mandate_status: Option<common_enums::ConnectorMandateStatus>,
    pub connector_mandate_request_reference_id: Option<String>,
}


pub struct PaymentMethodDeleteResponse {
    /// The unique identifier of the Payment method
    #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")]
    pub payment_method_id: String,

    /// Whether payment method was deleted or not
    #[schema(example = true)]
    pub deleted: bool,
}


pub async fn delete_by_unified_code_unified_message_locale(
        conn: &PgPooledConn,
        unified_code: String,
        unified_message: String,
        locale: String,
    ) -> StorageResult<bool> {
        generics::generic_delete::<<Self as HasTable>::Table, _>(
            conn,
            dsl::unified_code
                .eq(unified_code)
                .and(dsl::unified_message.eq(unified_message))
                .and(dsl::locale.eq(locale)),
        )
        .await
    }


impl Responder {
    let flow = Flow::PaymentsIncrementalAuthorization;
    let mut payload = json_payload.into_inner();
    let payment_id = path.into_inner();

    tracing::Span::current().record("payment_id", payment_id.get_string_repr());

    payload.payment_id = payment_id;
    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::IncrementalAuthorization,
                payment_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::IncrementalAuthorization>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentIncrementalAuthorization,
                req,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                None,
                HeaderPayload::default(),
            )
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: true,
        }),
        locking_action,
    ))
    .await
}


pub fn server(state: AppState) -> Scope {
        let mut routes_with_restricted_access = web::scope("");
        #[cfg(not(feature = "external_access_dc"))]
        {
            routes_with_restricted_access =
                routes_with_restricted_access.guard(actix_web::guard::Host("localhost"));
        }
        routes_with_restricted_access = routes_with_restricted_access
            .service(web::resource("/payment").route(web::post().to(dummy_connector_payment)));
        web::scope("/dummy-connector")
            .app_data(web::Data::new(state))
            .service(routes_with_restricted_access)
    }


pub struct RiskifiedFailedTransactionResponse {
    checkout: OrderResponse,
}


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

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

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

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

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

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

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


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

pub mod transformers;

use common_utils::{
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector, MinorUnit},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{
            Authorize, CalculateTax, Capture, PSync, PaymentMethodToken, Session, SetupMandate,
            Void,
        },
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        PaymentsTaxCalculationData, RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentsResponseData, RefundsResponseData, SupportedPaymentMethods,
        TaxCalculationResponseData,
    },
    types::PaymentsTaxCalculationRouterData,
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks,
};
use masking::{Mask, PeekInterface};
use transformers as taxjar;

use crate::{constants::headers, types::ResponseRouterData, utils};

#[derive(Clone)]
pub struct Taxjar {
    amount_converter: &'static (dyn AmountConvertor<Output = FloatMajorUnit> + Sync),
}

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

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

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

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Taxjar
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 Taxjar {
    fn id(&self) -> &'static str {
        "taxjar"
    }

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

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

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

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

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

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

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

impl ConnectorValidation for Taxjar {}

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

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

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

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

impl ConnectorIntegration<CalculateTax, PaymentsTaxCalculationData, TaxCalculationResponseData>
    for Taxjar
{
    fn get_headers(
        &self,
        req: &PaymentsTaxCalculationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

    fn get_url(
        &self,
        _req: &PaymentsTaxCalculationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}taxes", self.base_url(connectors)))
    }
    fn get_request_body(
        &self,
        req: &PaymentsTaxCalculationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            req.request.amount,
            req.request.currency,
        )?;

        let shipping = utils::convert_amount(
            self.amount_converter,
            req.request.shipping_cost.unwrap_or(MinorUnit::zero()),
            req.request.currency,
        )?;

        let order_amount = utils::convert_amount(
            self.amount_converter,
            req.request
                .order_details
                .as_ref()
                .map(|details| details.iter().map(|item| item.amount).sum())
                .unwrap_or(MinorUnit::zero()),
            req.request.currency,
        )?;

        let connector_router_data =
            taxjar::TaxjarRouterData::from((amount, order_amount, shipping, req));
        let connector_req = taxjar::TaxjarPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }
    fn build_request(
        &self,
        req: &PaymentsTaxCalculationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsTaxCalculationType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::PaymentsTaxCalculationType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsTaxCalculationType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsTaxCalculationRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsTaxCalculationRouterData, errors::ConnectorError> {
        let response: taxjar::TaxjarPaymentsResponse = res
            .response
            .parse_struct("Taxjar PaymentsResponse")
            .change_context(errors::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,
        })
    }

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

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

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

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

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

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

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Taxjar {
    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))
    }
}

static TAXJAR_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Taxjar",
    description: "TaxJar is a cloud-based platform that automates sales tax calculations, reporting, and filing for businesses across multiple channels",
    connector_type: common_enums::HyperswitchConnectorCategory::TaxCalculationProvider,
    integration_status: common_enums::ConnectorIntegrationStatus::Sandbox,
};

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

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

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


pub struct BatchDecryptDataResponse {
    pub data: DecryptedDataGroup,
}


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

pub mod transformers;
use api_models::{self, webhooks::IncomingWebhookEvent};
#[cfg(feature = "payouts")]
use base64::Engine;
#[cfg(feature = "payouts")]
use common_utils::crypto;
use common_utils::errors::CustomResult;
#[cfg(feature = "payouts")]
use common_utils::ext_traits::{ByteSliceExt as _, BytesExt};
#[cfg(feature = "payouts")]
use common_utils::request::RequestContent;
#[cfg(feature = "payouts")]
use common_utils::request::{Method, Request, RequestBuilder};
#[cfg(feature = "payouts")]
use common_utils::types::MinorUnitForConnector;
#[cfg(feature = "payouts")]
use common_utils::types::{AmountConvertor, MinorUnit};
#[cfg(not(feature = "payouts"))]
use error_stack::report;
use error_stack::ResultExt;
#[cfg(feature = "payouts")]
use http::HeaderName;
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::router_data::{ErrorResponse, RouterData};
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::router_flow_types::PoFulfill;
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::types::{PayoutsData, PayoutsResponseData, PayoutsRouterData};
use hyperswitch_domain_models::{
    api::ApplicationResponse,
    router_data::{AccessToken, ConnectorAuthType},
    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::{
        ConnectorInfo, PaymentsResponseData, RefundsResponseData, SupportedPaymentMethods,
    },
};
#[cfg(feature = "payouts")]
use hyperswitch_interfaces::events::connector_api_logs::ConnectorEvent;
#[cfg(feature = "payouts")]
use hyperswitch_interfaces::types::{PayoutFulfillType, Response};
use hyperswitch_interfaces::{
    api::{self, ConnectorCommon, ConnectorIntegration, ConnectorSpecifications},
    configs::Connectors,
    errors::ConnectorError,
    webhooks::{IncomingWebhook, IncomingWebhookFlowError, IncomingWebhookRequestDetails},
};
use masking::{Mask as _, Maskable, Secret};
#[cfg(feature = "payouts")]
use ring::hmac;
#[cfg(feature = "payouts")]
use router_env::{instrument, tracing};
#[cfg(feature = "payouts")]
use transformers::get_adyen_webhook_event;

use self::transformers as adyenplatform;
use crate::constants::headers;
#[cfg(feature = "payouts")]
use crate::types::ResponseRouterData;
#[cfg(feature = "payouts")]
use crate::utils::convert_amount;

#[derive(Clone)]
pub struct Adyenplatform {
    #[cfg(feature = "payouts")]
    amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
}
impl Adyenplatform {
    pub const fn new() -> &'static Self {
        &Self {
            #[cfg(feature = "payouts")]
            amount_converter: &MinorUnitForConnector,
        }
    }
}

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

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

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

    #[cfg(feature = "payouts")]
    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        let response: adyenplatform::AdyenTransferErrorResponse = res
            .response
            .parse_struct("AdyenTransferErrorResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

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

impl api::Payment for Adyenplatform {}
impl api::PaymentAuthorize for Adyenplatform {}
impl api::PaymentSync for Adyenplatform {}
impl api::PaymentVoid for Adyenplatform {}
impl api::PaymentCapture for Adyenplatform {}
impl api::MandateSetup for Adyenplatform {}
impl api::ConnectorAccessToken for Adyenplatform {}
impl api::PaymentToken for Adyenplatform {}
impl api::ConnectorValidation for Adyenplatform {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Adyenplatform
{
}

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

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

impl api::PaymentSession for Adyenplatform {}

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

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

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

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

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

impl api::Payouts for Adyenplatform {}
#[cfg(feature = "payouts")]
impl api::PayoutFulfill for Adyenplatform {}

#[cfg(feature = "payouts")]
impl ConnectorIntegration<PoFulfill, PayoutsData, PayoutsResponseData> for Adyenplatform {
    fn get_url(
        &self,
        _req: &PayoutsRouterData<PoFulfill>,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!(
            "{}btl/v4/transfers",
            connectors.adyenplatform.base_url,
        ))
    }

    fn get_headers(
        &self,
        req: &PayoutsRouterData<PoFulfill>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PayoutFulfillType::get_content_type(self).to_string().into(),
        )];
        let auth = adyenplatform::AdyenplatformAuthType::try_from(&req.connector_auth_type)
            .change_context(ConnectorError::FailedToObtainAuthType)?;
        let mut api_key = vec![(
            headers::AUTHORIZATION.to_string(),
            auth.api_key.into_masked(),
        )];
        header.append(&mut api_key);
        Ok(header)
    }

    fn get_request_body(
        &self,
        req: &PayoutsRouterData<PoFulfill>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.destination_currency,
        )?;
        let connector_router_data =
            adyenplatform::AdyenPlatformRouterData::try_from((amount, req))?;
        let connector_req = adyenplatform::AdyenTransferRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

        Ok(Some(request))
    }

    #[instrument(skip_all)]
    fn handle_response(
        &self,
        data: &PayoutsRouterData<PoFulfill>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PayoutsRouterData<PoFulfill>, ConnectorError> {
        let response: adyenplatform::AdyenTransferResponse = res
            .response
            .parse_struct("AdyenTransferResponse")
            .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,
        })
    }

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

impl api::Refund for Adyenplatform {}
impl api::RefundExecute for Adyenplatform {}
impl api::RefundSync for Adyenplatform {}

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

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

#[async_trait::async_trait]
impl IncomingWebhook for Adyenplatform {
    #[cfg(feature = "payouts")]
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, ConnectorError> {
        Ok(Box::new(crypto::HmacSha256))
    }

    #[cfg(feature = "payouts")]
    fn get_webhook_source_verification_signature(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, ConnectorError> {
        let base64_signature = request
            .headers
            .get(HeaderName::from_static("hmacsignature"))
            .ok_or(ConnectorError::WebhookSourceVerificationFailed)?;
        Ok(base64_signature.as_bytes().to_vec())
    }

    #[cfg(feature = "payouts")]
    fn get_webhook_source_verification_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, ConnectorError> {
        Ok(request.body.to_vec())
    }

    #[cfg(feature = "payouts")]
    async fn verify_webhook_source(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>,
        connector_label: &str,
    ) -> CustomResult<bool, ConnectorError> {
        use common_utils::consts;

        let connector_webhook_secrets = self
            .get_webhook_source_verification_merchant_secret(
                merchant_id,
                connector_label,
                connector_webhook_details,
            )
            .await
            .change_context(ConnectorError::WebhookSourceVerificationFailed)?;

        let signature = self
            .get_webhook_source_verification_signature(request, &connector_webhook_secrets)
            .change_context(ConnectorError::WebhookSourceVerificationFailed)?;

        let message = self
            .get_webhook_source_verification_message(
                request,
                merchant_id,
                &connector_webhook_secrets,
            )
            .change_context(ConnectorError::WebhookSourceVerificationFailed)?;

        let raw_key = hex::decode(connector_webhook_secrets.secret)
            .change_context(ConnectorError::WebhookVerificationSecretInvalid)?;

        let signing_key = hmac::Key::new(hmac::HMAC_SHA256, &raw_key);
        let signed_messaged = hmac::sign(&signing_key, &message);
        let payload_sign = consts::BASE64_ENGINE.encode(signed_messaged.as_ref());
        Ok(payload_sign.as_bytes().eq(&signature))
    }

    fn get_webhook_object_reference_id(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            let webhook_body: adyenplatform::AdyenplatformIncomingWebhook = request
                .body
                .parse_struct("AdyenplatformIncomingWebhook")
                .change_context(ConnectorError::WebhookSourceVerificationFailed)?;

            Ok(api_models::webhooks::ObjectReferenceId::PayoutId(
                api_models::webhooks::PayoutIdType::PayoutAttemptId(webhook_body.data.reference),
            ))
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }

    fn get_webhook_api_response(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
        error_kind: Option<IncomingWebhookFlowError>,
    ) -> CustomResult<ApplicationResponse<serde_json::Value>, ConnectorError> {
        if error_kind.is_some() {
            Ok(ApplicationResponse::JsonWithHeaders((
                serde_json::Value::Null,
                vec![(
                    "x-http-code".to_string(),
                    Maskable::Masked(Secret::new("404".to_string())),
                )],
            )))
        } else {
            Ok(ApplicationResponse::StatusOk)
        }
    }

    fn get_webhook_event_type(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            let webhook_body: adyenplatform::AdyenplatformIncomingWebhook = request
                .body
                .parse_struct("AdyenplatformIncomingWebhook")
                .change_context(ConnectorError::WebhookSourceVerificationFailed)?;

            Ok(get_adyen_webhook_event(
                webhook_body.webhook_type,
                webhook_body.data.status,
                webhook_body.data.tracking,
                webhook_body.data.category.as_ref(),
            ))
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }

    fn get_webhook_resource_object(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            let webhook_body: adyenplatform::AdyenplatformIncomingWebhook = request
                .body
                .parse_struct("AdyenplatformIncomingWebhook")
                .change_context(ConnectorError::WebhookSourceVerificationFailed)?;
            Ok(Box::new(webhook_body))
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }
}

static ADYENPLATFORM_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Adyen Platform",
    description: "Adyen Platform for marketplace payouts and disbursements",
    connector_type: common_enums::HyperswitchConnectorCategory::PayoutProcessor,
    integration_status: common_enums::ConnectorIntegrationStatus::Sandbox,
};

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

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

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


pub struct TransactionResponse {
    #[serde(rename = "NUMTRANS")]
    pub transaction_number: String,

    #[serde(rename = "NUMAPPEL")]
    pub paybox_order_id: String,

    #[serde(rename = "CODEREPONSE")]
    pub response_code: String,

    #[serde(rename = "COMMENTAIRE")]
    pub response_message: String,
    #[serde(rename = "PORTEUR")]
    pub carrier_id: Option<Secret<String>>,

    #[serde(rename = "REFABONNE")]
    pub customer_id: Option<Secret<String>>,
}


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

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

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

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

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

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

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: consts::NO_ERROR_CODE.to_string(),
            message: response.message.clone(),
            reason: Some(response.message),
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


pub async fn retrieve_gsm_rule(
    state: SessionState,
    gsm_request: gsm_api_types::GsmRetrieveRequest,
) -> RouterResponse<gsm_api_types::GsmResponse> {
    let db = state.store.as_ref();
    let gsm_api_types::GsmRetrieveRequest {
        connector,
        flow,
        sub_flow,
        code,
        message,
    } = gsm_request;
    GsmInterface::find_gsm_rule(db, connector.to_string(), flow, sub_flow, code, message)
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "GSM with given key does not exist in our records".to_string(),
        })
        .map(|gsm| services::ApplicationResponse::Json(gsm.foreign_into()))
}


pub struct PaymentProcessedAmount;


impl StripebillingInvoiceBody {
    pub fn get_invoice_webhook_data_from_body(
        body: &[u8],
    ) -> CustomResult<Self, errors::ConnectorError> {
        let webhook_body = body
            .parse_struct::<Self>("StripebillingInvoiceBody")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        Ok(webhook_body)
    }
}


pub fn get_setup_mandate_body(req: &types::SetupMandateRouterData) -> Result<Vec<u8>, Error> {
    let card_holder_name = req.get_billing_full_name()?;
    let auth_details = BamboraapacAuthType::try_from(&req.connector_auth_type)?;
    let body = match &req.request.payment_method_data {
        PaymentMethodData::Card(card) => {
            format!(
                r#"
                <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                xmlns:sipp="http://www.ippayments.com.au/interface/api/sipp">
                <soapenv:Header/>
                <soapenv:Body>
                    <sipp:TokeniseCreditCard>
                        <sipp:tokeniseCreditCardXML>
                            <![CDATA[
                                <TokeniseCreditCard>
                                    <CardNumber>{}</CardNumber>
                                    <ExpM>{}</ExpM>
                                    <ExpY>{}</ExpY>
                                    <CardHolderName>{}</CardHolderName>
                                    <TokeniseAlgorithmID>2</TokeniseAlgorithmID>
                                    <UserName>{}</UserName>
                                    <Password>{}</Password>
                                </TokeniseCreditCard>
                            ]]>
                        </sipp:tokeniseCreditCardXML>
                    </sipp:TokeniseCreditCard>
                </soapenv:Body>
                </soapenv:Envelope>
                "#,
                card.card_number.get_card_no(),
                card.card_exp_month.peek(),
                card.get_expiry_year_4_digit().peek(),
                card_holder_name.peek(),
                auth_details.username.peek(),
                auth_details.password.peek(),
            )
        }
        _ => {
            return Err(errors::ConnectorError::NotImplemented(
                utils::get_unimplemented_payment_method_error_message("Bambora APAC"),
            ))?;
        }
    };

    Ok(body.as_bytes().to_vec())
}


pub struct Zsl;


impl PaymentSync for Riskified {}


impl User {
    pub fn server(state: AppState) -> Scope {
        let mut route = web::scope("/v2/users").app_data(web::Data::new(state));

        route = route.service(
            web::resource("/create-merchant")
                .route(web::post().to(user::user_merchant_account_create)),
        );
        route = route.service(
            web::scope("/list")
                .service(
                    web::resource("/merchant")
                        .route(web::get().to(user::list_merchants_for_user_in_org)),
                )
                .service(
                    web::resource("/profile")
                        .route(web::get().to(user::list_profiles_for_user_in_org_and_merchant)),
                ),
        );

        route = route.service(
            web::scope("/switch")
                .service(
                    web::resource("/merchant")
                        .route(web::post().to(user::switch_merchant_for_user_in_org)),
                )
                .service(
                    web::resource("/profile")
                        .route(web::post().to(user::switch_profile_for_user_in_org_and_merchant)),
                ),
        );

        route = route.service(
            web::resource("/data")
                .route(web::get().to(user::get_multiple_dashboard_metadata))
                .route(web::post().to(user::set_dashboard_metadata)),
        );

        route
    }
}


/// Throws errors when the requested resource is not found
    pub async fn get_resource<D, R>(
        &self,
        state: &KeyManagerState,
        key_store: &MerchantKeyStore,
        resources: MutexGuard<'_, Vec<D>>,
        filter_fn: impl Fn(&&D) -> bool,
        error_message: String,
    ) -> CustomResult<R, StorageError>
    where
        D: Sync + ReverseConversion<R> + Clone,
        R: Conversion,
    {
        match self
            .find_resource(state, key_store, resources, filter_fn)
            .await?
        {
            Some(res) => Ok(res),
            None => Err(StorageError::ValueNotFound(error_message).into()),
        }
    }


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

//! Interactions with the AWS SES SDK

use aws_sdk_sesv2::types::Body;
use common_utils::{errors::CustomResult, pii};
use serde::Deserialize;

/// Implementation of aws ses client
pub mod ses;

/// Implementation of SMTP server client
pub mod smtp;

/// Implementation of Email client when email support is disabled
pub mod no_email;

/// Custom Result type alias for Email operations.
pub type EmailResult<T> = CustomResult<T, EmailError>;

/// A trait that defines the methods that must be implemented to send email.
#[async_trait::async_trait]
pub trait EmailClient: Sync + Send + dyn_clone::DynClone {
    /// The rich text type of the email client
    type RichText;

    /// Sends an email to the specified recipient with the given subject and body.
    async fn send_email(
        &self,
        recipient: pii::Email,
        subject: String,
        body: Self::RichText,
        proxy_url: Option<&String>,
    ) -> EmailResult<()>;

    /// Convert Stringified HTML to client native rich text format
    /// This has to be done because not all clients may format html as the same
    fn convert_to_rich_text(
        &self,
        intermediate_string: IntermediateString,
    ) -> CustomResult<Self::RichText, EmailError>
    where
        Self::RichText: Send;
}

/// A super trait which is automatically implemented for all EmailClients
#[async_trait::async_trait]
pub trait EmailService: Sync + Send + dyn_clone::DynClone {
    /// Compose and send email using the email data
    async fn compose_and_send_email(
        &self,
        base_url: &str,
        email_data: Box<dyn EmailData + Send>,
        proxy_url: Option<&String>,
    ) -> EmailResult<()>;
}

#[async_trait::async_trait]
impl<T> EmailService for T
where
    T: EmailClient,
    <Self as EmailClient>::RichText: Send,
{
    async fn compose_and_send_email(
        &self,
        base_url: &str,
        email_data: Box<dyn EmailData + Send>,
        proxy_url: Option<&String>,
    ) -> EmailResult<()> {
        let email_data = email_data.get_email_data(base_url);
        let email_data = email_data.await?;

        let EmailContents {
            subject,
            body,
            recipient,
        } = email_data;

        let rich_text_string = self.convert_to_rich_text(body)?;

        self.send_email(recipient, subject, rich_text_string, proxy_url)
            .await
    }
}

/// This is a struct used to create Intermediate String for rich text ( html )
#[derive(Debug)]
pub struct IntermediateString(String);

impl IntermediateString {
    /// Create a new Instance of IntermediateString using a string
    pub fn new(inner: String) -> Self {
        Self(inner)
    }

    /// Get the inner String
    pub fn into_inner(self) -> String {
        self.0
    }
}

/// Temporary output for the email subject
#[derive(Debug)]
pub struct EmailContents {
    /// The subject of email
    pub subject: String,

    /// This will be the intermediate representation of the email body in a generic format.
    /// The email clients can convert this intermediate representation to their client specific rich text format
    pub body: IntermediateString,

    /// The email of the recipient to whom the email has to be sent
    pub recipient: pii::Email,
}

/// A trait which will contain the logic of generating the email subject and body
#[async_trait::async_trait]
pub trait EmailData {
    /// Get the email contents
    async fn get_email_data(&self, base_url: &str) -> CustomResult<EmailContents, EmailError>;
}

dyn_clone::clone_trait_object!(EmailClient<RichText = Body>);

/// List of available email clients to choose from
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(tag = "active_email_client")]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum EmailClientConfigs {
    #[default]
    /// Default Email client to use when no client is specified
    NoEmailClient,
    /// AWS ses email client
    Ses {
        /// AWS SES client configuration
        aws_ses: ses::SESConfig,
    },
    /// Other Simple SMTP server
    Smtp {
        /// SMTP server configuration
        smtp: smtp::SmtpServerConfig,
    },
}

/// Struct that contains the settings required to construct an EmailClient.
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(default)]
pub struct EmailSettings {
    /// The AWS region to send SES requests to.
    pub aws_region: String,

    /// Number of days for verification of the email
    pub allowed_unverified_days: i64,

    /// Sender email
    pub sender_email: String,

    #[serde(flatten)]
    /// The client specific configurations
    pub client_config: EmailClientConfigs,

    /// Recipient email for recon emails
    pub recon_recipient_email: pii::Email,

    /// Recipient email for recon emails
    pub prod_intent_recipient_email: pii::Email,
}

impl EmailSettings {
    /// Validation for the Email client specific configurations
    pub fn validate(&self) -> Result<(), &'static str> {
        match &self.client_config {
            EmailClientConfigs::Ses { ref aws_ses } => aws_ses.validate(),
            EmailClientConfigs::Smtp { ref smtp } => smtp.validate(),
            EmailClientConfigs::NoEmailClient => Ok(()),
        }
    }
}

/// Errors that could occur from EmailClient.
#[derive(Debug, thiserror::Error)]
pub enum EmailError {
    /// An error occurred when building email client.
    #[error("Error building email client")]
    ClientBuildingFailure,

    /// An error occurred when sending email
    #[error("Error sending email to recipient")]
    EmailSendingFailure,

    /// Failed to generate the email token
    #[error("Failed to generate email token")]
    TokenGenerationFailure,

    /// The expected feature is not implemented
    #[error("Feature not implemented")]
    NotImplemented,

    /// An error occurred when building email content.
    #[error("Error building email content")]
    ContentBuildFailure,
}


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


#[serde(rename_all = "snake_case")]
pub enum MerchantRecipientData {
    ConnectorRecipientId(Secret<String>),
    WalletId(Secret<String>),
    AccountData(MerchantAccountData),
}

impl ForeignFrom<MerchantRecipientData> for api_models::admin::MerchantRecipientData {
    fn foreign_from(value: MerchantRecipientData) -> Self {
        match value {
            MerchantRecipientData::ConnectorRecipientId(id) => Self::ConnectorRecipientId(id),
            MerchantRecipientData::WalletId(id) => Self::WalletId(id),
            MerchantRecipientData::AccountData(data) => {
                Self::AccountData(api_models::admin::MerchantAccountData::foreign_from(data))
            }
        }
    }
}

impl From<api_models::admin::MerchantRecipientData> for MerchantRecipientData {
    fn from(value: api_models::admin::MerchantRecipientData) -> Self {
        match value {
            api_models::admin::MerchantRecipientData::ConnectorRecipientId(id) => {
                Self::ConnectorRecipientId(id)
            }
            api_models::admin::MerchantRecipientData::WalletId(id) => Self::WalletId(id),
            api_models::admin::MerchantRecipientData::AccountData(data) => {
                Self::AccountData(data.into())
            }
        }
    }
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AdditionalMerchantData {
    OpenBankingRecipientData(MerchantRecipientData),
}

impl ForeignFrom<api_models::admin::AdditionalMerchantData> for AdditionalMerchantData {
    fn foreign_from(value: api_models::admin::AdditionalMerchantData) -> Self {
        match value {
            api_models::admin::AdditionalMerchantData::OpenBankingRecipientData(data) => {
                Self::OpenBankingRecipientData(MerchantRecipientData::from(data))
            }
        }
    }
}

impl ForeignFrom<AdditionalMerchantData> for api_models::admin::AdditionalMerchantData {
    fn foreign_from(value: AdditionalMerchantData) -> Self {
        match value {
            AdditionalMerchantData::OpenBankingRecipientData(data) => {
                Self::OpenBankingRecipientData(
                    api_models::admin::MerchantRecipientData::foreign_from(data),
                )
            }
        }
    }
}

impl ForeignFrom<api_models::admin::ConnectorAuthType> for ConnectorAuthType {
    fn foreign_from(value: api_models::admin::ConnectorAuthType) -> Self {
        match value {
            api_models::admin::ConnectorAuthType::TemporaryAuth => Self::TemporaryAuth,
            api_models::admin::ConnectorAuthType::HeaderKey { api_key } => {
                Self::HeaderKey { api_key }
            }
            api_models::admin::ConnectorAuthType::BodyKey { api_key, key1 } => {
                Self::BodyKey { api_key, key1 }
            }
            api_models::admin::ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => Self::SignatureKey {
                api_key,
                key1,
                api_secret,
            },
            api_models::admin::ConnectorAuthType::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            } => Self::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            },
            api_models::admin::ConnectorAuthType::CurrencyAuthKey { auth_key_map } => {
                Self::CurrencyAuthKey { auth_key_map }
            }
            api_models::admin::ConnectorAuthType::NoKey => Self::NoKey,
            api_models::admin::ConnectorAuthType::CertificateAuth {
                certificate,
                private_key,
            } => Self::CertificateAuth {
                certificate,
                private_key,
            },
        }
    }
}

impl ForeignFrom<ConnectorAuthType> for api_models::admin::ConnectorAuthType {
    fn foreign_from(from: ConnectorAuthType) -> Self {
        match from {
            ConnectorAuthType::TemporaryAuth => Self::TemporaryAuth,
            ConnectorAuthType::HeaderKey { api_key } => Self::HeaderKey { api_key },
            ConnectorAuthType::BodyKey { api_key, key1 } => Self::BodyKey { api_key, key1 },
            ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => Self::SignatureKey {
                api_key,
                key1,
                api_secret,
            },
            ConnectorAuthType::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            } => Self::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            },
            ConnectorAuthType::CurrencyAuthKey { auth_key_map } => {
                Self::CurrencyAuthKey { auth_key_map }
            }
            ConnectorAuthType::NoKey => Self::NoKey,
            ConnectorAuthType::CertificateAuth {
                certificate,
                private_key,
            } => Self::CertificateAuth {
                certificate,
                private_key,
            },
        }
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ConnectorsList {
    pub connectors: Vec<String>,
}

impl ForeignFrom<&PaymentsAuthorizeRouterData> for AuthorizeSessionTokenData {
    fn foreign_from(data: &PaymentsAuthorizeRouterData) -> Self {
        Self {
            amount_to_capture: data.amount_captured,
            currency: data.request.currency,
            connector_transaction_id: data.payment_id.clone(),
            amount: Some(data.request.amount),
        }
    }
}

impl ForeignFrom<&ExternalVaultProxyPaymentsRouterData> for AuthorizeSessionTokenData {
    fn foreign_from(data: &ExternalVaultProxyPaymentsRouterData) -> Self {
        Self {
            amount_to_capture: data.amount_captured,
            currency: data.request.currency,
            connector_transaction_id: data.payment_id.clone(),
            amount: Some(data.request.amount),
        }
    }
}

impl<'a> ForeignFrom<&'a SetupMandateRouterData> for AuthorizeSessionTokenData {
    fn foreign_from(data: &'a SetupMandateRouterData) -> Self {
        Self {
            amount_to_capture: data.request.amount,
            currency: data.request.currency,
            connector_transaction_id: data.payment_id.clone(),
            amount: data.request.amount,
        }
    }
}

pub trait Tokenizable {
    fn set_session_token(&mut self, token: Option<String>);
}

impl Tokenizable for SetupMandateRequestData {
    fn set_session_token(&mut self, _token: Option<String>) {}
}

impl Tokenizable for PaymentsAuthorizeData {
    fn set_session_token(&mut self, token: Option<String>) {
        self.session_token = token;
    }
}

impl Tokenizable for CompleteAuthorizeData {
    fn set_session_token(&mut self, _token: Option<String>) {}
}

impl Tokenizable for ExternalVaultProxyPaymentsData {
    fn set_session_token(&mut self, token: Option<String>) {
        self.session_token = token;
    }
}

impl ForeignFrom<&SetupMandateRouterData> for PaymentsAuthorizeData {
    fn foreign_from(data: &SetupMandateRouterData) -> Self {
        Self {
            currency: data.request.currency,
            payment_method_data: data.request.payment_method_data.clone(),
            confirm: data.request.confirm,
            statement_descriptor_suffix: data.request.statement_descriptor_suffix.clone(),
            mandate_id: data.request.mandate_id.clone(),
            setup_future_usage: data.request.setup_future_usage,
            off_session: data.request.off_session,
            setup_mandate_details: data.request.setup_mandate_details.clone(),
            router_return_url: data.request.router_return_url.clone(),
            email: data.request.email.clone(),
            customer_name: data.request.customer_name.clone(),
            amount: 0,
            order_tax_amount: Some(MinorUnit::zero()),
            minor_amount: MinorUnit::new(0),
            statement_descriptor: None,
            capture_method: None,
            webhook_url: None,
            complete_authorize_url: None,
            browser_info: data.request.browser_info.clone(),
            order_details: None,
            order_category: None,
            session_token: None,
            enrolled_for_3ds: true,
            related_transaction_id: None,
            payment_experience: None,
            payment_method_type: None,
            customer_id: None,
            surcharge_details: None,
            request_incremental_authorization: data.request.request_incremental_authorization,
            metadata: None,
            request_extended_authorization: None,
            authentication_data: None,
            customer_acceptance: data.request.customer_acceptance.clone(),
            split_payments: None, // TODO: allow charges on mandates?
            merchant_order_reference_id: None,
            integrity_object: None,
            additional_payment_method_data: None,
            shipping_cost: data.request.shipping_cost,
            merchant_account_id: None,
            merchant_config_currency: None,
            connector_testing_data: data.request.connector_testing_data.clone(),
            order_id: None,
            locale: None,
            payment_channel: None,
            enable_partial_authorization: data.request.enable_partial_authorization,
        }
    }
}

impl<F1, F2, T1, T2> ForeignFrom<(&RouterData<F1, T1, PaymentsResponseData>, T2)>
    for RouterData<F2, T2, PaymentsResponseData>
{
    fn foreign_from(item: (&RouterData<F1, T1, PaymentsResponseData>, T2)) -> Self {
        let data = item.0;
        let request = item.1;
        Self {
            flow: PhantomData,
            request,
            merchant_id: data.merchant_id.clone(),
            connector: data.connector.clone(),
            attempt_id: data.attempt_id.clone(),
            tenant_id: data.tenant_id.clone(),
            status: data.status,
            payment_method: data.payment_method,
            connector_auth_type: data.connector_auth_type.clone(),
            description: data.description.clone(),
            address: data.address.clone(),
            auth_type: data.auth_type,
            connector_meta_data: data.connector_meta_data.clone(),
            connector_wallets_details: data.connector_wallets_details.clone(),
            amount_captured: data.amount_captured,
            minor_amount_captured: data.minor_amount_captured,
            access_token: data.access_token.clone(),
            response: data.response.clone(),
            payment_id: data.payment_id.clone(),
            session_token: data.session_token.clone(),
            reference_id: data.reference_id.clone(),
            customer_id: data.customer_id.clone(),
            payment_method_token: None,
            preprocessing_id: None,
            connector_customer: data.connector_customer.clone(),
            recurring_mandate_payment_data: data.recurring_mandate_payment_data.clone(),
            connector_request_reference_id: data.connector_request_reference_id.clone(),
            #[cfg(feature = "payouts")]
            payout_method_data: data.payout_method_data.clone(),
            #[cfg(feature = "payouts")]
            quote_id: data.quote_id.clone(),
            test_mode: data.test_mode,
            payment_method_status: None,
            payment_method_balance: data.payment_method_balance.clone(),
            connector_api_version: data.connector_api_version.clone(),
            connector_http_status_code: data.connector_http_status_code,
            external_latency: data.external_latency,
            apple_pay_flow: data.apple_pay_flow.clone(),
            frm_metadata: data.frm_metadata.clone(),
            dispute_id: data.dispute_id.clone(),
            refund_id: data.refund_id.clone(),
            connector_response: data.connector_response.clone(),
            integrity_check: Ok(()),
            additional_merchant_data: data.additional_merchant_data.clone(),
            header_payload: data.header_payload.clone(),
            connector_mandate_request_reference_id: data
                .connector_mandate_request_reference_id
                .clone(),
            authentication_id: data.authentication_id.clone(),
            psd2_sca_exemption_type: data.psd2_sca_exemption_type,
            raw_connector_response: data.raw_connector_response.clone(),
            is_payment_id_from_merchant: data.is_payment_id_from_merchant,
            l2_l3_data: data.l2_l3_data.clone(),
            minor_amount_capturable: data.minor_amount_capturable,
        }
    }
}

#[cfg(feature = "payouts")]
impl<F1, F2>
    ForeignFrom<(
        &RouterData<F1, PayoutsData, PayoutsResponseData>,
        PayoutsData,
    )> for RouterData<F2, PayoutsData, PayoutsResponseData>
{
    fn foreign_from(
        item: (
            &RouterData<F1, PayoutsData, PayoutsResponseData>,
            PayoutsData,
        ),
    ) -> Self {
        let data = item.0;
        let request = item.1;
        Self {
            flow: PhantomData,
            request,
            merchant_id: data.merchant_id.clone(),
            connector: data.connector.clone(),
            attempt_id: data.attempt_id.clone(),
            tenant_id: data.tenant_id.clone(),
            status: data.status,
            payment_method: data.payment_method,
            connector_auth_type: data.connector_auth_type.clone(),
            description: data.description.clone(),
            address: data.address.clone(),
            auth_type: data.auth_type,
            connector_meta_data: data.connector_meta_data.clone(),
            connector_wallets_details: data.connector_wallets_details.clone(),
            amount_captured: data.amount_captured,
            minor_amount_captured: data.minor_amount_captured,
            access_token: data.access_token.clone(),
            response: data.response.clone(),
            payment_id: data.payment_id.clone(),
            session_token: data.session_token.clone(),
            reference_id: data.reference_id.clone(),
            customer_id: data.customer_id.clone(),
            payment_method_token: None,
            recurring_mandate_payment_data: None,
            preprocessing_id: None,
            connector_customer: data.connector_customer.clone(),
            connector_request_reference_id:
                IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_PAYOUTS_FLOW.to_string(),
            payout_method_data: data.payout_method_data.clone(),
            quote_id: data.quote_id.clone(),
            test_mode: data.test_mode,
            payment_method_balance: None,
            payment_method_status: None,
            connector_api_version: None,
            connector_http_status_code: data.connector_http_status_code,
            external_latency: data.external_latency,
            apple_pay_flow: None,
            frm_metadata: None,
            refund_id: None,
            dispute_id: None,
            connector_response: data.connector_response.clone(),
            integrity_check: Ok(()),
            header_payload: data.header_payload.clone(),
            authentication_id: None,
            psd2_sca_exemption_type: None,
            additional_merchant_data: data.additional_merchant_data.clone(),
            connector_mandate_request_reference_id: None,
            raw_connector_response: None,
            is_payment_id_from_merchant: data.is_payment_id_from_merchant,
            l2_l3_data: None,
            minor_amount_capturable: None,
        }
    }
}

#[cfg(feature = "v2")]
impl ForeignFrom<&domain::MerchantConnectorAccountFeatureMetadata>
    for api_models::admin::MerchantConnectorAccountFeatureMetadata
{
    fn foreign_from(item: &domain::MerchantConnectorAccountFeatureMetadata) -> Self {
        let revenue_recovery = item
            .revenue_recovery
            .as_ref()
            .map(
                |revenue_recovery_metadata| api_models::admin::RevenueRecoveryMetadata {
                    max_retry_count: revenue_recovery_metadata.max_retry_count,
                    billing_connector_retry_threshold: revenue_recovery_metadata
                        .billing_connector_retry_threshold,
                    billing_account_reference: revenue_recovery_metadata
                        .mca_reference
                        .recovery_to_billing
                        .clone(),
                },
            );
        Self { revenue_recovery }
    }
}

#[cfg(feature = "v2")]
impl ForeignTryFrom<&api_models::admin::MerchantConnectorAccountFeatureMetadata>
    for domain::MerchantConnectorAccountFeatureMetadata
{
    type Error = errors::ApiErrorResponse;
    fn foreign_try_from(
        feature_metadata: &api_models::admin::MerchantConnectorAccountFeatureMetadata,
    ) -> Result<Self, Self::Error> {
        let revenue_recovery = feature_metadata
            .revenue_recovery
            .as_ref()
            .map(|revenue_recovery_metadata| {
                domain::AccountReferenceMap::new(
                    revenue_recovery_metadata.billing_account_reference.clone(),
                )
                .map(|mca_reference| domain::RevenueRecoveryMetadata {
                    max_retry_count: revenue_recovery_metadata.max_retry_count,
                    billing_connector_retry_threshold: revenue_recovery_metadata
                        .billing_connector_retry_threshold,
                    mca_reference,
                })
            })
            .transpose()?;

        Ok(Self { revenue_recovery })
    }
}


pub fn to_card_details_from_locker(self) -> payment_methods::CardDetailFromLocker {
        payment_methods::CardDetailFromLocker {
            card_number: None,
            card_holder_name: self.card_holder_name.clone(),
            card_issuer: self.card_issuer.clone(),
            card_network: self.card_network.clone(),
            card_type: self.card_type.clone(),
            issuer_country: self.clone().get_issuer_country_alpha2(),
            last4_digits: self.last4_digits,
            expiry_month: self.expiry_month,
            expiry_year: self.expiry_year,
            card_fingerprint: None,
            nick_name: self.nick_name,
            card_isin: self.card_isin,
            saved_to_locker: self.saved_to_locker,
        }
    }


pub async fn retrieve_merchant_account(
    state: web::Data<AppState>,
    req: HttpRequest,
    mid: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse {
    let flow = Flow::MerchantsAccountRetrieve;
    let merchant_id = mid.into_inner();
    let payload = admin::MerchantId {
        merchant_id: merchant_id.clone(),
    };
    api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| get_merchant_account(state, req, None),
        auth::auth_type(
            &auth::V2AdminApiAuth,
            &auth::JWTAuthMerchantFromRoute {
                merchant_id,
                // This should ideally be MerchantAccountRead, but since FE is calling this API for
                // profile level users currently keeping this as ProfileAccountRead. FE is removing
                // this API call for profile level users.
                // TODO: Convert this to MerchantAccountRead once FE changes are done.
                required_permission: Permission::ProfileAccountRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    )
    .await
}


impl api::PaymentCapture for Multisafepay {}

impl api::PaymentCapture for for Multisafepay


File: crates/router/src/configs.rs

use hyperswitch_interfaces::secrets_interface::secret_state::RawSecret;

pub(crate) mod defaults;
pub mod secrets_transformers;
pub mod settings;
mod validations;

pub type Settings = settings::Settings<RawSecret>;


pub async fn get_role_info_from_db(&self, state: &SessionState) -> UserResult<RoleInfo> {
        RoleInfo::from_role_id_org_id_tenant_id(
            state,
            &self.role_id,
            &self.org_id,
            self.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id),
        )
        .await
        .change_context(UserErrors::InternalServerError)
    }


impl api::PaymentVoid for Tokenio {}


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

pub mod transformers;
pub mod utils;

use api_models::blocklist as api_blocklist;

use crate::{
    core::errors::{self, RouterResponse},
    routes::SessionState,
    services,
    types::domain,
};

pub async fn add_entry_to_blocklist(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    body: api_blocklist::AddToBlocklistRequest,
) -> RouterResponse<api_blocklist::AddToBlocklistResponse> {
    utils::insert_entry_into_blocklist(
        &state,
        merchant_context.get_merchant_account().get_id(),
        body,
    )
    .await
    .map(services::ApplicationResponse::Json)
}

pub async fn remove_entry_from_blocklist(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    body: api_blocklist::DeleteFromBlocklistRequest,
) -> RouterResponse<api_blocklist::DeleteFromBlocklistResponse> {
    utils::delete_entry_from_blocklist(
        &state,
        merchant_context.get_merchant_account().get_id(),
        body,
    )
    .await
    .map(services::ApplicationResponse::Json)
}

pub async fn list_blocklist_entries(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    query: api_blocklist::ListBlocklistQuery,
) -> RouterResponse<Vec<api_blocklist::BlocklistResponse>> {
    utils::list_blocklist_entries_for_merchant(
        &state,
        merchant_context.get_merchant_account().get_id(),
        query,
    )
    .await
    .map(services::ApplicationResponse::Json)
}

pub async fn toggle_blocklist_guard(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    query: api_blocklist::ToggleBlocklistQuery,
) -> RouterResponse<api_blocklist::ToggleBlocklistResponse> {
    utils::toggle_blocklist_guard_for_merchant(
        &state,
        merchant_context.get_merchant_account().get_id(),
        query,
    )
    .await
    .map(services::ApplicationResponse::Json)
}


File: crates/router/src/workflows/outgoing_webhook_retry.rs
 Public structs: 1

#[cfg(feature = "payouts")]
use api_models::payouts as payout_models;
use api_models::{
    enums::EventType,
    webhook_events::OutgoingWebhookRequestContent,
    webhooks::{OutgoingWebhook, OutgoingWebhookContent},
};
use common_utils::{
    consts::DEFAULT_LOCALE,
    ext_traits::{StringExt, ValueExt},
    id_type,
};
use diesel_models::process_tracker::business_status;
use error_stack::ResultExt;
use masking::PeekInterface;
use router_env::tracing::{self, instrument};
use scheduler::{
    consumer::{self, workflows::ProcessTrackerWorkflow},
    types::process_data,
    utils as scheduler_utils,
};

#[cfg(feature = "payouts")]
use crate::core::payouts;
use crate::{
    core::{
        payments,
        webhooks::{self as webhooks_core, types::OutgoingWebhookTrackingData},
    },
    db::StorageInterface,
    errors, logger,
    routes::{app::ReqState, SessionState},
    types::{domain, storage},
};

pub struct OutgoingWebhookRetryWorkflow;

#[async_trait::async_trait]
impl ProcessTrackerWorkflow<SessionState> for OutgoingWebhookRetryWorkflow {
    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn execute_workflow<'a>(
        &'a self,
        state: &'a SessionState,
        process: storage::ProcessTracker,
    ) -> Result<(), errors::ProcessTrackerError> {
        let delivery_attempt = storage::enums::WebhookDeliveryAttempt::AutomaticRetry;
        let tracking_data: OutgoingWebhookTrackingData = process
            .tracking_data
            .clone()
            .parse_value("OutgoingWebhookTrackingData")?;

        let db = &*state.store;
        let key_manager_state = &state.into();
        let key_store = db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &tracking_data.merchant_id,
                &db.get_master_key().to_vec().into(),
            )
            .await?;
        let business_profile = db
            .find_business_profile_by_profile_id(
                key_manager_state,
                &key_store,
                &tracking_data.business_profile_id,
            )
            .await?;

        let event_id = webhooks_core::utils::generate_event_id();
        let idempotent_event_id = webhooks_core::utils::get_idempotent_event_id(
            &tracking_data.primary_object_id,
            tracking_data.event_type,
            delivery_attempt,
        );

        let initial_event = match &tracking_data.initial_attempt_id {
            Some(initial_attempt_id) => {
                db.find_event_by_merchant_id_event_id(
                    key_manager_state,
                    &business_profile.merchant_id,
                    initial_attempt_id,
                    &key_store,
                )
                .await?
            }
            // Tracking data inserted by old version of application, fetch event using old event ID
            // format
            None => {
                let old_event_id = format!(
                    "{}_{}",
                    tracking_data.primary_object_id, tracking_data.event_type
                );
                db.find_event_by_merchant_id_event_id(
                    key_manager_state,
                    &business_profile.merchant_id,
                    &old_event_id,
                    &key_store,
                )
                .await?
            }
        };

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

        let event = db
            .insert_event(key_manager_state, new_event, &key_store)
            .await
            .inspect_err(|error| {
                logger::error!(?error, "Failed to insert event in events table");
            })?;

        match &event.request {
            Some(request) => {
                let request_content: OutgoingWebhookRequestContent = request
                    .get_inner()
                    .peek()
                    .parse_struct("OutgoingWebhookRequestContent")?;

                Box::pin(webhooks_core::trigger_webhook_and_raise_event(
                    state.clone(),
                    business_profile,
                    &key_store,
                    event,
                    request_content,
                    delivery_attempt,
                    None,
                    Some(process),
                ))
                .await;
            }

            // Event inserted by old version of application, fetch current information about
            // resource
            None => {
                let merchant_account = db
                    .find_merchant_account_by_merchant_id(
                        key_manager_state,
                        &tracking_data.merchant_id,
                        &key_store,
                    )
                    .await?;

                let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                    domain::Context(merchant_account.clone(), key_store.clone()),
                ));
                // TODO: Add request state for the PT flows as well
                let (content, event_type) = Box::pin(get_outgoing_webhook_content_and_event_type(
                    state.clone(),
                    state.get_req_state(),
                    merchant_account.clone(),
                    key_store.clone(),
                    &tracking_data,
                ))
                .await?;

                match event_type {
                    // Resource status is same as the event type of the current event
                    Some(event_type) if event_type == tracking_data.event_type => {
                        let outgoing_webhook = OutgoingWebhook {
                            merchant_id: tracking_data.merchant_id.clone(),
                            event_id: event.event_id.clone(),
                            event_type,
                            content: content.clone(),
                            timestamp: event.created_at,
                        };

                        let request_content = webhooks_core::get_outgoing_webhook_request(
                            &merchant_context,
                            outgoing_webhook,
                            &business_profile,
                        )
                        .map_err(|error| {
                            logger::error!(
                                ?error,
                                "Failed to obtain outgoing webhook request content"
                            );
                            errors::ProcessTrackerError::EApiErrorResponse
                        })?;

                        Box::pin(webhooks_core::trigger_webhook_and_raise_event(
                            state.clone(),
                            business_profile,
                            &key_store,
                            event,
                            request_content,
                            delivery_attempt,
                            Some(content),
                            Some(process),
                        ))
                        .await;
                    }
                    // Resource status has changed since the event was created, finish task
                    _ => {
                        logger::warn!(
                            %event.event_id,
                            "The current status of the resource `{:?}` (event type: {:?}) and the status of \
                            the resource when the event was created (event type: {:?}) differ, finishing task",
                            tracking_data.primary_object_id,
                            event_type,
                            tracking_data.event_type
                        );
                        db.as_scheduler()
                            .finish_process_with_business_status(
                                process.clone(),
                                business_status::RESOURCE_STATUS_MISMATCH,
                            )
                            .await?;
                    }
                }
            }
        };

        Ok(())
    }
    #[cfg(feature = "v2")]
    async fn execute_workflow<'a>(
        &'a self,
        _state: &'a SessionState,
        _process: storage::ProcessTracker,
    ) -> Result<(), errors::ProcessTrackerError> {
        todo!()
    }

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

/// Get the schedule time for the specified retry count.
///
/// The schedule time can be configured in configs with this key: `pt_mapping_outgoing_webhooks`.
///
/// ```json
/// {
///   "default_mapping": {
///     "start_after": 60,
///     "frequency": [300],
///     "count": [5]
///   },
///   "custom_merchant_mapping": {
///     "merchant_id1": {
///       "start_after": 30,
///       "frequency": [300],
///       "count": [2]
///     }
///   }
/// }
/// ```
///
/// This configuration value represents:
/// - `default_mapping.start_after`: The first retry attempt should happen after 60 seconds by
///   default.
/// - `default_mapping.frequency` and `count`: The next 5 retries should have an interval of 300
///   seconds between them by default.
/// - `custom_merchant_mapping.merchant_id1`: Merchant-specific retry configuration for merchant
///   with merchant ID `merchant_id1`.
#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub(crate) async fn get_webhook_delivery_retry_schedule_time(
    db: &dyn StorageInterface,
    merchant_id: &id_type::MerchantId,
    retry_count: i32,
) -> Option<time::PrimitiveDateTime> {
    let key = "pt_mapping_outgoing_webhooks";

    let result = db
        .find_config_by_key(key)
        .await
        .map(|value| value.config)
        .and_then(|config| {
            config
                .parse_struct("OutgoingWebhookRetryProcessTrackerMapping")
                .change_context(errors::StorageError::DeserializationFailed)
        });
    let mapping = result.map_or_else(
        |error| {
            if error.current_context().is_db_not_found() {
                logger::debug!("Outgoing webhooks retry config `{key}` not found, ignoring");
            } else {
                logger::error!(
                    ?error,
                    "Failed to read outgoing webhooks retry config `{key}`"
                );
            }
            process_data::OutgoingWebhookRetryProcessTrackerMapping::default()
        },
        |mapping| {
            logger::debug!(?mapping, "Using custom outgoing webhooks retry config");
            mapping
        },
    );

    let time_delta = scheduler_utils::get_outgoing_webhook_retry_schedule_time(
        mapping,
        merchant_id,
        retry_count,
    );

    scheduler_utils::get_time_from_delta(time_delta)
}

/// Schedule the webhook delivery task for retry
#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub(crate) async fn retry_webhook_delivery_task(
    db: &dyn StorageInterface,
    merchant_id: &id_type::MerchantId,
    process: storage::ProcessTracker,
) -> errors::CustomResult<(), errors::StorageError> {
    let schedule_time =
        get_webhook_delivery_retry_schedule_time(db, merchant_id, process.retry_count + 1).await;

    match schedule_time {
        Some(schedule_time) => {
            db.as_scheduler()
                .retry_process(process, schedule_time)
                .await
        }
        None => {
            db.as_scheduler()
                .finish_process_with_business_status(process, business_status::RETRIES_EXCEEDED)
                .await
        }
    }
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
async fn get_outgoing_webhook_content_and_event_type(
    state: SessionState,
    req_state: ReqState,
    merchant_account: domain::MerchantAccount,
    key_store: domain::MerchantKeyStore,
    tracking_data: &OutgoingWebhookTrackingData,
) -> Result<(OutgoingWebhookContent, Option<EventType>), errors::ProcessTrackerError> {
    use api_models::{
        disputes::DisputeRetrieveRequest,
        mandates::MandateId,
        payments::{PaymentIdType, PaymentsResponse, PaymentsRetrieveRequest},
        refunds::{RefundResponse, RefundsRetrieveRequest},
    };

    use crate::{
        core::{
            disputes::retrieve_dispute,
            mandate::get_mandate,
            payments::{payments_core, CallConnectorAction, PaymentStatus},
            refunds::refund_retrieve_core_with_refund_id,
        },
        services::{ApplicationResponse, AuthFlow},
        types::{api::PSync, transformers::ForeignFrom},
    };

    let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
        merchant_account.clone(),
        key_store.clone(),
    )));
    match tracking_data.event_class {
        diesel_models::enums::EventClass::Payments => {
            let payment_id = tracking_data.primary_object_id.clone();
            let payment_id = id_type::PaymentId::try_from(std::borrow::Cow::Owned(payment_id))
                .map_err(|payment_id_parsing_error| {
                    logger::error!(
                        ?payment_id_parsing_error,
                        "Failed to parse payment ID from tracking data"
                    );
                    errors::ProcessTrackerError::DeserializationFailed
                })?;
            let request = PaymentsRetrieveRequest {
                resource_id: PaymentIdType::PaymentIntentId(payment_id),
                merchant_id: Some(tracking_data.merchant_id.clone()),
                force_sync: false,
                ..Default::default()
            };

            let payments_response = match Box::pin(payments_core::<
                PSync,
                PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<PSync>,
            >(
                state,
                req_state,
                merchant_context.clone(),
                None,
                PaymentStatus,
                request,
                AuthFlow::Client,
                CallConnectorAction::Avoid,
                None,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            ))
            .await?
            {
                ApplicationResponse::Json(payments_response)
                | ApplicationResponse::JsonWithHeaders((payments_response, _)) => {
                    Ok(payments_response)
                }
                ApplicationResponse::StatusOk
                | ApplicationResponse::TextPlain(_)
                | ApplicationResponse::JsonForRedirection(_)
                | ApplicationResponse::Form(_)
                | ApplicationResponse::GenericLinkForm(_)
                | ApplicationResponse::PaymentLinkForm(_)
                | ApplicationResponse::FileData(_) => {
                    Err(errors::ProcessTrackerError::ResourceFetchingFailed {
                        resource_name: tracking_data.primary_object_id.clone(),
                    })
                }
            }?;
            let event_type: Option<EventType> = payments_response.status.into();
            logger::debug!(current_resource_status=%payments_response.status);

            Ok((
                OutgoingWebhookContent::PaymentDetails(Box::new(payments_response)),
                event_type,
            ))
        }

        diesel_models::enums::EventClass::Refunds => {
            let refund_id = tracking_data.primary_object_id.clone();
            let request = RefundsRetrieveRequest {
                refund_id,
                force_sync: Some(false),
                merchant_connector_details: None,
            };

            let refund = Box::pin(refund_retrieve_core_with_refund_id(
                state,
                merchant_context.clone(),
                None,
                request,
            ))
            .await?;
            let event_type: Option<EventType> = refund.refund_status.into();
            logger::debug!(current_resource_status=%refund.refund_status);
            let refund_response = RefundResponse::foreign_from(refund);

            Ok((
                OutgoingWebhookContent::RefundDetails(Box::new(refund_response)),
                event_type,
            ))
        }

        diesel_models::enums::EventClass::Disputes => {
            let dispute_id = tracking_data.primary_object_id.clone();
            let request = DisputeRetrieveRequest {
                dispute_id,
                force_sync: None,
            };

            let dispute_response = match Box::pin(retrieve_dispute(
                state,
                merchant_context.clone(),
                None,
                request,
            ))
            .await?
            {
                ApplicationResponse::Json(dispute_response)
                | ApplicationResponse::JsonWithHeaders((dispute_response, _)) => {
                    Ok(dispute_response)
                }
                ApplicationResponse::StatusOk
                | ApplicationResponse::TextPlain(_)
                | ApplicationResponse::JsonForRedirection(_)
                | ApplicationResponse::Form(_)
                | ApplicationResponse::GenericLinkForm(_)
                | ApplicationResponse::PaymentLinkForm(_)
                | ApplicationResponse::FileData(_) => {
                    Err(errors::ProcessTrackerError::ResourceFetchingFailed {
                        resource_name: tracking_data.primary_object_id.clone(),
                    })
                }
            }
            .map(Box::new)?;
            let event_type = Some(EventType::from(dispute_response.dispute_status));
            logger::debug!(current_resource_status=%dispute_response.dispute_status);

            Ok((
                OutgoingWebhookContent::DisputeDetails(dispute_response),
                event_type,
            ))
        }

        diesel_models::enums::EventClass::Mandates => {
            let mandate_id = tracking_data.primary_object_id.clone();
            let request = MandateId { mandate_id };

            let mandate_response =
                match get_mandate(state, merchant_context.clone(), request).await? {
                    ApplicationResponse::Json(mandate_response)
                    | ApplicationResponse::JsonWithHeaders((mandate_response, _)) => {
                        Ok(mandate_response)
                    }
                    ApplicationResponse::StatusOk
                    | ApplicationResponse::TextPlain(_)
                    | ApplicationResponse::JsonForRedirection(_)
                    | ApplicationResponse::Form(_)
                    | ApplicationResponse::GenericLinkForm(_)
                    | ApplicationResponse::PaymentLinkForm(_)
                    | ApplicationResponse::FileData(_) => {
                        Err(errors::ProcessTrackerError::ResourceFetchingFailed {
                            resource_name: tracking_data.primary_object_id.clone(),
                        })
                    }
                }
                .map(Box::new)?;
            let event_type: Option<EventType> = mandate_response.status.into();
            logger::debug!(current_resource_status=%mandate_response.status);

            Ok((
                OutgoingWebhookContent::MandateDetails(mandate_response),
                event_type,
            ))
        }
        #[cfg(feature = "payouts")]
        diesel_models::enums::EventClass::Payouts => {
            let payout_id = tracking_data.primary_object_id.clone();
            let request = payout_models::PayoutRequest::PayoutActionRequest(
                payout_models::PayoutActionRequest {
                    payout_id: id_type::PayoutId::try_from(std::borrow::Cow::Owned(payout_id))?,
                },
            );

            let payout_data = Box::pin(payouts::make_payout_data(
                &state,
                &merchant_context,
                None,
                &request,
                DEFAULT_LOCALE,
            ))
            .await?;

            let payout_create_response =
                payouts::response_handler(&state, &merchant_context, &payout_data).await?;

            let event_type: Option<EventType> = payout_data.payout_attempt.status.into();
            logger::debug!(current_resource_status=%payout_data.payout_attempt.status);

            Ok((
                OutgoingWebhookContent::PayoutDetails(Box::new(payout_create_response)),
                event_type,
            ))
        }
    }
}


pub struct SoapHeader {}


pub struct JpmorganPSyncResponse {
    transaction_id: String,
    transaction_state: JpmorganTransactionState,
}


pub struct ActionLinks {
    supply_3ds_device_data: Option<ActionLink>,
    settle_payment: Option<ActionLink>,
    partially_settle_payment: Option<ActionLink>,
    refund_payment: Option<ActionLink>,
    partially_refund_payment: Option<ActionLink>,
    cancel_payment: Option<ActionLink>,
}


impl api::PaymentVoid for Datatrans {}


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

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

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

    fn get_auth_header(
        &self,
        _auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        Ok(vec![])
    }

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

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

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