mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	feat(payment_methods): enable auto-retries for apple pay (#4721)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
		| @ -3170,6 +3170,28 @@ where | |||||||
|             { |             { | ||||||
|                 routing_data.business_sub_label = choice.sub_label.clone(); |                 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)); |             return Ok(ConnectorCallType::PreDetermined(connector_data)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -122,36 +122,6 @@ fn is_dynamic_fields_required( | |||||||
|         .unwrap_or(false) |         .unwrap_or(false) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn get_applepay_metadata( |  | ||||||
|     connector_metadata: Option<common_utils::pii::SecretSerdeValue>, |  | ||||||
| ) -> RouterResult<payment_types::ApplepaySessionTokenMetadata> { |  | ||||||
|     connector_metadata |  | ||||||
|         .clone() |  | ||||||
|         .parse_value::<api_models::payments::ApplepayCombinedSessionTokenData>( |  | ||||||
|             "ApplepayCombinedSessionTokenData", |  | ||||||
|         ) |  | ||||||
|         .map(|combined_metadata| { |  | ||||||
|             api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( |  | ||||||
|                 combined_metadata.apple_pay_combined, |  | ||||||
|             ) |  | ||||||
|         }) |  | ||||||
|         .or_else(|_| { |  | ||||||
|             connector_metadata |  | ||||||
|                 .parse_value::<api_models::payments::ApplepaySessionTokenData>( |  | ||||||
|                     "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( | fn build_apple_pay_session_request( | ||||||
|     state: &routes::AppState, |     state: &routes::AppState, | ||||||
|     request: payment_types::ApplepaySessionRequest, |     request: payment_types::ApplepaySessionRequest, | ||||||
| @ -196,7 +166,8 @@ async fn create_applepay_session_token( | |||||||
|         ) |         ) | ||||||
|     } else { |     } else { | ||||||
|         // Get the apple pay metadata |         // 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 |         // Get payment request data , apple pay session request and merchant keys | ||||||
|         let ( |         let ( | ||||||
| @ -213,6 +184,8 @@ async fn create_applepay_session_token( | |||||||
|                     payment_request_data, |                     payment_request_data, | ||||||
|                     session_token_data, |                     session_token_data, | ||||||
|                 } => { |                 } => { | ||||||
|  |                     logger::info!("Apple pay simplified flow"); | ||||||
|  |  | ||||||
|                     let merchant_identifier = state |                     let merchant_identifier = state | ||||||
|                         .conf |                         .conf | ||||||
|                         .applepay_merchant_configs |                         .applepay_merchant_configs | ||||||
| @ -254,6 +227,8 @@ async fn create_applepay_session_token( | |||||||
|                     payment_request_data, |                     payment_request_data, | ||||||
|                     session_token_data, |                     session_token_data, | ||||||
|                 } => { |                 } => { | ||||||
|  |                     logger::info!("Apple pay manual flow"); | ||||||
|  |  | ||||||
|                     let apple_pay_session_request = |                     let apple_pay_session_request = | ||||||
|                         get_session_request_for_manual_apple_pay(session_token_data.clone()); |                         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) => { |             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( |                 let apple_pay_session_request = get_session_request_for_manual_apple_pay( | ||||||
|                     apple_pay_metadata.session_token_data.clone(), |                     apple_pay_metadata.session_token_data.clone(), | ||||||
|                 ); |                 ); | ||||||
|  | |||||||
| @ -3888,6 +3888,122 @@ pub fn validate_customer_access( | |||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub fn is_apple_pay_simplified_flow( | ||||||
|  |     connector_metadata: Option<pii::SecretSerdeValue>, | ||||||
|  | ) -> CustomResult<bool, errors::ApiErrorResponse> { | ||||||
|  |     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<pii::SecretSerdeValue>, | ||||||
|  | ) -> RouterResult<api_models::payments::ApplepaySessionTokenMetadata> { | ||||||
|  |     connector_metadata | ||||||
|  |         .clone() | ||||||
|  |         .parse_value::<api_models::payments::ApplepayCombinedSessionTokenData>( | ||||||
|  |             "ApplepayCombinedSessionTokenData", | ||||||
|  |         ) | ||||||
|  |         .map(|combined_metadata| { | ||||||
|  |             api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( | ||||||
|  |                 combined_metadata.apple_pay_combined, | ||||||
|  |             ) | ||||||
|  |         }) | ||||||
|  |         .or_else(|_| { | ||||||
|  |             connector_metadata | ||||||
|  |                 .parse_value::<api_models::payments::ApplepaySessionTokenData>( | ||||||
|  |                     "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<F>( | ||||||
|  |     state: AppState, | ||||||
|  |     merchant_account: &domain::MerchantAccount, | ||||||
|  |     payment_data: &mut PaymentData<F>, | ||||||
|  |     key_store: &domain::MerchantKeyStore, | ||||||
|  |     decided_connector_data: api::ConnectorData, | ||||||
|  |     merchant_connector_id: Option<&String>, | ||||||
|  | ) -> CustomResult<Option<Vec<api::ConnectorData>>, 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)] | #[derive(Debug, serde::Serialize, serde::Deserialize)] | ||||||
| pub struct ApplePayData { | pub struct ApplePayData { | ||||||
|     version: masking::Secret<String>, |     version: masking::Secret<String>, | ||||||
| @ -4040,6 +4156,8 @@ impl ApplePayData { | |||||||
|         &self, |         &self, | ||||||
|         symmetric_key: &[u8], |         symmetric_key: &[u8], | ||||||
|     ) -> CustomResult<String, errors::ApplePayDecryptionError> { |     ) -> CustomResult<String, errors::ApplePayDecryptionError> { | ||||||
|  |         logger::info!("Decrypt apple pay token"); | ||||||
|  |  | ||||||
|         let data = BASE64_ENGINE |         let data = BASE64_ENGINE | ||||||
|             .decode(self.data.peek().as_bytes()) |             .decode(self.data.peek().as_bytes()) | ||||||
|             .change_context(errors::ApplePayDecryptionError::Base64DecodingFailed)?; |             .change_context(errors::ApplePayDecryptionError::Base64DecodingFailed)?; | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Shankar Singh C
					Shankar Singh C