From d942a31d60595d366977746be7215620da0ababd Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Wed, 22 May 2024 20:53:05 +0530 Subject: [PATCH] feat(payment_methods): enable auto-retries for apple pay (#4721) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/router/src/core/payments.rs | 22 ++++ .../src/core/payments/flows/session_flow.rs | 39 ++---- crates/router/src/core/payments/helpers.rs | 118 ++++++++++++++++++ 3 files changed, 148 insertions(+), 31 deletions(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 175400c54bf1..9b2082cf31fd 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3170,6 +3170,28 @@ where { routing_data.business_sub_label = choice.sub_label.clone(); } + + if payment_data.payment_attempt.payment_method_type + == Some(storage_enums::PaymentMethodType::ApplePay) + { + let retryable_connector_data = helpers::get_apple_pay_retryable_connectors( + state, + merchant_account, + payment_data, + key_store, + connector_data.clone(), + #[cfg(feature = "connector_choice_mca_id")] + choice.merchant_connector_id.clone().as_ref(), + #[cfg(not(feature = "connector_choice_mca_id"))] + None, + ) + .await?; + + if let Some(connector_data_list) = retryable_connector_data { + return Ok(ConnectorCallType::Retryable(connector_data_list)); + } + } + return Ok(ConnectorCallType::PreDetermined(connector_data)); } } diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index 2eb0c921bbf2..76ff96c70215 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -122,36 +122,6 @@ fn is_dynamic_fields_required( .unwrap_or(false) } -fn get_applepay_metadata( - connector_metadata: Option, -) -> RouterResult { - connector_metadata - .clone() - .parse_value::( - "ApplepayCombinedSessionTokenData", - ) - .map(|combined_metadata| { - api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( - combined_metadata.apple_pay_combined, - ) - }) - .or_else(|_| { - connector_metadata - .parse_value::( - "ApplepaySessionTokenData", - ) - .map(|old_metadata| { - api_models::payments::ApplepaySessionTokenMetadata::ApplePay( - old_metadata.apple_pay, - ) - }) - }) - .change_context(errors::ApiErrorResponse::InvalidDataFormat { - field_name: "connector_metadata".to_string(), - expected_format: "applepay_metadata_format".to_string(), - }) -} - fn build_apple_pay_session_request( state: &routes::AppState, request: payment_types::ApplepaySessionRequest, @@ -196,7 +166,8 @@ async fn create_applepay_session_token( ) } else { // Get the apple pay metadata - let apple_pay_metadata = get_applepay_metadata(router_data.connector_meta_data.clone())?; + let apple_pay_metadata = + helpers::get_applepay_metadata(router_data.connector_meta_data.clone())?; // Get payment request data , apple pay session request and merchant keys let ( @@ -213,6 +184,8 @@ async fn create_applepay_session_token( payment_request_data, session_token_data, } => { + logger::info!("Apple pay simplified flow"); + let merchant_identifier = state .conf .applepay_merchant_configs @@ -254,6 +227,8 @@ async fn create_applepay_session_token( payment_request_data, session_token_data, } => { + logger::info!("Apple pay manual flow"); + let apple_pay_session_request = get_session_request_for_manual_apple_pay(session_token_data.clone()); @@ -269,6 +244,8 @@ async fn create_applepay_session_token( } }, payment_types::ApplepaySessionTokenMetadata::ApplePay(apple_pay_metadata) => { + logger::info!("Apple pay manual flow"); + let apple_pay_session_request = get_session_request_for_manual_apple_pay( apple_pay_metadata.session_token_data.clone(), ); diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index eb3dca110603..06c8c7f2c947 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3888,6 +3888,122 @@ pub fn validate_customer_access( Ok(()) } +pub fn is_apple_pay_simplified_flow( + connector_metadata: Option, +) -> CustomResult { + let apple_pay_metadata = get_applepay_metadata(connector_metadata)?; + + Ok(match apple_pay_metadata { + api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( + apple_pay_combined_metadata, + ) => match apple_pay_combined_metadata { + api_models::payments::ApplePayCombinedMetadata::Simplified { .. } => true, + api_models::payments::ApplePayCombinedMetadata::Manual { .. } => false, + }, + api_models::payments::ApplepaySessionTokenMetadata::ApplePay(_) => false, + }) +} + +pub fn get_applepay_metadata( + connector_metadata: Option, +) -> RouterResult { + connector_metadata + .clone() + .parse_value::( + "ApplepayCombinedSessionTokenData", + ) + .map(|combined_metadata| { + api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( + combined_metadata.apple_pay_combined, + ) + }) + .or_else(|_| { + connector_metadata + .parse_value::( + "ApplepaySessionTokenData", + ) + .map(|old_metadata| { + api_models::payments::ApplepaySessionTokenMetadata::ApplePay( + old_metadata.apple_pay, + ) + }) + }) + .change_context(errors::ApiErrorResponse::InvalidDataFormat { + field_name: "connector_metadata".to_string(), + expected_format: "applepay_metadata_format".to_string(), + }) +} + +pub async fn get_apple_pay_retryable_connectors( + state: AppState, + merchant_account: &domain::MerchantAccount, + payment_data: &mut PaymentData, + key_store: &domain::MerchantKeyStore, + decided_connector_data: api::ConnectorData, + merchant_connector_id: Option<&String>, +) -> CustomResult>, errors::ApiErrorResponse> +where + F: Send + Clone, +{ + let profile_id = &payment_data + .payment_intent + .profile_id + .clone() + .get_required_value("profile_id") + .change_context(errors::ApiErrorResponse::MissingRequiredField { + field_name: "profile_id", + })?; + + let merchant_connector_account = get_merchant_connector_account( + &state, + merchant_account.merchant_id.as_str(), + payment_data.creds_identifier.to_owned(), + key_store, + profile_id, // need to fix this + &decided_connector_data.connector_name.to_string(), + merchant_connector_id, + ) + .await? + .get_metadata(); + + let connector_data_list = if is_apple_pay_simplified_flow(merchant_connector_account)? { + let merchant_connector_account_list = state + .store + .find_merchant_connector_account_by_merchant_id_and_disabled_list( + merchant_account.merchant_id.as_str(), + true, + key_store, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?; + + let mut connector_data_list = vec![decided_connector_data.clone()]; + + for merchant_connector_account in merchant_connector_account_list { + if is_apple_pay_simplified_flow(merchant_connector_account.metadata)? { + let connector_data = api::ConnectorData::get_connector_by_name( + &state.conf.connectors, + &merchant_connector_account.connector_name.to_string(), + api::GetToken::Connector, + Some(merchant_connector_account.merchant_connector_id), + ) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Invalid connector name received")?; + + if !connector_data_list.iter().any(|connector_details| { + connector_details.merchant_connector_id == connector_data.merchant_connector_id + }) { + connector_data_list.push(connector_data) + } + } + } + Some(connector_data_list) + } else { + None + }; + Ok(connector_data_list) +} + #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct ApplePayData { version: masking::Secret, @@ -4040,6 +4156,8 @@ impl ApplePayData { &self, symmetric_key: &[u8], ) -> CustomResult { + logger::info!("Decrypt apple pay token"); + let data = BASE64_ENGINE .decode(self.data.peek().as_bytes()) .change_context(errors::ApplePayDecryptionError::Base64DecodingFailed)?;