mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-03 21:37:41 +08:00
fix(router): mark retry payment as failure if connector_tokenization fails (#5114)
This commit is contained in:
@ -33,7 +33,6 @@ use crate::{
|
|||||||
storage::enums,
|
storage::enums,
|
||||||
transformers::{ForeignFrom, ForeignTryFrom},
|
transformers::{ForeignFrom, ForeignTryFrom},
|
||||||
},
|
},
|
||||||
unimplemented_payment_method,
|
|
||||||
utils::OptionExt,
|
utils::OptionExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1761,9 +1760,11 @@ impl TryFrom<(&types::PaymentsAuthorizeRouterData, MinorUnit)> for PaymentIntent
|
|||||||
|
|
||||||
let payment_method_token = match payment_method_token {
|
let payment_method_token = match payment_method_token {
|
||||||
types::PaymentMethodToken::Token(payment_method_token) => payment_method_token,
|
types::PaymentMethodToken::Token(payment_method_token) => payment_method_token,
|
||||||
types::PaymentMethodToken::ApplePayDecrypt(_) => Err(
|
types::PaymentMethodToken::ApplePayDecrypt(_) => {
|
||||||
unimplemented_payment_method!("Apple Pay", "Simplified", "Stripe"),
|
Err(errors::ConnectorError::InvalidWalletToken {
|
||||||
)?,
|
wallet_name: "Apple Pay".to_string(),
|
||||||
|
})?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Some(StripePaymentMethodData::Wallet(
|
Some(StripePaymentMethodData::Wallet(
|
||||||
StripeWallet::ApplepayPayment(ApplepayPayment {
|
StripeWallet::ApplepayPayment(ApplepayPayment {
|
||||||
|
|||||||
@ -302,6 +302,7 @@ where
|
|||||||
#[cfg(not(feature = "frm"))]
|
#[cfg(not(feature = "frm"))]
|
||||||
None,
|
None,
|
||||||
&business_profile,
|
&business_profile,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -373,6 +374,7 @@ where
|
|||||||
#[cfg(not(feature = "frm"))]
|
#[cfg(not(feature = "frm"))]
|
||||||
None,
|
None,
|
||||||
&business_profile,
|
&business_profile,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -1394,6 +1396,7 @@ pub async fn call_connector_service<F, RouterDReq, ApiRequest>(
|
|||||||
header_payload: HeaderPayload,
|
header_payload: HeaderPayload,
|
||||||
frm_suggestion: Option<storage_enums::FrmSuggestion>,
|
frm_suggestion: Option<storage_enums::FrmSuggestion>,
|
||||||
business_profile: &storage::business_profile::BusinessProfile,
|
business_profile: &storage::business_profile::BusinessProfile,
|
||||||
|
is_retry_payment: bool,
|
||||||
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
|
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
|
||||||
where
|
where
|
||||||
F: Send + Clone + Sync,
|
F: Send + Clone + Sync,
|
||||||
@ -1476,7 +1479,7 @@ where
|
|||||||
|
|
||||||
router_data = router_data.add_session_token(state, &connector).await?;
|
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,
|
&add_access_token_result,
|
||||||
&mut router_data,
|
&mut router_data,
|
||||||
&call_connector_action,
|
&call_connector_action,
|
||||||
@ -1526,16 +1529,22 @@ where
|
|||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
let pm_token = router_data
|
let payment_method_token_response = router_data
|
||||||
.add_payment_method_token(state, &connector, &tokenization_action)
|
.add_payment_method_token(
|
||||||
|
state,
|
||||||
|
&connector,
|
||||||
|
&tokenization_action,
|
||||||
|
should_continue_further,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
if let Some(payment_method_token) = pm_token.clone() {
|
|
||||||
router_data.payment_method_token = Some(
|
let mut should_continue_further =
|
||||||
hyperswitch_domain_models::router_data::PaymentMethodToken::Token(Secret::new(
|
tokenization::update_router_data_with_payment_method_token_result(
|
||||||
payment_method_token,
|
payment_method_token_response,
|
||||||
)),
|
&mut router_data,
|
||||||
|
is_retry_payment,
|
||||||
|
should_continue_further,
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
(router_data, should_continue_further) = complete_preprocessing_steps_if_required(
|
(router_data, should_continue_further) = complete_preprocessing_steps_if_required(
|
||||||
state,
|
state,
|
||||||
|
|||||||
@ -84,13 +84,17 @@ pub trait Feature<F, T> {
|
|||||||
_state: &SessionState,
|
_state: &SessionState,
|
||||||
_connector: &api::ConnectorData,
|
_connector: &api::ConnectorData,
|
||||||
_tokenization_action: &payments::TokenizationAction,
|
_tokenization_action: &payments::TokenizationAction,
|
||||||
) -> RouterResult<Option<String>>
|
_should_continue_payment: bool,
|
||||||
|
) -> RouterResult<types::PaymentMethodTokenResult>
|
||||||
where
|
where
|
||||||
F: Clone,
|
F: Clone,
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
|
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>(
|
async fn preprocessing_steps<'a>(
|
||||||
|
|||||||
@ -140,7 +140,8 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
|||||||
state: &SessionState,
|
state: &SessionState,
|
||||||
connector: &api::ConnectorData,
|
connector: &api::ConnectorData,
|
||||||
tokenization_action: &payments::TokenizationAction,
|
tokenization_action: &payments::TokenizationAction,
|
||||||
) -> RouterResult<Option<String>> {
|
should_continue_payment: bool,
|
||||||
|
) -> RouterResult<types::PaymentMethodTokenResult> {
|
||||||
let request = self.request.clone();
|
let request = self.request.clone();
|
||||||
tokenization::add_payment_method_token(
|
tokenization::add_payment_method_token(
|
||||||
state,
|
state,
|
||||||
@ -148,6 +149,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
|||||||
tokenization_action,
|
tokenization_action,
|
||||||
self,
|
self,
|
||||||
types::PaymentMethodTokenizationData::try_from(request)?,
|
types::PaymentMethodTokenizationData::try_from(request)?,
|
||||||
|
should_continue_payment,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@ -103,7 +103,8 @@ impl Feature<api::CompleteAuthorize, types::CompleteAuthorizeData>
|
|||||||
state: &SessionState,
|
state: &SessionState,
|
||||||
connector: &api::ConnectorData,
|
connector: &api::ConnectorData,
|
||||||
_tokenization_action: &payments::TokenizationAction,
|
_tokenization_action: &payments::TokenizationAction,
|
||||||
) -> RouterResult<Option<String>> {
|
should_continue_payment: bool,
|
||||||
|
) -> RouterResult<types::PaymentMethodTokenResult> {
|
||||||
// TODO: remove this and handle it in core
|
// TODO: remove this and handle it in core
|
||||||
if matches!(connector.connector_name, types::Connector::Payme) {
|
if matches!(connector.connector_name, types::Connector::Payme) {
|
||||||
let request = self.request.clone();
|
let request = self.request.clone();
|
||||||
@ -113,10 +114,14 @@ impl Feature<api::CompleteAuthorize, types::CompleteAuthorizeData>
|
|||||||
&payments::TokenizationAction::TokenizeInConnector,
|
&payments::TokenizationAction::TokenizeInConnector,
|
||||||
self,
|
self,
|
||||||
types::PaymentMethodTokenizationData::try_from(request)?,
|
types::PaymentMethodTokenizationData::try_from(request)?,
|
||||||
|
should_continue_payment,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
} else {
|
} 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,
|
state: &SessionState,
|
||||||
connector: &api::ConnectorData,
|
connector: &api::ConnectorData,
|
||||||
tokenization_action: &payments::TokenizationAction,
|
tokenization_action: &payments::TokenizationAction,
|
||||||
) -> RouterResult<Option<String>> {
|
should_continue_payment: bool,
|
||||||
|
) -> RouterResult<types::PaymentMethodTokenResult> {
|
||||||
let request = self.request.clone();
|
let request = self.request.clone();
|
||||||
tokenization::add_payment_method_token(
|
tokenization::add_payment_method_token(
|
||||||
state,
|
state,
|
||||||
@ -115,6 +116,7 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
|
|||||||
tokenization_action,
|
tokenization_action,
|
||||||
self,
|
self,
|
||||||
types::PaymentMethodTokenizationData::try_from(request)?,
|
types::PaymentMethodTokenizationData::try_from(request)?,
|
||||||
|
should_continue_payment,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@ -321,6 +321,7 @@ where
|
|||||||
api::HeaderPayload::default(),
|
api::HeaderPayload::default(),
|
||||||
frm_suggestion,
|
frm_suggestion,
|
||||||
business_profile,
|
business_profile,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@ -784,7 +784,9 @@ pub async fn add_payment_method_token<F: Clone, T: types::Tokenizable + Clone>(
|
|||||||
tokenization_action: &payments::TokenizationAction,
|
tokenization_action: &payments::TokenizationAction,
|
||||||
router_data: &mut types::RouterData<F, T, types::PaymentsResponseData>,
|
router_data: &mut types::RouterData<F, T, types::PaymentsResponseData>,
|
||||||
pm_token_request_data: types::PaymentMethodTokenizationData,
|
pm_token_request_data: types::PaymentMethodTokenizationData,
|
||||||
) -> RouterResult<Option<String>> {
|
should_continue_payment: bool,
|
||||||
|
) -> RouterResult<types::PaymentMethodTokenResult> {
|
||||||
|
if should_continue_payment {
|
||||||
match tokenization_action {
|
match tokenization_action {
|
||||||
payments::TokenizationAction::TokenizeInConnector
|
payments::TokenizationAction::TokenizeInConnector
|
||||||
| payments::TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt(_) => {
|
| payments::TokenizationAction::TokenizeInConnectorAndApplepayPreDecrypt(_) => {
|
||||||
@ -794,8 +796,10 @@ pub async fn add_payment_method_token<F: Clone, T: types::Tokenizable + Clone>(
|
|||||||
types::PaymentsResponseData,
|
types::PaymentsResponseData,
|
||||||
> = connector.connector.get_connector_integration();
|
> = connector.connector.get_connector_integration();
|
||||||
|
|
||||||
let pm_token_response_data: Result<types::PaymentsResponseData, types::ErrorResponse> =
|
let pm_token_response_data: Result<
|
||||||
Err(types::ErrorResponse::default());
|
types::PaymentsResponseData,
|
||||||
|
types::ErrorResponse,
|
||||||
|
> = Err(types::ErrorResponse::default());
|
||||||
|
|
||||||
let pm_token_router_data =
|
let pm_token_router_data =
|
||||||
helpers::router_data_type_conversion::<_, api::PaymentMethodToken, _, _, _, _>(
|
helpers::router_data_type_conversion::<_, api::PaymentMethodToken, _, _, _, _>(
|
||||||
@ -827,19 +831,62 @@ pub async fn add_payment_method_token<F: Clone, T: types::Tokenizable + Clone>(
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
let pm_token = match resp.response {
|
let payment_token_resp = resp.response.map(|res| {
|
||||||
Ok(response) => match response {
|
if let types::PaymentsResponseData::TokenizationResponse { token } = res {
|
||||||
types::PaymentsResponseData::TokenizationResponse { token } => Some(token),
|
Some(token)
|
||||||
_ => None,
|
} else {
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
logger::debug!(payment_method_tokenization_error=?err);
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
Ok(pm_token)
|
|
||||||
|
Ok(types::PaymentMethodTokenResult {
|
||||||
|
payment_method_token_result: payment_token_resp,
|
||||||
|
is_payment_method_tokenization_performed: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ => Ok(None),
|
_ => Ok(types::PaymentMethodTokenResult {
|
||||||
|
payment_method_token_result: Ok(None),
|
||||||
|
is_payment_method_tokenization_performed: false,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
} 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -545,6 +545,11 @@ pub struct AddAccessTokenResult {
|
|||||||
pub connector_supports_access_token: bool,
|
pub connector_supports_access_token: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PaymentMethodTokenResult {
|
||||||
|
pub payment_method_token_result: Result<Option<String>, ErrorResponse>,
|
||||||
|
pub is_payment_method_tokenization_performed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Redirection {
|
pub enum Redirection {
|
||||||
Redirect,
|
Redirect,
|
||||||
|
|||||||
Reference in New Issue
Block a user