diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 50ab48a47f..503c7b4e18 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -837,7 +837,4 @@ click_to_pay = {connector_list = "adyen, cybersource, trustpay"} [revenue_recovery] monitoring_threshold_in_seconds = 60 -retry_algorithm_type = "cascading" - -[list_dispute_supported_connectors] -connector_list = "worldpayvantiv" +retry_algorithm_type = "cascading" \ No newline at end of file diff --git a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs index 06fdedf640..6e59e858f2 100644 --- a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs @@ -3856,33 +3856,86 @@ fn get_dispute_stage( pub fn get_dispute_status( dispute_cycle: String, -) -> Result> { - match connector_utils::normalize_string(dispute_cycle.clone()) - .change_context(errors::ConnectorError::RequestEncodingFailed)? - .as_str() - { - "arbitration" - | "arbitrationmastercard" - | "arbitrationsplit" - | "representment" - | "issuerarbitration" - | "prearbitration" - | "responsetoissuerarbitration" - | "arbitrationchargeback" => Ok(api_models::enums::DisputeStatus::DisputeChallenged), - "chargebackreversal" | "issueracceptedprearbitration" | "arbitrationwon" => { - Ok(api_models::enums::DisputeStatus::DisputeWon) - } - "arbitrationlost" | "issuerdeclinedprearbitration" => { - Ok(api_models::enums::DisputeStatus::DisputeLost) - } - "firstchargeback" | "retrievalrequest" | "rapiddisputeresolution" => { - Ok(api_models::enums::DisputeStatus::DisputeOpened) + dispute_activities: Vec, +) -> Result> { + if let Some(activity) = get_last_non_auxiliary_activity_type(dispute_activities) { + match activity.as_ref() { + "Merchant Accept" + | "Issuer Accepted Pre-Arbitration" + | "Vantiv Accept" + | "Sent Credit" => Ok(common_enums::DisputeStatus::DisputeAccepted), + + "Merchant Represent" + | "Respond to Dispute" + | "Respond to PreArb" + | "Request Arbitration" + | "Request Pre-Arbitration" + | "Create Arbitration" + | "Record Arbitration" + | "Create Pre-Arbitration" + | "File Arbitration" + | "File Pre-Arbitration" + | "File Visa Pre-Arbitration" + | "Send Representment" + | "Send Response" + | "Arbitration" + | "Arbitration (Mastercard)" + | "Arbitration Chargeback" + | "Issuer Declined Pre-Arbitration" + | "Issuer Arbitration" + | "Request Response to Pre-Arbitration" + | "Vantiv Represent" + | "Vantiv Respond" + | "Auto Represent" + | "Arbitration Ruling" => Ok(common_enums::DisputeStatus::DisputeChallenged), + + "Arbitration Lost" | "Unsuccessful Arbitration" | "Unsuccessful Pre-Arbitration" => { + Ok(common_enums::DisputeStatus::DisputeLost) + } + + "Arbitration Won" + | "Arbitration Split" + | "Successful Arbitration" + | "Successful Pre-Arbitration" => Ok(common_enums::DisputeStatus::DisputeWon), + + "Chargeback Reversal" => Ok(common_enums::DisputeStatus::DisputeCancelled), + + "Receive Network Transaction" => Ok(common_enums::DisputeStatus::DisputeOpened), + + "Unaccept" | "Unrepresent" => Ok(common_enums::DisputeStatus::DisputeOpened), + + unexpected_activity => Err(errors::ConnectorError::UnexpectedResponseError( + bytes::Bytes::from(format!("Dispute Activity: {unexpected_activity})")), + ) + .into()), } - _ => Err(errors::ConnectorError::NotSupported { - message: format!("Dispute status {dispute_cycle}"), - connector: "worldpayvantiv", + } else { + match connector_utils::normalize_string(dispute_cycle.clone()) + .change_context(errors::ConnectorError::RequestEncodingFailed)? + .as_str() + { + "arbitration" + | "arbitrationmastercard" + | "arbitrationsplit" + | "representment" + | "issuerarbitration" + | "prearbitration" + | "responsetoissuerarbitration" + | "arbitrationchargeback" => Ok(api_models::enums::DisputeStatus::DisputeChallenged), + "chargebackreversal" | "issueracceptedprearbitration" | "arbitrationwon" => { + Ok(api_models::enums::DisputeStatus::DisputeWon) + } + "arbitrationlost" | "issuerdeclinedprearbitration" => { + Ok(api_models::enums::DisputeStatus::DisputeLost) + } + "firstchargeback" | "retrievalrequest" | "rapiddisputeresolution" => { + Ok(api_models::enums::DisputeStatus::DisputeOpened) + } + dispute_cycle => Err(errors::ConnectorError::UnexpectedResponseError( + bytes::Bytes::from(format!("Dispute Stage: {dispute_cycle}")), + ) + .into()), } - .into()), } } @@ -3914,7 +3967,7 @@ impl TryFrom for DisputeSyncResponse { amount, currency: item.chargeback_currency_type, dispute_stage: get_dispute_stage(item.cycle.clone())?, - dispute_status: get_dispute_status(item.cycle.clone())?, + dispute_status: get_dispute_status(item.cycle.clone(), item.activity)?, connector_status: item.cycle.clone(), connector_dispute_id: item.case_id.clone(), connector_reason: item.reason_code_description.clone(), @@ -4148,3 +4201,40 @@ impl }) } } + +fn get_last_non_auxiliary_activity_type(activities: Vec) -> Option { + let auxiliary_activities: std::collections::HashSet<&'static str> = [ + "Add Note", + "Attach Document", + "Attempted Attach Document", + "Delete Document", + "Update Document", + "Move To Error Queue", + "Assign to Vantiv", + "Assign To Merchant", + "Merchant Auto Assign", + "Issuer Recalled", + "Network Decision", + "Request Declined", + "Sent Gift", + "Successful PayPal", + ] + .iter() + .copied() + .collect(); + + let mut last_non_auxiliary_activity = None; + + for activity in activities { + let auxiliary_activity = activity + .activity_type + .as_deref() + .map(|activity_type| auxiliary_activities.contains(activity_type)) + .unwrap_or(false); + + if !auxiliary_activity { + last_non_auxiliary_activity = activity.activity_type.clone() + } + } + last_non_auxiliary_activity +} diff --git a/crates/router/src/core/disputes.rs b/crates/router/src/core/disputes.rs index 82e8548408..3581ca0a29 100644 --- a/crates/router/src/core/disputes.rs +++ b/crates/router/src/core/disputes.rs @@ -276,8 +276,10 @@ pub async fn accept_dispute( core_utils::validate_profile_id_from_auth_layer(profile_id, &dispute)?; let dispute_id = dispute.dispute_id.clone(); common_utils::fp_utils::when( - !(dispute.dispute_stage == storage_enums::DisputeStage::Dispute - && dispute.dispute_status == storage_enums::DisputeStatus::DisputeOpened), + !core_utils::should_proceed_with_accept_dispute( + dispute.dispute_stage, + dispute.dispute_status, + ), || { metrics::ACCEPT_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC.add(1, &[]); Err(errors::ApiErrorResponse::DisputeStatusValidationFailed { diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 987ae7f602..1cf4a2a50d 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -929,6 +929,9 @@ pub fn validate_dispute_status( DisputeStatus::DisputeChallenged | DisputeStatus::DisputeWon | DisputeStatus::DisputeLost + | DisputeStatus::DisputeAccepted + | DisputeStatus::DisputeCancelled + | DisputeStatus::DisputeExpired ), DisputeStatus::DisputeWon => matches!(dispute_status, DisputeStatus::DisputeWon), DisputeStatus::DisputeLost => matches!(dispute_status, DisputeStatus::DisputeLost), @@ -2614,12 +2617,27 @@ pub fn should_proceed_with_submit_evidence( dispute_stage: DisputeStage, dispute_status: DisputeStatus, ) -> bool { - matches!(dispute_stage, DisputeStage::DisputeReversal) - || matches!( - dispute_status, - DisputeStatus::DisputeExpired - | DisputeStatus::DisputeCancelled - | DisputeStatus::DisputeWon - | DisputeStatus::DisputeLost, - ) + matches!( + dispute_stage, + DisputeStage::PreDispute + | DisputeStage::Dispute + | DisputeStage::PreArbitration + | DisputeStage::Arbitration + ) && matches!( + dispute_status, + DisputeStatus::DisputeOpened | DisputeStatus::DisputeChallenged + ) +} + +pub fn should_proceed_with_accept_dispute( + dispute_stage: DisputeStage, + dispute_status: DisputeStatus, +) -> bool { + matches!( + dispute_stage, + DisputeStage::PreDispute | DisputeStage::Dispute | DisputeStage::PreArbitration + ) && matches!( + dispute_status, + DisputeStatus::DisputeChallenged | DisputeStatus::DisputeOpened + ) }