From 116b6e73d75929855b170a14d9ebe66978fabb37 Mon Sep 17 00:00:00 2001 From: AkshayaFoiger Date: Wed, 6 Aug 2025 21:54:24 +0530 Subject: [PATCH 1/5] add support to accept liability during pre-arbitration --- crates/router/src/core/disputes.rs | 6 ++++-- crates/router/src/core/utils.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) 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 de5f3d4997..73f2f5f14c 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -2612,3 +2612,17 @@ pub fn should_proceed_with_submit_evidence( | DisputeStatus::DisputeLost, ) } + + +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 + ) +} + From 432fb5b4e79573a0bfc91090681c06524ef0ce38 Mon Sep 17 00:00:00 2001 From: AkshayaFoiger Date: Wed, 6 Aug 2025 23:20:17 +0530 Subject: [PATCH 2/5] handle vantiv status --- .../connectors/worldpayvantiv/transformers.rs | 101 ++++++++++++++++-- crates/router/src/core/utils.rs | 33 +++--- 2 files changed, 112 insertions(+), 22 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs index 09c9522091..a09b597f89 100644 --- a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs @@ -3647,7 +3647,58 @@ fn get_dispute_stage( pub fn get_dispute_status( dispute_cycle: String, -) -> Result> { + 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()), + } + } else { match connector_utils::normalize_string(dispute_cycle.clone()) .change_context(errors::ConnectorError::RequestEncodingFailed)? .as_str() @@ -3669,13 +3720,13 @@ pub fn get_dispute_status( "firstchargeback" | "retrievalrequest" | "rapiddisputeresolution" => { Ok(api_models::enums::DisputeStatus::DisputeOpened) } - _ => Err(errors::ConnectorError::NotSupported { - message: format!("Dispute status {dispute_cycle}"), - connector: "worldpayvantiv", - } + dispute_cycle => Err(errors::ConnectorError::UnexpectedResponseError( + bytes::Bytes::from(format!("Dispute Stage: {dispute_cycle}")), + ) .into()), } } +} fn convert_string_to_primitive_date( item: Option, @@ -3705,7 +3756,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(), @@ -3939,3 +3990,41 @@ 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", + "Receive Network Transaction", + "Issuer Recalled", + "Network Decision", + "Request Declined", + "Sent Gift", + "Successful PayPal", + ] + .iter() + .cloned() + .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 +} \ No newline at end of file diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 73f2f5f14c..132118b48b 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -2603,26 +2603,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 - ) + matches!( + dispute_stage, + DisputeStage::PreDispute | DisputeStage::Dispute | DisputeStage::PreArbitration + ) && matches!( + dispute_status, + DisputeStatus::DisputeChallenged | DisputeStatus::DisputeOpened + ) } - From 3080bc8b2045ac334f09000aaa800f3cb02e18eb Mon Sep 17 00:00:00 2001 From: AkshayaFoiger Date: Thu, 7 Aug 2025 00:02:45 +0530 Subject: [PATCH 3/5] fix disputes --- config/deployments/production.toml | 5 +---- .../src/connectors/worldpayvantiv/transformers.rs | 1 - crates/router/src/core/utils.rs | 3 +++ 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 1bdd2c37f9..b4f62f5adb 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 = 2592000 -retry_algorithm_type = "cascading" - -[list_dispute_supported_connectors] -connector_list = "worldpayvantiv" \ No newline at end of file +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 a09b597f89..1060111b53 100644 --- a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs @@ -4002,7 +4002,6 @@ fn get_last_non_auxiliary_activity_type(activities: Vec) -> Option matches!(dispute_status, DisputeStatus::DisputeWon), DisputeStatus::DisputeLost => matches!(dispute_status, DisputeStatus::DisputeLost), From e5b11cbd6d20951a31d91a530009ced59d63524d Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:48:36 +0000 Subject: [PATCH 4/5] chore: run formatter --- .../connectors/worldpayvantiv/transformers.rs | 86 ++++++++++--------- crates/router/src/core/utils.rs | 2 +- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs index 9c7ea382ff..6a7ddf1a8a 100644 --- a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs @@ -3858,13 +3858,13 @@ pub fn get_dispute_status( dispute_cycle: String, dispute_activities: Vec, ) -> Result> { - if let Some(activity) = get_last_non_auxiliary_activity_type(dispute_activities) - { + 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 Accept" + | "Issuer Accepted Pre-Arbitration" + | "Vantiv Accept" + | "Sent Credit" => Ok(common_enums::DisputeStatus::DisputeAccepted), + "Merchant Represent" | "Respond to Dispute" | "Respond to PreArb" @@ -3886,56 +3886,58 @@ pub fn get_dispute_status( | "Request Response to Pre-Arbitration" | "Vantiv Represent" | "Vantiv Respond" - | "Auto Represent" + | "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" + + "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()), - } - } 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) + } 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()), } - dispute_cycle => Err(errors::ConnectorError::UnexpectedResponseError( - bytes::Bytes::from(format!("Dispute Stage: {dispute_cycle}")), - ) - .into()), } } -} fn convert_string_to_primitive_date( item: Option, @@ -4202,7 +4204,7 @@ impl fn get_last_non_auxiliary_activity_type(activities: Vec) -> Option { let auxiliary_activities: std::collections::HashSet<&'static str> = [ - "Add Note", + "Add Note", "Attach Document", "Attempted Attach Document", "Delete Document", @@ -4235,4 +4237,4 @@ fn get_last_non_auxiliary_activity_type(activities: Vec) -> Option matches!(dispute_status, DisputeStatus::DisputeWon), DisputeStatus::DisputeLost => matches!(dispute_status, DisputeStatus::DisputeLost), From e919bc7ba5dc4efe2489411b555415e0a26252ad Mon Sep 17 00:00:00 2001 From: AkshayaFoiger Date: Thu, 7 Aug 2025 01:10:15 +0530 Subject: [PATCH 5/5] fix clippy --- .../src/connectors/worldpayvantiv/transformers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs index 6a7ddf1a8a..6e59e858f2 100644 --- a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs @@ -4220,7 +4220,7 @@ fn get_last_non_auxiliary_activity_type(activities: Vec) -> Option