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

mod aci_result_codes;
pub mod transformers;

use std::sync::LazyLock;

use api_models::webhooks::IncomingWebhookEvent;
use common_enums::enums;
use common_utils::{
    crypto,
    errors::{CryptoError, CustomResult},
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorIntegration, ConnectorSpecifications, ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{
        PaymentsAuthorizeType, PaymentsCaptureType, PaymentsSyncType, PaymentsVoidType,
        RefundExecuteType, Response,
    },
    webhooks::{IncomingWebhook, IncomingWebhookRequestDetails},
};
use masking::{Mask, PeekInterface};
use ring::aead::{self, UnboundKey};
use transformers as aci;

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

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

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

impl ConnectorCommon for Aci {
    fn id(&self) -> &'static str {
        "aci"
    }
    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Base
    }
    fn common_get_content_type(&self) -> &'static str {
        "application/x-www-form-urlencoded"
    }

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

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

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: aci::AciErrorResponse = res
            .response
            .parse_struct("AciErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_error_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.result.code,
            message: response.result.description,
            reason: response.result.parameter_errors.map(|errors| {
                errors
                    .into_iter()
                    .map(|error_description| {
                        format!(
                            "Field is {} and the message is {}",
                            error_description.name, error_description.message
                        )
                    })
                    .collect::<Vec<String>>()
                    .join("; ")
            }),
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorValidation for Aci {
    fn validate_mandate_payment(
        &self,
        pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        let mandate_supported_pmd = std::collections::HashSet::from([PaymentMethodDataType::Card]);
        is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id())
    }
}

impl api::Payment for Aci {}

impl api::PaymentAuthorize for Aci {}
impl api::PaymentSync for Aci {}
impl api::PaymentVoid for Aci {}
impl api::PaymentCapture for Aci {}
impl api::PaymentSession for Aci {}
impl api::ConnectorAccessToken for Aci {}
impl api::PaymentToken for Aci {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Aci
{
    fn build_request(
        &self,
        _req: &RouterData<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "Payment method tokenization not supported".to_string(),
            connector: "ACI",
        }
        .into())
    }
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Aci {
    fn build_request(
        &self,
        _req: &RouterData<Session, PaymentsSessionData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "Payment sessions not supported".to_string(),
            connector: "ACI",
        }
        .into())
    }
}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Aci {
    fn build_request(
        &self,
        _req: &RouterData<AccessTokenAuth, AccessTokenRequestData, AccessToken>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "Access token authentication not supported".to_string(),
            connector: "ACI",
        }
        .into())
    }
}

impl api::MandateSetup for Aci {}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Aci {
    fn get_headers(
        &self,
        req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.common_get_content_type().to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

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

    fn get_url(
        &self,
        _req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}v1/registrations", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = aci::AciMandateRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<
        RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        errors::ConnectorError,
    > {
        let response: aci::AciMandateResponse = res
            .response
            .parse_struct("AciMandateResponse")
            .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)
    }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Aci {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PaymentsCaptureType::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_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

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

    fn get_request_body(
        &self,
        req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount_to_capture,
            req.request.currency,
        )?;
        let connector_router_data = aci::AciRouterData::from((amount, req));
        let connector_req = aci::AciCaptureRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

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

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

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Aci {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PaymentsSyncType::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_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let auth = aci::AciAuthType::try_from(&req.connector_auth_type)?;
        Ok(format!(
            "{}{}{}{}{}",
            self.base_url(connectors),
            "v1/payments/",
            req.request
                .connector_transaction_id
                .get_connector_transaction_id()
                .change_context(errors::ConnectorError::MissingConnectorTransactionID)?,
            "?entityId=",
            auth.entity_id.peek()
        ))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError>
    where
        PaymentsSyncData: Clone,
        PaymentsResponseData: Clone,
    {
        let response: aci::AciPaymentsResponse =
            res.response
                .parse_struct("AciPaymentsResponse")
                .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)
    }
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Aci {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::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_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        match req.request.connector_mandate_id() {
            Some(mandate_id) => Ok(format!(
                "{}v1/registrations/{}/payments",
                self.base_url(connectors),
                mandate_id
            )),
            _ => Ok(format!("{}{}", self.base_url(connectors), "v1/payments")),
        }
    }

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

        let connector_router_data = aci::AciRouterData::from((amount, req));
        let connector_req = aci::AciPaymentsRequest::try_from(&connector_router_data)?;

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

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

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

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Aci {
    fn get_headers(
        &self,
        req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::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_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let id = &req.request.connector_transaction_id;
        Ok(format!("{}v1/payments/{}", self.base_url(connectors), id))
    }

    fn get_request_body(
        &self,
        req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = aci::AciCancelRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(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: aci::AciPaymentsResponse =
            res.response
                .parse_struct("AciPaymentsResponse")
                .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)
    }
}

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

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Aci {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            RefundExecuteType::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_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

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

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

        let connector_router_data = aci::AciRouterData::from((amount, req));
        let connector_req = aci::AciRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::FormUrlEncoded(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(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: aci::AciRefundResponse = res
            .response
            .parse_struct("AciRefundResponse")
            .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::ResponseDeserializationFailed)
    }
    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

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

/// Decrypts an AES-256-GCM encrypted payload where the IV, auth tag, and ciphertext
/// are provided separately as hex strings. This is specifically tailored for ACI webhooks.
///
/// # Arguments
/// * `hex_key`: The encryption key as a hex string (must decode to 32 bytes).
/// * `hex_iv`: The initialization vector (nonce) as a hex string (must decode to 12 bytes).
/// * `hex_auth_tag`: The authentication tag as a hex string (must decode to 16 bytes).
/// * `hex_encrypted_body`: The encrypted payload as a hex string.
fn decrypt_aci_webhook_payload(
    hex_key: &str,
    hex_iv: &str,
    hex_auth_tag: &str,
    hex_encrypted_body: &str,
) -> CustomResult<Vec<u8>, CryptoError> {
    let key_bytes = hex::decode(hex_key)
        .change_context(CryptoError::DecodingFailed)
        .attach_printable("Failed to decode hex key")?;
    let iv_bytes = hex::decode(hex_iv)
        .change_context(CryptoError::DecodingFailed)
        .attach_printable("Failed to decode hex IV")?;
    let auth_tag_bytes = hex::decode(hex_auth_tag)
        .change_context(CryptoError::DecodingFailed)
        .attach_printable("Failed to decode hex auth tag")?;
    let encrypted_body_bytes = hex::decode(hex_encrypted_body)
        .change_context(CryptoError::DecodingFailed)
        .attach_printable("Failed to decode hex encrypted body")?;
    if key_bytes.len() != 32 {
        return Err(CryptoError::InvalidKeyLength)
            .attach_printable("Key must be 32 bytes for AES-256-GCM");
    }
    if iv_bytes.len() != aead::NONCE_LEN {
        return Err(CryptoError::InvalidIvLength)
            .attach_printable(format!("IV must be {} bytes for AES-GCM", aead::NONCE_LEN));
    }
    if auth_tag_bytes.len() != 16 {
        return Err(CryptoError::InvalidTagLength)
            .attach_printable("Auth tag must be 16 bytes for AES-256-GCM");
    }

    let unbound_key = UnboundKey::new(&aead::AES_256_GCM, &key_bytes)
        .change_context(CryptoError::DecodingFailed)
        .attach_printable("Failed to create unbound key")?;

    let less_safe_key = aead::LessSafeKey::new(unbound_key);

    let nonce_arr: [u8; aead::NONCE_LEN] = iv_bytes
        .as_slice()
        .try_into()
        .map_err(|_| CryptoError::InvalidIvLength)
        .attach_printable_lazy(|| {
            format!(
                "IV length is {} but expected {}",
                iv_bytes.len(),
                aead::NONCE_LEN
            )
        })?;
    let nonce = aead::Nonce::assume_unique_for_key(nonce_arr);

    let mut ciphertext_and_tag = encrypted_body_bytes;
    ciphertext_and_tag.extend_from_slice(&auth_tag_bytes);

    less_safe_key
        .open_in_place(nonce, aead::Aad::empty(), &mut ciphertext_and_tag)
        .change_context(CryptoError::DecodingFailed)
        .attach_printable("Failed to decrypt payload using LessSafeKey")?;

    let original_ciphertext_len = ciphertext_and_tag.len() - auth_tag_bytes.len();
    ciphertext_and_tag.truncate(original_ciphertext_len);

    Ok(ciphertext_and_tag)
}

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

    fn get_webhook_source_verification_signature(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let header_value_str = request
            .headers
            .get("X-Authentication-Tag")
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)
            .attach_printable("Missing X-Authentication-Tag header")?
            .to_str()
            .map_err(|_| errors::ConnectorError::WebhookSignatureNotFound)
            .attach_printable("Invalid X-Authentication-Tag header value (not UTF-8)")?;
        Ok(header_value_str.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 webhook_secret_str = String::from_utf8(connector_webhook_secrets.secret.to_vec())
            .map_err(|_| errors::ConnectorError::WebhookVerificationSecretInvalid)
            .attach_printable("ACI webhook secret is not a valid UTF-8 string")?;

        let iv_hex_str = request
            .headers
            .get("X-Initialization-Vector")
            .ok_or(errors::ConnectorError::WebhookSourceVerificationFailed)
            .attach_printable("Missing X-Initialization-Vector header")?
            .to_str()
            .map_err(|_| errors::ConnectorError::WebhookSourceVerificationFailed)
            .attach_printable("Invalid X-Initialization-Vector header value (not UTF-8)")?;

        let auth_tag_hex_str = request
            .headers
            .get("X-Authentication-Tag")
            .ok_or(errors::ConnectorError::WebhookSourceVerificationFailed)
            .attach_printable("Missing X-Authentication-Tag header")?
            .to_str()
            .map_err(|_| errors::ConnectorError::WebhookSourceVerificationFailed)
            .attach_printable("Invalid X-Authentication-Tag header value (not UTF-8)")?;

        let encrypted_body_hex = String::from_utf8(request.body.to_vec())
            .map_err(|_| errors::ConnectorError::WebhookBodyDecodingFailed)
            .attach_printable(
                "Failed to read encrypted body as UTF-8 string for verification message",
            )?;

        decrypt_aci_webhook_payload(
            &webhook_secret_str,
            iv_hex_str,
            auth_tag_hex_str,
            &encrypted_body_hex,
        )
        .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
        .attach_printable("Failed to decrypt ACI webhook payload for verification")
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let aci_notification: aci::AciWebhookNotification =
            serde_json::from_slice(request.body)
                .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)
                .attach_printable("Failed to deserialize ACI webhook notification for ID extraction (expected decrypted payload)")?;

        let id_value_str = aci_notification
            .payload
            .get("id")
            .and_then(|id| id.as_str())
            .ok_or_else(|| {
                report!(errors::ConnectorError::WebhookResourceObjectNotFound)
                    .attach_printable("Missing 'id' in webhook payload for ID extraction")
            })?;

        let payment_type_str = aci_notification
            .payload
            .get("paymentType")
            .and_then(|pt| pt.as_str());

        if payment_type_str.is_some_and(|pt| pt.to_uppercase() == "RF") {
            Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                api_models::webhooks::RefundIdType::ConnectorRefundId(id_value_str.to_string()),
            ))
        } else {
            Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::ConnectorTransactionId(
                    id_value_str.to_string(),
                ),
            ))
        }
    }

    fn get_webhook_event_type(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        let aci_notification: aci::AciWebhookNotification =
            serde_json::from_slice(request.body)
                .change_context(errors::ConnectorError::WebhookEventTypeNotFound)
                 .attach_printable("Failed to deserialize ACI webhook notification for event type (expected decrypted payload)")?;

        match aci_notification.event_type {
            aci::AciWebhookEventType::Payment => {
                let payment_payload: aci::AciPaymentWebhookPayload =
                    serde_json::from_value(aci_notification.payload)
                        .change_context(errors::ConnectorError::WebhookEventTypeNotFound)
                        .attach_printable("Could not deserialize ACI payment webhook payload for event type determination")?;

                let code = &payment_payload.result.code;
                if aci_result_codes::SUCCESSFUL_CODES.contains(&code.as_str()) {
                    if payment_payload.payment_type.to_uppercase() == "RF" {
                        Ok(IncomingWebhookEvent::RefundSuccess)
                    } else {
                        Ok(IncomingWebhookEvent::PaymentIntentSuccess)
                    }
                } else if aci_result_codes::PENDING_CODES.contains(&code.as_str()) {
                    if payment_payload.payment_type.to_uppercase() == "RF" {
                        Ok(IncomingWebhookEvent::EventNotSupported)
                    } else {
                        Ok(IncomingWebhookEvent::PaymentIntentProcessing)
                    }
                } else if aci_result_codes::FAILURE_CODES.contains(&code.as_str()) {
                    if payment_payload.payment_type.to_uppercase() == "RF" {
                        Ok(IncomingWebhookEvent::RefundFailure)
                    } else {
                        Ok(IncomingWebhookEvent::PaymentIntentFailure)
                    }
                } else {
                    Ok(IncomingWebhookEvent::EventNotSupported)
                }
            }
        }
    }

    fn get_webhook_resource_object(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let aci_notification: aci::AciWebhookNotification =
            serde_json::from_slice(request.body)
                .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)
                .attach_printable("Failed to deserialize ACI webhook notification for resource object (expected decrypted payload)")?;

        match aci_notification.event_type {
            aci::AciWebhookEventType::Payment => {
                let payment_payload: aci::AciPaymentWebhookPayload =
                    serde_json::from_value(aci_notification.payload)
                        .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)
                        .attach_printable("Failed to deserialize ACI payment webhook payload")?;
                Ok(Box::new(payment_payload))
            }
        }
    }
}


// Function: can_user_access_theme
// File: crates/router/src/utils/user/theme.rs
// Module: router

pub fn can_user_access_theme(
    user: &UserFromToken,
    user_entity_type: &EntityType,
    theme: &Theme,
) -> UserResult<()>


// Function: insert_payment_token_reference_record
// File: crates/diesel_models/src/payment_method.rs
// Module: diesel_models

// Documentation: Insert a new payment token reference for the given connector_id

pub fn insert_payment_token_reference_record(
        &mut self,
        connector_id: &common_utils::id_type::MerchantConnectorAccountId,
        record: ConnectorTokenReferenceRecord,
    )


// Struct: JuspaythreedsserverCard
// File: crates/hyperswitch_connectors/src/connectors/juspaythreedsserver/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct JuspaythreedsserverCard


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

impl Responder


// Struct: ErrorDetails
// File: crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ErrorDetails


// File: crates/masking/src/vec.rs
// Module: masking

//! Secret `Vec` types
//!
//! There is not alias type by design.

#[cfg(feature = "serde")]
use super::{SerializableSecret, Serialize};

#[cfg(feature = "serde")]
impl<S: Serialize> SerializableSecret for Vec<S> {}


// Function: delete_disputes
// File: crates/diesel_models/src/query/user/sample_data.rs
// Module: diesel_models

pub fn delete_disputes(
    conn: &PgPooledConn,
    merchant_id: &common_utils::id_type::MerchantId,
) -> StorageResult<Vec<Dispute>>


// Function: log_refund
// File: crates/router/src/services/kafka.rs
// Module: router

pub fn log_refund(
        &self,
        refund: &Refund,
        old_refund: Option<Refund>,
        tenant_id: TenantID,
    ) -> MQResult<()>


// Function: update_merchant_default_config
// File: crates/router/src/core/routing/helpers.rs
// Module: router

// Documentation: Merchant's already created config can be updated and this change will be reflected in DB as well for the particular updated config

pub fn update_merchant_default_config(
    db: &dyn StorageInterface,
    merchant_id: &str,
    connectors: Vec<routing_types::RoutableConnectorChoice>,
    transaction_type: &storage::enums::TransactionType,
) -> RouterResult<()>


// Struct: RecoveryAction
// File: crates/router/src/core/webhooks/recovery_incoming.rs
// Module: router
// Implementations: 1

pub struct RecoveryAction


// Function: is_all_none
// File: crates/api_models/src/analytics/search.rs
// Module: api_models

pub fn is_all_none(&self) -> bool


// Struct: PaymentsResponseBankRedirect
// File: crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaymentsResponseBankRedirect


// Function: call_connector_service
// File: crates/router/src/core/payments.rs
// Module: router

pub fn call_connector_service<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    call_connector_action: CallConnectorAction,
    validate_result: &operations::ValidateResult,
    schedule_time: Option<time::PrimitiveDateTime>,
    header_payload: HeaderPayload,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
    is_retry_payment: bool,
    return_raw_connector_response: Option<bool>,
    merchant_connector_account: helpers::MerchantConnectorAccountType,
    mut router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    tokenization_action: TokenizationAction,
) -> RouterResult<(
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    helpers::MerchantConnectorAccountType,
)>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,


// Struct: InespayPSyncResponseData
// File: crates/hyperswitch_connectors/src/connectors/inespay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct InespayPSyncResponseData


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

//! Secrets management interface

pub mod secret_handler;

pub mod secret_state;

use common_utils::errors::CustomResult;
use masking::Secret;

/// Trait defining the interface for managing application secrets
#[async_trait::async_trait]
pub trait SecretManagementInterface: Send + Sync {
    /*
    /// Given an input, encrypt/store the secret
    async fn store_secret(
        &self,
        input: Secret<String>,
    ) -> CustomResult<String, SecretsManagementError>;
    */

    /// Given an input, decrypt/retrieve the secret
    async fn get_secret(
        &self,
        input: Secret<String>,
    ) -> CustomResult<Secret<String>, SecretsManagementError>;
}

/// Errors that may occur during secret management
#[derive(Debug, thiserror::Error)]
pub enum SecretsManagementError {
    /// An error occurred when retrieving raw data.
    #[error("Failed to fetch the raw data")]
    FetchSecretFailed,

    /// Failed while creating kms client
    #[error("Failed while creating a secrets management client")]
    ClientCreationFailed,
}


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

impl Responder


// Implementation: impl AuthenticationEligibilityRequest
// File: crates/api_models/src/authentication.rs
// Module: api_models
// Methods: 4 total (4 public)

impl AuthenticationEligibilityRequest


// Function: construct_uas_router_data
// File: crates/router/src/core/unified_authentication_service/utils.rs
// Module: router

pub fn construct_uas_router_data<F: Clone, Req, Res>(
    state: &SessionState,
    authentication_connector_name: String,
    payment_method: PaymentMethod,
    merchant_id: common_utils::id_type::MerchantId,
    address: Option<PaymentAddress>,
    request_data: Req,
    merchant_connector_account: &payments::helpers::MerchantConnectorAccountType,
    authentication_id: Option<common_utils::id_type::AuthenticationId>,
    payment_id: Option<common_utils::id_type::PaymentId>,
) -> RouterResult<RouterData<F, Req, Res>>


// Function: update
// File: crates/diesel_models/src/query/relay.rs
// Module: diesel_models

pub fn update(
        self,
        conn: &PgPooledConn,
        relay: RelayUpdateInternal,
    ) -> StorageResult<Self>


// Implementation: impl webhooks::IncomingWebhook for for Deutschebank
// File: crates/hyperswitch_connectors/src/connectors/deutschebank.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl webhooks::IncomingWebhook for for Deutschebank


// File: crates/analytics/src/refunds/core.rs
// Module: analytics
// Public functions: 2

#![allow(dead_code)]
use std::collections::{HashMap, HashSet};

use api_models::analytics::{
    refunds::{
        RefundDimensions, RefundDistributions, RefundMetrics, RefundMetricsBucketIdentifier,
        RefundMetricsBucketResponse,
    },
    GetRefundFilterRequest, GetRefundMetricRequest, RefundFilterValue, RefundFiltersResponse,
    RefundsAnalyticsMetadata, RefundsMetricsResponse,
};
use bigdecimal::ToPrimitive;
use common_enums::Currency;
use common_utils::errors::CustomResult;
use currency_conversion::{conversion::convert, types::ExchangeRates};
use error_stack::ResultExt;
use router_env::{
    logger,
    tracing::{self, Instrument},
};

use super::{
    distribution::RefundDistributionRow,
    filters::{get_refund_filter_for_dimension, RefundFilterRow},
    metrics::RefundMetricRow,
    RefundMetricsAccumulator,
};
use crate::{
    enums::AuthInfo,
    errors::{AnalyticsError, AnalyticsResult},
    metrics,
    refunds::{accumulator::RefundDistributionAccumulator, RefundMetricAccumulator},
    AnalyticsProvider,
};

#[derive(Debug)]
pub enum TaskType {
    MetricTask(
        RefundMetrics,
        CustomResult<HashSet<(RefundMetricsBucketIdentifier, RefundMetricRow)>, AnalyticsError>,
    ),
    DistributionTask(
        RefundDistributions,
        CustomResult<Vec<(RefundMetricsBucketIdentifier, RefundDistributionRow)>, AnalyticsError>,
    ),
}

pub async fn get_metrics(
    pool: &AnalyticsProvider,
    ex_rates: &Option<ExchangeRates>,
    auth: &AuthInfo,
    req: GetRefundMetricRequest,
) -> AnalyticsResult<RefundsMetricsResponse<RefundMetricsBucketResponse>> {
    let mut metrics_accumulator: HashMap<RefundMetricsBucketIdentifier, RefundMetricsAccumulator> =
        HashMap::new();
    let mut set = tokio::task::JoinSet::new();
    for metric_type in req.metrics.iter().cloned() {
        let req = req.clone();
        let pool = pool.clone();
        let task_span = tracing::debug_span!(
            "analytics_refund_query",
            refund_metric = metric_type.as_ref()
        );
        // Currently JoinSet works with only static lifetime references even if the task pool does not outlive the given reference
        // We can optimize away this clone once that is fixed
        let auth_scoped = auth.to_owned();
        set.spawn(
            async move {
                let data = pool
                    .get_refund_metrics(
                        &metric_type,
                        &req.group_by_names.clone(),
                        &auth_scoped,
                        &req.filters,
                        req.time_series.map(|t| t.granularity),
                        &req.time_range,
                    )
                    .await
                    .change_context(AnalyticsError::UnknownError);
                TaskType::MetricTask(metric_type, data)
            }
            .instrument(task_span),
        );
    }

    if let Some(distribution) = req.clone().distribution {
        let req = req.clone();
        let pool = pool.clone();
        let task_span = tracing::debug_span!(
            "analytics_refunds_distribution_query",
            refund_distribution = distribution.distribution_for.as_ref()
        );

        let auth_scoped = auth.to_owned();
        set.spawn(
            async move {
                let data = pool
                    .get_refund_distribution(
                        &distribution,
                        &req.group_by_names.clone(),
                        &auth_scoped,
                        &req.filters,
                        &req.time_series.map(|t| t.granularity),
                        &req.time_range,
                    )
                    .await
                    .change_context(AnalyticsError::UnknownError);
                TaskType::DistributionTask(distribution.distribution_for, data)
            }
            .instrument(task_span),
        );
    }

    while let Some(task_type) = set
        .join_next()
        .await
        .transpose()
        .change_context(AnalyticsError::UnknownError)?
    {
        match task_type {
            TaskType::MetricTask(metric, data) => {
                let data = data?;
                let attributes = router_env::metric_attributes!(
                    ("metric_type", metric.to_string()),
                    ("source", pool.to_string()),
                );

                let value = u64::try_from(data.len());
                if let Ok(val) = value {
                    metrics::BUCKETS_FETCHED.record(val, attributes);
                    logger::debug!("Attributes: {:?}, Buckets fetched: {}", attributes, val);
                }

                for (id, value) in data {
                    logger::debug!(bucket_id=?id, bucket_value=?value, "Bucket row for metric {metric}");
                    let metrics_builder = metrics_accumulator.entry(id).or_default();
                    match metric {
                        RefundMetrics::RefundSuccessRate
                        | RefundMetrics::SessionizedRefundSuccessRate => metrics_builder
                            .refund_success_rate
                            .add_metrics_bucket(&value),
                        RefundMetrics::RefundCount | RefundMetrics::SessionizedRefundCount => {
                            metrics_builder.refund_count.add_metrics_bucket(&value)
                        }
                        RefundMetrics::RefundSuccessCount
                        | RefundMetrics::SessionizedRefundSuccessCount => {
                            metrics_builder.refund_success.add_metrics_bucket(&value)
                        }
                        RefundMetrics::RefundProcessedAmount
                        | RefundMetrics::SessionizedRefundProcessedAmount => {
                            metrics_builder.processed_amount.add_metrics_bucket(&value)
                        }
                        RefundMetrics::SessionizedRefundReason => {
                            metrics_builder.refund_reason.add_metrics_bucket(&value)
                        }
                        RefundMetrics::SessionizedRefundErrorMessage => metrics_builder
                            .refund_error_message
                            .add_metrics_bucket(&value),
                    }
                }

                logger::debug!(
                    "Analytics Accumulated Results: metric: {}, results: {:#?}",
                    metric,
                    metrics_accumulator
                );
            }
            TaskType::DistributionTask(distribution, data) => {
                let data = data?;
                let attributes = router_env::metric_attributes!(
                    ("distribution_type", distribution.to_string()),
                    ("source", pool.to_string()),
                );
                let value = u64::try_from(data.len());
                if let Ok(val) = value {
                    metrics::BUCKETS_FETCHED.record(val, attributes);
                    logger::debug!("Attributes: {:?}, Buckets fetched: {}", attributes, val);
                }

                for (id, value) in data {
                    logger::debug!(bucket_id=?id, bucket_value=?value, "Bucket row for distribution {distribution}");

                    let metrics_builder = metrics_accumulator.entry(id).or_default();
                    match distribution {
                        RefundDistributions::SessionizedRefundReason => metrics_builder
                            .refund_reason_distribution
                            .add_distribution_bucket(&value),
                        RefundDistributions::SessionizedRefundErrorMessage => metrics_builder
                            .refund_error_message_distribution
                            .add_distribution_bucket(&value),
                    }
                }
                logger::debug!(
                    "Analytics Accumulated Results: distribution: {}, results: {:#?}",
                    distribution,
                    metrics_accumulator
                );
            }
        }
    }

    let mut success = 0;
    let mut total = 0;
    let mut total_refund_processed_amount = 0;
    let mut total_refund_processed_amount_in_usd = 0;
    let mut total_refund_processed_count = 0;
    let mut total_refund_reason_count = 0;
    let mut total_refund_error_message_count = 0;
    let query_data: Vec<RefundMetricsBucketResponse> = metrics_accumulator
        .into_iter()
        .map(|(id, val)| {
            let mut collected_values = val.collect();
            if let Some(success_count) = collected_values.successful_refunds {
                success += success_count;
            }
            if let Some(total_count) = collected_values.total_refunds {
                total += total_count;
            }
            if let Some(amount) = collected_values.refund_processed_amount {
                let amount_in_usd = if let Some(ex_rates) = ex_rates {
                    id.currency
                        .and_then(|currency| {
                            i64::try_from(amount)
                                .inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
                                .ok()
                                .and_then(|amount_i64| {
                                    convert(ex_rates, currency, Currency::USD, amount_i64)
                                        .inspect_err(|e| {
                                            logger::error!("Currency conversion error: {:?}", e)
                                        })
                                        .ok()
                                })
                        })
                        .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
                        .unwrap_or_default()
                } else {
                    None
                };
                collected_values.refund_processed_amount_in_usd = amount_in_usd;
                total_refund_processed_amount += amount;
                total_refund_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
            }
            if let Some(count) = collected_values.refund_processed_count {
                total_refund_processed_count += count;
            }
            if let Some(total_count) = collected_values.refund_reason_count {
                total_refund_reason_count += total_count;
            }
            if let Some(total_count) = collected_values.refund_error_message_count {
                total_refund_error_message_count += total_count;
            }
            RefundMetricsBucketResponse {
                values: collected_values,
                dimensions: id,
            }
        })
        .collect();
    let total_refund_success_rate = match (success, total) {
        (s, t) if t > 0 => Some(f64::from(s) * 100.0 / f64::from(t)),
        _ => None,
    };
    Ok(RefundsMetricsResponse {
        query_data,
        meta_data: [RefundsAnalyticsMetadata {
            total_refund_success_rate,
            total_refund_processed_amount: Some(total_refund_processed_amount),
            total_refund_processed_amount_in_usd: if ex_rates.is_some() {
                Some(total_refund_processed_amount_in_usd)
            } else {
                None
            },
            total_refund_processed_count: Some(total_refund_processed_count),
            total_refund_reason_count: Some(total_refund_reason_count),
            total_refund_error_message_count: Some(total_refund_error_message_count),
        }],
    })
}

pub async fn get_filters(
    pool: &AnalyticsProvider,
    req: GetRefundFilterRequest,
    auth: &AuthInfo,
) -> AnalyticsResult<RefundFiltersResponse> {
    let mut res = RefundFiltersResponse::default();
    for dim in req.group_by_names {
        let values = match pool {
                        AnalyticsProvider::Sqlx(pool) => {
                get_refund_filter_for_dimension(dim, auth, &req.time_range, pool)
                    .await
            }
                        AnalyticsProvider::Clickhouse(pool) => {
                get_refund_filter_for_dimension(dim, auth, &req.time_range, pool)
                    .await
            }
                    AnalyticsProvider::CombinedCkh(sqlx_pool, ckh_pool) => {
                let ckh_result = get_refund_filter_for_dimension(
                    dim,
                    auth,
                    &req.time_range,
                    ckh_pool,
                )
                .await;
                let sqlx_result = get_refund_filter_for_dimension(
                    dim,
                    auth,
                    &req.time_range,
                    sqlx_pool,
                )
                .await;
                match (&sqlx_result, &ckh_result) {
                    (Ok(ref sqlx_res), Ok(ref ckh_res)) if sqlx_res != ckh_res => {
                        router_env::logger::error!(clickhouse_result=?ckh_res, postgres_result=?sqlx_res, "Mismatch between clickhouse & postgres refunds analytics filters")
                    },
                    _ => {}
                };
                ckh_result
            }
                    AnalyticsProvider::CombinedSqlx(sqlx_pool, ckh_pool) => {
                let ckh_result = get_refund_filter_for_dimension(
                    dim,
                    auth,
                    &req.time_range,
                    ckh_pool,
                )
                .await;
                let sqlx_result = get_refund_filter_for_dimension(
                    dim,
                    auth,
                    &req.time_range,
                    sqlx_pool,
                )
                .await;
                match (&sqlx_result, &ckh_result) {
                    (Ok(ref sqlx_res), Ok(ref ckh_res)) if sqlx_res != ckh_res => {
                        router_env::logger::error!(clickhouse_result=?ckh_res, postgres_result=?sqlx_res, "Mismatch between clickhouse & postgres refunds analytics filters")
                    },
                    _ => {}
                };
                sqlx_result
            }
        }
        .change_context(AnalyticsError::UnknownError)?
        .into_iter()
        .filter_map(|fil: RefundFilterRow| match dim {
            RefundDimensions::Currency => fil.currency.map(|i| i.as_ref().to_string()),
            RefundDimensions::RefundStatus => fil.refund_status.map(|i| i.as_ref().to_string()),
            RefundDimensions::Connector => fil.connector,
            RefundDimensions::RefundType => fil.refund_type.map(|i| i.as_ref().to_string()),
            RefundDimensions::ProfileId => fil.profile_id,
            RefundDimensions::RefundReason => fil.refund_reason,
            RefundDimensions::RefundErrorMessage => fil.refund_error_message,
        })
        .collect::<Vec<String>>();
        res.query_data.push(RefundFilterValue {
            dimension: dim,
            values,
        })
    }
    Ok(res)
}


// Function: should_initiate_capture_flow
// File: crates/router/src/core/payments/flows.rs
// Module: router

// Documentation: Determines whether a capture API call should be made for a payment attempt This function evaluates whether an authorized payment should proceed with a capture API call based on various payment parameters. It's primarily used in two-step (auth + capture) payment flows for CaptureMethod SequentialAutomatic

pub fn should_initiate_capture_flow(
    connector_name: &router_types::Connector,
    customer_acceptance: Option<CustomerAcceptance>,
    capture_method: Option<api_enums::CaptureMethod>,
    setup_future_usage: Option<api_enums::FutureUsage>,
    status: common_enums::AttemptStatus,
) -> bool


    fn should_return_error_when_missing_multiple_fields() {
        // Arrange
        let expected_missing_field_names: Vec<&'static str> =
            vec!["shipping.address.zip", "shipping.address.country"];
        let stripe_shipping_address = create_stripe_shipping_address(
            "name".to_string(),
            Some("line1".to_string()),
            None,
            None,
        );
        let payment_method = &StripePaymentMethodType::AfterpayClearpay;

        //Act
        let result = validate_shipping_address_against_payment_method(
            &Some(stripe_shipping_address),
            Some(payment_method),
        );

        // Assert
        assert!(result.is_err());
        let missing_fields = get_missing_fields(result.unwrap_err().current_context()).to_owned();
        for field in missing_fields {
            assert!(expected_missing_field_names.contains(&field));
        }
    }

    fn get_missing_fields(connector_error: &ConnectorError) -> Vec<&'static str> {
        if let ConnectorError::MissingRequiredFields { field_names } = connector_error {
            return field_names.to_vec();
        }

        vec![]
    }

    fn create_stripe_shipping_address(
        name: String,
        line1: Option<String>,
        country: Option<CountryAlpha2>,
        zip: Option<String>,
    ) -> StripeShippingAddress {
        StripeShippingAddress {
            name: Some(Secret::new(name)),
            line1: line1.map(Secret::new),
            country,
            zip: zip.map(Secret::new),
            city: Some(String::from("city")),
            line2: Some(Secret::new(String::from("line2"))),
            state: Some(Secret::new(String::from("state"))),
            phone: Some(Secret::new(String::from("pbone number"))),
        }
    }
}


// Function: get_dotted_jwe
// File: crates/router/src/core/payment_methods/transformers.rs
// Module: router

pub fn get_dotted_jwe(jwe: encryption::JweBody) -> String


// Struct: PayuRefundRequestData
// File: crates/hyperswitch_connectors/src/connectors/payu/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PayuRefundRequestData


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

impl Responder


// Trait: ConnectorCommon
// File: crates/pm_auth/src/types/api.rs
// Module: pm_auth

pub trait ConnectorCommon


#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentsMandateReferenceRecord {
    pub connector_mandate_id: String,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<i64>,
    pub original_payment_authorized_currency: Option<common_enums::Currency>,
    pub mandate_metadata: Option<pii::SecretSerdeValue>,
    pub connector_mandate_status: Option<common_enums::ConnectorMandateStatus>,
    pub connector_mandate_request_reference_id: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ConnectorTokenReferenceRecord {
    pub connector_token: String,
    pub payment_method_subtype: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<common_utils::types::MinorUnit>,
    pub original_payment_authorized_currency: Option<common_enums::Currency>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_token_status: common_enums::ConnectorTokenStatus,
    pub connector_token_request_reference_id: Option<String>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct PaymentsMandateReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, PaymentsMandateReferenceRecord>,
);

#[cfg(feature = "v1")]
impl std::ops::Deref for PaymentsMandateReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, PaymentsMandateReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(feature = "v1")]
impl std::ops::DerefMut for PaymentsMandateReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct PaymentsTokenReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, ConnectorTokenReferenceRecord>,
);

#[cfg(feature = "v2")]
impl std::ops::Deref for PaymentsTokenReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, ConnectorTokenReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(feature = "v2")]
impl std::ops::DerefMut for PaymentsTokenReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v1")]
common_utils::impl_to_sql_from_sql_json!(PaymentsMandateReference);

#[cfg(feature = "v2")]
common_utils::impl_to_sql_from_sql_json!(PaymentsTokenReference);

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct PayoutsMandateReferenceRecord {
    pub transfer_method_id: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct PayoutsMandateReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, PayoutsMandateReferenceRecord>,
);

impl std::ops::Deref for PayoutsMandateReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, PayoutsMandateReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

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

#[cfg(feature = "v1")]
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct CommonMandateReference {
    pub payments: Option<PaymentsMandateReference>,
    pub payouts: Option<PayoutsMandateReference>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct CommonMandateReference {
    pub payments: Option<PaymentsTokenReference>,
    pub payouts: Option<PayoutsMandateReference>,
}

impl CommonMandateReference {
    pub fn get_mandate_details_value(&self) -> CustomResult<serde_json::Value, ParsingError> {
        let mut payments = self
            .payments
            .as_ref()
            .map_or_else(|| Ok(serde_json::json!({})), serde_json::to_value)
            .change_context(ParsingError::StructParseFailure("payment mandate details"))?;

        self.payouts
            .as_ref()
            .map(|payouts_mandate| {
                serde_json::to_value(payouts_mandate).map(|payouts_mandate_value| {
                    payments.as_object_mut().map(|payments_object| {
                        payments_object.insert("payouts".to_string(), payouts_mandate_value);
                    })
                })
            })
            .transpose()
            .change_context(ParsingError::StructParseFailure("payout mandate details"))?;

        Ok(payments)
    }

    #[cfg(feature = "v2")]
    /// Insert a new payment token reference for the given connector_id
    pub fn insert_payment_token_reference_record(
        &mut self,
        connector_id: &common_utils::id_type::MerchantConnectorAccountId,
        record: ConnectorTokenReferenceRecord,
    ) {
        match self.payments {
            Some(ref mut payments_reference) => {
                payments_reference.insert(connector_id.clone(), record);
            }
            None => {
                let mut payments_reference = HashMap::new();
                payments_reference.insert(connector_id.clone(), record);
                self.payments = Some(PaymentsTokenReference(payments_reference));
            }
        }
    }
}

impl diesel::serialize::ToSql<diesel::sql_types::Jsonb, diesel::pg::Pg> for CommonMandateReference {
    fn to_sql<'b>(
        &'b self,
        out: &mut diesel::serialize::Output<'b, '_, diesel::pg::Pg>,
    ) -> diesel::serialize::Result {
        let payments = self.get_mandate_details_value()?;

        <serde_json::Value as diesel::serialize::ToSql<
            diesel::sql_types::Jsonb,
            diesel::pg::Pg,
        >>::to_sql(&payments, &mut out.reborrow())
    }
}

#[cfg(feature = "v1")]
impl<DB: diesel::backend::Backend> diesel::deserialize::FromSql<diesel::sql_types::Jsonb, DB>
    for CommonMandateReference
where
    serde_json::Value: diesel::deserialize::FromSql<diesel::sql_types::Jsonb, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
        let value = <serde_json::Value as diesel::deserialize::FromSql<
            diesel::sql_types::Jsonb,
            DB,
        >>::from_sql(bytes)?;

        let payments_data = value
            .clone()
            .as_object_mut()
            .map(|obj| {
                obj.remove("payouts");

                serde_json::from_value::<PaymentsMandateReference>(serde_json::Value::Object(
                    obj.clone(),
                ))
                .inspect_err(|err| {
                    router_env::logger::error!("Failed to parse payments data: {}", err);
                })
                .change_context(ParsingError::StructParseFailure(
                    "Failed to parse payments data",
                ))
            })
            .transpose()?;

        let payouts_data = serde_json::from_value::<Option<Self>>(value)
            .inspect_err(|err| {
                router_env::logger::error!("Failed to parse payouts data: {}", err);
            })
            .change_context(ParsingError::StructParseFailure(
                "Failed to parse payouts data",
            ))
            .map(|optional_common_mandate_details| {
                optional_common_mandate_details
                    .and_then(|common_mandate_details| common_mandate_details.payouts)
            })?;

        Ok(Self {
            payments: payments_data,
            payouts: payouts_data,
        })
    }
}

#[cfg(feature = "v2")]
impl<DB: diesel::backend::Backend> diesel::deserialize::FromSql<diesel::sql_types::Jsonb, DB>
    for CommonMandateReference
where
    serde_json::Value: diesel::deserialize::FromSql<diesel::sql_types::Jsonb, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
        let value = <serde_json::Value as diesel::deserialize::FromSql<
            diesel::sql_types::Jsonb,
            DB,
        >>::from_sql(bytes)?;

        let payments_data = value
            .clone()
            .as_object_mut()
            .map(|obj| {
                obj.remove("payouts");

                serde_json::from_value::<PaymentsTokenReference>(serde_json::Value::Object(
                    obj.clone(),
                ))
                .inspect_err(|err| {
                    router_env::logger::error!("Failed to parse payments data: {}", err);
                })
                .change_context(ParsingError::StructParseFailure(
                    "Failed to parse payments data",
                ))
            })
            .transpose()?;

        let payouts_data = serde_json::from_value::<Option<Self>>(value)
            .inspect_err(|err| {
                router_env::logger::error!("Failed to parse payouts data: {}", err);
            })
            .change_context(ParsingError::StructParseFailure(
                "Failed to parse payouts data",
            ))
            .map(|optional_common_mandate_details| {
                optional_common_mandate_details
                    .and_then(|common_mandate_details| common_mandate_details.payouts)
            })?;

        Ok(Self {
            payments: payments_data,
            payouts: payouts_data,
        })
    }
}

#[cfg(feature = "v1")]
impl From<PaymentsMandateReference> for CommonMandateReference {
    fn from(payment_reference: PaymentsMandateReference) -> Self {
        Self {
            payments: Some(payment_reference),
            payouts: None,
        }
    }
}


// Function: list_routing_configs
// File: crates/router/src/routes/routing.rs
// Module: router

pub fn list_routing_configs(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<RoutingRetrieveQuery>,
    transaction_type: Option<enums::TransactionType>,
) -> impl Responder


// Function: get_auth_events_filter_for_dimension
// File: crates/analytics/src/auth_events/filters.rs
// Module: analytics

pub fn get_auth_events_filter_for_dimension<T>(
    dimension: AuthEventDimensions,
    auth: &AuthInfo,
    time_range: &TimeRange,
    pool: &T,
) -> FiltersResult<Vec<AuthEventFilterRow>>
where
    T: AnalyticsDataSource + AuthEventFilterAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,


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

impl Responder


// Trait: ReportSwitchExt
// File: crates/common_utils/src/errors.rs
// Module: common_utils

// Documentation: Allows [error_stack::Report] to change between error contexts using the dependent [ErrorSwitch] trait to define relations & mappings between traits

pub trait ReportSwitchExt<T, U>


// Function: upsert_surcharge_decision_config
// File: crates/router/src/core/surcharge_decision_config.rs
// Module: router

pub fn upsert_surcharge_decision_config(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    request: SurchargeDecisionConfigReq,
) -> RouterResponse<SurchargeDecisionManagerRecord>


// Struct: TokenizedBankTransferValue1
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Implementations: 0

pub struct TokenizedBankTransferValue1


// Trait: ValidateRequest
// File: crates/router/src/core/payments/operations.rs
// Module: router

pub trait ValidateRequest<F, R, D>


// Struct: EliminationRoutingConfig
// File: crates/api_models/src/routing.rs
// Module: api_models
// Implementations: 2
// Traits: Default

pub struct EliminationRoutingConfig


// Implementation: impl ConnectorSpecifications for for Fiservemea
// File: crates/hyperswitch_connectors/src/connectors/fiservemea.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl ConnectorSpecifications for for Fiservemea


// Struct: CoinbaseErrorData
// File: crates/hyperswitch_connectors/src/connectors/coinbase/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CoinbaseErrorData


// Function: get_end_date
// File: crates/hyperswitch_domain_models/src/mandates.rs
// Module: hyperswitch_domain_models

pub fn get_end_date(
        &self,
        format: date_time::DateFormat,
    ) -> error_stack::Result<Option<String>, ParsingError>


// Struct: CustomerAccountLink
// File: crates/hyperswitch_connectors/src/connectors/gocardless/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CustomerAccountLink


// Function: delete_totp_from_redis
// File: crates/router/src/utils/user/two_factor_auth.rs
// Module: router

pub fn delete_totp_from_redis(state: &SessionState, user_id: &str) -> UserResult<()>


// Function: transfer_key_store_to_key_manager
// File: crates/router/src/core/admin.rs
// Module: router

pub fn transfer_key_store_to_key_manager(
    state: SessionState,
    req: admin_types::MerchantKeyTransferRequest,
) -> RouterResponse<admin_types::TransferKeyResponse>


// Struct: MerchantInitiatedTransactionResponse
// File: crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MerchantInitiatedTransactionResponse


// Struct: CardTokenData
// File: crates/router/src/types/storage/payment_method.rs
// Module: router
// Implementations: 0

pub struct CardTokenData


// Struct: AUBankAccount
// File: crates/hyperswitch_connectors/src/connectors/gocardless/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AUBankAccount


// Struct: Thunes
// File: crates/hyperswitch_connectors/src/connectors/thunes.rs
// Module: hyperswitch_connectors
// Implementations: 17
// Traits: api::Payment, api::PaymentSession, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentToken, ConnectorCommon, ConnectorValidation, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct Thunes

api::Payment api::PaymentSession api::ConnectorAccessToken api::MandateSetup api::PaymentAuthorize api::PaymentSync api::PaymentCapture api::PaymentVoid api::Refund api::RefundExecute api::RefundSync api::PaymentToken ConnectorCommon ConnectorValidation webhooks::IncomingWebhook ConnectorSpecifications


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

impl Responder


// Struct: ProphetpayRefundSyncRequest
// File: crates/hyperswitch_connectors/src/connectors/prophetpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ProphetpayRefundSyncRequest


// Implementation: impl DynamicRoutingAlgorithmRef
// File: crates/api_models/src/routing.rs
// Module: api_models
// Methods: 3 total (3 public)

impl DynamicRoutingAlgorithmRef


// Implementation: impl api::PaymentSync for for Cybersource
// File: crates/hyperswitch_connectors/src/connectors/cybersource.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Cybersource


// File: crates/router/src/compatibility/stripe/payment_intents.rs
// Module: router
// Public functions: 8

pub mod types;

use actix_web::{web, HttpRequest, HttpResponse};
use api_models::payments as payment_types;
#[cfg(feature = "v1")]
use error_stack::report;
#[cfg(feature = "v1")]
use router_env::Tag;
use router_env::{instrument, tracing, Flow};

use crate::{
    compatibility::{stripe::errors, wrap},
    core::payments,
    routes::{self},
    services::{api, authentication as auth},
};
#[cfg(feature = "v1")]
use crate::{
    core::api_locking::GetLockingInput,
    logger,
    routes::payments::get_or_generate_payment_id,
    types::{api as api_types, domain},
};

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsCreate, payment_id))]
pub async fn payment_intents_create(
    state: web::Data<routes::AppState>,
    qs_config: web::Data<serde_qs::Config>,
    req: HttpRequest,
    form_payload: web::Bytes,
) -> HttpResponse {
    let payload: types::StripePaymentIntentRequest = match qs_config
        .deserialize_bytes(&form_payload)
        .map_err(|err| report!(errors::StripeErrorCode::from(err)))
    {
        Ok(p) => p,
        Err(err) => return api::log_and_return_error_response(err),
    };

    tracing::Span::current().record(
        "payment_id",
        payload
            .id
            .as_ref()
            .map(|payment_id| payment_id.get_string_repr())
            .unwrap_or_default(),
    );

    logger::info!(tag = ?Tag::CompatibilityLayerRequest, payload = ?payload);

    let mut create_payment_req: payment_types::PaymentsRequest = match payload.try_into() {
        Ok(req) => req,
        Err(err) => return api::log_and_return_error_response(err),
    };

    if let Err(err) = get_or_generate_payment_id(&mut create_payment_req) {
        return api::log_and_return_error_response(err);
    }
    let flow = Flow::PaymentsCreate;
    let locking_action = create_payment_req.get_locking_input(flow.clone());
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        create_payment_req,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            let eligible_connectors = req.connector.clone();
            payments::payments_core::<
                api_types::Authorize,
                api_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Authorize>,
            >(
                state,
                req_state,
                merchant_context,
                None,
                payments::PaymentCreate,
                req,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                eligible_connectors,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            )
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsRetrieveForceSync))]
pub async fn payment_intents_retrieve(
    state: web::Data<routes::AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::PaymentId>,
    query_payload: web::Query<types::StripePaymentRetrieveBody>,
) -> HttpResponse {
    let payload = payment_types::PaymentsRetrieveRequest {
        resource_id: api_types::PaymentIdType::PaymentIntentId(path.into_inner()),
        merchant_id: None,
        force_sync: true,
        connector: None,
        param: None,
        merchant_connector_details: None,
        client_secret: query_payload.client_secret.clone(),
        expand_attempts: None,
        expand_captures: None,
        all_keys_required: None,
    };

    let api_auth = auth::ApiKeyAuth {
        is_connected_allowed: false,
        is_platform_allowed: false,
    };

    let (auth_type, auth_flow) =
        match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth) {
            Ok(auth) => auth,
            Err(err) => return api::log_and_return_error_response(report!(err)),
        };

    let flow = Flow::PaymentsRetrieveForceSync;
    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        payload,
        |state, auth, payload, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::PSync,
                api_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::PSync>,
            >(
                state,
                req_state,
                merchant_context,
                None,
                payments::PaymentStatus,
                payload,
                auth_flow,
                payments::CallConnectorAction::Trigger,
                None,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            )
        },
        &*auth_type,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow))]
pub async fn payment_intents_retrieve_with_gateway_creds(
    state: web::Data<routes::AppState>,
    qs_config: web::Data<serde_qs::Config>,
    req: HttpRequest,
    form_payload: web::Bytes,
) -> HttpResponse {
    let json_payload: payment_types::PaymentRetrieveBodyWithCredentials = match qs_config
        .deserialize_bytes(&form_payload)
        .map_err(|err| report!(errors::StripeErrorCode::from(err)))
    {
        Ok(p) => p,
        Err(err) => return api::log_and_return_error_response(err),
    };

    let payload = payment_types::PaymentsRetrieveRequest {
        resource_id: payment_types::PaymentIdType::PaymentIntentId(json_payload.payment_id),
        merchant_id: json_payload.merchant_id.clone(),
        force_sync: json_payload.force_sync.unwrap_or(false),
        merchant_connector_details: json_payload.merchant_connector_details.clone(),
        ..Default::default()
    };

    let api_auth = auth::ApiKeyAuth {
        is_connected_allowed: false,
        is_platform_allowed: false,
    };

    let (auth_type, _auth_flow) = match auth::get_auth_type_and_flow(req.headers(), api_auth) {
        Ok(auth) => auth,
        Err(err) => return api::log_and_return_error_response(report!(err)),
    };

    let flow = match json_payload.force_sync {
        Some(true) => Flow::PaymentsRetrieveForceSync,
        _ => Flow::PaymentsRetrieve,
    };

    tracing::Span::current().record("flow", flow.to_string());

    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        payload,
        |state, auth, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::PSync,
                payment_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::PSync>,
            >(
                state,
                req_state,
                merchant_context,
                None,
                payments::PaymentStatus,
                req,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                None,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            )
        },
        &*auth_type,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsUpdate))]
pub async fn payment_intents_update(
    state: web::Data<routes::AppState>,
    qs_config: web::Data<serde_qs::Config>,
    req: HttpRequest,
    form_payload: web::Bytes,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> HttpResponse {
    let payment_id = path.into_inner();
    let stripe_payload: types::StripePaymentIntentRequest = match qs_config
        .deserialize_bytes(&form_payload)
    {
        Ok(p) => p,
        Err(err) => {
            return api::log_and_return_error_response(report!(errors::StripeErrorCode::from(err)))
        }
    };

    let mut payload: payment_types::PaymentsRequest = match stripe_payload.try_into() {
        Ok(req) => req,
        Err(err) => return api::log_and_return_error_response(err),
    };

    payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(payment_id));

    let api_auth = auth::ApiKeyAuth {
        is_connected_allowed: false,
        is_platform_allowed: false,
    };

    let (auth_type, auth_flow) = match auth::get_auth_type_and_flow(req.headers(), api_auth) {
        Ok(auth) => auth,
        Err(err) => return api::log_and_return_error_response(report!(err)),
    };

    let flow = Flow::PaymentsUpdate;
    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        payload,
        |state, auth, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            let eligible_connectors = req.connector.clone();
            payments::payments_core::<
                api_types::Authorize,
                api_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Authorize>,
            >(
                state,
                req_state,
                merchant_context,
                None,
                payments::PaymentUpdate,
                req,
                auth_flow,
                payments::CallConnectorAction::Trigger,
                eligible_connectors,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            )
        },
        &*auth_type,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsConfirm, payment_id))]
pub async fn payment_intents_confirm(
    state: web::Data<routes::AppState>,
    qs_config: web::Data<serde_qs::Config>,
    req: HttpRequest,
    form_payload: web::Bytes,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> HttpResponse {
    let payment_id = path.into_inner();
    let stripe_payload: types::StripePaymentIntentRequest = match qs_config
        .deserialize_bytes(&form_payload)
    {
        Ok(p) => p,
        Err(err) => {
            return api::log_and_return_error_response(report!(errors::StripeErrorCode::from(err)))
        }
    };

    tracing::Span::current().record(
        "payment_id",
        stripe_payload.id.as_ref().map(|id| id.get_string_repr()),
    );

    logger::info!(tag = ?Tag::CompatibilityLayerRequest, payload = ?stripe_payload);

    let mut payload: payment_types::PaymentsRequest = match stripe_payload.try_into() {
        Ok(req) => req,
        Err(err) => return api::log_and_return_error_response(err),
    };

    payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(payment_id));
    payload.confirm = Some(true);

    let api_auth = auth::ApiKeyAuth {
        is_connected_allowed: false,
        is_platform_allowed: false,
    };

    let (auth_type, auth_flow) =
        match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth) {
            Ok(auth) => auth,
            Err(err) => return api::log_and_return_error_response(err),
        };

    let flow = Flow::PaymentsConfirm;
    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        payload,
        |state, auth, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            let eligible_connectors = req.connector.clone();
            payments::payments_core::<
                api_types::Authorize,
                api_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Authorize>,
            >(
                state,
                req_state,
                merchant_context,
                None,
                payments::PaymentConfirm,
                req,
                auth_flow,
                payments::CallConnectorAction::Trigger,
                eligible_connectors,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            )
        },
        &*auth_type,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsCapture, payment_id))]
pub async fn payment_intents_capture(
    state: web::Data<routes::AppState>,
    qs_config: web::Data<serde_qs::Config>,
    req: HttpRequest,
    form_payload: web::Bytes,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> HttpResponse {
    let stripe_payload: payment_types::PaymentsCaptureRequest = match qs_config
        .deserialize_bytes(&form_payload)
    {
        Ok(p) => p,
        Err(err) => {
            return api::log_and_return_error_response(report!(errors::StripeErrorCode::from(err)))
        }
    };

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

    logger::info!(tag = ?Tag::CompatibilityLayerRequest, payload = ?stripe_payload);

    let payload = payment_types::PaymentsCaptureRequest {
        payment_id: path.into_inner(),
        ..stripe_payload
    };

    let flow = Flow::PaymentsCapture;
    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        payload,
        |state, auth: auth::AuthenticationData, payload, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::Capture,
                api_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Capture>,
            >(
                state,
                req_state,
                merchant_context,
                None,
                payments::PaymentCapture,
                payload,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                None,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            )
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsCancel, payment_id))]
pub async fn payment_intents_cancel(
    state: web::Data<routes::AppState>,
    qs_config: web::Data<serde_qs::Config>,
    req: HttpRequest,
    form_payload: web::Bytes,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> HttpResponse {
    let payment_id = path.into_inner();
    let stripe_payload: types::StripePaymentCancelRequest = match qs_config
        .deserialize_bytes(&form_payload)
    {
        Ok(p) => p,
        Err(err) => {
            return api::log_and_return_error_response(report!(errors::StripeErrorCode::from(err)))
        }
    };

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

    logger::info!(tag = ?Tag::CompatibilityLayerRequest, payload = ?stripe_payload);

    let mut payload: payment_types::PaymentsCancelRequest = stripe_payload.into();
    payload.payment_id = payment_id;

    let api_auth = auth::ApiKeyAuth {
        is_connected_allowed: false,
        is_platform_allowed: false,
    };

    let (auth_type, auth_flow) = match auth::get_auth_type_and_flow(req.headers(), api_auth) {
        Ok(auth) => auth,
        Err(err) => return api::log_and_return_error_response(report!(err)),
    };

    let flow = Flow::PaymentsCancel;
    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        payload,
        |state, auth, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::Void,
                api_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Void>,
            >(
                state,
                req_state,
                merchant_context,
                None,
                payments::PaymentCancel,
                req,
                auth_flow,
                payments::CallConnectorAction::Trigger,
                None,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            )
        },
        &*auth_type,
        locking_action,
    ))
    .await
}
#[instrument(skip_all, fields(flow = ?Flow::PaymentsList))]
#[cfg(feature = "olap")]
pub async fn payment_intent_list(
    state: web::Data<routes::AppState>,
    req: HttpRequest,
    payload: web::Query<types::StripePaymentListConstraints>,
) -> HttpResponse {
    let payload = match payment_types::PaymentListConstraints::try_from(payload.into_inner()) {
        Ok(p) => p,
        Err(err) => return api::log_and_return_error_response(err),
    };
    use crate::core::api_locking;
    let flow = Flow::PaymentsList;
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentListResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::list_payments(state, merchant_context, None, req)
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


// Function: get_frm_filter_for_dimension
// File: crates/analytics/src/frm/filters.rs
// Module: analytics

pub fn get_frm_filter_for_dimension<T>(
    dimension: FrmDimensions,
    merchant_id: &common_utils::id_type::MerchantId,
    time_range: &TimeRange,
    pool: &T,
) -> FiltersResult<Vec<FrmFilterRow>>
where
    T: AnalyticsDataSource + FrmFilterAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,


// Implementation: impl api::RefundSync for for Checkout
// File: crates/hyperswitch_connectors/src/connectors/checkout.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundSync for for Checkout