Skip to content

feat(router): [worldpayvantiv] add dispute list sync and implement dispute #8830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 43 commits into from
Aug 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
a578b60
Store connectors that support only dispute list API in config (.toml)
AkshayaFoiger Jul 14, 2025
a503ea6
add dispute_polling_interval to profile
AkshayaFoiger Jul 15, 2025
d6b0c50
add dispute flow
AkshayaFoiger Jul 22, 2025
ae522f3
add third_base_url for vantiv
AkshayaFoiger Jul 22, 2025
159567d
add an endpoint
AkshayaFoiger Jul 24, 2025
bd96bd0
change the endpoint path
AkshayaFoiger Jul 24, 2025
0467f03
fix clippy
AkshayaFoiger Jul 24, 2025
a3d9c32
add Dsync fow
AkshayaFoiger Jul 26, 2025
097c5f8
Create dispute workflow
AkshayaFoiger Jul 26, 2025
e3709b3
add dispute list, sync, accept and defend api's to vantiv
AkshayaFoiger Jul 27, 2025
55a8bff
upload file draft
AkshayaFoiger Jul 28, 2025
bdfe168
add submit evidence and file retrieve call
AkshayaFoiger Aug 3, 2025
df7a0a3
add sipute list sync
AkshayaFoiger Aug 4, 2025
abb8204
update the process tracker txn id
AkshayaFoiger Aug 4, 2025
f855810
resolve comments and fix clippy
AkshayaFoiger Aug 5, 2025
c709ef2
fix formatting
AkshayaFoiger Aug 5, 2025
cbadd02
Merge branch 'main' into dispute-list-sync
AkshayaFoiger Aug 5, 2025
7d13d0a
fix clippy error
AkshayaFoiger Aug 5, 2025
2238c95
add dispute_polling_interval to schema_v2
AkshayaFoiger Aug 5, 2025
35c0c58
remove loggers and files
AkshayaFoiger Aug 5, 2025
fa2fcb6
fix clippy error
AkshayaFoiger Aug 5, 2025
12e1ca1
fix migration error v2
AkshayaFoiger Aug 5, 2025
7611930
add the config
AkshayaFoiger Aug 5, 2025
d833f41
Merge branch 'main' into dispute-list-sync
AkshayaFoiger Aug 5, 2025
a67ec5c
fix clippy
AkshayaFoiger Aug 5, 2025
dc7be82
chore: run formatter
hyperswitch-bot[bot] Aug 5, 2025
6743f06
Merge branch 'main' into dispute-list-sync
AkshayaFoiger Aug 5, 2025
4cc1f41
add status in the submit evidence request
AkshayaFoiger Aug 5, 2025
65c794c
Merge branch 'main' into dispute-list-sync
AkshayaFoiger Aug 5, 2025
cb261b9
Update crates/router/src/workflows/process_dispute.rs
AkshayaFoiger Aug 5, 2025
cd6caf3
Update crates/router/src/core/utils.rs
AkshayaFoiger Aug 5, 2025
1d6bce8
resolve comment
AkshayaFoiger Aug 5, 2025
7b6f57f
Merge branch 'main' into dispute-list-sync
AkshayaFoiger Aug 5, 2025
761a804
chore: run formatter
hyperswitch-bot[bot] Aug 5, 2025
b7a8607
resolve comments
AkshayaFoiger Aug 6, 2025
383bcdf
Merge branch 'main' into dispute-list-sync
AkshayaFoiger Aug 6, 2025
dee74ac
chore: run formatter
hyperswitch-bot[bot] Aug 6, 2025
16a3a4a
fic clippy
AkshayaFoiger Aug 6, 2025
755df0d
chore: run formatter
hyperswitch-bot[bot] Aug 6, 2025
245b477
fix clippy
AkshayaFoiger Aug 6, 2025
e552218
Merge branch 'main' into dispute-list-sync
AkshayaFoiger Aug 6, 2025
efd3f01
fix merge conflict
AkshayaFoiger Aug 6, 2025
a399510
chore: run formatter
hyperswitch-bot[bot] Aug 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion api-reference/v1/openapi_spec_v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -4140,6 +4140,16 @@
"schema": {
"type": "string"
}
},
{
"name": "force_sync",
"in": "query",
"description": "Decider to enable or disable the connector call for dispute retrieve request",
"required": false,
"schema": {
"type": "boolean",
"nullable": true
}
}
],
"responses": {
Expand Down Expand Up @@ -14348,7 +14358,9 @@
"enum": [
"pre_dispute",
"dispute",
"pre_arbitration"
"pre_arbitration",
"arbitration",
"dispute_reversal"
]
},
"DisputeStatus": {
Expand Down Expand Up @@ -28116,6 +28128,13 @@
}
],
"nullable": true
},
"dispute_polling_interval": {
"type": "integer",
"format": "int32",
"description": "Time interval (in hours) for polling the connector to check dispute statuses",
"example": 2,
"nullable": true
}
},
"additionalProperties": false
Expand Down Expand Up @@ -28434,6 +28453,13 @@
}
],
"nullable": true
},
"dispute_polling_interval": {
"type": "integer",
"format": "int32",
"example": 2,
"nullable": true,
"minimum": 0
}
}
},
Expand Down
4 changes: 3 additions & 1 deletion api-reference/v2/openapi_spec_v2.json
Original file line number Diff line number Diff line change
Expand Up @@ -10404,7 +10404,9 @@
"enum": [
"pre_dispute",
"dispute",
"pre_arbitration"
"pre_arbitration",
"arbitration",
"dispute_reversal"
]
},
"DisputeStatus": {
Expand Down
5 changes: 4 additions & 1 deletion config/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
worldpay.base_url = "https://try.access.worldpay.com/"
worldpayvantiv.base_url = "https://transact.vantivprelive.com/vap/communicator/online"
worldpayvantiv.secondary_base_url = "https://onlinessr.vantivprelive.com"
worldpayvantiv.third_base_url = "https://services.vantivprelive.com"
worldpayxml.base_url = "https://secure-test.worldpay.com/jsp/merchant/xml/paymentService.jsp"
xendit.base_url = "https://api.xendit.co"
zsl.base_url = "https://api.sitoffalb.net/"
Expand Down Expand Up @@ -1177,4 +1178,6 @@ enabled = false # Enable or disable chat features
hyperswitch_ai_host = "http://0.0.0.0:8000" # Hyperswitch ai workflow host

[proxy_status_mapping]
proxy_connector_http_status_code = false # If enabled, the http status code of the connector will be proxied in the response
proxy_connector_http_status_code = false # If enabled, the http status code of the connector will be proxied in the response
[list_dispute_supported_connectors]
connector_list = "worldpayvantiv"
4 changes: 4 additions & 0 deletions config/deployments/integration_test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
worldpay.base_url = "https://try.access.worldpay.com/"
worldpayvantiv.base_url = "https://transact.vantivprelive.com/vap/communicator/online"
worldpayvantiv.secondary_base_url = "https://onlinessr.vantivprelive.com"
worldpayvantiv.third_base_url = "https://services.vantivprelive.com"
worldpayxml.base_url = "https://secure-test.worldpay.com/jsp/merchant/xml/paymentService.jsp"
xendit.base_url = "https://api.xendit.co"
zen.base_url = "https://api.zen-test.com/"
Expand Down Expand Up @@ -824,3 +825,6 @@ retry_algorithm_type = "cascading"

[authentication_providers]
click_to_pay = {connector_list = "adyen, cybersource, trustpay"}

[list_dispute_supported_connectors]
connector_list = "worldpayvantiv"
4 changes: 4 additions & 0 deletions config/deployments/production.toml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ worldline.base_url = "https://eu.api-ingenico.com/"
worldpay.base_url = "https://access.worldpay.com/"
worldpayvantiv.base_url = "https://transact.vantivcnp.com/vap/communicator/online"
worldpayvantiv.secondary_base_url = "https://onlinessr.vantivcnp.com"
worldpayvantiv.third_base_url = "https://services.vantivprelive.com" # pre-live environment
worldpayxml.base_url = "https://secure.worldpay.com/jsp/merchant/xml/paymentService.jsp"
xendit.base_url = "https://api.xendit.co"
zen.base_url = "https://api.zen.com/"
Expand Down Expand Up @@ -837,3 +838,6 @@ 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"
4 changes: 4 additions & 0 deletions config/deployments/sandbox.toml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
worldpay.base_url = "https://try.access.worldpay.com/"
worldpayvantiv.base_url = "https://transact.vantivprelive.com/vap/communicator/online"
worldpayvantiv.secondary_base_url = "https://onlinessr.vantivprelive.com"
worldpayvantiv.third_base_url = "https://services.vantivprelive.com"
worldpayxml.base_url = "https://secure-test.worldpay.com/jsp/merchant/xml/paymentService.jsp"
xendit.base_url = "https://api.xendit.co"
zen.base_url = "https://api.zen-test.com/"
Expand Down Expand Up @@ -842,3 +843,6 @@ 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"
5 changes: 4 additions & 1 deletion config/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
worldpay.base_url = "https://try.access.worldpay.com/"
worldpayvantiv.base_url = "https://transact.vantivprelive.com/vap/communicator/online"
worldpayvantiv.secondary_base_url = "https://onlinessr.vantivprelive.com"
worldpayvantiv.third_base_url = "https://services.vantivprelive.com"
worldpayxml.base_url = "https://secure-test.worldpay.com/jsp/merchant/xml/paymentService.jsp"
xendit.base_url = "https://api.xendit.co"
trustpay.base_url = "https://test-tpgw.trustpay.eu/"
Expand Down Expand Up @@ -1279,4 +1280,6 @@ enabled = false
hyperswitch_ai_host = "http://0.0.0.0:8000"

[proxy_status_mapping]
proxy_connector_http_status_code = false # If enabled, the http status code of the connector will be proxied in the response
proxy_connector_http_status_code = false # If enabled, the http status code of the connector will be proxied in the response
[list_dispute_supported_connectors]
connector_list = "worldpayvantiv"
5 changes: 4 additions & 1 deletion config/docker_compose.toml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
worldpay.base_url = "https://try.access.worldpay.com/"
worldpayvantiv.base_url = "https://transact.vantivprelive.com/vap/communicator/online"
worldpayvantiv.secondary_base_url = "https://onlinessr.vantivprelive.com"
worldpayvantiv.third_base_url = "https://services.vantivprelive.com"
worldpayxml.base_url = "https://secure-test.worldpay.com/jsp/merchant/xml/paymentService.jsp"
xendit.base_url = "https://api.xendit.co"
zen.base_url = "https://api.zen-test.com/"
Expand Down Expand Up @@ -1167,4 +1168,6 @@ cluster = "CLUSTER" # value of CLUSTER from deployment
version = "HOSTNAME" # value of HOSTNAME from deployment which tells its version

[proxy_status_mapping]
proxy_connector_http_status_code = false # If enabled, the http status code of the connector will be proxied in the response
proxy_connector_http_status_code = false # If enabled, the http status code of the connector will be proxied in the response
[list_dispute_supported_connectors]
connector_list = "worldpayvantiv"
10 changes: 10 additions & 0 deletions crates/api_models/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2183,6 +2183,10 @@ pub struct ProfileCreate {
/// It is used in payment processing, fraud detection, and regulatory compliance to determine regional rules and routing behavior.
#[schema(value_type = Option<MerchantCountryCode>, example = "840")]
pub merchant_country_code: Option<common_types::payments::MerchantCountryCode>,

/// Time interval (in hours) for polling the connector to check dispute statuses
#[schema(value_type = Option<i32>, example = 2)]
pub dispute_polling_interval: Option<primitive_wrappers::DisputePollingIntervalInHours>,
}

#[nutype::nutype(
Expand Down Expand Up @@ -2518,6 +2522,9 @@ pub struct ProfileResponse {
/// It is used in payment processing, fraud detection, and regulatory compliance to determine regional rules and routing behavior.
#[schema(value_type = Option<MerchantCountryCode>, example = "840")]
pub merchant_country_code: Option<common_types::payments::MerchantCountryCode>,

#[schema(value_type = Option<u32>, example = 2)]
pub dispute_polling_interval: Option<primitive_wrappers::DisputePollingIntervalInHours>,
}

#[cfg(feature = "v2")]
Expand Down Expand Up @@ -2846,6 +2853,9 @@ pub struct ProfileUpdate {
/// It is used in payment processing, fraud detection, and regulatory compliance to determine regional rules and routing behavior.
#[schema(value_type = Option<MerchantCountryCode>, example = "840")]
pub merchant_country_code: Option<common_types::payments::MerchantCountryCode>,

#[schema(value_type = Option<u32>, example = 2)]
pub dispute_polling_interval: Option<primitive_wrappers::DisputePollingIntervalInHours>,
}

#[cfg(feature = "v2")]
Expand Down
14 changes: 14 additions & 0 deletions crates/api_models/src/disputes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,26 @@ pub struct DeleteEvidenceRequest {
pub evidence_type: EvidenceType,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct DisputeRetrieveRequest {
/// The identifier for dispute
pub dispute_id: String,
/// Decider to enable or disable the connector call for dispute retrieve request
pub force_sync: Option<bool>,
}

#[derive(Clone, Debug, serde::Serialize)]
pub struct DisputesAggregateResponse {
/// Different status of disputes with their count
pub status_with_count: HashMap<DisputeStatus, i64>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct DisputeRetrieveBody {
/// Decider to enable or disable the connector call for dispute retrieve request
pub force_sync: Option<bool>,
}

fn parse_comma_separated<'de, D, T>(v: D) -> Result<Option<Vec<T>>, D::Error>
where
D: serde::Deserializer<'de>,
Expand Down
11 changes: 10 additions & 1 deletion crates/api_models/src/events/dispute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use common_utils::events::{ApiEventMetric, ApiEventsType};

use super::{
DeleteEvidenceRequest, DisputeResponse, DisputeResponsePaymentsRetrieve,
DisputesAggregateResponse, SubmitEvidenceRequest,
DisputeRetrieveRequest, DisputesAggregateResponse, SubmitEvidenceRequest,
};

impl ApiEventMetric for SubmitEvidenceRequest {
Expand All @@ -12,6 +12,15 @@ impl ApiEventMetric for SubmitEvidenceRequest {
})
}
}

impl ApiEventMetric for DisputeRetrieveRequest {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Dispute {
dispute_id: self.dispute_id.clone(),
})
}
}

impl ApiEventMetric for DisputeResponsePaymentsRetrieve {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Dispute {
Expand Down
6 changes: 6 additions & 0 deletions crates/api_models/src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ pub struct FileMetadataResponse {
/// File availability
pub available: bool,
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct FileRetrieveQuery {
///Dispute Id
pub dispute_id: Option<String>,
}
12 changes: 6 additions & 6 deletions crates/api_models/src/webhooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,32 +242,32 @@ impl From<IncomingWebhookEvent> for WebhookFlow {

pub type MerchantWebhookConfig = std::collections::HashSet<IncomingWebhookEvent>;

#[derive(Clone)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum RefundIdType {
RefundId(String),
ConnectorRefundId(String),
}

#[derive(Clone)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum MandateIdType {
MandateId(String),
ConnectorMandateId(String),
}

#[derive(Clone)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum AuthenticationIdType {
AuthenticationId(common_utils::id_type::AuthenticationId),
ConnectorAuthenticationId(String),
}

#[cfg(feature = "payouts")]
#[derive(Clone)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum PayoutIdType {
PayoutAttemptId(String),
ConnectorPayoutId(String),
}

#[derive(Clone)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum ObjectReferenceId {
PaymentId(payments::PaymentIdType),
RefundId(RefundIdType),
Expand All @@ -280,7 +280,7 @@ pub enum ObjectReferenceId {
}

#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
#[derive(Clone)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum InvoiceIdType {
ConnectorInvoiceId(String),
}
Expand Down
2 changes: 1 addition & 1 deletion crates/common_enums/src/connector_enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ impl Connector {
matches!((self, payment_method), (Self::Razorpay, PaymentMethod::Upi))
}
pub fn supports_file_storage_module(self) -> bool {
matches!(self, Self::Stripe | Self::Checkout)
matches!(self, Self::Stripe | Self::Checkout | Self::Worldpayvantiv)
}
pub fn requires_defend_dispute(self) -> bool {
matches!(self, Self::Checkout)
Expand Down
5 changes: 5 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2627,6 +2627,8 @@ pub enum DisputeStage {
#[default]
Dispute,
PreArbitration,
Arbitration,
DisputeReversal,
}

/// Status of the dispute
Expand Down Expand Up @@ -3125,6 +3127,7 @@ pub enum FileUploadProvider {
Router,
Stripe,
Checkout,
Worldpayvantiv,
}

#[derive(
Expand Down Expand Up @@ -8620,6 +8623,8 @@ pub enum ProcessTrackerRunner {
AttachPayoutAccountWorkflow,
PaymentMethodStatusUpdateWorkflow,
PassiveRecoveryWorkflow,
ProcessDisputeWorkflow,
DisputeListWorkflow,
}

#[derive(Debug)]
Expand Down
6 changes: 6 additions & 0 deletions crates/common_types/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ pub const API_VERSION: common_enums::ApiVersion = common_enums::ApiVersion::V1;
#[cfg(feature = "v2")]
pub const API_VERSION: common_enums::ApiVersion = common_enums::ApiVersion::V2;

/// Maximum Dispute Polling Interval In Hours
pub const MAX_DISPUTE_POLLING_INTERVAL_IN_HOURS: i32 = 24;

///Default Dispute Polling Interval In Hours
pub const DEFAULT_DISPUTE_POLLING_INTERVAL_IN_HOURS: i32 = 24;

/// Default payment intent statuses that trigger a webhook
pub const DEFAULT_PAYMENT_WEBHOOK_TRIGGER_STATUSES: &[common_enums::IntentStatus] = &[
common_enums::IntentStatus::Succeeded,
Expand Down
Loading
Loading