// File: crates/hyperswitch_connectors/src/connectors/xendit/transformers.rs
// Module: hyperswitch_connectors
// Public structs: 24

use std::collections::HashMap;

use cards::CardNumber;
use common_enums::{enums, Currency};
use common_utils::{pii, request::Method, types::FloatMajorUnit};
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::{
        PaymentsAuthorizeData, PaymentsCaptureData, PaymentsPreProcessingData, ResponseId,
    },
    router_response_types::{
        MandateReference, PaymentsResponseData, RedirectForm, RefundsResponseData,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsPreProcessingRouterData,
        PaymentsSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
    errors,
};
use masking::{ExposeInterface, PeekInterface, Secret};
use serde::{Deserialize, Serialize};

use crate::{
    types::{PaymentsSyncResponseRouterData, RefundsResponseRouterData, ResponseRouterData},
    utils::{
        get_unimplemented_payment_method_error_message, CardData, PaymentsAuthorizeRequestData,
        PaymentsSyncRequestData, RouterData as OtherRouterData,
    },
};

//TODO: Fill the struct with respective fields
pub struct XenditRouterData<T> {
    pub amount: FloatMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}

#[derive(Serialize, Deserialize, Debug)]
pub enum PaymentMethodType {
    CARD,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ChannelProperties {
    pub success_return_url: String,
    pub failure_return_url: String,
    pub skip_three_d_secure: bool,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum PaymentMethod {
    Card(CardPaymentRequest),
}
#[derive(Serialize, Deserialize, Debug)]
pub struct CardPaymentRequest {
    #[serde(rename = "type")]
    pub payment_type: PaymentMethodType,
    pub card: CardInfo,
    pub reusability: TransactionType,
    pub reference_id: Secret<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MandatePaymentRequest {
    pub amount: FloatMajorUnit,
    pub currency: Currency,
    pub capture_method: String,
    pub payment_method_id: Secret<String>,
    pub channel_properties: ChannelProperties,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct XenditRedirectionResponse {
    pub status: PaymentStatus,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct XenditPaymentsCaptureRequest {
    pub capture_amount: FloatMajorUnit,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct XenditPaymentsRequest {
    pub amount: FloatMajorUnit,
    pub currency: Currency,
    pub capture_method: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method: Option<PaymentMethod>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method_id: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub channel_properties: Option<ChannelProperties>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct XenditSplitRoute {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub flat_amount: Option<FloatMajorUnit>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub percent_amount: Option<i64>,
    pub currency: Currency,
    pub destination_account_id: String,
    pub reference_id: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct XenditSplitRequest {
    pub name: String,
    pub description: String,
    pub routes: Vec<XenditSplitRoute>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct XenditSplitRequestData {
    #[serde(flatten)]
    pub split_data: XenditSplitRequest,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct XenditSplitResponse {
    id: String,
    name: String,
    description: String,
    routes: Vec<XenditSplitRoute>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct CardInfo {
    pub channel_properties: ChannelProperties,
    pub card_information: CardInformation,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct CardInformation {
    pub card_number: CardNumber,
    pub expiry_month: Secret<String>,
    pub expiry_year: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cvv: Option<Secret<String>>,
    pub cardholder_name: Secret<String>,
    pub cardholder_email: pii::Email,
    pub cardholder_phone_number: Secret<String>,
}
pub mod auth_headers {
    pub const WITH_SPLIT_RULE: &str = "with-split-rule";
    pub const FOR_USER_ID: &str = "for-user-id";
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TransactionType {
    OneTimeUse,
    MultipleUse,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct XenditErrorResponse {
    pub error_code: String,
    pub message: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PaymentStatus {
    Pending,
    RequiresAction,
    Failed,
    Succeeded,
    AwaitingCapture,
    Verified,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum XenditResponse {
    Payment(XenditPaymentResponse),
    Webhook(XenditWebhookEvent),
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct XenditPaymentResponse {
    pub id: String,
    pub status: PaymentStatus,
    pub actions: Option<Vec<Action>>,
    pub payment_method: PaymentMethodInfo,
    pub failure_code: Option<String>,
    pub reference_id: Secret<String>,
    pub amount: FloatMajorUnit,
    pub currency: Currency,
}

fn map_payment_response_to_attempt_status(
    response: XenditPaymentResponse,
    is_auto_capture: bool,
) -> enums::AttemptStatus {
    match response.status {
        PaymentStatus::Failed => enums::AttemptStatus::Failure,
        PaymentStatus::Succeeded | PaymentStatus::Verified => {
            if is_auto_capture {
                enums::AttemptStatus::Charged
            } else {
                enums::AttemptStatus::Authorized
            }
        }
        PaymentStatus::Pending => enums::AttemptStatus::Pending,
        PaymentStatus::RequiresAction => enums::AttemptStatus::AuthenticationPending,
        PaymentStatus::AwaitingCapture => enums::AttemptStatus::Authorized,
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct XenditCaptureResponse {
    pub id: String,
    pub status: PaymentStatus,
    pub actions: Option<Vec<Action>>,
    pub payment_method: PaymentMethodInfo,
    pub failure_code: Option<String>,
    pub reference_id: Secret<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum MethodType {
    Get,
    Post,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Action {
    pub method: MethodType,
    pub url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentMethodInfo {
    pub id: Secret<String>,
}
impl TryFrom<XenditRouterData<&PaymentsAuthorizeRouterData>> for XenditPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: XenditRouterData<&PaymentsAuthorizeRouterData>) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(card_data) => Ok(Self {
                capture_method: match item.router_data.request.is_auto_capture()? {
                    true => "AUTOMATIC".to_string(),
                    false => "MANUAL".to_string(),
                },
                currency: item.router_data.request.currency,
                amount: item.amount,
                payment_method: Some(PaymentMethod::Card(CardPaymentRequest {
                    payment_type: PaymentMethodType::CARD,
                    reference_id: Secret::new(
                        item.router_data.connector_request_reference_id.clone(),
                    ),
                    card: CardInfo {
                        channel_properties: ChannelProperties {
                            success_return_url: item.router_data.request.get_router_return_url()?,
                            failure_return_url: item.router_data.request.get_router_return_url()?,
                            skip_three_d_secure: !item.router_data.is_three_ds(),
                        },
                        card_information: CardInformation {
                            card_number: card_data.card_number.clone(),
                            expiry_month: card_data.card_exp_month.clone(),
                            expiry_year: card_data.get_expiry_year_4_digit(),
                            cvv: if card_data.card_cvc.clone().expose().is_empty() {
                                None
                            } else {
                                Some(card_data.card_cvc.clone())
                            },
                            cardholder_name: card_data
                                .get_cardholder_name()
                                .or(item.router_data.get_billing_full_name())?,
                            cardholder_email: item
                                .router_data
                                .get_billing_email()
                                .or(item.router_data.request.get_email())?,
                            cardholder_phone_number: item.router_data.get_billing_phone_number()?,
                        },
                    },
                    reusability: match item.router_data.request.is_mandate_payment() {
                        true => TransactionType::MultipleUse,
                        false => TransactionType::OneTimeUse,
                    },
                })),
                payment_method_id: None,
                channel_properties: None,
            }),
            PaymentMethodData::MandatePayment => Ok(Self {
                channel_properties: Some(ChannelProperties {
                    success_return_url: item.router_data.request.get_router_return_url()?,
                    failure_return_url: item.router_data.request.get_router_return_url()?,
                    skip_three_d_secure: true,
                }),
                capture_method: match item.router_data.request.is_auto_capture()? {
                    true => "AUTOMATIC".to_string(),
                    false => "MANUAL".to_string(),
                },
                currency: item.router_data.request.currency,
                amount: item.amount,
                payment_method_id: Some(Secret::new(
                    item.router_data.request.get_connector_mandate_id()?,
                )),
                payment_method: None,
            }),
            _ => Err(errors::ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("xendit"),
            )
            .into()),
        }
    }
}
impl TryFrom<XenditRouterData<&PaymentsCaptureRouterData>> for XenditPaymentsCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: XenditRouterData<&PaymentsCaptureRouterData>) -> Result<Self, Self::Error> {
        Ok(Self {
            capture_amount: item.amount,
        })
    }
}
impl<F>
    TryFrom<
        ResponseRouterData<F, XenditPaymentResponse, PaymentsAuthorizeData, PaymentsResponseData>,
    > for RouterData<F, PaymentsAuthorizeData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        item: ResponseRouterData<
            F,
            XenditPaymentResponse,
            PaymentsAuthorizeData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let status = map_payment_response_to_attempt_status(
            item.response.clone(),
            item.data.request.is_auto_capture()?,
        );
        let response = if status == enums::AttemptStatus::Failure {
            Err(ErrorResponse {
                code: item
                    .response
                    .failure_code
                    .clone()
                    .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
                message: item
                    .response
                    .failure_code
                    .clone()
                    .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
                reason: Some(
                    item.response
                        .failure_code
                        .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
                ),
                attempt_status: None,
                connector_transaction_id: Some(item.response.id.clone()),
                status_code: item.http_code,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        } else {
            let charges = match item.data.request.split_payments.as_ref() {
                Some(common_types::payments::SplitPaymentsRequest::XenditSplitPayment(
                    common_types::payments::XenditSplitRequest::MultipleSplits(_),
                )) => item
                    .data
                    .response
                    .as_ref()
                    .ok()
                    .and_then(|response| match response {
                        PaymentsResponseData::TransactionResponse { charges, .. } => {
                            charges.clone()
                        }
                        _ => None,
                    }),
                Some(common_types::payments::SplitPaymentsRequest::XenditSplitPayment(
                    common_types::payments::XenditSplitRequest::SingleSplit(ref split_data),
                )) => {
                    let charges = common_types::domain::XenditSplitSubMerchantData {
                        for_user_id: split_data.for_user_id.clone(),
                    };
                    Some(
                        common_types::payments::ConnectorChargeResponseData::XenditSplitPayment(
                            common_types::payments::XenditChargeResponseData::SingleSplit(charges),
                        ),
                    )
                }
                _ => None,
            };

            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()),
                redirection_data: match item.response.actions {
                    Some(actions) if !actions.is_empty() => {
                        actions.first().map_or(Box::new(None), |single_action| {
                            Box::new(Some(RedirectForm::Form {
                                endpoint: single_action.url.clone(),
                                method: match single_action.method {
                                    MethodType::Get => Method::Get,
                                    MethodType::Post => Method::Post,
                                },
                                form_fields: HashMap::new(),
                            }))
                        })
                    }
                    _ => Box::new(None),
                },
                mandate_reference: match item.data.request.is_mandate_payment() {
                    true => Box::new(Some(MandateReference {
                        connector_mandate_id: Some(item.response.payment_method.id.expose()),
                        payment_method_id: None,
                        mandate_metadata: None,
                        connector_mandate_request_reference_id: None,
                    })),
                    false => Box::new(None),
                },
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(
                    item.response.reference_id.peek().to_string(),
                ),
                incremental_authorization_allowed: None,
                charges,
            })
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

impl<F>
    TryFrom<ResponseRouterData<F, XenditCaptureResponse, PaymentsCaptureData, PaymentsResponseData>>
    for RouterData<F, PaymentsCaptureData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        item: ResponseRouterData<
            F,
            XenditCaptureResponse,
            PaymentsCaptureData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let status = match item.response.status {
            PaymentStatus::Failed => enums::AttemptStatus::Failure,
            PaymentStatus::Succeeded | PaymentStatus::Verified => enums::AttemptStatus::Charged,
            PaymentStatus::Pending => enums::AttemptStatus::Pending,
            PaymentStatus::RequiresAction => enums::AttemptStatus::AuthenticationPending,
            PaymentStatus::AwaitingCapture => enums::AttemptStatus::Authorized,
        };
        let response = if status == enums::AttemptStatus::Failure {
            Err(ErrorResponse {
                code: item
                    .response
                    .failure_code
                    .clone()
                    .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
                message: item
                    .response
                    .failure_code
                    .clone()
                    .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
                reason: Some(
                    item.response
                        .failure_code
                        .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
                ),
                attempt_status: None,
                connector_transaction_id: None,
                status_code: item.http_code,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        } else {
            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::NoResponseId,
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(
                    item.response.reference_id.peek().to_string(),
                ),
                incremental_authorization_allowed: None,
                charges: None,
            })
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<F, XenditSplitResponse, PaymentsPreProcessingData, PaymentsResponseData>,
    > for RouterData<F, PaymentsPreProcessingData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        item: ResponseRouterData<
            F,
            XenditSplitResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let for_user_id = match item.data.request.split_payments {
            Some(common_types::payments::SplitPaymentsRequest::XenditSplitPayment(
                common_types::payments::XenditSplitRequest::MultipleSplits(ref split_data),
            )) => split_data.for_user_id.clone(),
            _ => None,
        };

        let routes: Vec<common_types::payments::XenditSplitRoute> = item
            .response
            .routes
            .iter()
            .map(|route| {
                let required_conversion_type = common_utils::types::FloatMajorUnitForConnector;
                route
                    .flat_amount
                    .map(|amount| {
                        common_utils::types::AmountConvertor::convert_back(
                            &required_conversion_type,
                            amount,
                            item.data.request.currency.unwrap_or(Currency::USD),
                        )
                        .map_err(|_| {
                            errors::ConnectorError::RequestEncodingFailedWithReason(
                                "Failed to convert the amount into a major unit".to_owned(),
                            )
                        })
                    })
                    .transpose()
                    .map(|flat_amount| common_types::payments::XenditSplitRoute {
                        flat_amount,
                        percent_amount: route.percent_amount,
                        currency: route.currency,
                        destination_account_id: route.destination_account_id.clone(),
                        reference_id: route.reference_id.clone(),
                    })
            })
            .collect::<Result<Vec<_>, _>>()?;

        let charges = common_types::payments::XenditMultipleSplitResponse {
            split_rule_id: item.response.id,
            for_user_id,
            name: item.response.name,
            description: item.response.description,
            routes,
        };

        let response = PaymentsResponseData::TransactionResponse {
            resource_id: ResponseId::NoResponseId,
            redirection_data: Box::new(None),
            mandate_reference: Box::new(None),
            connector_metadata: None,
            network_txn_id: None,
            connector_response_reference_id: None,
            incremental_authorization_allowed: None,
            charges: Some(
                common_types::payments::ConnectorChargeResponseData::XenditSplitPayment(
                    common_types::payments::XenditChargeResponseData::MultipleSplits(charges),
                ),
            ),
        };

        Ok(Self {
            response: Ok(response),
            ..item.data
        })
    }
}

impl TryFrom<PaymentsSyncResponseRouterData<XenditResponse>> for PaymentsSyncRouterData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: PaymentsSyncResponseRouterData<XenditResponse>) -> Result<Self, Self::Error> {
        match item.response {
            XenditResponse::Payment(payment_response) => {
                let status = map_payment_response_to_attempt_status(
                    payment_response.clone(),
                    item.data.request.is_auto_capture()?,
                );
                let response = if status == enums::AttemptStatus::Failure {
                    Err(ErrorResponse {
                        code: payment_response
                            .failure_code
                            .clone()
                            .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
                        message: payment_response
                            .failure_code
                            .clone()
                            .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
                        reason: Some(
                            payment_response
                                .failure_code
                                .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
                        ),
                        attempt_status: None,
                        connector_transaction_id: Some(payment_response.id.clone()),
                        status_code: item.http_code,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::NoResponseId,
                        redirection_data: Box::new(None),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: None,
                        incremental_authorization_allowed: None,
                        charges: None,
                    })
                };
                Ok(Self {
                    status,
                    response,
                    ..item.data
                })
            }
            XenditResponse::Webhook(webhook_event) => {
                let status = match webhook_event.event {
                    XenditEventType::PaymentSucceeded | XenditEventType::CaptureSucceeded => {
                        enums::AttemptStatus::Charged
                    }
                    XenditEventType::PaymentAwaitingCapture => enums::AttemptStatus::Authorized,
                    XenditEventType::PaymentFailed | XenditEventType::CaptureFailed => {
                        enums::AttemptStatus::Failure
                    }
                };
                Ok(Self {
                    status,
                    ..item.data
                })
            }
        }
    }
}
impl<T> From<(FloatMajorUnit, T)> for XenditRouterData<T> {
    fn from((amount, item): (FloatMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}
pub struct XenditAuthType {
    pub(super) api_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for XenditAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
                api_key: api_key.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

impl TryFrom<&PaymentsPreProcessingRouterData> for XenditSplitRequestData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentsPreProcessingRouterData) -> Result<Self, Self::Error> {
        if let Some(common_types::payments::SplitPaymentsRequest::XenditSplitPayment(
            common_types::payments::XenditSplitRequest::MultipleSplits(ref split_data),
        )) = item.request.split_payments.clone()
        {
            let routes: Vec<XenditSplitRoute> = split_data
                .routes
                .iter()
                .map(|route| {
                    let required_conversion_type = common_utils::types::FloatMajorUnitForConnector;
                    route
                        .flat_amount
                        .map(|amount| {
                            common_utils::types::AmountConvertor::convert(
                                &required_conversion_type,
                                amount,
                                item.request.currency.unwrap_or(Currency::USD),
                            )
                            .map_err(|_| {
                                errors::ConnectorError::RequestEncodingFailedWithReason(
                                    "Failed to convert the amount into a major unit".to_owned(),
                                )
                            })
                        })
                        .transpose()
                        .map(|flat_amount| XenditSplitRoute {
                            flat_amount,
                            percent_amount: route.percent_amount,
                            currency: route.currency,
                            destination_account_id: route.destination_account_id.clone(),
                            reference_id: route.reference_id.clone(),
                        })
                })
                .collect::<Result<Vec<_>, _>>()?;

            let split_data = XenditSplitRequest {
                name: split_data.name.clone(),
                description: split_data.description.clone(),
                routes,
            };

            Ok(Self { split_data })
        } else {
            Err(errors::ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("Xendit"),
            )
            .into())
        }
    }
}

//TODO: Fill the struct with respective fields
// REFUND :
// Type definition for RefundRequest
#[derive(Default, Debug, Serialize)]
pub struct XenditRefundRequest {
    pub amount: FloatMajorUnit,
    pub payment_request_id: String,
    pub reason: String,
}

impl<F> TryFrom<&XenditRouterData<&RefundsRouterData<F>>> for XenditRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &XenditRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        Ok(Self {
            amount: item.amount.to_owned(),
            payment_request_id: item.router_data.request.connector_transaction_id.clone(),
            reason: "REQUESTED_BY_CUSTOMER".to_string(),
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum RefundStatus {
    RequiresAction,
    Succeeded,
    Failed,
    Pending,
    Cancelled,
}

impl From<RefundStatus> for enums::RefundStatus {
    fn from(item: RefundStatus) -> Self {
        match item {
            RefundStatus::Succeeded => Self::Success,
            RefundStatus::Failed | RefundStatus::Cancelled => Self::Failure,
            RefundStatus::Pending | RefundStatus::RequiresAction => Self::Pending,
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefundResponse {
    pub id: String,
    pub status: RefundStatus,
    pub amount: FloatMajorUnit,
    pub currency: String,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id,
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id.to_string(),
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

#[derive(Default, Debug, Serialize, Deserialize)]
pub struct XenditMetadata {
    pub for_user_id: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct XenditWebhookEvent {
    pub event: XenditEventType,
    pub data: EventDetails,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct EventDetails {
    pub id: String,
    pub payment_request_id: Option<String>,
    pub amount: FloatMajorUnit,
    pub currency: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum XenditEventType {
    #[serde(rename = "payment.succeeded")]
    PaymentSucceeded,
    #[serde(rename = "payment.awaiting_capture")]
    PaymentAwaitingCapture,
    #[serde(rename = "payment.failed")]
    PaymentFailed,
    #[serde(rename = "capture.succeeded")]
    CaptureSucceeded,
    #[serde(rename = "capture.failed")]
    CaptureFailed,
}


// Struct: NordeaPaymentsInitiateResponse
// File: crates/hyperswitch_connectors/src/connectors/nordea/responses.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NordeaPaymentsInitiateResponse


// Implementation: impl Test
// File: crates/router_derive/src/lib.rs
// Module: router_derive
// Methods: 1 total (0 public)

impl Test


// Function: check_client_secret_and_get_auth
// File: crates/router/src/services/authentication.rs
// Module: router

pub fn check_client_secret_and_get_auth<T>(
    headers: &HeaderMap,
    payload: &impl ClientSecretFetch,
    api_auth: ApiKeyAuth,
) -> RouterResult<(
    Box<dyn AuthenticateAndFetch<AuthenticationData, T>>,
    api::AuthFlow,
)>
where
    T: SessionStateInfo + Sync + Send,
    ApiKeyAuth: AuthenticateAndFetch<AuthenticationData, T>,
    PublishableKeyAuth: AuthenticateAndFetch<AuthenticationData, T>,


// Function: new
// File: crates/hyperswitch_connectors/src/connectors/aci.rs
// Module: hyperswitch_connectors

pub fn new() -> &'static Self


// Implementation: impl SingleUsePaymentMethodToken
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Methods: 1 total (1 public)

impl SingleUsePaymentMethodToken


// Implementation: impl PaymentAttemptBatchNew
// File: crates/diesel_models/src/user/sample_data.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl PaymentAttemptBatchNew


// Struct: SilverflowCaptureRequest
// File: crates/hyperswitch_connectors/src/connectors/silverflow/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SilverflowCaptureRequest


// Implementation: impl PaymentIntentFetchConstraints
// File: crates/hyperswitch_domain_models/src/payments/payment_intent.rs
// Module: hyperswitch_domain_models
// Methods: 1 total (1 public)

impl PaymentIntentFetchConstraints


// File: crates/analytics/src/api_event/metrics/latency.rs
// Module: analytics
// Public structs: 1

use std::collections::HashSet;

use api_models::analytics::{
    api_event::{ApiEventDimensions, ApiEventFilters, ApiEventMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

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

#[derive(Default)]
pub(super) struct MaxLatency;

#[async_trait::async_trait]
impl<T> super::ApiEventMetric<T> for MaxLatency
where
    T: AnalyticsDataSource + super::ApiEventMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        _dimensions: &[ApiEventDimensions],
        merchant_id: &common_utils::id_type::MerchantId,
        filters: &ApiEventFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(ApiEventMetricsBucketIdentifier, ApiEventMetricRow)>> {
        let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::ApiEvents);

        query_builder
            .add_select_column(Aggregate::Sum {
                field: "latency",
                alias: Some("latency_sum"),
            })
            .switch()?;

        query_builder
            .add_select_column(Aggregate::Count {
                field: Some("latency"),
                alias: Some("latency_count"),
            })
            .switch()?;

        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;
        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

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

        query_builder
            .add_filter_clause("merchant_id", merchant_id)
            .switch()?;

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

        query_builder
            .add_custom_filter_clause("request", "10.63.134.6", FilterTypes::NotLike)
            .attach_printable("Error filtering out locker IP")
            .switch()?;

        query_builder
            .execute_query::<LatencyAvg, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    ApiEventMetricsBucketIdentifier::new(TimeRange {
                        start_time: match (granularity, i.start_bucket) {
                            (Some(g), Some(st)) => g.clip_to_start(st)?,
                            _ => time_range.start_time,
                        },
                        end_time: granularity.as_ref().map_or_else(
                            || Ok(time_range.end_time),
                            |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                        )?,
                    }),
                    ApiEventMetricRow {
                        latency: if i.latency_count != 0 {
                            Some(i.latency_sum.unwrap_or(0) / i.latency_count)
                        } else {
                            None
                        },
                        api_count: None,
                        status_code_count: None,
                        start_bucket: i.start_bucket,
                        end_bucket: i.end_bucket,
                    },
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(ApiEventMetricsBucketIdentifier, ApiEventMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}

#[derive(Debug, PartialEq, Eq, serde::Deserialize)]
pub struct LatencyAvg {
    latency_sum: Option<u64>,
    latency_count: u64,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub start_bucket: Option<PrimitiveDateTime>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub end_bucket: Option<PrimitiveDateTime>,
}


// Struct: CardInfo
// File: crates/diesel_models/src/cards_info.rs
// Module: diesel_models
// Implementations: 0

pub struct CardInfo


// Struct: SecuredSecret
// File: crates/hyperswitch_interfaces/src/secrets_interface/secret_state.rs
// Module: hyperswitch_interfaces
// Implementations: 1
// Traits: SecretState

pub struct SecuredSecret


// Implementation: impl ConnectorStatusAndDisabledValidation
// File: crates/router/src/core/admin.rs
// Module: router
// Methods: 1 total (0 public)

impl ConnectorStatusAndDisabledValidation


// Function: get_month
// File: crates/cards/src/lib.rs
// Module: cards

pub fn get_month(&self) -> &CardExpirationMonth


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

pub struct BankPaymentInformation


// Struct: DeleteCardToken
// File: crates/router/src/types/payment_methods.rs
// Module: router
// Implementations: 0

pub struct DeleteCardToken


// Struct: RelayResponse
// File: crates/api_models/src/relay.rs
// Module: api_models
// Implementations: 1
// Traits: common_utils::events::ApiEventMetric

pub struct RelayResponse


// Implementation: impl CardExpirationYear
// File: crates/cards/src/lib.rs
// Module: cards
// Methods: 2 total (2 public)

impl CardExpirationYear


// Struct: FlexitiAccessTokenRequest
// File: crates/hyperswitch_connectors/src/connectors/flexiti/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FlexitiAccessTokenRequest


// File: crates/router/tests/connectors/finix.rs
// Module: router

use hyperswitch_domain_models::payment_method_data::{Card, PaymentMethodData};
use masking::Secret;
use router::types::{self, api, storage::enums};
use test_utils::connector_auth;

use crate::utils::{self, ConnectorActions};

#[derive(Clone, Copy)]
struct FinixTest;
impl ConnectorActions for FinixTest {}
impl utils::Connector for FinixTest {
    fn get_data(&self) -> api::ConnectorData {
        use router::connector::Finix;
        utils::construct_connector_data_old(
            Box::new(Finix::new()),
            types::Connector::Plaid,
            api::GetToken::Connector,
            None,
        )
    }

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .finix
                .expect("Missing connector authentication configuration")
                .into(),
        )
    }

    fn get_name(&self) -> String {
        "finix".to_string()
    }
}

static CONNECTOR: FinixTest = FinixTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    None
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}

// Captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Partially captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(
            payment_method_details(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized,);
}

// Voids a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_void_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_void_payment(
            payment_method_details(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: String::from(""),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}

// Refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Synchronizes a refund using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_manually_captured_refund() {
    let refund_response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_make_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_auto_captured_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                capture_method: Some(enums::CaptureMethod::Automatic),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged,);
}

// Refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_auto_captured_payment() {
    let response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        refund_response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() {
    CONNECTOR
        .make_payment_and_multiple_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
}

// Synchronizes a refund using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_refund() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Cards Negative scenarios
// Creates a payment with incorrect CVC.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_cvc() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_cvc: Secret::new("12345".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's security code is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry month.
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_exp_month: Secret::new("20".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration month is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry year.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_expiry_year() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: PaymentMethodData::Card(Card {
                    card_exp_year: Secret::new("2000".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration year is invalid.".to_string(),
    );
}

// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let void_response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        void_response.response.unwrap_err().message,
        "You cannot cancel this PaymentIntent because it has a status of succeeded."
    );
}

// Captures a payment using invalid connector payment id.
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let capture_response = CONNECTOR
        .capture_payment("123456789".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        capture_response.response.unwrap_err().message,
        String::from("No such payment_intent: '123456789'")
    );
}

// Refunds a payment with refund amount higher than payment amount.
#[actix_web::test]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
    let response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 150,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Refund amount (₹1.50) is greater than charge amount (₹1.00)",
    );
}

// Connector dependent test cases goes here

// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests


// File: crates/router/src/core/errors/utils.rs
// Module: router

use common_utils::errors::CustomResult;

use crate::{core::errors, logger};

pub trait StorageErrorExt<T, E> {
    #[track_caller]
    fn to_not_found_response(self, not_found_response: E) -> error_stack::Result<T, E>;

    #[track_caller]
    fn to_duplicate_response(self, duplicate_response: E) -> error_stack::Result<T, E>;
}

impl<T> StorageErrorExt<T, errors::CustomersErrorResponse>
    for error_stack::Result<T, errors::StorageError>
{
    #[track_caller]
    fn to_not_found_response(
        self,
        not_found_response: errors::CustomersErrorResponse,
    ) -> error_stack::Result<T, errors::CustomersErrorResponse> {
        self.map_err(|err| match err.current_context() {
            error if error.is_db_not_found() => err.change_context(not_found_response),
            errors::StorageError::CustomerRedacted => {
                err.change_context(errors::CustomersErrorResponse::CustomerRedacted)
            }
            _ => err.change_context(errors::CustomersErrorResponse::InternalServerError),
        })
    }

    fn to_duplicate_response(
        self,
        duplicate_response: errors::CustomersErrorResponse,
    ) -> error_stack::Result<T, errors::CustomersErrorResponse> {
        self.map_err(|err| {
            if err.current_context().is_db_unique_violation() {
                err.change_context(duplicate_response)
            } else {
                err.change_context(errors::CustomersErrorResponse::InternalServerError)
            }
        })
    }
}

impl<T> StorageErrorExt<T, errors::ApiErrorResponse>
    for error_stack::Result<T, errors::StorageError>
{
    #[track_caller]
    fn to_not_found_response(
        self,
        not_found_response: errors::ApiErrorResponse,
    ) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let new_err = match err.current_context() {
                errors::StorageError::ValueNotFound(_) => not_found_response,
                errors::StorageError::CustomerRedacted => {
                    errors::ApiErrorResponse::CustomerRedacted
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(new_err)
        })
    }

    #[track_caller]
    fn to_duplicate_response(
        self,
        duplicate_response: errors::ApiErrorResponse,
    ) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let new_err = match err.current_context() {
                errors::StorageError::DuplicateValue { .. } => duplicate_response,
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(new_err)
        })
    }
}

pub trait ConnectorErrorExt<T> {
    #[track_caller]
    fn to_refund_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_payment_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_setup_mandate_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_dispute_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_files_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[cfg(feature = "payouts")]
    #[track_caller]
    fn to_payout_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;
    #[track_caller]
    fn to_vault_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse>;

    // Validates if the result, is Ok(..) or WebhookEventTypeNotFound all the other error variants
    // are cascaded while these two event types are handled via `Option`
    #[track_caller]
    fn allow_webhook_event_type_not_found(
        self,
        enabled: bool,
    ) -> error_stack::Result<Option<T>, errors::ConnectorError>;
}

impl<T> ConnectorErrorExt<T> for error_stack::Result<T, errors::ConnectorError> {
    fn to_refund_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| match err.current_context() {
            errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                let response_str = std::str::from_utf8(bytes);
                let data = match response_str {
                    Ok(s) => serde_json::from_str(s)
                        .map_err(
                            |error| logger::error!(%error,"Failed to convert response to JSON"),
                        )
                        .ok(),
                    Err(error) => {
                        logger::error!(%error,"Failed to convert response to UTF8 string");
                        None
                    }
                };
                err.change_context(errors::ApiErrorResponse::RefundFailed { data })
            }
            errors::ConnectorError::NotImplemented(reason) => {
                errors::ApiErrorResponse::NotImplemented {
                    message: errors::NotImplementedMessage::Reason(reason.to_string()),
                }
                .into()
            }
            errors::ConnectorError::NotSupported { message, connector } => {
                errors::ApiErrorResponse::NotSupported {
                    message: format!("{message} is not supported by {connector}"),
                }
                .into()
            }
            errors::ConnectorError::CaptureMethodNotSupported => {
                errors::ApiErrorResponse::NotSupported {
                    message: "Capture Method Not Supported".to_owned(),
                }
                .into()
            }
            errors::ConnectorError::FailedToObtainIntegrationUrl
            | errors::ConnectorError::RequestEncodingFailed
            | errors::ConnectorError::RequestEncodingFailedWithReason(_)
            | errors::ConnectorError::ParsingFailed
            | errors::ConnectorError::ResponseDeserializationFailed
            | errors::ConnectorError::UnexpectedResponseError(_)
            | errors::ConnectorError::RoutingRulesParsingError
            | errors::ConnectorError::FailedToObtainPreferredConnector
            | errors::ConnectorError::ProcessingStepFailed(_)
            | errors::ConnectorError::InvalidConnectorName
            | errors::ConnectorError::InvalidWallet
            | errors::ConnectorError::ResponseHandlingFailed
            | errors::ConnectorError::MissingRequiredField { .. }
            | errors::ConnectorError::MissingRequiredFields { .. }
            | errors::ConnectorError::FailedToObtainAuthType
            | errors::ConnectorError::FailedToObtainCertificate
            | errors::ConnectorError::NoConnectorMetaData
            | errors::ConnectorError::NoConnectorWalletDetails
            | errors::ConnectorError::FailedToObtainCertificateKey
            | errors::ConnectorError::MaxFieldLengthViolated { .. }
            | errors::ConnectorError::FlowNotSupported { .. }
            | errors::ConnectorError::MissingConnectorMandateID
            | errors::ConnectorError::MissingConnectorMandateMetadata
            | errors::ConnectorError::MissingConnectorTransactionID
            | errors::ConnectorError::MissingConnectorRefundID
            | errors::ConnectorError::MissingApplePayTokenData
            | errors::ConnectorError::WebhooksNotImplemented
            | errors::ConnectorError::WebhookBodyDecodingFailed
            | errors::ConnectorError::WebhookSignatureNotFound
            | errors::ConnectorError::WebhookSourceVerificationFailed
            | errors::ConnectorError::WebhookVerificationSecretNotFound
            | errors::ConnectorError::WebhookVerificationSecretInvalid
            | errors::ConnectorError::WebhookReferenceIdNotFound
            | errors::ConnectorError::WebhookEventTypeNotFound
            | errors::ConnectorError::WebhookResourceObjectNotFound
            | errors::ConnectorError::WebhookResponseEncodingFailed
            | errors::ConnectorError::InvalidDateFormat
            | errors::ConnectorError::DateFormattingFailed
            | errors::ConnectorError::InvalidDataFormat { .. }
            | errors::ConnectorError::MismatchedPaymentData
            | errors::ConnectorError::MandatePaymentDataMismatch { .. }
            | errors::ConnectorError::InvalidWalletToken { .. }
            | errors::ConnectorError::MissingConnectorRelatedTransactionID { .. }
            | errors::ConnectorError::FileValidationFailed { .. }
            | errors::ConnectorError::MissingConnectorRedirectionPayload { .. }
            | errors::ConnectorError::FailedAtConnector { .. }
            | errors::ConnectorError::MissingPaymentMethodType
            | errors::ConnectorError::InSufficientBalanceInPaymentMethod
            | errors::ConnectorError::RequestTimeoutReceived
            | errors::ConnectorError::CurrencyNotSupported { .. }
            | errors::ConnectorError::InvalidConnectorConfig { .. }
            | errors::ConnectorError::AmountConversionFailed
            | errors::ConnectorError::GenericError { .. } => {
                err.change_context(errors::ApiErrorResponse::RefundFailed { data: None })
            }
        })
    }

    fn to_payment_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                    let response_str = std::str::from_utf8(bytes);
                    let data = match response_str {
                        Ok(s) => serde_json::from_str(s)
                            .map_err(
                                |error| logger::error!(%error,"Failed to convert response to JSON"),
                            )
                            .ok(),
                        Err(error) => {
                            logger::error!(%error,"Failed to convert response to UTF8 string");
                            None
                        }
                    };
                    errors::ApiErrorResponse::PaymentAuthorizationFailed { data }
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields { field_names: field_names.to_vec() }
                }
                errors::ConnectorError::NotImplemented(reason) => {
                    errors::ApiErrorResponse::NotImplemented {
                        message: errors::NotImplementedMessage::Reason(
                            reason.to_string(),
                        ),
                    }
                }
                errors::ConnectorError::MismatchedPaymentData => {
                    errors::ApiErrorResponse::InvalidDataValue {
                        field_name:
                            "payment_method_data, payment_method_type and payment_experience does not match",
                    }
                },
                errors::ConnectorError::MandatePaymentDataMismatch {fields}=> {
                    errors::ApiErrorResponse::MandatePaymentDataMismatch {
                        fields: fields.to_owned(),
                    }
                },
                errors::ConnectorError::NotSupported { message, connector } => {
                    errors::ApiErrorResponse::NotSupported { message: format!("{message} is not supported by {connector}") }
                },
                errors::ConnectorError::FlowNotSupported{ flow, connector } => {
                    errors::ApiErrorResponse::FlowNotSupported { flow: flow.to_owned(), connector: connector.to_owned() }
                },
                errors::ConnectorError::MaxFieldLengthViolated{ connector, field_name,  max_length, received_length} => {
                    errors::ApiErrorResponse::MaxFieldLengthViolated { connector: connector.to_string(), field_name: field_name.to_string(), max_length: *max_length, received_length: *received_length }
                },
                errors::ConnectorError::InvalidDataFormat { field_name } => {
                    errors::ApiErrorResponse::InvalidDataValue { field_name }
                },
                errors::ConnectorError::CaptureMethodNotSupported => {
                    errors::ApiErrorResponse::NotSupported {
                        message: "Capture Method Not Supported".to_owned(),
                    }
                }
                errors::ConnectorError::InvalidWalletToken {wallet_name} => errors::ApiErrorResponse::InvalidWalletToken {wallet_name: wallet_name.to_string()},
                errors::ConnectorError::CurrencyNotSupported { message, connector} => errors::ApiErrorResponse::CurrencyNotSupported { message: format!("Credentials for the currency {message} are not configured with the connector {connector}/hyperswitch") },
                errors::ConnectorError::FailedToObtainAuthType =>  errors::ApiErrorResponse::InvalidConnectorConfiguration {config: "connector_account_details".to_string()},
                errors::ConnectorError::InvalidConnectorConfig { config }  => errors::ApiErrorResponse::InvalidConnectorConfiguration { config: config.to_string() },
                errors::ConnectorError::FailedToObtainIntegrationUrl |
                errors::ConnectorError::RequestEncodingFailed |
                errors::ConnectorError::RequestEncodingFailedWithReason(_) |
                errors::ConnectorError::ParsingFailed |
                errors::ConnectorError::ResponseDeserializationFailed |
                errors::ConnectorError::UnexpectedResponseError(_) |
                errors::ConnectorError::RoutingRulesParsingError |
                errors::ConnectorError::FailedToObtainPreferredConnector |
                errors::ConnectorError::InvalidConnectorName |
                errors::ConnectorError::InvalidWallet |
                errors::ConnectorError::ResponseHandlingFailed |
                errors::ConnectorError::FailedToObtainCertificate |
                errors::ConnectorError::NoConnectorMetaData | errors::ConnectorError::NoConnectorWalletDetails |
                errors::ConnectorError::FailedToObtainCertificateKey |
                errors::ConnectorError::MissingConnectorMandateID |
                errors::ConnectorError::MissingConnectorMandateMetadata |
                errors::ConnectorError::MissingConnectorTransactionID |
                errors::ConnectorError::MissingConnectorRefundID |
                errors::ConnectorError::MissingApplePayTokenData |
                errors::ConnectorError::WebhooksNotImplemented |
                errors::ConnectorError::WebhookBodyDecodingFailed |
                errors::ConnectorError::WebhookSignatureNotFound |
                errors::ConnectorError::WebhookSourceVerificationFailed |
                errors::ConnectorError::WebhookVerificationSecretNotFound |
                errors::ConnectorError::WebhookVerificationSecretInvalid |
                errors::ConnectorError::WebhookReferenceIdNotFound |
                errors::ConnectorError::WebhookEventTypeNotFound |
                errors::ConnectorError::WebhookResourceObjectNotFound |
                errors::ConnectorError::WebhookResponseEncodingFailed |
                errors::ConnectorError::InvalidDateFormat |
                errors::ConnectorError::DateFormattingFailed |
                errors::ConnectorError::MissingConnectorRelatedTransactionID { .. } |
                errors::ConnectorError::FileValidationFailed { .. } |
                errors::ConnectorError::MissingConnectorRedirectionPayload { .. } |
                errors::ConnectorError::FailedAtConnector { .. } |
                errors::ConnectorError::MissingPaymentMethodType |
                errors::ConnectorError::InSufficientBalanceInPaymentMethod |
                errors::ConnectorError::RequestTimeoutReceived |
                errors::ConnectorError::ProcessingStepFailed(None)|
                errors::ConnectorError::GenericError {..} |
                errors::ConnectorError::AmountConversionFailed => errors::ApiErrorResponse::InternalServerError
            };
            err.change_context(error)
        })
    }

    fn to_setup_mandate_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = err.current_context();
            let data = match error {
                errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                    let response_str = std::str::from_utf8(bytes);
                    let error_response = match response_str {
                        Ok(s) => serde_json::from_str(s)
                            .map_err(
                                |err| logger::error!(%err, "Failed to convert response to JSON"),
                            )
                            .ok(),
                        Err(err) => {
                            logger::error!(%err, "Failed to convert response to UTF8 string");
                            None
                        }
                    };
                    errors::ApiErrorResponse::PaymentAuthorizationFailed {
                        data: error_response,
                    }
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::FailedToObtainIntegrationUrl => {
                    errors::ApiErrorResponse::InvalidConnectorConfiguration {
                        config: "connector_account_details".to_string(),
                    }
                }
                errors::ConnectorError::InvalidConnectorConfig { config: field_name } => {
                    errors::ApiErrorResponse::InvalidConnectorConfiguration {
                        config: field_name.to_string(),
                    }
                }
                errors::ConnectorError::InvalidWalletToken { wallet_name } => {
                    errors::ApiErrorResponse::InvalidWalletToken {
                        wallet_name: wallet_name.to_string(),
                    }
                }
                errors::ConnectorError::CaptureMethodNotSupported => {
                    errors::ApiErrorResponse::NotSupported {
                        message: "Capture Method Not Supported".to_owned(),
                    }
                }
                errors::ConnectorError::RequestEncodingFailed
                | errors::ConnectorError::RequestEncodingFailedWithReason(_)
                | errors::ConnectorError::ParsingFailed
                | errors::ConnectorError::ResponseDeserializationFailed
                | errors::ConnectorError::UnexpectedResponseError(_)
                | errors::ConnectorError::RoutingRulesParsingError
                | errors::ConnectorError::FailedToObtainPreferredConnector
                | errors::ConnectorError::InvalidConnectorName
                | errors::ConnectorError::InvalidWallet
                | errors::ConnectorError::ResponseHandlingFailed
                | errors::ConnectorError::MissingRequiredFields { .. }
                | errors::ConnectorError::FailedToObtainAuthType
                | errors::ConnectorError::FailedToObtainCertificate
                | errors::ConnectorError::NoConnectorMetaData
                | errors::ConnectorError::NoConnectorWalletDetails
                | errors::ConnectorError::FailedToObtainCertificateKey
                | errors::ConnectorError::NotImplemented(_)
                | errors::ConnectorError::NotSupported { .. }
                | errors::ConnectorError::MaxFieldLengthViolated { .. }
                | errors::ConnectorError::FlowNotSupported { .. }
                | errors::ConnectorError::MissingConnectorMandateID
                | errors::ConnectorError::MissingConnectorMandateMetadata
                | errors::ConnectorError::MissingConnectorTransactionID
                | errors::ConnectorError::MissingConnectorRefundID
                | errors::ConnectorError::MissingApplePayTokenData
                | errors::ConnectorError::WebhooksNotImplemented
                | errors::ConnectorError::WebhookBodyDecodingFailed
                | errors::ConnectorError::WebhookSignatureNotFound
                | errors::ConnectorError::WebhookSourceVerificationFailed
                | errors::ConnectorError::WebhookVerificationSecretNotFound
                | errors::ConnectorError::WebhookVerificationSecretInvalid
                | errors::ConnectorError::WebhookReferenceIdNotFound
                | errors::ConnectorError::WebhookEventTypeNotFound
                | errors::ConnectorError::WebhookResourceObjectNotFound
                | errors::ConnectorError::WebhookResponseEncodingFailed
                | errors::ConnectorError::InvalidDateFormat
                | errors::ConnectorError::DateFormattingFailed
                | errors::ConnectorError::InvalidDataFormat { .. }
                | errors::ConnectorError::MismatchedPaymentData
                | errors::ConnectorError::MandatePaymentDataMismatch { .. }
                | errors::ConnectorError::MissingConnectorRelatedTransactionID { .. }
                | errors::ConnectorError::FileValidationFailed { .. }
                | errors::ConnectorError::MissingConnectorRedirectionPayload { .. }
                | errors::ConnectorError::FailedAtConnector { .. }
                | errors::ConnectorError::MissingPaymentMethodType
                | errors::ConnectorError::InSufficientBalanceInPaymentMethod
                | errors::ConnectorError::RequestTimeoutReceived
                | errors::ConnectorError::CurrencyNotSupported { .. }
                | errors::ConnectorError::ProcessingStepFailed(None)
                | errors::ConnectorError::AmountConversionFailed
                | errors::ConnectorError::GenericError { .. } => {
                    logger::error!(%error,"Setup Mandate flow failed");
                    errors::ApiErrorResponse::PaymentAuthorizationFailed { data: None }
                }
            };
            err.change_context(data)
        })
    }

    fn to_dispute_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                    let response_str = std::str::from_utf8(bytes);
                    let data = match response_str {
                        Ok(s) => serde_json::from_str(s)
                            .map_err(
                                |error| logger::error!(%error,"Failed to convert response to JSON"),
                            )
                            .ok(),
                        Err(error) => {
                            logger::error!(%error,"Failed to convert response to UTF8 string");
                            None
                        }
                    };
                    errors::ApiErrorResponse::DisputeFailed { data }
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields {
                        field_names: field_names.to_vec(),
                    }
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(error)
        })
    }

    fn to_files_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                    let response_str = std::str::from_utf8(bytes);
                    let data = match response_str {
                        Ok(s) => serde_json::from_str(s)
                            .map_err(
                                |error| logger::error!(%error,"Failed to convert response to JSON"),
                            )
                            .ok(),
                        Err(error) => {
                            logger::error!(%error,"Failed to convert response to UTF8 string");
                            None
                        }
                    };
                    errors::ApiErrorResponse::DisputeFailed { data }
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields {
                        field_names: field_names.to_vec(),
                    }
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(error)
        })
    }

    #[cfg(feature = "payouts")]
    fn to_payout_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(Some(bytes)) => {
                    let response_str = std::str::from_utf8(bytes);
                    let data = match response_str {
                        Ok(s) => serde_json::from_str(s)
                            .map_err(
                                |error| logger::error!(%error,"Failed to convert response to JSON"),
                            )
                            .ok(),
                        Err(error) => {
                            logger::error!(%error,"Failed to convert response to UTF8 string");
                            None
                        }
                    };
                    errors::ApiErrorResponse::PayoutFailed { data }
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields {
                        field_names: field_names.to_vec(),
                    }
                }
                errors::ConnectorError::NotSupported { message, connector } => {
                    errors::ApiErrorResponse::NotSupported {
                        message: format!("{message} by {connector}"),
                    }
                }
                errors::ConnectorError::NotImplemented(reason) => {
                    errors::ApiErrorResponse::NotImplemented {
                        message: errors::NotImplementedMessage::Reason(reason.to_string()),
                    }
                }
                errors::ConnectorError::InvalidConnectorConfig { config } => {
                    errors::ApiErrorResponse::InvalidConnectorConfiguration {
                        config: config.to_string(),
                    }
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(error)
        })
    }

    fn to_vault_failed_response(self) -> error_stack::Result<T, errors::ApiErrorResponse> {
        self.map_err(|err| {
            let error = match err.current_context() {
                errors::ConnectorError::ProcessingStepFailed(_) => {
                    errors::ApiErrorResponse::ExternalVaultFailed
                }
                errors::ConnectorError::MissingRequiredField { field_name } => {
                    errors::ApiErrorResponse::MissingRequiredField { field_name }
                }
                errors::ConnectorError::MissingRequiredFields { field_names } => {
                    errors::ApiErrorResponse::MissingRequiredFields {
                        field_names: field_names.to_vec(),
                    }
                }
                errors::ConnectorError::NotSupported { message, connector } => {
                    errors::ApiErrorResponse::NotSupported {
                        message: format!("{message} by {connector}"),
                    }
                }
                errors::ConnectorError::NotImplemented(reason) => {
                    errors::ApiErrorResponse::NotImplemented {
                        message: errors::NotImplementedMessage::Reason(reason.to_string()),
                    }
                }
                errors::ConnectorError::InvalidConnectorConfig { config } => {
                    errors::ApiErrorResponse::InvalidConnectorConfiguration {
                        config: config.to_string(),
                    }
                }
                _ => errors::ApiErrorResponse::InternalServerError,
            };
            err.change_context(error)
        })
    }

    fn allow_webhook_event_type_not_found(
        self,
        enabled: bool,
    ) -> CustomResult<Option<T>, errors::ConnectorError> {
        match self {
            Ok(event_type) => Ok(Some(event_type)),
            Err(error) => match error.current_context() {
                errors::ConnectorError::WebhookEventTypeNotFound if enabled => Ok(None),
                _ => Err(error),
            },
        }
    }
}

pub trait RedisErrorExt {
    #[track_caller]
    fn to_redis_failed_response(self, key: &str) -> error_stack::Report<errors::StorageError>;
}

impl RedisErrorExt for error_stack::Report<errors::RedisError> {
    fn to_redis_failed_response(self, key: &str) -> error_stack::Report<errors::StorageError> {
        match self.current_context() {
            errors::RedisError::NotFound => self.change_context(
                errors::StorageError::ValueNotFound(format!("Data does not exist for key {key}")),
            ),
            errors::RedisError::SetNxFailed => {
                self.change_context(errors::StorageError::DuplicateValue {
                    entity: "redis",
                    key: Some(key.to_string()),
                })
            }
            _ => self.change_context(errors::StorageError::KVError),
        }
    }
}

#[cfg(feature = "olap")]
impl<T> StorageErrorExt<T, errors::UserErrors> for error_stack::Result<T, errors::StorageError> {
    #[track_caller]
    fn to_not_found_response(
        self,
        not_found_response: errors::UserErrors,
    ) -> error_stack::Result<T, errors::UserErrors> {
        self.map_err(|e| {
            if e.current_context().is_db_not_found() {
                e.change_context(not_found_response)
            } else {
                e.change_context(errors::UserErrors::InternalServerError)
            }
        })
    }

    #[track_caller]
    fn to_duplicate_response(
        self,
        duplicate_response: errors::UserErrors,
    ) -> error_stack::Result<T, errors::UserErrors> {
        self.map_err(|e| {
            if e.current_context().is_db_unique_violation() {
                e.change_context(duplicate_response)
            } else {
                e.change_context(errors::UserErrors::InternalServerError)
            }
        })
    }
}


// Implementation: impl std::fmt::Display for for Op
// File: crates/storage_impl/src/redis/kv_store.rs
// Module: storage_impl
// Methods: 1 total (0 public)

impl std::fmt::Display for for Op


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

impl api::PaymentSync for for Hyperwallet


// Struct: PartnerReferralOperations
// File: crates/router/src/types/api/connector_onboarding/paypal.rs
// Module: router
// Implementations: 0

pub struct PartnerReferralOperations


// Function: get_id_type_from_header_if_present
// File: crates/router/src/services/authentication.rs
// Module: router

pub fn get_id_type_from_header_if_present<T>(&self, key: &str) -> RouterResult<Option<T>>
    where
        T: TryFrom<
            std::borrow::Cow<'static, str>,
            Error = error_stack::Report<errors::ValidationError>,
        >,


// Struct: Shift4AuthType
// File: crates/hyperswitch_connectors/src/connectors/shift4/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Shift4AuthType


// Implementation: impl api::PaymentAuthorize for for Datatrans
// File: crates/hyperswitch_connectors/src/connectors/datatrans.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Datatrans


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

impl api::MandateSetup for for Airwallex


// Struct: NmiCaptureRequest
// File: crates/hyperswitch_connectors/src/connectors/nmi/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NmiCaptureRequest


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

impl webhooks::IncomingWebhook for for Trustpayments


// Struct: Paypal
// File: crates/hyperswitch_connectors/src/connectors/paypal.rs
// Module: hyperswitch_connectors
// Implementations: 30
// Traits: api::Payment, api::PaymentSession, api::PaymentToken, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentsCompleteAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::ConnectorVerifyWebhookSource, api::PaymentPostSessionTokens, api::PaymentSessionUpdate, api::Payouts, api::PayoutCreate, api::PayoutFulfill, api::PayoutSync, ConnectorCommon, ConnectorValidation, PaymentIncrementalAuthorization, api::PaymentsPreProcessing, IncomingWebhook, ConnectorRedirectResponse, ConnectorErrorTypeMapping, ConnectorSpecifications

pub struct Paypal

api::Payment api::PaymentSession api::PaymentToken api::ConnectorAccessToken api::MandateSetup api::PaymentAuthorize api::PaymentsCompleteAuthorize api::PaymentSync api::PaymentCapture api::PaymentVoid api::Refund api::RefundExecute api::RefundSync api::ConnectorVerifyWebhookSource api::PaymentPostSessionTokens api::PaymentSessionUpdate api::Payouts api::PayoutCreate api::PayoutFulfill api::PayoutSync ConnectorCommon ConnectorValidation PaymentIncrementalAuthorization api::PaymentsPreProcessing IncomingWebhook ConnectorRedirectResponse ConnectorErrorTypeMapping ConnectorSpecifications


// Struct: UrlDetails
// File: crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct UrlDetails


// Function: retrieve_dispute_evidence
// File: crates/router/src/core/disputes.rs
// Module: router

pub fn retrieve_dispute_evidence(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: Option<common_utils::id_type::ProfileId>,
    req: disputes::DisputeId,
) -> RouterResponse<Vec<api_models::disputes::DisputeEvidenceBlock>>


// Function: get_card_details
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models

pub fn get_card_details(&self) -> Option<CardDetailsPaymentMethod>


// Function: revenue_recovery_data_backfill_status
// File: crates/router/src/routes/revenue_recovery_data_backfill.rs
// Module: router

pub fn revenue_recovery_data_backfill_status(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<String>,
) -> HttpResponse


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

impl Refund for for Wise


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

// Documentation: get_skip_saving_wallet_at_connector_key

pub fn get_skip_saving_wallet_at_connector_key(&self) -> String


// Function: new
// File: crates/router/src/core/webhooks/recovery_incoming.rs
// Module: router

pub fn new(
        payment_intent: &revenue_recovery::RecoveryPaymentIntent,
        payment_attempt: &revenue_recovery::RecoveryPaymentAttempt,
    ) -> Self


// Struct: ThreeDSecureIoConnectorMetaData
// File: crates/hyperswitch_connectors/src/connectors/threedsecureio/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ThreeDSecureIoConnectorMetaData


// Struct: PaymentData
// File: crates/api_models/src/three_ds_decision_rule.rs
// Module: api_models
// Implementations: 0

pub struct PaymentData


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

impl webhooks::IncomingWebhook for for Bankofamerica


// Struct: AciCaptureResult
// File: crates/hyperswitch_connectors/src/connectors/aci/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AciCaptureResult


// Implementation: impl ConnectorAccessToken for for KafkaStore
// File: crates/router/src/db/kafka_store.rs
// Module: router
// Methods: 2 total (0 public)

impl ConnectorAccessToken for for KafkaStore


// Struct: Href
// File: crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Href


// Implementation: impl Bitpay
// File: crates/hyperswitch_connectors/src/connectors/bitpay.rs
// Module: hyperswitch_connectors
// Methods: 1 total (1 public)

impl Bitpay


// File: crates/scheduler/src/consumer/workflows.rs
// Module: scheduler

use async_trait::async_trait;
use common_utils::errors::CustomResult;
pub use diesel_models::process_tracker as storage;
use diesel_models::process_tracker::business_status;
use router_env::logger;

use crate::{errors, SchedulerSessionState};

pub type WorkflowSelectorFn =
    fn(&storage::ProcessTracker) -> Result<(), errors::ProcessTrackerError>;

#[async_trait]
pub trait ProcessTrackerWorkflows<T>: Send + Sync {
    // The core execution of the workflow
    async fn trigger_workflow<'a>(
        &'a self,
        _state: &'a T,
        _process: storage::ProcessTracker,
    ) -> CustomResult<(), errors::ProcessTrackerError> {
        Err(errors::ProcessTrackerError::NotImplemented)?
    }

    async fn execute_workflow<'a>(
        &'a self,
        operation: Box<dyn ProcessTrackerWorkflow<T>>,
        state: &'a T,
        process: storage::ProcessTracker,
    ) -> CustomResult<(), errors::ProcessTrackerError>
    where
        T: SchedulerSessionState,
    {
        let app_state = &state.clone();
        let output = operation.execute_workflow(app_state, process.clone()).await;
        match output {
            Ok(_) => operation.success_handler(app_state, process).await,
            Err(error) => match operation
                .error_handler(app_state, process.clone(), error)
                .await
            {
                Ok(_) => (),
                Err(error) => {
                    logger::error!(
                        ?error,
                        "Failed to handle process tracker workflow execution error"
                    );
                    let status = app_state
                        .get_db()
                        .as_scheduler()
                        .finish_process_with_business_status(
                            process,
                            business_status::GLOBAL_FAILURE,
                        )
                        .await;
                    if let Err(error) = status {
                        logger::error!(?error, "Failed to update process business status");
                    }
                }
            },
        };
        Ok(())
    }
}

#[async_trait]
pub trait ProcessTrackerWorkflow<T>: Send + Sync {
    /// The core execution of the workflow
    async fn execute_workflow<'a>(
        &'a self,
        _state: &'a T,
        _process: storage::ProcessTracker,
    ) -> Result<(), errors::ProcessTrackerError> {
        Err(errors::ProcessTrackerError::NotImplemented)?
    }

    /// Callback function after successful execution of the `execute_workflow`
    async fn success_handler<'a>(&'a self, _state: &'a T, _process: storage::ProcessTracker) {}

    /// Callback function after error received from `execute_workflow`
    async fn error_handler<'a>(
        &'a self,
        _state: &'a T,
        _process: storage::ProcessTracker,
        _error: errors::ProcessTrackerError,
    ) -> CustomResult<(), errors::ProcessTrackerError> {
        Err(errors::ProcessTrackerError::NotImplemented)?
    }
}


// Struct: ErrorsObject
// File: crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ErrorsObject


// Struct: OrganizationNew
// File: crates/diesel_models/src/organization.rs
// Module: diesel_models
// Implementations: 4
// Traits: OrganizationBridge, OrganizationBridge

pub struct OrganizationNew


// Struct: BillTo
// File: crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BillTo