Skip to content

Commit 670e277

Browse files
Sayak BhattacharyaSayak Bhattacharya
authored andcommitted
feat(connector): [FISERV] Populated Network Advice Fields in ErrorResponse & Added Integrity Check Suppport for Payment & Refund Flows
1 parent 071b073 commit 670e277

File tree

2 files changed

+246
-49
lines changed

2 files changed

+246
-49
lines changed

crates/hyperswitch_connectors/src/connectors/fiserv.rs

Lines changed: 166 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use uuid::Uuid;
5151
use crate::{
5252
constants::headers,
5353
types::ResponseRouterData,
54+
utils as connector_utils,
5455
utils::{construct_not_implemented_error_report, convert_amount},
5556
};
5657

@@ -168,37 +169,64 @@ impl ConnectorCommon for Fiserv {
168169
event_builder.map(|i| i.set_error_response_body(&response));
169170
router_env::logger::info!(connector_response=?response);
170171

171-
let fiserv::ErrorResponse { error, details } = response;
172-
173-
Ok(error
174-
.or(details)
175-
.and_then(|error_details| {
176-
error_details.first().map(|first_error| ErrorResponse {
177-
code: first_error
178-
.code
179-
.to_owned()
180-
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
181-
message: first_error.message.to_owned(),
182-
reason: first_error.field.to_owned(),
183-
status_code: res.status_code,
184-
attempt_status: None,
185-
connector_transaction_id: None,
186-
network_advice_code: None,
187-
network_decline_code: None,
188-
network_error_message: None,
189-
})
190-
})
191-
.unwrap_or(ErrorResponse {
192-
code: consts::NO_ERROR_CODE.to_string(),
193-
message: consts::NO_ERROR_MESSAGE.to_string(),
194-
reason: None,
195-
status_code: res.status_code,
196-
attempt_status: None,
197-
connector_transaction_id: None,
198-
network_advice_code: None,
199-
network_decline_code: None,
200-
network_error_message: None,
201-
}))
172+
let error_details_opt = response.error.as_ref().and_then(|v| v.first());
173+
174+
let (
175+
code,
176+
message,
177+
reason,
178+
network_advice_code,
179+
network_decline_code,
180+
network_error_message,
181+
) = if let Some(first_error) = error_details_opt {
182+
let code = first_error
183+
.code
184+
.clone()
185+
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string());
186+
187+
let message = first_error
188+
.message
189+
.clone()
190+
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string());
191+
192+
let reason = first_error.additional_info.clone();
193+
194+
let network_advice_code = first_error.message.clone();
195+
196+
let network_decline_code = first_error.code.clone();
197+
198+
let network_error_message = first_error.additional_info.clone();
199+
200+
(
201+
code,
202+
message,
203+
reason,
204+
network_advice_code,
205+
network_decline_code,
206+
network_error_message,
207+
)
208+
} else {
209+
(
210+
consts::NO_ERROR_CODE.to_string(),
211+
consts::NO_ERROR_MESSAGE.to_string(),
212+
None,
213+
None,
214+
None,
215+
None,
216+
)
217+
};
218+
219+
Ok(ErrorResponse {
220+
code,
221+
message,
222+
reason,
223+
status_code: res.status_code,
224+
attempt_status: None,
225+
connector_transaction_id: None,
226+
network_advice_code,
227+
network_decline_code,
228+
network_error_message,
229+
})
202230
}
203231
}
204232

@@ -404,14 +432,38 @@ impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Fis
404432
.response
405433
.parse_struct("Fiserv PaymentSyncResponse")
406434
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
435+
436+
let p_sync_response = response.sync_responses.first().ok_or(
437+
errors::ConnectorError::MissingRequiredField {
438+
field_name: "P_Sync_Responses[0]",
439+
},
440+
)?;
441+
442+
let response_integrity_object = connector_utils::get_sync_integrity_object(
443+
self.amount_converter,
444+
p_sync_response.payment_receipt.approved_amount.total,
445+
p_sync_response
446+
.payment_receipt
447+
.approved_amount
448+
.currency
449+
.to_string()
450+
.clone(),
451+
)?;
452+
407453
event_builder.map(|i| i.set_response_body(&response));
408454
router_env::logger::info!(connector_response=?response);
409-
RouterData::try_from(ResponseRouterData {
455+
456+
let new_router_data = RouterData::try_from(ResponseRouterData {
410457
response,
411458
data: data.clone(),
412459
http_code: res.status_code,
413460
})
414-
.change_context(errors::ConnectorError::ResponseHandlingFailed)
461+
.change_context(errors::ConnectorError::ResponseHandlingFailed);
462+
463+
new_router_data.map(|mut router_data| {
464+
router_data.request.integrity_object = Some(response_integrity_object);
465+
router_data
466+
})
415467
}
416468

417469
fn get_error_response(
@@ -483,16 +535,30 @@ impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> fo
483535
.response
484536
.parse_struct("Fiserv Payment Response")
485537
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
486-
538+
let response_integrity_object = connector_utils::get_capture_integrity_object(
539+
self.amount_converter,
540+
Some(response.payment_receipt.approved_amount.total),
541+
response
542+
.payment_receipt
543+
.approved_amount
544+
.currency
545+
.to_string()
546+
.clone(),
547+
)?;
487548
event_builder.map(|i| i.set_response_body(&response));
488549
router_env::logger::info!(connector_response=?response);
489550

490-
RouterData::try_from(ResponseRouterData {
551+
let new_router_data = RouterData::try_from(ResponseRouterData {
491552
response,
492553
data: data.clone(),
493554
http_code: res.status_code,
494555
})
495-
.change_context(errors::ConnectorError::ResponseHandlingFailed)
556+
.change_context(errors::ConnectorError::ResponseHandlingFailed);
557+
558+
new_router_data.map(|mut router_data| {
559+
router_data.request.integrity_object = Some(response_integrity_object);
560+
router_data
561+
})
496562
}
497563

498564
fn get_url(
@@ -595,14 +661,32 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
595661
.response
596662
.parse_struct("Fiserv PaymentResponse")
597663
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
664+
665+
let response_integrity_object = connector_utils::get_authorise_integrity_object(
666+
self.amount_converter,
667+
response.payment_receipt.approved_amount.total,
668+
response
669+
.payment_receipt
670+
.approved_amount
671+
.currency
672+
.to_string()
673+
.clone(),
674+
)?;
675+
598676
event_builder.map(|i| i.set_response_body(&response));
599677
router_env::logger::info!(connector_response=?response);
600-
RouterData::try_from(ResponseRouterData {
678+
679+
let new_router_data = RouterData::try_from(ResponseRouterData {
601680
response,
602681
data: data.clone(),
603682
http_code: res.status_code,
604683
})
605-
.change_context(errors::ConnectorError::ResponseHandlingFailed)
684+
.change_context(errors::ConnectorError::ResponseHandlingFailed);
685+
686+
new_router_data.map(|mut router_data| {
687+
router_data.request.integrity_object = Some(response_integrity_object);
688+
router_data
689+
})
606690
}
607691

608692
fn get_error_response(
@@ -684,14 +768,31 @@ impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Fiserv
684768
res.response
685769
.parse_struct("fiserv RefundResponse")
686770
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
771+
772+
let response_integrity_object = connector_utils::get_refund_integrity_object(
773+
self.amount_converter,
774+
response.payment_receipt.approved_amount.total,
775+
response
776+
.payment_receipt
777+
.approved_amount
778+
.currency
779+
.to_string()
780+
.clone(),
781+
)?;
782+
687783
event_builder.map(|i| i.set_response_body(&response));
688784
router_env::logger::info!(connector_response=?response);
689-
RouterData::try_from(ResponseRouterData {
785+
let new_router_data = RouterData::try_from(ResponseRouterData {
690786
response,
691787
data: data.clone(),
692788
http_code: res.status_code,
693789
})
694-
.change_context(errors::ConnectorError::ResponseHandlingFailed)
790+
.change_context(errors::ConnectorError::ResponseHandlingFailed);
791+
792+
new_router_data.map(|mut router_data| {
793+
router_data.request.integrity_object = Some(response_integrity_object);
794+
router_data
795+
})
695796
}
696797
fn get_error_response(
697798
&self,
@@ -767,14 +868,37 @@ impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Fiserv {
767868
.response
768869
.parse_struct("Fiserv Refund Response")
769870
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
871+
872+
let r_sync_response = response.sync_responses.first().ok_or(
873+
errors::ConnectorError::MissingRequiredField {
874+
field_name: "R_Sync_Responses[0]",
875+
},
876+
)?;
877+
878+
let response_integrity_object = connector_utils::get_refund_integrity_object(
879+
self.amount_converter,
880+
r_sync_response.payment_receipt.approved_amount.total,
881+
r_sync_response
882+
.payment_receipt
883+
.approved_amount
884+
.currency
885+
.to_string()
886+
.clone(),
887+
)?;
888+
770889
event_builder.map(|i| i.set_response_body(&response));
771890
router_env::logger::info!(connector_response=?response);
772-
RouterData::try_from(ResponseRouterData {
891+
let new_router_data = RouterData::try_from(ResponseRouterData {
773892
response,
774893
data: data.clone(),
775894
http_code: res.status_code,
776895
})
777-
.change_context(errors::ConnectorError::ResponseHandlingFailed)
896+
.change_context(errors::ConnectorError::ResponseHandlingFailed);
897+
898+
new_router_data.map(|mut router_data| {
899+
router_data.request.integrity_object = Some(response_integrity_object);
900+
router_data
901+
})
778902
}
779903

780904
fn get_error_response(

0 commit comments

Comments
 (0)