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

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

use base64::Engine;
use common_enums::{enums, CaptureMethod, PaymentMethod, PaymentMethodType};
use common_utils::{
    consts::BASE64_ENGINE,
    errors::{self as common_errors, CustomResult},
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
};
use error_stack::{report, ResultExt};
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, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData, TokenizationRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response, TokenizationType},
    webhooks,
};
use masking::{Mask, PeekInterface};
use reqwest::multipart::Form;
use serde::Serialize;
use serde_json::Value;
use transformers as hipay;

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

pub fn build_form_from_struct<T: Serialize>(data: T) -> Result<Form, common_errors::ParsingError> {
    let mut form = Form::new();
    let serialized = serde_json::to_value(&data).map_err(|e| {
        router_env::logger::error!("Error serializing data to JSON value: {:?}", e);
        common_errors::ParsingError::EncodeError("json-value")
    })?;
    let serialized_object = serialized.as_object().ok_or_else(|| {
        router_env::logger::error!("Error: Expected JSON object but got something else");
        common_errors::ParsingError::EncodeError("Expected object")
    })?;
    for (key, values) in serialized_object {
        let value = match values {
            Value::String(s) => s.clone(),
            Value::Number(n) => n.to_string(),
            Value::Bool(b) => b.to_string(),
            Value::Null => "".to_string(),
            Value::Array(_) | Value::Object(_) => {
                router_env::logger::error!(serialization_error =? "Form Construction Failed.");
                "".to_string()
            }
        };
        form = form.text(key.clone(), value.clone());
    }
    Ok(form)
}
#[derive(Clone)]
pub struct Hipay {
    amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}

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

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

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Hipay
{
    fn get_headers(
        &self,
        req: &TokenizationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        _req: &TokenizationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}v2/token/create",
            connectors.hipay.secondary_base_url.clone()
        ))
    }

    fn get_request_body(
        &self,
        req: &TokenizationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = transformers::HiPayTokenRequest::try_from(req)?;
        router_env::logger::info!(raw_connector_request=?connector_req);
        let connector_req = build_form_from_struct(connector_req)
            .change_context(errors::ConnectorError::ParsingFailed)?;
        Ok(RequestContent::FormData(connector_req))
    }

    fn build_request(
        &self,
        req: &TokenizationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&TokenizationType::get_url(self, req, connectors)?)
                .headers(TokenizationType::get_headers(self, req, connectors)?)
                .set_body(TokenizationType::get_request_body(self, req, connectors)?)
                .build(),
        ))
    }
    fn handle_response(
        &self,
        data: &TokenizationRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<TokenizationRouterData, errors::ConnectorError>
    where
        PaymentsResponseData: Clone,
    {
        let response: transformers::HipayTokenResponse = res
            .response
            .parse_struct("HipayTokenResponse")
            .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> {
        event_builder.map(|event| event.set_error(serde_json::json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
        Ok(ErrorResponse::get_not_implemented())
    }
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Hipay
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::ACCEPT.to_string(),
            "application/json".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 Hipay {
    fn id(&self) -> &'static str {
        "hipay"
    }

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

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

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

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: hipay::HipayErrorResponse =
            res.response
                .parse_struct("HipayErrorResponse")
                .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.code.to_string(),
            message: response.message,
            reason: response.description,
            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 Hipay {}

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

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

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

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

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}v1/order", connectors.hipay.base_url.clone()))
    }

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

        let connector_router_data = hipay::HipayRouterData::from((amount, req));
        let connector_req = hipay::HipayPaymentsRequest::try_from(&connector_router_data)?;
        router_env::logger::info!(raw_connector_request=?connector_req);
        let connector_req = build_form_from_struct(connector_req)
            .change_context(errors::ConnectorError::ParsingFailed)?;
        Ok(RequestContent::FormData(connector_req))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsAuthorizeType::get_url(
                    self, req, connectors,
                )?)
                .headers(types::PaymentsAuthorizeType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::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: hipay::HipayPaymentsResponse = res
            .response
            .parse_struct("Hipay 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 Hipay {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_payment_id = req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
        Ok(format!(
            "{}v3/transaction/{}",
            connectors.hipay.third_base_url.clone(),
            connector_payment_id
        ))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::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: hipay::HipaySyncResponse = res
            .response
            .parse_struct("hipay HipaySyncResponse")
            .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 Hipay {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}v1/maintenance/transaction/{}",
            connectors.hipay.base_url.clone(),
            req.request.connector_transaction_id
        ))
    }

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

        let connector_router_data = hipay::HipayRouterData::from((capture_amount, req));
        let connector_req = hipay::HipayMaintenanceRequest::try_from(&connector_router_data)?;
        router_env::logger::info!(raw_connector_request=?connector_req);
        let connector_req = build_form_from_struct(connector_req)
            .change_context(errors::ConnectorError::ParsingFailed)?;
        Ok(RequestContent::FormData(connector_req))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, errors::ConnectorError> {
        let response: hipay::HipayMaintenanceResponse<hipay::HipayPaymentStatus> = res
            .response
            .parse_struct("Hipay HipayMaintenanceResponse")
            .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<Void, PaymentsCancelData, PaymentsResponseData> for Hipay {
    fn get_headers(
        &self,
        req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }
    fn get_url(
        &self,
        req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}v1/maintenance/transaction/{}",
            self.base_url(connectors),
            req.request.connector_transaction_id
        ))
    }

    fn build_request(
        &self,
        req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let request = RequestBuilder::new()
            .method(Method::Post)
            .url(&types::PaymentsVoidType::get_url(self, req, connectors)?)
            .headers(types::PaymentsVoidType::get_headers(self, req, connectors)?)
            .set_body(types::PaymentsVoidType::get_request_body(
                self, req, connectors,
            )?)
            .build();
        Ok(Some(request))
    }
    fn get_request_body(
        &self,
        req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = hipay::HipayMaintenanceRequest::try_from(req)?;
        router_env::logger::info!(raw_connector_request=?connector_req);
        let connector_req = build_form_from_struct(connector_req)
            .change_context(errors::ConnectorError::ParsingFailed)?;
        Ok(RequestContent::FormData(connector_req))
    }

    fn handle_response(
        &self,
        data: &PaymentsCancelRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCancelRouterData, errors::ConnectorError> {
        let response: hipay::HipayMaintenanceResponse<hipay::HipayPaymentStatus> = res
            .response
            .parse_struct("Hipay HipayMaintenanceResponse")
            .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,
        })
    }
}

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

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

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

        let connector_router_data = hipay::HipayRouterData::from((refund_amount, req));
        let connector_req = hipay::HipayMaintenanceRequest::try_from(&connector_router_data)?;
        router_env::logger::info!(raw_connector_request=?connector_req);
        let connector_req = build_form_from_struct(connector_req)
            .change_context(errors::ConnectorError::ParsingFailed)?;
        Ok(RequestContent::FormData(connector_req))
    }

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

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: hipay::HipayMaintenanceResponse<hipay::RefundStatus> = res
            .response
            .parse_struct("hipay 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 Hipay {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_payment_id = req.request.connector_transaction_id.clone();
        Ok(format!(
            "{}v3/transaction/{}",
            connectors.hipay.third_base_url.clone(),
            connector_payment_id
        ))
    }

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

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: hipay::RefundResponse = res
            .response
            .parse_struct("hipay 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 Hipay {
    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 HIPAY_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> = LazyLock::new(|| {
    let supported_capture_methods = vec![
        CaptureMethod::Automatic,
        CaptureMethod::Manual,
        CaptureMethod::SequentialAutomatic,
    ];

    let supported_card_network = vec![
        common_enums::CardNetwork::Mastercard,
        common_enums::CardNetwork::Visa,
        common_enums::CardNetwork::Interac,
        common_enums::CardNetwork::AmericanExpress,
        common_enums::CardNetwork::JCB,
        common_enums::CardNetwork::DinersClub,
        common_enums::CardNetwork::Discover,
        common_enums::CardNetwork::CartesBancaires,
        common_enums::CardNetwork::UnionPay,
    ];

    let mut hipay_supported_payment_methods = SupportedPaymentMethods::new();

    hipay_supported_payment_methods.add(
        PaymentMethod::Card,
        PaymentMethodType::Credit,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: Some(
                api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                    api_models::feature_matrix::CardSpecificFeatures {
                        three_ds: common_enums::FeatureStatus::Supported,
                        no_three_ds: common_enums::FeatureStatus::Supported,
                        supported_card_networks: supported_card_network.clone(),
                    }
                }),
            ),
        },
    );

    hipay_supported_payment_methods.add(
        PaymentMethod::Card,
        PaymentMethodType::Debit,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: Some(
                api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                    api_models::feature_matrix::CardSpecificFeatures {
                        three_ds: common_enums::FeatureStatus::Supported,
                        no_three_ds: common_enums::FeatureStatus::Supported,
                        supported_card_networks: supported_card_network.clone(),
                    }
                }),
            ),
        },
    );

    hipay_supported_payment_methods
});

static HIPAY_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Hipay",
    description: "HiPay is an independent global payment service provider that is based in France.",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: enums::ConnectorIntegrationStatus::Sandbox,
};

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

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

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

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


// Implementation: impl api::PaymentToken for for Santander
// File: crates/hyperswitch_connectors/src/connectors/santander.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentToken for for Santander


// Struct: CustomerUpdateRequestInternal
// File: crates/api_models/src/customers.rs
// Module: api_models
// Implementations: 0

pub struct CustomerUpdateRequestInternal


// File: crates/diesel_models/src/query/merchant_connector_account.rs
// Module: diesel_models
// Public functions: 15

use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods, Table};

use super::generics;
#[cfg(feature = "v1")]
use crate::schema::merchant_connector_account::dsl;
#[cfg(feature = "v2")]
use crate::schema_v2::merchant_connector_account::dsl;
use crate::{
    errors,
    merchant_connector_account::{
        MerchantConnectorAccount, MerchantConnectorAccountNew,
        MerchantConnectorAccountUpdateInternal,
    },
    PgPooledConn, StorageResult,
};

impl MerchantConnectorAccountNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<MerchantConnectorAccount> {
        generics::generic_insert(conn, self).await
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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


// Struct: CardInfoResponse
// File: crates/router/src/core/cards_info.rs
// Module: router
// Implementations: 1
// Traits: State

pub struct CardInfoResponse


// Function: get_poll_id
// File: crates/common_utils/src/id_type/merchant.rs
// Module: common_utils

// Documentation: get_poll_id

pub fn get_poll_id(&self, unique_id: &str) -> String


// Struct: Rule
// File: crates/router/src/core/payments/routing/utils.rs
// Module: router
// Implementations: 0

pub struct Rule


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

impl Responder


// Function: get_optional_email
// File: crates/api_models/src/customers.rs
// Module: api_models

pub fn get_optional_email(&self) -> Option<pii::Email>


// Struct: TrustlyData
// File: crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TrustlyData


        #[cfg(feature = "kv_store")]
        self.drainer.validate()?;
        self.api_keys.get_inner().validate()?;

        self.file_storage
            .validate()
            .map_err(|err| ApplicationError::InvalidConfigurationValueError(err.to_string()))?;

        self.crm
            .validate()
            .map_err(|err| ApplicationError::InvalidConfigurationValueError(err.to_string()))?;

        self.lock_settings.validate()?;
        self.events.validate()?;

        #[cfg(feature = "olap")]
        self.opensearch.validate()?;

        self.encryption_management
            .validate()
            .map_err(|err| ApplicationError::InvalidConfigurationValueError(err.into()))?;

        self.secrets_management
            .validate()
            .map_err(|err| ApplicationError::InvalidConfigurationValueError(err.into()))?;
        self.generic_link.payment_method_collect.validate()?;
        self.generic_link.payout_link.validate()?;

        #[cfg(feature = "v2")]
        self.cell_information.validate()?;
        self.network_tokenization_service
            .as_ref()
            .map(|x| x.get_inner().validate())
            .transpose()?;

        self.paze_decrypt_keys
            .as_ref()
            .map(|x| x.get_inner().validate())
            .transpose()?;

        self.google_pay_decrypt_keys
            .as_ref()
            .map(|x| x.validate())
            .transpose()?;

        self.key_manager.get_inner().validate()?;
        #[cfg(feature = "email")]
        self.email
            .validate()
            .map_err(|err| ApplicationError::InvalidConfigurationValueError(err.into()))?;

        self.theme
            .storage
            .validate()
            .map_err(|err| ApplicationError::InvalidConfigurationValueError(err.to_string()))?;

        self.platform.validate()?;

        self.open_router.validate()?;

        // Validate gRPC client settings
        #[cfg(feature = "revenue_recovery")]
        self.grpc_client
            .recovery_decider_client
            .as_ref()
            .map(|config| config.validate())
            .transpose()
            .map_err(|err| ApplicationError::InvalidConfigurationValueError(err.to_string()))?;

        Ok(())
    }
}

impl Settings<RawSecret> {
    #[cfg(feature = "kv_store")]
    pub fn is_kv_soft_kill_mode(&self) -> bool {
        self.kv_config.soft_kill.unwrap_or(false)
    }

    #[cfg(not(feature = "kv_store"))]
    pub fn is_kv_soft_kill_mode(&self) -> bool {
        false
    }
}

#[cfg(feature = "payouts")]
#[derive(Debug, Deserialize, Clone, Default)]
pub struct Payouts {
    pub payout_eligibility: bool,
    #[serde(default)]
    pub required_fields: PayoutRequiredFields,
}

#[derive(Debug, Clone, Default)]
pub struct LockSettings {
    pub redis_lock_expiry_seconds: u32,
    pub delay_between_retries_in_milliseconds: u32,
    pub lock_retries: u32,
}

impl<'de> Deserialize<'de> for LockSettings {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(deny_unknown_fields)]
        struct Inner {
            redis_lock_expiry_seconds: u32,
            delay_between_retries_in_milliseconds: u32,
        }

        let Inner {
            redis_lock_expiry_seconds,
            delay_between_retries_in_milliseconds,
        } = Inner::deserialize(deserializer)?;
        let redis_lock_expiry_milliseconds = redis_lock_expiry_seconds * 1000;
        Ok(Self {
            redis_lock_expiry_seconds,
            delay_between_retries_in_milliseconds,
            lock_retries: redis_lock_expiry_milliseconds / delay_between_retries_in_milliseconds,
        })
    }
}

#[cfg(feature = "olap")]
#[derive(Debug, Deserialize, Clone, Default)]
pub struct ConnectorOnboarding {
    pub paypal: PayPalOnboarding,
}

#[cfg(feature = "olap")]
#[derive(Debug, Deserialize, Clone, Default)]
pub struct PayPalOnboarding {
    pub client_id: Secret<String>,
    pub client_secret: Secret<String>,
    pub partner_id: Secret<String>,
    pub enabled: bool,
}

#[cfg(feature = "tls")]
#[derive(Debug, Deserialize, Clone)]
pub struct ServerTls {
    /// Port to host the TLS secure server on
    pub port: u16,
    /// Use a different host (optional) (defaults to the host provided in [`Server`] config)
    pub host: Option<String>,
    /// private key file path associated with TLS (path to the private key file (`pem` format))
    pub private_key: PathBuf,
    /// certificate file associated with TLS (path to the certificate file (`pem` format))
    pub certificate: PathBuf,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct CellInformation {
    pub id: id_type::CellId,
}

#[cfg(feature = "v2")]
impl Default for CellInformation {
    fn default() -> Self {
        // We provide a static default cell id for constructing application settings.
        // This will only panic at application startup if we're unable to construct the default,
        // around the time of deserializing application settings.
        // And a panic at application startup is considered acceptable.
        #[allow(clippy::expect_used)]
        let cell_id =
            id_type::CellId::from_string("defid").expect("Failed to create a default for Cell Id");
        Self { id: cell_id }
    }
}

#[derive(Debug, Deserialize, Clone, Default)]
pub struct ThemeSettings {
    pub storage: FileStorageConfig,
    pub email_config: EmailThemeConfig,
}

fn deserialize_hashmap_inner<K, V>(
    value: HashMap<String, String>,
) -> Result<HashMap<K, HashSet<V>>, String>
where
    K: Eq + std::str::FromStr + std::hash::Hash,
    V: Eq + std::str::FromStr + std::hash::Hash,
    <K as std::str::FromStr>::Err: std::fmt::Display,
    <V as std::str::FromStr>::Err: std::fmt::Display,
{
    let (values, errors) = value
        .into_iter()
        .map(
            |(k, v)| match (K::from_str(k.trim()), deserialize_hashset_inner(v)) {
                (Err(error), _) => Err(format!(
                    "Unable to deserialize `{}` as `{}`: {error}",
                    k,
                    std::any::type_name::<K>()
                )),
                (_, Err(error)) => Err(error),
                (Ok(key), Ok(value)) => Ok((key, value)),
            },
        )
        .fold(
            (HashMap::new(), Vec::new()),
            |(mut values, mut errors), result| match result {
                Ok((key, value)) => {
                    values.insert(key, value);
                    (values, errors)
                }
                Err(error) => {
                    errors.push(error);
                    (values, errors)
                }
            },
        );
    if !errors.is_empty() {
        Err(format!("Some errors occurred:\n{}", errors.join("\n")))
    } else {
        Ok(values)
    }
}

fn deserialize_hashmap<'a, D, K, V>(deserializer: D) -> Result<HashMap<K, HashSet<V>>, D::Error>
where
    D: serde::Deserializer<'a>,
    K: Eq + std::str::FromStr + std::hash::Hash,
    V: Eq + std::str::FromStr + std::hash::Hash,
    <K as std::str::FromStr>::Err: std::fmt::Display,
    <V as std::str::FromStr>::Err: std::fmt::Display,
{
    use serde::de::Error;
    deserialize_hashmap_inner(<HashMap<String, String>>::deserialize(deserializer)?)
        .map_err(D::Error::custom)
}

fn deserialize_hashset_inner<T>(value: impl AsRef<str>) -> Result<HashSet<T>, String>
where
    T: Eq + std::str::FromStr + std::hash::Hash,
    <T as std::str::FromStr>::Err: std::fmt::Display,
{
    let (values, errors) = value
        .as_ref()
        .trim()
        .split(',')
        .map(|s| {
            T::from_str(s.trim()).map_err(|error| {
                format!(
                    "Unable to deserialize `{}` as `{}`: {error}",
                    s.trim(),
                    std::any::type_name::<T>()
                )
            })
        })
        .fold(
            (HashSet::new(), Vec::new()),
            |(mut values, mut errors), result| match result {
                Ok(t) => {
                    values.insert(t);
                    (values, errors)
                }
                Err(error) => {
                    errors.push(error);
                    (values, errors)
                }
            },
        );
    if !errors.is_empty() {
        Err(format!("Some errors occurred:\n{}", errors.join("\n")))
    } else {
        Ok(values)
    }
}

fn deserialize_hashset<'a, D, T>(deserializer: D) -> Result<HashSet<T>, D::Error>
where
    D: serde::Deserializer<'a>,
    T: Eq + std::str::FromStr + std::hash::Hash,
    <T as std::str::FromStr>::Err: std::fmt::Display,
{
    use serde::de::Error;

    deserialize_hashset_inner(<String>::deserialize(deserializer)?).map_err(D::Error::custom)
}

fn deserialize_optional_hashset<'a, D, T>(deserializer: D) -> Result<Option<HashSet<T>>, D::Error>
where
    D: serde::Deserializer<'a>,
    T: Eq + std::str::FromStr + std::hash::Hash,
    <T as std::str::FromStr>::Err: std::fmt::Display,
{
    use serde::de::Error;

    <Option<String>>::deserialize(deserializer).map(|value| {
        value.map_or(Ok(None), |inner: String| {
            let list = deserialize_hashset_inner(inner).map_err(D::Error::custom)?;
            match list.len() {
                0 => Ok(None),
                _ => Ok(Some(list)),
            }
        })
    })?
}

fn deserialize_merchant_ids_inner(
    value: impl AsRef<str>,
) -> Result<HashSet<id_type::MerchantId>, String> {
    let (values, errors) = value
        .as_ref()
        .trim()
        .split(',')
        .map(|s| {
            let trimmed = s.trim();
            id_type::MerchantId::wrap(trimmed.to_owned()).map_err(|error| {
                format!("Unable to deserialize `{trimmed}` as `MerchantId`: {error}")
            })
        })
        .fold(
            (HashSet::new(), Vec::new()),
            |(mut values, mut errors), result| match result {
                Ok(t) => {
                    values.insert(t);
                    (values, errors)
                }
                Err(error) => {
                    errors.push(error);
                    (values, errors)
                }
            },
        );

    if !errors.is_empty() {
        Err(format!("Some errors occurred:\n{}", errors.join("\n")))
    } else {
        Ok(values)
    }
}

fn deserialize_merchant_ids<'de, D>(
    deserializer: D,
) -> Result<HashSet<id_type::MerchantId>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let s = String::deserialize(deserializer)?;
    deserialize_merchant_ids_inner(s).map_err(serde::de::Error::custom)
}

impl<'de> Deserialize<'de> for TenantConfig {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        struct Inner {
            base_url: String,
            schema: String,
            accounts_schema: String,
            redis_key_prefix: String,
            clickhouse_database: String,
            user: TenantUserConfig,
        }

        let hashmap = <HashMap<id_type::TenantId, Inner>>::deserialize(deserializer)?;

        Ok(Self(
            hashmap
                .into_iter()
                .map(|(key, value)| {
                    (
                        key.clone(),
                        Tenant {
                            tenant_id: key,
                            base_url: value.base_url,
                            schema: value.schema,
                            accounts_schema: value.accounts_schema,
                            redis_key_prefix: value.redis_key_prefix,
                            clickhouse_database: value.clickhouse_database,
                            user: value.user,
                        },
                    )
                })
                .collect(),
        ))
    }
}

#[cfg(test)]
mod hashmap_deserialization_test {
    #![allow(clippy::unwrap_used)]
    use std::collections::{HashMap, HashSet};

    use serde::de::{
        value::{Error as ValueError, MapDeserializer},
        IntoDeserializer,
    };

    use super::deserialize_hashmap;

    #[test]
    fn test_payment_method_and_payment_method_types() {
        use diesel_models::enums::{PaymentMethod, PaymentMethodType};

        let input_map: HashMap<String, String> = HashMap::from([
            ("bank_transfer".to_string(), "ach,bacs".to_string()),
            ("wallet".to_string(), "paypal,venmo".to_string()),
        ]);
        let deserializer: MapDeserializer<
            '_,
            std::collections::hash_map::IntoIter<String, String>,
            ValueError,
        > = input_map.into_deserializer();
        let result = deserialize_hashmap::<'_, _, PaymentMethod, PaymentMethodType>(deserializer);
        let expected_result = HashMap::from([
            (
                PaymentMethod::BankTransfer,
                HashSet::from([PaymentMethodType::Ach, PaymentMethodType::Bacs]),
            ),
            (
                PaymentMethod::Wallet,
                HashSet::from([PaymentMethodType::Paypal, PaymentMethodType::Venmo]),
            ),
        ]);

        assert!(result.is_ok());
        assert_eq!(result.unwrap(), expected_result);
    }

    #[test]
    fn test_payment_method_and_payment_method_types_with_spaces() {
        use diesel_models::enums::{PaymentMethod, PaymentMethodType};

        let input_map: HashMap<String, String> = HashMap::from([
            (" bank_transfer ".to_string(), " ach , bacs ".to_string()),
            ("wallet ".to_string(), " paypal , pix , venmo ".to_string()),
        ]);
        let deserializer: MapDeserializer<
            '_,
            std::collections::hash_map::IntoIter<String, String>,
            ValueError,
        > = input_map.into_deserializer();
        let result = deserialize_hashmap::<'_, _, PaymentMethod, PaymentMethodType>(deserializer);
        let expected_result = HashMap::from([
            (
                PaymentMethod::BankTransfer,
                HashSet::from([PaymentMethodType::Ach, PaymentMethodType::Bacs]),
            ),
            (
                PaymentMethod::Wallet,
                HashSet::from([
                    PaymentMethodType::Paypal,
                    PaymentMethodType::Pix,
                    PaymentMethodType::Venmo,
                ]),
            ),
        ]);

        assert!(result.is_ok());
        assert_eq!(result.unwrap(), expected_result);
    }

    #[test]
    fn test_payment_method_deserializer_error() {
        use diesel_models::enums::{PaymentMethod, PaymentMethodType};

        let input_map: HashMap<String, String> = HashMap::from([
            ("unknown".to_string(), "ach,bacs".to_string()),
            ("wallet".to_string(), "paypal,unknown".to_string()),
        ]);
        let deserializer: MapDeserializer<
            '_,
            std::collections::hash_map::IntoIter<String, String>,
            ValueError,
        > = input_map.into_deserializer();
        let result = deserialize_hashmap::<'_, _, PaymentMethod, PaymentMethodType>(deserializer);

        assert!(result.is_err());
    }
}

#[cfg(test)]
mod hashset_deserialization_test {
    #![allow(clippy::unwrap_used)]
    use std::collections::HashSet;

    use serde::de::{
        value::{Error as ValueError, StrDeserializer},
        IntoDeserializer,
    };

    use super::deserialize_hashset;

    #[test]
    fn test_payment_method_hashset_deserializer() {
        use diesel_models::enums::PaymentMethod;

        let deserializer: StrDeserializer<'_, ValueError> = "wallet,card".into_deserializer();
        let payment_methods = deserialize_hashset::<'_, _, PaymentMethod>(deserializer);
        let expected_payment_methods = HashSet::from([PaymentMethod::Wallet, PaymentMethod::Card]);

        assert!(payment_methods.is_ok());
        assert_eq!(payment_methods.unwrap(), expected_payment_methods);
    }

    #[test]
    fn test_payment_method_hashset_deserializer_with_spaces() {
        use diesel_models::enums::PaymentMethod;

        let deserializer: StrDeserializer<'_, ValueError> =
            "wallet, card, bank_debit".into_deserializer();
        let payment_methods = deserialize_hashset::<'_, _, PaymentMethod>(deserializer);
        let expected_payment_methods = HashSet::from([
            PaymentMethod::Wallet,
            PaymentMethod::Card,
            PaymentMethod::BankDebit,
        ]);

        assert!(payment_methods.is_ok());
        assert_eq!(payment_methods.unwrap(), expected_payment_methods);
    }

    #[test]
    fn test_payment_method_hashset_deserializer_error() {
        use diesel_models::enums::PaymentMethod;

        let deserializer: StrDeserializer<'_, ValueError> =
            "wallet, card, unknown".into_deserializer();
        let payment_methods = deserialize_hashset::<'_, _, PaymentMethod>(deserializer);

        assert!(payment_methods.is_err());
    }
}


// Struct: StaxBankTokenizeData
// File: crates/hyperswitch_connectors/src/connectors/stax/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct StaxBankTokenizeData


// File: crates/hyperswitch_interfaces/src/metrics.rs
// Module: hyperswitch_interfaces

//! Metrics interface

use router_env::{counter_metric, global_meter};

global_meter!(GLOBAL_METER, "ROUTER_API");

counter_metric!(UNIMPLEMENTED_FLOW, GLOBAL_METER);


// Implementation: impl PaymentCapture for for DummyConnector
// File: crates/hyperswitch_connectors/src/connectors/dummyconnector.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl PaymentCapture for for DummyConnector


// Function: refunds_create
// File: crates/router/src/routes/refunds.rs
// Module: router

pub fn refunds_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<refunds::RefundRequest>,
) -> HttpResponse


// Implementation: impl RefundResponse
// File: crates/api_models/src/refunds.rs
// Module: api_models
// Methods: 1 total (1 public)

impl RefundResponse


            limit,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
    let mut customer_pms = Vec::new();

    let profile_id = payment_intent
        .as_ref()
        .map(|payment_intent| {
            payment_intent
                .profile_id
                .clone()
                .get_required_value("profile_id")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("profile_id is not set in payment_intent")
        })
        .transpose()?;

    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        profile_id.as_ref(),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?;

    let is_connector_agnostic_mit_enabled = business_profile
        .as_ref()
        .and_then(|business_profile| business_profile.is_connector_agnostic_mit_enabled)
        .unwrap_or(false);

    for pm in resp.into_iter() {
        let parent_payment_method_token = generate_id(consts::ID_LENGTH, "token");

        let payment_method = pm
            .get_payment_method_type()
            .get_required_value("payment_method")?;

        let pm_list_context = get_pm_list_context(
            state,
            &payment_method,
            merchant_context.get_merchant_key_store(),
            &pm,
            Some(parent_payment_method_token.clone()),
            true,
            false,
            &merchant_context,
        )
        .await?;

        if pm_list_context.is_none() {
            continue;
        }

        let pm_list_context = pm_list_context.get_required_value("PaymentMethodListContext")?;

        // Retrieve the masked bank details to be sent as a response
        let bank_details = if payment_method == enums::PaymentMethod::BankDebit {
            get_masked_bank_details(&pm).await.unwrap_or_else(|error| {
                logger::error!(?error);
                None
            })
        } else {
            None
        };

        let payment_method_billing = pm
            .payment_method_billing_address
            .clone()
            .map(|decrypted_data| decrypted_data.into_inner().expose())
            .map(|decrypted_value| decrypted_value.parse_value("payment method billing address"))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("unable to decrypt payment method billing address details")?;
        let connector_mandate_details = pm
            .get_common_mandate_reference()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to deserialize to Payment Mandate Reference ")?;
        let mca_enabled = get_mca_status(
            state,
            merchant_context.get_merchant_key_store(),
            profile_id.clone(),
            merchant_context.get_merchant_account().get_id(),
            is_connector_agnostic_mit_enabled,
            Some(connector_mandate_details),
            pm.network_transaction_id.as_ref(),
        )
        .await?;

        let requires_cvv = if is_connector_agnostic_mit_enabled {
            requires_cvv
                && !(off_session_payment_flag
                    && (pm.connector_mandate_details.is_some()
                        || pm.network_transaction_id.is_some()))
        } else {
            requires_cvv && !(off_session_payment_flag && pm.connector_mandate_details.is_some())
        };
        // Need validation for enabled payment method ,querying MCA
        let pma = api::CustomerPaymentMethod {
            payment_token: parent_payment_method_token.to_owned(),
            payment_method_id: pm.payment_method_id.clone(),
            customer_id: pm.customer_id.clone(),
            payment_method,
            payment_method_type: pm.get_payment_method_subtype(),
            payment_method_issuer: pm.payment_method_issuer,
            card: pm_list_context.card_details,
            metadata: pm.metadata,
            payment_method_issuer_code: pm.payment_method_issuer_code,
            recurring_enabled: mca_enabled,
            installment_payment_enabled: Some(false),
            payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]),
            created: Some(pm.created_at),
            #[cfg(feature = "payouts")]
            bank_transfer: pm_list_context.bank_transfer_details,
            bank: bank_details,
            surcharge_details: None,
            requires_cvv,
            last_used_at: Some(pm.last_used_at),
            default_payment_method_set: customer.default_payment_method_id.is_some()
                && customer.default_payment_method_id == Some(pm.payment_method_id),
            billing: payment_method_billing,
        };
        if requires_cvv || mca_enabled.unwrap_or(false) {
            customer_pms.push(pma.to_owned());
        }

        let redis_conn = state
            .store
            .get_redis_conn()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to get redis connection")?;

        let intent_fulfillment_time = business_profile
            .as_ref()
            .and_then(|b_profile| b_profile.get_order_fulfillment_time())
            .unwrap_or(consts::DEFAULT_INTENT_FULFILLMENT_TIME);

        let hyperswitch_token_data = pm_list_context
            .hyperswitch_token_data
            .get_required_value("PaymentTokenData")?;

        ParentPaymentMethodToken::create_key_for_token((
            &parent_payment_method_token,
            pma.payment_method,
        ))
        .insert(intent_fulfillment_time, hyperswitch_token_data, state)
        .await?;

        if let Some(metadata) = pma.metadata {
            let pm_metadata_vec: payment_methods::PaymentMethodMetadata = metadata
                .parse_value("PaymentMethodMetadata")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable(
                    "Failed to deserialize metadata to PaymentmethodMetadata struct",
                )?;

            for pm_metadata in pm_metadata_vec.payment_method_tokenization {
                let key = format!(
                    "pm_token_{}_{}_{}",
                    parent_payment_method_token, pma.payment_method, pm_metadata.0
                );

                redis_conn
                    .set_key_with_expiry(&key.into(), pm_metadata.1, intent_fulfillment_time)
                    .await
                    .change_context(errors::StorageError::KVError)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to add data in redis")?;
            }
        }
    }

    let mut response = api::CustomerPaymentMethodsListResponse {
        customer_payment_methods: customer_pms,
        is_guest_customer: payment_intent.as_ref().map(|_| false), //to return this key only when the request is tied to a payment intent
    };

    Box::pin(perform_surcharge_ops(
        payment_intent,
        state,
        merchant_context,
        business_profile,
        &mut response,
    ))
    .await?;

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

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
pub async fn get_pm_list_context(
    state: &routes::SessionState,
    payment_method: &enums::PaymentMethod,
    #[cfg(feature = "payouts")] key_store: &domain::MerchantKeyStore,
    #[cfg(not(feature = "payouts"))] _key_store: &domain::MerchantKeyStore,
    pm: &domain::PaymentMethod,
    #[cfg(feature = "payouts")] parent_payment_method_token: Option<String>,
    #[cfg(not(feature = "payouts"))] _parent_payment_method_token: Option<String>,
    is_payment_associated: bool,
    force_fetch_card_from_vault: bool,
    merchant_context: &domain::MerchantContext,
) -> Result<Option<PaymentMethodListContext>, error_stack::Report<errors::ApiErrorResponse>> {
    let cards = PmCards {
        state,
        merchant_context,
    };
    let payment_method_retrieval_context = match payment_method {
        enums::PaymentMethod::Card => {
            let card_details = if force_fetch_card_from_vault {
                Some(cards.get_card_details_from_locker(pm).await?)
            } else {
                cards.get_card_details_with_locker_fallback(pm).await?
            };

            card_details.as_ref().map(|card| PaymentMethodListContext {
                card_details: Some(card.clone()),
                #[cfg(feature = "payouts")]
                bank_transfer_details: None,
                hyperswitch_token_data: is_payment_associated.then_some(
                    PaymentTokenData::permanent_card(
                        Some(pm.get_id().clone()),
                        pm.locker_id.clone().or(Some(pm.get_id().clone())),
                        pm.locker_id.clone().unwrap_or(pm.get_id().clone()),
                        pm.network_token_requestor_reference_id
                            .clone()
                            .or(Some(pm.get_id().clone())),
                    ),
                ),
            })
        }

        enums::PaymentMethod::BankDebit => {
            // Retrieve the pm_auth connector details so that it can be tokenized
            let bank_account_token_data = get_bank_account_connector_details(pm)
                .await
                .unwrap_or_else(|err| {
                    logger::error!(error=?err);
                    None
                });

            bank_account_token_data.map(|data| {
                let token_data = PaymentTokenData::AuthBankDebit(data);

                PaymentMethodListContext {
                    card_details: None,
                    #[cfg(feature = "payouts")]
                    bank_transfer_details: None,
                    hyperswitch_token_data: is_payment_associated.then_some(token_data),
                }
            })
        }

        enums::PaymentMethod::Wallet => Some(PaymentMethodListContext {
            card_details: None,
            #[cfg(feature = "payouts")]
            bank_transfer_details: None,
            hyperswitch_token_data: is_payment_associated
                .then_some(PaymentTokenData::wallet_token(pm.get_id().clone())),
        }),

        #[cfg(feature = "payouts")]
        enums::PaymentMethod::BankTransfer => Some(PaymentMethodListContext {
            card_details: None,
            bank_transfer_details: Some(
                get_bank_from_hs_locker(
                    state,
                    key_store,
                    parent_payment_method_token.as_ref(),
                    &pm.customer_id,
                    &pm.merchant_id,
                    pm.locker_id.as_ref().unwrap_or(pm.get_id()),
                )
                .await?,
            ),
            hyperswitch_token_data: parent_payment_method_token
                .map(|token| PaymentTokenData::temporary_generic(token.clone())),
        }),

        _ => Some(PaymentMethodListContext {
            card_details: None,
            #[cfg(feature = "payouts")]
            bank_transfer_details: None,
            hyperswitch_token_data: is_payment_associated.then_some(
                PaymentTokenData::temporary_generic(generate_id(consts::ID_LENGTH, "token")),
            ),
        }),
    };

    Ok(payment_method_retrieval_context)
}

#[cfg(feature = "v1")]
async fn perform_surcharge_ops(
    payment_intent: Option<storage::PaymentIntent>,
    state: &routes::SessionState,
    merchant_context: domain::MerchantContext,
    business_profile: Option<Profile>,
    response: &mut api::CustomerPaymentMethodsListResponse,
) -> Result<(), error_stack::Report<errors::ApiErrorResponse>> {
    let payment_attempt = payment_intent
        .as_ref()
        .async_map(|payment_intent| async {
            state
                .store
                .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                    payment_intent.get_id(),
                    merchant_context.get_merchant_account().get_id(),
                    &payment_intent.active_attempt.get_id(),
                    merchant_context.get_merchant_account().storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
        })
        .await
        .transpose()?;
    if let Some((payment_attempt, payment_intent, business_profile)) = payment_attempt
        .zip(payment_intent)
        .zip(business_profile)
        .map(|((pa, pi), bp)| (pa, pi, bp))
    {
        call_surcharge_decision_management_for_saved_card(
            state,
            &merchant_context,
            &business_profile,
            &payment_attempt,
            payment_intent,
            response,
        )
        .await?;
    }

    Ok(())
}

#[cfg(feature = "v2")]
pub async fn perform_surcharge_ops(
    _payment_intent: Option<storage::PaymentIntent>,
    _state: &routes::SessionState,
    _merchant_context: &domain::MerchantContext,
    _business_profile: Option<Profile>,
    _response: &mut api_models::payment_methods::CustomerPaymentMethodsListResponse,
) -> Result<(), error_stack::Report<errors::ApiErrorResponse>> {
    todo!()
}

#[cfg(feature = "v1")]
pub async fn get_mca_status(
    state: &routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    profile_id: Option<id_type::ProfileId>,
    merchant_id: &id_type::MerchantId,
    is_connector_agnostic_mit_enabled: bool,
    connector_mandate_details: Option<CommonMandateReference>,
    network_transaction_id: Option<&String>,
) -> errors::RouterResult<Option<bool>> {
    if is_connector_agnostic_mit_enabled && network_transaction_id.is_some() {
        return Ok(Some(true));
    }
    if let Some(connector_mandate_details) = connector_mandate_details {
        let mcas = state
            .store
            .find_merchant_connector_account_by_merchant_id_and_disabled_list(
                &state.into(),
                merchant_id,
                true,
                key_store,
            )
            .await
            .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                id: merchant_id.get_string_repr().to_owned(),
            })?;

        return Ok(Some(
            mcas.is_merchant_connector_account_id_in_connector_mandate_details(
                profile_id.as_ref(),
                &connector_mandate_details,
            ),
        ));
    }
    Ok(Some(false))
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
pub async fn get_mca_status(
    state: &routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    profile_id: Option<id_type::ProfileId>,
    merchant_id: &id_type::MerchantId,
    is_connector_agnostic_mit_enabled: bool,
    connector_mandate_details: Option<&CommonMandateReference>,
    network_transaction_id: Option<&String>,
    merchant_connector_accounts: &domain::MerchantConnectorAccounts,
) -> bool {
    if is_connector_agnostic_mit_enabled && network_transaction_id.is_some() {
        return true;
    }
    match connector_mandate_details {
        Some(connector_mandate_details) => merchant_connector_accounts
            .is_merchant_connector_account_id_in_connector_mandate_details(
                profile_id.as_ref(),
                connector_mandate_details,
            ),
        None => false,
    }
}

pub async fn decrypt_generic_data<T>(
    state: &routes::SessionState,
    data: Option<Encryption>,
    key_store: &domain::MerchantKeyStore,
) -> errors::RouterResult<Option<T>>
where
    T: serde::de::DeserializeOwned,
{
    let key = key_store.key.get_inner().peek();
    let identifier = Identifier::Merchant(key_store.merchant_id.clone());
    let decrypted_data = domain::types::crypto_operation::<serde_json::Value, masking::WithType>(
        &state.into(),
        type_name!(T),
        domain::types::CryptoOperation::DecryptOptional(data),
        identifier,
        key,
    )
    .await
    .and_then(|val| val.try_into_optionaloperation())
    .change_context(errors::StorageError::DecryptionError)
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("unable to decrypt data")?;

    decrypted_data
        .map(|decrypted_data| decrypted_data.into_inner().expose())
        .map(|decrypted_value| decrypted_value.parse_value("generic_data"))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("unable to parse generic data value")
}

#[cfg(feature = "v1")]
pub async fn get_card_details_from_locker(
    state: &routes::SessionState,
    pm: &domain::PaymentMethod,
) -> errors::RouterResult<api::CardDetailFromLocker> {
    let card = get_card_from_locker(
        state,
        &pm.customer_id,
        &pm.merchant_id,
        pm.locker_id.as_ref().unwrap_or(pm.get_id()),
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Error getting card from card vault")?;

    payment_methods::get_card_detail(pm, card)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Get Card Details Failed")
}

#[cfg(feature = "v1")]
pub async fn get_lookup_key_from_locker(
    state: &routes::SessionState,
    payment_token: &str,
    pm: &domain::PaymentMethod,
    merchant_key_store: &domain::MerchantKeyStore,
) -> errors::RouterResult<api::CardDetailFromLocker> {
    let card_detail = get_card_details_from_locker(state, pm).await?;
    let card = card_detail.clone();

    let resp = TempLockerCardSupport::create_payment_method_data_in_temp_locker(
        state,
        payment_token,
        card,
        pm,
        merchant_key_store,
    )
    .await?;
    Ok(resp)
}

pub async fn get_masked_bank_details(
    pm: &domain::PaymentMethod,
) -> errors::RouterResult<Option<MaskedBankDetails>> {
    #[cfg(feature = "v1")]
    let payment_method_data = pm
        .payment_method_data
        .clone()
        .map(|x| x.into_inner().expose())
        .map(
            |v| -> Result<PaymentMethodsData, error_stack::Report<errors::ApiErrorResponse>> {
                v.parse_value::<PaymentMethodsData>("PaymentMethodsData")
                    .change_context(errors::StorageError::DeserializationFailed)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to deserialize Payment Method Auth config")
            },
        )
        .transpose()?;

    #[cfg(feature = "v2")]
    let payment_method_data = pm.payment_method_data.clone().map(|x| x.into_inner());

    match payment_method_data {
        Some(pmd) => match pmd {
            PaymentMethodsData::Card(_) => Ok(None),
            PaymentMethodsData::BankDetails(bank_details) => Ok(Some(MaskedBankDetails {
                mask: bank_details.mask,
            })),
            PaymentMethodsData::WalletDetails(_) => Ok(None),
        },
        None => Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable("Unable to fetch payment method data"),
    }
}

#[cfg(feature = "v1")]
pub async fn get_bank_account_connector_details(
    pm: &domain::PaymentMethod,
) -> errors::RouterResult<Option<BankAccountTokenData>> {
    let payment_method_data = pm
        .payment_method_data
        .clone()
        .map(|x| x.into_inner().expose())
        .map(
            |v| -> Result<PaymentMethodsData, error_stack::Report<errors::ApiErrorResponse>> {
                v.parse_value::<PaymentMethodsData>("PaymentMethodsData")
                    .change_context(errors::StorageError::DeserializationFailed)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to deserialize Payment Method Auth config")
            },
        )
        .transpose()?;

    match payment_method_data {
        Some(pmd) => match pmd {
            PaymentMethodsData::Card(_) => Err(errors::ApiErrorResponse::UnprocessableEntity {
                message: "Card is not a valid entity".to_string(),
            }
            .into()),
            PaymentMethodsData::WalletDetails(_) => {
                Err(errors::ApiErrorResponse::UnprocessableEntity {
                    message: "Wallet is not a valid entity".to_string(),
                }
                .into())
            }
            PaymentMethodsData::BankDetails(bank_details) => {
                let connector_details = bank_details
                    .connector_details
                    .first()
                    .ok_or(errors::ApiErrorResponse::InternalServerError)?;

                let pm_type = pm
                    .get_payment_method_subtype()
                    .get_required_value("payment_method_type")
                    .attach_printable("PaymentMethodType not found")?;

                let pm = pm
                    .get_payment_method_type()
                    .get_required_value("payment_method")
                    .attach_printable("PaymentMethod not found")?;

                let token_data = BankAccountTokenData {
                    payment_method_type: pm_type,
                    payment_method: pm,
                    connector_details: connector_details.clone(),
                };

                Ok(Some(token_data))
            }
        },
        None => Ok(None),
    }
}

pub async fn update_last_used_at(
    payment_method: &domain::PaymentMethod,
    state: &routes::SessionState,
    storage_scheme: MerchantStorageScheme,
    key_store: &domain::MerchantKeyStore,
) -> errors::RouterResult<()> {
    let update_last_used = storage::PaymentMethodUpdate::LastUsedUpdate {
        last_used_at: common_utils::date_time::now(),
    };

    state
        .store
        .update_payment_method(
            &(state.into()),
            key_store,
            payment_method.clone(),
            update_last_used,
            storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update the last_used_at in db")?;

    Ok(())
}
#[cfg(feature = "payouts")]
pub async fn get_bank_from_hs_locker(
    state: &routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    temp_token: Option<&String>,
    customer_id: &id_type::CustomerId,
    merchant_id: &id_type::MerchantId,
    token_ref: &str,
) -> errors::RouterResult<api::BankPayout> {
    let payment_method = get_payment_method_from_hs_locker(
        state,
        key_store,
        customer_id,
        merchant_id,
        token_ref,
        None,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Error getting payment method from locker")?;
    let pm_parsed: api::PayoutMethodData = payment_method
        .peek()
        .to_string()
        .parse_struct("PayoutMethodData")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    match &pm_parsed {
        api::PayoutMethodData::Bank(bank) => {
            if let Some(token) = temp_token {
                vault::Vault::store_payout_method_data_in_locker(
                    state,
                    Some(token.clone()),
                    &pm_parsed,
                    Some(customer_id.to_owned()),
                    key_store,
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error storing payout method data in temporary locker")?;
            }
            Ok(bank.to_owned())
        }
        api::PayoutMethodData::Card(_) => Err(errors::ApiErrorResponse::InvalidRequestData {
            message: "Expected bank details, found card details instead".to_string(),
        }
        .into()),
        api::PayoutMethodData::Wallet(_) => Err(errors::ApiErrorResponse::InvalidRequestData {
            message: "Expected bank details, found wallet details instead".to_string(),
        }
        .into()),
    }
}

#[cfg(feature = "v1")]
pub struct TempLockerCardSupport;

#[cfg(feature = "v1")]
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 async fn create_encrypted_data<T>(
    key_manager_state: &KeyManagerState,
    key_store: &domain::MerchantKeyStore,
    data: T,
) -> Result<Encryptable<Secret<serde_json::Value>>, error_stack::Report<errors::StorageError>>
where
    T: Debug + serde::Serialize,
{
    let key = key_store.key.get_inner().peek();
    let identifier = Identifier::Merchant(key_store.merchant_id.clone());

    let encoded_data = Encode::encode_to_value(&data)
        .change_context(errors::StorageError::SerializationFailed)
        .attach_printable("Unable to encode data")?;

    let secret_data = Secret::<_, masking::WithType>::new(encoded_data);

    let encrypted_data = domain::types::crypto_operation(
        key_manager_state,
        type_name!(payment_method::PaymentMethod),
        domain::types::CryptoOperation::Encrypt(secret_data),
        identifier.clone(),
        key,
    )
    .await
    .and_then(|val| val.try_into_operation())
    .change_context(errors::StorageError::EncryptionError)
    .attach_printable("Unable to encrypt data")?;

    Ok(encrypted_data)
}

pub async fn list_countries_currencies_for_connector_payment_method(
    state: routes::SessionState,
    req: ListCountriesCurrenciesRequest,
    _profile_id: Option<id_type::ProfileId>,
) -> errors::RouterResponse<ListCountriesCurrenciesResponse> {
    Ok(services::ApplicationResponse::Json(
        list_countries_currencies_for_connector_payment_method_util(
            state.conf.pm_filters.clone(),
            req.connector,
            req.payment_method_type,
        )
        .await,
    ))
}

// This feature will be more efficient as a WASM function rather than as an API.
// So extracting this logic to a separate function so that it can be used in WASM as well.
pub async fn list_countries_currencies_for_connector_payment_method_util(
    connector_filters: settings::ConnectorFilters,
    connector: api_enums::Connector,
    payment_method_type: api_enums::PaymentMethodType,
) -> ListCountriesCurrenciesResponse {
    let payment_method_type =
        settings::PaymentMethodFilterKey::PaymentMethodType(payment_method_type);

    let (currencies, country_codes) = connector_filters
        .0
        .get(&connector.to_string())
        .and_then(|filter| filter.0.get(&payment_method_type))
        .map(|filter| (filter.currency.clone(), filter.country.clone()))
        .unwrap_or_else(|| {
            connector_filters
                .0
                .get("default")
                .and_then(|filter| filter.0.get(&payment_method_type))
                .map_or((None, None), |filter| {
                    (filter.currency.clone(), filter.country.clone())
                })
        });

    let currencies =
        currencies.unwrap_or_else(|| api_enums::Currency::iter().collect::<HashSet<_>>());
    let country_codes =
        country_codes.unwrap_or_else(|| api_enums::CountryAlpha2::iter().collect::<HashSet<_>>());

    ListCountriesCurrenciesResponse {
        currencies,
        countries: country_codes
            .into_iter()
            .map(|country_code| CountryCodeWithName {
                code: country_code,
                name: common_enums::Country::from_alpha2(country_code),
            })
            .collect(),
    }
}

#[cfg(feature = "v1")]
pub async fn tokenize_card_flow(
    state: &routes::SessionState,
    req: domain::CardNetworkTokenizeRequest,
    merchant_context: &domain::MerchantContext,
) -> errors::RouterResult<api::CardNetworkTokenizeResponse> {
    match req.data {
        domain::TokenizeDataRequest::Card(ref card_req) => {
            let executor = tokenize::CardNetworkTokenizeExecutor::new(
                state,
                merchant_context,
                card_req,
                &req.customer,
            );
            let builder =
                tokenize::NetworkTokenizationBuilder::<tokenize::TokenizeWithCard>::default();
            execute_card_tokenization(executor, builder, card_req).await
        }
        domain::TokenizeDataRequest::ExistingPaymentMethod(ref payment_method) => {
            let executor = tokenize::CardNetworkTokenizeExecutor::new(
                state,
                merchant_context,
                payment_method,
                &req.customer,
            );
            let builder =
                tokenize::NetworkTokenizationBuilder::<tokenize::TokenizeWithPmId>::default();
            Box::pin(execute_payment_method_tokenization(
                executor,
                builder,
                payment_method,
            ))
            .await
        }
    }
}

#[cfg(feature = "v1")]
pub async fn execute_card_tokenization(
    executor: tokenize::CardNetworkTokenizeExecutor<'_, domain::TokenizeCardRequest>,
    builder: tokenize::NetworkTokenizationBuilder<'_, tokenize::TokenizeWithCard>,
    req: &domain::TokenizeCardRequest,
) -> errors::RouterResult<api::CardNetworkTokenizeResponse> {
    // Validate request and get optional customer
    let optional_customer = executor
        .validate_request_and_fetch_optional_customer()
        .await?;
    let builder = builder.set_validate_result();

    // Perform BIN lookup and validate card network
    let optional_card_info = executor
        .fetch_bin_details_and_validate_card_network(
            req.raw_card_number.clone(),
            req.card_issuer.as_ref(),
            req.card_network.as_ref(),
            req.card_type.as_ref(),
            req.card_issuing_country.as_ref(),
        )
        .await?;
    let builder = builder.set_card_details(req, optional_card_info);

    // Create customer if not present
    let customer = match optional_customer {
        Some(customer) => customer,
        None => executor.create_customer().await?,
    };
    let builder = builder.set_customer(&customer);

    // Tokenize card
    let (optional_card, optional_cvc) = builder.get_optional_card_and_cvc();
    let domain_card = optional_card
        .get_required_value("card")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    let network_token_details = executor
        .tokenize_card(&customer.id, &domain_card, optional_cvc)
        .await?;
    let builder = builder.set_token_details(&network_token_details);

    // Store card and token in locker
    let store_card_and_token_resp = executor
        .store_card_and_token_in_locker(&network_token_details, &domain_card, &customer.id)
        .await?;
    let builder = builder.set_stored_card_response(&store_card_and_token_resp);
    let builder = builder.set_stored_token_response(&store_card_and_token_resp);

    // Create payment method
    let payment_method = executor
        .create_payment_method(
            &store_card_and_token_resp,
            &network_token_details,
            &domain_card,
            &customer.id,
        )
        .await?;
    let builder = builder.set_payment_method_response(&payment_method);

    Ok(builder.build())
}

#[cfg(feature = "v1")]
pub async fn execute_payment_method_tokenization(
    executor: tokenize::CardNetworkTokenizeExecutor<'_, domain::TokenizePaymentMethodRequest>,
    builder: tokenize::NetworkTokenizationBuilder<'_, tokenize::TokenizeWithPmId>,
    req: &domain::TokenizePaymentMethodRequest,
) -> errors::RouterResult<api::CardNetworkTokenizeResponse> {
    // Fetch payment method
    let payment_method = executor
        .fetch_payment_method(&req.payment_method_id)
        .await?;
    let builder = builder.set_payment_method(&payment_method);

    // Validate payment method and customer
    let (locker_id, customer) = executor
        .validate_request_and_locker_reference_and_customer(&payment_method)
        .await?;
    let builder = builder.set_validate_result(&customer);

    // Fetch card from locker
    let card_details = get_card_from_locker(
        executor.state,
        &customer.id,
        executor.merchant_account.get_id(),
        &locker_id,
    )
    .await?;

    // Perform BIN lookup and validate card network
    let optional_card_info = executor
        .fetch_bin_details_and_validate_card_network(
            card_details.card_number.clone(),
            None,
            None,
            None,
            None,
        )
        .await?;
    let builder = builder.set_card_details(&card_details, optional_card_info, req.card_cvc.clone());

    // Tokenize card
    let (optional_card, optional_cvc) = builder.get_optional_card_and_cvc();
    let domain_card = optional_card.get_required_value("card")?;
    let network_token_details = executor
        .tokenize_card(&customer.id, &domain_card, optional_cvc)
        .await?;
    let builder = builder.set_token_details(&network_token_details);

    // Store token in locker
    let store_token_resp = executor
        .store_network_token_in_locker(
            &network_token_details,
            &customer.id,
            card_details.name_on_card.clone(),
            card_details.nick_name.clone().map(Secret::new),
        )
        .await?;
    let builder = builder.set_stored_token_response(&store_token_resp);

    // Update payment method
    let updated_payment_method = executor
        .update_payment_method(
            &store_token_resp,
            payment_method,
            &network_token_details,
            &domain_card,
        )
        .await?;
    let builder = builder.set_payment_method(&updated_payment_method);

    Ok(builder.build())
}


// Function: upload_file_to_user_theme_storage
// File: crates/router/src/routes/user/theme.rs
// Module: router

pub fn upload_file_to_user_theme_storage(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<String>,
    MultipartForm(payload)


// Function: is_disabled
// File: crates/hyperswitch_domain_models/src/merchant_connector_account.rs
// Module: hyperswitch_domain_models

pub fn is_disabled(&self) -> bool


// Struct: Amount
// File: crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Amount


// Struct: DefaultObject
// File: crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DefaultObject


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

impl api::MandateSetup for for Archipel


// Implementation: impl ConnectorCommon for for Wise
// File: crates/hyperswitch_connectors/src/connectors/wise.rs
// Module: hyperswitch_connectors
// Methods: 4 total (0 public)

impl ConnectorCommon for for Wise


// Function: transform_card
// File: crates/connector_configs/src/transformer.rs
// Module: connector_configs

pub fn transform_card(
        payment_method_type: PaymentMethodType,
        card_provider: Vec<api_models::enums::CardNetwork>,
    ) -> payment_methods::RequestPaymentMethodTypes


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

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

use super::{
    DeleteEvidenceRequest, DisputeResponse, DisputeResponsePaymentsRetrieve,
    DisputeRetrieveRequest, DisputesAggregateResponse, SubmitEvidenceRequest,
};

impl ApiEventMetric for SubmitEvidenceRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Dispute {
            dispute_id: self.dispute_id.clone(),
        })
    }
}

impl ApiEventMetric for DisputeRetrieveRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Dispute {
            dispute_id: self.dispute_id.clone(),
        })
    }
}

impl ApiEventMetric for DisputeResponsePaymentsRetrieve {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Dispute {
            dispute_id: self.dispute_id.clone(),
        })
    }
}
impl ApiEventMetric for DisputeResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Dispute {
            dispute_id: self.dispute_id.clone(),
        })
    }
}
impl ApiEventMetric for DeleteEvidenceRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Dispute {
            dispute_id: self.dispute_id.clone(),
        })
    }
}

impl ApiEventMetric for DisputesAggregateResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::ResourceListAPI)
    }
}


// Implementation: impl api::PaymentToken for for Phonepe
// File: crates/hyperswitch_connectors/src/connectors/phonepe.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentToken for for Phonepe