File: crates/router/src/services/kafka/refund.rs
 Public functions: 2
 Public structs: 2

#[cfg(feature = "v2")]
use common_utils::pii;
#[cfg(feature = "v2")]
use common_utils::types::{self, ChargeRefunds};
use common_utils::{
    id_type,
    types::{ConnectorTransactionIdTrait, MinorUnit},
};
use diesel_models::{enums as storage_enums, refund::Refund};
use time::OffsetDateTime;

use crate::events;

#[cfg(feature = "v1")]
#[derive(serde::Serialize, Debug)]
pub struct KafkaRefund<'a> {
    pub internal_reference_id: &'a String,
    pub refund_id: &'a String, //merchant_reference id
    pub payment_id: &'a id_type::PaymentId,
    pub merchant_id: &'a id_type::MerchantId,
    pub connector_transaction_id: &'a String,
    pub connector: &'a String,
    pub connector_refund_id: Option<&'a String>,
    pub external_reference_id: Option<&'a String>,
    pub refund_type: &'a storage_enums::RefundType,
    pub total_amount: &'a MinorUnit,
    pub currency: &'a storage_enums::Currency,
    pub refund_amount: &'a MinorUnit,
    pub refund_status: &'a storage_enums::RefundStatus,
    pub sent_to_gateway: &'a bool,
    pub refund_error_message: Option<&'a String>,
    pub refund_arn: Option<&'a String>,
    #[serde(default, with = "time::serde::timestamp")]
    pub created_at: OffsetDateTime,
    #[serde(default, with = "time::serde::timestamp")]
    pub modified_at: OffsetDateTime,
    pub description: Option<&'a String>,
    pub attempt_id: &'a String,
    pub refund_reason: Option<&'a String>,
    pub refund_error_code: Option<&'a String>,
    pub profile_id: Option<&'a id_type::ProfileId>,
    pub organization_id: &'a id_type::OrganizationId,
}

#[cfg(feature = "v1")]
impl<'a> KafkaRefund<'a> {
    pub fn from_storage(refund: &'a Refund) -> Self {
        Self {
            internal_reference_id: &refund.internal_reference_id,
            refund_id: &refund.refund_id,
            payment_id: &refund.payment_id,
            merchant_id: &refund.merchant_id,
            connector_transaction_id: refund.get_connector_transaction_id(),
            connector: &refund.connector,
            connector_refund_id: refund.get_optional_connector_refund_id(),
            external_reference_id: refund.external_reference_id.as_ref(),
            refund_type: &refund.refund_type,
            total_amount: &refund.total_amount,
            currency: &refund.currency,
            refund_amount: &refund.refund_amount,
            refund_status: &refund.refund_status,
            sent_to_gateway: &refund.sent_to_gateway,
            refund_error_message: refund.refund_error_message.as_ref(),
            refund_arn: refund.refund_arn.as_ref(),
            created_at: refund.created_at.assume_utc(),
            modified_at: refund.modified_at.assume_utc(),
            description: refund.description.as_ref(),
            attempt_id: &refund.attempt_id,
            refund_reason: refund.refund_reason.as_ref(),
            refund_error_code: refund.refund_error_code.as_ref(),
            profile_id: refund.profile_id.as_ref(),
            organization_id: &refund.organization_id,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(serde::Serialize, Debug)]
pub struct KafkaRefund<'a> {
    pub refund_id: &'a id_type::GlobalRefundId,
    pub merchant_reference_id: &'a id_type::RefundReferenceId,
    pub payment_id: &'a id_type::GlobalPaymentId,
    pub merchant_id: &'a id_type::MerchantId,
    pub connector_transaction_id: &'a types::ConnectorTransactionId,
    pub connector: &'a String,
    pub connector_refund_id: Option<&'a types::ConnectorTransactionId>,
    pub external_reference_id: Option<&'a String>,
    pub refund_type: &'a storage_enums::RefundType,
    pub total_amount: &'a MinorUnit,
    pub currency: &'a storage_enums::Currency,
    pub refund_amount: &'a MinorUnit,
    pub refund_status: &'a storage_enums::RefundStatus,
    pub sent_to_gateway: &'a bool,
    pub refund_error_message: Option<&'a String>,
    pub refund_arn: Option<&'a String>,
    #[serde(default, with = "time::serde::timestamp")]
    pub created_at: OffsetDateTime,
    #[serde(default, with = "time::serde::timestamp")]
    pub modified_at: OffsetDateTime,
    pub description: Option<&'a String>,
    pub attempt_id: &'a id_type::GlobalAttemptId,
    pub refund_reason: Option<&'a String>,
    pub refund_error_code: Option<&'a String>,
    pub profile_id: Option<&'a id_type::ProfileId>,
    pub organization_id: &'a id_type::OrganizationId,

    pub metadata: Option<&'a pii::SecretSerdeValue>,
    pub updated_by: &'a String,
    pub merchant_connector_id: Option<&'a id_type::MerchantConnectorAccountId>,
    pub charges: Option<&'a ChargeRefunds>,
    pub connector_refund_data: Option<&'a String>,
    pub connector_transaction_data: Option<&'a String>,
    pub split_refunds: Option<&'a common_types::refunds::SplitRefund>,
    pub unified_code: Option<&'a String>,
    pub unified_message: Option<&'a String>,
    pub processor_refund_data: Option<&'a String>,
    pub processor_transaction_data: Option<&'a String>,
}

#[cfg(feature = "v2")]
impl<'a> KafkaRefund<'a> {
    pub fn from_storage(refund: &'a Refund) -> Self {
        let Refund {
            payment_id,
            merchant_id,
            connector_transaction_id,
            connector,
            connector_refund_id,
            external_reference_id,
            refund_type,
            total_amount,
            currency,
            refund_amount,
            refund_status,
            sent_to_gateway,
            refund_error_message,
            metadata,
            refund_arn,
            created_at,
            modified_at,
            description,
            attempt_id,
            refund_reason,
            refund_error_code,
            profile_id,
            updated_by,
            charges,
            organization_id,
            split_refunds,
            unified_code,
            unified_message,
            processor_refund_data,
            processor_transaction_data,
            id,
            merchant_reference_id,
            connector_id,
        } = refund;

        Self {
            refund_id: id,
            merchant_reference_id,
            payment_id,
            merchant_id,
            connector_transaction_id,
            connector,
            connector_refund_id: connector_refund_id.as_ref(),
            external_reference_id: external_reference_id.as_ref(),
            refund_type,
            total_amount,
            currency,
            refund_amount,
            refund_status,
            sent_to_gateway,
            refund_error_message: refund_error_message.as_ref(),
            refund_arn: refund_arn.as_ref(),
            created_at: created_at.assume_utc(),
            modified_at: modified_at.assume_utc(),
            description: description.as_ref(),
            attempt_id,
            refund_reason: refund_reason.as_ref(),
            refund_error_code: refund_error_code.as_ref(),
            profile_id: profile_id.as_ref(),
            organization_id,
            metadata: metadata.as_ref(),
            updated_by,
            merchant_connector_id: connector_id.as_ref(),
            charges: charges.as_ref(),
            connector_refund_data: processor_refund_data.as_ref(),
            connector_transaction_data: processor_transaction_data.as_ref(),
            split_refunds: split_refunds.as_ref(),
            unified_code: unified_code.as_ref(),
            unified_message: unified_message.as_ref(),
            processor_refund_data: processor_refund_data.as_ref(),
            processor_transaction_data: processor_transaction_data.as_ref(),
        }
    }
}

impl super::KafkaMessage for KafkaRefund<'_> {
    #[cfg(feature = "v1")]
    fn key(&self) -> String {
        format!(
            "{}_{}_{}_{}",
            self.merchant_id.get_string_repr(),
            self.payment_id.get_string_repr(),
            self.attempt_id,
            self.refund_id
        )
    }
    #[cfg(feature = "v2")]
    fn key(&self) -> String {
        format!(
            "{}_{}_{}_{}",
            self.merchant_id.get_string_repr(),
            self.payment_id.get_string_repr(),
            self.attempt_id.get_string_repr(),
            self.merchant_reference_id.get_string_repr()
        )
    }

    fn event_type(&self) -> events::EventType {
        events::EventType::Refund
    }
}


pub struct ElavonRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}


/// Get the four-digit expiration year from the Apple Pay pre-decrypt data
    pub fn get_four_digit_expiry_year(&self) -> Secret<String> {
        let mut year = self.application_expiration_year.peek().clone();
        if year.len() == 2 {
            year = format!("20{year}");
        }
        Secret::new(year)
    }


pub struct MerchantConnectorAccount;


OpenAPI Block Path: components.schemas.PaymentLinkInitiateRequest
{
  "type": "object",
  "required": [
    "merchant_id",
    "payment_id"
  ],
  "properties": {
    "merchant_id": {
      "type": "string"
    },
    "payment_id": {
      "type": "string"
    }
  }
}


pub async fn payments_status_with_gateway_creds(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Json<api_models::payments::PaymentsRetrieveRequest>,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder {
    use hyperswitch_domain_models::payments::PaymentStatusData;

    let flow = match payload.force_sync {
        true => Flow::PaymentsRetrieveForceSync,
        false => Flow::PaymentsRetrieve,
    };
    tracing::Span::current().record("flow", flow.to_string());

    let global_payment_id = path.into_inner();
    tracing::Span::current().record("payment_id", global_payment_id.get_string_repr());

    let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId {
        global_payment_id,
        payload: payload.into_inner(),
    };

    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };

    let locking_action = internal_payload.get_locking_input(flow.clone());

    let auth_type = if state.conf.merchant_id_auth.merchant_id_auth_enabled {
        &auth::MerchantIdAuth
    } else {
        match env::which() {
            env::Env::Production => &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            _ => auth::auth_type(
                &auth::V2ApiKeyAuth {
                    is_connected_allowed: false,
                    is_platform_allowed: false,
                },
                &auth::JWTAuth {
                    permission: Permission::ProfilePaymentWrite,
                },
                req.headers(),
            ),
        }
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        internal_payload,
        |state, auth: auth::AuthenticationData, req, req_state| async {
            let payment_id = req.global_payment_id;
            let request = req.payload;

            let operation = payments::operations::PaymentGet;

            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            Box::pin(payments::payments_core::<
                api_types::PSync,
                api_models::payments::PaymentsResponse,
                _,
                _,
                _,
                PaymentStatusData<api_types::PSync>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                operation,
                request,
                payment_id,
                payments::CallConnectorAction::Trigger,
                header_payload.clone(),
            ))
            .await
        },
        auth_type,
        locking_action,
    ))
    .await
}


pub fn get_connector_name(&self) -> Option<common_enums::connector_enums::Connector> {
        match self {
            Self::MerchantConnectorAccount(merchant_connector_account) => {
                Some(merchant_connector_account.connector_name)
            }
            Self::MerchantConnectorDetails(merchant_connector_details) => {
                Some(merchant_connector_details.connector_name)
            }
        }
    }


pub async fn payouts_list_core(
    _state: SessionState,
    _merchant_context: domain::MerchantContext,
    _profile_id_list: Option<Vec<id_type::ProfileId>>,
    _constraints: payouts::PayoutListConstraints,
) -> RouterResponse<payouts::PayoutListResponse> {
    todo!()
}


pub struct DeutschebankRefundRequest {
    changed_amount: MinorUnit,
    kind: DeutschebankTransactionKind,
}


pub struct Cards3DSRequest {
    #[serde(rename = "card[number]")]
    pub card_number: CardNumber,
    #[serde(rename = "card[expMonth]")]
    pub card_exp_month: Secret<String>,
    #[serde(rename = "card[expYear]")]
    pub card_exp_year: Secret<String>,
    return_url: String,
}


pub struct ExternalAuthenticationPayload {
    /// trans_status
    pub trans_status: common_enums::TransactionStatus,
    /// authentication_value
    pub authentication_value: Option<masking::Secret<String>>,
    /// eci
    pub eci: Option<String>,
    /// Indicates whether the challenge was canceled by the user or system.
    pub challenge_cancel: Option<String>,
    /// Reason for the challenge code, if applicable.
    pub challenge_code_reason: Option<String>,
}


pub trait EmbedError: Sized {
    fn embed(self) -> Self {
        self
    }
}

impl EmbedError for Report<api_models::errors::types::ApiErrorResponse> {
    fn embed(self) -> Self {
        #[cfg(feature = "detailed_errors")]
        {
            let mut report = self;
            let error_trace = serde_json::to_value(&report).ok().and_then(|inner| {
                serde_json::from_value::<Vec<errors::NestedErrorStack<'_>>>(inner)
                    .ok()
                    .map(Into::<errors::VecLinearErrorStack<'_>>::into)
                    .map(serde_json::to_value)
                    .transpose()
                    .ok()
                    .flatten()
            });

            match report.downcast_mut::<api_models::errors::types::ApiErrorResponse>() {
                None => {}
                Some(inner) => {
                    inner.get_internal_error_mut().stacktrace = error_trace;
                }
            }
            report
        }

        #[cfg(not(feature = "detailed_errors"))]
        self
    }
}

impl EmbedError
    for Report<hyperswitch_domain_models::errors::api_error_response::ApiErrorResponse>
{
}

pub fn http_response_json<T: body::MessageBody + 'static>(response: T) -> HttpResponse {
    HttpResponse::Ok()
        .content_type(mime::APPLICATION_JSON)
        .body(response)
}

pub fn http_server_error_json_response<T: body::MessageBody + 'static>(
    response: T,
) -> HttpResponse {
    HttpResponse::InternalServerError()
        .content_type(mime::APPLICATION_JSON)
        .body(response)
}

pub fn http_response_json_with_headers<T: body::MessageBody + 'static>(
    response: T,
    headers: Vec<(String, Maskable<String>)>,
    request_duration: Option<Duration>,
    status_code: Option<http::StatusCode>,
) -> HttpResponse {
    let mut response_builder = HttpResponse::build(status_code.unwrap_or(http::StatusCode::OK));
    for (header_name, header_value) in headers {
        let is_sensitive_header = header_value.is_masked();
        let mut header_value = header_value.into_inner();
        if header_name == X_HS_LATENCY {
            if let Some(request_duration) = request_duration {
                if let Ok(external_latency) = header_value.parse::<u128>() {
                    let updated_duration = request_duration.as_millis() - external_latency;
                    header_value = updated_duration.to_string();
                }
            }
        }
        let mut header_value = match HeaderValue::from_str(header_value.as_str()) {
            Ok(header_value) => header_value,
            Err(error) => {
                logger::error!(?error);
                return http_server_error_json_response("Something Went Wrong");
            }
        };

        if is_sensitive_header {
            header_value.set_sensitive(true);
        }
        response_builder.append_header((header_name, header_value));
    }

    response_builder
        .content_type(mime::APPLICATION_JSON)
        .body(response)
}

pub fn http_response_plaintext<T: body::MessageBody + 'static>(res: T) -> HttpResponse {
    HttpResponse::Ok().content_type(mime::TEXT_PLAIN).body(res)
}

pub fn http_response_file_data<T: body::MessageBody + 'static>(
    res: T,
    content_type: mime::Mime,
) -> HttpResponse {
    HttpResponse::Ok().content_type(content_type).body(res)
}

pub fn http_response_html_data<T: body::MessageBody + 'static>(
    res: T,
    optional_headers: Option<HashSet<(&'static str, String)>>,
) -> HttpResponse {
    let mut res_builder = HttpResponse::Ok();
    res_builder.content_type(mime::TEXT_HTML);

    if let Some(headers) = optional_headers {
        for (key, value) in headers {
            if let Ok(header_val) = HeaderValue::try_from(value) {
                res_builder.insert_header((HeaderName::from_static(key), header_val));
            }
        }
    }

    res_builder.body(res)
}

pub fn http_response_ok() -> HttpResponse {
    HttpResponse::Ok().finish()
}

pub fn http_redirect_response<T: body::MessageBody + 'static>(
    response: T,
    redirection_response: api::RedirectionResponse,
) -> HttpResponse {
    HttpResponse::Ok()
        .content_type(mime::APPLICATION_JSON)
        .append_header((
            "Location",
            redirection_response.return_url_with_query_params,
        ))
        .status(http::StatusCode::FOUND)
        .body(response)
}

pub fn http_response_err<T: body::MessageBody + 'static>(response: T) -> HttpResponse {
    HttpResponse::BadRequest()
        .content_type(mime::APPLICATION_JSON)
        .body(response)
}

pub trait Authenticate {
    fn get_client_secret(&self) -> Option<&String> {
        None
    }

    fn should_return_raw_response(&self) -> Option<bool> {
        None
    }
}

#[cfg(feature = "v2")]
impl Authenticate for api_models::payments::PaymentsConfirmIntentRequest {
    fn should_return_raw_response(&self) -> Option<bool> {
        self.return_raw_connector_response
    }
}
#[cfg(feature = "v2")]
impl Authenticate for api_models::payments::ProxyPaymentsRequest {}

#[cfg(feature = "v2")]
impl Authenticate for api_models::payments::ExternalVaultProxyPaymentsRequest {}

#[cfg(feature = "v1")]
impl Authenticate for api_models::payments::PaymentsRequest {
    fn get_client_secret(&self) -> Option<&String> {
        self.client_secret.as_ref()
    }

    fn should_return_raw_response(&self) -> Option<bool> {
        // In v1, this maps to `all_keys_required` to retain backward compatibility.
        // The equivalent field in v2 is `return_raw_connector_response`.
        self.all_keys_required
    }
}

#[cfg(feature = "v1")]
impl Authenticate for api_models::payment_methods::PaymentMethodListRequest {
    fn get_client_secret(&self) -> Option<&String> {
        self.client_secret.as_ref()
    }
}

#[cfg(feature = "v1")]
impl Authenticate for api_models::payments::PaymentsSessionRequest {
    fn get_client_secret(&self) -> Option<&String> {
        Some(&self.client_secret)
    }
}
impl Authenticate for api_models::payments::PaymentsDynamicTaxCalculationRequest {
    fn get_client_secret(&self) -> Option<&String> {
        Some(self.client_secret.peek())
    }
}

impl Authenticate for api_models::payments::PaymentsPostSessionTokensRequest {
    fn get_client_secret(&self) -> Option<&String> {
        Some(self.client_secret.peek())
    }
}

impl Authenticate for api_models::payments::PaymentsUpdateMetadataRequest {}
impl Authenticate for api_models::payments::PaymentsRetrieveRequest {
    #[cfg(feature = "v2")]
    fn should_return_raw_response(&self) -> Option<bool> {
        self.return_raw_connector_response
    }

    #[cfg(feature = "v1")]
    fn should_return_raw_response(&self) -> Option<bool> {
        // In v1, this maps to `all_keys_required` to retain backward compatibility.
        // The equivalent field in v2 is `return_raw_connector_response`.
        self.all_keys_required
    }
}
impl Authenticate for api_models::payments::PaymentsCancelRequest {}
impl Authenticate for api_models::payments::PaymentsCancelPostCaptureRequest {}
impl Authenticate for api_models::payments::PaymentsCaptureRequest {}
impl Authenticate for api_models::payments::PaymentsIncrementalAuthorizationRequest {}
impl Authenticate for api_models::payments::PaymentsStartRequest {}
// impl Authenticate for api_models::payments::PaymentsApproveRequest {}
impl Authenticate for api_models::payments::PaymentsRejectRequest {}
// #[cfg(feature = "v2")]
// impl Authenticate for api_models::payments::PaymentsIntentResponse {}

pub fn build_redirection_form(
    form: &RedirectForm,
    payment_method_data: Option<PaymentMethodData>,
    amount: String,
    currency: String,
    config: Settings,
) -> maud::Markup {
    use maud::PreEscaped;
    let logging_template =
        include_str!("redirection/assets/redirect_error_logs_push.js").to_string();
    match form {
        RedirectForm::Form {
            endpoint,
            method,
            form_fields,
        } => maud::html! {
        (maud::DOCTYPE)
        html {
            meta name="viewport" content="width=device-width, initial-scale=1";
            head {
                style {
                    r##"

                    "##
                }
                (PreEscaped(r##"
                <style>
                    #loader1 {
                        width: 500px,
                    }
                    @media (max-width: 600px) {
                        #loader1 {
                            width: 200px
                        }
                    }
                </style>
                "##))
            }

            body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {

                div id="loader1" class="lottie" style="height: 150px; display: block; position: relative; margin-left: auto; margin-right: auto;" { "" }

                (PreEscaped(r#"<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.4/lottie.min.js"></script>"#))

                (PreEscaped(r#"
                <script>
                var anime = bodymovin.loadAnimation({
                    container: document.getElementById('loader1'),
                    renderer: 'svg',
                    loop: true,
                    autoplay: true,
                    name: 'hyperswitch loader',
                    animationData: {"v":"4.8.0","meta":{"g":"LottieFiles AE 3.1.1","a":"","k":"","d":"","tc":""},"fr":29.9700012207031,"ip":0,"op":31.0000012626559,"w":400,"h":250,"nm":"loader_shape","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[278.25,202.671,0],"ix":2},"a":{"a":0,"k":[23.72,23.72,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[12.935,0],[0,-12.936],[-12.935,0],[0,12.935]],"o":[[-12.952,0],[0,12.935],[12.935,0],[0,-12.936]],"v":[[0,-23.471],[-23.47,0.001],[0,23.471],[23.47,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":19.99,"s":[100]},{"t":29.9800012211104,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[23.72,23.721],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":48.0000019550801,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"square 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[196.25,201.271,0],"ix":2},"a":{"a":0,"k":[22.028,22.03,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.914,0],[0,0],[0,-1.914],[0,0],[-1.914,0],[0,0],[0,1.914],[0,0]],"o":[[0,0],[-1.914,0],[0,0],[0,1.914],[0,0],[1.914,0],[0,0],[0,-1.914]],"v":[[18.313,-21.779],[-18.312,-21.779],[-21.779,-18.313],[-21.779,18.314],[-18.312,21.779],[18.313,21.779],[21.779,18.314],[21.779,-18.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14.99,"s":[100]},{"t":24.9800010174563,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[22.028,22.029],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Triangle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[116.25,200.703,0],"ix":2},"a":{"a":0,"k":[27.11,21.243,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.558,-0.879],[0,0],[-1.133,0],[0,0],[0.609,0.947],[0,0]],"o":[[-0.558,-0.879],[0,0],[-0.609,0.947],[0,0],[1.133,0],[0,0],[0,0]],"v":[[1.209,-20.114],[-1.192,-20.114],[-26.251,18.795],[-25.051,20.993],[25.051,20.993],[26.251,18.795],[1.192,-20.114]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9.99,"s":[100]},{"t":19.9800008138021,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.11,21.243],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":48.0000019550801,"st":0,"bm":0}],"markers":[]}
                })
                </script>
                "#))

                h3 style="text-align: center;" { "Please wait while we process your payment..." }
                    form action=(PreEscaped(endpoint)) method=(method.to_string()) #payment_form {
                        @for (field, value) in form_fields {
                        input type="hidden" name=(field) value=(value);
                    }
                }
                (PreEscaped(format!(r#"
                    <script type="text/javascript"> {logging_template}
                    var frm = document.getElementById("payment_form");
                    var formFields = frm.querySelectorAll("input");

                    if (frm.method.toUpperCase() === "GET" && formFields.length === 0) {{
                        window.setTimeout(function () {{
                            window.location.href = frm.action;
                        }}, 300);
                    }} else {{
                        window.setTimeout(function () {{
                            frm.submit();
                        }}, 300);
                    }}
                    </script>
                    "#)))

            }
        }
        },
        RedirectForm::Html { html_data } => {
            PreEscaped(format!("{html_data} <script>{logging_template}</script>"))
        }
        RedirectForm::BarclaycardAuthSetup {
            access_token,
            ddc_url,
            reference_id,
        } => {
            maud::html! {
            (maud::DOCTYPE)
            html {
                head {
                    meta name="viewport" content="width=device-width, initial-scale=1";
                }
                    body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {

                        div id="loader1" class="lottie" style="height: 150px; display: block; position: relative; margin-top: 150px; margin-left: auto; margin-right: auto;" { "" }

                        (PreEscaped(r#"<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.4/lottie.min.js"></script>"#))

                        (PreEscaped(r#"
                            <script>
                            var anime = bodymovin.loadAnimation({
                                container: document.getElementById('loader1'),
                                renderer: 'svg',
                                loop: true,
                                autoplay: true,
                                name: 'hyperswitch loader',
                                animationData: {"v":"4.8.0","meta":{"g":"LottieFiles AE 3.1.1","a":"","k":"","d":"","tc":""},"fr":29.9700012207031,"ip":0,"op":31.0000012626559,"w":400,"h":250,"nm":"loader_shape","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[278.25,202.671,0],"ix":2},"a":{"a":0,"k":[23.72,23.72,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[12.935,0],[0,-12.936],[-12.935,0],[0,12.935]],"o":[[-12.952,0],[0,12.935],[12.935,0],[0,-12.936]],"v":[[0,-23.471],[-23.47,0.001],[0,23.471],[23.47,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":19.99,"s":[100]},{"t":29.9800012211104,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[23.72,23.721],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":48.0000019550801,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"square 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[196.25,201.271,0],"ix":2},"a":{"a":0,"k":[22.028,22.03,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.914,0],[0,0],[0,-1.914],[0,0],[-1.914,0],[0,0],[0,1.914],[0,0]],"o":[[0,0],[-1.914,0],[0,0],[0,1.914],[0,0],[1.914,0],[0,0],[0,-1.914]],"v":[[18.313,-21.779],[-18.312,-21.779],[-21.779,-18.313],[-21.779,18.314],[-18.312,21.779],[18.313,21.779],[21.779,18.314],[21.779,-18.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14.99,"s":[100]},{"t":24.9800010174563,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[22.028,22.029],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Triangle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[116.25,200.703,0],"ix":2},"a":{"a":0,"k":[27.11,21.243,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.558,-0.879],[0,0],[-1.133,0],[0,0],[0.609,0.947],[0,0]],"o":[[-0.558,-0.879],[0,0],[-0.609,0.947],[0,0],[1.133,0],[0,0],[0,0]],"v":[[1.209,-20.114],[-1.192,-20.114],[-26.251,18.795],[-25.051,20.993],[25.051,20.993],[26.251,18.795],[1.192,-20.114]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9.99,"s":[100]},{"t":19.9800008138021,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.11,21.243],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":48.0000019550801,"st":0,"bm":0}],"markers":[]}
                            })
                            </script>
                            "#))

                        h3 style="text-align: center;" { "Please wait while we process your payment..." }
                    }

                (PreEscaped(r#"<iframe id="cardinal_collection_iframe" name="collectionIframe" height="10" width="10" style="display: none;"></iframe>"#))
                (PreEscaped(format!("<form id=\"cardinal_collection_form\" method=\"POST\" target=\"collectionIframe\" action=\"{ddc_url}\">
                <input id=\"cardinal_collection_form_input\" type=\"hidden\" name=\"JWT\" value=\"{access_token}\">
              </form>")))
              (PreEscaped(r#"<script>
              window.onload = function() {
              var cardinalCollectionForm = document.querySelector('#cardinal_collection_form'); if(cardinalCollectionForm) cardinalCollectionForm.submit();
              }
              </script>"#))
              (PreEscaped(format!("<script>
                {logging_template}
                window.addEventListener(\"message\", function(event) {{
                    if (event.origin === \"https://centinelapistag.cardinalcommerce.com\" || event.origin === \"https://centinelapi.cardinalcommerce.com\") {{
                      window.location.href = window.location.pathname.replace(/payments\\/redirect\\/(\\w+)\\/(\\w+)\\/\\w+/, \"payments/$1/$2/redirect/complete/cybersource?referenceId={reference_id}\");
                    }}
                  }}, false);
                </script>
                ")))
            }}
        }
        RedirectForm::BarclaycardConsumerAuth {
            access_token,
            step_up_url,
        } => {
            maud::html! {
            (maud::DOCTYPE)
            html {
                head {
                    meta name="viewport" content="width=device-width, initial-scale=1";
                }
                    body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {

                        div id="loader1" class="lottie" style="height: 150px; display: block; position: relative; margin-top: 150px; margin-left: auto; margin-right: auto;" { "" }

                        (PreEscaped(r#"<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.4/lottie.min.js"></script>"#))

                        (PreEscaped(r#"
                            <script>
                            var anime = bodymovin.loadAnimation({
                                container: document.getElementById('loader1'),
                                renderer: 'svg',
                                loop: true,
                                autoplay: true,
                                name: 'hyperswitch loader',


pub async fn delete_by_payment_method_id(
        conn: &PgPooledConn,
        payment_method_id: String,
    ) -> StorageResult<Self> {
        generics::generic_delete_one_with_result::<<Self as HasTable>::Table, _, Self>(
            conn,
            dsl::payment_method_id.eq(payment_method_id),
        )
        .await
    }


pub struct CardTestingGuardData {
    pub is_card_ip_blocking_enabled: bool,
    pub card_ip_blocking_cache_key: String,
    pub is_guest_user_card_blocking_enabled: bool,
    pub guest_user_card_blocking_cache_key: String,
    pub is_customer_id_blocking_enabled: bool,
    pub customer_id_blocking_cache_key: String,
    pub card_testing_guard_expiry: i32,
}


pub struct Fulfillments {
    ///shipment_id of the shipped items
    #[schema(max_length = 255, example = "ship_101")]
    pub shipment_id: String,
    ///products sent in the shipment
    #[schema(value_type = Option<Vec<Product>>)]
    pub products: Option<Vec<Product>>,
    ///destination address of the shipment
    #[schema(value_type = Destination)]
    pub destination: Destination,
}


pub struct DisputePayload {
    /// amount
    pub amount: StringMinorUnit,
    /// currency
    pub currency: common_enums::enums::Currency,
    /// dispute_stage
    pub dispute_stage: common_enums::enums::DisputeStage,
    /// connector_status
    pub connector_status: String,
    /// connector_dispute_id
    pub connector_dispute_id: String,
    /// connector_reason
    pub connector_reason: Option<String>,
    /// connector_reason_code
    pub connector_reason_code: Option<String>,
    /// challenge_required_by
    pub challenge_required_by: Option<PrimitiveDateTime>,
    /// created_at
    pub created_at: Option<PrimitiveDateTime>,
    /// updated_at
    pub updated_at: Option<PrimitiveDateTime>,
}


File: crates/router/src/db/dispute.rs
 Public structs: 1

use std::collections::HashMap;

use error_stack::report;
use hyperswitch_domain_models::disputes;
use router_env::{instrument, tracing};

use super::{MockDb, Store};
use crate::{
    connection,
    core::errors::{self, CustomResult},
    types::storage::{self, DisputeDbExt},
};

#[async_trait::async_trait]
pub trait DisputeInterface {
    async fn insert_dispute(
        &self,
        dispute: storage::DisputeNew,
    ) -> CustomResult<storage::Dispute, errors::StorageError>;

    async fn find_by_merchant_id_payment_id_connector_dispute_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
        connector_dispute_id: &str,
    ) -> CustomResult<Option<storage::Dispute>, errors::StorageError>;

    async fn find_dispute_by_merchant_id_dispute_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        dispute_id: &str,
    ) -> CustomResult<storage::Dispute, errors::StorageError>;

    async fn find_disputes_by_constraints(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        dispute_constraints: &disputes::DisputeListConstraints,
    ) -> CustomResult<Vec<storage::Dispute>, errors::StorageError>;

    async fn find_disputes_by_merchant_id_payment_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> CustomResult<Vec<storage::Dispute>, errors::StorageError>;

    async fn update_dispute(
        &self,
        this: storage::Dispute,
        dispute: storage::DisputeUpdate,
    ) -> CustomResult<storage::Dispute, errors::StorageError>;

    async fn get_dispute_status_with_count(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
        time_range: &common_utils::types::TimeRange,
    ) -> CustomResult<Vec<(common_enums::enums::DisputeStatus, i64)>, errors::StorageError>;
}

#[async_trait::async_trait]
impl DisputeInterface for Store {
    #[instrument(skip_all)]
    async fn insert_dispute(
        &self,
        dispute: storage::DisputeNew,
    ) -> CustomResult<storage::Dispute, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        dispute
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_by_merchant_id_payment_id_connector_dispute_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
        connector_dispute_id: &str,
    ) -> CustomResult<Option<storage::Dispute>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::Dispute::find_by_merchant_id_payment_id_connector_dispute_id(
            &conn,
            merchant_id,
            payment_id,
            connector_dispute_id,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_dispute_by_merchant_id_dispute_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        dispute_id: &str,
    ) -> CustomResult<storage::Dispute, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::Dispute::find_by_merchant_id_dispute_id(&conn, merchant_id, dispute_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_disputes_by_constraints(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        dispute_constraints: &disputes::DisputeListConstraints,
    ) -> CustomResult<Vec<storage::Dispute>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::Dispute::filter_by_constraints(&conn, merchant_id, dispute_constraints)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_disputes_by_merchant_id_payment_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> CustomResult<Vec<storage::Dispute>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::Dispute::find_by_merchant_id_payment_id(&conn, merchant_id, payment_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn update_dispute(
        &self,
        this: storage::Dispute,
        dispute: storage::DisputeUpdate,
    ) -> CustomResult<storage::Dispute, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        this.update(&conn, dispute)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn get_dispute_status_with_count(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
        time_range: &common_utils::types::TimeRange,
    ) -> CustomResult<Vec<(common_enums::DisputeStatus, i64)>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::Dispute::get_dispute_status_with_count(
            &conn,
            merchant_id,
            profile_id_list,
            time_range,
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl DisputeInterface for MockDb {
    async fn insert_dispute(
        &self,
        dispute: storage::DisputeNew,
    ) -> CustomResult<storage::Dispute, errors::StorageError> {
        let evidence = dispute.evidence.ok_or(errors::StorageError::MockDbError)?;

        let mut locked_disputes = self.disputes.lock().await;

        if locked_disputes
            .iter()
            .any(|d| d.dispute_id == dispute.dispute_id)
        {
            Err(errors::StorageError::MockDbError)?;
        }

        let now = common_utils::date_time::now();

        let new_dispute = storage::Dispute {
            dispute_id: dispute.dispute_id,
            amount: dispute.amount,
            currency: dispute.currency,
            dispute_stage: dispute.dispute_stage,
            dispute_status: dispute.dispute_status,
            payment_id: dispute.payment_id,
            attempt_id: dispute.attempt_id,
            merchant_id: dispute.merchant_id,
            connector_status: dispute.connector_status,
            connector_dispute_id: dispute.connector_dispute_id,
            connector_reason: dispute.connector_reason,
            connector_reason_code: dispute.connector_reason_code,
            challenge_required_by: dispute.challenge_required_by,
            connector_created_at: dispute.connector_created_at,
            connector_updated_at: dispute.connector_updated_at,
            created_at: now,
            modified_at: now,
            connector: dispute.connector,
            profile_id: dispute.profile_id,
            evidence,
            merchant_connector_id: dispute.merchant_connector_id,
            dispute_amount: dispute.dispute_amount,
            organization_id: dispute.organization_id,
            dispute_currency: dispute.dispute_currency,
        };

        locked_disputes.push(new_dispute.clone());

        Ok(new_dispute)
    }
    async fn find_by_merchant_id_payment_id_connector_dispute_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
        connector_dispute_id: &str,
    ) -> CustomResult<Option<storage::Dispute>, errors::StorageError> {
        Ok(self
            .disputes
            .lock()
            .await
            .iter()
            .find(|d| {
                d.merchant_id == *merchant_id
                    && d.payment_id == *payment_id
                    && d.connector_dispute_id == connector_dispute_id
            })
            .cloned())
    }

    async fn find_dispute_by_merchant_id_dispute_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        dispute_id: &str,
    ) -> CustomResult<storage::Dispute, errors::StorageError> {
        let locked_disputes = self.disputes.lock().await;

        locked_disputes
            .iter()
            .find(|d| d.merchant_id == *merchant_id && d.dispute_id == dispute_id)
            .cloned()
            .ok_or(errors::StorageError::ValueNotFound(format!("No dispute available for merchant_id = {merchant_id:?} and dispute_id = {dispute_id}"))
            .into())
    }

    async fn find_disputes_by_constraints(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        dispute_constraints: &disputes::DisputeListConstraints,
    ) -> CustomResult<Vec<storage::Dispute>, errors::StorageError> {
        let locked_disputes = self.disputes.lock().await;
        let limit_usize = dispute_constraints
            .limit
            .unwrap_or(u32::MAX)
            .try_into()
            .unwrap_or(usize::MAX);
        let offset_usize = dispute_constraints
            .offset
            .unwrap_or(0)
            .try_into()
            .unwrap_or(usize::MIN);
        let filtered_disputes: Vec<storage::Dispute> = locked_disputes
            .iter()
            .filter(|dispute| {
                dispute.merchant_id == *merchant_id
                    && dispute_constraints
                        .dispute_id
                        .as_ref()
                        .map_or(true, |id| &dispute.dispute_id == id)
                    && dispute_constraints
                        .payment_id
                        .as_ref()
                        .map_or(true, |id| &dispute.payment_id == id)
                    && dispute_constraints
                        .profile_id
                        .as_ref()
                        .map_or(true, |profile_ids| {
                            dispute
                                .profile_id
                                .as_ref()
                                .map_or(true, |id| profile_ids.contains(id))
                        })
                    && dispute_constraints
                        .dispute_status
                        .as_ref()
                        .map_or(true, |statuses| statuses.contains(&dispute.dispute_status))
                    && dispute_constraints
                        .dispute_stage
                        .as_ref()
                        .map_or(true, |stages| stages.contains(&dispute.dispute_stage))
                    && dispute_constraints.reason.as_ref().map_or(true, |reason| {
                        dispute
                            .connector_reason
                            .as_ref()
                            .map_or(true, |d_reason| d_reason == reason)
                    })
                    && dispute_constraints
                        .connector
                        .as_ref()
                        .map_or(true, |connectors| {
                            connectors
                                .iter()
                                .any(|connector| dispute.connector.as_str() == *connector)
                        })
                    && dispute_constraints
                        .merchant_connector_id
                        .as_ref()
                        .map_or(true, |id| {
                            dispute.merchant_connector_id.as_ref() == Some(id)
                        })
                    && dispute_constraints
                        .currency
                        .as_ref()
                        .map_or(true, |currencies| {
                            currencies.iter().any(|currency| {
                                dispute
                                    .dispute_currency
                                    .map(|dispute_currency| &dispute_currency == currency)
                                    .unwrap_or(dispute.currency.as_str() == currency.to_string())
                            })
                        })
                    && dispute_constraints
                        .time_range
                        .as_ref()
                        .map_or(true, |range| {
                            let dispute_time = dispute.created_at;
                            dispute_time >= range.start_time
                                && range
                                    .end_time
                                    .map_or(true, |end_time| dispute_time <= end_time)
                        })
            })
            .skip(offset_usize)
            .take(limit_usize)
            .cloned()
            .collect();

        Ok(filtered_disputes)
    }

    async fn find_disputes_by_merchant_id_payment_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> CustomResult<Vec<storage::Dispute>, errors::StorageError> {
        let locked_disputes = self.disputes.lock().await;

        Ok(locked_disputes
            .iter()
            .filter(|d| d.merchant_id == *merchant_id && d.payment_id == *payment_id)
            .cloned()
            .collect())
    }

    async fn update_dispute(
        &self,
        this: storage::Dispute,
        dispute: storage::DisputeUpdate,
    ) -> CustomResult<storage::Dispute, errors::StorageError> {
        let mut locked_disputes = self.disputes.lock().await;

        let dispute_to_update = locked_disputes
            .iter_mut()
            .find(|d| d.dispute_id == this.dispute_id)
            .ok_or(errors::StorageError::MockDbError)?;

        let now = common_utils::date_time::now();

        match dispute {
            storage::DisputeUpdate::Update {
                dispute_stage,
                dispute_status,
                connector_status,
                connector_reason,
                connector_reason_code,
                challenge_required_by,
                connector_updated_at,
            } => {
                if connector_reason.is_some() {
                    dispute_to_update.connector_reason = connector_reason;
                }

                if connector_reason_code.is_some() {
                    dispute_to_update.connector_reason_code = connector_reason_code;
                }

                if challenge_required_by.is_some() {
                    dispute_to_update.challenge_required_by = challenge_required_by;
                }

                if connector_updated_at.is_some() {
                    dispute_to_update.connector_updated_at = connector_updated_at;
                }

                dispute_to_update.dispute_stage = dispute_stage;
                dispute_to_update.dispute_status = dispute_status;
                dispute_to_update.connector_status = connector_status;
            }
            storage::DisputeUpdate::StatusUpdate {
                dispute_status,
                connector_status,
            } => {
                if let Some(status) = connector_status {
                    dispute_to_update.connector_status = status;
                }
                dispute_to_update.dispute_status = dispute_status;
            }
            storage::DisputeUpdate::EvidenceUpdate { evidence } => {
                dispute_to_update.evidence = evidence;
            }
        }

        dispute_to_update.modified_at = now;

        Ok(dispute_to_update.clone())
    }

    async fn get_dispute_status_with_count(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
        time_range: &common_utils::types::TimeRange,
    ) -> CustomResult<Vec<(common_enums::DisputeStatus, i64)>, errors::StorageError> {
        let locked_disputes = self.disputes.lock().await;

        let filtered_disputes_data = locked_disputes
            .iter()
            .filter(|d| {
                d.merchant_id == *merchant_id
                    && d.created_at >= time_range.start_time
                    && time_range
                        .end_time
                        .as_ref()
                        .map(|received_end_time| received_end_time >= &d.created_at)
                        .unwrap_or(true)
                    && profile_id_list
                        .as_ref()
                        .zip(d.profile_id.as_ref())
                        .map(|(received_profile_list, received_profile_id)| {
                            received_profile_list.contains(received_profile_id)
                        })
                        .unwrap_or(true)
            })
            .cloned()
            .collect::<Vec<storage::Dispute>>();

        Ok(filtered_disputes_data
            .into_iter()
            .fold(
                HashMap::new(),
                |mut acc: HashMap<common_enums::DisputeStatus, i64>, value| {
                    acc.entry(value.dispute_status)
                        .and_modify(|value| *value += 1)
                        .or_insert(1);
                    acc
                },
            )
            .into_iter()
            .collect::<Vec<(common_enums::DisputeStatus, i64)>>())
    }
}

#[cfg(test)]
mod tests {
    #![allow(clippy::expect_used, clippy::unwrap_used)]
    mod mockdb_dispute_interface {
        use std::borrow::Cow;

        use common_enums::enums::Currency;
        use common_utils::types::{AmountConvertor, MinorUnit, StringMinorUnitForConnector};
        use diesel_models::{
            dispute::DisputeNew,
            enums::{DisputeStage, DisputeStatus},
        };
        use hyperswitch_domain_models::disputes::DisputeListConstraints;
        use masking::Secret;
        use redis_interface::RedisSettings;
        use serde_json::Value;
        use time::macros::datetime;

        use crate::db::{dispute::DisputeInterface, MockDb};

        pub struct DisputeNewIds {
            dispute_id: String,
            payment_id: common_utils::id_type::PaymentId,
            attempt_id: String,
            merchant_id: common_utils::id_type::MerchantId,
            connector_dispute_id: String,
        }

        fn create_dispute_new(dispute_ids: DisputeNewIds) -> DisputeNew {
            DisputeNew {
                dispute_id: dispute_ids.dispute_id,
                amount: StringMinorUnitForConnector::convert(
                    &StringMinorUnitForConnector,
                    MinorUnit::new(0),
                    Currency::USD,
                )
                .expect("Amount Conversion Error"),
                currency: "currency".into(),
                dispute_stage: DisputeStage::Dispute,
                dispute_status: DisputeStatus::DisputeOpened,
                payment_id: dispute_ids.payment_id,
                attempt_id: dispute_ids.attempt_id,
                merchant_id: dispute_ids.merchant_id,
                connector_status: "connector_status".into(),
                connector_dispute_id: dispute_ids.connector_dispute_id,
                connector_reason: Some("connector_reason".into()),
                connector_reason_code: Some("connector_reason_code".into()),
                challenge_required_by: Some(datetime!(2019-01-01 0:00)),
                connector_created_at: Some(datetime!(2019-01-02 0:00)),
                connector_updated_at: Some(datetime!(2019-01-03 0:00)),
                connector: "connector".into(),
                evidence: Some(Secret::from(Value::String("evidence".into()))),
                profile_id: Some(common_utils::generate_profile_id_of_default_length()),
                merchant_connector_id: None,
                dispute_amount: MinorUnit::new(1040),
                organization_id: common_utils::id_type::OrganizationId::default(),
                dispute_currency: Some(Currency::default()),
            }
        }

        #[tokio::test]
        async fn test_insert_dispute() {
            let mockdb = MockDb::new(&RedisSettings::default())
                .await
                .expect("Failed to create a mock DB");

            let merchant_id =
                common_utils::id_type::MerchantId::try_from(Cow::from("merchant_1")).unwrap();

            let created_dispute = mockdb
                .insert_dispute(create_dispute_new(DisputeNewIds {
                    dispute_id: "dispute_1".into(),
                    attempt_id: "attempt_1".into(),
                    merchant_id: merchant_id.clone(),
                    payment_id: common_utils::id_type::PaymentId::try_from(Cow::Borrowed(
                        "payment_1",
                    ))
                    .unwrap(),
                    connector_dispute_id: "connector_dispute_1".into(),
                }))
                .await
                .unwrap();

            let found_dispute = mockdb
                .disputes
                .lock()
                .await
                .iter()
                .find(|d| d.dispute_id == created_dispute.dispute_id)
                .cloned();

            assert!(found_dispute.is_some());

            assert_eq!(created_dispute, found_dispute.unwrap());
        }

        #[tokio::test]
        async fn test_find_by_merchant_id_payment_id_connector_dispute_id() {
            let merchant_id =
                common_utils::id_type::MerchantId::try_from(Cow::from("merchant_1")).unwrap();

            let mockdb = MockDb::new(&RedisSettings::default())
                .await
                .expect("Failed to create Mock store");

            let created_dispute = mockdb
                .insert_dispute(create_dispute_new(DisputeNewIds {
                    dispute_id: "dispute_1".into(),
                    attempt_id: "attempt_1".into(),
                    merchant_id: merchant_id.clone(),
                    payment_id: common_utils::id_type::PaymentId::try_from(Cow::Borrowed(
                        "payment_1",
                    ))
                    .unwrap(),
                    connector_dispute_id: "connector_dispute_1".into(),
                }))
                .await
                .unwrap();

            let _ = mockdb
                .insert_dispute(create_dispute_new(DisputeNewIds {
                    dispute_id: "dispute_2".into(),
                    attempt_id: "attempt_1".into(),
                    merchant_id: merchant_id.clone(),
                    payment_id: common_utils::id_type::PaymentId::try_from(Cow::Borrowed(
                        "payment_1",
                    ))
                    .unwrap(),
                    connector_dispute_id: "connector_dispute_2".into(),
                }))
                .await
                .unwrap();

            let found_dispute = mockdb
                .find_by_merchant_id_payment_id_connector_dispute_id(
                    &merchant_id,
                    &common_utils::id_type::PaymentId::try_from(Cow::Borrowed("payment_1"))
                        .unwrap(),
                    "connector_dispute_1",
                )
                .await
                .unwrap();

            assert!(found_dispute.is_some());

            assert_eq!(created_dispute, found_dispute.unwrap());
        }

        #[tokio::test]
        async fn test_find_dispute_by_merchant_id_dispute_id() {
            let merchant_id =
                common_utils::id_type::MerchantId::try_from(Cow::from("merchant_1")).unwrap();

            let payment_id =
                common_utils::id_type::PaymentId::try_from(Cow::Borrowed("payment_1")).unwrap();

            let mockdb = MockDb::new(&RedisSettings::default())
                .await
                .expect("Failed to create Mock store");

            let created_dispute = mockdb
                .insert_dispute(create_dispute_new(DisputeNewIds {
                    dispute_id: "dispute_1".into(),
                    attempt_id: "attempt_1".into(),
                    merchant_id: merchant_id.clone(),
                    payment_id: payment_id.clone(),
                    connector_dispute_id: "connector_dispute_1".into(),
                }))
                .await
                .unwrap();

            let _ = mockdb
                .insert_dispute(create_dispute_new(DisputeNewIds {
                    dispute_id: "dispute_2".into(),
                    attempt_id: "attempt_1".into(),
                    merchant_id: merchant_id.clone(),
                    payment_id: payment_id.clone(),
                    connector_dispute_id: "connector_dispute_1".into(),
                }))
                .await
                .unwrap();

            let found_dispute = mockdb
                .find_dispute_by_merchant_id_dispute_id(&merchant_id, "dispute_1")
                .await
                .unwrap();

            assert_eq!(created_dispute, found_dispute);
        }

        #[tokio::test]
        async fn test_find_disputes_by_merchant_id() {
            let merchant_id =
                common_utils::id_type::MerchantId::try_from(Cow::from("merchant_2")).unwrap();

            let payment_id =
                common_utils::id_type::PaymentId::try_from(Cow::Borrowed("payment_1")).unwrap();

            let mockdb = MockDb::new(&RedisSettings::default())
                .await
                .expect("Failed to create Mock store");

            let created_dispute = mockdb
                .insert_dispute(create_dispute_new(DisputeNewIds {
                    dispute_id: "dispute_1".into(),
                    attempt_id: "attempt_1".into(),
                    merchant_id: merchant_id.clone(),
                    payment_id: payment_id.clone(),
                    connector_dispute_id: "connector_dispute_1".into(),
                }))
                .await
                .unwrap();

            let _ = mockdb
                .insert_dispute(create_dispute_new(DisputeNewIds {
                    dispute_id: "dispute_2".into(),
                    attempt_id: "attempt_1".into(),
                    merchant_id: merchant_id.clone(),
                    payment_id: payment_id.clone(),
                    connector_dispute_id: "connector_dispute_1".into(),
                }))
                .await
                .unwrap();

            let found_disputes = mockdb
                .find_disputes_by_constraints(
                    &merchant_id,
                    &DisputeListConstraints {
                        dispute_id: None,
                        payment_id: None,
                        profile_id: None,
                        connector: None,
                        merchant_connector_id: None,
                        currency: None,
                        limit: None,
                        offset: None,
                        dispute_status: None,
                        dispute_stage: None,
                        reason: None,
                        time_range: None,
                    },
                )
                .await
                .unwrap();

            assert_eq!(1, found_disputes.len());

            assert_eq!(created_dispute, found_disputes.first().unwrap().clone());
        }

        #[tokio::test]
        async fn test_find_disputes_by_merchant_id_payment_id() {
            let merchant_id =
                common_utils::id_type::MerchantId::try_from(Cow::from("merchant_1")).unwrap();

            let payment_id =
                common_utils::id_type::PaymentId::try_from(Cow::Borrowed("payment_1")).unwrap();

            let mockdb = MockDb::new(&RedisSettings::default())
                .await
                .expect("Failed to create Mock store");

            let created_dispute = mockdb
                .insert_dispute(create_dispute_new(DisputeNewIds {
                    dispute_id: "dispute_1".into(),
                    attempt_id: "attempt_1".into(),
                    merchant_id: merchant_id.clone(),
                    payment_id: payment_id.clone(),
                    connector_dispute_id: "connector_dispute_1".into(),
                }))
                .await
                .unwrap();

            let _ = mockdb
                .insert_dispute(create_dispute_new(DisputeNewIds {
                    dispute_id: "dispute_2".into(),
                    attempt_id: "attempt_1".into(),
                    merchant_id: merchant_id.clone(),
                    payment_id: payment_id.clone(),
                    connector_dispute_id: "connector_dispute_1".into(),
                }))
                .await
                .unwrap();

            let found_disputes = mockdb
                .find_disputes_by_merchant_id_payment_id(&merchant_id, &payment_id)
                .await
                .unwrap();

            assert_eq!(1, found_disputes.len());

            assert_eq!(created_dispute, found_disputes.first().unwrap().clone());
        }

        mod update_dispute {
            use std::borrow::Cow;

            use diesel_models::{
                dispute::DisputeUpdate,
                enums::{DisputeStage, DisputeStatus},
            };
            use masking::Secret;
            use serde_json::Value;
            use time::macros::datetime;

            use crate::db::{
                dispute::{
                    tests::mockdb_dispute_interface::{create_dispute_new, DisputeNewIds},
                    DisputeInterface,
                },
                MockDb,
            };

            #[tokio::test]
            async fn test_update_dispute_update() {
                let merchant_id =
                    common_utils::id_type::MerchantId::try_from(Cow::from("merchant_1")).unwrap();

                let payment_id =
                    common_utils::id_type::PaymentId::try_from(Cow::Borrowed("payment_1")).unwrap();

                let mockdb = MockDb::new(&redis_interface::RedisSettings::default())
                    .await
                    .expect("Failed to create Mock store");

                let created_dispute = mockdb
                    .insert_dispute(create_dispute_new(DisputeNewIds {
                        dispute_id: "dispute_1".into(),
                        attempt_id: "attempt_1".into(),
                        merchant_id: merchant_id.clone(),
                        payment_id: payment_id.clone(),
                        connector_dispute_id: "connector_dispute_1".into(),
                    }))
                    .await
                    .unwrap();

                let updated_dispute = mockdb
                    .update_dispute(
                        created_dispute.clone(),
                        DisputeUpdate::Update {
                            dispute_stage: DisputeStage::PreDispute,
                            dispute_status: DisputeStatus::DisputeAccepted,
                            connector_status: "updated_connector_status".into(),
                            connector_reason: Some("updated_connector_reason".into()),
                            connector_reason_code: Some("updated_connector_reason_code".into()),
                            challenge_required_by: Some(datetime!(2019-01-10 0:00)),
                            connector_updated_at: Some(datetime!(2019-01-11 0:00)),
                        },
                    )
                    .await
                    .unwrap();

                assert_eq!(created_dispute.dispute_id, updated_dispute.dispute_id);
                assert_eq!(created_dispute.amount, updated_dispute.amount);
                assert_eq!(created_dispute.currency, updated_dispute.currency);
                assert_ne!(created_dispute.dispute_stage, updated_dispute.dispute_stage);
                assert_ne!(
                    created_dispute.dispute_status,
                    updated_dispute.dispute_status
                );
                assert_eq!(created_dispute.payment_id, updated_dispute.payment_id);
                assert_eq!(created_dispute.attempt_id, updated_dispute.attempt_id);
                assert_eq!(created_dispute.merchant_id, updated_dispute.merchant_id);
                assert_ne!(
                    created_dispute.connector_status,
                    updated_dispute.connector_status
                );
                assert_eq!(
                    created_dispute.connector_dispute_id,
                    updated_dispute.connector_dispute_id
                );
                assert_ne!(
                    created_dispute.connector_reason,
                    updated_dispute.connector_reason
                );
                assert_ne!(
                    created_dispute.connector_reason_code,
                    updated_dispute.connector_reason_code
                );
                assert_ne!(
                    created_dispute.challenge_required_by,
                    updated_dispute.challenge_required_by
                );
                assert_eq!(
                    created_dispute.connector_created_at,
                    updated_dispute.connector_created_at
                );
                assert_ne!(
                    created_dispute.connector_updated_at,
                    updated_dispute.connector_updated_at
                );
                assert_eq!(created_dispute.created_at, updated_dispute.created_at);
                assert_ne!(created_dispute.modified_at, updated_dispute.modified_at);
                assert_eq!(created_dispute.connector, updated_dispute.connector);
                assert_eq!(created_dispute.evidence, updated_dispute.evidence);
            }

            #[tokio::test]
            async fn test_update_dispute_update_status() {
                let merchant_id =
                    common_utils::id_type::MerchantId::try_from(Cow::from("merchant_1")).unwrap();

                let payment_id =
                    common_utils::id_type::PaymentId::try_from(Cow::Borrowed("payment_1")).unwrap();

                let mockdb = MockDb::new(&redis_interface::RedisSettings::default())
                    .await
                    .expect("Failed to create Mock store");

                let created_dispute = mockdb
                    .insert_dispute(create_dispute_new(DisputeNewIds {
                        dispute_id: "dispute_1".into(),
                        attempt_id: "attempt_1".into(),
                        merchant_id: merchant_id.clone(),
                        payment_id: payment_id.clone(),
                        connector_dispute_id: "connector_dispute_1".into(),
                    }))
                    .await
                    .unwrap();

                let updated_dispute = mockdb
                    .update_dispute(
                        created_dispute.clone(),
                        DisputeUpdate::StatusUpdate {
                            dispute_status: DisputeStatus::DisputeExpired,
                            connector_status: Some("updated_connector_status".into()),
                        },
                    )
                    .await
                    .unwrap();

                assert_eq!(created_dispute.dispute_id, updated_dispute.dispute_id);
                assert_eq!(created_dispute.amount, updated_dispute.amount);
                assert_eq!(created_dispute.currency, updated_dispute.currency);
                assert_eq!(created_dispute.dispute_stage, updated_dispute.dispute_stage);
                assert_ne!(
                    created_dispute.dispute_status,
                    updated_dispute.dispute_status
                );
                assert_eq!(created_dispute.payment_id, updated_dispute.payment_id);
                assert_eq!(created_dispute.attempt_id, updated_dispute.attempt_id);
                assert_eq!(created_dispute.merchant_id, updated_dispute.merchant_id);
                assert_ne!(
                    created_dispute.connector_status,
                    updated_dispute.connector_status
                );
                assert_eq!(
                    created_dispute.connector_dispute_id,
                    updated_dispute.connector_dispute_id
                );
                assert_eq!(
                    created_dispute.connector_reason,
                    updated_dispute.connector_reason
                );
                assert_eq!(
                    created_dispute.connector_reason_code,
                    updated_dispute.connector_reason_code
                );
                assert_eq!(
                    created_dispute.challenge_required_by,
                    updated_dispute.challenge_required_by
                );
                assert_eq!(
                    created_dispute.connector_created_at,
                    updated_dispute.connector_created_at
                );
                assert_eq!(
                    created_dispute.connector_updated_at,
                    updated_dispute.connector_updated_at
                );
                assert_eq!(created_dispute.created_at, updated_dispute.created_at);
                assert_ne!(created_dispute.modified_at, updated_dispute.modified_at);
                assert_eq!(created_dispute.connector, updated_dispute.connector);
                assert_eq!(created_dispute.evidence, updated_dispute.evidence);
            }

            #[tokio::test]
            async fn test_update_dispute_update_evidence() {
                let merchant_id =
                    common_utils::id_type::MerchantId::try_from(Cow::from("merchant_1")).unwrap();

                let payment_id =
                    common_utils::id_type::PaymentId::try_from(Cow::Borrowed("payment_1")).unwrap();

                let mockdb = MockDb::new(&redis_interface::RedisSettings::default())
                    .await
                    .expect("Failed to create Mock store");

                let created_dispute = mockdb
                    .insert_dispute(create_dispute_new(DisputeNewIds {
                        dispute_id: "dispute_1".into(),
                        attempt_id: "attempt_1".into(),
                        merchant_id: merchant_id.clone(),
                        payment_id: payment_id.clone(),
                        connector_dispute_id: "connector_dispute_1".into(),
                    }))
                    .await
                    .unwrap();

                let updated_dispute = mockdb
                    .update_dispute(
                        created_dispute.clone(),
                        DisputeUpdate::EvidenceUpdate {
                            evidence: Secret::from(Value::String("updated_evidence".into())),
                        },
                    )
                    .await
                    .unwrap();

                assert_eq!(created_dispute.dispute_id, updated_dispute.dispute_id);
                assert_eq!(created_dispute.amount, updated_dispute.amount);
                assert_eq!(created_dispute.currency, updated_dispute.currency);
                assert_eq!(created_dispute.dispute_stage, updated_dispute.dispute_stage);
                assert_eq!(
                    created_dispute.dispute_status,
                    updated_dispute.dispute_status
                );
                assert_eq!(created_dispute.payment_id, updated_dispute.payment_id);
                assert_eq!(created_dispute.attempt_id, updated_dispute.attempt_id);
                assert_eq!(created_dispute.merchant_id, updated_dispute.merchant_id);
                assert_eq!(
                    created_dispute.connector_status,
                    updated_dispute.connector_status
                );
                assert_eq!(
                    created_dispute.connector_dispute_id,
                    updated_dispute.connector_dispute_id
                );
                assert_eq!(
                    created_dispute.connector_reason,
                    updated_dispute.connector_reason
                );
                assert_eq!(
                    created_dispute.connector_reason_code,
                    updated_dispute.connector_reason_code
                );
                assert_eq!(
                    created_dispute.challenge_required_by,
                    updated_dispute.challenge_required_by
                );
                assert_eq!(
                    created_dispute.connector_created_at,
                    updated_dispute.connector_created_at
                );
                assert_eq!(
                    created_dispute.connector_updated_at,
                    updated_dispute.connector_updated_at
                );
                assert_eq!(created_dispute.created_at, updated_dispute.created_at);
                assert_ne!(created_dispute.modified_at, updated_dispute.modified_at);
                assert_eq!(created_dispute.connector, updated_dispute.connector);
                assert_ne!(created_dispute.evidence, updated_dispute.evidence);
            }
        }
    }
}


pub fn get_type(&self) -> DataType {
        match self {
            Self::Number(_) => DataType::Number,
            Self::StrValue(_) => DataType::StrValue,
            Self::MetadataVariant(_) => DataType::MetadataValue,
            Self::EnumVariant(_) => DataType::EnumVariant,
            Self::NumberComparisonArray(_) => DataType::Number,
            Self::NumberArray(_) => DataType::Number,
            Self::EnumVariantArray(_) => DataType::EnumVariant,
        }
    }


pub async fn customers_create() {}


pub struct ConnectorVolumeSplitV0 {
    pub connector: RoutableConnectorChoice,
    pub split: u8,
}


pub async fn get_user_scoped_metadata_from_db(
    state: &SessionState,
    user_id: String,
    merchant_id: id_type::MerchantId,
    org_id: id_type::OrganizationId,
    metadata_keys: Vec<DBEnum>,
) -> UserResult<Vec<DashboardMetadata>> {
    match state
        .store
        .find_user_scoped_dashboard_metadata(&user_id, &merchant_id, &org_id, metadata_keys)
        .await
    {
        Ok(data) => Ok(data),
        Err(e) => {
            if e.current_context().is_db_not_found() {
                return Ok(Vec::with_capacity(0));
            }
            Err(e
                .change_context(UserErrors::InternalServerError)
                .attach_printable("DB Error Fetching DashboardMetaData"))
        }
    }
}


Documentation: api-reference/v1/payouts/payouts--confirm.mdx
# Type: Doc File

---
openapi: post /payouts/{payout_id}/confirm
---


impl CaptureSyncResponse {
    pub fn get_amount_captured(&self) -> Option<MinorUnit> {
        match self {
            Self::Success { amount, .. } | Self::Error { amount, .. } => *amount,
        }
    }
    pub fn get_connector_response_reference_id(&self) -> Option<String> {
        match self {
            Self::Success {
                connector_response_reference_id,
                ..
            } => connector_response_reference_id.clone(),
            Self::Error { .. } => None,
        }
    }
}


pub struct ExtendedAuthorizationAppliedBool(bool);


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

    pub async fn find_by_global_customer_id_merchant_id_status(
        conn: &PgPooledConn,
        customer_id: &common_utils::id_type::GlobalCustomerId,
        merchant_id: &common_utils::id_type::MerchantId,
        status: storage_enums::PaymentMethodStatus,
        limit: Option<i64>,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::customer_id
                .eq(customer_id.to_owned())
                .and(dsl::merchant_id.eq(merchant_id.to_owned()))
                .and(dsl::status.eq(status)),
            limit,
            None,
            Some(dsl::last_used_at.desc()),
        )
        .await
    }

    pub async fn find_by_global_customer_id(
        conn: &PgPooledConn,
        customer_id: &common_utils::id_type::GlobalCustomerId,
        limit: Option<i64>,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::customer_id.eq(customer_id.to_owned()),
            limit,
            None,
            Some(dsl::last_used_at.desc()),
        )
        .await
    }

    pub async fn update_with_id(
        self,
        conn: &PgPooledConn,
        payment_method: payment_method::PaymentMethodUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(conn, pm_id.eq(self.id.to_owned()), payment_method)
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn find_by_fingerprint_id(
        conn: &PgPooledConn,
        fingerprint_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::locker_fingerprint_id.eq(fingerprint_id.to_owned()),
        )
        .await
    }

    pub async fn get_count_by_merchant_id_status(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        status: common_enums::PaymentMethodStatus,
    ) -> StorageResult<i64> {
        let query = <Self as HasTable>::table().count().filter(
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::status.eq(status.to_owned())),
        );

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

        generics::db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(
            query.get_result_async::<i64>(conn),
            generics::db_metrics::DatabaseOperation::Count,
        )
        .await
        .change_context(errors::DatabaseError::Others)
        .attach_printable("Failed to get a count of payment methods")
    }
}


impl<'a> KafkaPaymentIntent<'a> {
    pub fn from_storage(intent: &'a PaymentIntent, infra_values: Option<Value>) -> Self {
        Self {
            payment_id: &intent.payment_id,
            merchant_id: &intent.merchant_id,
            status: intent.status,
            amount: intent.amount,
            currency: intent.currency,
            amount_captured: intent.amount_captured,
            customer_id: intent.customer_id.as_ref(),
            description: intent.description.as_ref(),
            return_url: intent.return_url.as_ref(),
            metadata: intent.metadata.as_ref().map(|x| x.to_string()),
            connector_id: intent.connector_id.as_ref(),
            statement_descriptor_name: intent.statement_descriptor_name.as_ref(),
            statement_descriptor_suffix: intent.statement_descriptor_suffix.as_ref(),
            created_at: intent.created_at.assume_utc(),
            modified_at: intent.modified_at.assume_utc(),
            last_synced: intent.last_synced.map(|i| i.assume_utc()),
            setup_future_usage: intent.setup_future_usage,
            off_session: intent.off_session,
            client_secret: intent.client_secret.as_ref(),
            active_attempt_id: intent.active_attempt.get_id(),
            business_country: intent.business_country,
            business_label: intent.business_label.as_ref(),
            attempt_count: intent.attempt_count,
            profile_id: intent.profile_id.as_ref(),
            payment_confirm_source: intent.payment_confirm_source,
            // TODO: use typed information here to avoid PII logging
            billing_details: None,
            shipping_details: None,
            customer_email: intent
                .customer_details
                .as_ref()
                .and_then(|value| value.get_inner().peek().as_object())
                .and_then(|obj| obj.get("email"))
                .and_then(|email| email.as_str())
                .map(|email| HashedString::from(Secret::new(email.to_string()))),
            feature_metadata: intent.feature_metadata.as_ref(),
            merchant_order_reference_id: intent.merchant_order_reference_id.as_ref(),
            organization_id: &intent.organization_id,
            infra_values,
        }
    }
}


pub async fn get_api_event_metrics(
        &self,
        metric: &ApiEventMetrics,
        dimensions: &[ApiEventDimensions],
        merchant_id: &common_utils::id_type::MerchantId,
        filters: &ApiEventFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
    ) -> types::MetricsResult<HashSet<(ApiEventMetricsBucketIdentifier, ApiEventMetricRow)>> {
        match self {
            Self::Sqlx(_pool) => Err(report!(MetricsError::NotImplemented)),
            Self::Clickhouse(ckh_pool)
            | Self::CombinedCkh(_, ckh_pool)
            | Self::CombinedSqlx(_, ckh_pool) => {
                // Since API events are ckh only use ckh here
                metric
                    .load_metrics(
                        dimensions,
                        merchant_id,
                        filters,
                        granularity,
                        time_range,
                        ckh_pool,
                    )
                    .await
            }
        }
    }


pub struct WellsfargoNotAvailableErrorObject {
    #[serde(rename = "type")]
    pub error_type: Option<String>,
    pub message: Option<String>,
}


pub fn push(&mut self, elem: V) -> K {
        let curr_len = self.data.len();
        self.data.push(elem);
        K::with_id(curr_len)
    }


impl Responder {
    let flow = Flow::AuthenticationSyncPostUpdate;
    let (merchant_id, authentication_id) = path.into_inner();
    let payload = AuthenticationSyncPostUpdateRequest { authentication_id };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            unified_authentication_service::authentication_post_sync_core(
                state,
                merchant_context,
                req,
            )
        },
        &auth::MerchantIdAuth(merchant_id),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


File: crates/hyperswitch_interfaces/src/api/fraud_check.rs

//! FRM interface
use hyperswitch_domain_models::{
    router_flow_types::{Checkout, Fulfillment, RecordReturn, Sale, Transaction},
    router_request_types::fraud_check::{
        FraudCheckCheckoutData, FraudCheckFulfillmentData, FraudCheckRecordReturnData,
        FraudCheckSaleData, FraudCheckTransactionData,
    },
    router_response_types::fraud_check::FraudCheckResponseData,
};

use crate::api::ConnectorIntegration;

/// trait FraudCheckSale
pub trait FraudCheckSale:
    ConnectorIntegration<Sale, FraudCheckSaleData, FraudCheckResponseData>
{
}

/// trait FraudCheckCheckout
pub trait FraudCheckCheckout:
    ConnectorIntegration<Checkout, FraudCheckCheckoutData, FraudCheckResponseData>
{
}

/// trait FraudCheckTransaction
pub trait FraudCheckTransaction:
    ConnectorIntegration<Transaction, FraudCheckTransactionData, FraudCheckResponseData>
{
}

/// trait FraudCheckFulfillment
pub trait FraudCheckFulfillment:
    ConnectorIntegration<Fulfillment, FraudCheckFulfillmentData, FraudCheckResponseData>
{
}

/// trait FraudCheckRecordReturn
pub trait FraudCheckRecordReturn:
    ConnectorIntegration<RecordReturn, FraudCheckRecordReturnData, FraudCheckResponseData>
{
}

/// trait FraudCheck
pub trait FraudCheck:
    super::ConnectorCommon
    + FraudCheckSale
    + FraudCheckTransaction
    + FraudCheckCheckout
    + FraudCheckFulfillment
    + FraudCheckRecordReturn
{
}


pub async fn record_internal_attempt_api(
    state: &SessionState,
    payment_intent: &payments_domain::PaymentIntent,
    revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
    revenue_recovery_metadata: &payments_api::PaymentRevenueRecoveryMetadata,
    card_info: payments_api::AdditionalCardInfo,
    payment_processor_token: &str,
) -> RouterResult<payments_api::PaymentAttemptRecordResponse> {
    let revenue_recovery_attempt_data =
        recovery_incoming::RevenueRecoveryAttempt::get_revenue_recovery_attempt(
            payment_intent,
            revenue_recovery_metadata,
            &revenue_recovery_payment_data.billing_mca,
            card_info,
            payment_processor_token,
        )
        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
            message: "get_revenue_recovery_attempt was not constructed".to_string(),
        })?;

    let request_payload = revenue_recovery_attempt_data
        .create_payment_record_request(
            state,
            &revenue_recovery_payment_data.billing_mca.id,
            Some(
                revenue_recovery_metadata
                    .active_attempt_payment_connector_id
                    .clone(),
            ),
            Some(revenue_recovery_metadata.connector),
            common_enums::TriggeredBy::Internal,
        )
        .await
        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
            message: "Cannot Create the payment record Request".to_string(),
        })?;

    let merchant_context_from_revenue_recovery_payment_data =
        MerchantContext::NormalMerchant(Box::new(Context(
            revenue_recovery_payment_data.merchant_account.clone(),
            revenue_recovery_payment_data.key_store.clone(),
        )));

    let attempt_response = Box::pin(payments::record_attempt_core(
        state.clone(),
        state.get_req_state(),
        merchant_context_from_revenue_recovery_payment_data,
        revenue_recovery_payment_data.profile.clone(),
        request_payload,
        payment_intent.id.clone(),
        hyperswitch_domain_models::payments::HeaderPayload::default(),
    ))
    .await;

    match attempt_response {
        Ok(services::ApplicationResponse::JsonWithHeaders((attempt_response, _))) => {
            Ok(attempt_response)
        }
        Ok(_) => Err(errors::ApiErrorResponse::PaymentNotFound)
            .attach_printable("Unexpected response from record attempt core"),
        error @ Err(_) => {
            router_env::logger::error!(?error);
            Err(errors::ApiErrorResponse::PaymentNotFound)
                .attach_printable("failed to record attempt for revenue recovery workflow")
        }
    }
}


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

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

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

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let auth = plaid::PlaidAuthType::try_from(auth_type)
            .change_context(ConnectorError::FailedToObtainAuthType)?;
        let client_id = auth.client_id.into_masked();
        let secret = auth.secret.into_masked();

        Ok(vec![
            ("PLAID-CLIENT-ID".to_string(), client_id),
            ("PLAID-SECRET".to_string(), secret),
        ])
    }

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

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.error_code.unwrap_or(NO_ERROR_CODE.to_string()),
            message: response.error_message,
            reason: response.display_message,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}