diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 10d091a3b8..53850e97af 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -33,7 +33,6 @@ use crate::{ storage::enums, transformers::{ForeignFrom, ForeignTryFrom}, }, - unimplemented_payment_method, utils::OptionExt, }; @@ -1761,9 +1760,11 @@ impl TryFrom<(&types::PaymentsAuthorizeRouterData, MinorUnit)> for PaymentIntent let payment_method_token = match payment_method_token { types::PaymentMethodToken::Token(payment_method_token) => payment_method_token, - types::PaymentMethodToken::ApplePayDecrypt(_) => Err( - unimplemented_payment_method!("Apple Pay", "Simplified", "Stripe"), - )?, + types::PaymentMethodToken::ApplePayDecrypt(_) => { + Err(errors::ConnectorError::InvalidWalletToken { + wallet_name: "Apple Pay".to_string(), + })? + } }; Some(StripePaymentMethodData::Wallet( StripeWallet::ApplepayPayment(ApplepayPayment { diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 8290177f21..69c55b152e 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -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( header_payload: HeaderPayload, frm_suggestion: Option, business_profile: &storage::business_profile::BusinessProfile, + is_retry_payment: bool, ) -> RouterResult> 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, diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 9ecd0c4c8a..23ab253ee5 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -84,13 +84,17 @@ pub trait Feature { _state: &SessionState, _connector: &api::ConnectorData, _tokenization_action: &payments::TokenizationAction, - ) -> RouterResult> + _should_continue_payment: bool, + ) -> RouterResult where F: Clone, Self: Sized, dyn api::Connector: services::ConnectorIntegration, { - Ok(None) + Ok(types::PaymentMethodTokenResult { + payment_method_token_result: Ok(None), + is_payment_method_tokenization_performed: false, + }) } async fn preprocessing_steps<'a>( diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 23971f3696..c06d9a70e1 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -140,7 +140,8 @@ impl Feature for types::PaymentsAu state: &SessionState, connector: &api::ConnectorData, tokenization_action: &payments::TokenizationAction, - ) -> RouterResult> { + should_continue_payment: bool, + ) -> RouterResult { let request = self.request.clone(); tokenization::add_payment_method_token( state, @@ -148,6 +149,7 @@ impl Feature for types::PaymentsAu tokenization_action, self, types::PaymentMethodTokenizationData::try_from(request)?, + should_continue_payment, ) .await } diff --git a/crates/router/src/core/payments/flows/complete_authorize_flow.rs b/crates/router/src/core/payments/flows/complete_authorize_flow.rs index bb56cb5a7c..6bef7e9585 100644 --- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs +++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs @@ -103,7 +103,8 @@ impl Feature state: &SessionState, connector: &api::ConnectorData, _tokenization_action: &payments::TokenizationAction, - ) -> RouterResult> { + should_continue_payment: bool, + ) -> RouterResult { // 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 &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, + }) } } diff --git a/crates/router/src/core/payments/flows/setup_mandate_flow.rs b/crates/router/src/core/payments/flows/setup_mandate_flow.rs index 07803a0c0f..773677f3e7 100644 --- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs +++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs @@ -107,7 +107,8 @@ impl Feature for types::Setup state: &SessionState, connector: &api::ConnectorData, tokenization_action: &payments::TokenizationAction, - ) -> RouterResult> { + should_continue_payment: bool, + ) -> RouterResult { let request = self.request.clone(); tokenization::add_payment_method_token( state, @@ -115,6 +116,7 @@ impl Feature for types::Setup tokenization_action, self, types::PaymentMethodTokenizationData::try_from(request)?, + should_continue_payment, ) .await } diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index e8ceeaa7ec..ef8e0c01fe 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -321,6 +321,7 @@ where api::HeaderPayload::default(), frm_suggestion, business_profile, + true, ) .await } diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index d6ceeee65e..da18fd7dd5 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -784,62 +784,109 @@ pub async fn add_payment_method_token( tokenization_action: &payments::TokenizationAction, router_data: &mut types::RouterData, pm_token_request_data: types::PaymentMethodTokenizationData, -) -> RouterResult> { - 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 { + 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 = - 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( + payment_method_token_result: types::PaymentMethodTokenResult, + router_data: &mut types::RouterData, + 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 } } diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 607e5bd823..f0087ef2f3 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -545,6 +545,11 @@ pub struct AddAccessTokenResult { pub connector_supports_access_token: bool, } +pub struct PaymentMethodTokenResult { + pub payment_method_token_result: Result, ErrorResponse>, + pub is_payment_method_tokenization_performed: bool, +} + #[derive(Debug, Clone, Copy)] pub enum Redirection { Redirect,