mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +08:00
feat(core): Added integrity framework for Authorize and Sync flow with connector as Stripe (#5109)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Hrithikesh <61539176+hrithikesh026@users.noreply.github.com> Co-authored-by: Narayan Bhat <narayan.bhat@juspay.in>
This commit is contained in:
@ -181,6 +181,7 @@ pub fn construct_router_data<F: Clone, Req, Res>(
|
||||
refund_id: None,
|
||||
payment_method_status: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -130,6 +130,7 @@ impl ConstructFlowSpecificData<frm_api::Checkout, FraudCheckCheckoutData, FraudC
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
|
||||
Ok(router_data)
|
||||
|
||||
@ -111,6 +111,7 @@ pub async fn construct_fulfillment_router_data<'a>(
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
Ok(router_data)
|
||||
}
|
||||
|
||||
@ -102,6 +102,7 @@ impl ConstructFlowSpecificData<RecordReturn, FraudCheckRecordReturnData, FraudCh
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
|
||||
Ok(router_data)
|
||||
|
||||
@ -111,6 +111,7 @@ impl ConstructFlowSpecificData<frm_api::Sale, FraudCheckSaleData, FraudCheckResp
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
|
||||
Ok(router_data)
|
||||
|
||||
@ -115,6 +115,7 @@ impl
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
|
||||
Ok(router_data)
|
||||
|
||||
@ -75,6 +75,7 @@ pub async fn construct_mandate_revoke_router_data(
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
|
||||
Ok(router_data)
|
||||
|
||||
@ -76,7 +76,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
||||
if self.should_proceed_with_authorize() {
|
||||
self.decide_authentication_type();
|
||||
logger::debug!(auth_type=?self.auth_type);
|
||||
let resp = services::execute_connector_processing_step(
|
||||
let mut new_router_data = services::execute_connector_processing_step(
|
||||
state,
|
||||
connector_integration,
|
||||
&self,
|
||||
@ -86,8 +86,16 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
||||
.await
|
||||
.to_payment_failed_response()?;
|
||||
|
||||
// Initiating Integrity check
|
||||
let integrity_result = helpers::check_integrity_based_on_flow(
|
||||
&new_router_data.request,
|
||||
&new_router_data.response,
|
||||
);
|
||||
|
||||
new_router_data.integrity_check = integrity_result;
|
||||
|
||||
metrics::PAYMENT_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics
|
||||
Ok(resp)
|
||||
Ok(new_router_data)
|
||||
} else {
|
||||
Ok(self.clone())
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ impl Feature<api::PSync, types::PaymentsSyncData>
|
||||
(types::SyncRequestType::MultipleCaptureSync(_), Err(err)) => Err(err),
|
||||
_ => {
|
||||
// for bulk sync of captures, above logic needs to be handled at connector end
|
||||
let resp = services::execute_connector_processing_step(
|
||||
let mut new_router_data = services::execute_connector_processing_step(
|
||||
state,
|
||||
connector_integration,
|
||||
&self,
|
||||
@ -94,7 +94,16 @@ impl Feature<api::PSync, types::PaymentsSyncData>
|
||||
)
|
||||
.await
|
||||
.to_payment_failed_response()?;
|
||||
Ok(resp)
|
||||
|
||||
// Initiating Integrity checks
|
||||
let integrity_result = helpers::check_integrity_based_on_flow(
|
||||
&new_router_data.request,
|
||||
&new_router_data.response,
|
||||
);
|
||||
|
||||
new_router_data.integrity_check = integrity_result;
|
||||
|
||||
Ok(new_router_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ use hyperswitch_domain_models::{
|
||||
payments::{payment_attempt::PaymentAttempt, payment_intent::CustomerData, PaymentIntent},
|
||||
router_data::KlarnaSdkResponse,
|
||||
};
|
||||
use hyperswitch_interfaces::integrity::{CheckIntegrity, FlowIntegrity, GetIntegrityObject};
|
||||
use josekit::jwe;
|
||||
use masking::{ExposeInterface, PeekInterface};
|
||||
use openssl::{
|
||||
@ -66,7 +67,7 @@ use crate::{
|
||||
},
|
||||
transformers::{ForeignFrom, ForeignTryFrom},
|
||||
AdditionalPaymentMethodConnectorResponse, ErrorResponse, MandateReference,
|
||||
RecurringMandatePaymentData, RouterData,
|
||||
PaymentsResponseData, RecurringMandatePaymentData, RouterData,
|
||||
},
|
||||
utils::{
|
||||
self,
|
||||
@ -3523,6 +3524,7 @@ pub fn router_data_type_conversion<F1, F2, Req1, Req2, Res1, Res2>(
|
||||
refund_id: router_data.refund_id,
|
||||
dispute_id: router_data.dispute_id,
|
||||
connector_response: router_data.connector_response,
|
||||
integrity_check: Ok(()),
|
||||
connector_wallets_details: router_data.connector_wallets_details,
|
||||
}
|
||||
}
|
||||
@ -4946,6 +4948,35 @@ pub fn get_redis_key_for_extended_card_info(merchant_id: &str, payment_id: &str)
|
||||
format!("{merchant_id}_{payment_id}_extended_card_info")
|
||||
}
|
||||
|
||||
pub fn check_integrity_based_on_flow<T, Request>(
|
||||
request: &Request,
|
||||
payment_response_data: &Result<PaymentsResponseData, ErrorResponse>,
|
||||
) -> Result<(), common_utils::errors::IntegrityCheckError>
|
||||
where
|
||||
T: FlowIntegrity,
|
||||
Request: GetIntegrityObject<T> + CheckIntegrity<Request, T>,
|
||||
{
|
||||
let connector_transaction_id = match payment_response_data {
|
||||
Ok(resp_data) => match resp_data {
|
||||
PaymentsResponseData::TransactionResponse {
|
||||
connector_response_reference_id,
|
||||
..
|
||||
} => connector_response_reference_id,
|
||||
PaymentsResponseData::TransactionUnresolvedResponse {
|
||||
connector_response_reference_id,
|
||||
..
|
||||
} => connector_response_reference_id,
|
||||
PaymentsResponseData::PreProcessingResponse {
|
||||
connector_response_reference_id,
|
||||
..
|
||||
} => connector_response_reference_id,
|
||||
_ => &None,
|
||||
},
|
||||
Err(_) => &None,
|
||||
};
|
||||
request.check_integrity(request, connector_transaction_id.to_owned())
|
||||
}
|
||||
|
||||
pub async fn config_skip_saving_wallet_at_connector(
|
||||
db: &dyn StorageInterface,
|
||||
merchant_id: &String,
|
||||
|
||||
@ -7,7 +7,7 @@ use error_stack::{report, ResultExt};
|
||||
use futures::FutureExt;
|
||||
use hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt;
|
||||
use router_derive;
|
||||
use router_env::{instrument, logger, tracing};
|
||||
use router_env::{instrument, logger, metrics::add_attributes, tracing};
|
||||
use storage_impl::DataModelExt;
|
||||
use tracing_futures::Instrument;
|
||||
|
||||
@ -868,94 +868,11 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
};
|
||||
(capture_update, attempt_update)
|
||||
}
|
||||
|
||||
Ok(payments_response) => {
|
||||
let attempt_status = payment_data.payment_attempt.status.to_owned();
|
||||
let connector_status = router_data.status.to_owned();
|
||||
let updated_attempt_status = match (
|
||||
connector_status,
|
||||
attempt_status,
|
||||
payment_data.frm_message.to_owned(),
|
||||
) {
|
||||
(
|
||||
enums::AttemptStatus::Authorized,
|
||||
enums::AttemptStatus::Unresolved,
|
||||
Some(frm_message),
|
||||
) => match frm_message.frm_status {
|
||||
enums::FraudCheckStatus::Fraud | enums::FraudCheckStatus::ManualReview => {
|
||||
attempt_status
|
||||
}
|
||||
_ => router_data.get_attempt_status_for_db_update(&payment_data),
|
||||
},
|
||||
_ => router_data.get_attempt_status_for_db_update(&payment_data),
|
||||
};
|
||||
match payments_response {
|
||||
types::PaymentsResponseData::PreProcessingResponse {
|
||||
pre_processing_id,
|
||||
connector_metadata,
|
||||
connector_response_reference_id,
|
||||
..
|
||||
} => {
|
||||
let connector_transaction_id = match pre_processing_id.to_owned() {
|
||||
types::PreprocessingResponseId::PreProcessingId(_) => None,
|
||||
types::PreprocessingResponseId::ConnectorTransactionId(
|
||||
connector_txn_id,
|
||||
) => Some(connector_txn_id),
|
||||
};
|
||||
let preprocessing_step_id = match pre_processing_id {
|
||||
types::PreprocessingResponseId::PreProcessingId(pre_processing_id) => {
|
||||
Some(pre_processing_id)
|
||||
}
|
||||
types::PreprocessingResponseId::ConnectorTransactionId(_) => None,
|
||||
};
|
||||
let payment_attempt_update =
|
||||
storage::PaymentAttemptUpdate::PreprocessingUpdate {
|
||||
status: updated_attempt_status,
|
||||
payment_method_id: payment_data
|
||||
.payment_attempt
|
||||
.payment_method_id
|
||||
.clone(),
|
||||
connector_metadata,
|
||||
preprocessing_step_id,
|
||||
connector_transaction_id,
|
||||
connector_response_reference_id,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
};
|
||||
|
||||
(None, Some(payment_attempt_update))
|
||||
}
|
||||
types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id,
|
||||
redirection_data,
|
||||
connector_metadata,
|
||||
connector_response_reference_id,
|
||||
incremental_authorization_allowed,
|
||||
charge_id,
|
||||
..
|
||||
} => {
|
||||
payment_data
|
||||
.payment_intent
|
||||
.incremental_authorization_allowed =
|
||||
core_utils::get_incremental_authorization_allowed_value(
|
||||
incremental_authorization_allowed,
|
||||
payment_data
|
||||
.payment_intent
|
||||
.request_incremental_authorization,
|
||||
);
|
||||
let connector_transaction_id = match resource_id {
|
||||
types::ResponseId::NoResponseId => None,
|
||||
types::ResponseId::ConnectorTransactionId(id)
|
||||
| types::ResponseId::EncodedData(id) => Some(id),
|
||||
};
|
||||
|
||||
let encoded_data = payment_data.payment_attempt.encoded_data.clone();
|
||||
|
||||
let authentication_data = redirection_data
|
||||
.as_ref()
|
||||
.map(Encode::encode_to_value)
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Could not parse the connector response")?;
|
||||
|
||||
// match on connector integrity check
|
||||
match router_data.integrity_check.clone() {
|
||||
Err(err) => {
|
||||
let auth_update = if Some(router_data.auth_type)
|
||||
!= payment_data.payment_attempt.authentication_type
|
||||
{
|
||||
@ -963,133 +880,259 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// incase of success, update error code and error message
|
||||
let error_status = if router_data.status == enums::AttemptStatus::Charged {
|
||||
Some(None)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if router_data.status == enums::AttemptStatus::Charged {
|
||||
payment_data
|
||||
.payment_intent
|
||||
.fingerprint_id
|
||||
.clone_from(&payment_data.payment_attempt.fingerprint_id);
|
||||
metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]);
|
||||
}
|
||||
|
||||
let payment_method_id = payment_data.payment_attempt.payment_method_id.clone();
|
||||
|
||||
utils::add_apple_pay_payment_status_metrics(
|
||||
router_data.status,
|
||||
router_data.apple_pay_flow.clone(),
|
||||
payment_data.payment_attempt.connector.clone(),
|
||||
payment_data.payment_attempt.merchant_id.clone(),
|
||||
);
|
||||
let (capture_updates, payment_attempt_update) = match payment_data
|
||||
.multiple_capture_data
|
||||
{
|
||||
Some(multiple_capture_data) => {
|
||||
let capture_update = storage::CaptureUpdate::ResponseUpdate {
|
||||
status: enums::CaptureStatus::foreign_try_from(router_data.status)?,
|
||||
connector_capture_id: connector_transaction_id.clone(),
|
||||
connector_response_reference_id,
|
||||
};
|
||||
let capture_update_list = vec![(
|
||||
multiple_capture_data.get_latest_capture().clone(),
|
||||
capture_update,
|
||||
)];
|
||||
(
|
||||
Some((multiple_capture_data, capture_update_list)),
|
||||
auth_update.map(|auth_type| {
|
||||
storage::PaymentAttemptUpdate::AuthenticationTypeUpdate {
|
||||
authentication_type: auth_type,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
None => (
|
||||
None,
|
||||
Some(storage::PaymentAttemptUpdate::ResponseUpdate {
|
||||
status: updated_attempt_status,
|
||||
connector: None,
|
||||
connector_transaction_id: connector_transaction_id.clone(),
|
||||
authentication_type: auth_update,
|
||||
amount_capturable: router_data
|
||||
.request
|
||||
.get_amount_capturable(&payment_data, updated_attempt_status)
|
||||
.map(MinorUnit::new),
|
||||
payment_method_id,
|
||||
mandate_id: payment_data.payment_attempt.mandate_id.clone(),
|
||||
connector_metadata,
|
||||
payment_token: None,
|
||||
error_code: error_status.clone(),
|
||||
error_message: error_status.clone(),
|
||||
error_reason: error_status.clone(),
|
||||
unified_code: error_status.clone(),
|
||||
unified_message: error_status,
|
||||
connector_response_reference_id,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
authentication_data,
|
||||
encoded_data,
|
||||
payment_method_data: additional_payment_method_data,
|
||||
charge_id,
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
(capture_updates, payment_attempt_update)
|
||||
}
|
||||
types::PaymentsResponseData::TransactionUnresolvedResponse {
|
||||
resource_id,
|
||||
reason,
|
||||
connector_response_reference_id,
|
||||
} => {
|
||||
let connector_transaction_id = match resource_id {
|
||||
types::ResponseId::NoResponseId => None,
|
||||
types::ResponseId::ConnectorTransactionId(id)
|
||||
| types::ResponseId::EncodedData(id) => Some(id),
|
||||
};
|
||||
let field_name = err.field_names;
|
||||
let connector_transaction_id = err.connector_transaction_id;
|
||||
(
|
||||
None,
|
||||
Some(storage::PaymentAttemptUpdate::UnresolvedResponseUpdate {
|
||||
status: updated_attempt_status,
|
||||
Some(storage::PaymentAttemptUpdate::ErrorUpdate {
|
||||
connector: None,
|
||||
connector_transaction_id,
|
||||
payment_method_id: payment_data
|
||||
.payment_attempt
|
||||
.payment_method_id
|
||||
.clone(),
|
||||
error_code: Some(reason.clone().map(|cd| cd.code)),
|
||||
error_message: Some(reason.clone().map(|cd| cd.message)),
|
||||
error_reason: Some(reason.map(|cd| cd.message)),
|
||||
connector_response_reference_id,
|
||||
status: enums::AttemptStatus::Pending,
|
||||
error_message: Some(Some("Integrity Check Failed!".to_string())),
|
||||
error_code: Some(Some("IE".to_string())),
|
||||
error_reason: Some(Some(format!(
|
||||
"Integrity Check Failed! Value mismatched for fields {field_name}"
|
||||
))),
|
||||
amount_capturable: None,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
unified_code: None,
|
||||
unified_message: None,
|
||||
connector_transaction_id,
|
||||
payment_method_data: None,
|
||||
authentication_type: auth_update,
|
||||
}),
|
||||
)
|
||||
}
|
||||
types::PaymentsResponseData::SessionResponse { .. } => (None, None),
|
||||
types::PaymentsResponseData::SessionTokenResponse { .. } => (None, None),
|
||||
types::PaymentsResponseData::TokenizationResponse { .. } => (None, None),
|
||||
types::PaymentsResponseData::ConnectorCustomerResponse { .. } => (None, None),
|
||||
types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => (None, None),
|
||||
types::PaymentsResponseData::IncrementalAuthorizationResponse { .. } => {
|
||||
(None, None)
|
||||
}
|
||||
types::PaymentsResponseData::MultipleCaptureResponse {
|
||||
capture_sync_response_list,
|
||||
} => match payment_data.multiple_capture_data {
|
||||
Some(multiple_capture_data) => {
|
||||
let capture_update_list = response_to_capture_update(
|
||||
&multiple_capture_data,
|
||||
Ok(()) => {
|
||||
let attempt_status = payment_data.payment_attempt.status.to_owned();
|
||||
let connector_status = router_data.status.to_owned();
|
||||
let updated_attempt_status = match (
|
||||
connector_status,
|
||||
attempt_status,
|
||||
payment_data.frm_message.to_owned(),
|
||||
) {
|
||||
(
|
||||
enums::AttemptStatus::Authorized,
|
||||
enums::AttemptStatus::Unresolved,
|
||||
Some(frm_message),
|
||||
) => match frm_message.frm_status {
|
||||
enums::FraudCheckStatus::Fraud
|
||||
| enums::FraudCheckStatus::ManualReview => attempt_status,
|
||||
_ => router_data.get_attempt_status_for_db_update(&payment_data),
|
||||
},
|
||||
_ => router_data.get_attempt_status_for_db_update(&payment_data),
|
||||
};
|
||||
match payments_response {
|
||||
types::PaymentsResponseData::PreProcessingResponse {
|
||||
pre_processing_id,
|
||||
connector_metadata,
|
||||
connector_response_reference_id,
|
||||
..
|
||||
} => {
|
||||
let connector_transaction_id = match pre_processing_id.to_owned() {
|
||||
types::PreprocessingResponseId::PreProcessingId(_) => None,
|
||||
types::PreprocessingResponseId::ConnectorTransactionId(
|
||||
connector_txn_id,
|
||||
) => Some(connector_txn_id),
|
||||
};
|
||||
let preprocessing_step_id = match pre_processing_id {
|
||||
types::PreprocessingResponseId::PreProcessingId(
|
||||
pre_processing_id,
|
||||
) => Some(pre_processing_id),
|
||||
types::PreprocessingResponseId::ConnectorTransactionId(_) => None,
|
||||
};
|
||||
let payment_attempt_update =
|
||||
storage::PaymentAttemptUpdate::PreprocessingUpdate {
|
||||
status: updated_attempt_status,
|
||||
payment_method_id: payment_data
|
||||
.payment_attempt
|
||||
.payment_method_id
|
||||
.clone(),
|
||||
connector_metadata,
|
||||
preprocessing_step_id,
|
||||
connector_transaction_id,
|
||||
connector_response_reference_id,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
};
|
||||
|
||||
(None, Some(payment_attempt_update))
|
||||
}
|
||||
types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id,
|
||||
redirection_data,
|
||||
connector_metadata,
|
||||
connector_response_reference_id,
|
||||
incremental_authorization_allowed,
|
||||
charge_id,
|
||||
..
|
||||
} => {
|
||||
payment_data
|
||||
.payment_intent
|
||||
.incremental_authorization_allowed =
|
||||
core_utils::get_incremental_authorization_allowed_value(
|
||||
incremental_authorization_allowed,
|
||||
payment_data
|
||||
.payment_intent
|
||||
.request_incremental_authorization,
|
||||
);
|
||||
let connector_transaction_id = match resource_id {
|
||||
types::ResponseId::NoResponseId => None,
|
||||
types::ResponseId::ConnectorTransactionId(id)
|
||||
| types::ResponseId::EncodedData(id) => Some(id),
|
||||
};
|
||||
|
||||
let encoded_data = payment_data.payment_attempt.encoded_data.clone();
|
||||
|
||||
let authentication_data = redirection_data
|
||||
.as_ref()
|
||||
.map(Encode::encode_to_value)
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Could not parse the connector response")?;
|
||||
|
||||
let auth_update = if Some(router_data.auth_type)
|
||||
!= payment_data.payment_attempt.authentication_type
|
||||
{
|
||||
Some(router_data.auth_type)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// incase of success, update error code and error message
|
||||
let error_status =
|
||||
if router_data.status == enums::AttemptStatus::Charged {
|
||||
Some(None)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if router_data.status == enums::AttemptStatus::Charged {
|
||||
payment_data
|
||||
.payment_intent
|
||||
.fingerprint_id
|
||||
.clone_from(&payment_data.payment_attempt.fingerprint_id);
|
||||
metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]);
|
||||
}
|
||||
|
||||
let payment_method_id =
|
||||
payment_data.payment_attempt.payment_method_id.clone();
|
||||
|
||||
utils::add_apple_pay_payment_status_metrics(
|
||||
router_data.status,
|
||||
router_data.apple_pay_flow.clone(),
|
||||
payment_data.payment_attempt.connector.clone(),
|
||||
payment_data.payment_attempt.merchant_id.clone(),
|
||||
);
|
||||
let (capture_updates, payment_attempt_update) = match payment_data
|
||||
.multiple_capture_data
|
||||
{
|
||||
Some(multiple_capture_data) => {
|
||||
let capture_update = storage::CaptureUpdate::ResponseUpdate {
|
||||
status: enums::CaptureStatus::foreign_try_from(
|
||||
router_data.status,
|
||||
)?,
|
||||
connector_capture_id: connector_transaction_id.clone(),
|
||||
connector_response_reference_id,
|
||||
};
|
||||
let capture_update_list = vec![(
|
||||
multiple_capture_data.get_latest_capture().clone(),
|
||||
capture_update,
|
||||
)];
|
||||
(Some((multiple_capture_data, capture_update_list)), auth_update.map(|auth_type| {
|
||||
storage::PaymentAttemptUpdate::AuthenticationTypeUpdate {
|
||||
authentication_type: auth_type,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
}
|
||||
}))
|
||||
}
|
||||
None => (
|
||||
None,
|
||||
Some(storage::PaymentAttemptUpdate::ResponseUpdate {
|
||||
status: updated_attempt_status,
|
||||
connector: None,
|
||||
connector_transaction_id: connector_transaction_id.clone(),
|
||||
authentication_type: auth_update,
|
||||
amount_capturable: router_data
|
||||
.request
|
||||
.get_amount_capturable(
|
||||
&payment_data,
|
||||
updated_attempt_status,
|
||||
)
|
||||
.map(MinorUnit::new),
|
||||
payment_method_id,
|
||||
mandate_id: payment_data.payment_attempt.mandate_id.clone(),
|
||||
connector_metadata,
|
||||
payment_token: None,
|
||||
error_code: error_status.clone(),
|
||||
error_message: error_status.clone(),
|
||||
error_reason: error_status.clone(),
|
||||
unified_code: error_status.clone(),
|
||||
unified_message: error_status,
|
||||
connector_response_reference_id,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
authentication_data,
|
||||
encoded_data,
|
||||
payment_method_data: additional_payment_method_data,
|
||||
charge_id,
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
(capture_updates, payment_attempt_update)
|
||||
}
|
||||
types::PaymentsResponseData::TransactionUnresolvedResponse {
|
||||
resource_id,
|
||||
reason,
|
||||
connector_response_reference_id,
|
||||
} => {
|
||||
let connector_transaction_id = match resource_id {
|
||||
types::ResponseId::NoResponseId => None,
|
||||
types::ResponseId::ConnectorTransactionId(id)
|
||||
| types::ResponseId::EncodedData(id) => Some(id),
|
||||
};
|
||||
(
|
||||
None,
|
||||
Some(storage::PaymentAttemptUpdate::UnresolvedResponseUpdate {
|
||||
status: updated_attempt_status,
|
||||
connector: None,
|
||||
connector_transaction_id,
|
||||
payment_method_id: payment_data
|
||||
.payment_attempt
|
||||
.payment_method_id
|
||||
.clone(),
|
||||
error_code: Some(reason.clone().map(|cd| cd.code)),
|
||||
error_message: Some(reason.clone().map(|cd| cd.message)),
|
||||
error_reason: Some(reason.map(|cd| cd.message)),
|
||||
connector_response_reference_id,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
types::PaymentsResponseData::SessionResponse { .. } => (None, None),
|
||||
types::PaymentsResponseData::SessionTokenResponse { .. } => (None, None),
|
||||
types::PaymentsResponseData::TokenizationResponse { .. } => (None, None),
|
||||
types::PaymentsResponseData::ConnectorCustomerResponse { .. } => {
|
||||
(None, None)
|
||||
}
|
||||
types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => {
|
||||
(None, None)
|
||||
}
|
||||
types::PaymentsResponseData::IncrementalAuthorizationResponse {
|
||||
..
|
||||
} => (None, None),
|
||||
types::PaymentsResponseData::MultipleCaptureResponse {
|
||||
capture_sync_response_list,
|
||||
)?;
|
||||
(Some((multiple_capture_data, capture_update_list)), None)
|
||||
} => match payment_data.multiple_capture_data {
|
||||
Some(multiple_capture_data) => {
|
||||
let capture_update_list = response_to_capture_update(
|
||||
&multiple_capture_data,
|
||||
capture_sync_response_list,
|
||||
)?;
|
||||
(Some((multiple_capture_data, capture_update_list)), None)
|
||||
}
|
||||
None => (None, None),
|
||||
},
|
||||
}
|
||||
None => (None, None),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1308,7 +1351,33 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
.as_mut()
|
||||
.map(|info| info.status = status)
|
||||
});
|
||||
Ok(payment_data)
|
||||
|
||||
match router_data.integrity_check {
|
||||
Ok(()) => Ok(payment_data),
|
||||
Err(err) => {
|
||||
metrics::INTEGRITY_CHECK_FAILED.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&add_attributes([
|
||||
(
|
||||
"connector",
|
||||
payment_data.payment_attempt.connector.unwrap_or_default(),
|
||||
),
|
||||
("merchant_id", payment_data.payment_attempt.merchant_id),
|
||||
]),
|
||||
);
|
||||
Err(error_stack::Report::new(
|
||||
errors::ApiErrorResponse::IntegrityCheckFailed {
|
||||
reason: payment_data
|
||||
.payment_attempt
|
||||
.error_message
|
||||
.unwrap_or_default(),
|
||||
field_names: err.field_names,
|
||||
connector_transaction_id: payment_data.payment_attempt.connector_transaction_id,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_payment_method_status_and_ntid<F: Clone>(
|
||||
|
||||
@ -195,6 +195,7 @@ where
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
|
||||
Ok(router_data)
|
||||
@ -1349,6 +1350,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthoriz
|
||||
.transpose()?,
|
||||
customer_acceptance: payment_data.customer_acceptance,
|
||||
charges,
|
||||
integrity_object: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1358,7 +1360,14 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsSyncData
|
||||
|
||||
fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result<Self, Self::Error> {
|
||||
let payment_data = additional_data.payment_data;
|
||||
let amount = payment_data
|
||||
.surcharge_details
|
||||
.as_ref()
|
||||
.map(|surcharge_details| surcharge_details.final_amount)
|
||||
.unwrap_or(payment_data.amount.into());
|
||||
Ok(Self {
|
||||
amount,
|
||||
integrity_object: None,
|
||||
mandate_id: payment_data.mandate_id.clone(),
|
||||
connector_transaction_id: match payment_data.payment_attempt.connector_transaction_id {
|
||||
Some(connector_txn_id) => {
|
||||
|
||||
@ -209,6 +209,7 @@ pub async fn construct_payout_router_data<'a, F>(
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
|
||||
Ok(router_data)
|
||||
@ -371,6 +372,7 @@ pub async fn construct_refund_router_data<'a, F>(
|
||||
refund_id: Some(refund.refund_id.clone()),
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
|
||||
Ok(router_data)
|
||||
@ -608,6 +610,7 @@ pub async fn construct_accept_dispute_router_data<'a>(
|
||||
dispute_id: Some(dispute.dispute_id.clone()),
|
||||
refund_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
Ok(router_data)
|
||||
}
|
||||
@ -703,6 +706,7 @@ pub async fn construct_submit_evidence_router_data<'a>(
|
||||
refund_id: None,
|
||||
dispute_id: Some(dispute.dispute_id.clone()),
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
Ok(router_data)
|
||||
}
|
||||
@ -804,6 +808,7 @@ pub async fn construct_upload_file_router_data<'a>(
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
Ok(router_data)
|
||||
}
|
||||
@ -902,6 +907,7 @@ pub async fn construct_defend_dispute_router_data<'a>(
|
||||
refund_id: None,
|
||||
dispute_id: Some(dispute.dispute_id.clone()),
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
Ok(router_data)
|
||||
}
|
||||
@ -989,6 +995,7 @@ pub async fn construct_retrieve_file_router_data<'a>(
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
Ok(router_data)
|
||||
}
|
||||
|
||||
@ -119,6 +119,7 @@ pub async fn construct_webhook_router_data<'a>(
|
||||
refund_id: None,
|
||||
dispute_id: None,
|
||||
connector_response: None,
|
||||
integrity_check: Ok(()),
|
||||
};
|
||||
Ok(router_data)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user