impl webhooks::IncomingWebhook for Trustpay {
    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let details: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        let payment_attempt_id = details
            .payment_information
            .references
            .merchant_reference
            .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?;

        match details.payment_information.credit_debit_indicator {
            trustpay::CreditDebitIndicator::Crdt => {
                Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                    api_models::payments::PaymentIdType::PaymentAttemptId(payment_attempt_id),
                ))
            }
            trustpay::CreditDebitIndicator::Dbit => {
                if details.payment_information.status == trustpay::WebhookStatus::Chargebacked {
                    Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                        api_models::payments::PaymentIdType::PaymentAttemptId(payment_attempt_id),
                    ))
                } else {
                    Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                        api_models::webhooks::RefundIdType::RefundId(payment_attempt_id),
                    ))
                }
            }
        }
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let response: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        match (
            response.payment_information.credit_debit_indicator,
            response.payment_information.status,
        ) {
            (trustpay::CreditDebitIndicator::Crdt, trustpay::WebhookStatus::Paid) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess)
            }
            (trustpay::CreditDebitIndicator::Crdt, trustpay::WebhookStatus::Rejected) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentFailure)
            }
            (trustpay::CreditDebitIndicator::Dbit, trustpay::WebhookStatus::Paid) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::RefundSuccess)
            }
            (trustpay::CreditDebitIndicator::Dbit, trustpay::WebhookStatus::Refunded) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::RefundSuccess)
            }
            (trustpay::CreditDebitIndicator::Dbit, trustpay::WebhookStatus::Rejected) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::RefundFailure)
            }
            (trustpay::CreditDebitIndicator::Dbit, trustpay::WebhookStatus::Chargebacked) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::DisputeLost)
            }

            (
                trustpay::CreditDebitIndicator::Dbit | trustpay::CreditDebitIndicator::Crdt,
                trustpay::WebhookStatus::Unknown,
            ) => Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported),
            (trustpay::CreditDebitIndicator::Crdt, trustpay::WebhookStatus::Refunded) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported)
            }
            (trustpay::CreditDebitIndicator::Crdt, trustpay::WebhookStatus::Chargebacked) => {
                Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported)
            }
        }
    }

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let details: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        Ok(Box::new(details.payment_information))
    }

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

    fn get_webhook_source_verification_signature(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let response: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        hex::decode(response.signature)
            .change_context(errors::ConnectorError::WebhookSignatureNotFound)
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let trustpay_response: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        let response: serde_json::Value = request.body.parse_struct("Webhook Value").switch()?;
        let values = utils::collect_and_sort_values_by_removing_signature(
            &response,
            &trustpay_response.signature,
        );
        let payload = values.join("/");
        Ok(payload.into_bytes())
    }

    fn get_dispute_details(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<DisputePayload, errors::ConnectorError> {
        let trustpay_response: trustpay::TrustpayWebhookResponse = request
            .body
            .parse_struct("TrustpayWebhookResponse")
            .switch()?;
        let payment_info = trustpay_response.payment_information;
        let reason = payment_info.status_reason_information.unwrap_or_default();
        let connector_dispute_id = payment_info
            .references
            .payment_id
            .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        let amount = utils::convert_back_amount_to_minor_units(
            self.amount_converter_to_float_major_unit,
            payment_info.amount.amount,
            payment_info.amount.currency,
        )?;
        Ok(DisputePayload {
            amount: utils::convert_amount(
                self.amount_converter_to_string_minor_unit,
                amount,
                payment_info.amount.currency,
            )?,
            currency: payment_info.amount.currency,
            dispute_stage: api_models::enums::DisputeStage::Dispute,
            connector_dispute_id,
            connector_reason: reason.reason.reject_reason,
            connector_reason_code: reason.reason.code,
            challenge_required_by: None,
            connector_status: payment_info.status.to_string(),
            created_at: None,
            updated_at: None,
        })
    }
}

impl webhooks::IncomingWebhook for for Trustpay


Documentation: api-reference/v1/payment-methods/payment-method--set-default-payment-method-for-customer.mdx
# Type: Doc File

---
openapi: post /{customer_id}/payment_methods/{payment_method_id}/default
---


pub fn contains_key(&self, key: K) -> bool {
        key.get_id() < self.data.len()
    }


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

pub mod transformers;
use std::sync::LazyLock;

use common_enums::enums;
use common_utils::{
    crypto::{self, GenerateDigest},
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hex::encode;
use hyperswitch_domain_models::{
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    consts, errors,
    events::connector_api_logs::ConnectorEvent,
    types::{PaymentsAuthorizeType, PaymentsSyncType, RefundExecuteType, RefundSyncType, Response},
    webhooks,
};
use masking::ExposeInterface;
use rand::distributions::DistString;
use time::OffsetDateTime;
use transformers as globepay;

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

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

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

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

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

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Globepay
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 header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.get_content_type().to_string().into(),
        )];
        Ok(header)
    }
}

fn get_globlepay_query_params(
    connector_auth_type: &ConnectorAuthType,
) -> CustomResult<String, errors::ConnectorError> {
    let auth_type = globepay::GlobepayAuthType::try_from(connector_auth_type)?;
    let time = (OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000_000).to_string();
    let nonce_str = rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 12);
    let valid_string = format!(
        "{}&{time}&{nonce_str}&{}",
        auth_type.partner_code.expose(),
        auth_type.credential_code.expose()
    );
    let digest = crypto::Sha256
        .generate_digest(valid_string.as_bytes())
        .change_context(errors::ConnectorError::RequestEncodingFailed)
        .attach_printable("error encoding the query params")?;
    let sign = encode(digest).to_lowercase();
    let param = format!("?sign={sign}&time={time}&nonce_str={nonce_str}");
    Ok(param)
}

fn get_partner_code(
    connector_auth_type: &ConnectorAuthType,
) -> CustomResult<String, errors::ConnectorError> {
    let auth_type = globepay::GlobepayAuthType::try_from(connector_auth_type)?;
    Ok(auth_type.partner_code.expose())
}

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

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

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

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: globepay::GlobepayErrorResponse = res
            .response
            .parse_struct("GlobepayErrorResponse")
            .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.return_code.to_string(),
            message: consts::NO_ERROR_MESSAGE.to_string(),
            reason: Some(response.return_msg),
            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 Globepay {}

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

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

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

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Globepay {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        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: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let query_params = get_globlepay_query_params(&req.connector_auth_type)?;
        if matches!(
            req.request.capture_method,
            Some(enums::CaptureMethod::Automatic) | Some(enums::CaptureMethod::SequentialAutomatic)
        ) {
            Ok(format!(
                "{}api/v1.0/gateway/partners/{}/orders/{}{query_params}",
                self.base_url(connectors),
                get_partner_code(&req.connector_auth_type)?,
                req.payment_id
            ))
        } else {
            Err(errors::ConnectorError::FlowNotSupported {
                flow: "Manual Capture".to_owned(),
                connector: "Globepay".to_owned(),
            }
            .into())
        }
    }

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

        let connector_router_data = globepay::GlobepayRouterData::from((amount, req));
        let connector_req = globepay::GlobepayPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: globepay::GlobepayPaymentsResponse = res
            .response
            .parse_struct("Globepay PaymentsAuthorizeResponse")
            .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 Globepay {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        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: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let query_params = get_globlepay_query_params(&req.connector_auth_type)?;
        Ok(format!(
            "{}api/v1.0/gateway/partners/{}/orders/{}{query_params}",
            self.base_url(connectors),
            get_partner_code(&req.connector_auth_type)?,
            req.payment_id
        ))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: globepay::GlobepaySyncResponse = res
            .response
            .parse_struct("globepay PaymentsSyncResponse")
            .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<Capture, PaymentsCaptureData, PaymentsResponseData> for Globepay {
    fn build_request(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Manual Capture".to_owned(),
            connector: "Globepay".to_owned(),
        }
        .into())
    }
}

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

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Globepay {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        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: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let query_params = get_globlepay_query_params(&req.connector_auth_type)?;
        Ok(format!(
            "{}api/v1.0/gateway/partners/{}/orders/{}/refunds/{}{query_params}",
            self.base_url(connectors),
            get_partner_code(&req.connector_auth_type)?,
            req.payment_id,
            req.request.refund_id
        ))
    }

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

        let connector_router_data = globepay::GlobepayRouterData::from((refund_amount, req));
        let connector_req = globepay::GlobepayRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: globepay::GlobepayRefundResponse = res
            .response
            .parse_struct("Globalpay RefundResponse")
            .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<RSync, RefundsData, RefundsResponseData> for Globepay {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        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: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let query_params = get_globlepay_query_params(&req.connector_auth_type)?;
        Ok(format!(
            "{}api/v1.0/gateway/partners/{}/orders/{}/refunds/{}{query_params}",
            self.base_url(connectors),
            get_partner_code(&req.connector_auth_type)?,
            req.payment_id,
            req.request.refund_id
        ))
    }

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

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: globepay::GlobepayRefundResponse = res
            .response
            .parse_struct("Globalpay RefundResponse")
            .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)
    }
}

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Globepay {
    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 GLOBEPAY_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(|| {
        let supported_capture_methods = vec![
            enums::CaptureMethod::Automatic,
            enums::CaptureMethod::SequentialAutomatic,
        ];

        let mut globepay_supported_payment_methods = SupportedPaymentMethods::new();

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

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

        globepay_supported_payment_methods
    });

static GLOBEPAY_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "Globepay",
        description: "GlobePay Limited is a professional cross-border payment solution provider (WeChat Pay & Alipay) in the UK",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Sandbox,
    };

static GLOBEPAY_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 0] = [];

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

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

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


pub struct OpayoRefundRequest {
    pub amount: MinorUnit,
}


pub struct BlikBankRedirectAdditionalData {
    #[schema(value_type = Option<String>, example = "3GD9MO")]
    pub blik_code: Option<String>,
}


pub struct RefundListConstraints {
    pub payment_id: Option<common_utils::id_type::PaymentId>,
    pub refund_id: Option<String>,
    pub profile_id: Option<Vec<common_utils::id_type::ProfileId>>,
    pub limit: Option<i64>,
    pub offset: Option<i64>,
    pub time_range: Option<common_utils::types::TimeRange>,
    pub amount_filter: Option<api_models::payments::AmountFilter>,
    pub connector: Option<Vec<String>>,
    pub merchant_connector_id: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
    pub currency: Option<Vec<common_enums::Currency>>,
    pub refund_status: Option<Vec<common_enums::RefundStatus>>,
}


pub struct OnboardTransferMethodRequest {
    pub country_code: enums::CountryAlpha2,
    pub currency_code: Currency,
    #[serde(rename = "type")]
    pub transfer_method_type: TransferMethodType,
    pub display_name: Secret<String>,
    pub bank_account: BankAccount,
    pub profile: Profile,
}


pub struct NovalnetCard {
    card_number: CardNumber,
    card_expiry_month: Secret<String>,
    card_expiry_year: Secret<String>,
    card_cvc: Secret<String>,
    card_holder: Secret<String>,
}


pub struct TrustpaymentsCard {
    number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    cvc: Secret<String>,
    complete: bool,
}


impl ConnectorValidation for Novalnet {
    fn validate_psync_reference_id(
        &self,
        data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        if data.encoded_data.is_some()
            || data
                .connector_transaction_id
                .get_connector_transaction_id()
                .is_ok()
        {
            return Ok(());
        }

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

impl ConnectorValidation for for Novalnet


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

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

        hex::decode(security_header)
            .change_context(errors::ConnectorError::WebhookSignatureNotFound)
    }
    fn get_webhook_source_verification_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let timestamp = get_header_key_value("bls-ipn-timestamp", request.headers)?;
        Ok(format!("{}{}", timestamp, String::from_utf8_lossy(request.body)).into_bytes())
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let webhook_body: bluesnap::BluesnapWebhookBody =
            serde_urlencoded::from_bytes(request.body)
                .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        match webhook_body.transaction_type {
            bluesnap::BluesnapWebhookEvents::Decline
            | bluesnap::BluesnapWebhookEvents::CcChargeFailed
            | bluesnap::BluesnapWebhookEvents::Charge
            | bluesnap::BluesnapWebhookEvents::Chargeback
            | bluesnap::BluesnapWebhookEvents::ChargebackStatusChanged => {
                if webhook_body.merchant_transaction_id.is_empty() {
                    Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                        api_models::payments::PaymentIdType::ConnectorTransactionId(
                            webhook_body.reference_number,
                        ),
                    ))
                } else {
                    Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                        api_models::payments::PaymentIdType::PaymentAttemptId(
                            webhook_body.merchant_transaction_id,
                        ),
                    ))
                }
            }
            bluesnap::BluesnapWebhookEvents::Refund => {
                Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                    api_models::webhooks::RefundIdType::ConnectorRefundId(
                        webhook_body
                            .reversal_ref_num
                            .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
                    ),
                ))
            }
            bluesnap::BluesnapWebhookEvents::Unknown => {
                Err(report!(errors::ConnectorError::WebhookReferenceIdNotFound))
            }
        }
    }

    fn get_webhook_event_type(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        let details: bluesnap::BluesnapWebhookObjectEventType =
            serde_urlencoded::from_bytes(request.body)
                .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
        IncomingWebhookEvent::try_from(details)
    }

    fn get_dispute_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<DisputePayload, errors::ConnectorError> {
        let dispute_details: bluesnap::BluesnapDisputeWebhookBody =
            serde_urlencoded::from_bytes(request.body)
                .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        let amount = convert_back_amount_to_minor_units(
            self.amount_converter_float_major_unit,
            dispute_details.invoice_charge_amount,
            dispute_details.currency,
        )?;
        Ok(DisputePayload {
            amount: convert_amount(
                self.amount_converter_to_string_minor_unit,
                amount,
                dispute_details.currency,
            )?,
            currency: dispute_details.currency,
            dispute_stage: api_models::enums::DisputeStage::Dispute,
            connector_dispute_id: dispute_details.reversal_ref_num,
            connector_reason: dispute_details.reversal_reason,
            connector_reason_code: None,
            challenge_required_by: None,
            connector_status: dispute_details.cb_status,
            created_at: None,
            updated_at: None,
        })
    }

    fn get_webhook_resource_object(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let resource: bluesnap::BluesnapWebhookObjectResource =
            serde_urlencoded::from_bytes(request.body)
                .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;

        Ok(Box::new(resource))
    }
}


pub fn get_credentials(
        &self,
    ) -> CustomResult<&PaypalConnectorCredentials, errors::ConnectorError> {
        match self {
            Self::TemporaryAuth => Err(errors::ConnectorError::InvalidConnectorConfig {
                config: "TemporaryAuth found in connector_account_details",
            }
            .into()),
            Self::AuthWithDetails(credentials) => Ok(credentials),
        }
    }


pub struct BokuPaymentsResponse {
    charge_status: String, // xml parse only string to fields
    charge_id: String,
    hosted: Option<HostedUrlResponse>,
}


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


File: crates/common_utils/src/keymanager.rs
 Public functions: 4

//! Consists of all the common functions to use the Keymanager.

use core::fmt::Debug;
use std::str::FromStr;

use base64::Engine;
use error_stack::ResultExt;
use http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
use masking::{PeekInterface, StrongSecret};
use once_cell::sync::OnceCell;
use router_env::{instrument, logger, tracing};

use crate::{
    consts::{BASE64_ENGINE, TENANT_HEADER},
    errors,
    types::keymanager::{
        BatchDecryptDataRequest, DataKeyCreateResponse, DecryptDataRequest,
        EncryptionCreateRequest, EncryptionTransferRequest, GetKeymanagerTenant, KeyManagerState,
        TransientBatchDecryptDataRequest, TransientDecryptDataRequest,
    },
};

const CONTENT_TYPE: &str = "Content-Type";
static ENCRYPTION_API_CLIENT: OnceCell<reqwest::Client> = OnceCell::new();
static DEFAULT_ENCRYPTION_VERSION: &str = "v1";
#[cfg(feature = "km_forward_x_request_id")]
const X_REQUEST_ID: &str = "X-Request-Id";

/// Get keymanager client constructed from the url and state
#[instrument(skip_all)]
#[allow(unused_mut)]
fn get_api_encryption_client(
    state: &KeyManagerState,
) -> errors::CustomResult<reqwest::Client, errors::KeyManagerClientError> {
    let get_client = || {
        let mut client = reqwest::Client::builder()
            .redirect(reqwest::redirect::Policy::none())
            .pool_idle_timeout(std::time::Duration::from_secs(
                state.client_idle_timeout.unwrap_or_default(),
            ));

        #[cfg(feature = "keymanager_mtls")]
        {
            let cert = state.cert.clone();
            let ca = state.ca.clone();

            let identity = reqwest::Identity::from_pem(cert.peek().as_ref())
                .change_context(errors::KeyManagerClientError::ClientConstructionFailed)?;
            let ca_cert = reqwest::Certificate::from_pem(ca.peek().as_ref())
                .change_context(errors::KeyManagerClientError::ClientConstructionFailed)?;

            client = client
                .use_rustls_tls()
                .identity(identity)
                .add_root_certificate(ca_cert)
                .https_only(true);
        }

        client
            .build()
            .change_context(errors::KeyManagerClientError::ClientConstructionFailed)
    };

    Ok(ENCRYPTION_API_CLIENT.get_or_try_init(get_client)?.clone())
}

/// Generic function to send the request to keymanager
#[instrument(skip_all)]
pub async fn send_encryption_request<T>(
    state: &KeyManagerState,
    headers: HeaderMap,
    url: String,
    method: Method,
    request_body: T,
) -> errors::CustomResult<reqwest::Response, errors::KeyManagerClientError>
where
    T: ConvertRaw,
{
    let client = get_api_encryption_client(state)?;
    let url = reqwest::Url::parse(&url)
        .change_context(errors::KeyManagerClientError::UrlEncodingFailed)?;

    client
        .request(method, url)
        .json(&ConvertRaw::convert_raw(request_body)?)
        .headers(headers)
        .send()
        .await
        .change_context(errors::KeyManagerClientError::RequestNotSent(
            "Unable to send request to encryption service".to_string(),
        ))
}

/// Generic function to call the Keymanager and parse the response back
#[instrument(skip_all)]
pub async fn call_encryption_service<T, R>(
    state: &KeyManagerState,
    method: Method,
    endpoint: &str,
    request_body: T,
) -> errors::CustomResult<R, errors::KeyManagerClientError>
where
    T: GetKeymanagerTenant + ConvertRaw + Send + Sync + 'static + Debug,
    R: serde::de::DeserializeOwned,
{
    let url = format!("{}/{endpoint}", &state.url);

    logger::info!(key_manager_request=?request_body);
    let mut header = vec![];
    header.push((
        HeaderName::from_str(CONTENT_TYPE)
            .change_context(errors::KeyManagerClientError::FailedtoConstructHeader)?,
        HeaderValue::from_str("application/json")
            .change_context(errors::KeyManagerClientError::FailedtoConstructHeader)?,
    ));
    #[cfg(feature = "km_forward_x_request_id")]
    if let Some(request_id) = state.request_id {
        header.push((
            HeaderName::from_str(X_REQUEST_ID)
                .change_context(errors::KeyManagerClientError::FailedtoConstructHeader)?,
            HeaderValue::from_str(request_id.as_hyphenated().to_string().as_str())
                .change_context(errors::KeyManagerClientError::FailedtoConstructHeader)?,
        ))
    }

    //Add Tenant ID
    header.push((
        HeaderName::from_str(TENANT_HEADER)
            .change_context(errors::KeyManagerClientError::FailedtoConstructHeader)?,
        HeaderValue::from_str(request_body.get_tenant_id(state).get_string_repr())
            .change_context(errors::KeyManagerClientError::FailedtoConstructHeader)?,
    ));

    let response = send_encryption_request(
        state,
        HeaderMap::from_iter(header.into_iter()),
        url,
        method,
        request_body,
    )
    .await
    .map_err(|err| err.change_context(errors::KeyManagerClientError::RequestSendFailed))?;

    logger::info!(key_manager_response=?response);

    match response.status() {
        StatusCode::OK => response
            .json::<R>()
            .await
            .change_context(errors::KeyManagerClientError::ResponseDecodingFailed),
        StatusCode::INTERNAL_SERVER_ERROR => {
            Err(errors::KeyManagerClientError::InternalServerError(
                response
                    .bytes()
                    .await
                    .change_context(errors::KeyManagerClientError::ResponseDecodingFailed)?,
            )
            .into())
        }
        StatusCode::BAD_REQUEST => Err(errors::KeyManagerClientError::BadRequest(
            response
                .bytes()
                .await
                .change_context(errors::KeyManagerClientError::ResponseDecodingFailed)?,
        )
        .into()),
        _ => Err(errors::KeyManagerClientError::Unexpected(
            response
                .bytes()
                .await
                .change_context(errors::KeyManagerClientError::ResponseDecodingFailed)?,
        )
        .into()),
    }
}

/// Trait to convert the raw data to the required format for encryption service request
pub trait ConvertRaw {
    /// Return type of the convert_raw function
    type Output: serde::Serialize;
    /// Function to convert the raw data to the required format for encryption service request
    fn convert_raw(self) -> Result<Self::Output, errors::KeyManagerClientError>;
}

impl<T: serde::Serialize> ConvertRaw for T {
    type Output = T;
    fn convert_raw(self) -> Result<Self::Output, errors::KeyManagerClientError> {
        Ok(self)
    }
}

impl ConvertRaw for TransientDecryptDataRequest {
    type Output = DecryptDataRequest;
    fn convert_raw(self) -> Result<Self::Output, errors::KeyManagerClientError> {
        let data = match String::from_utf8(self.data.peek().clone()) {
            Ok(data) => data,
            Err(_) => {
                let data = BASE64_ENGINE.encode(self.data.peek().clone());
                format!("{DEFAULT_ENCRYPTION_VERSION}:{data}")
            }
        };
        Ok(DecryptDataRequest {
            identifier: self.identifier,
            data: StrongSecret::new(data),
        })
    }
}

impl ConvertRaw for TransientBatchDecryptDataRequest {
    type Output = BatchDecryptDataRequest;
    fn convert_raw(self) -> Result<Self::Output, errors::KeyManagerClientError> {
        let data = self
            .data
            .iter()
            .map(|(k, v)| {
                let value = match String::from_utf8(v.peek().clone()) {
                    Ok(data) => data,
                    Err(_) => {
                        let data = BASE64_ENGINE.encode(v.peek().clone());
                        format!("{DEFAULT_ENCRYPTION_VERSION}:{data}")
                    }
                };
                (k.to_owned(), StrongSecret::new(value))
            })
            .collect();
        Ok(BatchDecryptDataRequest {
            data,
            identifier: self.identifier,
        })
    }
}

/// A function to create the key in keymanager
#[instrument(skip_all)]
pub async fn create_key_in_key_manager(
    state: &KeyManagerState,
    request_body: EncryptionCreateRequest,
) -> errors::CustomResult<DataKeyCreateResponse, errors::KeyManagerError> {
    call_encryption_service(state, Method::POST, "key/create", request_body)
        .await
        .change_context(errors::KeyManagerError::KeyAddFailed)
}

/// A function to transfer the key in keymanager
#[instrument(skip_all)]
pub async fn transfer_key_to_key_manager(
    state: &KeyManagerState,
    request_body: EncryptionTransferRequest,
) -> errors::CustomResult<DataKeyCreateResponse, errors::KeyManagerError> {
    call_encryption_service(state, Method::POST, "key/transfer", request_body)
        .await
        .change_context(errors::KeyManagerError::KeyTransferFailed)
}


pub fn is_three_decimal_currency(self) -> bool {
        match self {
            Self::BHD | Self::IQD | Self::JOD | Self::KWD | Self::LYD | Self::OMR | Self::TND => {
                true
            }
            Self::AED
            | Self::AFN
            | Self::ALL
            | Self::AMD
            | Self::AOA
            | Self::ANG
            | Self::ARS
            | Self::AUD
            | Self::AWG
            | Self::AZN
            | Self::BAM
            | Self::BBD
            | Self::BDT
            | Self::BGN
            | Self::BIF
            | Self::BMD
            | Self::BND
            | Self::BOB
            | Self::BRL
            | Self::BSD
            | Self::BTN
            | Self::BWP
            | Self::BYN
            | Self::BZD
            | Self::CAD
            | Self::CDF
            | Self::CHF
            | Self::CLF
            | Self::CLP
            | Self::CNY
            | Self::COP
            | Self::CRC
            | Self::CUC
            | Self::CUP
            | Self::CVE
            | Self::CZK
            | Self::DJF
            | Self::DKK
            | Self::DOP
            | Self::DZD
            | Self::EGP
            | Self::ERN
            | Self::ETB
            | Self::EUR
            | Self::FJD
            | Self::FKP
            | Self::GBP
            | Self::GEL
            | Self::GHS
            | Self::GIP
            | Self::GMD
            | Self::GNF
            | Self::GTQ
            | Self::GYD
            | Self::HKD
            | Self::HNL
            | Self::HRK
            | Self::HTG
            | Self::HUF
            | Self::IDR
            | Self::ILS
            | Self::INR
            | Self::IRR
            | Self::ISK
            | Self::JMD
            | Self::JPY
            | Self::KES
            | Self::KGS
            | Self::KHR
            | Self::KMF
            | Self::KPW
            | Self::KRW
            | Self::KYD
            | Self::KZT
            | Self::LAK
            | Self::LBP
            | Self::LKR
            | Self::LRD
            | Self::LSL
            | Self::MAD
            | Self::MDL
            | Self::MGA
            | Self::MKD
            | Self::MMK
            | Self::MNT
            | Self::MOP
            | Self::MRU
            | Self::MUR
            | Self::MVR
            | Self::MWK
            | Self::MXN
            | Self::MYR
            | Self::MZN
            | Self::NAD
            | Self::NGN
            | Self::NIO
            | Self::NOK
            | Self::NPR
            | Self::NZD
            | Self::PAB
            | Self::PEN
            | Self::PGK
            | Self::PHP
            | Self::PKR
            | Self::PLN
            | Self::PYG
            | Self::QAR
            | Self::RON
            | Self::RSD
            | Self::RUB
            | Self::RWF
            | Self::SAR
            | Self::SBD
            | Self::SCR
            | Self::SDG
            | Self::SEK
            | Self::SGD
            | Self::SHP
            | Self::SLE
            | Self::SLL
            | Self::SOS
            | Self::SRD
            | Self::SSP
            | Self::STD
            | Self::STN
            | Self::SVC
            | Self::SYP
            | Self::SZL
            | Self::THB
            | Self::TJS
            | Self::TMT
            | Self::TOP
            | Self::TRY
            | Self::TTD
            | Self::TWD
            | Self::TZS
            | Self::UAH
            | Self::UGX
            | Self::USD
            | Self::UYU
            | Self::UZS
            | Self::VES
            | Self::VND
            | Self::VUV
            | Self::WST
            | Self::XAF
            | Self::XCD
            | Self::XPF
            | Self::XOF
            | Self::YER
            | Self::ZAR
            | Self::ZMW
            | Self::ZWL => false,
        }
    }


pub struct MonerisAuthResponse {
    access_token: Secret<String>,
    token_type: String,
    expires_in: String,
}


impl ConnectorTypeAndConnectorName<'_> {
    fn get_routable_connector(&self) -> RouterResult<Option<api_enums::RoutableConnectors>> {
        let mut routable_connector =
            api_enums::RoutableConnectors::from_str(&self.connector_name.to_string()).ok();

        let vault_connector =
            api_enums::convert_vault_connector(self.connector_name.to_string().as_str());
        let pm_auth_connector =
            api_enums::convert_pm_auth_connector(self.connector_name.to_string().as_str());
        let authentication_connector =
            api_enums::convert_authentication_connector(self.connector_name.to_string().as_str());
        let tax_connector =
            api_enums::convert_tax_connector(self.connector_name.to_string().as_str());
        let billing_connector =
            api_enums::convert_billing_connector(self.connector_name.to_string().as_str());

        if pm_auth_connector.is_some() {
            if self.connector_type != &api_enums::ConnectorType::PaymentMethodAuth
                && self.connector_type != &api_enums::ConnectorType::PaymentProcessor
            {
                return Err(errors::ApiErrorResponse::InvalidRequestData {
                    message: "Invalid connector type given".to_string(),
                }
                .into());
            }
        } else if authentication_connector.is_some() {
            if self.connector_type != &api_enums::ConnectorType::AuthenticationProcessor {
                return Err(errors::ApiErrorResponse::InvalidRequestData {
                    message: "Invalid connector type given".to_string(),
                }
                .into());
            }
        } else if tax_connector.is_some() {
            if self.connector_type != &api_enums::ConnectorType::TaxProcessor {
                return Err(errors::ApiErrorResponse::InvalidRequestData {
                    message: "Invalid connector type given".to_string(),
                }
                .into());
            }
        } else if billing_connector.is_some() {
            if self.connector_type != &api_enums::ConnectorType::BillingProcessor {
                return Err(errors::ApiErrorResponse::InvalidRequestData {
                    message: "Invalid connector type given".to_string(),
                }
                .into());
            }
        } else if vault_connector.is_some() {
            if self.connector_type != &api_enums::ConnectorType::VaultProcessor {
                return Err(errors::ApiErrorResponse::InvalidRequestData {
                    message: "Invalid connector type given".to_string(),
                }
                .into());
            }
        } else {
            let routable_connector_option = self
                .connector_name
                .to_string()
                .parse::<api_enums::RoutableConnectors>()
                .change_context(errors::ApiErrorResponse::InvalidRequestData {
                    message: "Invalid connector name given".to_string(),
                })?;
            routable_connector = Some(routable_connector_option);
        };
        Ok(routable_connector)
    }
}


pub async fn payment_methods_session_confirm(
    state: SessionState,
    req_state: routes::app::ReqState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
    request: payment_methods::PaymentMethodSessionConfirmRequest,
) -> RouterResponse<payment_methods::PaymentMethodSessionResponse> {
    let db: &dyn StorageInterface = state.store.as_ref();
    let key_manager_state = &(&state).into();

    // Validate if the session still exists
    let payment_method_session = db
        .get_payment_methods_session(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &payment_method_session_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "payment methods session does not exist or has expired".to_string(),
        })
        .attach_printable("Failed to retrieve payment methods session from db")?;

    let payment_method_session_billing = payment_method_session
        .billing
        .clone()
        .map(|billing| billing.into_inner())
        .map(From::from);

    // Unify the billing address that we receive from the session and from the confirm request
    let unified_billing_address = request
        .payment_method_data
        .billing
        .clone()
        .map(|payment_method_billing| {
            payment_method_billing.unify_address(payment_method_session_billing.as_ref())
        })
        .or_else(|| payment_method_session_billing.clone());

    let customer_id = payment_method_session.customer_id.clone();

    let create_payment_method_request = get_payment_method_create_request(
        request
            .payment_method_data
            .payment_method_data
            .as_ref()
            .get_required_value("payment_method_data")?,
        request.payment_method_type,
        request.payment_method_subtype,
        customer_id.clone(),
        unified_billing_address.as_ref(),
        Some(&payment_method_session),
    )
    .attach_printable("Failed to create payment method request")?;

    let (payment_method_response, payment_method) = Box::pin(create_payment_method_core(
        &state,
        &req_state,
        create_payment_method_request.clone(),
        &merchant_context,
        &profile,
    ))
    .await?;

    let parent_payment_method_token = generate_id(consts::ID_LENGTH, "token");

    let token_data = get_pm_list_token_data(request.payment_method_type, &payment_method)?;

    let intent_fulfillment_time = common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME;

    // insert the token data into redis
    if let Some(token_data) = token_data {
        pm_routes::ParentPaymentMethodToken::create_key_for_token((
            &parent_payment_method_token,
            request.payment_method_type,
        ))
        .insert(intent_fulfillment_time, token_data, &state)
        .await?;
    };

    let update_payment_method_session = hyperswitch_domain_models::payment_methods::PaymentMethodsSessionUpdateEnum::UpdateAssociatedPaymentMethods {
        associated_payment_methods:  Some(vec![parent_payment_method_token.clone()])
    };

    vault::insert_cvc_using_payment_token(
        &state,
        &parent_payment_method_token,
        create_payment_method_request.payment_method_data.clone(),
        request.payment_method_type,
        intent_fulfillment_time,
        merchant_context.get_merchant_key_store().key.get_inner(),
    )
    .await?;

    let payment_method_session = db
        .update_payment_method_session(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &payment_method_session_id,
            update_payment_method_session,
            payment_method_session,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "payment methods session does not exist or has expired".to_string(),
        })
        .attach_printable("Failed to update payment methods session from db")?;

    let payments_response = match &payment_method_session.psp_tokenization {
        Some(common_types::payment_methods::PspTokenization {
            tokenization_type: common_enums::TokenizationType::MultiUse,
            ..
        }) => {
            let zero_auth_request = construct_zero_auth_payments_request(
                &request,
                &payment_method_session,
                &payment_method_response,
            )?;
            let payments_response = Box::pin(create_zero_auth_payment(
                state.clone(),
                req_state,
                merchant_context.clone(),
                profile.clone(),
                zero_auth_request,
            ))
            .await?;

            Some(payments_response)
        }
        Some(common_types::payment_methods::PspTokenization {
            tokenization_type: common_enums::TokenizationType::SingleUse,
            ..
        }) => {
            Box::pin(create_single_use_tokenization_flow(
                state.clone(),
                req_state.clone(),
                merchant_context.clone(),
                profile.clone(),
                &create_payment_method_request.clone(),
                &payment_method_response,
                &payment_method_session,
            ))
            .await?;
            None
        }
        None => None,
    };

    let tokenization_response = match payment_method_session.tokenization_data.clone() {
        Some(tokenization_data) => {
            let tokenization_response = tokenization_core::create_vault_token_core(
                state.clone(),
                &merchant_context.get_merchant_account().clone(),
                &merchant_context.get_merchant_key_store().clone(),
                api_models::tokenization::GenericTokenizationRequest {
                    customer_id: customer_id.clone(),
                    token_request: tokenization_data,
                },
            )
            .await?;
            let token = match tokenization_response {
                services::ApplicationResponse::Json(response) => Some(response),
                _ => None,
            };
            Some(token)
        }
        None => None,
    };

    logger::debug!(?tokenization_response, "Tokenization response");

    //TODO: update the payment method session with the payment id and payment method id
    let payment_method_session_response = transformers::generate_payment_method_session_response(
        payment_method_session,
        Secret::new("CLIENT_SECRET_REDACTED".to_string()),
        payments_response,
        (tokenization_response.flatten()),
    );

    Ok(services::ApplicationResponse::Json(
        payment_method_session_response,
    ))
}


pub struct PaypalSourceVerificationResponse {
    pub verification_status: PaypalSourceVerificationStatus,
}


impl Responder {
    let flow = Flow::ToggleDynamicRouting;
    let wrapper = routing_types::ToggleDynamicRoutingWrapper {
        feature_to_enable: query.into_inner().enable,
        profile_id: path.into_inner().profile_id,
    };
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        wrapper.clone(),
        |state,
         auth: auth::AuthenticationData,
         wrapper: routing_types::ToggleDynamicRoutingWrapper,
         _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            routing::toggle_specific_dynamic_routing(
                state,
                merchant_context,
                wrapper.feature_to_enable,
                wrapper.profile_id,
                api_models::routing::DynamicRoutingType::EliminationRouting,
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuthProfileFromRoute {
                profile_id: wrapper.profile_id,
                required_permission: Permission::ProfileRoutingWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


File: crates/analytics/src/payments/metrics/sessionized_metrics/payment_processed_amount.rs

use std::collections::HashSet;

use api_models::analytics::{
    payments::{PaymentDimensions, PaymentFilters, PaymentMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use diesel_models::enums as storage_enums;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

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

#[derive(Default)]
pub(crate) struct PaymentProcessedAmount;

#[async_trait::async_trait]
impl<T> super::PaymentMetric<T> for PaymentProcessedAmount
where
    T: AnalyticsDataSource + super::PaymentMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[PaymentDimensions],
        auth: &AuthInfo,
        filters: &PaymentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>> {
        let mut query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::PaymentSessionized);

        let mut dimensions = dimensions.to_vec();

        dimensions.push(PaymentDimensions::PaymentStatus);

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

        query_builder.add_select_column("first_attempt").switch()?;

        query_builder.add_select_column("currency").switch()?;

        query_builder
            .add_select_column(Aggregate::Sum {
                field: "amount",
                alias: Some("total"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

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

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

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

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

        query_builder
            .add_group_by_clause("currency")
            .attach_printable("Error grouping by currency")
            .switch()?;

        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .add_filter_clause(
                PaymentDimensions::PaymentStatus,
                storage_enums::AttemptStatus::Charged,
            )
            .switch()?;

        query_builder
            .execute_query::<PaymentMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    PaymentMetricsBucketIdentifier::new(
                        i.currency.as_ref().map(|i| i.0),
                        None,
                        i.connector.clone(),
                        i.authentication_type.as_ref().map(|i| i.0),
                        i.payment_method.clone(),
                        i.payment_method_type.clone(),
                        i.client_source.clone(),
                        i.client_version.clone(),
                        i.profile_id.clone(),
                        i.card_network.clone(),
                        i.merchant_id.clone(),
                        i.card_last_4.clone(),
                        i.card_issuer.clone(),
                        i.error_reason.clone(),
                        i.routing_approach.as_ref().map(|i| i.0.clone()),
                        i.signature_network.clone(),
                        i.is_issuer_regulated,
                        i.is_debit_routed,
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


pub async fn get_mandate(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: mandates::MandateId,
) -> RouterResponse<mandates::MandateResponse> {
    let mandate = state
        .store
        .as_ref()
        .find_mandate_by_merchant_id_mandate_id(
            merchant_context.get_merchant_account().get_id(),
            &req.mandate_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
    Ok(services::ApplicationResponse::Json(
        mandates::MandateResponse::from_db_mandate(
            &state,
            merchant_context.get_merchant_key_store().clone(),
            mandate,
            merchant_context.get_merchant_account(),
        )
        .await?,
    ))
}


pub struct AwsFileStorageConfig {
    /// The AWS region to send file uploads
    region: String,
    /// The AWS s3 bucket to send file uploads
    bucket_name: String,
}


impl TempLockerCardSupport {
    #[instrument(skip_all)]
    async fn create_payment_method_data_in_temp_locker(
        state: &routes::SessionState,
        payment_token: &str,
        card: api::CardDetailFromLocker,
        pm: &domain::PaymentMethod,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> errors::RouterResult<api::CardDetailFromLocker> {
        let card_number = card.card_number.clone().get_required_value("card_number")?;
        let card_exp_month = card
            .expiry_month
            .clone()
            .expose_option()
            .get_required_value("expiry_month")?;
        let card_exp_year = card
            .expiry_year
            .clone()
            .expose_option()
            .get_required_value("expiry_year")?;
        let card_holder_name = card
            .card_holder_name
            .clone()
            .expose_option()
            .unwrap_or_default();
        let value1 = payment_methods::mk_card_value1(
            card_number,
            card_exp_year,
            card_exp_month,
            Some(card_holder_name),
            None,
            None,
            None,
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error getting Value1 for locker")?;
        let value2 = payment_methods::mk_card_value2(
            None,
            None,
            None,
            Some(pm.customer_id.clone()),
            Some(pm.get_id().to_string()),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error getting Value2 for locker")?;

        let value1 = vault::VaultPaymentMethod::Card(value1);
        let value2 = vault::VaultPaymentMethod::Card(value2);

        let value1 = value1
            .encode_to_string_of_json()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Wrapped value1 construction failed when saving card to locker")?;

        let value2 = value2
            .encode_to_string_of_json()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Wrapped value2 construction failed when saving card to locker")?;

        let lookup_key = vault::create_tokenize(
            state,
            value1,
            Some(value2),
            payment_token.to_string(),
            merchant_key_store.key.get_inner(),
        )
        .await?;
        vault::add_delete_tokenized_data_task(
            &*state.store,
            &lookup_key,
            enums::PaymentMethod::Card,
        )
        .await?;
        metrics::TOKENIZED_DATA_COUNT.add(1, &[]);
        metrics::TASKS_ADDED_COUNT.add(
            1,
            router_env::metric_attributes!(("flow", "DeleteTokenizeData")),
        );
        Ok(card)
    }
}


pub struct CryptoData {
    pub pay_currency: Option<String>,
    pub network: Option<String>,
}


File: crates/hyperswitch_connectors/src/connectors/worldpay/requests.rs
 Public structs: 24

use masking::Secret;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorldpayPaymentsRequest {
    pub transaction_reference: String,
    pub merchant: Merchant,
    pub instruction: Instruction,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub customer: Option<Customer>,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Merchant {
    pub entity: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mcc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_facilitator: Option<PaymentFacilitator>,
}

#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Instruction {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub settlement: Option<AutoSettlement>,
    pub method: PaymentMethod,
    pub payment_instrument: PaymentInstrument,
    pub narrative: InstructionNarrative,
    pub value: PaymentValue,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub debt_repayment: Option<bool>,
    #[serde(rename = "threeDS", skip_serializing_if = "Option::is_none")]
    pub three_ds: Option<ThreeDSRequest>,
    /// For setting up mandates
    pub token_creation: Option<TokenCreation>,
    /// For specifying CIT vs MIT
    pub customer_agreement: Option<CustomerAgreement>,
}

#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct TokenCreation {
    #[serde(rename = "type")]
    pub token_type: TokenCreationType,
}

#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum TokenCreationType {
    Worldpay,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CustomerAgreement {
    #[serde(rename = "type")]
    pub agreement_type: CustomerAgreementType,
    pub stored_card_usage: Option<StoredCardUsageType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scheme_reference: Option<Secret<String>>,
}

#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum CustomerAgreementType {
    Subscription,
    Unscheduled,
}

#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum StoredCardUsageType {
    First,
    Subsequent,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PaymentInstrument {
    Card(CardPayment),
    CardToken(CardToken),
    RawCardForNTI(RawCardDetails),
    Googlepay(WalletPayment),
    Applepay(WalletPayment),
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardPayment {
    #[serde(flatten)]
    pub raw_card_details: RawCardDetails,
    pub cvc: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub card_holder_name: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub billing_address: Option<BillingAddress>,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RawCardDetails {
    #[serde(rename = "type")]
    pub payment_type: PaymentType,
    pub card_number: cards::CardNumber,
    pub expiry_date: ExpiryDate,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardToken {
    #[serde(rename = "type")]
    pub payment_type: PaymentType,
    pub href: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cvc: Option<Secret<String>>,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WalletPayment {
    #[serde(rename = "type")]
    pub payment_type: PaymentType,
    pub wallet_token: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub billing_address: Option<BillingAddress>,
}

#[derive(
    Clone, Copy, Debug, Eq, Default, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
)]
#[serde(rename_all = "lowercase")]
pub enum PaymentType {
    #[default]
    Plain,
    Token,
    Encrypted,
    Checkout,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct ExpiryDate {
    pub month: Secret<i8>,
    pub year: Secret<i32>,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BillingAddress {
    pub address1: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub address2: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub address3: Option<Secret<String>>,
    pub city: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub state: Option<Secret<String>>,
    pub postal_code: Secret<String>,
    pub country_code: common_enums::CountryAlpha2,
}

#[derive(
    Clone, Copy, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
)]
#[serde(rename_all = "camelCase")]
pub enum Channel {
    #[default]
    Ecom,
    Moto,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Customer {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub risk_profile: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub authentication: Option<CustomerAuthentication>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum CustomerAuthentication {
    ThreeDS(ThreeDS),
    Token(NetworkToken),
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreeDS {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub authentication_value: Option<Secret<String>>,
    pub version: ThreeDSVersion,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transaction_id: Option<String>,
    pub eci: String,
    #[serde(rename = "type")]
    pub auth_type: CustomerAuthType,
}

#[derive(
    Clone, Copy, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
)]
pub enum ThreeDSVersion {
    #[default]
    #[serde(rename = "1")]
    One,
    #[serde(rename = "2")]
    Two,
}

#[derive(
    Clone, Copy, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
)]
pub enum CustomerAuthType {
    #[serde(rename = "3DS")]
    #[default]
    Variant3Ds,
    #[serde(rename = "card/networkToken")]
    NetworkToken,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NetworkToken {
    #[serde(rename = "type")]
    pub auth_type: CustomerAuthType,
    pub authentication_value: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub eci: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AutoSettlement {
    pub auto: bool,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreeDSRequest {
    #[serde(rename = "type")]
    pub three_ds_type: String,
    pub mode: String,
    pub device_data: ThreeDSRequestDeviceData,
    pub challenge: ThreeDSRequestChallenge,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreeDSRequestDeviceData {
    pub accept_header: String,
    pub user_agent_header: String,
    pub browser_language: Option<String>,
    pub browser_screen_width: Option<u32>,
    pub browser_screen_height: Option<u32>,
    pub browser_color_depth: Option<String>,
    pub time_zone: Option<String>,
    pub browser_java_enabled: Option<bool>,
    pub browser_javascript_enabled: Option<bool>,
    pub channel: Option<ThreeDSRequestChannel>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ThreeDSRequestChannel {
    Browser,
    Native,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreeDSRequestChallenge {
    pub return_url: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub preference: Option<ThreeDsPreference>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ThreeDsPreference {
    ChallengeMandated,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PaymentMethod {
    #[default]
    Card,
    ApplePay,
    GooglePay,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InstructionNarrative {
    pub line1: String,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct PaymentValue {
    pub amount: i64,
    pub currency: api_models::enums::Currency,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentFacilitator {
    pub pf_id: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub iso_id: Option<Secret<String>>,
    pub sub_merchant: SubMerchant,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubMerchant {
    pub city: String,
    pub name: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub state: Option<String>,
    pub postal_code: Secret<String>,
    pub merchant_id: Secret<String>,
    pub country_code: String,
    pub street: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tax_id: Option<String>,
}

#[derive(Default, Debug, Serialize)]
pub struct WorldpayPartialRequest {
    pub value: PaymentValue,
    pub reference: String,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WorldpayCompleteAuthorizationRequest {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub collection_reference: Option<String>,
}

pub(super) const THREE_DS_MODE: &str = "always";
pub(super) const THREE_DS_TYPE: &str = "integrated";


pub fn get_key(&self) -> Option<dir::DirKey> {
        match self {
            Self::Assertion(val) => Some(val.get_key()),
            Self::Negation(vals) => vals.first().map(|v| (*v).get_key()),
        }
    }


OpenAPI Block Path: components.schemas.PayoutConnectors
{
  "type": "string",
  "enum": [
    "adyen",
    "adyenplatform",
    "cybersource",
    "ebanx",
    "nomupay",
    "payone",
    "paypal",
    "stripe",
    "wise"
  ]
}


pub struct ThreeDsAuthDetails {
    pub three_ds_server_trans_id: String,
    pub acs_trans_id: String,
    pub acs_reference_number: String,
    pub acs_operator_id: Option<String>,
    pub ds_reference_number: String,
    pub ds_trans_id: String,
    pub sdk_trans_id: Option<String>,
    pub trans_status: common_enums::TransactionStatus,
    pub acs_challenge_mandated: Option<ACSChallengeMandatedEnum>,
    pub message_type: String,
    pub message_version: String,
    pub acs_url: Option<url::Url>,
    pub challenge_request: Option<String>,
    pub acs_signed_content: Option<String>,
    pub authentication_value: Option<Secret<String>>,
    pub eci: Option<String>,
    pub challenge_code: Option<String>,
    pub challenge_cancel: Option<String>,
    pub challenge_code_reason: Option<String>,
    pub message_extension: Option<common_utils::pii::SecretSerdeValue>,
}


pub async fn routing_retrieve_config(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::RoutingId>,
) -> impl Responder {
    let algorithm_id = path.into_inner();
    let flow = Flow::RoutingRetrieveConfig;
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        algorithm_id,
        |state, auth: auth::AuthenticationData, algorithm_id, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            routing::retrieve_routing_algorithm_from_algorithm_id(
                state,
                merchant_context,
                auth.profile_id,
                algorithm_id,
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::ProfileRoutingRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub fn get_co_badged_card_data(&self) -> Option<payment_methods::CoBadgedCardData> {
        todo!()
    }