mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 10:06:32 +08:00 
			
		
		
		
	feat: applepay through trustpay (#1422)
Co-authored-by: dracarys18 <karthikey.hegde@juspay.in>
This commit is contained in:
		 Sangamesh Kulkarni
					Sangamesh Kulkarni
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							641995371d
						
					
				
				
					commit
					8032e0290b
				
			| @ -657,6 +657,9 @@ pub enum StripeNextAction { | ||||
|     DisplayBankTransferInformation { | ||||
|         bank_transfer_steps_and_charges_details: payments::BankTransferNextStepsData, | ||||
|     }, | ||||
|     ThirdPartySdkSessionToken { | ||||
|         session_token: Option<payments::SessionToken>, | ||||
|     }, | ||||
| } | ||||
|  | ||||
| pub(crate) fn into_stripe_next_action( | ||||
| @ -677,5 +680,8 @@ pub(crate) fn into_stripe_next_action( | ||||
|         } => StripeNextAction::DisplayBankTransferInformation { | ||||
|             bank_transfer_steps_and_charges_details, | ||||
|         }, | ||||
|         payments::NextActionData::ThirdPartySdkSessionToken { session_token } => { | ||||
|             StripeNextAction::ThirdPartySdkSessionToken { session_token } | ||||
|         } | ||||
|     }) | ||||
| } | ||||
|  | ||||
| @ -317,6 +317,9 @@ pub enum StripeNextAction { | ||||
|     DisplayBankTransferInformation { | ||||
|         bank_transfer_steps_and_charges_details: payments::BankTransferNextStepsData, | ||||
|     }, | ||||
|     ThirdPartySdkSessionToken { | ||||
|         session_token: Option<payments::SessionToken>, | ||||
|     }, | ||||
| } | ||||
|  | ||||
| pub(crate) fn into_stripe_next_action( | ||||
| @ -337,6 +340,9 @@ pub(crate) fn into_stripe_next_action( | ||||
|         } => StripeNextAction::DisplayBankTransferInformation { | ||||
|             bank_transfer_steps_and_charges_details, | ||||
|         }, | ||||
|         payments::NextActionData::ThirdPartySdkSessionToken { session_token } => { | ||||
|             StripeNextAction::ThirdPartySdkSessionToken { session_token } | ||||
|         } | ||||
|     }) | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -83,6 +83,7 @@ pub struct Settings { | ||||
|     pub dummy_connector: DummyConnector, | ||||
|     #[cfg(feature = "email")] | ||||
|     pub email: EmailSettings, | ||||
|     pub delayed_session_response: DelayedSessionConfig, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize, Clone, Default)] | ||||
| @ -507,6 +508,27 @@ pub struct FileUploadConfig { | ||||
|     pub bucket_name: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize, Clone, Default)] | ||||
| pub struct DelayedSessionConfig { | ||||
|     #[serde(deserialize_with = "delayed_session_deser")] | ||||
|     pub connectors_with_delayed_session_response: HashSet<api_models::enums::Connector>, | ||||
| } | ||||
|  | ||||
| fn delayed_session_deser<'a, D>( | ||||
|     deserializer: D, | ||||
| ) -> Result<HashSet<api_models::enums::Connector>, D::Error> | ||||
| where | ||||
|     D: Deserializer<'a>, | ||||
| { | ||||
|     let value = <String>::deserialize(deserializer)?; | ||||
|     value | ||||
|         .trim() | ||||
|         .split(',') | ||||
|         .map(api_models::enums::Connector::from_str) | ||||
|         .collect::<Result<_, _>>() | ||||
|         .map_err(D::Error::custom) | ||||
| } | ||||
|  | ||||
| impl Settings { | ||||
|     pub fn new() -> ApplicationResult<Self> { | ||||
|         Self::with_config_path(None) | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| use api_models::enums as api_enums; | ||||
| use api_models::{enums as api_enums, payments}; | ||||
| use base64::Engine; | ||||
| use common_utils::{ | ||||
|     errors::CustomResult, | ||||
| @ -321,11 +321,12 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons | ||||
|         let wallet_token = consts::BASE64_ENGINE | ||||
|             .decode(response.wallet_token.clone().expose()) | ||||
|             .into_report() | ||||
|             .change_context(errors::ConnectorError::ParsingFailed)?; | ||||
|             .change_context(errors::ConnectorError::ResponseHandlingFailed)?; | ||||
|  | ||||
|         let session_response: api_models::payments::ApplePaySessionResponse = wallet_token[..] | ||||
|             .parse_struct("ApplePayResponse") | ||||
|             .change_context(errors::ConnectorError::ParsingFailed)?; | ||||
|         let session_response: api_models::payments::NoThirdPartySdkSessionResponse = | ||||
|             wallet_token[..] | ||||
|                 .parse_struct("NoThirdPartySdkSessionResponse") | ||||
|                 .change_context(errors::ConnectorError::ParsingFailed)?; | ||||
|  | ||||
|         let metadata = item.data.get_connector_meta()?.expose(); | ||||
|         let applepay_metadata = metadata | ||||
| @ -338,13 +339,16 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons | ||||
|             response: Ok(types::PaymentsResponseData::SessionResponse { | ||||
|                 session_token: types::api::SessionToken::ApplePay(Box::new( | ||||
|                     api_models::payments::ApplepaySessionTokenResponse { | ||||
|                         session_token_data: session_response, | ||||
|                         payment_request_data: api_models::payments::ApplePayPaymentRequest { | ||||
|                         session_token_data: | ||||
|                             api_models::payments::ApplePaySessionResponse::NoThirdPartySdk( | ||||
|                                 session_response, | ||||
|                             ), | ||||
|                         payment_request_data: Some(api_models::payments::ApplePayPaymentRequest { | ||||
|                             country_code: item.data.get_billing_country()?, | ||||
|                             currency_code: item.data.request.currency.to_string(), | ||||
|                             total: api_models::payments::AmountInfo { | ||||
|                                 label: applepay_metadata.data.payment_request_data.label, | ||||
|                                 total_type: "final".to_string(), | ||||
|                                 total_type: Some("final".to_string()), | ||||
|                                 amount: item.data.request.amount.to_string(), | ||||
|                             }, | ||||
|                             merchant_capabilities: applepay_metadata | ||||
| @ -355,12 +359,20 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons | ||||
|                                 .data | ||||
|                                 .payment_request_data | ||||
|                                 .supported_networks, | ||||
|                             merchant_identifier: applepay_metadata | ||||
|                                 .data | ||||
|                                 .session_token_data | ||||
|                                 .merchant_identifier, | ||||
|                         }, | ||||
|                             merchant_identifier: Some( | ||||
|                                 applepay_metadata | ||||
|                                     .data | ||||
|                                     .session_token_data | ||||
|                                     .merchant_identifier, | ||||
|                             ), | ||||
|                         }), | ||||
|                         connector: "bluesnap".to_string(), | ||||
|                         delayed_session_token: false, | ||||
|                         sdk_next_action: { | ||||
|                             payments::SdkNextAction { | ||||
|                                 next_action: payments::NextActionCall::Confirm, | ||||
|                             } | ||||
|                         }, | ||||
|                     }, | ||||
|                 )), | ||||
|             }), | ||||
|  | ||||
| @ -2167,8 +2167,11 @@ impl<F, T> | ||||
|         }; | ||||
|         Ok(Self { | ||||
|             response: Ok(types::PaymentsResponseData::PreProcessingResponse { | ||||
|                 pre_processing_id: item.response.id, | ||||
|                 pre_processing_id: types::PreprocessingResponseId::PreProcessingId( | ||||
|                     item.response.id, | ||||
|                 ), | ||||
|                 connector_metadata: Some(connector_metadata), | ||||
|                 session_token: None, | ||||
|             }), | ||||
|             status, | ||||
|             ..item.data | ||||
|  | ||||
| @ -343,6 +343,102 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme | ||||
| { | ||||
| } | ||||
|  | ||||
| impl api::PaymentsPreProcessing for Trustpay {} | ||||
|  | ||||
| impl | ||||
|     ConnectorIntegration< | ||||
|         api::PreProcessing, | ||||
|         types::PaymentsPreProcessingData, | ||||
|         types::PaymentsResponseData, | ||||
|     > for Trustpay | ||||
| { | ||||
|     fn get_headers( | ||||
|         &self, | ||||
|         req: &types::PaymentsPreProcessingRouterData, | ||||
|         _connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> { | ||||
|         let mut header = vec![( | ||||
|             headers::CONTENT_TYPE.to_string(), | ||||
|             types::PaymentsPreProcessingType::get_content_type(self) | ||||
|                 .to_string() | ||||
|                 .into(), | ||||
|         )]; | ||||
|         let mut api_key = self.get_auth_header(&req.connector_auth_type)?; | ||||
|         header.append(&mut api_key); | ||||
|         Ok(header) | ||||
|     } | ||||
|  | ||||
|     fn get_content_type(&self) -> &'static str { | ||||
|         self.common_get_content_type() | ||||
|     } | ||||
|  | ||||
|     fn get_url( | ||||
|         &self, | ||||
|         _req: &types::PaymentsPreProcessingRouterData, | ||||
|         connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<String, errors::ConnectorError> { | ||||
|         Ok(format!("{}{}", self.base_url(connectors), "api/v1/intent")) | ||||
|     } | ||||
|  | ||||
|     fn get_request_body( | ||||
|         &self, | ||||
|         req: &types::PaymentsPreProcessingRouterData, | ||||
|     ) -> CustomResult<Option<String>, errors::ConnectorError> { | ||||
|         let create_intent_req = trustpay::TrustpayCreateIntentRequest::try_from(req)?; | ||||
|         let trustpay_req = | ||||
|             utils::Encode::<trustpay::TrustpayCreateIntentRequest>::url_encode(&create_intent_req) | ||||
|                 .change_context(errors::ConnectorError::RequestEncodingFailed)?; | ||||
|         Ok(Some(trustpay_req)) | ||||
|     } | ||||
|  | ||||
|     fn build_request( | ||||
|         &self, | ||||
|         req: &types::PaymentsPreProcessingRouterData, | ||||
|         connectors: &settings::Connectors, | ||||
|     ) -> CustomResult<Option<services::Request>, errors::ConnectorError> { | ||||
|         let req = Some( | ||||
|             services::RequestBuilder::new() | ||||
|                 .method(services::Method::Post) | ||||
|                 .attach_default_headers() | ||||
|                 .headers(types::PaymentsPreProcessingType::get_headers( | ||||
|                     self, req, connectors, | ||||
|                 )?) | ||||
|                 .url(&types::PaymentsPreProcessingType::get_url( | ||||
|                     self, req, connectors, | ||||
|                 )?) | ||||
|                 .body(types::PaymentsPreProcessingType::get_request_body( | ||||
|                     self, req, | ||||
|                 )?) | ||||
|                 .build(), | ||||
|         ); | ||||
|         Ok(req) | ||||
|     } | ||||
|  | ||||
|     fn handle_response( | ||||
|         &self, | ||||
|         data: &types::PaymentsPreProcessingRouterData, | ||||
|         res: Response, | ||||
|     ) -> CustomResult<types::PaymentsPreProcessingRouterData, errors::ConnectorError> { | ||||
|         let response: trustpay::TrustpayCreateIntentResponse = res | ||||
|             .response | ||||
|             .parse_struct("TrustpayCreateIntentResponse") | ||||
|             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; | ||||
|         types::RouterData::try_from(types::ResponseRouterData { | ||||
|             response, | ||||
|             data: data.clone(), | ||||
|             http_code: res.status_code, | ||||
|         }) | ||||
|         .change_context(errors::ConnectorError::ResponseHandlingFailed) | ||||
|     } | ||||
|  | ||||
|     fn get_error_response( | ||||
|         &self, | ||||
|         res: Response, | ||||
|     ) -> CustomResult<ErrorResponse, errors::ConnectorError> { | ||||
|         self.build_error_response(res) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl api::PaymentSession for Trustpay {} | ||||
|  | ||||
| impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData> | ||||
|  | ||||
| @ -16,6 +16,7 @@ use crate::{ | ||||
|     core::errors, | ||||
|     services, | ||||
|     types::{self, api, storage::enums, BrowserInformation}, | ||||
|     utils::OptionExt, | ||||
| }; | ||||
|  | ||||
| type Error = error_stack::Report<errors::ConnectorError>; | ||||
| @ -474,7 +475,7 @@ pub struct PaymentsResponseCards { | ||||
|     pub status: i64, | ||||
|     pub description: Option<String>, | ||||
|     pub instance_id: String, | ||||
|     pub payment_status: String, | ||||
|     pub payment_status: Option<String>, | ||||
|     pub payment_description: Option<String>, | ||||
|     pub redirect_url: Option<Url>, | ||||
|     pub redirect_params: Option<HashMap<String, String>>, | ||||
| @ -553,8 +554,13 @@ fn handle_cards_response( | ||||
|     ), | ||||
|     errors::ConnectorError, | ||||
| > { | ||||
|     // By default, payment status is pending(000.200.000 status code) | ||||
|     let (status, msg) = get_transaction_status( | ||||
|         response.payment_status.as_str(), | ||||
|         response | ||||
|             .payment_status | ||||
|             .to_owned() | ||||
|             .unwrap_or("000.200.000".to_string()) | ||||
|             .as_str(), | ||||
|         response.redirect_url.clone(), | ||||
|     )?; | ||||
|     let form_fields = response.redirect_params.unwrap_or_default(); | ||||
| @ -567,7 +573,9 @@ fn handle_cards_response( | ||||
|         }); | ||||
|     let error = if msg.is_some() { | ||||
|         Some(types::ErrorResponse { | ||||
|             code: response.payment_status, | ||||
|             code: response | ||||
|                 .payment_status | ||||
|                 .unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()), | ||||
|             message: msg.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()), | ||||
|             reason: None, | ||||
|             status_code, | ||||
| @ -802,6 +810,164 @@ impl<F, T> TryFrom<types::ResponseRouterData<F, TrustpayAuthUpdateResponse, T, t | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Serialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct TrustpayCreateIntentRequest { | ||||
|     pub amount: String, | ||||
|     pub currency: String, | ||||
|     // If true, Apple Pay will be initialized | ||||
|     pub init_apple_pay: Option<bool>, | ||||
| } | ||||
|  | ||||
| impl TryFrom<&types::PaymentsSessionRouterData> for TrustpayCreateIntentRequest { | ||||
|     type Error = Error; | ||||
|     fn try_from(item: &types::PaymentsSessionRouterData) -> Result<Self, Self::Error> { | ||||
|         Ok(Self { | ||||
|             amount: item.request.amount.to_string(), | ||||
|             currency: item.request.currency.to_string(), | ||||
|             init_apple_pay: Some(true), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl TryFrom<&types::PaymentsPreProcessingRouterData> for TrustpayCreateIntentRequest { | ||||
|     type Error = Error; | ||||
|     fn try_from(item: &types::PaymentsPreProcessingRouterData) -> Result<Self, Self::Error> { | ||||
|         Ok(Self { | ||||
|             amount: item | ||||
|                 .request | ||||
|                 .amount | ||||
|                 .get_required_value("amount") | ||||
|                 .change_context(errors::ConnectorError::MissingRequiredField { | ||||
|                     field_name: "amount", | ||||
|                 })? | ||||
|                 .to_string(), | ||||
|             currency: item | ||||
|                 .request | ||||
|                 .currency | ||||
|                 .get_required_value("currency") | ||||
|                 .change_context(errors::ConnectorError::MissingRequiredField { | ||||
|                     field_name: "currency", | ||||
|                 })? | ||||
|                 .to_string(), | ||||
|             init_apple_pay: Some(true), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct TrustpayCreateIntentResponse { | ||||
|     // TrustPay's authorization secrets used by client | ||||
|     pub secrets: SdkSecretInfo, | ||||
|     // 	Data object to be used for Apple Pay | ||||
|     pub apple_init_result_data: TrustpayApplePayResponse, | ||||
|     // Unique operation/transaction identifier | ||||
|     pub instance_id: String, | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct SdkSecretInfo { | ||||
|     pub display: Secret<String>, | ||||
|     pub payment: Secret<String>, | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct TrustpayApplePayResponse { | ||||
|     pub country_code: api_models::enums::CountryAlpha2, | ||||
|     pub currency_code: String, | ||||
|     pub supported_networks: Vec<String>, | ||||
|     pub merchant_capabilities: Vec<String>, | ||||
|     pub total: ApplePayTotalInfo, | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct ApplePayTotalInfo { | ||||
|     pub label: String, | ||||
|     pub amount: String, | ||||
| } | ||||
|  | ||||
| impl<F, T> | ||||
|     TryFrom< | ||||
|         types::ResponseRouterData<F, TrustpayCreateIntentResponse, T, types::PaymentsResponseData>, | ||||
|     > for types::RouterData<F, T, types::PaymentsResponseData> | ||||
| { | ||||
|     type Error = Error; | ||||
|     fn try_from( | ||||
|         item: types::ResponseRouterData< | ||||
|             F, | ||||
|             TrustpayCreateIntentResponse, | ||||
|             T, | ||||
|             types::PaymentsResponseData, | ||||
|         >, | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         let response = item.response; | ||||
|  | ||||
|         Ok(Self { | ||||
|             response: Ok(types::PaymentsResponseData::PreProcessingResponse { | ||||
|                 connector_metadata: None, | ||||
|                 pre_processing_id: types::PreprocessingResponseId::ConnectorTransactionId( | ||||
|                     response.instance_id, | ||||
|                 ), | ||||
|                 session_token: Some(types::api::SessionToken::ApplePay(Box::new( | ||||
|                     api_models::payments::ApplepaySessionTokenResponse { | ||||
|                         session_token_data: | ||||
|                             api_models::payments::ApplePaySessionResponse::ThirdPartySdk( | ||||
|                                 api_models::payments::ThirdPartySdkSessionResponse { | ||||
|                                     secrets: response.secrets.into(), | ||||
|                                 }, | ||||
|                             ), | ||||
|                         payment_request_data: Some(api_models::payments::ApplePayPaymentRequest { | ||||
|                             country_code: response.apple_init_result_data.country_code, | ||||
|                             currency_code: response.apple_init_result_data.currency_code.clone(), | ||||
|                             supported_networks: response | ||||
|                                 .apple_init_result_data | ||||
|                                 .supported_networks | ||||
|                                 .clone(), | ||||
|                             merchant_capabilities: response | ||||
|                                 .apple_init_result_data | ||||
|                                 .merchant_capabilities | ||||
|                                 .clone(), | ||||
|                             total: response.apple_init_result_data.total.into(), | ||||
|                             merchant_identifier: None, | ||||
|                         }), | ||||
|                         connector: "trustpay".to_string(), | ||||
|                         delayed_session_token: true, | ||||
|                         sdk_next_action: { | ||||
|                             api_models::payments::SdkNextAction { | ||||
|                                 next_action: api_models::payments::NextActionCall::Sync, | ||||
|                             } | ||||
|                         }, | ||||
|                     }, | ||||
|                 ))), | ||||
|             }), | ||||
|             ..item.data | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<SdkSecretInfo> for api_models::payments::SecretInfoToInitiateSdk { | ||||
|     fn from(value: SdkSecretInfo) -> Self { | ||||
|         Self { | ||||
|             display: value.display, | ||||
|             payment: value.payment, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<ApplePayTotalInfo> for api_models::payments::AmountInfo { | ||||
|     fn from(value: ApplePayTotalInfo) -> Self { | ||||
|         Self { | ||||
|             label: value.label, | ||||
|             amount: value.amount, | ||||
|             total_type: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Serialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct TrustpayRefundRequestCards { | ||||
|  | ||||
| @ -156,7 +156,7 @@ where | ||||
|                     &merchant_account, | ||||
|                     connector, | ||||
|                     &operation, | ||||
|                     &payment_data, | ||||
|                     &mut payment_data, | ||||
|                     &customer, | ||||
|                     call_connector_action, | ||||
|                     tokenization_action, | ||||
| @ -399,6 +399,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize { | ||||
|                     .and_then(|next_action_data| match next_action_data { | ||||
|                         api_models::payments::NextActionData::RedirectToUrl { redirect_to_url } => Some(redirect_to_url), | ||||
|                         api_models::payments::NextActionData::DisplayBankTransferInformation { .. } => None, | ||||
|                         api_models::payments::NextActionData::ThirdPartySdkSessionToken { .. } => None | ||||
|                     }) | ||||
|                     .ok_or(errors::ApiErrorResponse::InternalServerError) | ||||
|                     .into_report() | ||||
| @ -489,7 +490,7 @@ pub async fn call_connector_service<F, Op, Req>( | ||||
|     merchant_account: &domain::MerchantAccount, | ||||
|     connector: api::ConnectorData, | ||||
|     _operation: &Op, | ||||
|     payment_data: &PaymentData<F>, | ||||
|     payment_data: &mut PaymentData<F>, | ||||
|     customer: &Option<domain::Customer>, | ||||
|     call_connector_action: CallConnectorAction, | ||||
|     tokenization_action: TokenizationAction, | ||||
| @ -542,6 +543,14 @@ where | ||||
|     ) | ||||
|     .await?; | ||||
|  | ||||
|     if let Ok(types::PaymentsResponseData::PreProcessingResponse { | ||||
|         session_token: Some(session_token), | ||||
|         .. | ||||
|     }) = router_data.response.to_owned() | ||||
|     { | ||||
|         payment_data.sessions_token.push(session_token); | ||||
|     }; | ||||
|  | ||||
|     let router_data_res = if should_continue_payment { | ||||
|         router_data | ||||
|             .decide_flows( | ||||
| @ -590,7 +599,6 @@ where | ||||
|  | ||||
|     for session_connector_data in connectors.iter() { | ||||
|         let connector_id = session_connector_data.connector.connector.id(); | ||||
|  | ||||
|         let router_data = payment_data | ||||
|             .construct_router_data(state, connector_id, merchant_account, customer) | ||||
|             .await?; | ||||
| @ -612,10 +620,17 @@ where | ||||
|         let connector_name = session_connector.connector.connector_name.to_string(); | ||||
|         match connector_res { | ||||
|             Ok(connector_response) => { | ||||
|                 if let Ok(types::PaymentsResponseData::SessionResponse { session_token }) = | ||||
|                 if let Ok(types::PaymentsResponseData::SessionResponse { session_token, .. }) = | ||||
|                     connector_response.response | ||||
|                 { | ||||
|                     payment_data.sessions_token.push(session_token); | ||||
|                     // If session token is NoSessionTokenReceived, it is not pushed into the sessions_token as there is no response or there can be some error | ||||
|                     // In case of error, that error is already logged | ||||
|                     if !matches!( | ||||
|                         session_token, | ||||
|                         api_models::payments::SessionToken::NoSessionTokenReceived, | ||||
|                     ) { | ||||
|                         payment_data.sessions_token.push(session_token); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             Err(connector_error) => { | ||||
| @ -716,17 +731,17 @@ where | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn complete_preprocessing_steps_if_required<F, Req, Res>( | ||||
| async fn complete_preprocessing_steps_if_required<F, Req>( | ||||
|     state: &AppState, | ||||
|     connector: &api::ConnectorData, | ||||
|     payment_data: &PaymentData<F>, | ||||
|     router_data: types::RouterData<F, Req, Res>, | ||||
|     router_data: types::RouterData<F, Req, types::PaymentsResponseData>, | ||||
|     should_continue_payment: bool, | ||||
| ) -> RouterResult<(types::RouterData<F, Req, Res>, bool)> | ||||
| ) -> RouterResult<(types::RouterData<F, Req, types::PaymentsResponseData>, bool)> | ||||
| where | ||||
|     F: Send + Clone + Sync, | ||||
|     Req: Send + Sync, | ||||
|     types::RouterData<F, Req, Res>: Feature<F, Req> + Send, | ||||
|     types::RouterData<F, Req, types::PaymentsResponseData>: Feature<F, Req> + Send, | ||||
|     dyn api::Connector: services::api::ConnectorIntegration<F, Req, types::PaymentsResponseData>, | ||||
| { | ||||
|     //TODO: For ACH transfers, if preprocessing_step is not required for connectors encountered in future, add the check | ||||
| @ -744,6 +759,16 @@ where | ||||
|             } | ||||
|             _ => (router_data, should_continue_payment), | ||||
|         }, | ||||
|         Some(api_models::payments::PaymentMethodData::Wallet(_)) => { | ||||
|             if connector.connector_name.to_string() == *"trustpay" { | ||||
|                 ( | ||||
|                     router_data.preprocessing_steps(state, connector).await?, | ||||
|                     false, | ||||
|                 ) | ||||
|             } else { | ||||
|                 (router_data, should_continue_payment) | ||||
|             } | ||||
|         } | ||||
|         _ => (router_data, should_continue_payment), | ||||
|     }; | ||||
|  | ||||
| @ -869,7 +894,6 @@ where | ||||
|                 .payment_method | ||||
|                 .get_required_value("payment_method")?; | ||||
|             let payment_method_type = &payment_data.payment_attempt.payment_method_type; | ||||
|  | ||||
|             let is_connector_tokenization_enabled = | ||||
|                 is_payment_method_tokenization_enabled_for_connector( | ||||
|                     state, | ||||
|  | ||||
| @ -647,7 +647,6 @@ default_imp_for_pre_processing_steps!( | ||||
|     connector::Payu, | ||||
|     connector::Rapyd, | ||||
|     connector::Shift4, | ||||
|     connector::Trustpay, | ||||
|     connector::Worldline, | ||||
|     connector::Worldpay, | ||||
|     connector::Zen | ||||
|  | ||||
| @ -337,6 +337,7 @@ impl TryFrom<types::PaymentsAuthorizeData> for types::PaymentsPreProcessingData | ||||
|         Ok(Self { | ||||
|             email: data.email, | ||||
|             currency: Some(data.currency), | ||||
|             amount: Some(data.amount), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| use api_models::payments as payment_types; | ||||
| use async_trait::async_trait; | ||||
| use common_utils::ext_traits::ByteSliceExt; | ||||
| use error_stack::{report, ResultExt}; | ||||
| use error_stack::{Report, ResultExt}; | ||||
|  | ||||
| use super::{ConstructFlowSpecificData, Feature}; | ||||
| use crate::{ | ||||
| @ -10,7 +10,7 @@ use crate::{ | ||||
|         errors::{self, ConnectorErrorExt, RouterResult}, | ||||
|         payments::{self, access_token, transformers, PaymentData}, | ||||
|     }, | ||||
|     headers, | ||||
|     headers, logger, | ||||
|     routes::{self, metrics}, | ||||
|     services, | ||||
|     types::{self, api, domain}, | ||||
| @ -78,18 +78,22 @@ impl Feature<api::Session, types::PaymentsSessionData> for types::PaymentsSessio | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn mk_applepay_session_request( | ||||
|     state: &routes::AppState, | ||||
|     router_data: &types::PaymentsSessionRouterData, | ||||
| ) -> RouterResult<(services::Request, payment_types::ApplepaySessionTokenData)> { | ||||
|     let connector_metadata = router_data.connector_meta_data.clone(); | ||||
|  | ||||
|     let applepay_metadata = connector_metadata | ||||
| fn get_applepay_metadata( | ||||
|     connector_metadata: Option<common_utils::pii::SecretSerdeValue>, | ||||
| ) -> RouterResult<payment_types::ApplepaySessionTokenData> { | ||||
|     connector_metadata | ||||
|         .parse_value::<payment_types::ApplepaySessionTokenData>("ApplepaySessionTokenData") | ||||
|         .change_context(errors::ApiErrorResponse::InvalidDataFormat { | ||||
|             field_name: "connector_metadata".to_string(), | ||||
|             expected_format: "applepay_metadata_format".to_string(), | ||||
|         })?; | ||||
|         }) | ||||
| } | ||||
|  | ||||
| fn mk_applepay_session_request( | ||||
|     state: &routes::AppState, | ||||
|     router_data: &types::PaymentsSessionRouterData, | ||||
| ) -> RouterResult<services::Request> { | ||||
|     let applepay_metadata = get_applepay_metadata(router_data.connector_meta_data.clone())?; | ||||
|     let request = payment_types::ApplepaySessionRequest { | ||||
|         merchant_identifier: applepay_metadata | ||||
|             .data | ||||
| @ -134,14 +138,10 @@ fn mk_applepay_session_request( | ||||
|                 .clone(), | ||||
|         )) | ||||
|         .add_certificate_key(Some( | ||||
|             applepay_metadata | ||||
|                 .data | ||||
|                 .session_token_data | ||||
|                 .certificate_keys | ||||
|                 .clone(), | ||||
|             applepay_metadata.data.session_token_data.certificate_keys, | ||||
|         )) | ||||
|         .build(); | ||||
|     Ok((session_request, applepay_metadata)) | ||||
|     Ok(session_request) | ||||
| } | ||||
|  | ||||
| async fn create_applepay_session_token( | ||||
| @ -149,83 +149,138 @@ async fn create_applepay_session_token( | ||||
|     router_data: &types::PaymentsSessionRouterData, | ||||
|     connector: &api::ConnectorData, | ||||
| ) -> RouterResult<types::PaymentsSessionRouterData> { | ||||
|     let (applepay_session_request, applepay_metadata) = | ||||
|         mk_applepay_session_request(state, router_data)?; | ||||
|     let response = services::call_connector_api(state, applepay_session_request) | ||||
|         .await | ||||
|         .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|         .attach_printable("Failure in calling connector api")?; | ||||
|     let session_response: payment_types::ApplePaySessionResponse = match response { | ||||
|         Ok(resp) => resp | ||||
|             .response | ||||
|             .parse_struct("ApplePaySessionResponse") | ||||
|             .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|             .attach_printable("Failed to parse ApplePaySessionResponse struct"), | ||||
|         Err(err) => { | ||||
|             let error_response: payment_types::ApplepayErrorResponse = err | ||||
|                 .response | ||||
|                 .parse_struct("ApplepayErrorResponse") | ||||
|                 .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|                 .attach_printable("Failed to parse ApplepayErrorResponse struct")?; | ||||
|             Err( | ||||
|                 report!(errors::ApiErrorResponse::InternalServerError).attach_printable(format!( | ||||
|                     "Failed with {} status code and the error response is {:?}", | ||||
|                     err.status_code, error_response | ||||
|                 )), | ||||
|             ) | ||||
|         } | ||||
|     }?; | ||||
|     let connectors_with_delayed_response = &state | ||||
|         .conf | ||||
|         .delayed_session_response | ||||
|         .connectors_with_delayed_session_response; | ||||
|  | ||||
|     let amount_info = payment_types::AmountInfo { | ||||
|         label: applepay_metadata.data.payment_request_data.label, | ||||
|         total_type: "final".to_string(), | ||||
|         amount: connector::utils::to_currency_base_unit( | ||||
|             router_data.request.amount, | ||||
|             router_data.request.currency, | ||||
|     let connector_name = connector.connector_name; | ||||
|     let delayed_response = connectors_with_delayed_response.contains(&connector_name); | ||||
|  | ||||
|     if delayed_response { | ||||
|         let delayed_response_apple_pay_session = | ||||
|             Some(payment_types::ApplePaySessionResponse::NoSessionResponse); | ||||
|         create_apple_pay_session_response( | ||||
|             router_data, | ||||
|             delayed_response_apple_pay_session, | ||||
|             None, // Apple pay payment request will be none for delayed session response | ||||
|             connector_name.to_string(), | ||||
|             delayed_response, | ||||
|             payment_types::NextActionCall::Confirm, | ||||
|         ) | ||||
|         .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|         .attach_printable("Failed to convert currency to base unit")?, | ||||
|     }; | ||||
|     } else { | ||||
|         let applepay_metadata = get_applepay_metadata(router_data.connector_meta_data.clone())?; | ||||
|  | ||||
|     let applepay_payment_request = payment_types::ApplePayPaymentRequest { | ||||
|         country_code: router_data | ||||
|             .request | ||||
|             .country | ||||
|             .to_owned() | ||||
|             .get_required_value("country_code") | ||||
|             .change_context(errors::ApiErrorResponse::MissingRequiredField { | ||||
|                 field_name: "country_code", | ||||
|         let amount_info = payment_types::AmountInfo { | ||||
|             label: applepay_metadata.data.payment_request_data.label, | ||||
|             total_type: Some("final".to_string()), | ||||
|             amount: connector::utils::to_currency_base_unit( | ||||
|                 router_data.request.amount, | ||||
|                 router_data.request.currency, | ||||
|             ) | ||||
|             .change_context(errors::ApiErrorResponse::PreconditionFailed { | ||||
|                 message: "Failed to convert currency to base unit".to_string(), | ||||
|             })?, | ||||
|         currency_code: router_data.request.currency.to_string(), | ||||
|         total: amount_info, | ||||
|         merchant_capabilities: applepay_metadata | ||||
|             .data | ||||
|             .payment_request_data | ||||
|             .merchant_capabilities, | ||||
|         supported_networks: applepay_metadata | ||||
|             .data | ||||
|             .payment_request_data | ||||
|             .supported_networks, | ||||
|         merchant_identifier: applepay_metadata | ||||
|             .data | ||||
|             .session_token_data | ||||
|             .merchant_identifier, | ||||
|     }; | ||||
|         }; | ||||
|  | ||||
|     let response_router_data = types::PaymentsSessionRouterData { | ||||
|         response: Ok(types::PaymentsResponseData::SessionResponse { | ||||
|             session_token: payment_types::SessionToken::ApplePay(Box::new( | ||||
|                 payment_types::ApplepaySessionTokenResponse { | ||||
|                     session_token_data: session_response, | ||||
|                     payment_request_data: applepay_payment_request, | ||||
|                     connector: connector.connector_name.to_string(), | ||||
|                 }, | ||||
|             )), | ||||
|         let applepay_payment_request = payment_types::ApplePayPaymentRequest { | ||||
|             country_code: router_data | ||||
|                 .request | ||||
|                 .country | ||||
|                 .to_owned() | ||||
|                 .get_required_value("country_code") | ||||
|                 .change_context(errors::ApiErrorResponse::MissingRequiredField { | ||||
|                     field_name: "country_code", | ||||
|                 })?, | ||||
|             currency_code: router_data.request.currency.to_string(), | ||||
|             total: amount_info, | ||||
|             merchant_capabilities: applepay_metadata | ||||
|                 .data | ||||
|                 .payment_request_data | ||||
|                 .merchant_capabilities, | ||||
|             supported_networks: applepay_metadata | ||||
|                 .data | ||||
|                 .payment_request_data | ||||
|                 .supported_networks, | ||||
|             merchant_identifier: Some( | ||||
|                 applepay_metadata | ||||
|                     .data | ||||
|                     .session_token_data | ||||
|                     .merchant_identifier, | ||||
|             ), | ||||
|         }; | ||||
|  | ||||
|         let applepay_session_request = mk_applepay_session_request(state, router_data)?; | ||||
|         let response = services::call_connector_api(state, applepay_session_request).await; | ||||
|  | ||||
|         // logging the error if present in session call response | ||||
|         log_session_response_if_error(&response); | ||||
|  | ||||
|         let apple_pay_session_response = response | ||||
|             .ok() | ||||
|             .and_then(|apple_pay_res| { | ||||
|                 apple_pay_res | ||||
|                     .map(|res| { | ||||
|                         let response: Result< | ||||
|                             payment_types::NoThirdPartySdkSessionResponse, | ||||
|                             Report<common_utils::errors::ParsingError>, | ||||
|                         > = res.response.parse_struct("NoThirdPartySdkSessionResponse"); | ||||
|  | ||||
|                         // logging the parsing failed error | ||||
|                         if let Err(error) = response.as_ref() { | ||||
|                             logger::error!(?error); | ||||
|                         }; | ||||
|  | ||||
|                         response.ok() | ||||
|                     }) | ||||
|                     .ok() | ||||
|             }) | ||||
|             .flatten(); | ||||
|  | ||||
|         let session_response = | ||||
|             apple_pay_session_response.map(payment_types::ApplePaySessionResponse::NoThirdPartySdk); | ||||
|  | ||||
|         create_apple_pay_session_response( | ||||
|             router_data, | ||||
|             session_response, | ||||
|             Some(applepay_payment_request), | ||||
|             connector_name.to_string(), | ||||
|             delayed_response, | ||||
|             payment_types::NextActionCall::Confirm, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn create_apple_pay_session_response( | ||||
|     router_data: &types::PaymentsSessionRouterData, | ||||
|     session_response: Option<payment_types::ApplePaySessionResponse>, | ||||
|     apple_pay_payment_request: Option<payment_types::ApplePayPaymentRequest>, | ||||
|     connector_name: String, | ||||
|     delayed_response: bool, | ||||
|     next_action: payment_types::NextActionCall, | ||||
| ) -> RouterResult<types::PaymentsSessionRouterData> { | ||||
|     match session_response { | ||||
|         Some(response) => Ok(types::PaymentsSessionRouterData { | ||||
|             response: Ok(types::PaymentsResponseData::SessionResponse { | ||||
|                 session_token: payment_types::SessionToken::ApplePay(Box::new( | ||||
|                     payment_types::ApplepaySessionTokenResponse { | ||||
|                         session_token_data: response, | ||||
|                         payment_request_data: apple_pay_payment_request, | ||||
|                         connector: connector_name, | ||||
|                         delayed_session_token: delayed_response, | ||||
|                         sdk_next_action: { payment_types::SdkNextAction { next_action } }, | ||||
|                     }, | ||||
|                 )), | ||||
|             }), | ||||
|             ..router_data.clone() | ||||
|         }), | ||||
|         ..router_data.clone() | ||||
|     }; | ||||
|  | ||||
|     Ok(response_router_data) | ||||
|         None => Ok(types::PaymentsSessionRouterData { | ||||
|             response: Ok(types::PaymentsResponseData::SessionResponse { | ||||
|                 session_token: payment_types::SessionToken::NoSessionTokenReceived, | ||||
|             }), | ||||
|             ..router_data.clone() | ||||
|         }), | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn create_gpay_session_token( | ||||
| @ -278,6 +333,18 @@ fn create_gpay_session_token( | ||||
|     Ok(response_router_data) | ||||
| } | ||||
|  | ||||
| fn log_session_response_if_error( | ||||
|     response: &Result<Result<types::Response, types::Response>, Report<errors::ApiClientError>>, | ||||
| ) { | ||||
|     if let Err(error) = response.as_ref() { | ||||
|         logger::error!(?error); | ||||
|     }; | ||||
|     response | ||||
|         .as_ref() | ||||
|         .ok() | ||||
|         .map(|res| res.as_ref().map_err(|error| logger::error!(?error))); | ||||
| } | ||||
|  | ||||
| impl types::PaymentsSessionRouterData { | ||||
|     pub async fn decide_flow<'a, 'b>( | ||||
|         &'b self, | ||||
|  | ||||
| @ -315,13 +315,28 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>( | ||||
|             types::PaymentsResponseData::PreProcessingResponse { | ||||
|                 pre_processing_id, | ||||
|                 connector_metadata, | ||||
|                 .. | ||||
|             } => { | ||||
|                 let connector_transaction_id = match pre_processing_id.to_owned() { | ||||
|                     types::PreprocessingResponseId::PreProcessingId(_) => None, | ||||
|                     types::PreprocessingResponseId::ConnectorTransactionId(connector_txn_id) => { | ||||
|                         Some(connector_txn_id) | ||||
|                     } | ||||
|                 }; | ||||
|                 let preprocessing_step_id = match pre_processing_id { | ||||
|                     types::PreprocessingResponseId::PreProcessingId(pre_processing_id) => { | ||||
|                         Some(pre_processing_id) | ||||
|                     } | ||||
|                     types::PreprocessingResponseId::ConnectorTransactionId(_) => None, | ||||
|                 }; | ||||
|                 let payment_attempt_update = storage::PaymentAttemptUpdate::PreprocessingUpdate { | ||||
|                     status: router_data.status, | ||||
|                     payment_method_id: Some(router_data.payment_method_id), | ||||
|                     connector_metadata, | ||||
|                     preprocessing_step_id: Some(pre_processing_id), | ||||
|                     preprocessing_step_id, | ||||
|                     connector_transaction_id, | ||||
|                 }; | ||||
|  | ||||
|                 (Some(payment_attempt_update), None) | ||||
|             } | ||||
|             types::PaymentsResponseData::TransactionResponse { | ||||
|  | ||||
| @ -377,15 +377,15 @@ where | ||||
|         for (connector, payment_method_type, business_sub_label) in | ||||
|             connector_and_supporting_payment_method_type | ||||
|         { | ||||
|             if let Ok(connector_data) = api::ConnectorData::get_connector_by_name( | ||||
|                 connectors, | ||||
|                 &connector, | ||||
|                 api::GetToken::from(payment_method_type), | ||||
|             ) | ||||
|             .map_err(|err| { | ||||
|                 logger::error!(session_token_error=?err); | ||||
|                 err | ||||
|             }) { | ||||
|             let connector_type = | ||||
|                 get_connector_type_for_session_token(payment_method_type, request, &connector); | ||||
|             if let Ok(connector_data) = | ||||
|                 api::ConnectorData::get_connector_by_name(connectors, &connector, connector_type) | ||||
|                     .map_err(|err| { | ||||
|                         logger::error!(session_token_error=?err); | ||||
|                         err | ||||
|                     }) | ||||
|             { | ||||
|                 session_connector_data.push(api::SessionConnectorData { | ||||
|                     payment_method_type, | ||||
|                     connector: connector_data, | ||||
| @ -412,11 +412,11 @@ impl From<api_models::enums::PaymentMethodType> for api::GetToken { | ||||
|  | ||||
| pub fn get_connector_type_for_session_token( | ||||
|     payment_method_type: api_models::enums::PaymentMethodType, | ||||
|     _request: &api::PaymentsSessionRequest, | ||||
|     connector: String, | ||||
|     request: &api::PaymentsSessionRequest, | ||||
|     connector: &str, | ||||
| ) -> api::GetToken { | ||||
|     if payment_method_type == api_models::enums::PaymentMethodType::ApplePay { | ||||
|         if connector == *"bluesnap" { | ||||
|         if is_apple_pay_get_token_connector(connector, request) { | ||||
|             api::GetToken::Connector | ||||
|         } else { | ||||
|             api::GetToken::ApplePayMetadata | ||||
| @ -425,3 +425,11 @@ pub fn get_connector_type_for_session_token( | ||||
|         api::GetToken::from(payment_method_type) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn is_apple_pay_get_token_connector( | ||||
|     connector: &str, | ||||
|     _request: &api::PaymentsSessionRequest, | ||||
| ) -> bool { | ||||
|     // Add connectors here, which all are required to hit connector for session call | ||||
|     matches!(connector, "bluesnap") | ||||
| } | ||||
|  | ||||
| @ -169,6 +169,7 @@ where | ||||
|             payment_data.connector_response.authentication_data, | ||||
|             &operation, | ||||
|             payment_data.ephemeral_key, | ||||
|             payment_data.sessions_token, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @ -260,6 +261,7 @@ pub fn payments_to_payments_response<R, Op>( | ||||
|     redirection_data: Option<serde_json::Value>, | ||||
|     operation: &Op, | ||||
|     ephemeral_key_option: Option<ephemeral_key::EphemeralKey>, | ||||
|     session_tokens: Vec<api::SessionToken>, | ||||
| ) -> RouterResponse<api::PaymentsResponse> | ||||
| where | ||||
|     Op: Debug, | ||||
| @ -337,6 +339,15 @@ where | ||||
|                         })); | ||||
|                 }; | ||||
|  | ||||
|                 // next action check for third party sdk session (for ex: Apple pay through trustpay has third party sdk session response) | ||||
|                 if third_party_sdk_session_next_action(&payment_attempt, operation) { | ||||
|                     next_action_response = Some( | ||||
|                         api_models::payments::NextActionData::ThirdPartySdkSessionToken { | ||||
|                             session_token: session_tokens.get(0).cloned(), | ||||
|                         }, | ||||
|                     ) | ||||
|                 } | ||||
|  | ||||
|                 let mut response: api::PaymentsResponse = Default::default(); | ||||
|                 let routed_through = payment_attempt.connector.clone(); | ||||
|  | ||||
| @ -514,6 +525,34 @@ where | ||||
|     output | ||||
| } | ||||
|  | ||||
| pub fn third_party_sdk_session_next_action<Op>( | ||||
|     payment_attempt: &storage::PaymentAttempt, | ||||
|     operation: &Op, | ||||
| ) -> bool | ||||
| where | ||||
|     Op: Debug, | ||||
| { | ||||
|     // If the operation is confirm, we will send session token response in next action | ||||
|     if format!("{operation:?}").eq("PaymentConfirm") { | ||||
|         payment_attempt | ||||
|             .connector | ||||
|             .as_ref() | ||||
|             .map(|connector| matches!(connector.as_str(), "trustpay")) | ||||
|             .and_then(|is_connector_supports_third_party_sdk| { | ||||
|                 if is_connector_supports_third_party_sdk { | ||||
|                     payment_attempt | ||||
|                         .payment_method | ||||
|                         .map(|pm| matches!(pm, storage_models::enums::PaymentMethod::Wallet)) | ||||
|                 } else { | ||||
|                     Some(false) | ||||
|                 } | ||||
|             }) | ||||
|             .unwrap_or(false) | ||||
|     } else { | ||||
|         false | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse { | ||||
|     fn foreign_from(item: (storage::PaymentIntent, storage::PaymentAttempt)) -> Self { | ||||
|         let pi = item.0; | ||||
| @ -900,6 +939,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsPreProce | ||||
|         Ok(Self { | ||||
|             email: payment_data.email, | ||||
|             currency: Some(payment_data.currency), | ||||
|             amount: Some(payment_data.amount.into()), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -201,6 +201,9 @@ Never share your secret api keys. Keep them guarded and secure. | ||||
|         api_models::payments::PaymentsSessionResponse, | ||||
|         api_models::payments::SessionToken, | ||||
|         api_models::payments::ApplePaySessionResponse, | ||||
|         api_models::payments::ThirdPartySdkSessionResponse, | ||||
|         api_models::payments::NoThirdPartySdkSessionResponse, | ||||
|         api_models::payments::SecretInfoToInitiateSdk, | ||||
|         api_models::payments::ApplePayPaymentRequest, | ||||
|         api_models::payments::AmountInfo, | ||||
|         api_models::payments::GooglePayWalletData, | ||||
| @ -216,6 +219,8 @@ Never share your secret api keys. Keep them guarded and secure. | ||||
|         api_models::payments::KlarnaSessionTokenResponse, | ||||
|         api_models::payments::PaypalSessionTokenResponse, | ||||
|         api_models::payments::ApplepaySessionTokenResponse, | ||||
|         api_models::payments::SdkNextAction, | ||||
|         api_models::payments::NextActionCall, | ||||
|         api_models::payments::GpayTokenizationData, | ||||
|         api_models::payments::GooglePayPaymentMethodInfo, | ||||
|         api_models::payments::ApplePayWalletData, | ||||
| @ -231,6 +236,7 @@ Never share your secret api keys. Keep them guarded and secure. | ||||
|         api_models::payments::ReceiverDetails, | ||||
|         api_models::payments::AchTransfer, | ||||
|         api_models::payments::ApplePayRedirectData, | ||||
|         api_models::payments::ApplePayThirdPartySdkData, | ||||
|         api_models::payments::GooglePayRedirectData, | ||||
|         api_models::payments::SepaBankTransferInstructions, | ||||
|         api_models::payments::BacsBankTransferInstructions, | ||||
|  | ||||
| @ -270,6 +270,7 @@ pub struct PaymentMethodTokenizationData { | ||||
| pub struct PaymentsPreProcessingData { | ||||
|     pub email: Option<Email>, | ||||
|     pub currency: Option<storage_enums::Currency>, | ||||
|     pub amount: Option<i64>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| @ -425,11 +426,18 @@ pub enum PaymentsResponseData { | ||||
|         related_transaction_id: Option<String>, | ||||
|     }, | ||||
|     PreProcessingResponse { | ||||
|         pre_processing_id: String, | ||||
|         pre_processing_id: PreprocessingResponseId, | ||||
|         connector_metadata: Option<serde_json::Value>, | ||||
|         session_token: Option<api::SessionToken>, | ||||
|     }, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum PreprocessingResponseId { | ||||
|     PreProcessingId(String), | ||||
|     ConnectorTransactionId(String), | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Default)] | ||||
| pub enum ResponseId { | ||||
|     ConnectorTransactionId(String), | ||||
|  | ||||
		Reference in New Issue
	
	Block a user