diff --git a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs index 6b880a5eda..06fdedf640 100644 --- a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs @@ -49,6 +49,7 @@ pub mod worldpayvantiv_constants { pub const MAX_ID_LENGTH: usize = 26; pub const XML_STANDALONE: &str = "yes"; pub const XML_CHARGEBACK: &str = "http://www.vantivcnp.com/chargebacks"; + pub const MAC_FIELD_NUMBER: &str = "39"; } pub struct WorldpayvantivRouterData { @@ -1110,6 +1111,44 @@ pub struct PaymentResponse { pub token_response: Option, pub network_transaction_id: Option>, pub approved_amount: Option, + pub enhanced_auth_response: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct EnhancedAuthResponse { + pub network_response: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct NetworkResponse { + pub endpoint: Option, + #[serde(default)] + #[serde(rename = "networkField")] + pub network_fields: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct NetworkField { + #[serde(rename = "@fieldNumber")] + pub field_number: String, + #[serde(rename = "@fieldName", skip_serializing_if = "Option::is_none")] + pub field_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub field_value: Option, + #[serde(default)] + #[serde(rename = "networkSubField")] + pub network_sub_fields: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct NetworkSubField { + #[serde(rename = "@fieldNumber")] + pub field_number: String, + pub field_value: String, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -1187,6 +1226,24 @@ where capture_response.response, )?; if connector_utils::is_payment_failure(status) { + let network_decline_code = item + .response + .sale_response + .as_ref() + .and_then(|pr| pr.enhanced_auth_response.as_ref()) + .and_then(|ea| ea.network_response.as_ref()) + .and_then(|nr| { + nr.network_fields + .iter() + .find(|f| { + f.field_number == *worldpayvantiv_constants::MAC_FIELD_NUMBER + }) + .and_then(|f| f.field_value.clone()) + }); + + let network_error_message = network_decline_code + .as_ref() + .map(|_| capture_response.message.clone()); Ok(Self { status, response: Err(ErrorResponse { @@ -1197,8 +1254,8 @@ where attempt_status: None, connector_transaction_id: Some(capture_response.cnp_txn_id), network_advice_code: None, - network_decline_code: None, - network_error_message: None, + network_decline_code, + network_error_message, }), ..item.data }) @@ -1221,21 +1278,39 @@ where }) } } - None => Ok(Self { - status: common_enums::AttemptStatus::CaptureFailed, - response: Err(ErrorResponse { - code: item.response.response_code, - message: item.response.message.clone(), - reason: Some(item.response.message.clone()), - status_code: item.http_code, - attempt_status: None, - connector_transaction_id: None, - network_advice_code: None, - network_decline_code: None, - network_error_message: None, - }), - ..item.data - }), + None => { + let network_decline_code = item + .response + .sale_response + .as_ref() + .and_then(|pr| pr.enhanced_auth_response.as_ref()) + .and_then(|ea| ea.network_response.as_ref()) + .and_then(|nr| { + nr.network_fields + .iter() + .find(|f| f.field_number == *worldpayvantiv_constants::MAC_FIELD_NUMBER) + .and_then(|f| f.field_value.clone()) + }); + + let network_error_message = network_decline_code + .as_ref() + .map(|_| item.response.message.clone()); + Ok(Self { + status: common_enums::AttemptStatus::CaptureFailed, + response: Err(ErrorResponse { + code: item.response.response_code, + message: item.response.message.clone(), + reason: Some(item.response.message.clone()), + status_code: item.http_code, + attempt_status: None, + connector_transaction_id: None, + network_advice_code: None, + network_decline_code, + network_error_message, + }), + ..item.data + }) + } } } } @@ -1254,6 +1329,24 @@ impl TryFrom TryFrom TryFrom Ok(Self { - // Incase of API failure - status: common_enums::AttemptStatus::VoidFailed, - response: Err(ErrorResponse { - code: item.response.response_code, - message: item.response.message.clone(), - reason: Some(item.response.message.clone()), - status_code: item.http_code, - attempt_status: None, - connector_transaction_id: None, - network_advice_code: None, - network_decline_code: None, - network_error_message: None, - }), - ..item.data - }), + None => { + let network_decline_code = item + .response + .sale_response + .as_ref() + .and_then(|pr| pr.enhanced_auth_response.as_ref()) + .and_then(|ea| ea.network_response.as_ref()) + .and_then(|nr| { + nr.network_fields + .iter() + .find(|f| f.field_number == *worldpayvantiv_constants::MAC_FIELD_NUMBER) + .and_then(|f| f.field_value.clone()) + }); + + let network_error_message = network_decline_code + .as_ref() + .map(|_| item.response.message.clone()); + Ok(Self { + // Incase of API failure + status: common_enums::AttemptStatus::VoidFailed, + response: Err(ErrorResponse { + code: item.response.response_code, + message: item.response.message.clone(), + reason: Some(item.response.message.clone()), + status_code: item.http_code, + attempt_status: None, + connector_transaction_id: None, + network_advice_code: None, + network_decline_code, + network_error_message, + }), + ..item.data + }) + } } } } @@ -1395,6 +1506,24 @@ impl TryFrom> for RefundsR Some(credit_response) => { let refund_status = get_refund_status(credit_response.response)?; if connector_utils::is_refund_failure(refund_status) { + let network_decline_code = item + .response + .sale_response + .as_ref() + .and_then(|pr| pr.enhanced_auth_response.as_ref()) + .and_then(|ea| ea.network_response.as_ref()) + .and_then(|nr| { + nr.network_fields + .iter() + .find(|f| { + f.field_number == *worldpayvantiv_constants::MAC_FIELD_NUMBER + }) + .and_then(|f| f.field_value.clone()) + }); + + let network_error_message = network_decline_code + .as_ref() + .map(|_| credit_response.message.clone()); Ok(Self { response: Err(ErrorResponse { code: credit_response.response.to_string(), @@ -1404,8 +1533,8 @@ impl TryFrom> for RefundsR attempt_status: None, connector_transaction_id: None, network_advice_code: None, - network_decline_code: None, - network_error_message: None, + network_decline_code, + network_error_message, }), ..item.data }) @@ -1419,20 +1548,38 @@ impl TryFrom> for RefundsR }) } } - None => Ok(Self { - response: Err(ErrorResponse { - code: item.response.response_code, - message: item.response.message.clone(), - reason: Some(item.response.message.clone()), - status_code: item.http_code, - attempt_status: None, - connector_transaction_id: None, - network_advice_code: None, - network_decline_code: None, - network_error_message: None, - }), - ..item.data - }), + None => { + let network_decline_code = item + .response + .sale_response + .as_ref() + .and_then(|pr| pr.enhanced_auth_response.as_ref()) + .and_then(|ea| ea.network_response.as_ref()) + .and_then(|nr| { + nr.network_fields + .iter() + .find(|f| f.field_number == *worldpayvantiv_constants::MAC_FIELD_NUMBER) + .and_then(|f| f.field_value.clone()) + }); + + let network_error_message = network_decline_code + .as_ref() + .map(|_| item.response.message.clone()); + Ok(Self { + response: Err(ErrorResponse { + code: item.response.response_code, + message: item.response.message.clone(), + reason: Some(item.response.message.clone()), + status_code: item.http_code, + attempt_status: None, + connector_transaction_id: None, + network_advice_code: None, + network_decline_code, + network_error_message, + }), + ..item.data + }) + } } } } @@ -1527,10 +1674,26 @@ impl fn try_from( item: ResponseRouterData, ) -> Result { - match (item.response.sale_response, item.response.authorization_response) { + match (item.response.sale_response.as_ref(), item.response.authorization_response.as_ref()) { (Some(sale_response), None) => { let status = get_attempt_status(WorldpayvantivPaymentFlow::Sale, sale_response.response)?; if connector_utils::is_payment_failure(status) { + let network_decline_code = item + .response + .sale_response + .as_ref() + .and_then(|pr| pr.enhanced_auth_response.as_ref()) + .and_then(|ea| ea.network_response.as_ref()) + .and_then(|nr| { + nr.network_fields + .iter() + .find(|f| f.field_number == *worldpayvantiv_constants::MAC_FIELD_NUMBER) + .and_then(|f| f.field_value.clone()) + }); + + let network_error_message = network_decline_code + .as_ref() + .map(|_| sale_response.message.clone()); Ok(Self { status, response: Err(ErrorResponse { @@ -1539,10 +1702,10 @@ impl reason: Some(sale_response.message.clone()), status_code: item.http_code, attempt_status: None, - connector_transaction_id: Some(sale_response.order_id), + connector_transaction_id: Some(sale_response.order_id.clone()), network_advice_code: None, - network_decline_code: None, - network_error_message: None, + network_decline_code, + network_error_message, }), ..item.data }) @@ -1552,18 +1715,18 @@ impl }; let connector_metadata = Some(report_group.encode_to_value() .change_context(errors::ConnectorError::ResponseHandlingFailed)?); - let mandate_reference_data = sale_response.token_response.map(MandateReference::from); + let mandate_reference_data = sale_response.token_response.clone().map(MandateReference::from); let connector_response = sale_response.fraud_result.as_ref().map(get_connector_response); Ok(Self { status, response: Ok(PaymentsResponseData::TransactionResponse { - resource_id: ResponseId::ConnectorTransactionId(sale_response.cnp_txn_id), + resource_id: ResponseId::ConnectorTransactionId(sale_response.cnp_txn_id.clone()), redirection_data: Box::new(None), mandate_reference: Box::new(mandate_reference_data), connector_metadata, network_txn_id: None, - connector_response_reference_id: Some(sale_response.order_id), + connector_response_reference_id: Some(sale_response.order_id.clone()), incremental_authorization_allowed: None, charges: None, }), @@ -1582,6 +1745,22 @@ impl let status = get_attempt_status(payment_flow_type, auth_response.response)?; if connector_utils::is_payment_failure(status) { + let network_decline_code = item + .response + .authorization_response + .as_ref() + .and_then(|pr| pr.enhanced_auth_response.as_ref()) + .and_then(|ea| ea.network_response.as_ref()) + .and_then(|nr| { + nr.network_fields + .iter() + .find(|f| f.field_number == *worldpayvantiv_constants::MAC_FIELD_NUMBER) + .and_then(|f| f.field_value.clone()) + }); + + let network_error_message = network_decline_code + .as_ref() + .map(|_| auth_response.message.clone()); Ok(Self { status, response: Err(ErrorResponse { @@ -1590,10 +1769,10 @@ impl reason: Some(auth_response.message.clone()), status_code: item.http_code, attempt_status: None, - connector_transaction_id: Some(auth_response.order_id), + connector_transaction_id: Some(auth_response.order_id.clone()), network_advice_code: None, - network_decline_code: None, - network_error_message: None, + network_decline_code, + network_error_message, }), ..item.data }) @@ -1603,18 +1782,18 @@ impl }; let connector_metadata = Some(report_group.encode_to_value() .change_context(errors::ConnectorError::ResponseHandlingFailed)?); - let mandate_reference_data = auth_response.token_response.map(MandateReference::from); + let mandate_reference_data = auth_response.token_response.clone().map(MandateReference::from); let connector_response = auth_response.fraud_result.as_ref().map(get_connector_response); Ok(Self { status, response: Ok(PaymentsResponseData::TransactionResponse { - resource_id: ResponseId::ConnectorTransactionId(auth_response.cnp_txn_id), + resource_id: ResponseId::ConnectorTransactionId(auth_response.cnp_txn_id.clone()), redirection_data: Box::new(None), mandate_reference: Box::new(mandate_reference_data), connector_metadata, network_txn_id: None, - connector_response_reference_id: Some(auth_response.order_id), + connector_response_reference_id: Some(auth_response.order_id.clone()), incremental_authorization_allowed: None, charges: None, }), @@ -1633,7 +1812,8 @@ impl }) } }, - (None, None) => { Ok(Self { + (None, None) => { + Ok(Self { status: common_enums::AttemptStatus::Failure, response: Err(ErrorResponse { code: item.response.response_code.clone(),