        data: &PaymentsPreProcessingRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsPreProcessingRouterData, errors::ConnectorError> {
        let response: adyen::AdyenBalanceResponse = res
            .response
            .parse_struct("AdyenBalanceResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        let currency = match data.request.currency {
            Some(currency) => currency,
            None => Err(errors::ConnectorError::MissingRequiredField {
                field_name: "currency",
            })?,
        };
        let amount = match data.request.minor_amount {
            Some(amount) => amount,
            None => Err(errors::ConnectorError::MissingRequiredField {
                field_name: "amount",
            })?,
        };

        let amount = convert_amount(self.amount_converter, amount, currency)?;

        if response.balance.currency != currency || response.balance.value < amount {
            Ok(RouterData {
                response: Err(ErrorResponse {
                    code: NO_ERROR_CODE.to_string(),
                    message: NO_ERROR_MESSAGE.to_string(),
                    reason: Some(constants::LOW_BALANCE_ERROR_MESSAGE.to_string()),
                    status_code: res.status_code,
                    attempt_status: Some(enums::AttemptStatus::Failure),
                    connector_transaction_id: Some(response.psp_reference),
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                }),
                ..data.clone()
            })
        } else {
            RouterData::try_from(ResponseRouterData {
                response,
                data: data.clone(),
                http_code: res.status_code,
            })
            .change_context(errors::ConnectorError::ResponseHandlingFailed)
        }
    }

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

    fn get_5xx_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 Adyen {
    fn get_headers(
        &self,
        req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PaymentsAuthorizeType::get_content_type(self)
                .to_string()
                .into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

    fn get_url(
        &self,
        req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let id = req.request.connector_transaction_id.clone();

        let endpoint = build_env_specific_endpoint(
            self.base_url(connectors),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!(
            "{endpoint}{ADYEN_API_VERSION}/payments/{id}/cancels",
        ))
    }

    fn get_request_body(
        &self,
        req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = adyen::AdyenCancelRequest::try_from(req)?;

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

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

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

impl api::Payouts for Adyen {}
#[cfg(feature = "payouts")]
impl api::PayoutCancel for Adyen {}
#[cfg(feature = "payouts")]
impl api::PayoutCreate for Adyen {}
#[cfg(feature = "payouts")]
impl api::PayoutEligibility for Adyen {}
#[cfg(feature = "payouts")]
impl api::PayoutFulfill for Adyen {}

#[cfg(feature = "payouts")]
impl ConnectorIntegration<PoCancel, PayoutsData, PayoutsResponseData> for Adyen {
    fn get_url(
        &self,
        req: &PayoutsRouterData<PoCancel>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let endpoint = build_env_specific_endpoint(
            connectors.adyen.payout_base_url.as_str(),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!(
            "{endpoint}pal/servlet/Payout/{ADYEN_API_VERSION}/declineThirdParty",
        ))
    }

    fn get_headers(
        &self,
        req: &PayoutsRouterData<PoCancel>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PayoutCancelType::get_content_type(self).to_string().into(),
        )];
        let auth = adyen::AdyenAuthType::try_from(&req.connector_auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        let mut api_key = vec![(
            headers::X_API_KEY.to_string(),
            auth.review_key.unwrap_or(auth.api_key).into_masked(),
        )];
        header.append(&mut api_key);
        Ok(header)
    }

    fn get_request_body(
        &self,
        req: &PayoutsRouterData<PoCancel>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = adyen::AdyenPayoutCancelRequest::try_from(req)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

        Ok(Some(request))
    }

    #[instrument(skip_all)]
    fn handle_response(
        &self,
        data: &PayoutsRouterData<PoCancel>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PayoutsRouterData<PoCancel>, errors::ConnectorError> {
        let response: adyen::AdyenPayoutResponse = res
            .response
            .parse_struct("AdyenPayoutResponse")
            .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)
    }
    fn get_5xx_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

#[cfg(feature = "payouts")]
impl ConnectorIntegration<PoCreate, PayoutsData, PayoutsResponseData> for Adyen {
    fn get_url(
        &self,
        req: &PayoutsRouterData<PoCreate>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let endpoint = build_env_specific_endpoint(
            connectors.adyen.payout_base_url.as_str(),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!(
            "{endpoint}pal/servlet/Payout/{ADYEN_API_VERSION}/storeDetailAndSubmitThirdParty",
        ))
    }

    fn get_headers(
        &self,
        req: &PayoutsRouterData<PoCreate>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PayoutCreateType::get_content_type(self).to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

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

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

        Ok(Some(request))
    }

    #[instrument(skip_all)]
    fn handle_response(
        &self,
        data: &PayoutsRouterData<PoCreate>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PayoutsRouterData<PoCreate>, errors::ConnectorError> {
        let response: adyen::AdyenPayoutResponse = res
            .response
            .parse_struct("AdyenPayoutResponse")
            .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)
    }
    fn get_5xx_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

#[cfg(feature = "payouts")]
impl ConnectorIntegration<PoEligibility, PayoutsData, PayoutsResponseData> for Adyen {
    fn get_url(
        &self,
        req: &PayoutsRouterData<PoEligibility>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let endpoint = build_env_specific_endpoint(
            self.base_url(connectors),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!("{endpoint}{ADYEN_API_VERSION}/payments"))
    }

    fn get_headers(
        &self,
        req: &PayoutsRouterData<PoEligibility>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PayoutEligibilityType::get_content_type(self)
                .to_string()
                .into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

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

        let connector_router_data = adyen::AdyenRouterData::try_from((amount, req))?;
        let connector_req = adyen::AdyenPayoutEligibilityRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

        Ok(Some(request))
    }

    #[instrument(skip_all)]
    fn handle_response(
        &self,
        data: &PayoutsRouterData<PoEligibility>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PayoutsRouterData<PoEligibility>, errors::ConnectorError> {
        let response: adyen::AdyenPayoutResponse = res
            .response
            .parse_struct("AdyenPayoutResponse")
            .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)
    }
    fn get_5xx_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

#[cfg(feature = "payouts")]
impl ConnectorIntegration<PoFulfill, PayoutsData, PayoutsResponseData> for Adyen {
    fn get_url(
        &self,
        req: &PayoutsRouterData<PoFulfill>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let endpoint = build_env_specific_endpoint(
            connectors.adyen.payout_base_url.as_str(),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        let payout_type = req.request.get_payout_type()?;
        Ok(format!(
            "{}pal/servlet/Payout/{}/{}",
            endpoint,
            ADYEN_API_VERSION,
            match payout_type {
                enums::PayoutType::Bank | enums::PayoutType::Wallet =>
                    "confirmThirdParty".to_string(),
                enums::PayoutType::Card => "payout".to_string(),
            }
        ))
    }

    fn get_headers(
        &self,
        req: &PayoutsRouterData<PoFulfill>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PayoutFulfillType::get_content_type(self).to_string().into(),
        )];
        let auth = adyen::AdyenAuthType::try_from(&req.connector_auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        let payout_type = req
            .request
            .payout_type
            .to_owned()
            .get_required_value("payout_type")
            .change_context(errors::ConnectorError::MissingRequiredField {
                field_name: "payout_type",
            })?;
        let mut api_key = vec![(
            headers::X_API_KEY.to_string(),
            match payout_type {
                enums::PayoutType::Bank | enums::PayoutType::Wallet => {
                    auth.review_key.unwrap_or(auth.api_key).into_masked()
                }
                enums::PayoutType::Card => auth.api_key.into_masked(),
            },
        )];
        header.append(&mut api_key);
        Ok(header)
    }

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

        let connector_router_data = adyen::AdyenRouterData::try_from((amount, req))?;
        let connector_req = adyen::AdyenPayoutFulfillRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

        Ok(Some(request))
    }

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

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

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Adyen {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            RefundExecuteType::get_content_type(self).to_string().into(),
        )];
        let mut api_header = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_header);
        Ok(header)
    }

    fn get_url(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_payment_id = req.request.connector_transaction_id.clone();

        let endpoint = build_env_specific_endpoint(
            self.base_url(connectors),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!(
            "{endpoint}{ADYEN_API_VERSION}/payments/{connector_payment_id}/refunds",
        ))
    }

    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 = adyen::AdyenRouterData::try_from((refund_amount, req))?;
        let connector_req = adyen::AdyenRefundRequest::try_from(&connector_router_data)?;

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

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

    #[instrument(skip_all)]
    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: adyen::AdyenRefundResponse = res
            .response
            .parse_struct("AdyenRefundResponse")
            .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,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
    fn get_5xx_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 Adyen {}

fn get_webhook_object_from_body(
    body: &[u8],
) -> CustomResult<adyen::AdyenNotificationRequestItemWH, common_utils::errors::ParsingError> {
    let mut webhook: adyen::AdyenIncomingWebhook = body.parse_struct("AdyenIncomingWebhook")?;

    let item_object = webhook
        .notification_items
        .drain(..)
        .next()
        // TODO: ParsingError doesn't seem to be an apt error for this case
        .ok_or(common_utils::errors::ParsingError::UnknownError)?;

    Ok(item_object.notification_request_item)
}

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

    fn get_webhook_source_verification_signature(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let notif_item = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;

        let base64_signature = notif_item.additional_data.hmac_signature.expose();
        Ok(base64_signature.as_bytes().to_vec())
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;

        let message = format!(
            "{}:{}:{}:{}:{}:{}:{}:{}",
            notif.psp_reference,
            notif.original_reference.unwrap_or_default(),
            notif.merchant_account_code,
            notif.merchant_reference,
            notif.amount.value,
            notif.amount.currency,
            notif.event_code,
            notif.success
        );

        Ok(message.into_bytes())
    }

    async fn verify_webhook_source(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        _connector_account_details: common_utils::crypto::Encryptable<Secret<serde_json::Value>>,
        connector_label: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        let connector_webhook_secrets = self
            .get_webhook_source_verification_merchant_secret(
                merchant_id,
                connector_label,
                connector_webhook_details,
            )
            .await
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;

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

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

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

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

    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        // for capture_event, original_reference field will have the authorized payment's PSP reference
        if adyen::is_capture_or_cancel_event(&notif.event_code) {
            return Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::ConnectorTransactionId(
                    notif
                        .original_reference
                        .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
                ),
            ));
        }
        if adyen::is_transaction_event(&notif.event_code) {
            return Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::PaymentAttemptId(notif.merchant_reference),
            ));
        }
        if adyen::is_refund_event(&notif.event_code) {
            return Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                api_models::webhooks::RefundIdType::RefundId(notif.merchant_reference),
            ));
        }
        if adyen::is_chargeback_event(&notif.event_code) {
            return Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::ConnectorTransactionId(
                    notif
                        .original_reference
                        .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
                ),
            ));
        }
        #[cfg(feature = "payouts")]
        if adyen::is_payout_event(&notif.event_code) {
            return Ok(api_models::webhooks::ObjectReferenceId::PayoutId(
                api_models::webhooks::PayoutIdType::PayoutAttemptId(notif.merchant_reference),
            ));
        }
        Err(report!(errors::ConnectorError::WebhookReferenceIdNotFound))
    }

    fn get_webhook_event_type(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;

        Ok(transformers::get_adyen_webhook_event(
            notif.event_code,
            notif.success,
            notif.additional_data.dispute_status,
        ))
    }

    fn get_webhook_resource_object(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;

        let response = adyen::AdyenWebhookResponse::from(notif);

        Ok(Box::new(response))
    }

    fn get_webhook_api_response(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
        _error_kind: Option<IncomingWebhookFlowError>,
    ) -> CustomResult<ApplicationResponse<serde_json::Value>, errors::ConnectorError> {
        Ok(ApplicationResponse::TextPlain("[accepted]".to_string()))
    }

    fn get_dispute_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<disputes::DisputePayload, errors::ConnectorError> {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        let amount = convert_amount(
            self.amount_converter_webhooks,
            notif.amount.value,
            notif.amount.currency,
        )?;
        Ok(disputes::DisputePayload {
            amount,
            currency: notif.amount.currency,
            dispute_stage: api_models::enums::DisputeStage::from(notif.event_code.clone()),
            connector_dispute_id: notif.psp_reference,
            connector_reason: notif.reason,
            connector_reason_code: notif.additional_data.chargeback_reason_code,
            challenge_required_by: notif.additional_data.defense_period_ends_at,
            connector_status: notif.event_code.to_string(),
            created_at: notif.event_date,
            updated_at: notif.event_date,
        })
    }

    fn get_mandate_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<hyperswitch_domain_models::router_flow_types::ConnectorMandateDetails>,
        errors::ConnectorError,
    > {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        let mandate_reference =
            notif
                .additional_data
                .recurring_detail_reference
                .map(|mandate_id| {
                    hyperswitch_domain_models::router_flow_types::ConnectorMandateDetails {
                        connector_mandate_id: mandate_id.clone(),
                    }
                });
        Ok(mandate_reference)
    }

    fn get_network_txn_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<hyperswitch_domain_models::router_flow_types::ConnectorNetworkTxnId>,
        errors::ConnectorError,
    > {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        let optional_network_txn_id =
            notif
                .additional_data
                .network_tx_reference
                .map(|network_txn_id| {
                    hyperswitch_domain_models::router_flow_types::ConnectorNetworkTxnId::new(
                        network_txn_id,
                    )
                });
        Ok(optional_network_txn_id)
    }
}

impl Dispute for Adyen {}
impl DefendDispute for Adyen {}
impl AcceptDispute for Adyen {}
impl SubmitEvidence for Adyen {}

impl ConnectorIntegration<Accept, AcceptDisputeRequestData, AcceptDisputeResponse> for Adyen {
    fn get_headers(
        &self,
        req: &AcceptDisputeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            AcceptDisputeType::get_content_type(self).to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

    fn get_url(
        &self,
        req: &AcceptDisputeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let endpoint = build_env_specific_endpoint(
            connectors.adyen.dispute_base_url.as_str(),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!(
            "{endpoint}ca/services/DisputeService/v30/acceptDispute",
        ))
    }

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


impl api::PaymentToken for Thunes {}


impl api::PayoutRecipientAccount for Stripe {}

impl api::PayoutRecipientAccount for for Stripe


pub async fn authorize_postprocessing_steps<F: Clone>(
    state: &SessionState,
    router_data: &types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
    confirm: bool,
    connector: &api::ConnectorData,
) -> RouterResult<types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>> {
    if confirm {
        let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
            api::PostProcessing,
            types::PaymentsPostProcessingData,
            types::PaymentsResponseData,
        > = connector.connector.get_connector_integration();

        let postprocessing_request_data =
            types::PaymentsPostProcessingData::try_from(router_data.to_owned())?;

        let postprocessing_response_data: Result<
            types::PaymentsResponseData,
            types::ErrorResponse,
        > = Err(types::ErrorResponse::default());

        let postprocessing_router_data =
            helpers::router_data_type_conversion::<_, api::PostProcessing, _, _, _, _>(
                router_data.clone(),
                postprocessing_request_data,
                postprocessing_response_data,
            );

        let resp = services::execute_connector_processing_step(
            state,
            connector_integration,
            &postprocessing_router_data,
            payments::CallConnectorAction::Trigger,
            None,
            None,
        )
        .await
        .to_payment_failed_response()?;

        let authorize_router_data = helpers::router_data_type_conversion::<_, F, _, _, _, _>(
            resp.clone(),
            router_data.request.to_owned(),
            resp.response,
        );

        Ok(authorize_router_data)
    } else {
        Ok(router_data.clone())
    }
}


pub struct ThreeDSPaymentData {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub firstname: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lastname: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<pii::Email>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub streetaddress: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub city: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub zipcode: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub state: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub country: Option<enums::CountryAlpha2>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub browser_info: Option<HipayBrowserInfo>,
}


pub struct ConnectorApiIntegrationPayload {
    pub connector_type: String,
    pub profile_id: common_utils::id_type::ProfileId,
    pub connector_name: api_models::enums::Connector,
    #[serde(skip_deserializing)]
    #[schema(example = "stripe_US_travel")]
    pub connector_label: Option<String>,
    pub merchant_connector_id: Option<String>,
    pub disabled: bool,
    pub test_mode: bool,
    pub payment_methods_enabled: Option<Vec<PaymentMethodsEnabled>>,
    pub metadata: Option<ApiModelMetaData>,
    pub connector_webhook_details: Option<api_models::admin::MerchantConnectorWebhookDetails>,
}


pub struct NovalnetResponseMerchant {
    pub project: Option<Secret<i64>>,
    pub project_name: Option<Secret<String>>,
    pub project_url: Option<url::Url>,
    pub vendor: Option<Secret<i64>>,
}


impl api::PaymentsCompleteAuthorize for Paypal {}

impl api::PaymentsCompleteAuthorize for for Paypal


pub struct ConnectorEventsRequest {
    pub payment_id: common_utils::id_type::PaymentId,
    pub refund_id: Option<String>,
    pub dispute_id: Option<String>,
}


pub trait OrganizationBridge {
    fn get_organization_id(&self) -> id_type::OrganizationId;
    fn get_organization_name(&self) -> Option<String>;
    fn set_organization_name(&mut self, organization_name: String);
}


pub async fn routing_retrieve_linked_config(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<routing_types::RoutingRetrieveLinkQuery>,
    transaction_type: Option<enums::TransactionType>,
) -> impl Responder {
    use crate::services::authentication::AuthenticationData;
    let flow = Flow::RoutingRetrieveActiveConfig;
    let query = query.into_inner();
    if let Some(profile_id) = query.profile_id.clone() {
        Box::pin(oss_api::server_wrap(
            flow,
            state,
            &req,
            query.clone(),
            |state, auth: AuthenticationData, query_params, _| {
                let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                    domain::Context(auth.merchant_account, auth.key_store),
                ));
                routing::retrieve_linked_routing_config(
                    state,
                    merchant_context,
                    auth.profile_id,
                    query_params,
                    transaction_type
                        .or(query.transaction_type)
                        .unwrap_or(enums::TransactionType::Payment),
                )
            },
            auth::auth_type(
                &auth::HeaderAuth(auth::ApiKeyAuth {
                    is_connected_allowed: false,
                    is_platform_allowed: false,
                }),
                &auth::JWTAuthProfileFromRoute {
                    profile_id,
                    required_permission: Permission::ProfileRoutingRead,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    } else {
        Box::pin(oss_api::server_wrap(
            flow,
            state,
            &req,
            query.clone(),
            |state, auth: AuthenticationData, query_params, _| {
                let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                    domain::Context(auth.merchant_account, auth.key_store),
                ));
                routing::retrieve_linked_routing_config(
                    state,
                    merchant_context,
                    auth.profile_id,
                    query_params,
                    transaction_type
                        .or(query.transaction_type)
                        .unwrap_or(enums::TransactionType::Payment),
                )
            },
            auth::auth_type(
                &auth::HeaderAuth(auth::ApiKeyAuth {
                    is_connected_allowed: false,
                    is_platform_allowed: false,
                }),
                &auth::JWTAuth {
                    permission: Permission::ProfileRoutingRead,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }
}


pub struct PaystackPSyncData {
    status: PaystackPSyncStatus,
    reference: String,
}


pub struct RazorpayOrderResponse {
    pub id: String,
}


impl api::PaymentVoid for Braintree {}


pub struct AliPayHkRedirection {}


File: crates/router/src/utils/user/theme.rs
 Public functions: 10

use std::path::PathBuf;

use common_enums::EntityType;
use common_utils::{ext_traits::AsyncExt, id_type, types::user::ThemeLineage};
use diesel_models::user::theme::Theme;
use error_stack::ResultExt;
use hyperswitch_domain_models::merchant_key_store::MerchantKeyStore;

use crate::{
    core::errors::{StorageErrorExt, UserErrors, UserResult},
    routes::SessionState,
    services::authentication::UserFromToken,
};

fn get_theme_dir_key(theme_id: &str) -> PathBuf {
    ["themes", theme_id].iter().collect()
}

pub fn get_specific_file_key(theme_id: &str, file_name: &str) -> PathBuf {
    let mut path = get_theme_dir_key(theme_id);
    path.push(file_name);
    path
}

pub fn get_theme_file_key(theme_id: &str) -> PathBuf {
    get_specific_file_key(theme_id, "theme.json")
}

fn path_buf_to_str(path: &PathBuf) -> UserResult<&str> {
    path.to_str()
        .ok_or(UserErrors::InternalServerError)
        .attach_printable(format!("Failed to convert path {path:#?} to string"))
}

pub async fn retrieve_file_from_theme_bucket(
    state: &SessionState,
    path: &PathBuf,
) -> UserResult<Vec<u8>> {
    state
        .theme_storage_client
        .retrieve_file(path_buf_to_str(path)?)
        .await
        .change_context(UserErrors::ErrorRetrievingFile)
}

pub async fn upload_file_to_theme_bucket(
    state: &SessionState,
    path: &PathBuf,
    data: Vec<u8>,
) -> UserResult<()> {
    state
        .theme_storage_client
        .upload_file(path_buf_to_str(path)?, data)
        .await
        .change_context(UserErrors::ErrorUploadingFile)
}

pub async fn validate_lineage(state: &SessionState, lineage: &ThemeLineage) -> UserResult<()> {
    match lineage {
        ThemeLineage::Tenant { tenant_id } => {
            validate_tenant(state, tenant_id)?;
            Ok(())
        }
        ThemeLineage::Organization { tenant_id, org_id } => {
            validate_tenant(state, tenant_id)?;
            validate_org(state, org_id).await?;
            Ok(())
        }
        ThemeLineage::Merchant {
            tenant_id,
            org_id,
            merchant_id,
        } => {
            validate_tenant(state, tenant_id)?;
            validate_org(state, org_id).await?;
            validate_merchant(state, org_id, merchant_id).await?;
            Ok(())
        }
        ThemeLineage::Profile {
            tenant_id,
            org_id,
            merchant_id,
            profile_id,
        } => {
            validate_tenant(state, tenant_id)?;
            validate_org(state, org_id).await?;
            let key_store = validate_merchant_and_get_key_store(state, org_id, merchant_id).await?;
            validate_profile(state, profile_id, merchant_id, &key_store).await?;
            Ok(())
        }
    }
}

fn validate_tenant(state: &SessionState, tenant_id: &id_type::TenantId) -> UserResult<()> {
    if &state.tenant.tenant_id != tenant_id {
        return Err(UserErrors::InvalidThemeLineage("tenant_id".to_string()).into());
    }
    Ok(())
}

async fn validate_org(state: &SessionState, org_id: &id_type::OrganizationId) -> UserResult<()> {
    state
        .accounts_store
        .find_organization_by_org_id(org_id)
        .await
        .to_not_found_response(UserErrors::InvalidThemeLineage("org_id".to_string()))
        .map(|_| ())
}

async fn validate_merchant_and_get_key_store(
    state: &SessionState,
    org_id: &id_type::OrganizationId,
    merchant_id: &id_type::MerchantId,
) -> UserResult<MerchantKeyStore> {
    let key_store = state
        .store
        .get_merchant_key_store_by_merchant_id(
            &state.into(),
            merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(UserErrors::InvalidThemeLineage("merchant_id".to_string()))?;

    let merchant_account = state
        .store
        .find_merchant_account_by_merchant_id(&state.into(), merchant_id, &key_store)
        .await
        .to_not_found_response(UserErrors::InvalidThemeLineage("merchant_id".to_string()))?;

    if &merchant_account.organization_id != org_id {
        return Err(UserErrors::InvalidThemeLineage("merchant_id".to_string()).into());
    }

    Ok(key_store)
}

async fn validate_merchant(
    state: &SessionState,
    org_id: &id_type::OrganizationId,
    merchant_id: &id_type::MerchantId,
) -> UserResult<()> {
    validate_merchant_and_get_key_store(state, org_id, merchant_id)
        .await
        .map(|_| ())
}

async fn validate_profile(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    merchant_id: &id_type::MerchantId,
    key_store: &MerchantKeyStore,
) -> UserResult<()> {
    state
        .store
        .find_business_profile_by_merchant_id_profile_id(
            &state.into(),
            key_store,
            merchant_id,
            profile_id,
        )
        .await
        .to_not_found_response(UserErrors::InvalidThemeLineage("profile_id".to_string()))
        .map(|_| ())
}

pub async fn get_most_specific_theme_using_token_and_min_entity(
    state: &SessionState,
    user_from_token: &UserFromToken,
    min_entity: EntityType,
) -> UserResult<Option<Theme>> {
    get_most_specific_theme_using_lineage(
        state,
        ThemeLineage::new(
            min_entity,
            user_from_token
                .tenant_id
                .clone()
                .unwrap_or(state.tenant.tenant_id.clone()),
            user_from_token.org_id.clone(),
            user_from_token.merchant_id.clone(),
            user_from_token.profile_id.clone(),
        ),
    )
    .await
}

pub async fn get_most_specific_theme_using_lineage(
    state: &SessionState,
    lineage: ThemeLineage,
) -> UserResult<Option<Theme>> {
    match state
        .store
        .find_most_specific_theme_in_lineage(lineage)
        .await
    {
        Ok(theme) => Ok(Some(theme)),
        Err(e) => {
            if e.current_context().is_db_not_found() {
                Ok(None)
            } else {
                Err(e.change_context(UserErrors::InternalServerError))
            }
        }
    }
}

pub async fn get_theme_using_optional_theme_id(
    state: &SessionState,
    theme_id: Option<String>,
) -> UserResult<Option<Theme>> {
    match theme_id
        .async_map(|theme_id| state.store.find_theme_by_theme_id(theme_id))
        .await
        .transpose()
    {
        Ok(theme) => Ok(theme),
        Err(e) => {
            if e.current_context().is_db_not_found() {
                Ok(None)
            } else {
                Err(e.change_context(UserErrors::InternalServerError))
            }
        }
    }
}
pub async fn get_theme_lineage_from_user_token(
    user_from_token: &UserFromToken,
    state: &SessionState,
    request_entity_type: &EntityType,
) -> UserResult<ThemeLineage> {
    let tenant_id = user_from_token
        .tenant_id
        .clone()
        .unwrap_or(state.tenant.tenant_id.clone());
    let org_id = user_from_token.org_id.clone();
    let merchant_id = user_from_token.merchant_id.clone();
    let profile_id = user_from_token.profile_id.clone();

    Ok(ThemeLineage::new(
        *request_entity_type,
        tenant_id,
        org_id,
        merchant_id,
        profile_id,
    ))
}

pub async fn can_user_access_theme(
    user: &UserFromToken,
    user_entity_type: &EntityType,
    theme: &Theme,
) -> UserResult<()> {
    if user_entity_type < &theme.entity_type {
        return Err(UserErrors::ThemeNotFound.into());
    }

    match theme.entity_type {
        EntityType::Tenant => {
            if user.tenant_id.as_ref() == Some(&theme.tenant_id)
                && theme.org_id.is_none()
                && theme.merchant_id.is_none()
                && theme.profile_id.is_none()
            {
                Ok(())
            } else {
                Err(UserErrors::ThemeNotFound.into())
            }
        }
        EntityType::Organization => {
            if user.tenant_id.as_ref() == Some(&theme.tenant_id)
                && theme.org_id.as_ref() == Some(&user.org_id)
                && theme.merchant_id.is_none()
                && theme.profile_id.is_none()
            {
                Ok(())
            } else {
                Err(UserErrors::ThemeNotFound.into())
            }
        }
        EntityType::Merchant => {
            if user.tenant_id.as_ref() == Some(&theme.tenant_id)
                && theme.org_id.as_ref() == Some(&user.org_id)
                && theme.merchant_id.as_ref() == Some(&user.merchant_id)
                && theme.profile_id.is_none()
            {
                Ok(())
            } else {
                Err(UserErrors::ThemeNotFound.into())
            }
        }
        EntityType::Profile => {
            if user.tenant_id.as_ref() == Some(&theme.tenant_id)
                && theme.org_id.as_ref() == Some(&user.org_id)
                && theme.merchant_id.as_ref() == Some(&user.merchant_id)
                && theme.profile_id.as_ref() == Some(&user.profile_id)
            {
                Ok(())
            } else {
                Err(UserErrors::ThemeNotFound.into())
            }
        }
    }
}


impl<'a> NetworkTokenizationBuilder<'a, CardRequestValidated> {
    pub fn set_card_details(
        self,
        card_req: &'a domain::TokenizeCardRequest,
        optional_card_info: Option<diesel_models::CardInfo>,
    ) -> NetworkTokenizationBuilder<'a, CardDetailsAssigned> {
        let card = domain::CardDetail {
            card_number: card_req.raw_card_number.clone(),
            card_exp_month: card_req.card_expiry_month.clone(),
            card_exp_year: card_req.card_expiry_year.clone(),
            bank_code: optional_card_info
                .as_ref()
                .and_then(|card_info| card_info.bank_code.clone()),
            nick_name: card_req.nick_name.clone(),
            card_holder_name: card_req.card_holder_name.clone(),
            card_issuer: optional_card_info
                .as_ref()
                .map_or(card_req.card_issuer.clone(), |card_info| {
                    card_info.card_issuer.clone()
                }),
            card_network: optional_card_info
                .as_ref()
                .map_or(card_req.card_network.clone(), |card_info| {
                    card_info.card_network.clone()
                }),
            card_type: optional_card_info.as_ref().map_or(
                card_req
                    .card_type
                    .as_ref()
                    .map(|card_type| card_type.to_string()),
                |card_info| card_info.card_type.clone(),
            ),
            card_issuing_country: optional_card_info
                .as_ref()
                .map_or(card_req.card_issuing_country.clone(), |card_info| {
                    card_info.card_issuing_country.clone()
                }),
            co_badged_card_data: None,
        };
        NetworkTokenizationBuilder {
            state: std::marker::PhantomData,
            card: Some(card),
            card_cvc: card_req.card_cvc.clone(),
            customer: self.customer,
            network_token: self.network_token,
            stored_card: self.stored_card,
            stored_token: self.stored_token,
            payment_method_response: self.payment_method_response,
            card_tokenized: self.card_tokenized,
            error_code: self.error_code,
            error_message: self.error_message,
        }
    }
}


pub async fn flatten_join_error<T>(handle: Handle<T>) -> RouterResult<T> {
    match handle.await {
        Ok(Ok(t)) => Ok(t),
        Ok(Err(err)) => Err(err),
        Err(err) => Err(err)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Join Error"),
    }
}


File: crates/hyperswitch_constraint_graph/src/lib.rs

pub mod builder;
mod dense_map;
pub mod error;
pub mod graph;
pub mod types;

pub use builder::ConstraintGraphBuilder;
pub use error::{AnalysisTrace, GraphError};
pub use graph::ConstraintGraph;
#[cfg(feature = "viz")]
pub use types::NodeViz;
pub use types::{
    CheckingContext, CycleCheck, DomainId, DomainIdentifier, Edge, EdgeId, KeyNode, Memoization,
    Node, NodeId, NodeValue, Relation, Strength, ValueNode,
};


pub struct PaystackRefundsData {
    status: PaystackRefundStatus,
    id: i64,
}


OpenAPI Block Path: paths."/api_keys/{merchant_id}/list"
{
  "get": {
    "tags": [
      "API Key"
    ],
    "summary": "API Key - List",
    "description": "List all the API Keys associated to a merchant account.",
    "operationId": "List all API Keys associated with a merchant account",
    "parameters": [
      {
        "name": "merchant_id",
        "in": "path",
        "description": "The unique identifier for the merchant account",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      {
        "name": "limit",
        "in": "query",
        "description": "The maximum number of API Keys to include in the response",
        "required": false,
        "schema": {
          "type": "integer",
          "format": "int64",
          "nullable": true
        }
      },
      {
        "name": "skip",
        "in": "query",
        "description": "The number of API Keys to skip when retrieving the list of API keys.",
        "required": false,
        "schema": {
          "type": "integer",
          "format": "int64",
          "nullable": true
        }
      }
    ],
    "responses": {
      "200": {
        "description": "List of API Keys retrieved successfully",
        "content": {
          "application/json": {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/RetrieveApiKeyResponse"
              }
            }
          }
        }
      }
    },
    "security": [
      {
        "admin_api_key": []
      }
    ]
  }
}


OpenAPI Block Path: components.schemas.PixBankTransfer
{
  "type": "object",
  "required": [
    "bank_account_number",
    "pix_key"
  ],
  "properties": {
    "bank_name": {
      "type": "string",
      "description": "Bank name",
      "example": "Deutsche Bank",
      "nullable": true
    },
    "bank_branch": {
      "type": "string",
      "description": "Bank branch",
      "example": "3707",
      "nullable": true
    },
    "bank_account_number": {
      "type": "string",
      "description": "Bank account number is an unique identifier assigned by a bank to a customer.",
      "example": "000123456"
    },
    "pix_key": {
      "type": "string",
      "description": "Unique key for pix customer",
      "example": "000123456"
    },
    "tax_id": {
      "type": "string",
      "description": "Individual taxpayer identification number",
      "example": "000123456",
      "nullable": true
    }
  }
}


impl api::MandateSetup for Paystack {}


pub fn get_id(&self) -> &id_type::GlobalAttemptId {
        &self.id
    }


pub struct PriorityLogicOutput {
    pub is_enforcement: Option<bool>,
    pub gws: Option<Vec<String>>,
    pub priority_logic_tag: Option<String>,
    pub gateway_reference_ids: Option<HashMap<String, String>>,
    pub primary_logic: Option<PriorityLogicData>,
    pub fallback_logic: Option<PriorityLogicData>,
}


OpenAPI Block Path: paths."/payments/session_tokens"
{
  "post": {
    "tags": [
      "Payments"
    ],
    "summary": "Payments - Session token",
    "description": "Creates a session object or a session token for wallets like Apple Pay, Google Pay, etc. These tokens are used by Hyperswitch's SDK to initiate these wallets' SDK.",
    "operationId": "Create Session tokens for a Payment",
    "requestBody": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/PaymentsSessionRequest"
          }
        }
      },
      "required": true
    },
    "responses": {
      "200": {
        "description": "Payment session object created or session token was retrieved from wallets",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PaymentsSessionResponse"
            }
          }
        }
      },
      "400": {
        "description": "Missing mandatory fields",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/GenericErrorResponseOpenApi"
            }
          }
        }
      }
    },
    "security": [
      {
        "publishable_key": []
      }
    ]
  }
}


impl MerchantAccount {
    pub fn server(state: AppState) -> Scope {
        let mut routes = web::scope("/accounts")
            .service(web::resource("").route(web::post().to(admin::merchant_account_create)))
            .service(web::resource("/list").route(web::get().to(admin::merchant_account_list)))
            .service(
                web::resource("/{id}/kv")
                    .route(web::post().to(admin::merchant_account_toggle_kv))
                    .route(web::get().to(admin::merchant_account_kv_status)),
            )
            .service(
                web::resource("/transfer")
                    .route(web::post().to(admin::merchant_account_transfer_keys)),
            )
            .service(
                web::resource("/kv").route(web::post().to(admin::merchant_account_toggle_all_kv)),
            )
            .service(
                web::resource("/{id}")
                    .route(web::get().to(admin::retrieve_merchant_account))
                    .route(web::post().to(admin::update_merchant_account))
                    .route(web::delete().to(admin::delete_merchant_account)),
            );
        if state.conf.platform.enabled {
            routes = routes.service(
                web::resource("/{id}/platform")
                    .route(web::post().to(admin::merchant_account_enable_platform_account)),
            )
        }
        routes.app_data(web::Data::new(state))
    }
}


pub async fn get_profile_sdk_events(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<api_models::analytics::sdk_events::SdkEventsRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetSdkEvents;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                utils::check_if_profile_id_is_present_in_payment_intent(
                    req.payment_id.clone(),
                    &state,
                    &auth,
                )
                .await
                .change_context(AnalyticsError::AccessForbiddenError)?;
                sdk_events_core(&state.pool, req, &auth.merchant_account.publishable_key)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


File: crates/router/src/types/storage/payment_attempt.rs

use common_utils::types::MinorUnit;
use diesel_models::{capture::CaptureNew, enums};
use error_stack::ResultExt;
pub use hyperswitch_domain_models::payments::payment_attempt::{
    PaymentAttempt, PaymentAttemptUpdate,
};

use crate::{
    core::errors, errors::RouterResult, types::transformers::ForeignFrom, utils::OptionExt,
};
pub trait PaymentAttemptExt {
    fn make_new_capture(
        &self,
        capture_amount: MinorUnit,
        capture_status: enums::CaptureStatus,
    ) -> RouterResult<CaptureNew>;

    fn get_next_capture_id(&self) -> String;
    fn get_total_amount(&self) -> MinorUnit;
    fn get_surcharge_details(&self) -> Option<api_models::payments::RequestSurchargeDetails>;
}

impl PaymentAttemptExt for PaymentAttempt {
    #[cfg(feature = "v2")]
    fn make_new_capture(
        &self,
        capture_amount: MinorUnit,
        capture_status: enums::CaptureStatus,
    ) -> RouterResult<CaptureNew> {
        todo!()
    }

    #[cfg(feature = "v1")]
    fn make_new_capture(
        &self,
        capture_amount: MinorUnit,
        capture_status: enums::CaptureStatus,
    ) -> RouterResult<CaptureNew> {
        let capture_sequence = self.multiple_capture_count.unwrap_or_default() + 1;
        let now = common_utils::date_time::now();
        Ok(CaptureNew {
            payment_id: self.payment_id.clone(),
            merchant_id: self.merchant_id.clone(),
            capture_id: self.get_next_capture_id(),
            status: capture_status,
            amount: capture_amount,
            currency: self.currency,
            connector: self
                .connector
                .clone()
                .get_required_value("connector")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable(
                    "connector field is required in payment_attempt to create a capture",
                )?,
            error_message: None,
            tax_amount: None,
            created_at: now,
            modified_at: now,
            error_code: None,
            error_reason: None,
            authorized_attempt_id: self.attempt_id.clone(),
            capture_sequence,
            connector_capture_id: None,
            connector_response_reference_id: None,
            processor_capture_data: None,
            // Below fields are deprecated. Please add any new fields above this line.
            connector_capture_data: None,
        })
    }

    #[cfg(feature = "v1")]
    fn get_next_capture_id(&self) -> String {
        let next_sequence_number = self.multiple_capture_count.unwrap_or_default() + 1;
        format!("{}_{}", self.attempt_id.clone(), next_sequence_number)
    }

    #[cfg(feature = "v2")]
    fn get_next_capture_id(&self) -> String {
        todo!()
    }

    #[cfg(feature = "v1")]
    fn get_surcharge_details(&self) -> Option<api_models::payments::RequestSurchargeDetails> {
        self.net_amount
            .get_surcharge_amount()
            .map(
                |surcharge_amount| api_models::payments::RequestSurchargeDetails {
                    surcharge_amount,
                    tax_amount: self.net_amount.get_tax_on_surcharge(),
                },
            )
    }

    #[cfg(feature = "v2")]
    fn get_surcharge_details(&self) -> Option<api_models::payments::RequestSurchargeDetails> {
        todo!()
    }

    #[cfg(feature = "v1")]
    fn get_total_amount(&self) -> MinorUnit {
        self.net_amount.get_total_amount()
    }

    #[cfg(feature = "v2")]
    fn get_total_amount(&self) -> MinorUnit {
        todo!()
    }
}

pub trait AttemptStatusExt {
    fn maps_to_intent_status(self, intent_status: enums::IntentStatus) -> bool;
}

impl AttemptStatusExt for enums::AttemptStatus {
    fn maps_to_intent_status(self, intent_status: enums::IntentStatus) -> bool {
        enums::IntentStatus::foreign_from(self) == intent_status
    }
}

#[cfg(test)]
#[cfg(all(
    feature = "v1", // Ignoring tests for v2 since they aren't actively running
    feature = "dummy_connector"
))]
mod tests {
    #![allow(clippy::expect_used, clippy::unwrap_used, clippy::print_stderr)]
    use hyperswitch_domain_models::payments::payment_attempt::PaymentAttemptNew;
    use tokio::sync::oneshot;
    use uuid::Uuid;

    use crate::{
        configs::settings::Settings,
        db::StorageImpl,
        routes, services,
        types::{self, storage::enums},
    };

    async fn create_single_connection_test_transaction_pool() -> routes::AppState {
        // Set pool size to 1 and minimum idle connection size to 0
        std::env::set_var("ROUTER__MASTER_DATABASE__POOL_SIZE", "1");
        std::env::set_var("ROUTER__MASTER_DATABASE__MIN_IDLE", "0");
        std::env::set_var("ROUTER__REPLICA_DATABASE__POOL_SIZE", "1");
        std::env::set_var("ROUTER__REPLICA_DATABASE__MIN_IDLE", "0");

        let conf = Settings::new().expect("invalid settings");
        let tx: oneshot::Sender<()> = oneshot::channel().0;
        let api_client = Box::new(services::MockApiClient);
        Box::pin(routes::AppState::with_storage(
            conf,
            StorageImpl::PostgresqlTest,
            tx,
            api_client,
        ))
        .await
    }

    #[tokio::test]
    async fn test_payment_attempt_insert() {
        let state = create_single_connection_test_transaction_pool().await;
        let payment_id =
            common_utils::id_type::PaymentId::generate_test_payment_id_for_sample_data();
        let current_time = common_utils::date_time::now();
        let connector = types::Connector::DummyConnector1.to_string();
        let payment_attempt = PaymentAttemptNew {
            payment_id: payment_id.clone(),
            connector: Some(connector),
            created_at: current_time.into(),
            modified_at: current_time.into(),
            merchant_id: Default::default(),
            attempt_id: Default::default(),
            status: Default::default(),
            net_amount: Default::default(),
            currency: Default::default(),
            save_to_locker: Default::default(),
            error_message: Default::default(),
            offer_amount: Default::default(),
            payment_method_id: Default::default(),
            payment_method: Default::default(),
            capture_method: Default::default(),
            capture_on: Default::default(),
            confirm: Default::default(),
            authentication_type: Default::default(),
            last_synced: Default::default(),
            cancellation_reason: Default::default(),
            amount_to_capture: Default::default(),
            mandate_id: Default::default(),
            browser_info: Default::default(),
            payment_token: Default::default(),
            error_code: Default::default(),
            connector_metadata: Default::default(),
            payment_experience: Default::default(),
            payment_method_type: Default::default(),
            payment_method_data: Default::default(),
            business_sub_label: Default::default(),
            straight_through_algorithm: Default::default(),
            preprocessing_step_id: Default::default(),
            mandate_details: Default::default(),
            error_reason: Default::default(),
            connector_response_reference_id: Default::default(),
            multiple_capture_count: Default::default(),
            amount_capturable: Default::default(),
            updated_by: Default::default(),
            authentication_data: Default::default(),
            encoded_data: Default::default(),
            merchant_connector_id: Default::default(),
            unified_code: Default::default(),
            unified_message: Default::default(),
            external_three_ds_authentication_attempted: Default::default(),
            authentication_connector: Default::default(),
            authentication_id: Default::default(),
            mandate_data: Default::default(),
            payment_method_billing_address_id: Default::default(),
            fingerprint_id: Default::default(),
            client_source: Default::default(),
            client_version: Default::default(),
            customer_acceptance: Default::default(),
            profile_id: common_utils::generate_profile_id_of_default_length(),
            organization_id: Default::default(),
            connector_mandate_detail: Default::default(),
            request_extended_authorization: Default::default(),
            extended_authorization_applied: Default::default(),
            capture_before: Default::default(),
            card_discovery: Default::default(),
            processor_merchant_id: Default::default(),
            created_by: None,
            setup_future_usage_applied: Default::default(),
            routing_approach: Default::default(),
            connector_request_reference_id: Default::default(),
            network_transaction_id: Default::default(),
        };

        let store = state
            .stores
            .get(state.conf.multitenancy.get_tenant_ids().first().unwrap())
            .unwrap();
        let response = store
            .insert_payment_attempt(payment_attempt, enums::MerchantStorageScheme::PostgresOnly)
            .await
            .unwrap();
        eprintln!("{response:?}");

        assert_eq!(response.payment_id, payment_id.clone());
    }

    #[tokio::test]
    /// Example of unit test
    /// Kind of test: state-based testing
    async fn test_find_payment_attempt() {
        let state = create_single_connection_test_transaction_pool().await;
        let current_time = common_utils::date_time::now();
        let payment_id =
            common_utils::id_type::PaymentId::generate_test_payment_id_for_sample_data();
        let attempt_id = Uuid::new_v4().to_string();
        let merchant_id = common_utils::id_type::MerchantId::new_from_unix_timestamp();
        let connector = types::Connector::DummyConnector1.to_string();

        let payment_attempt = PaymentAttemptNew {
            payment_id: payment_id.clone(),
            merchant_id: merchant_id.clone(),
            connector: Some(connector),
            created_at: current_time.into(),
            modified_at: current_time.into(),
            attempt_id: attempt_id.clone(),
            status: Default::default(),
            net_amount: Default::default(),
            currency: Default::default(),
            save_to_locker: Default::default(),
            error_message: Default::default(),
            offer_amount: Default::default(),
            payment_method_id: Default::default(),
            payment_method: Default::default(),
            capture_method: Default::default(),
            capture_on: Default::default(),
            confirm: Default::default(),
            authentication_type: Default::default(),
            last_synced: Default::default(),
            cancellation_reason: Default::default(),
            amount_to_capture: Default::default(),
            mandate_id: Default::default(),
            browser_info: Default::default(),
            payment_token: Default::default(),
            error_code: Default::default(),
            connector_metadata: Default::default(),
            payment_experience: Default::default(),
            payment_method_type: Default::default(),
            payment_method_data: Default::default(),
            business_sub_label: Default::default(),
            straight_through_algorithm: Default::default(),
            preprocessing_step_id: Default::default(),
            mandate_details: Default::default(),
            error_reason: Default::default(),
            connector_response_reference_id: Default::default(),
            multiple_capture_count: Default::default(),
            amount_capturable: Default::default(),
            updated_by: Default::default(),
            authentication_data: Default::default(),
            encoded_data: Default::default(),
            merchant_connector_id: Default::default(),
            unified_code: Default::default(),
            unified_message: Default::default(),
            external_three_ds_authentication_attempted: Default::default(),
            authentication_connector: Default::default(),
            authentication_id: Default::default(),
            mandate_data: Default::default(),
            payment_method_billing_address_id: Default::default(),
            fingerprint_id: Default::default(),
            client_source: Default::default(),
            client_version: Default::default(),
            customer_acceptance: Default::default(),
            profile_id: common_utils::generate_profile_id_of_default_length(),
            organization_id: Default::default(),
            connector_mandate_detail: Default::default(),
            request_extended_authorization: Default::default(),
            extended_authorization_applied: Default::default(),
            capture_before: Default::default(),
            card_discovery: Default::default(),
            processor_merchant_id: Default::default(),
            created_by: None,
            setup_future_usage_applied: Default::default(),
            routing_approach: Default::default(),
            connector_request_reference_id: Default::default(),
            network_transaction_id: Default::default(),
        };
        let store = state
            .stores
            .get(state.conf.multitenancy.get_tenant_ids().first().unwrap())
            .unwrap();
        store
            .insert_payment_attempt(payment_attempt, enums::MerchantStorageScheme::PostgresOnly)
            .await
            .unwrap();

        let response = store
            .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                &payment_id,
                &merchant_id,
                &attempt_id,
                enums::MerchantStorageScheme::PostgresOnly,
            )
            .await
            .unwrap();

        eprintln!("{response:?}");

        assert_eq!(response.payment_id, payment_id);
    }

    #[tokio::test]
    /// Example of unit test
    /// Kind of test: state-based testing
    async fn test_payment_attempt_mandate_field() {
        let state = create_single_connection_test_transaction_pool().await;
        let uuid = Uuid::new_v4().to_string();
        let merchant_id =
            common_utils::id_type::MerchantId::try_from(std::borrow::Cow::from("merchant1"))
                .unwrap();

        let payment_id =
            common_utils::id_type::PaymentId::generate_test_payment_id_for_sample_data();
        let current_time = common_utils::date_time::now();
        let connector = types::Connector::DummyConnector1.to_string();

        let payment_attempt = PaymentAttemptNew {
            payment_id: payment_id.clone(),
            merchant_id: merchant_id.clone(),
            connector: Some(connector),
            created_at: current_time.into(),
            modified_at: current_time.into(),
            mandate_id: Some("man_121212".to_string()),
            attempt_id: uuid.clone(),
            status: Default::default(),
            net_amount: Default::default(),
            currency: Default::default(),
            save_to_locker: Default::default(),
            error_message: Default::default(),
            offer_amount: Default::default(),
            payment_method_id: Default::default(),
            payment_method: Default::default(),
            capture_method: Default::default(),
            capture_on: Default::default(),
            confirm: Default::default(),
            authentication_type: Default::default(),
            last_synced: Default::default(),
            cancellation_reason: Default::default(),
            amount_to_capture: Default::default(),
            browser_info: Default::default(),
            payment_token: Default::default(),
            error_code: Default::default(),
            connector_metadata: Default::default(),
            payment_experience: Default::default(),
            payment_method_type: Default::default(),
            payment_method_data: Default::default(),
            business_sub_label: Default::default(),
            straight_through_algorithm: Default::default(),
            preprocessing_step_id: Default::default(),
            mandate_details: Default::default(),
            error_reason: Default::default(),
            connector_response_reference_id: Default::default(),
            multiple_capture_count: Default::default(),
            amount_capturable: Default::default(),
            updated_by: Default::default(),
            authentication_data: Default::default(),
            encoded_data: Default::default(),
            merchant_connector_id: Default::default(),
            unified_code: Default::default(),
            unified_message: Default::default(),
            external_three_ds_authentication_attempted: Default::default(),
            authentication_connector: Default::default(),
            authentication_id: Default::default(),
            mandate_data: Default::default(),
            payment_method_billing_address_id: Default::default(),
            fingerprint_id: Default::default(),
            client_source: Default::default(),
            client_version: Default::default(),
            customer_acceptance: Default::default(),
            profile_id: common_utils::generate_profile_id_of_default_length(),
            organization_id: Default::default(),
            connector_mandate_detail: Default::default(),
            request_extended_authorization: Default::default(),
            extended_authorization_applied: Default::default(),
            capture_before: Default::default(),
            card_discovery: Default::default(),
            processor_merchant_id: Default::default(),
            created_by: None,
            setup_future_usage_applied: Default::default(),
            routing_approach: Default::default(),
            connector_request_reference_id: Default::default(),
            network_transaction_id: Default::default(),
        };
        let store = state
            .stores
            .get(state.conf.multitenancy.get_tenant_ids().first().unwrap())
            .unwrap();
        store
            .insert_payment_attempt(payment_attempt, enums::MerchantStorageScheme::PostgresOnly)
            .await
            .unwrap();

        let response = store
            .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                &payment_id,
                &merchant_id,
                &uuid,
                enums::MerchantStorageScheme::PostgresOnly,
            )
            .await
            .unwrap();
        // checking it after fetch
        assert_eq!(response.mandate_id, Some("man_121212".to_string()));
    }
}


File: crates/scheduler/src/scheduler.rs
 Public functions: 1

use std::sync::Arc;

use common_utils::{errors::CustomResult, id_type};
#[cfg(feature = "kv_store")]
use storage_impl::kv_router_store::KVRouterStore;
use storage_impl::mock_db::MockDb;
#[cfg(not(feature = "kv_store"))]
use storage_impl::RouterStore;
use tokio::sync::mpsc;

use super::env::logger::error;
pub use crate::{
    configs::settings::SchedulerSettings,
    consumer::{self, workflows},
    db::{process_tracker::ProcessTrackerInterface, queue::QueueInterface},
    errors,
    flow::SchedulerFlow,
    producer,
};

#[cfg(not(feature = "olap"))]
type StoreType = storage_impl::database::store::Store;
#[cfg(feature = "olap")]
type StoreType = storage_impl::database::store::ReplicaStore;

#[cfg(not(feature = "kv_store"))]
pub type Store = RouterStore<StoreType>;
#[cfg(feature = "kv_store")]
pub type Store = KVRouterStore<StoreType>;

pub trait AsSchedulerInterface {
    fn as_scheduler(&self) -> &dyn SchedulerInterface;
}

impl<T: SchedulerInterface> AsSchedulerInterface for T {
    fn as_scheduler(&self) -> &dyn SchedulerInterface {
        self
    }
}

#[async_trait::async_trait]
pub trait SchedulerInterface:
    ProcessTrackerInterface + QueueInterface + AsSchedulerInterface
{
}

#[async_trait::async_trait]
impl SchedulerInterface for Store {}

#[async_trait::async_trait]
impl SchedulerInterface for MockDb {}

#[async_trait::async_trait]
pub trait SchedulerAppState: Send + Sync + Clone {
    fn get_tenants(&self) -> Vec<id_type::TenantId>;
}
#[async_trait::async_trait]
pub trait SchedulerSessionState: Send + Sync + Clone {
    fn get_db(&self) -> Box<dyn SchedulerInterface>;
}
pub async fn start_process_tracker<
    T: SchedulerAppState + 'static,
    U: SchedulerSessionState + 'static,
    F,
>(
    state: &T,
    scheduler_flow: SchedulerFlow,
    scheduler_settings: Arc<SchedulerSettings>,
    channel: (mpsc::Sender<()>, mpsc::Receiver<()>),
    runner_from_task: impl workflows::ProcessTrackerWorkflows<U> + 'static + Copy + std::fmt::Debug,
    app_state_to_session_state: F,
) -> CustomResult<(), errors::ProcessTrackerError>
where
    F: Fn(&T, &id_type::TenantId) -> CustomResult<U, errors::ProcessTrackerError>,
{
    match scheduler_flow {
        SchedulerFlow::Producer => {
            producer::start_producer(
                state,
                scheduler_settings,
                channel,
                app_state_to_session_state,
            )
            .await?
        }
        SchedulerFlow::Consumer => {
            consumer::start_consumer(
                state,
                scheduler_settings,
                runner_from_task,
                channel,
                app_state_to_session_state,
            )
            .await?
        }
        SchedulerFlow::Cleaner => {
            error!("This flow has not been implemented yet!");
        }
    }
    Ok(())
}


OpenAPI Block Path: components.schemas.PayPalWalletData
{
  "type": "object",
  "required": [
    "token"
  ],
  "properties": {
    "token": {
      "type": "string",
      "description": "Token generated for the Apple pay"
    }
  }
}


pub async fn payment_external_authentication(
    _state: SessionState,
    _merchant_context: domain::MerchantContext,
    _req: api_models::payments::PaymentsExternalAuthenticationRequest,
) -> RouterResponse<api_models::payments::PaymentsExternalAuthenticationResponse> {
    todo!()
}


pub struct MandateGenericData {
    pub token: Option<String>,
    pub payment_method: Option<enums::PaymentMethod>,
    pub payment_method_type: Option<enums::PaymentMethodType>,
    pub mandate_data: Option<MandateData>,
    pub recurring_mandate_payment_data:
        Option<hyperswitch_domain_models::router_data::RecurringMandatePaymentData>,
    pub mandate_connector: Option<payments::MandateConnectorDetails>,
    pub payment_method_info: Option<domain::PaymentMethod>,
}


impl RefundSync for Netcetera {}


pub struct GsmResponse {
    /// The connector through which payment has gone through
    pub connector: String,
    /// The flow in which the code and message occurred for a connector
    pub flow: String,
    /// The sub_flow in which the code and message occurred  for a connector
    pub sub_flow: String,
    /// code received from the connector
    pub code: String,
    /// message received from the connector
    pub message: String,
    /// status provided by the router
    pub status: String,
    /// optional error provided by the router
    pub router_error: Option<String>,
    /// decision to be taken for auto retries flow
    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
    #[schema(deprecated)]
    #[schema(value_type = GsmDecision)]
    pub decision: api_enums::GsmDecision,
    /// indicates if step_up retry is possible
    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
    #[schema(deprecated)]
    pub step_up_possible: bool,
    /// error code unified across the connectors
    pub unified_code: Option<String>,
    /// error message unified across the connectors
    pub unified_message: Option<String>,
    /// category in which error belongs to
    #[schema(value_type = Option<ErrorCategory>)]
    pub error_category: Option<api_enums::ErrorCategory>,
    /// indicates if retry with pan is possible
    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
    #[schema(deprecated)]
    pub clear_pan_possible: bool,
    /// Indicates the GSM feature associated with the request,
    /// such as retry mechanisms or other specific functionalities provided by the system.
    #[schema(value_type = GsmFeature)]
    pub feature: api_enums::GsmFeature,
    /// Contains the data relevant to the specified GSM feature, if applicable.
    /// For example, if the `feature` is `Retry`, this will include configuration
    /// details specific to the retry behavior.
    #[schema(value_type = GsmFeatureData)]
    pub feature_data: Option<common_types::domain::GsmFeatureData>,
}


pub struct RefundMetaData {
    refund_reference: String,
}


pub trait StorageInterface:
    Send
    + Sync
    + dyn_clone::DynClone
    + address::AddressInterface
    + api_keys::ApiKeyInterface
    + blocklist_lookup::BlocklistLookupInterface
    + configs::ConfigInterface
    + capture::CaptureInterface
    + customers::CustomerInterface<Error = StorageError>
    + dashboard_metadata::DashboardMetadataInterface
    + dispute::DisputeInterface
    + ephemeral_key::EphemeralKeyInterface
    + ephemeral_key::ClientSecretInterface
    + events::EventInterface
    + file::FileMetadataInterface
    + FraudCheckInterface
    + locker_mock_up::LockerMockUpInterface
    + mandate::MandateInterface
    + merchant_account::MerchantAccountInterface
    + merchant_connector_account::ConnectorAccessToken
    + merchant_connector_account::MerchantConnectorAccountInterface
    + PaymentAttemptInterface<Error = StorageError>
    + PaymentIntentInterface<Error = StorageError>
    + PaymentMethodInterface<Error = StorageError>
    + blocklist::BlocklistInterface
    + blocklist_fingerprint::BlocklistFingerprintInterface
    + dynamic_routing_stats::DynamicRoutingStatsInterface
    + scheduler::SchedulerInterface
    + PayoutAttemptInterface<Error = StorageError>
    + PayoutsInterface<Error = StorageError>
    + refund::RefundInterface
    + reverse_lookup::ReverseLookupInterface
    + CardsInfoInterface<Error = StorageError>
    + merchant_key_store::MerchantKeyStoreInterface
    + MasterKeyInterface
    + payment_link::PaymentLinkInterface
    + RedisConnInterface
    + RequestIdStore
    + business_profile::ProfileInterface
    + routing_algorithm::RoutingAlgorithmInterface
    + gsm::GsmInterface
    + unified_translations::UnifiedTranslationsInterface
    + authorization::AuthorizationInterface
    + user::sample_data::BatchSampleDataInterface
    + health_check::HealthCheckDbInterface
    + user_authentication_method::UserAuthenticationMethodInterface
    + authentication::AuthenticationInterface
    + generic_link::GenericLinkInterface
    + relay::RelayInterface
    + user::theme::ThemeInterface
    + payment_method_session::PaymentMethodsSessionInterface
    + tokenization::TokenizationInterface
    + callback_mapper::CallbackMapperInterface
    + subscription::SubscriptionInterface
    + 'static
{
    fn get_scheduler_db(&self) -> Box<dyn scheduler::SchedulerInterface>;
    fn get_payment_methods_store(&self) -> Box<dyn PaymentMethodsStorageInterface>;
    fn get_cache_store(&self) -> Box<(dyn RedisConnInterface + Send + Sync + 'static)>;
}


pub fn get_max_acs_protocol_version_if_available(&self) -> Option<AcsProtocolVersion> {
        let max_acs_version =
            self.acs_protocol_versions
                .as_ref()
                .and_then(|acs_protocol_versions| {
                    acs_protocol_versions
                        .iter()
                        .max_by_key(|acs_protocol_versions| acs_protocol_versions.version.clone())
                });
        max_acs_version.cloned()
    }


impl PaymentMethodsMigrateForm {
    pub fn validate_and_get_payment_method_records(self) -> MigrationValidationResult {
        // Step 1: Validate form-level conflicts
        let form_has_single_id = self.merchant_connector_id.is_some();
        let form_has_multiple_ids = self.merchant_connector_ids.is_some();

        if form_has_single_id && form_has_multiple_ids {
            return Err(errors::ApiErrorResponse::InvalidRequestData {
                message: "Both merchant_connector_id and merchant_connector_ids cannot be provided"
                    .to_string(),
            });
        }

        // Ensure at least one is provided
        if !form_has_single_id && !form_has_multiple_ids {
            return Err(errors::ApiErrorResponse::InvalidRequestData {
                message: "Either merchant_connector_id or merchant_connector_ids must be provided"
                    .to_string(),
            });
        }

        // Step 2: Parse CSV
        let records = parse_csv(self.file.data.to_bytes()).map_err(|e| {
            errors::ApiErrorResponse::PreconditionFailed {
                message: e.to_string(),
            }
        })?;

        // Step 3: Validate CSV vs Form conflicts
        MerchantConnectorValidator::validate_form_csv_conflicts(
            &records,
            form_has_single_id,
            form_has_multiple_ids,
        )?;

        // Step 4: Prepare the merchant connector account IDs for return
        let mca_ids = if let Some(ref single_id) = self.merchant_connector_id {
            Some(vec![(**single_id).clone()])
        } else if let Some(ref ids_string) = self.merchant_connector_ids {
            let parsed_ids = MerchantConnectorValidator::parse_comma_separated_ids(ids_string)?;
            if parsed_ids.is_empty() {
                None
            } else {
                Some(parsed_ids)
            }
        } else {
            None
        };

        // Step 5: Return the updated structure
        Ok((self.merchant_id.clone(), records, mca_ids))
    }
}


pub async fn find_most_specific_theme_in_lineage(
        conn: &PgPooledConn,
        lineage: ThemeLineage,
    ) -> StorageResult<Self> {
        let query = <Self as HasTable>::table().into_boxed();

        let query =
            lineage
                .get_same_and_higher_lineages()
                .into_iter()
                .fold(query, |mut query, lineage| {
                    query = query.or_filter(Self::lineage_filter(lineage));
                    query
                });

        logger::debug!(query = %debug_query::<Pg,_>(&query).to_string());

        let data: Vec<Self> = match track_database_call::<Self, _, _>(
            query.get_results_async(conn),
            DatabaseOperation::Filter,
        )
        .await
        {
            Ok(value) => Ok(value),
            Err(err) => match err {
                DieselError::NotFound => Err(report!(err)).change_context(DatabaseError::NotFound),
                _ => Err(report!(err)).change_context(DatabaseError::Others),
            },
        }?;

        data.into_iter()
            .min_by_key(|theme| theme.entity_type)
            .ok_or(report!(DatabaseError::NotFound))
    }


pub struct MollieCardTokenResponse {
    card_token: Secret<String>,
}


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


pub struct TokenizedBankRedirectValue1 {
    pub data: payments::BankRedirectData,
}


impl api::PaymentAuthorize for Vgs {}


pub struct CreateFileResponse {
    /// ID of the file created
    pub file_id: String,
}


pub(crate) fn get_internal_error(&self) -> &ApiError {
        match self {
            Self::Unauthorized(i)
            | Self::ForbiddenCommonResource(i)
            | Self::ForbiddenPrivateResource(i)
            | Self::Conflict(i)
            | Self::Gone(i)
            | Self::Unprocessable(i)
            | Self::InternalServerError(i)
            | Self::NotImplemented(i)
            | Self::NotFound(i)
            | Self::MethodNotAllowed(i)
            | Self::BadRequest(i)
            | Self::DomainError(i)
            | Self::ConnectorError(i, _) => i,
        }
    }


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

impl common_utils::events::ApiEventMetric for for RelayRetrieveRequest


File: crates/router/src/services/authentication/cookies.rs
 Public functions: 4

use cookie::Cookie;
#[cfg(feature = "olap")]
use cookie::{
    time::{Duration, OffsetDateTime},
    SameSite,
};
use error_stack::{report, ResultExt};
#[cfg(feature = "olap")]
use masking::Mask;
#[cfg(feature = "olap")]
use masking::{ExposeInterface, Secret};

use crate::{
    consts::JWT_TOKEN_COOKIE_NAME,
    core::errors::{ApiErrorResponse, RouterResult},
};
#[cfg(feature = "olap")]
use crate::{
    consts::JWT_TOKEN_TIME_IN_SECS,
    core::errors::{UserErrors, UserResponse},
    services::ApplicationResponse,
};

#[cfg(feature = "olap")]
pub fn set_cookie_response<R>(response: R, token: Secret<String>) -> UserResponse<R> {
    let jwt_expiry_in_seconds = JWT_TOKEN_TIME_IN_SECS
        .try_into()
        .map_err(|_| UserErrors::InternalServerError)?;
    let (expiry, max_age) = get_expiry_and_max_age_from_seconds(jwt_expiry_in_seconds);

    let header_value = create_cookie(token, expiry, max_age)
        .to_string()
        .into_masked();
    let header_key = get_set_cookie_header();
    let header = vec![(header_key, header_value)];

    Ok(ApplicationResponse::JsonWithHeaders((response, header)))
}

#[cfg(feature = "olap")]
pub fn remove_cookie_response() -> UserResponse<()> {
    let (expiry, max_age) = get_expiry_and_max_age_from_seconds(0);

    let header_key = get_set_cookie_header();
    let header_value = create_cookie("".to_string().into(), expiry, max_age)
        .to_string()
        .into_masked();
    let header = vec![(header_key, header_value)];
    Ok(ApplicationResponse::JsonWithHeaders(((), header)))
}

pub fn get_jwt_from_cookies(cookies: &str) -> RouterResult<String> {
    Cookie::split_parse(cookies)
        .find_map(|cookie| {
            cookie
                .ok()
                .filter(|parsed_cookie| parsed_cookie.name() == JWT_TOKEN_COOKIE_NAME)
                .map(|parsed_cookie| parsed_cookie.value().to_owned())
        })
        .ok_or(report!(ApiErrorResponse::InvalidJwtToken))
        .attach_printable("Unable to find JWT token in cookies")
}

#[cfg(feature = "olap")]
fn create_cookie<'c>(
    token: Secret<String>,
    expires: OffsetDateTime,
    max_age: Duration,
) -> Cookie<'c> {
    Cookie::build((JWT_TOKEN_COOKIE_NAME, token.expose()))
        .http_only(true)
        .secure(true)
        .same_site(SameSite::Strict)
        .path("/")
        .expires(expires)
        .max_age(max_age)
        .build()
}

#[cfg(feature = "olap")]
fn get_expiry_and_max_age_from_seconds(seconds: i64) -> (OffsetDateTime, Duration) {
    let max_age = Duration::seconds(seconds);
    let expiry = OffsetDateTime::now_utc().saturating_add(max_age);
    (expiry, max_age)
}

#[cfg(feature = "olap")]
fn get_set_cookie_header() -> String {
    actix_http::header::SET_COOKIE.to_string()
}

pub fn get_cookie_header() -> String {
    actix_http::header::COOKIE.to_string()
}