Skip to content

Commit 1b2a98c

Browse files
fix(router): [worldpayvantiv] dispute validations and statuses (#8862)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
1 parent 4a8eea9 commit 1b2a98c

File tree

4 files changed

+147
-40
lines changed

4 files changed

+147
-40
lines changed

config/deployments/production.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,4 @@ click_to_pay = {connector_list = "adyen, cybersource, trustpay"}
837837

838838
[revenue_recovery]
839839
monitoring_threshold_in_seconds = 60
840-
retry_algorithm_type = "cascading"
841-
842-
[list_dispute_supported_connectors]
843-
connector_list = "worldpayvantiv"
840+
retry_algorithm_type = "cascading"

crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs

Lines changed: 116 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3856,33 +3856,86 @@ fn get_dispute_stage(
38563856

38573857
pub fn get_dispute_status(
38583858
dispute_cycle: String,
3859-
) -> Result<api_models::enums::DisputeStatus, error_stack::Report<errors::ConnectorError>> {
3860-
match connector_utils::normalize_string(dispute_cycle.clone())
3861-
.change_context(errors::ConnectorError::RequestEncodingFailed)?
3862-
.as_str()
3863-
{
3864-
"arbitration"
3865-
| "arbitrationmastercard"
3866-
| "arbitrationsplit"
3867-
| "representment"
3868-
| "issuerarbitration"
3869-
| "prearbitration"
3870-
| "responsetoissuerarbitration"
3871-
| "arbitrationchargeback" => Ok(api_models::enums::DisputeStatus::DisputeChallenged),
3872-
"chargebackreversal" | "issueracceptedprearbitration" | "arbitrationwon" => {
3873-
Ok(api_models::enums::DisputeStatus::DisputeWon)
3874-
}
3875-
"arbitrationlost" | "issuerdeclinedprearbitration" => {
3876-
Ok(api_models::enums::DisputeStatus::DisputeLost)
3877-
}
3878-
"firstchargeback" | "retrievalrequest" | "rapiddisputeresolution" => {
3879-
Ok(api_models::enums::DisputeStatus::DisputeOpened)
3859+
dispute_activities: Vec<Activity>,
3860+
) -> Result<common_enums::DisputeStatus, error_stack::Report<errors::ConnectorError>> {
3861+
if let Some(activity) = get_last_non_auxiliary_activity_type(dispute_activities) {
3862+
match activity.as_ref() {
3863+
"Merchant Accept"
3864+
| "Issuer Accepted Pre-Arbitration"
3865+
| "Vantiv Accept"
3866+
| "Sent Credit" => Ok(common_enums::DisputeStatus::DisputeAccepted),
3867+
3868+
"Merchant Represent"
3869+
| "Respond to Dispute"
3870+
| "Respond to PreArb"
3871+
| "Request Arbitration"
3872+
| "Request Pre-Arbitration"
3873+
| "Create Arbitration"
3874+
| "Record Arbitration"
3875+
| "Create Pre-Arbitration"
3876+
| "File Arbitration"
3877+
| "File Pre-Arbitration"
3878+
| "File Visa Pre-Arbitration"
3879+
| "Send Representment"
3880+
| "Send Response"
3881+
| "Arbitration"
3882+
| "Arbitration (Mastercard)"
3883+
| "Arbitration Chargeback"
3884+
| "Issuer Declined Pre-Arbitration"
3885+
| "Issuer Arbitration"
3886+
| "Request Response to Pre-Arbitration"
3887+
| "Vantiv Represent"
3888+
| "Vantiv Respond"
3889+
| "Auto Represent"
3890+
| "Arbitration Ruling" => Ok(common_enums::DisputeStatus::DisputeChallenged),
3891+
3892+
"Arbitration Lost" | "Unsuccessful Arbitration" | "Unsuccessful Pre-Arbitration" => {
3893+
Ok(common_enums::DisputeStatus::DisputeLost)
3894+
}
3895+
3896+
"Arbitration Won"
3897+
| "Arbitration Split"
3898+
| "Successful Arbitration"
3899+
| "Successful Pre-Arbitration" => Ok(common_enums::DisputeStatus::DisputeWon),
3900+
3901+
"Chargeback Reversal" => Ok(common_enums::DisputeStatus::DisputeCancelled),
3902+
3903+
"Receive Network Transaction" => Ok(common_enums::DisputeStatus::DisputeOpened),
3904+
3905+
"Unaccept" | "Unrepresent" => Ok(common_enums::DisputeStatus::DisputeOpened),
3906+
3907+
unexpected_activity => Err(errors::ConnectorError::UnexpectedResponseError(
3908+
bytes::Bytes::from(format!("Dispute Activity: {unexpected_activity})")),
3909+
)
3910+
.into()),
38803911
}
3881-
_ => Err(errors::ConnectorError::NotSupported {
3882-
message: format!("Dispute status {dispute_cycle}"),
3883-
connector: "worldpayvantiv",
3912+
} else {
3913+
match connector_utils::normalize_string(dispute_cycle.clone())
3914+
.change_context(errors::ConnectorError::RequestEncodingFailed)?
3915+
.as_str()
3916+
{
3917+
"arbitration"
3918+
| "arbitrationmastercard"
3919+
| "arbitrationsplit"
3920+
| "representment"
3921+
| "issuerarbitration"
3922+
| "prearbitration"
3923+
| "responsetoissuerarbitration"
3924+
| "arbitrationchargeback" => Ok(api_models::enums::DisputeStatus::DisputeChallenged),
3925+
"chargebackreversal" | "issueracceptedprearbitration" | "arbitrationwon" => {
3926+
Ok(api_models::enums::DisputeStatus::DisputeWon)
3927+
}
3928+
"arbitrationlost" | "issuerdeclinedprearbitration" => {
3929+
Ok(api_models::enums::DisputeStatus::DisputeLost)
3930+
}
3931+
"firstchargeback" | "retrievalrequest" | "rapiddisputeresolution" => {
3932+
Ok(api_models::enums::DisputeStatus::DisputeOpened)
3933+
}
3934+
dispute_cycle => Err(errors::ConnectorError::UnexpectedResponseError(
3935+
bytes::Bytes::from(format!("Dispute Stage: {dispute_cycle}")),
3936+
)
3937+
.into()),
38843938
}
3885-
.into()),
38863939
}
38873940
}
38883941

@@ -3914,7 +3967,7 @@ impl TryFrom<ChargebackCase> for DisputeSyncResponse {
39143967
amount,
39153968
currency: item.chargeback_currency_type,
39163969
dispute_stage: get_dispute_stage(item.cycle.clone())?,
3917-
dispute_status: get_dispute_status(item.cycle.clone())?,
3970+
dispute_status: get_dispute_status(item.cycle.clone(), item.activity)?,
39183971
connector_status: item.cycle.clone(),
39193972
connector_dispute_id: item.case_id.clone(),
39203973
connector_reason: item.reason_code_description.clone(),
@@ -4148,3 +4201,40 @@ impl
41484201
})
41494202
}
41504203
}
4204+
4205+
fn get_last_non_auxiliary_activity_type(activities: Vec<Activity>) -> Option<String> {
4206+
let auxiliary_activities: std::collections::HashSet<&'static str> = [
4207+
"Add Note",
4208+
"Attach Document",
4209+
"Attempted Attach Document",
4210+
"Delete Document",
4211+
"Update Document",
4212+
"Move To Error Queue",
4213+
"Assign to Vantiv",
4214+
"Assign To Merchant",
4215+
"Merchant Auto Assign",
4216+
"Issuer Recalled",
4217+
"Network Decision",
4218+
"Request Declined",
4219+
"Sent Gift",
4220+
"Successful PayPal",
4221+
]
4222+
.iter()
4223+
.copied()
4224+
.collect();
4225+
4226+
let mut last_non_auxiliary_activity = None;
4227+
4228+
for activity in activities {
4229+
let auxiliary_activity = activity
4230+
.activity_type
4231+
.as_deref()
4232+
.map(|activity_type| auxiliary_activities.contains(activity_type))
4233+
.unwrap_or(false);
4234+
4235+
if !auxiliary_activity {
4236+
last_non_auxiliary_activity = activity.activity_type.clone()
4237+
}
4238+
}
4239+
last_non_auxiliary_activity
4240+
}

crates/router/src/core/disputes.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,10 @@ pub async fn accept_dispute(
276276
core_utils::validate_profile_id_from_auth_layer(profile_id, &dispute)?;
277277
let dispute_id = dispute.dispute_id.clone();
278278
common_utils::fp_utils::when(
279-
!(dispute.dispute_stage == storage_enums::DisputeStage::Dispute
280-
&& dispute.dispute_status == storage_enums::DisputeStatus::DisputeOpened),
279+
!core_utils::should_proceed_with_accept_dispute(
280+
dispute.dispute_stage,
281+
dispute.dispute_status,
282+
),
281283
|| {
282284
metrics::ACCEPT_DISPUTE_STATUS_VALIDATION_FAILURE_METRIC.add(1, &[]);
283285
Err(errors::ApiErrorResponse::DisputeStatusValidationFailed {

crates/router/src/core/utils.rs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,9 @@ pub fn validate_dispute_status(
929929
DisputeStatus::DisputeChallenged
930930
| DisputeStatus::DisputeWon
931931
| DisputeStatus::DisputeLost
932+
| DisputeStatus::DisputeAccepted
933+
| DisputeStatus::DisputeCancelled
934+
| DisputeStatus::DisputeExpired
932935
),
933936
DisputeStatus::DisputeWon => matches!(dispute_status, DisputeStatus::DisputeWon),
934937
DisputeStatus::DisputeLost => matches!(dispute_status, DisputeStatus::DisputeLost),
@@ -2614,12 +2617,27 @@ pub fn should_proceed_with_submit_evidence(
26142617
dispute_stage: DisputeStage,
26152618
dispute_status: DisputeStatus,
26162619
) -> bool {
2617-
matches!(dispute_stage, DisputeStage::DisputeReversal)
2618-
|| matches!(
2619-
dispute_status,
2620-
DisputeStatus::DisputeExpired
2621-
| DisputeStatus::DisputeCancelled
2622-
| DisputeStatus::DisputeWon
2623-
| DisputeStatus::DisputeLost,
2624-
)
2620+
matches!(
2621+
dispute_stage,
2622+
DisputeStage::PreDispute
2623+
| DisputeStage::Dispute
2624+
| DisputeStage::PreArbitration
2625+
| DisputeStage::Arbitration
2626+
) && matches!(
2627+
dispute_status,
2628+
DisputeStatus::DisputeOpened | DisputeStatus::DisputeChallenged
2629+
)
2630+
}
2631+
2632+
pub fn should_proceed_with_accept_dispute(
2633+
dispute_stage: DisputeStage,
2634+
dispute_status: DisputeStatus,
2635+
) -> bool {
2636+
matches!(
2637+
dispute_stage,
2638+
DisputeStage::PreDispute | DisputeStage::Dispute | DisputeStage::PreArbitration
2639+
) && matches!(
2640+
dispute_status,
2641+
DisputeStatus::DisputeChallenged | DisputeStatus::DisputeOpened
2642+
)
26252643
}

0 commit comments

Comments
 (0)