mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +08:00
fix(router): mark retry payment as failure if connector_tokenization fails (#5114)
This commit is contained in:
@ -302,6 +302,7 @@ where
|
||||
#[cfg(not(feature = "frm"))]
|
||||
None,
|
||||
&business_profile,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -373,6 +374,7 @@ where
|
||||
#[cfg(not(feature = "frm"))]
|
||||
None,
|
||||
&business_profile,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -1394,6 +1396,7 @@ pub async fn call_connector_service<F, RouterDReq, ApiRequest>(
|
||||
header_payload: HeaderPayload,
|
||||
frm_suggestion: Option<storage_enums::FrmSuggestion>,
|
||||
business_profile: &storage::business_profile::BusinessProfile,
|
||||
is_retry_payment: bool,
|
||||
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
|
||||
where
|
||||
F: Send + Clone + Sync,
|
||||
@ -1476,7 +1479,7 @@ where
|
||||
|
||||
router_data = router_data.add_session_token(state, &connector).await?;
|
||||
|
||||
let mut should_continue_further = access_token::update_router_data_with_access_token_result(
|
||||
let should_continue_further = access_token::update_router_data_with_access_token_result(
|
||||
&add_access_token_result,
|
||||
&mut router_data,
|
||||
&call_connector_action,
|
||||
@ -1526,16 +1529,22 @@ where
|
||||
_ => (),
|
||||
};
|
||||
|
||||
let pm_token = router_data
|
||||
.add_payment_method_token(state, &connector, &tokenization_action)
|
||||
let payment_method_token_response = router_data
|
||||
.add_payment_method_token(
|
||||
state,
|
||||
&connector,
|
||||
&tokenization_action,
|
||||
should_continue_further,
|
||||
)
|
||||
.await?;
|
||||
if let Some(payment_method_token) = pm_token.clone() {
|
||||
router_data.payment_method_token = Some(
|
||||
hyperswitch_domain_models::router_data::PaymentMethodToken::Token(Secret::new(
|
||||
payment_method_token,
|
||||
)),
|
||||
|
||||
let mut should_continue_further =
|
||||
tokenization::update_router_data_with_payment_method_token_result(
|
||||
payment_method_token_response,
|
||||
&mut router_data,
|
||||
is_retry_payment,
|
||||
should_continue_further,
|
||||
);
|
||||
};
|
||||
|
||||
(router_data, should_continue_further) = complete_preprocessing_steps_if_required(
|
||||
state,
|
||||
|
||||
@ -84,13 +84,17 @@ pub trait Feature<F, T> {
|
||||
_state: &SessionState,
|
||||
_connector: &api::ConnectorData,
|
||||
_tokenization_action: &payments::TokenizationAction,
|
||||
) -> RouterResult<Option<String>>
|
||||
_should_continue_payment: bool,
|
||||
) -> RouterResult<types::PaymentMethodTokenResult>
|
||||
where
|
||||
F: Clone,
|
||||
Self: Sized,
|
||||
dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
|
||||
{
|
||||
Ok(None)
|
||||
Ok(types::PaymentMethodTokenResult {
|
||||
payment_method_token_result: Ok(None),
|
||||
is_payment_method_tokenization_performed: false,
|
||||
})
|
||||
}
|
||||
|
||||
async fn preprocessing_steps<'a>(
|
||||
|
||||
@ -140,7 +140,8 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
||||
state: &SessionState,
|
||||
connector: &api::ConnectorData,
|
||||
tokenization_action: &payments::TokenizationAction,
|
||||
) -> RouterResult<Option<String>> {
|
||||
should_continue_payment: bool,
|
||||
) -> RouterResult<types::PaymentMethodTokenResult> {
|
||||
let request = self.request.clone();
|
||||
tokenization::add_payment_method_token(
|
||||
state,
|
||||
@ -148,6 +149,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
||||
tokenization_action,
|
||||
self,
|
||||
types::PaymentMethodTokenizationData::try_from(request)?,
|
||||
should_continue_payment,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@ -103,7 +103,8 @@ impl Feature<api::CompleteAuthorize, types::CompleteAuthorizeData>
|
||||
state: &SessionState,
|
||||
connector: &api::ConnectorData,
|
||||
_tokenization_action: &payments::TokenizationAction,
|
||||
) -> RouterResult<Option<String>> {
|
||||
should_continue_payment: bool,
|
||||
) -> RouterResult<types::PaymentMethodTokenResult> {
|
||||
// TODO: remove this and handle it in core
|
||||
if matches!(connector.connector_name, types::Connector::Payme) {
|
||||
let request = self.request.clone();
|
||||
@ -113,10 +114,14 @@ impl Feature<api::CompleteAuthorize, types::CompleteAuthorizeData>
|
||||
&payments::TokenizationAction::TokenizeInConnector,
|
||||
self,
|
||||
types::PaymentMethodTokenizationData::try_from(request)?,
|
||||
should_continue_payment,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
Ok(None)
|
||||
Ok(types::PaymentMethodTokenResult {
|
||||
payment_method_token_result: Ok(None),
|
||||
is_payment_method_tokenization_performed: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -107,7 +107,8 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
|
||||
state: &SessionState,
|
||||
connector: &api::ConnectorData,
|
||||
tokenization_action: &payments::TokenizationAction,
|
||||
) -> RouterResult<Option<String>> {
|
||||
should_continue_payment: bool,
|
||||
) -> RouterResult<types::PaymentMethodTokenResult> {
|
||||
let request = self.request.clone();
|
||||
tokenization::add_payment_method_token(
|
||||
state,
|
||||
@ -115,6 +116,7 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
|
||||
tokenization_action,
|
||||
self,
|
||||
types::PaymentMethodTokenizationData::try_from(request)?,
|
||||
should_continue_payment,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@ -321,6 +321,7 @@ where
|
||||
api::HeaderPayload::default(),
|
||||
frm_suggestion,
|
||||
business_profile,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@ -784,62 +784,109 @@ pub async fn add_payment_method_token<F: Clone, T: types::Tokenizable + Clone>(
|
||||
tokenization_action: &payments::TokenizationAction,
|
||||
router_data: &mut types::RouterData<F, T, types::PaymentsResponseData>,
|
||||
pm_token_request_data: types::PaymentMethodTokenizationData,
|
||||
) -> RouterResult<Option<String>> {
|
||||
match tokenization_action {
|
||||
payments::TokenizationAction::TokenizeInConnector
|
||||
| payments::TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt(_) => {
|
||||
let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
|
||||
api::PaymentMethodToken,
|
||||
types::PaymentMethodTokenizationData,
|
||||
types::PaymentsResponseData,
|
||||
> = connector.connector.get_connector_integration();
|
||||
should_continue_payment: bool,
|
||||
) -> RouterResult<types::PaymentMethodTokenResult> {
|
||||
if should_continue_payment {
|
||||
match tokenization_action {
|
||||
payments::TokenizationAction::TokenizeInConnector
|
||||
| payments::TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt(_) => {
|
||||
let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
|
||||
api::PaymentMethodToken,
|
||||
types::PaymentMethodTokenizationData,
|
||||
types::PaymentsResponseData,
|
||||
> = connector.connector.get_connector_integration();
|
||||
|
||||
let pm_token_response_data: Result<types::PaymentsResponseData, types::ErrorResponse> =
|
||||
Err(types::ErrorResponse::default());
|
||||
let pm_token_response_data: Result<
|
||||
types::PaymentsResponseData,
|
||||
types::ErrorResponse,
|
||||
> = Err(types::ErrorResponse::default());
|
||||
|
||||
let pm_token_router_data =
|
||||
helpers::router_data_type_conversion::<_, api::PaymentMethodToken, _, _, _, _>(
|
||||
router_data.clone(),
|
||||
pm_token_request_data,
|
||||
pm_token_response_data,
|
||||
let pm_token_router_data =
|
||||
helpers::router_data_type_conversion::<_, api::PaymentMethodToken, _, _, _, _>(
|
||||
router_data.clone(),
|
||||
pm_token_request_data,
|
||||
pm_token_response_data,
|
||||
);
|
||||
|
||||
router_data
|
||||
.request
|
||||
.set_session_token(pm_token_router_data.session_token.clone());
|
||||
|
||||
let resp = services::execute_connector_processing_step(
|
||||
state,
|
||||
connector_integration,
|
||||
&pm_token_router_data,
|
||||
payments::CallConnectorAction::Trigger,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.to_payment_failed_response()?;
|
||||
|
||||
metrics::CONNECTOR_PAYMENT_METHOD_TOKENIZATION.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&add_attributes([
|
||||
("connector", connector.connector_name.to_string()),
|
||||
("payment_method", router_data.payment_method.to_string()),
|
||||
]),
|
||||
);
|
||||
|
||||
router_data
|
||||
.request
|
||||
.set_session_token(pm_token_router_data.session_token.clone());
|
||||
let payment_token_resp = resp.response.map(|res| {
|
||||
if let types::PaymentsResponseData::TokenizationResponse { token } = res {
|
||||
Some(token)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let resp = services::execute_connector_processing_step(
|
||||
state,
|
||||
connector_integration,
|
||||
&pm_token_router_data,
|
||||
payments::CallConnectorAction::Trigger,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.to_payment_failed_response()?;
|
||||
|
||||
metrics::CONNECTOR_PAYMENT_METHOD_TOKENIZATION.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&add_attributes([
|
||||
("connector", connector.connector_name.to_string()),
|
||||
("payment_method", router_data.payment_method.to_string()),
|
||||
]),
|
||||
);
|
||||
|
||||
let pm_token = match resp.response {
|
||||
Ok(response) => match response {
|
||||
types::PaymentsResponseData::TokenizationResponse { token } => Some(token),
|
||||
_ => None,
|
||||
},
|
||||
Err(err) => {
|
||||
logger::debug!(payment_method_tokenization_error=?err);
|
||||
None
|
||||
}
|
||||
};
|
||||
Ok(pm_token)
|
||||
Ok(types::PaymentMethodTokenResult {
|
||||
payment_method_token_result: payment_token_resp,
|
||||
is_payment_method_tokenization_performed: true,
|
||||
})
|
||||
}
|
||||
_ => Ok(types::PaymentMethodTokenResult {
|
||||
payment_method_token_result: Ok(None),
|
||||
is_payment_method_tokenization_performed: false,
|
||||
}),
|
||||
}
|
||||
_ => Ok(None),
|
||||
} else {
|
||||
logger::debug!("Skipping connector tokenization based on should_continue_payment flag");
|
||||
Ok(types::PaymentMethodTokenResult {
|
||||
payment_method_token_result: Ok(None),
|
||||
is_payment_method_tokenization_performed: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_router_data_with_payment_method_token_result<F: Clone, T>(
|
||||
payment_method_token_result: types::PaymentMethodTokenResult,
|
||||
router_data: &mut types::RouterData<F, T, types::PaymentsResponseData>,
|
||||
is_retry_payment: bool,
|
||||
should_continue_further: bool,
|
||||
) -> bool {
|
||||
if payment_method_token_result.is_payment_method_tokenization_performed {
|
||||
match payment_method_token_result.payment_method_token_result {
|
||||
Ok(pm_token_result) => {
|
||||
router_data.payment_method_token = pm_token_result.map(|pm_token| {
|
||||
hyperswitch_domain_models::router_data::PaymentMethodToken::Token(
|
||||
masking::Secret::new(pm_token),
|
||||
)
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
if is_retry_payment {
|
||||
router_data.response = Err(err);
|
||||
false
|
||||
} else {
|
||||
logger::debug!(payment_method_tokenization_error=?err);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
should_continue_further
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user