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

pub mod transformers;
use std::{fmt::Write, sync::LazyLock};

use api_models::webhooks::{IncomingWebhookEvent, ObjectReferenceId};
use common_enums::enums;
use common_utils::{
    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, 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, SetupMandateRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    consts::{ACCEPT_HEADER, NO_ERROR_CODE, NO_ERROR_MESSAGE, USER_AGENT},
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks::{IncomingWebhook, IncomingWebhookRequestDetails},
};
use masking::PeekInterface;
use transformers as itaubank;

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

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

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

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

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

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Itaubank
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 access_token =
            req.access_token
                .clone()
                .ok_or(errors::ConnectorError::MissingRequiredField {
                    field_name: "access_token",
                })?;

        let header = vec![
            (
                headers::AUTHORIZATION.to_string(),
                format!("Bearer {}", access_token.token.peek()).into(),
            ),
            (
                headers::ACCEPT.to_string(),
                ACCEPT_HEADER.to_string().into(),
            ),
            (
                headers::USER_AGENT.to_string(),
                USER_AGENT.to_string().into(),
            ),
            (
                headers::CONTENT_TYPE.to_string(),
                self.common_get_content_type().to_string().into(),
            ),
        ];

        Ok(header)
    }
}

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

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

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

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

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

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

        let reason = response
            .error
            .violacoes
            .map(|violacoes| {
                violacoes.iter().try_fold(String::new(), |mut acc, error| {
                    write!(
                        acc,
                        " razao - {}, propriedade - {}, valor - {};",
                        error.razao, error.propriedade, error.valor
                    )
                    .change_context(errors::ConnectorError::ResponseDeserializationFailed)
                    .attach_printable("Failed to concatenate error details")
                    .map(|_| acc)
                })
            })
            .transpose()?;

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.error.title.unwrap_or(NO_ERROR_CODE.to_string()),
            message: response
                .error
                .detail
                .unwrap_or(NO_ERROR_MESSAGE.to_string()),
            reason,
            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 Itaubank {}

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

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Itaubank {
    fn get_url(
        &self,
        _req: &RefreshTokenRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}api/oauth/jwt", self.base_url(connectors)))
    }
    fn get_content_type(&self) -> &'static str {
        "application/x-www-form-urlencoded"
    }
    fn get_headers(
        &self,
        _req: &RefreshTokenRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let flow_header = vec![
            (
                headers::CONTENT_TYPE.to_string(),
                types::RefreshTokenType::get_content_type(self)
                    .to_string()
                    .into(),
            ),
            (
                headers::ACCEPT.to_string(),
                ACCEPT_HEADER.to_string().into(),
            ),
            (
                headers::USER_AGENT.to_string(),
                USER_AGENT.to_string().into(),
            ),
        ];
        Ok(flow_header)
    }
    fn get_request_body(
        &self,
        req: &RefreshTokenRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = itaubank::ItaubankAuthRequest::try_from(req)?;

        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &RefreshTokenRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let auth_details = itaubank::ItaubankAuthType::try_from(&req.connector_auth_type)?;
        let req = Some(
            RequestBuilder::new()
                .method(Method::Post)
                .attach_default_headers()
                .headers(types::RefreshTokenType::get_headers(self, req, connectors)?)
                .url(&types::RefreshTokenType::get_url(self, req, connectors)?)
                .add_certificate(auth_details.certificate)
                .add_certificate_key(auth_details.certificate_key)
                .set_body(types::RefreshTokenType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        );

        Ok(req)
    }

    fn handle_response(
        &self,
        data: &RefreshTokenRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefreshTokenRouterData, errors::ConnectorError> {
        let response: itaubank::ItaubankUpdateTokenResponse = res
            .response
            .parse_struct("ItaubankUpdateTokenResponse")
            .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> {
        let response: itaubank::ItaubankTokenErrorResponse = res
            .response
            .parse_struct("ItaubankTokenErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.title.unwrap_or(NO_ERROR_CODE.to_string()),
            message: response
                .detail
                .to_owned()
                .or(response.user_message.to_owned())
                .unwrap_or(NO_ERROR_MESSAGE.to_string()),
            reason: response.detail.or(response.user_message),
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
    for Itaubank
{
    fn build_request(
        &self,
        _req: &SetupMandateRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "setup mandate".to_string(),
            connector: "itaubank".to_string(),
        }
        .into())
    }
}

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

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

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}itau-ep9-gtw-pix-recebimentos-ext-v2/v2/cob",
            self.base_url(connectors)
        ))
    }

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

        let connector_router_data = itaubank::ItaubankRouterData::from((amount, req));
        let connector_req = itaubank::ItaubankPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let auth_details = itaubank::ItaubankAuthType::try_from(&req.connector_auth_type)?;
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsAuthorizeType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::PaymentsAuthorizeType::get_headers(
                    self, req, connectors,
                )?)
                .add_certificate(auth_details.certificate)
                .add_certificate_key(auth_details.certificate_key)
                .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: itaubank::ItaubankPaymentsResponse = res
            .response
            .parse_struct("Itaubank 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 Itaubank {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}itau-ep9-gtw-pix-recebimentos-ext-v2/v2/cob/{}",
            self.base_url(connectors),
            req.request
                .connector_transaction_id
                .get_connector_transaction_id()
                .change_context(errors::ConnectorError::MissingConnectorTransactionID)?
        ))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let auth_details = itaubank::ItaubankAuthType::try_from(&req.connector_auth_type)?;
        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)?)
                .add_certificate(auth_details.certificate)
                .add_certificate_key(auth_details.certificate_key)
                .build(),
        ))
    }

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

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

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

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

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

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

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let auth_details = itaubank::ItaubankAuthType::try_from(&req.connector_auth_type)?;
        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,
                )?)
                .add_certificate(auth_details.certificate)
                .add_certificate_key(auth_details.certificate_key)
                .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: itaubank::ItaubankPaymentsResponse = res
            .response
            .parse_struct("Itaubank PaymentsCaptureResponse")
            .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 Itaubank {
    fn build_request(
        &self,
        _req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "void".to_string(),
            connector: "itaubank".to_string(),
        }
        .into())
    }
}

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

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

    fn get_url(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let itaubank_metadata = req.request.get_connector_metadata()?;
        let pix_data: itaubank::ItaubankMetaData = serde_json::from_value(itaubank_metadata)
            .change_context(errors::ConnectorError::MissingRequiredField {
                field_name: "itaubank_metadata",
            })?;
        Ok(format!(
            "{}itau-ep9-gtw-pix-recebimentos-ext-v2/v2/pix/{}/devolucao/{}",
            self.base_url(connectors),
            pix_data
                .pix_id
                .ok_or(errors::ConnectorError::MissingRequiredField {
                    field_name: "pix_id"
                })?,
            req.request.connector_transaction_id
        ))
    }

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

        let connector_router_data = itaubank::ItaubankRouterData::from((refund_amount, req));
        let connector_req = itaubank::ItaubankRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let auth_details = itaubank::ItaubankAuthType::try_from(&req.connector_auth_type)?;
        let request = RequestBuilder::new()
            .method(Method::Put)
            .url(&types::RefundExecuteType::get_url(self, req, connectors)?)
            .attach_default_headers()
            .headers(types::RefundExecuteType::get_headers(
                self, req, connectors,
            )?)
            .add_certificate(auth_details.certificate)
            .add_certificate_key(auth_details.certificate_key)
            .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: itaubank::RefundResponse = res
            .response
            .parse_struct("itaubank 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 Itaubank {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

    fn get_url(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let itaubank_metadata = req.request.get_connector_metadata()?;
        let pix_data: itaubank::ItaubankMetaData = serde_json::from_value(itaubank_metadata)
            .change_context(errors::ConnectorError::MissingRequiredField {
                field_name: "itaubank_metadata",
            })?;
        Ok(format!(
            "{}itau-ep9-gtw-pix-recebimentos-ext-v2/v2/pix/{}/devolucao/{}",
            self.base_url(connectors),
            pix_data
                .pix_id
                .ok_or(errors::ConnectorError::MissingRequiredField {
                    field_name: "pix_id"
                })?,
            req.request.connector_transaction_id
        ))
    }

    fn build_request(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let auth_details = itaubank::ItaubankAuthType::try_from(&req.connector_auth_type)?;
        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)?)
                .add_certificate(auth_details.certificate)
                .add_certificate_key(auth_details.certificate_key)
                .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: itaubank::RefundResponse = res
            .response
            .parse_struct("itaubank 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 IncomingWebhook for Itaubank {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

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

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

static ITAUBANK_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(|| {
        let mut itaubank_supported_payment_methods = SupportedPaymentMethods::new();
        let supported_capture_methods = vec![enums::CaptureMethod::Automatic];

        itaubank_supported_payment_methods.add(
            enums::PaymentMethod::BankTransfer,
            enums::PaymentMethodType::Pix,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::NotSupported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods,
                specific_features: None,
            },
        );

        itaubank_supported_payment_methods
    });

static ITAUBANK_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "Itaubank",
        description: "Itau Bank is a leading Brazilian financial institution offering a wide range of banking services, including retail banking, loans, and investment solutions.",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Sandbox,
    };

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

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

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

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


pub struct PlacetopayErrorResponse {
    pub status: PlacetopayError,
}


pub fn get_card_vault_data(&self) -> Option<payment_method_data::Card> {
        match self {
            Self::Card(card_data) => Some(card_data.clone()),
            Self::NetworkToken(_network_token_data) => None,
            Self::CardAndNetworkToken(vault_data) => Some(vault_data.card_data.clone()),
        }
    }


File: crates/router/src/types/api/disputes.rs
 Public structs: 6

pub use hyperswitch_interfaces::{
    api::disputes::{
        AcceptDispute, DefendDispute, Dispute, DisputeSync, FetchDisputes, SubmitEvidence,
    },
    disputes::DisputePayload,
};
use masking::{Deserialize, Serialize};

use crate::types;

#[derive(Default, Debug, Deserialize, Serialize)]
pub struct DisputeId {
    pub dispute_id: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DisputeFetchQueryData {
    pub fetch_from: String,
    pub fetch_till: String,
}

pub use hyperswitch_domain_models::router_flow_types::dispute::{
    Accept, Defend, Dsync, Evidence, Fetch,
};

pub use super::disputes_v2::{
    AcceptDisputeV2, DefendDisputeV2, DisputeSyncV2, DisputeV2, FetchDisputesV2, SubmitEvidenceV2,
};

#[derive(Default, Debug, Deserialize, Serialize)]
pub struct DisputeEvidence {
    pub cancellation_policy: Option<String>,
    pub customer_communication: Option<String>,
    pub customer_signature: Option<String>,
    pub receipt: Option<String>,
    pub refund_policy: Option<String>,
    pub service_documentation: Option<String>,
    pub shipping_documentation: Option<String>,
    pub invoice_showing_distinct_transactions: Option<String>,
    pub recurring_transaction_agreement: Option<String>,
    pub uncategorized_file: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
pub struct AttachEvidenceRequest {
    pub create_file_request: types::api::CreateFileRequest,
    pub evidence_type: EvidenceType,
}

#[derive(Debug, serde::Deserialize, strum::Display, strum::EnumString, Clone, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum EvidenceType {
    CancellationPolicy,
    CustomerCommunication,
    CustomerSignature,
    Receipt,
    RefundPolicy,
    ServiceDocumentation,
    ShippingDocumentation,
    InvoiceShowingDistinctTransactions,
    RecurringTransactionAgreement,
    UncategorizedFile,
}

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct ProcessDisputePTData {
    pub connector_name: String,
    pub dispute_payload: types::DisputeSyncResponse,
    pub merchant_id: common_utils::id_type::MerchantId,
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct DisputeListPTData {
    pub connector_name: String,
    pub merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub profile_id: common_utils::id_type::ProfileId,
    pub created_from: time::PrimitiveDateTime,
    pub created_till: time::PrimitiveDateTime,
}


pub async fn contract_based_routing_update_configs(
    state: SessionState,
    request: routing_types::ContractBasedRoutingConfig,
    merchant_context: domain::MerchantContext,
    algorithm_id: common_utils::id_type::RoutingId,
    profile_id: common_utils::id_type::ProfileId,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE.add(
        1,
        router_env::metric_attributes!(
            ("profile_id", profile_id.get_string_repr().to_owned()),
            (
                "algorithm_type",
                routing::DynamicRoutingType::ContractBasedRouting.to_string()
            )
        ),
    );
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let dynamic_routing_algo_to_update = db
        .find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, &algorithm_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;

    let mut config_to_update: routing::ContractBasedRoutingConfig = dynamic_routing_algo_to_update
        .algorithm_data
        .parse_value::<routing::ContractBasedRoutingConfig>("ContractBasedRoutingConfig")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("unable to deserialize algorithm data from routing table into ContractBasedRoutingConfig")?;

    // validate the contained mca_ids
    let mut contained_mca = Vec::new();
    if let Some(info_vec) = &request.label_info {
        for info in info_vec {
            let mca = db
                .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
                    key_manager_state,
                    merchant_context.get_merchant_account().get_id(),
                    &info.mca_id,
                    merchant_context.get_merchant_key_store(),
                )
                .await
                .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                    id: info.mca_id.get_string_repr().to_owned(),
                })?;

            utils::when(mca.connector_name != info.label, || {
                Err(errors::ApiErrorResponse::InvalidRequestData {
                    message: "Incorrect mca configuration received".to_string(),
                })
            })?;

            utils::when(
                contained_mca.iter().any(|mca_id| mca_id == &info.mca_id),
                || {
                    Err(error_stack::Report::new(
                        errors::ApiErrorResponse::InvalidRequestData {
                            message: "Duplicate mca configuration received".to_string(),
                        },
                    ))
                },
            )?;

            contained_mca.push(info.mca_id.to_owned());
        }
    }

    config_to_update.update(request);

    let updated_algorithm_id = common_utils::generate_routing_id_of_default_length();
    let timestamp = common_utils::date_time::now();
    let algo = RoutingAlgorithm {
        algorithm_id: updated_algorithm_id,
        profile_id: dynamic_routing_algo_to_update.profile_id,
        merchant_id: dynamic_routing_algo_to_update.merchant_id,
        name: dynamic_routing_algo_to_update.name,
        description: dynamic_routing_algo_to_update.description,
        kind: dynamic_routing_algo_to_update.kind,
        algorithm_data: serde_json::json!(config_to_update),
        created_at: timestamp,
        modified_at: timestamp,
        algorithm_for: dynamic_routing_algo_to_update.algorithm_for,
        decision_engine_routing_id: None,
    };
    let record = db
        .insert_routing_algorithm(algo)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to insert record in routing algorithm table")?;

    // redact cache for contract based routing configs
    let cache_key = format!(
        "{}_{}",
        profile_id.get_string_repr(),
        algorithm_id.get_string_repr()
    );
    let cache_entries_to_redact = vec![cache::CacheKind::ContractBasedDynamicRoutingCache(
        cache_key.into(),
    )];
    let _ = cache::redact_from_redis_and_publish(
        state.store.get_cache_store().as_ref(),
        cache_entries_to_redact,
    )
    .await
    .map_err(|e| router_env::logger::error!("unable to publish into the redact channel for evicting the contract based routing config cache {e:?}"));

    let new_record = record.foreign_into();

    metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE_SUCCESS_RESPONSE.add(
        1,
        router_env::metric_attributes!(("profile_id", profile_id.get_string_repr().to_owned())),
    );

    state
        .grpc_client
        .dynamic_routing
        .as_ref()
        .async_map(|dr_client| async {
            dr_client
                .contract_based_client
                .invalidate_contracts(
                    profile_id.get_string_repr().into(),
                    state.get_grpc_headers(),
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to invalidate the contract based routing keys")
        })
        .await
        .transpose()?;

    Ok(service_api::ApplicationResponse::Json(new_record))
}


pub async fn retrieve_payment_link(
    state: SessionState,
    payment_link_id: String,
) -> RouterResponse<api_models::payments::RetrievePaymentLinkResponse> {
    let db = &*state.store;
    let payment_link_config = db
        .find_payment_link_by_payment_link_id(&payment_link_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentLinkNotFound)?;

    let session_expiry = payment_link_config.fulfilment_time.unwrap_or_else(|| {
        common_utils::date_time::now()
            .saturating_add(time::Duration::seconds(DEFAULT_SESSION_EXPIRY))
    });

    let status = check_payment_link_status(session_expiry);

    let response = api_models::payments::RetrievePaymentLinkResponse::foreign_from((
        payment_link_config,
        status,
    ));
    Ok(services::ApplicationResponse::Json(response))
}


pub fn spawn_error_handlers(&self, tx: mpsc::Sender<()


impl common_utils::events::ApiEventMetric for RoutingEvaluateRequest {}

impl common_utils::events::ApiEventMetric for for RoutingEvaluateRequest


File: crates/diesel_models/src/query/blocklist.rs
 Public functions: 5

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

use super::generics;
use crate::{
    blocklist::{Blocklist, BlocklistNew},
    schema::blocklist::dsl,
    PgPooledConn, StorageResult,
};

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

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

    pub async fn list_by_merchant_id_data_kind(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        data_kind: common_enums::BlocklistDataKind,
        limit: i64,
        offset: i64,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::data_kind.eq(data_kind.to_owned())),
            Some(limit),
            Some(offset),
            Some(dsl::created_at.desc()),
        )
        .await
    }

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

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


pub fn add_outer_select_column(&mut self, column: impl ToSql<T>) -> QueryResult<()> {
        self.outer_select.push(
            column
                .to_sql(&self.table_engine)
                .change_context(QueryBuildingError::SqlSerializeError)
                .attach_printable("Error serializing outer select column")?,
        );
        Ok(())
    }


pub async fn mock_get_payment_method<'a>(
    state: &routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    card_id: &'a str,
) -> errors::CustomResult<payment_methods::GetPaymentMethodResponse, errors::VaultError> {
    let db = &*state.store;
    let locker_mock_up = db
        .find_locker_by_card_id(card_id)
        .await
        .change_context(errors::VaultError::FetchPaymentMethodFailed)?;
    let dec_data = if let Some(e) = locker_mock_up.enc_card_data {
        decode_and_decrypt_locker_data(state, key_store, e).await
    } else {
        Err(report!(errors::VaultError::FetchPaymentMethodFailed))
    }?;
    let payment_method_response = payment_methods::AddPaymentMethodResponse {
        payment_method_id: locker_mock_up
            .payment_method_id
            .unwrap_or(locker_mock_up.card_id),
        external_id: locker_mock_up.external_id,
        merchant_id: Some(locker_mock_up.merchant_id.to_owned()),
        nickname: locker_mock_up.nickname,
        customer_id: locker_mock_up.customer_id,
        duplicate: locker_mock_up.duplicate,
        payment_method_data: dec_data,
    };
    Ok(payment_methods::GetPaymentMethodResponse {
        payment_method: payment_method_response,
    })
}


impl std::ops::DerefMut for PaymentsMandateReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl std::ops::DerefMut for for PaymentsMandateReference


OpenAPI Block Path: components.schemas.TouchNGoRedirection
{
  "type": "object"
}


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


impl api::RefundSync for Barclaycard {}

impl api::RefundSync for for Barclaycard


impl api::Payment for Checkout {}


pub struct RedirectUrl {
    pub return_url: Option<String>,
    pub url: Option<String>,
}


pub struct GetSsoAuthUrlRequest {
    pub id: String,
}


    let business_profile_new =
        admin::create_profile_from_merchant_account(state, merchant_account, request, key_store)
            .await?;

    let profile_name = business_profile_new.profile_name.clone();

    state
        .store
        .insert_business_profile(&state.into(), key_store, business_profile_new)
        .await
        .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError {
            message: format!(
                "Business Profile with the profile_name {profile_name} already exists"
            ),
        })
        .attach_printable("Failed to insert Business profile because of duplication error")
}

#[cfg(feature = "olap")]
#[async_trait::async_trait]
trait ProfileCreateBridge {
    #[cfg(feature = "v1")]
    async fn create_domain_model_from_request(
        self,
        state: &SessionState,
        merchant_context: &domain::MerchantContext,
    ) -> RouterResult<domain::Profile>;

    #[cfg(feature = "v2")]
    async fn create_domain_model_from_request(
        self,
        state: &SessionState,
        key: &domain::MerchantKeyStore,
        merchant_id: &id_type::MerchantId,
    ) -> RouterResult<domain::Profile>;
}

#[cfg(feature = "olap")]
#[async_trait::async_trait]
impl ProfileCreateBridge for api::ProfileCreate {
    #[cfg(feature = "v1")]
    async fn create_domain_model_from_request(
        self,
        state: &SessionState,
        merchant_context: &domain::MerchantContext,
    ) -> RouterResult<domain::Profile> {
        use common_utils::ext_traits::AsyncExt;

        if let Some(session_expiry) = &self.session_expiry {
            helpers::validate_session_expiry(session_expiry.to_owned())?;
        }

        if let Some(intent_fulfillment_expiry) = self.intent_fulfillment_time {
            helpers::validate_intent_fulfillment_expiry(intent_fulfillment_expiry)?;
        }

        if let Some(ref routing_algorithm) = self.routing_algorithm {
            let _: api_models::routing::StaticRoutingAlgorithm = routing_algorithm
                .clone()
                .parse_value("RoutingAlgorithm")
                .change_context(errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "routing_algorithm",
                })
                .attach_printable("Invalid routing algorithm given")?;
        }

        // Generate a unique profile id
        let profile_id = common_utils::generate_profile_id_of_default_length();
        let profile_name = self.profile_name.unwrap_or("default".to_string());

        let current_time = date_time::now();

        let webhook_details = self.webhook_details.map(ForeignInto::foreign_into);

        let payment_response_hash_key = self
            .payment_response_hash_key
            .or(merchant_context
                .get_merchant_account()
                .payment_response_hash_key
                .clone())
            .unwrap_or(common_utils::crypto::generate_cryptographically_secure_random_string(64));

        let payment_link_config = self.payment_link_config.map(ForeignInto::foreign_into);
        let key_manager_state = state.into();
        let outgoing_webhook_custom_http_headers = self
            .outgoing_webhook_custom_http_headers
            .async_map(|headers| {
                cards::create_encrypted_data(
                    &key_manager_state,
                    merchant_context.get_merchant_key_store(),
                    headers,
                )
            })
            .await
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt outgoing webhook custom HTTP headers")?;

        let payout_link_config = self
            .payout_link_config
            .map(|payout_conf| match payout_conf.config.validate() {
                Ok(_) => Ok(payout_conf.foreign_into()),
                Err(e) => Err(error_stack::report!(
                    errors::ApiErrorResponse::InvalidRequestData {
                        message: e.to_string()
                    }
                )),
            })
            .transpose()?;

        let key = merchant_context
            .get_merchant_key_store()
            .key
            .clone()
            .into_inner();
        let key_manager_state = state.into();

        let card_testing_secret_key = Some(Secret::new(utils::generate_id(
            consts::FINGERPRINT_SECRET_LENGTH,
            "fs",
        )));

        let card_testing_guard_config = self
            .card_testing_guard_config
            .map(CardTestingGuardConfig::foreign_from)
            .or(Some(CardTestingGuardConfig::default()));

        let mut dynamic_routing_algorithm_ref =
            routing_types::DynamicRoutingAlgorithmRef::default();

        if self.is_debit_routing_enabled == Some(true) {
            routing::helpers::create_merchant_in_decision_engine_if_not_exists(
                state,
                &profile_id,
                &mut dynamic_routing_algorithm_ref,
            )
            .await;
        }

        let dynamic_routing_algorithm = serde_json::to_value(dynamic_routing_algorithm_ref)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("error serializing dynamic_routing_algorithm_ref to JSON Value")?;

        self.merchant_country_code
            .as_ref()
            .map(|country_code| country_code.validate_and_get_country_from_merchant_country_code())
            .transpose()
            .change_context(errors::ApiErrorResponse::InvalidRequestData {
                message: "Invalid merchant country code".to_string(),
            })?;

        Ok(domain::Profile::from(domain::ProfileSetter {
            profile_id,
            merchant_id: merchant_context.get_merchant_account().get_id().clone(),
            profile_name,
            created_at: current_time,
            modified_at: current_time,
            return_url: self
                .return_url
                .map(|return_url| return_url.to_string())
                .or(merchant_context.get_merchant_account().return_url.clone()),
            enable_payment_response_hash: self.enable_payment_response_hash.unwrap_or(
                merchant_context
                    .get_merchant_account()
                    .enable_payment_response_hash,
            ),
            payment_response_hash_key: Some(payment_response_hash_key),
            redirect_to_merchant_with_http_post: self
                .redirect_to_merchant_with_http_post
                .unwrap_or(
                    merchant_context
                        .get_merchant_account()
                        .redirect_to_merchant_with_http_post,
                ),
            webhook_details: webhook_details.or(merchant_context
                .get_merchant_account()
                .webhook_details
                .clone()),
            metadata: self.metadata,
            routing_algorithm: None,
            intent_fulfillment_time: self
                .intent_fulfillment_time
                .map(i64::from)
                .or(merchant_context
                    .get_merchant_account()
                    .intent_fulfillment_time)
                .or(Some(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME)),
            frm_routing_algorithm: self.frm_routing_algorithm.or(merchant_context
                .get_merchant_account()
                .frm_routing_algorithm
                .clone()),
            #[cfg(feature = "payouts")]
            payout_routing_algorithm: self.payout_routing_algorithm.or(merchant_context
                .get_merchant_account()
                .payout_routing_algorithm
                .clone()),
            #[cfg(not(feature = "payouts"))]
            payout_routing_algorithm: None,
            is_recon_enabled: merchant_context.get_merchant_account().is_recon_enabled,
            applepay_verified_domains: self.applepay_verified_domains,
            payment_link_config,
            session_expiry: self
                .session_expiry
                .map(i64::from)
                .or(Some(common_utils::consts::DEFAULT_SESSION_EXPIRY)),
            authentication_connector_details: self
                .authentication_connector_details
                .map(ForeignInto::foreign_into),
            payout_link_config,
            is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled,
            is_extended_card_info_enabled: None,
            extended_card_info_config: None,
            use_billing_as_payment_method_billing: self
                .use_billing_as_payment_method_billing
                .or(Some(true)),
            collect_shipping_details_from_wallet_connector: self
                .collect_shipping_details_from_wallet_connector
                .or(Some(false)),
            collect_billing_details_from_wallet_connector: self
                .collect_billing_details_from_wallet_connector
                .or(Some(false)),
            outgoing_webhook_custom_http_headers,
            tax_connector_id: self.tax_connector_id,
            is_tax_connector_enabled: self.is_tax_connector_enabled,
            always_collect_billing_details_from_wallet_connector: self
                .always_collect_billing_details_from_wallet_connector,
            always_collect_shipping_details_from_wallet_connector: self
                .always_collect_shipping_details_from_wallet_connector,
            dynamic_routing_algorithm: Some(dynamic_routing_algorithm),
            is_network_tokenization_enabled: self.is_network_tokenization_enabled,
            is_auto_retries_enabled: self.is_auto_retries_enabled.unwrap_or_default(),
            max_auto_retries_enabled: self.max_auto_retries_enabled.map(i16::from),
            always_request_extended_authorization: self.always_request_extended_authorization,
            is_click_to_pay_enabled: self.is_click_to_pay_enabled,
            authentication_product_ids: self.authentication_product_ids,
            card_testing_guard_config,
            card_testing_secret_key: card_testing_secret_key
                .async_lift(|inner| async {
                    domain_types::crypto_operation(
                        &key_manager_state,
                        common_utils::type_name!(domain::Profile),
                        domain_types::CryptoOperation::EncryptOptional(inner),
                        km_types::Identifier::Merchant(
                            merchant_context
                                .get_merchant_key_store()
                                .merchant_id
                                .clone(),
                        ),
                        key.peek(),
                    )
                    .await
                    .and_then(|val| val.try_into_optionaloperation())
                })
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("error while generating card testing secret key")?,
            is_clear_pan_retries_enabled: self.is_clear_pan_retries_enabled.unwrap_or_default(),
            force_3ds_challenge: self.force_3ds_challenge.unwrap_or_default(),
            is_debit_routing_enabled: self.is_debit_routing_enabled.unwrap_or_default(),
            merchant_business_country: self.merchant_business_country,
            is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
            is_pre_network_tokenization_enabled: self
                .is_pre_network_tokenization_enabled
                .unwrap_or_default(),
            merchant_category_code: self.merchant_category_code,
            merchant_country_code: self.merchant_country_code,
            dispute_polling_interval: self.dispute_polling_interval,
            is_manual_retry_enabled: self.is_manual_retry_enabled,
        }))
    }

    #[cfg(feature = "v2")]
    async fn create_domain_model_from_request(
        self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        merchant_id: &id_type::MerchantId,
    ) -> RouterResult<domain::Profile> {
        if let Some(session_expiry) = &self.session_expiry {
            helpers::validate_session_expiry(session_expiry.to_owned())?;
        }

        // Generate a unique profile id
        // TODO: the profile_id should be generated from the profile_name
        let profile_id = common_utils::generate_profile_id_of_default_length();
        let profile_name = self.profile_name;

        let current_time = date_time::now();

        let webhook_details = self.webhook_details.map(ForeignInto::foreign_into);

        let payment_response_hash_key = self
            .payment_response_hash_key
            .unwrap_or(common_utils::crypto::generate_cryptographically_secure_random_string(64));

        let payment_link_config = self.payment_link_config.map(ForeignInto::foreign_into);
        let key_manager_state = state.into();
        let outgoing_webhook_custom_http_headers = self
            .outgoing_webhook_custom_http_headers
            .async_map(|headers| {
                cards::create_encrypted_data(&key_manager_state, key_store, headers)
            })
            .await
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt outgoing webhook custom HTTP headers")?;

        let payout_link_config = self
            .payout_link_config
            .map(|payout_conf| match payout_conf.config.validate() {
                Ok(_) => Ok(payout_conf.foreign_into()),
                Err(e) => Err(error_stack::report!(
                    errors::ApiErrorResponse::InvalidRequestData {
                        message: e.to_string()
                    }
                )),
            })
            .transpose()?;

        let key = key_store.key.clone().into_inner();
        let key_manager_state = state.into();

        let card_testing_secret_key = Some(Secret::new(utils::generate_id(
            consts::FINGERPRINT_SECRET_LENGTH,
            "fs",
        )));

        let card_testing_guard_config = self
            .card_testing_guard_config
            .map(CardTestingGuardConfig::foreign_from)
            .or(Some(CardTestingGuardConfig::default()));

        Ok(domain::Profile::from(domain::ProfileSetter {
            id: profile_id,
            merchant_id: merchant_id.clone(),
            profile_name,
            created_at: current_time,
            modified_at: current_time,
            return_url: self.return_url,
            enable_payment_response_hash: self.enable_payment_response_hash.unwrap_or(true),
            payment_response_hash_key: Some(payment_response_hash_key),
            redirect_to_merchant_with_http_post: self
                .redirect_to_merchant_with_http_post
                .unwrap_or(true),
            webhook_details,
            metadata: self.metadata,
            is_recon_enabled: false,
            applepay_verified_domains: self.applepay_verified_domains,
            payment_link_config,
            session_expiry: self
                .session_expiry
                .map(i64::from)
                .or(Some(common_utils::consts::DEFAULT_SESSION_EXPIRY)),
            authentication_connector_details: self
                .authentication_connector_details
                .map(ForeignInto::foreign_into),
            payout_link_config,
            is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled,
            is_extended_card_info_enabled: None,
            extended_card_info_config: None,
            use_billing_as_payment_method_billing: self
                .use_billing_as_payment_method_billing
                .or(Some(true)),
            collect_shipping_details_from_wallet_connector: self
                .collect_shipping_details_from_wallet_connector_if_required
                .or(Some(false)),
            collect_billing_details_from_wallet_connector: self
                .collect_billing_details_from_wallet_connector_if_required
                .or(Some(false)),
            outgoing_webhook_custom_http_headers,
            always_collect_billing_details_from_wallet_connector: self
                .always_collect_billing_details_from_wallet_connector,
            always_collect_shipping_details_from_wallet_connector: self
                .always_collect_shipping_details_from_wallet_connector,
            routing_algorithm_id: None,
            frm_routing_algorithm_id: None,
            payout_routing_algorithm_id: None,
            order_fulfillment_time: self
                .order_fulfillment_time
                .map(|order_fulfillment_time| order_fulfillment_time.into_inner())
                .or(Some(common_utils::consts::DEFAULT_ORDER_FULFILLMENT_TIME)),
            order_fulfillment_time_origin: self.order_fulfillment_time_origin,
            default_fallback_routing: None,
            should_collect_cvv_during_payment: None,
            tax_connector_id: self.tax_connector_id,
            is_tax_connector_enabled: self.is_tax_connector_enabled,
            is_network_tokenization_enabled: self.is_network_tokenization_enabled,
            is_click_to_pay_enabled: self.is_click_to_pay_enabled,
            authentication_product_ids: self.authentication_product_ids,
            three_ds_decision_manager_config: None,
            card_testing_guard_config,
            card_testing_secret_key: card_testing_secret_key
                .async_lift(|inner| async {
                    domain_types::crypto_operation(
                        &key_manager_state,
                        common_utils::type_name!(domain::Profile),
                        domain_types::CryptoOperation::EncryptOptional(inner),
                        km_types::Identifier::Merchant(key_store.merchant_id.clone()),
                        key.peek(),
                    )
                    .await
                    .and_then(|val| val.try_into_optionaloperation())
                })
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("error while generating card testing secret key")?,
            is_clear_pan_retries_enabled: self.is_clear_pan_retries_enabled.unwrap_or_default(),
            is_debit_routing_enabled: self.is_debit_routing_enabled.unwrap_or_default(),
            merchant_business_country: self.merchant_business_country,
            revenue_recovery_retry_algorithm_type: None,
            revenue_recovery_retry_algorithm_data: None,
            is_iframe_redirection_enabled: None,
            is_external_vault_enabled: self.is_external_vault_enabled,
            external_vault_connector_details: self
                .external_vault_connector_details
                .map(ForeignInto::foreign_into),
            merchant_category_code: self.merchant_category_code,
            merchant_country_code: self.merchant_country_code,
            split_txns_enabled: self.split_txns_enabled.unwrap_or_default(),
        }))
    }
}

#[cfg(feature = "olap")]
pub async fn create_profile(
    state: SessionState,
    request: api::ProfileCreate,
    merchant_context: domain::MerchantContext,
) -> RouterResponse<api_models::admin::ProfileResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    #[cfg(feature = "v1")]
    let business_profile = request
        .create_domain_model_from_request(&state, &merchant_context)
        .await?;

    #[cfg(feature = "v2")]
    let business_profile = request
        .create_domain_model_from_request(
            &state,
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().get_id(),
        )
        .await?;

    let profile_id = business_profile.get_id().to_owned();

    let business_profile = db
        .insert_business_profile(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            business_profile,
        )
        .await
        .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError {
            message: format!(
                "Business Profile with the profile_id {} already exists",
                profile_id.get_string_repr()
            ),
        })
        .attach_printable("Failed to insert Business profile because of duplication error")?;

    #[cfg(feature = "v1")]
    if merchant_context
        .get_merchant_account()
        .default_profile
        .is_some()
    {
        let unset_default_profile = domain::MerchantAccountUpdate::UnsetDefaultProfile;
        db.update_merchant(
            key_manager_state,
            merchant_context.get_merchant_account().clone(),
            unset_default_profile,
            merchant_context.get_merchant_key_store(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
    }

    Ok(service_api::ApplicationResponse::Json(
        api_models::admin::ProfileResponse::foreign_try_from(business_profile)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to parse business profile details")?,
    ))
}

#[cfg(feature = "olap")]
pub async fn list_profile(
    state: SessionState,
    merchant_id: id_type::MerchantId,
    profile_id_list: Option<Vec<id_type::ProfileId>>,
) -> RouterResponse<Vec<api_models::admin::ProfileResponse>> {
    let db = state.store.as_ref();
    let key_store = db
        .get_merchant_key_store_by_merchant_id(
            &(&state).into(),
            &merchant_id,
            &db.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
    let profiles = db
        .list_profile_by_merchant_id(&(&state).into(), &key_store, &merchant_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?
        .clone();
    let profiles = core_utils::filter_objects_based_on_profile_id_list(profile_id_list, profiles);
    let mut business_profiles = Vec::new();
    for profile in profiles {
        let business_profile = api_models::admin::ProfileResponse::foreign_try_from(profile)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to parse business profile details")?;
        business_profiles.push(business_profile);
    }

    Ok(service_api::ApplicationResponse::Json(business_profiles))
}

pub async fn retrieve_profile(
    state: SessionState,
    profile_id: id_type::ProfileId,
    key_store: domain::MerchantKeyStore,
) -> RouterResponse<api_models::admin::ProfileResponse> {
    let db = state.store.as_ref();

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

    Ok(service_api::ApplicationResponse::Json(
        api_models::admin::ProfileResponse::foreign_try_from(business_profile)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to parse business profile details")?,
    ))
}

pub async fn delete_profile(
    state: SessionState,
    profile_id: id_type::ProfileId,
    merchant_id: &id_type::MerchantId,
) -> RouterResponse<bool> {
    let db = state.store.as_ref();
    let delete_result = db
        .delete_profile_by_profile_id_merchant_id(&profile_id, merchant_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    Ok(service_api::ApplicationResponse::Json(delete_result))
}

#[cfg(feature = "olap")]
#[async_trait::async_trait]
trait ProfileUpdateBridge {
    async fn get_update_profile_object(
        self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        business_profile: &domain::Profile,
    ) -> RouterResult<domain::ProfileUpdate>;
}

#[cfg(all(feature = "olap", feature = "v1"))]
#[async_trait::async_trait]
impl ProfileUpdateBridge for api::ProfileUpdate {
    async fn get_update_profile_object(
        self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        business_profile: &domain::Profile,
    ) -> RouterResult<domain::ProfileUpdate> {
        if let Some(session_expiry) = &self.session_expiry {
            helpers::validate_session_expiry(session_expiry.to_owned())?;
        }

        if let Some(intent_fulfillment_expiry) = self.intent_fulfillment_time {
            helpers::validate_intent_fulfillment_expiry(intent_fulfillment_expiry)?;
        }

        let webhook_details = self.webhook_details.map(ForeignInto::foreign_into);

        if let Some(ref routing_algorithm) = self.routing_algorithm {
            let _: api_models::routing::StaticRoutingAlgorithm = routing_algorithm
                .clone()
                .parse_value("RoutingAlgorithm")
                .change_context(errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "routing_algorithm",
                })
                .attach_printable("Invalid routing algorithm given")?;
        }

        let payment_link_config = self
            .payment_link_config
            .map(|payment_link_conf| match payment_link_conf.validate() {
                Ok(_) => Ok(payment_link_conf.foreign_into()),
                Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                    message: e.to_string()
                })),
            })
            .transpose()?;

        let extended_card_info_config = self
            .extended_card_info_config
            .as_ref()
            .map(|config| {
                config.encode_to_value().change_context(
                    errors::ApiErrorResponse::InvalidDataValue {
                        field_name: "extended_card_info_config",
                    },
                )
            })
            .transpose()?
            .map(Secret::new);
        let key_manager_state = state.into();
        let outgoing_webhook_custom_http_headers = self
            .outgoing_webhook_custom_http_headers
            .async_map(|headers| {
                cards::create_encrypted_data(&key_manager_state, key_store, headers)
            })
            .await
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt outgoing webhook custom HTTP headers")?;

        let payout_link_config = self
            .payout_link_config
            .map(|payout_conf| match payout_conf.config.validate() {
                Ok(_) => Ok(payout_conf.foreign_into()),
                Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                    message: e.to_string()
                })),
            })
            .transpose()?;

        let key = key_store.key.clone().into_inner();
        let key_manager_state = state.into();

        let card_testing_secret_key = match business_profile.card_testing_secret_key {
            Some(_) => None,
            None => {
                let card_testing_secret_key = Some(Secret::new(utils::generate_id(
                    consts::FINGERPRINT_SECRET_LENGTH,
                    "fs",
                )));

                card_testing_secret_key
                    .async_lift(|inner| async {
                        domain_types::crypto_operation(
                            &key_manager_state,
                            common_utils::type_name!(domain::Profile),
                            domain_types::CryptoOperation::EncryptOptional(inner),
                            km_types::Identifier::Merchant(key_store.merchant_id.clone()),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("error while generating card testing secret key")?
            }
        };

        let dynamic_routing_algo_ref = if self.is_debit_routing_enabled == Some(true) {
            let mut dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef =
                business_profile
                    .dynamic_routing_algorithm
                    .clone()
                    .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
                    .transpose()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable(
                        "unable to deserialize dynamic routing algorithm ref from business profile",
                    )?
                    .unwrap_or_default();

            routing::helpers::create_merchant_in_decision_engine_if_not_exists(
                state,
                business_profile.get_id(),
                &mut dynamic_routing_algo_ref,
            )
            .await;

            let dynamic_routing_algo_ref_value = serde_json::to_value(dynamic_routing_algo_ref)
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable(
                    "error serializing dynamic_routing_algorithm_ref to JSON Value",
                )?;

            Some(dynamic_routing_algo_ref_value)
        } else {
            self.dynamic_routing_algorithm
        };

        self.merchant_country_code
            .as_ref()
            .map(|country_code| country_code.validate_and_get_country_from_merchant_country_code())
            .transpose()
            .change_context(errors::ApiErrorResponse::InvalidRequestData {
                message: "Invalid merchant country code".to_string(),
            })?;

        Ok(domain::ProfileUpdate::Update(Box::new(
            domain::ProfileGeneralUpdate {
                profile_name: self.profile_name,
                return_url: self.return_url.map(|return_url| return_url.to_string()),
                enable_payment_response_hash: self.enable_payment_response_hash,
                payment_response_hash_key: self.payment_response_hash_key,
                redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post,
                webhook_details,
                metadata: self.metadata,
                routing_algorithm: self.routing_algorithm,
                intent_fulfillment_time: self.intent_fulfillment_time.map(i64::from),
                frm_routing_algorithm: self.frm_routing_algorithm,
                #[cfg(feature = "payouts")]
                payout_routing_algorithm: self.payout_routing_algorithm,
                #[cfg(not(feature = "payouts"))]
                payout_routing_algorithm: None,
                applepay_verified_domains: self.applepay_verified_domains,
                payment_link_config,
                session_expiry: self.session_expiry.map(i64::from),
                authentication_connector_details: self
                    .authentication_connector_details
                    .map(ForeignInto::foreign_into),
                payout_link_config,
                extended_card_info_config,
                use_billing_as_payment_method_billing: self.use_billing_as_payment_method_billing,
                collect_shipping_details_from_wallet_connector: self
                    .collect_shipping_details_from_wallet_connector,
                collect_billing_details_from_wallet_connector: self
                    .collect_billing_details_from_wallet_connector,
                is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled,
                outgoing_webhook_custom_http_headers,
                always_collect_billing_details_from_wallet_connector: self
                    .always_collect_billing_details_from_wallet_connector,
                always_collect_shipping_details_from_wallet_connector: self
                    .always_collect_shipping_details_from_wallet_connector,
                always_request_extended_authorization: self.always_request_extended_authorization,
                tax_connector_id: self.tax_connector_id,
                is_tax_connector_enabled: self.is_tax_connector_enabled,
                dynamic_routing_algorithm: dynamic_routing_algo_ref,
                is_network_tokenization_enabled: self.is_network_tokenization_enabled,
                is_auto_retries_enabled: self.is_auto_retries_enabled,
                max_auto_retries_enabled: self.max_auto_retries_enabled.map(i16::from),
                is_click_to_pay_enabled: self.is_click_to_pay_enabled,
                authentication_product_ids: self.authentication_product_ids,
                card_testing_guard_config: self
                    .card_testing_guard_config
                    .map(ForeignInto::foreign_into),
                card_testing_secret_key,
                is_clear_pan_retries_enabled: self.is_clear_pan_retries_enabled,
                force_3ds_challenge: self.force_3ds_challenge, //
                is_debit_routing_enabled: self.is_debit_routing_enabled,
                merchant_business_country: self.merchant_business_country,
                is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
                is_pre_network_tokenization_enabled: self.is_pre_network_tokenization_enabled,
                merchant_category_code: self.merchant_category_code,
                merchant_country_code: self.merchant_country_code,
                dispute_polling_interval: self.dispute_polling_interval,
                is_manual_retry_enabled: self.is_manual_retry_enabled,
            },
        )))
    }
}

#[cfg(all(feature = "olap", feature = "v2"))]
#[async_trait::async_trait]
impl ProfileUpdateBridge for api::ProfileUpdate {
    async fn get_update_profile_object(
        self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        business_profile: &domain::Profile,
    ) -> RouterResult<domain::ProfileUpdate> {
        if let Some(session_expiry) = &self.session_expiry {
            helpers::validate_session_expiry(session_expiry.to_owned())?;
        }

        let webhook_details = self.webhook_details.map(ForeignInto::foreign_into);

        let payment_link_config = self
            .payment_link_config
            .map(|payment_link_conf| match payment_link_conf.validate() {
                Ok(_) => Ok(payment_link_conf.foreign_into()),
                Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                    message: e.to_string()
                })),
            })
            .transpose()?;

        let extended_card_info_config = self
            .extended_card_info_config
            .as_ref()
            .map(|config| {
                config.encode_to_value().change_context(
                    errors::ApiErrorResponse::InvalidDataValue {
                        field_name: "extended_card_info_config",
                    },
                )
            })
            .transpose()?
            .map(Secret::new);
        let key_manager_state = state.into();
        let outgoing_webhook_custom_http_headers = self
            .outgoing_webhook_custom_http_headers
            .async_map(|headers| {
                cards::create_encrypted_data(&key_manager_state, key_store, headers)
            })
            .await
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt outgoing webhook custom HTTP headers")?;

        let payout_link_config = self
            .payout_link_config
            .map(|payout_conf| match payout_conf.config.validate() {
                Ok(_) => Ok(payout_conf.foreign_into()),
                Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                    message: e.to_string()
                })),
            })
            .transpose()?;

        let key = key_store.key.clone().into_inner();
        let key_manager_state = state.into();

        let card_testing_secret_key = match business_profile.card_testing_secret_key {
            Some(_) => None,
            None => {
                let card_testing_secret_key = Some(Secret::new(utils::generate_id(
                    consts::FINGERPRINT_SECRET_LENGTH,
                    "fs",
                )));

                card_testing_secret_key
                    .async_lift(|inner| async {
                        domain_types::crypto_operation(
                            &key_manager_state,
                            common_utils::type_name!(domain::Profile),
                            domain_types::CryptoOperation::EncryptOptional(inner),
                            km_types::Identifier::Merchant(key_store.merchant_id.clone()),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("error while generating card testing secret key")?
            }
        };

        let revenue_recovery_retry_algorithm_type = self.revenue_recovery_retry_algorithm_type;

        Ok(domain::ProfileUpdate::Update(Box::new(
            domain::ProfileGeneralUpdate {
                profile_name: self.profile_name,
                return_url: self.return_url,
                enable_payment_response_hash: self.enable_payment_response_hash,
                payment_response_hash_key: self.payment_response_hash_key,
                redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post,
                webhook_details,
                metadata: self.metadata,
                applepay_verified_domains: self.applepay_verified_domains,
                payment_link_config,
                session_expiry: self.session_expiry.map(i64::from),
                authentication_connector_details: self
                    .authentication_connector_details
                    .map(ForeignInto::foreign_into),
                payout_link_config,
                extended_card_info_config,
                use_billing_as_payment_method_billing: self.use_billing_as_payment_method_billing,
                collect_shipping_details_from_wallet_connector: self
                    .collect_shipping_details_from_wallet_connector_if_required,
                collect_billing_details_from_wallet_connector: self
                    .collect_billing_details_from_wallet_connector_if_required,
                is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled,
                outgoing_webhook_custom_http_headers,
                order_fulfillment_time: self
                    .order_fulfillment_time
                    .map(|order_fulfillment_time| order_fulfillment_time.into_inner()),
                order_fulfillment_time_origin: self.order_fulfillment_time_origin,
                always_collect_billing_details_from_wallet_connector: self
                    .always_collect_billing_details_from_wallet_connector,
                always_collect_shipping_details_from_wallet_connector: self
                    .always_collect_shipping_details_from_wallet_connector,
                is_network_tokenization_enabled: self.is_network_tokenization_enabled,
                is_click_to_pay_enabled: self.is_click_to_pay_enabled,
                authentication_product_ids: self.authentication_product_ids,
                three_ds_decision_manager_config: None,
                card_testing_guard_config: self
                    .card_testing_guard_config
                    .map(ForeignInto::foreign_into),
                card_testing_secret_key,
                is_debit_routing_enabled: self.is_debit_routing_enabled,
                merchant_business_country: self.merchant_business_country,
                is_iframe_redirection_enabled: None,
                is_external_vault_enabled: self.is_external_vault_enabled,
                external_vault_connector_details: self
                    .external_vault_connector_details
                    .map(ForeignInto::foreign_into),
                merchant_category_code: self.merchant_category_code,
                merchant_country_code: self.merchant_country_code,
                revenue_recovery_retry_algorithm_type,
                split_txns_enabled: self.split_txns_enabled,
            },
        )))
    }
}

#[cfg(feature = "olap")]
pub async fn update_profile(
    state: SessionState,
    profile_id: &id_type::ProfileId,
    key_store: domain::MerchantKeyStore,
    request: api::ProfileUpdate,
) -> RouterResponse<api::ProfileResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let business_profile = db
        .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    let profile_update = request
        .get_update_profile_object(&state, &key_store, &business_profile)
        .await?;

    let updated_business_profile = db
        .update_profile_by_profile_id(
            key_manager_state,
            &key_store,
            business_profile,
            profile_update,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    Ok(service_api::ApplicationResponse::Json(
        api_models::admin::ProfileResponse::foreign_try_from(updated_business_profile)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to parse business profile details")?,
    ))
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug)]
pub struct ProfileWrapper {
    pub profile: domain::Profile,
}

#[cfg(feature = "v2")]
impl ProfileWrapper {
    pub fn new(profile: domain::Profile) -> Self {
        Self { profile }
    }
    fn get_routing_config_cache_key(self) -> storage_impl::redis::cache::CacheKind<'static> {
        let merchant_id = self.profile.merchant_id.clone();

        let profile_id = self.profile.get_id().to_owned();

        storage_impl::redis::cache::CacheKind::Routing(
            format!(
                "routing_config_{}_{}",
                merchant_id.get_string_repr(),
                profile_id.get_string_repr()
            )
            .into(),
        )
    }

    pub async fn update_profile_and_invalidate_routing_config_for_active_algorithm_id_update(
        self,
        db: &dyn StorageInterface,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        algorithm_id: id_type::RoutingId,
        transaction_type: &storage::enums::TransactionType,
    ) -> RouterResult<()> {
        let routing_cache_key = self.clone().get_routing_config_cache_key();

        let (routing_algorithm_id, payout_routing_algorithm_id) = match transaction_type {
            storage::enums::TransactionType::Payment => (Some(algorithm_id), None),
            #[cfg(feature = "payouts")]
            storage::enums::TransactionType::Payout => (None, Some(algorithm_id)),
            //TODO: Handle ThreeDsAuthentication Transaction Type for Three DS Decision Rule Algorithm configuration
            storage::enums::TransactionType::ThreeDsAuthentication => todo!(),
        };

        let profile_update = domain::ProfileUpdate::RoutingAlgorithmUpdate {
            routing_algorithm_id,
            payout_routing_algorithm_id,
        };


/// Get string representation of the id
    pub fn get_string_repr(&self) -> &str {
        self.0.get_string_repr()
    }


pub struct SetupMandate;


pub async fn get_auth_events_filter_for_dimension<T>(
    dimension: AuthEventDimensions,
    auth: &AuthInfo,
    time_range: &TimeRange,
    pool: &T,
) -> FiltersResult<Vec<AuthEventFilterRow>>
where
    T: AnalyticsDataSource + AuthEventFilterAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    let mut query_builder: QueryBuilder<T> =
        QueryBuilder::new(AnalyticsCollection::Authentications);

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

    query_builder.set_distinct();

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

    query_builder
        .execute_query::<AuthEventFilterRow, _>(pool)
        .await
        .change_context(FiltersError::QueryBuildingError)?
        .change_context(FiltersError::QueryExecutionFailure)
}


impl api::PaymentVoid for Klarna {}