Skip to content

Commit cc84a80

Browse files
bsayak03Sayak Bhattacharyahyperswitch-bot[bot]sahkal
authored andcommitted
feat(connector): [TRUSTPAY] Added Integrity Checks for PSync & RSync flows & Added New Variants in AttemptStatus & IntentStatus (#8096)
Co-authored-by: Sayak Bhattacharya <[email protected]> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Sahkal Poddar <[email protected]>
1 parent 507da37 commit cc84a80

File tree

31 files changed

+148
-64
lines changed

31 files changed

+148
-64
lines changed

api-reference-v2/openapi_spec.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4828,7 +4828,8 @@
48284828
"failure",
48294829
"payment_method_awaited",
48304830
"confirmation_awaited",
4831-
"device_data_collection_pending"
4831+
"device_data_collection_pending",
4832+
"integrity_failure"
48324833
]
48334834
},
48344835
"AuthenticationConnectorDetails": {
@@ -12382,7 +12383,8 @@
1238212383
"requires_confirmation",
1238312384
"requires_capture",
1238412385
"partially_captured",
12385-
"partially_captured_and_capturable"
12386+
"partially_captured_and_capturable",
12387+
"conflicted"
1238612388
]
1238712389
},
1238812390
"JCSVoucherData": {

api-reference/openapi_spec.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6808,7 +6808,8 @@
68086808
"failure",
68096809
"payment_method_awaited",
68106810
"confirmation_awaited",
6811-
"device_data_collection_pending"
6811+
"device_data_collection_pending",
6812+
"integrity_failure"
68126813
]
68136814
},
68146815
"AuthenticationConnectorDetails": {
@@ -14608,7 +14609,8 @@
1460814609
"requires_confirmation",
1460914610
"requires_capture",
1461014611
"partially_captured",
14611-
"partially_captured_and_capturable"
14612+
"partially_captured_and_capturable",
14613+
"conflicted"
1461214614
]
1461314615
},
1461414616
"IssuerData": {

crates/common_enums/src/enums.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ pub enum AttemptStatus {
155155
PaymentMethodAwaited,
156156
ConfirmationAwaited,
157157
DeviceDataCollectionPending,
158+
IntegrityFailure,
158159
}
159160

160161
impl AttemptStatus {
@@ -183,7 +184,8 @@ impl AttemptStatus {
183184
| Self::Pending
184185
| Self::PaymentMethodAwaited
185186
| Self::ConfirmationAwaited
186-
| Self::DeviceDataCollectionPending => false,
187+
| Self::DeviceDataCollectionPending
188+
| Self::IntegrityFailure => false,
187189
}
188190
}
189191
}
@@ -1599,6 +1601,8 @@ pub enum IntentStatus {
15991601
PartiallyCaptured,
16001602
/// The payment has been captured partially and the remaining amount is capturable
16011603
PartiallyCapturedAndCapturable,
1604+
/// There has been a discrepancy between the amount/currency sent in the request and the amount/currency received by the processor
1605+
Conflicted,
16021606
}
16031607

16041608
impl IntentStatus {
@@ -1612,7 +1616,8 @@ impl IntentStatus {
16121616
| Self::RequiresPaymentMethod
16131617
| Self::RequiresConfirmation
16141618
| Self::RequiresCapture
1615-
| Self::PartiallyCapturedAndCapturable => false,
1619+
| Self::PartiallyCapturedAndCapturable
1620+
| Self::Conflicted => false,
16161621
}
16171622
}
16181623

@@ -1627,7 +1632,7 @@ impl IntentStatus {
16271632
| Self::Failed
16281633
| Self::Cancelled
16291634
| Self::PartiallyCaptured
1630-
| Self::RequiresCapture => false,
1635+
| Self::RequiresCapture | Self::Conflicted => false,
16311636
Self::Processing
16321637
| Self::RequiresCustomerAction
16331638
| Self::RequiresMerchantAction
@@ -1747,7 +1752,8 @@ impl From<AttemptStatus> for PaymentMethodStatus {
17471752
| AttemptStatus::PartialCharged
17481753
| AttemptStatus::PartialChargedAndChargeable
17491754
| AttemptStatus::ConfirmationAwaited
1750-
| AttemptStatus::DeviceDataCollectionPending => Self::Inactive,
1755+
| AttemptStatus::DeviceDataCollectionPending
1756+
| AttemptStatus::IntegrityFailure => Self::Inactive,
17511757
AttemptStatus::Charged | AttemptStatus::Authorized => Self::Active,
17521758
}
17531759
}

crates/common_enums/src/transformers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2095,7 +2095,7 @@ impl From<AttemptStatus> for IntentStatus {
20952095
Self::RequiresCustomerAction
20962096
}
20972097
AttemptStatus::Unresolved => Self::RequiresMerchantAction,
2098-
2098+
AttemptStatus::IntegrityFailure => Self::Conflicted,
20992099
AttemptStatus::PartialCharged => Self::PartiallyCaptured,
21002100
AttemptStatus::PartialChargedAndChargeable => Self::PartiallyCapturedAndCapturable,
21012101
AttemptStatus::Started

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -685,13 +685,12 @@ impl TryFrom<enums::AttemptStatus> for ChargebeeRecordStatus {
685685
| enums::AttemptStatus::Pending
686686
| enums::AttemptStatus::PaymentMethodAwaited
687687
| enums::AttemptStatus::ConfirmationAwaited
688-
| enums::AttemptStatus::DeviceDataCollectionPending => {
689-
Err(errors::ConnectorError::NotSupported {
690-
message: "Record back flow is only supported for terminal status".to_string(),
691-
connector: "chargebee",
692-
}
693-
.into())
688+
| enums::AttemptStatus::DeviceDataCollectionPending
689+
| enums::AttemptStatus::IntegrityFailure => Err(errors::ConnectorError::NotSupported {
690+
message: "Record back flow is only supported for terminal status".to_string(),
691+
connector: "chargebee",
694692
}
693+
.into()),
695694
}
696695
}
697696
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2530,9 +2530,8 @@ impl TryFrom<PaymentsCaptureResponseRouterData<PaypalCaptureResponse>>
25302530
| storage_enums::AttemptStatus::Voided => 0,
25312531
storage_enums::AttemptStatus::Charged
25322532
| storage_enums::AttemptStatus::PartialCharged
2533-
| storage_enums::AttemptStatus::PartialChargedAndChargeable => {
2534-
item.data.request.amount_to_capture
2535-
}
2533+
| storage_enums::AttemptStatus::PartialChargedAndChargeable
2534+
| storage_enums::AttemptStatus::IntegrityFailure => item.data.request.amount_to_capture,
25362535
};
25372536
let connector_payment_id: PaypalMeta =
25382537
to_connector_meta(item.data.request.connector_meta.clone())?;

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,13 +273,12 @@ impl TryFrom<enums::AttemptStatus> for RecurlyRecordStatus {
273273
| enums::AttemptStatus::Pending
274274
| enums::AttemptStatus::PaymentMethodAwaited
275275
| enums::AttemptStatus::ConfirmationAwaited
276-
| enums::AttemptStatus::DeviceDataCollectionPending => {
277-
Err(errors::ConnectorError::NotSupported {
278-
message: "Record back flow is only supported for terminal status".to_string(),
279-
connector: "recurly",
280-
}
281-
.into())
276+
| enums::AttemptStatus::DeviceDataCollectionPending
277+
| enums::AttemptStatus::IntegrityFailure => Err(errors::ConnectorError::NotSupported {
278+
message: "Record back flow is only supported for terminal status".to_string(),
279+
connector: "recurly",
282280
}
281+
.into()),
283282
}
284283
}
285284
}

crates/hyperswitch_connectors/src/connectors/trustpay.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
pub mod transformers;
2-
32
use base64::Engine;
43
use common_enums::{enums, PaymentAction};
54
use common_utils::{
@@ -54,7 +53,7 @@ use transformers as trustpay;
5453
use crate::{
5554
constants::headers,
5655
types::ResponseRouterData,
57-
utils::{self, ConnectorErrorType, PaymentsPreProcessingRequestData},
56+
utils::{self, self as connector_utils, ConnectorErrorType, PaymentsPreProcessingRequestData},
5857
};
5958

6059
#[derive(Clone)]
@@ -424,6 +423,31 @@ impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Tru
424423
.parse_struct("trustpay PaymentsResponse")
425424
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
426425

426+
if let trustpay::TrustpayPaymentsResponse::WebhookResponse(ref webhook_response) = response
427+
{
428+
let response_integrity_object = connector_utils::get_sync_integrity_object(
429+
self.amount_converter_to_float_major_unit,
430+
webhook_response.amount.amount,
431+
webhook_response.amount.currency.to_string(),
432+
)?;
433+
434+
event_builder.map(|i| i.set_response_body(&response));
435+
router_env::logger::info!(connector_response=?response);
436+
437+
let new_router_data = RouterData::try_from(ResponseRouterData {
438+
response,
439+
data: data.clone(),
440+
http_code: res.status_code,
441+
});
442+
443+
return new_router_data
444+
.map(|mut router_data| {
445+
router_data.request.integrity_object = Some(response_integrity_object);
446+
router_data
447+
})
448+
.change_context(errors::ConnectorError::ResponseHandlingFailed);
449+
}
450+
427451
event_builder.map(|i| i.set_response_body(&response));
428452
router_env::logger::info!(connector_response=?response);
429453

@@ -810,6 +834,29 @@ impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Trustpay
810834
.parse_struct("trustpay RefundResponse")
811835
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
812836

837+
if let trustpay::RefundResponse::WebhookRefund(ref webhook_response) = response {
838+
let response_integrity_object = connector_utils::get_refund_integrity_object(
839+
self.amount_converter_to_float_major_unit,
840+
webhook_response.amount.amount,
841+
webhook_response.amount.currency.to_string(),
842+
)?;
843+
844+
event_builder.map(|i| i.set_response_body(&response));
845+
router_env::logger::info!(connector_response=?response);
846+
847+
let new_router_data = RouterData::try_from(ResponseRouterData {
848+
response,
849+
data: data.clone(),
850+
http_code: res.status_code,
851+
});
852+
853+
return new_router_data
854+
.map(|mut router_data| {
855+
router_data.request.integrity_object = Some(response_integrity_object);
856+
router_data
857+
})
858+
.change_context(errors::ConnectorError::ResponseHandlingFailed);
859+
}
813860
event_builder.map(|i| i.set_response_body(&response));
814861
router_env::logger::info!(connector_response=?response);
815862

crates/hyperswitch_connectors/src/utils.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,8 @@ pub(crate) fn is_payment_failure(status: AttemptStatus) -> bool {
440440
| AttemptStatus::Pending
441441
| AttemptStatus::PaymentMethodAwaited
442442
| AttemptStatus::ConfirmationAwaited
443-
| AttemptStatus::DeviceDataCollectionPending => false,
443+
| AttemptStatus::DeviceDataCollectionPending
444+
| AttemptStatus::IntegrityFailure => false,
444445
}
445446
}
446447

@@ -6172,7 +6173,8 @@ impl FrmTransactionRouterDataRequest for FrmTransactionRouterData {
61726173
AttemptStatus::AuthenticationSuccessful
61736174
| AttemptStatus::PartialChargedAndChargeable
61746175
| AttemptStatus::Authorized
6175-
| AttemptStatus::Charged => Some(true),
6176+
| AttemptStatus::Charged
6177+
| AttemptStatus::IntegrityFailure => Some(true),
61766178

61776179
AttemptStatus::Started
61786180
| AttemptStatus::AuthenticationPending

crates/hyperswitch_domain_models/src/router_data.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,8 @@ impl
696696
// So set the amount capturable to zero
697697
common_enums::IntentStatus::Succeeded
698698
| common_enums::IntentStatus::Failed
699-
| common_enums::IntentStatus::Cancelled => Some(MinorUnit::zero()),
699+
| common_enums::IntentStatus::Cancelled
700+
| common_enums::IntentStatus::Conflicted => Some(MinorUnit::zero()),
700701
// For these statuses, update the capturable amount when it reaches terminal / capturable state
701702
common_enums::IntentStatus::RequiresCustomerAction
702703
| common_enums::IntentStatus::RequiresMerchantAction
@@ -726,7 +727,7 @@ impl
726727
match intent_status {
727728
// If the status is succeeded then we have captured the whole amount
728729
// we need not check for `amount_to_capture` here because passing `amount_to_capture` when authorizing is not supported
729-
common_enums::IntentStatus::Succeeded => {
730+
common_enums::IntentStatus::Succeeded | common_enums::IntentStatus::Conflicted => {
730731
let total_amount = payment_data.payment_attempt.amount_details.get_net_amount();
731732
Some(total_amount)
732733
}
@@ -912,7 +913,8 @@ impl
912913
// If the status is already succeeded / failed we cannot capture any more amount
913914
common_enums::IntentStatus::Succeeded
914915
| common_enums::IntentStatus::Failed
915-
| common_enums::IntentStatus::Cancelled => Some(MinorUnit::zero()),
916+
| common_enums::IntentStatus::Cancelled
917+
| common_enums::IntentStatus::Conflicted => Some(MinorUnit::zero()),
916918
// For these statuses, update the capturable amount when it reaches terminal / capturable state
917919
common_enums::IntentStatus::RequiresCustomerAction
918920
| common_enums::IntentStatus::RequiresMerchantAction
@@ -938,7 +940,7 @@ impl
938940
let intent_status = common_enums::IntentStatus::from(self.status);
939941
match intent_status {
940942
// If the status is succeeded then we have captured the whole amount
941-
common_enums::IntentStatus::Succeeded => {
943+
common_enums::IntentStatus::Succeeded | common_enums::IntentStatus::Conflicted => {
942944
let amount_to_capture = payment_data
943945
.payment_attempt
944946
.amount_details
@@ -1153,7 +1155,8 @@ impl
11531155
// If the status is already succeeded / failed we cannot capture any more amount
11541156
common_enums::IntentStatus::Succeeded
11551157
| common_enums::IntentStatus::Failed
1156-
| common_enums::IntentStatus::Cancelled => Some(MinorUnit::zero()),
1158+
| common_enums::IntentStatus::Cancelled
1159+
| common_enums::IntentStatus::Conflicted => Some(MinorUnit::zero()),
11571160
// For these statuses, update the capturable amount when it reaches terminal / capturable state
11581161
common_enums::IntentStatus::RequiresCustomerAction
11591162
| common_enums::IntentStatus::RequiresMerchantAction
@@ -1181,7 +1184,7 @@ impl
11811184
let intent_status = common_enums::IntentStatus::from(self.status);
11821185
match intent_status {
11831186
// If the status is succeeded then we have captured the whole amount or amount_to_capture
1184-
common_enums::IntentStatus::Succeeded => {
1187+
common_enums::IntentStatus::Succeeded | common_enums::IntentStatus::Conflicted => {
11851188
let amount_to_capture = payment_attempt.amount_details.get_amount_to_capture();
11861189

11871190
let amount_captured =
@@ -1385,7 +1388,8 @@ impl
13851388
// So set the amount capturable to zero
13861389
common_enums::IntentStatus::Succeeded
13871390
| common_enums::IntentStatus::Failed
1388-
| common_enums::IntentStatus::Cancelled => Some(MinorUnit::zero()),
1391+
| common_enums::IntentStatus::Cancelled
1392+
| common_enums::IntentStatus::Conflicted => Some(MinorUnit::zero()),
13891393
// For these statuses, update the capturable amount when it reaches terminal / capturable state
13901394
common_enums::IntentStatus::RequiresCustomerAction
13911395
| common_enums::IntentStatus::RequiresMerchantAction
@@ -1415,7 +1419,7 @@ impl
14151419
match intent_status {
14161420
// If the status is succeeded then we have captured the whole amount
14171421
// we need not check for `amount_to_capture` here because passing `amount_to_capture` when authorizing is not supported
1418-
common_enums::IntentStatus::Succeeded => {
1422+
common_enums::IntentStatus::Succeeded | common_enums::IntentStatus::Conflicted => {
14191423
let total_amount = payment_data.payment_attempt.amount_details.get_net_amount();
14201424
Some(total_amount)
14211425
}

0 commit comments

Comments
 (0)