From 5f40eee3fa264390ea6ac7feaca7737d83dccb3a Mon Sep 17 00:00:00 2001 From: Prajjwal Kumar Date: Thu, 25 Apr 2024 13:31:43 +0530 Subject: [PATCH] Refactor(core): make save_payment_method as post_update_tracker trait function (#4307) Co-authored-by: Narayan Bhat Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- .../src/payments/payment_attempt.rs | 4 + crates/diesel_models/src/payment_attempt.rs | 12 + crates/router/src/core/mandate.rs | 254 +++++++-------- crates/router/src/core/payments.rs | 51 +-- crates/router/src/core/payments/flows.rs | 4 - .../src/core/payments/flows/approve_flow.rs | 4 - .../src/core/payments/flows/authorize_flow.rs | 99 +----- .../src/core/payments/flows/cancel_flow.rs | 4 - .../src/core/payments/flows/capture_flow.rs | 4 - .../payments/flows/complete_authorize_flow.rs | 4 - .../flows/incremental_authorization_flow.rs | 4 - .../src/core/payments/flows/psync_flow.rs | 4 - .../src/core/payments/flows/reject_flow.rs | 4 - .../src/core/payments/flows/session_flow.rs | 15 +- .../core/payments/flows/setup_mandate_flow.rs | 304 +----------------- crates/router/src/core/payments/helpers.rs | 15 +- crates/router/src/core/payments/operations.rs | 18 +- .../payments/operations/payment_response.rs | 281 ++++++++++++++-- .../router/src/core/payments/tokenization.rs | 98 +++--- .../src/payments/payment_attempt.rs | 14 + 20 files changed, 543 insertions(+), 654 deletions(-) diff --git a/crates/data_models/src/payments/payment_attempt.rs b/crates/data_models/src/payments/payment_attempt.rs index ec0e087316..0c16563204 100644 --- a/crates/data_models/src/payments/payment_attempt.rs +++ b/crates/data_models/src/payments/payment_attempt.rs @@ -338,6 +338,10 @@ pub enum PaymentAttemptUpdate { error_message: Option>, updated_by: String, }, + PaymentMethodDetailsUpdate { + payment_method_id: Option, + updated_by: String, + }, VoidUpdate { status: storage_enums::AttemptStatus, cancellation_reason: Option, diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs index 6ad21eab12..603e0f4ebf 100644 --- a/crates/diesel_models/src/payment_attempt.rs +++ b/crates/diesel_models/src/payment_attempt.rs @@ -237,6 +237,10 @@ pub enum PaymentAttemptUpdate { cancellation_reason: Option, updated_by: String, }, + PaymentMethodDetailsUpdate { + payment_method_id: Option, + updated_by: String, + }, BlocklistUpdate { status: storage_enums::AttemptStatus, error_code: Option>, @@ -644,6 +648,14 @@ impl From for PaymentAttemptUpdateInternal { merchant_connector_id: Some(None), ..Default::default() }, + PaymentAttemptUpdate::PaymentMethodDetailsUpdate { + payment_method_id, + updated_by, + } => Self { + payment_method_id, + updated_by, + ..Default::default() + }, PaymentAttemptUpdate::ResponseUpdate { status, connector, diff --git a/crates/router/src/core/mandate.rs b/crates/router/src/core/mandate.rs index b54091e492..84aa9b48e6 100644 --- a/crates/router/src/core/mandate.rs +++ b/crates/router/src/core/mandate.rs @@ -340,156 +340,134 @@ where .change_context(errors::ApiErrorResponse::MandateUpdateFailed)?; Ok(resp) } + pub async fn mandate_procedure( state: &AppState, - mut resp: types::RouterData, - maybe_customer: &Option, + resp: &types::RouterData, + customer_id: &Option, pm_id: Option, merchant_connector_id: Option, storage_scheme: MerchantStorageScheme, -) -> errors::RouterResult> +) -> errors::RouterResult> where FData: MandateBehaviour, { - match resp.response { - Err(_) => {} - Ok(_) => match resp.request.get_mandate_id() { - Some(mandate_id) => { - if let Some(ref mandate_id) = mandate_id.mandate_id { - let orig_mandate = state - .store - .find_mandate_by_merchant_id_mandate_id( - resp.merchant_id.as_ref(), - mandate_id, - storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?; - let mandate = match orig_mandate.mandate_type { - storage_enums::MandateType::SingleUse => state - .store - .update_mandate_by_merchant_id_mandate_id( - &resp.merchant_id, - mandate_id, - storage::MandateUpdate::StatusUpdate { - mandate_status: storage_enums::MandateStatus::Revoked, - }, - orig_mandate, - storage_scheme, - ) - .await - .change_context(errors::ApiErrorResponse::MandateUpdateFailed), - storage_enums::MandateType::MultiUse => state - .store - .update_mandate_by_merchant_id_mandate_id( - &resp.merchant_id, - mandate_id, - storage::MandateUpdate::CaptureAmountUpdate { - amount_captured: Some( - orig_mandate.amount_captured.unwrap_or(0) - + resp.request.get_amount(), - ), - }, - orig_mandate, - storage_scheme, - ) - .await - .change_context(errors::ApiErrorResponse::MandateUpdateFailed), - }?; - metrics::SUBSEQUENT_MANDATE_PAYMENT.add( - &metrics::CONTEXT, - 1, - &[metrics::request::add_attributes( - "connector", - mandate.connector, - )], - ); - resp.payment_method_id = Some(mandate.payment_method_id); - } - } - None => { - if resp.request.get_setup_mandate_details().is_some() { - resp.payment_method_id = pm_id.clone(); - let (mandate_reference, network_txn_id) = match resp.response.as_ref().ok() { - Some(types::PaymentsResponseData::TransactionResponse { - mandate_reference, - network_txn_id, - .. - }) => (mandate_reference.clone(), network_txn_id.clone()), - _ => (None, None), - }; + let Ok(ref response) = resp.response else { + return Ok(None); + }; - let mandate_ids = mandate_reference - .as_ref() - .map(|md| { - md.encode_to_value() - .change_context( - errors::ApiErrorResponse::MandateSerializationFailed, - ) - .map(masking::Secret::new) - }) - .transpose()?; + match resp.request.get_mandate_id() { + Some(mandate_id) => { + let Some(ref mandate_id) = mandate_id.mandate_id else { + return Ok(None); + }; + let orig_mandate = state + .store + .find_mandate_by_merchant_id_mandate_id( + resp.merchant_id.as_ref(), + mandate_id, + storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?; + let mandate = match orig_mandate.mandate_type { + storage_enums::MandateType::SingleUse => state + .store + .update_mandate_by_merchant_id_mandate_id( + &resp.merchant_id, + mandate_id, + storage::MandateUpdate::StatusUpdate { + mandate_status: storage_enums::MandateStatus::Revoked, + }, + orig_mandate, + storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::MandateUpdateFailed), + storage_enums::MandateType::MultiUse => state + .store + .update_mandate_by_merchant_id_mandate_id( + &resp.merchant_id, + mandate_id, + storage::MandateUpdate::CaptureAmountUpdate { + amount_captured: Some( + orig_mandate.amount_captured.unwrap_or(0) + + resp.request.get_amount(), + ), + }, + orig_mandate, + storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::MandateUpdateFailed), + }?; + metrics::SUBSEQUENT_MANDATE_PAYMENT.add( + &metrics::CONTEXT, + 1, + &[metrics::request::add_attributes( + "connector", + mandate.connector, + )], + ); + Ok(Some(mandate_id.clone())) + } + None => { + let Some(_mandate_details) = resp.request.get_setup_mandate_details() else { + return Ok(None); + }; + let (mandate_reference, network_txn_id) = match &response { + types::PaymentsResponseData::TransactionResponse { + mandate_reference, + network_txn_id, + .. + } => (mandate_reference.clone(), network_txn_id.clone()), + _ => (None, None), + }; - if let Some(new_mandate_data) = payment_helper::generate_mandate( - resp.merchant_id.clone(), - resp.payment_id.clone(), - resp.connector.clone(), - resp.request.get_setup_mandate_details().cloned(), - maybe_customer, - pm_id.get_required_value("payment_method_id")?, - mandate_ids, - network_txn_id, - get_insensitive_payment_method_data_if_exists(&resp), - mandate_reference, - merchant_connector_id, - )? { - let connector = new_mandate_data.connector.clone(); - logger::debug!("{:?}", new_mandate_data); - resp.request - .set_mandate_id(Some(api_models::payments::MandateIds { - mandate_id: Some(new_mandate_data.mandate_id.clone()), - mandate_reference_id: new_mandate_data - .connector_mandate_ids - .clone() - .map(|ids| { - Some(ids) - .parse_value::( - "ConnectorMandateId", - ) - .change_context(errors::ApiErrorResponse::MandateDeserializationFailed) - }) - .transpose()? - .map_or( - new_mandate_data.network_transaction_id.clone().map(|id| { - api_models::payments::MandateReferenceId::NetworkMandateId( - id, - ) - }), - |connector_id| Some(api_models::payments::MandateReferenceId::ConnectorMandateId( - api_models::payments::ConnectorMandateReferenceId { - connector_mandate_id: connector_id.connector_mandate_id, - payment_method_id: connector_id.payment_method_id, - update_history:None, + let mandate_ids = mandate_reference + .as_ref() + .map(|md| { + md.encode_to_value() + .change_context(errors::ApiErrorResponse::MandateSerializationFailed) + .map(masking::Secret::new) + }) + .transpose()?; - } - ))) - })); - state - .store - .insert_mandate(new_mandate_data, storage_scheme) - .await - .to_duplicate_response(errors::ApiErrorResponse::DuplicateMandate)?; - metrics::MANDATE_COUNT.add( - &metrics::CONTEXT, - 1, - &[metrics::request::add_attributes("connector", connector)], - ); - }; - } - } - }, + let Some(new_mandate_data) = payment_helper::generate_mandate( + resp.merchant_id.clone(), + resp.payment_id.clone(), + resp.connector.clone(), + resp.request.get_setup_mandate_details().cloned(), + customer_id, + pm_id.get_required_value("payment_method_id")?, + mandate_ids, + network_txn_id, + get_insensitive_payment_method_data_if_exists(resp), + mandate_reference, + merchant_connector_id, + )? + else { + return Ok(None); + }; + + let connector = new_mandate_data.connector.clone(); + logger::debug!("{:?}", new_mandate_data); + + let res_mandate_id = new_mandate_data.mandate_id.clone(); + + state + .store + .insert_mandate(new_mandate_data, storage_scheme) + .await + .to_duplicate_response(errors::ApiErrorResponse::DuplicateMandate)?; + metrics::MANDATE_COUNT.add( + &metrics::CONTEXT, + 1, + &[metrics::request::add_attributes("connector", connector)], + ); + Ok(Some(res_mandate_id)) + } } - Ok(resp) } #[instrument(skip(state))] diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 59fd473399..2eb080f952 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -119,7 +119,7 @@ where // To perform router related operation for PaymentResponse PaymentResponse: Operation, - FData: Send + Sync, + FData: Send + Sync + Clone, Ctx: PaymentMethodRetrieve, { let operation: BoxedOperation<'_, F, Req, Ctx> = Box::new(operation); @@ -273,11 +273,11 @@ where req_state, &merchant_account, &key_store, - connector, + connector.clone(), &operation, &mut payment_data, &customer, - call_connector_action, + call_connector_action.clone(), &validate_result, schedule_time, header_payload, @@ -294,6 +294,18 @@ where external_latency = router_data.external_latency; //add connector http status code metrics add_connector_http_status_code_metrics(connector_http_status_code); + + operation + .to_post_update_tracker()? + .save_pm_and_mandate( + state, + &router_data, + &merchant_account, + &key_store, + &mut payment_data, + ) + .await?; + operation .to_post_update_tracker()? .update_tracker( @@ -333,7 +345,7 @@ where &operation, &mut payment_data, &customer, - call_connector_action, + call_connector_action.clone(), &validate_result, schedule_time, header_payload, @@ -361,7 +373,7 @@ where req_state, &mut payment_data, connectors, - connector_data, + connector_data.clone(), router_data, &merchant_account, &key_store, @@ -383,6 +395,18 @@ where external_latency = router_data.external_latency; //add connector http status code metrics add_connector_http_status_code_metrics(connector_http_status_code); + + operation + .to_post_update_tracker()? + .save_pm_and_mandate( + state, + &router_data, + &merchant_account, + &key_store, + &mut payment_data, + ) + .await?; + operation .to_post_update_tracker()? .update_tracker( @@ -697,7 +721,7 @@ pub async fn payments_core( ) -> RouterResponse where F: Send + Clone + Sync, - FData: Send + Sync, + FData: Send + Sync + Clone, Op: Operation + Send + Sync + Clone, Req: Debug + Authenticate + Clone, Res: transformers::ToResponse, Op>, @@ -1531,16 +1555,7 @@ where // and rely on previous status set in router_data router_data.status = payment_data.payment_attempt.status; router_data - .decide_flows( - state, - &connector, - customer, - call_connector_action, - merchant_account, - connector_request, - key_store, - payment_data.payment_intent.profile_id.clone(), - ) + .decide_flows(state, &connector, call_connector_action, connector_request) .await } else { Ok(router_data) @@ -1663,12 +1678,8 @@ where let res = router_data.decide_flows( state, &session_connector_data.connector, - customer, CallConnectorAction::Trigger, - merchant_account, None, - key_store, - payment_data.payment_intent.profile_id.clone(), ); join_handlers.push(res); diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 03cba29d02..59c0b687d6 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -44,12 +44,8 @@ pub trait Feature { self, state: &AppState, connector: &api::ConnectorData, - maybe_customer: &Option, call_connector_action: payments::CallConnectorAction, - merchant_account: &domain::MerchantAccount, connector_request: Option, - key_store: &domain::MerchantKeyStore, - profile_id: Option, ) -> RouterResult where Self: Sized, diff --git a/crates/router/src/core/payments/flows/approve_flow.rs b/crates/router/src/core/payments/flows/approve_flow.rs index 43e36847eb..ffa11fdb0f 100644 --- a/crates/router/src/core/payments/flows/approve_flow.rs +++ b/crates/router/src/core/payments/flows/approve_flow.rs @@ -49,12 +49,8 @@ impl Feature self, _state: &AppState, _connector: &api::ConnectorData, - _customer: &Option, _call_connector_action: payments::CallConnectorAction, - _merchant_account: &domain::MerchantAccount, _connector_request: Option, - _key_store: &domain::MerchantKeyStore, - _profile_id: Option, ) -> RouterResult { Err(ApiErrorResponse::NotImplemented { message: NotImplementedMessage::Reason("Flow not supported".to_string()), diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 10a4206a2b..e44456fb1c 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -61,12 +61,8 @@ impl Feature for types::PaymentsAu mut self, state: &AppState, connector: &api::ConnectorData, - maybe_customer: &Option, call_connector_action: payments::CallConnectorAction, - merchant_account: &domain::MerchantAccount, connector_request: Option, - key_store: &domain::MerchantKeyStore, - profile_id: Option, ) -> RouterResult { let connector_integration: services::BoxedConnectorIntegration< '_, @@ -78,7 +74,7 @@ impl Feature for types::PaymentsAu if self.should_proceed_with_authorize() { self.decide_authentication_type(); logger::debug!(auth_type=?self.auth_type); - let mut resp = services::execute_connector_processing_step( + let resp = services::execute_connector_processing_step( state, connector_integration, &self, @@ -89,98 +85,7 @@ impl Feature for types::PaymentsAu .to_payment_failed_response()?; metrics::PAYMENT_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics - - let is_mandate = resp.request.setup_mandate_details.is_some(); - - if is_mandate { - let (payment_method_id, payment_method_status) = - Box::pin(tokenization::save_payment_method( - state, - connector, - resp.to_owned(), - maybe_customer, - merchant_account, - self.request.payment_method_type, - key_store, - Some(resp.request.amount), - Some(resp.request.currency), - profile_id, - )) - .await?; - - resp.payment_method_id = payment_method_id.clone(); - resp.payment_method_status = payment_method_status; - - Ok(mandate::mandate_procedure( - state, - resp, - maybe_customer, - payment_method_id, - connector.merchant_connector_id.clone(), - merchant_account.storage_scheme, - ) - .await?) - } else { - let response = resp.clone(); - - logger::info!("Call to save_payment_method in locker"); - - let pm = Box::pin(tokenization::save_payment_method( - state, - connector, - response, - maybe_customer, - merchant_account, - self.request.payment_method_type, - key_store, - Some(resp.request.amount), - Some(resp.request.currency), - profile_id, - )) - .await; - - match pm { - Ok((payment_method_id, payment_method_status)) => { - resp.payment_method_id = payment_method_id.clone(); - resp.payment_method_status = payment_method_status; - } - Err(err) => logger::error!("Save pm to locker failed : {err:?}"), - } - - Ok(resp) - } - - // Async locker code (Commenting out the code for near future refactors) - // logger::info!("Call to save_payment_method in locker"); - // let _task_handle = tokio::spawn( - // async move { - // logger::info!("Starting async call to save_payment_method in locker"); - // - // let result = Box::pin(tokenization::save_payment_method( - // &state, - // &connector, - // response, - // &maybe_customer, - // &merchant_account, - // self.request.payment_method_type, - // &key_store, - // Some(resp.request.amount), - // Some(resp.request.currency), - // )) - // .await; - // - // if let Err(err) = result { - // logger::error!( - // "Asynchronously saving card in locker failed : {:?}", - // err - // ); - // } - // } - // .in_current_span(), - // ); - // - // Ok(resp) - // } + Ok(resp) } else { Ok(self.clone()) } diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs index 5814b1cafb..5f802a0bbe 100644 --- a/crates/router/src/core/payments/flows/cancel_flow.rs +++ b/crates/router/src/core/payments/flows/cancel_flow.rs @@ -48,12 +48,8 @@ impl Feature self, state: &AppState, connector: &api::ConnectorData, - _customer: &Option, call_connector_action: payments::CallConnectorAction, - _merchant_account: &domain::MerchantAccount, connector_request: Option, - _key_store: &domain::MerchantKeyStore, - _profile_id: Option, ) -> RouterResult { metrics::PAYMENT_CANCEL_COUNT.add( &metrics::CONTEXT, diff --git a/crates/router/src/core/payments/flows/capture_flow.rs b/crates/router/src/core/payments/flows/capture_flow.rs index 5f64014bce..56ae6500c3 100644 --- a/crates/router/src/core/payments/flows/capture_flow.rs +++ b/crates/router/src/core/payments/flows/capture_flow.rs @@ -49,12 +49,8 @@ impl Feature self, state: &AppState, connector: &api::ConnectorData, - _customer: &Option, call_connector_action: payments::CallConnectorAction, - _merchant_account: &domain::MerchantAccount, connector_request: Option, - _key_store: &domain::MerchantKeyStore, - _profile_id: Option, ) -> RouterResult { let connector_integration: services::BoxedConnectorIntegration< '_, 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 e64240387d..baf4190395 100644 --- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs +++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs @@ -63,12 +63,8 @@ impl Feature mut self, state: &AppState, connector: &api::ConnectorData, - _customer: &Option, call_connector_action: payments::CallConnectorAction, - _merchant_account: &domain::MerchantAccount, connector_request: Option, - _key_store: &domain::MerchantKeyStore, - _profile_id: Option, ) -> RouterResult { let connector_integration: services::BoxedConnectorIntegration< '_, diff --git a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs index 99f8e4831b..e702483022 100644 --- a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs +++ b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs @@ -56,12 +56,8 @@ impl Feature, call_connector_action: payments::CallConnectorAction, - _merchant_account: &domain::MerchantAccount, connector_request: Option, - _key_store: &domain::MerchantKeyStore, - _profile_id: Option, ) -> RouterResult { let connector_integration: services::BoxedConnectorIntegration< '_, diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs index 6463e87279..f410b65f07 100644 --- a/crates/router/src/core/payments/flows/psync_flow.rs +++ b/crates/router/src/core/payments/flows/psync_flow.rs @@ -52,12 +52,8 @@ impl Feature mut self, state: &AppState, connector: &api::ConnectorData, - _customer: &Option, call_connector_action: payments::CallConnectorAction, - _merchant_account: &domain::MerchantAccount, connector_request: Option, - _key_store: &domain::MerchantKeyStore, - _profile_id: Option, ) -> RouterResult { let connector_integration: services::BoxedConnectorIntegration< '_, diff --git a/crates/router/src/core/payments/flows/reject_flow.rs b/crates/router/src/core/payments/flows/reject_flow.rs index 4157edf8d0..89c1585fca 100644 --- a/crates/router/src/core/payments/flows/reject_flow.rs +++ b/crates/router/src/core/payments/flows/reject_flow.rs @@ -48,12 +48,8 @@ impl Feature self, _state: &AppState, _connector: &api::ConnectorData, - _customer: &Option, _call_connector_action: payments::CallConnectorAction, - _merchant_account: &domain::MerchantAccount, _connector_request: Option, - _key_store: &domain::MerchantKeyStore, - _profile_id: Option, ) -> RouterResult { Err(ApiErrorResponse::NotImplemented { message: NotImplementedMessage::Reason("Flow not supported".to_string()), diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index 7e15e2ed3f..3d21aa732d 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -53,12 +53,8 @@ impl Feature for types::PaymentsSessio self, state: &routes::AppState, connector: &api::ConnectorData, - customer: &Option, call_connector_action: payments::CallConnectorAction, - _merchant_account: &domain::MerchantAccount, _connector_request: Option, - _key_store: &domain::MerchantKeyStore, - _profile_id: Option, ) -> RouterResult { metrics::SESSION_TOKEN_CREATED.add( &metrics::CONTEXT, @@ -68,14 +64,8 @@ impl Feature for types::PaymentsSessio connector.connector_name.to_string(), )], ); - self.decide_flow( - state, - connector, - customer, - Some(true), - call_connector_action, - ) - .await + self.decide_flow(state, connector, Some(true), call_connector_action) + .await } async fn add_access_token<'a>( @@ -521,7 +511,6 @@ impl types::PaymentsSessionRouterData { &'b self, state: &'a routes::AppState, connector: &api::ConnectorData, - _customer: &Option, _confirm: Option, call_connector_action: payments::CallConnectorAction, ) -> RouterResult { 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 ff77efa7c4..6a810e9b3d 100644 --- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs +++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs @@ -1,14 +1,10 @@ -use api_models::enums::{PaymentMethod, PaymentMethodType}; use async_trait::async_trait; -use error_stack::ResultExt; use super::{ConstructFlowSpecificData, Feature}; use crate::{ - configs::settings, core::{ - errors::{self, ConnectorErrorExt, RouterResult, StorageErrorExt}, + errors::{self, ConnectorErrorExt, RouterResult}, mandate, - payment_methods::cards, payments::{ self, access_token, customers, helpers, tokenization, transformers, PaymentData, }, @@ -57,76 +53,26 @@ impl Feature for types::Setup self, state: &AppState, connector: &api::ConnectorData, - maybe_customer: &Option, call_connector_action: payments::CallConnectorAction, - merchant_account: &domain::MerchantAccount, connector_request: Option, - key_store: &domain::MerchantKeyStore, - profile_id: Option, ) -> RouterResult { - if let Some(mandate_id) = self - .request - .setup_mandate_details - .as_ref() - .and_then(|mandate_data| mandate_data.update_mandate_id.clone()) - { - Box::pin(self.update_mandate_flow( - state, - merchant_account, - mandate_id, - connector, - key_store, - call_connector_action, - &state.conf.mandates.update_mandate_supported, - connector_request, - maybe_customer, - profile_id, - )) - .await - } else { - let connector_integration: services::BoxedConnectorIntegration< - '_, - api::SetupMandate, - types::SetupMandateRequestData, - types::PaymentsResponseData, - > = connector.connector.get_connector_integration(); + let connector_integration: services::BoxedConnectorIntegration< + '_, + api::SetupMandate, + types::SetupMandateRequestData, + types::PaymentsResponseData, + > = connector.connector.get_connector_integration(); - let mut resp = services::execute_connector_processing_step( - state, - connector_integration, - &self, - call_connector_action.clone(), - connector_request, - ) - .await - .to_setup_mandate_failed_response()?; - - let (pm_id, payment_method_status) = Box::pin(tokenization::save_payment_method( - state, - connector, - resp.to_owned(), - maybe_customer, - merchant_account, - self.request.payment_method_type, - key_store, - resp.request.amount, - Some(resp.request.currency), - profile_id, - )) - .await?; - - resp.payment_method_id = pm_id.clone(); - resp.payment_method_status = payment_method_status; - mandate::mandate_procedure( - state, - resp, - maybe_customer, - pm_id, - connector.merchant_connector_id.clone(), - merchant_account.storage_scheme, - ) - .await - } + let resp = services::execute_connector_processing_step( + state, + connector_integration, + &self, + call_connector_action.clone(), + connector_request, + ) + .await + .to_setup_mandate_failed_response()?; + Ok(resp) } async fn add_access_token<'a>( @@ -210,222 +156,6 @@ impl TryFrom for types::ConnectorCustomerData { } } -#[allow(clippy::too_many_arguments)] -impl types::SetupMandateRouterData { - pub async fn decide_flow<'a, 'b>( - &'b self, - state: &'a AppState, - connector: &api::ConnectorData, - maybe_customer: &Option, - confirm: Option, - call_connector_action: payments::CallConnectorAction, - merchant_account: &domain::MerchantAccount, - key_store: &domain::MerchantKeyStore, - profile_id: Option, - ) -> RouterResult { - match confirm { - Some(true) => { - let connector_integration: services::BoxedConnectorIntegration< - '_, - api::SetupMandate, - types::SetupMandateRequestData, - types::PaymentsResponseData, - > = connector.connector.get_connector_integration(); - let mut resp = services::execute_connector_processing_step( - state, - connector_integration, - self, - call_connector_action, - None, - ) - .await - .to_setup_mandate_failed_response()?; - - let payment_method_type = self.request.payment_method_type; - - let (pm_id, payment_method_status) = Box::pin(tokenization::save_payment_method( - state, - connector, - resp.to_owned(), - maybe_customer, - merchant_account, - payment_method_type, - key_store, - resp.request.amount, - Some(resp.request.currency), - profile_id, - )) - .await?; - - resp.payment_method_id = pm_id.clone(); - resp.payment_method_status = payment_method_status; - - Ok(mandate::mandate_procedure( - state, - resp, - maybe_customer, - pm_id, - connector.merchant_connector_id.clone(), - merchant_account.storage_scheme, - ) - .await?) - } - _ => Ok(self.clone()), - } - } - - async fn update_mandate_flow( - self, - state: &AppState, - merchant_account: &domain::MerchantAccount, - mandate_id: String, - connector: &api::ConnectorData, - key_store: &domain::MerchantKeyStore, - call_connector_action: payments::CallConnectorAction, - supported_connectors_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, - connector_request: Option, - maybe_customer: &Option, - profile_id: Option, - ) -> RouterResult { - let payment_method_type = self.request.payment_method_type; - - let payment_method = self.request.payment_method_data.get_payment_method(); - let supported_connectors_config = payment_method.zip(payment_method_type).map_or_else( - || { - if payment_method == Some(PaymentMethod::Card) { - cards::filter_pm_based_on_update_mandate_support_for_connector( - supported_connectors_for_update_mandate, - &PaymentMethod::Card, - &PaymentMethodType::Credit, - connector.connector_name, - ) && cards::filter_pm_based_on_update_mandate_support_for_connector( - supported_connectors_for_update_mandate, - &PaymentMethod::Card, - &PaymentMethodType::Debit, - connector.connector_name, - ) - } else { - false - } - }, - |(pm, pmt)| { - cards::filter_pm_based_on_update_mandate_support_for_connector( - supported_connectors_for_update_mandate, - &pm, - &pmt, - connector.connector_name, - ) - }, - ); - if supported_connectors_config { - let connector_integration: services::BoxedConnectorIntegration< - '_, - api::SetupMandate, - types::SetupMandateRequestData, - types::PaymentsResponseData, - > = connector.connector.get_connector_integration(); - - let resp = services::execute_connector_processing_step( - state, - connector_integration, - &self, - call_connector_action.clone(), - connector_request, - ) - .await - .to_setup_mandate_failed_response()?; - let pm_id = Box::pin(tokenization::save_payment_method( - state, - connector, - resp.to_owned(), - maybe_customer, - merchant_account, - self.request.payment_method_type, - key_store, - resp.request.amount, - Some(resp.request.currency), - profile_id, - )) - .await? - .0; - let mandate = state - .store - .find_mandate_by_merchant_id_mandate_id( - &merchant_account.merchant_id, - &mandate_id, - merchant_account.storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?; - - let profile_id = mandate::helpers::get_profile_id_for_mandate( - state, - merchant_account, - mandate.clone(), - ) - .await?; - match resp.response { - Ok(types::PaymentsResponseData::TransactionResponse { .. }) => { - let connector_integration: services::BoxedConnectorIntegration< - '_, - types::api::MandateRevoke, - types::MandateRevokeRequestData, - types::MandateRevokeResponseData, - > = connector.connector.get_connector_integration(); - let merchant_connector_account = helpers::get_merchant_connector_account( - state, - &merchant_account.merchant_id, - None, - key_store, - &profile_id, - &mandate.connector, - mandate.merchant_connector_id.as_ref(), - ) - .await?; - - let router_data = mandate::utils::construct_mandate_revoke_router_data( - merchant_connector_account, - merchant_account, - mandate.clone(), - ) - .await?; - - let _response = services::execute_connector_processing_step( - state, - connector_integration, - &router_data, - call_connector_action, - None, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - // TODO:Add the revoke mandate task to process tracker - mandate::update_mandate_procedure( - state, - resp, - mandate, - &merchant_account.merchant_id, - pm_id, - merchant_account.storage_scheme, - ) - .await - } - Ok(_) => Err(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unexpected response received")?, - Err(_) => Ok(resp), - } - } else { - Err(errors::ApiErrorResponse::PreconditionFailed { - message: format!( - "Update Mandate flow not implemented for the connector {:?}", - connector.connector_name - ), - } - .into()) - } - } -} - impl mandate::MandateBehaviour for types::SetupMandateRequestData { fn get_amount(&self) -> i64 { 0 diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index de2fdd99b7..c8763584cc 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1247,7 +1247,7 @@ pub(crate) async fn get_payment_method_create_request( payment_method_data: Option<&domain::PaymentMethodData>, payment_method: Option, payment_method_type: Option, - customer: &domain::Customer, + customer_id: &Option, billing_name: Option>, ) -> RouterResult { match payment_method_data { @@ -1265,7 +1265,6 @@ pub(crate) async fn get_payment_method_create_request( card_issuer: card.card_issuer.clone(), card_type: card.card_type.clone(), }; - let customer_id = customer.customer_id.clone(); let payment_method_request = api::PaymentMethodCreate { payment_method: Some(payment_method), payment_method_type, @@ -1277,7 +1276,7 @@ pub(crate) async fn get_payment_method_create_request( wallet: None, card: Some(card_detail), metadata: None, - customer_id: Some(customer_id), + customer_id: customer_id.clone(), card_network: card .card_network .as_ref() @@ -1299,7 +1298,7 @@ pub(crate) async fn get_payment_method_create_request( wallet: None, card: None, metadata: None, - customer_id: Some(customer.customer_id.to_owned()), + customer_id: customer_id.clone(), card_network: None, client_secret: None, payment_method_data: None, @@ -2618,7 +2617,7 @@ pub fn generate_mandate( payment_id: String, connector: String, setup_mandate_details: Option, - customer: &Option, + customer_id: &Option, payment_method_id: String, connector_mandate_id: Option, network_txn_id: Option, @@ -2626,8 +2625,8 @@ pub fn generate_mandate( mandate_reference: Option, merchant_connector_id: Option, ) -> CustomResult, errors::ApiErrorResponse> { - match (setup_mandate_details, customer) { - (Some(data), Some(cus)) => { + match (setup_mandate_details, customer_id) { + (Some(data), Some(cus_id)) => { let mandate_id = utils::generate_id(consts::ID_LENGTH, "man"); // The construction of the mandate new must be visible @@ -2638,7 +2637,7 @@ pub fn generate_mandate( .get_required_value("customer_acceptance")?; new_mandate .set_mandate_id(mandate_id) - .set_customer_id(cus.customer_id.clone()) + .set_customer_id(cus_id.clone()) .set_merchant_id(merchant_id) .set_original_payment_id(Some(payment_id)) .set_payment_method_id(payment_method_id) diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 6b04c500dc..d4590782b0 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -212,7 +212,7 @@ pub trait UpdateTracker: Send { } #[async_trait] -pub trait PostUpdateTracker: Send { +pub trait PostUpdateTracker: Send { async fn update_tracker<'b>( &'b self, db: &'b AppState, @@ -222,7 +222,21 @@ pub trait PostUpdateTracker: Send { storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult where - F: 'b + Send; + F: 'b + Send + Sync; + + async fn save_pm_and_mandate<'b>( + &self, + _state: &AppState, + _resp: &types::RouterData, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _payment_data: &mut PaymentData, + ) -> CustomResult<(), errors::ApiErrorResponse> + where + F: 'b + Clone + Send + Sync, + { + Ok(()) + } } #[async_trait] diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index b96cf48323..4580181d39 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -15,7 +15,7 @@ use super::{Operation, PostUpdateTracker}; use crate::{ connector::utils::PaymentResponseRouterData, core::{ - errors::{self, RouterResult, StorageErrorExt}, + errors::{self, CustomResult, RouterResult, StorageErrorExt}, mandate, payment_methods::{self, PaymentMethodRetrieve}, payments::{ @@ -24,6 +24,7 @@ use crate::{ self as payments_helpers, update_additional_payment_data_with_connector_response_pm_data, }, + tokenization, types::MultipleCaptureData, PaymentData, }, @@ -31,7 +32,7 @@ use crate::{ }, routes::{metrics, AppState}, types::{ - self, api, + self, api, domain, storage::{self, enums}, transformers::{ForeignFrom, ForeignTryFrom}, CaptureSyncResponse, ErrorResponse, @@ -42,12 +43,12 @@ use crate::{ #[derive(Debug, Clone, Copy, router_derive::PaymentOperation)] #[operation( operations = "post_update_tracker", - flow = "sync_data, authorize_data, cancel_data, capture_data, complete_authorize_data, approve_data, reject_data, setup_mandate_data, session_data,incremental_authorization_data" + flow = "sync_data, cancel_data, authorize_data, capture_data, complete_authorize_data, approve_data, reject_data, setup_mandate_data, session_data,incremental_authorization_data" )] pub struct PaymentResponse; #[async_trait] -impl PostUpdateTracker, types::PaymentsAuthorizeData> +impl PostUpdateTracker, types::PaymentsAuthorizeData> for PaymentResponse { async fn update_tracker<'b>( @@ -63,7 +64,7 @@ impl PostUpdateTracker, types::PaymentsAuthorizeData storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where - F: 'b + Send, + F: 'b, { payment_data.mandate_id = payment_data .mandate_id @@ -80,6 +81,151 @@ impl PostUpdateTracker, types::PaymentsAuthorizeData Ok(payment_data) } + + async fn save_pm_and_mandate<'b>( + &self, + state: &AppState, + resp: &types::RouterData, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + payment_data: &mut PaymentData, + ) -> CustomResult<(), errors::ApiErrorResponse> + where + F: 'b + Clone + Send + Sync, + { + let customer_id = payment_data.payment_intent.customer_id.clone(); + let save_payment_data = tokenization::SavePaymentMethodData::from(resp); + let profile_id = payment_data.payment_intent.profile_id.clone(); + + let connector_name = payment_data + .payment_attempt + .connector + .clone() + .ok_or_else(|| { + logger::error!("Missing required Param connector_name"); + errors::ApiErrorResponse::MissingRequiredField { + field_name: "connector_name", + } + })?; + let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone(); + let billing_name = resp + .address + .get_payment_method_billing() + .and_then(|billing_details| billing_details.address.as_ref()) + .and_then(|address| address.get_optional_full_name()); + + let save_payment_call_future = Box::pin(tokenization::save_payment_method( + state, + connector_name.clone(), + merchant_connector_id.clone(), + save_payment_data, + customer_id.clone(), + merchant_account, + resp.request.payment_method_type, + key_store, + Some(resp.request.amount), + Some(resp.request.currency), + profile_id, + billing_name.clone(), + )); + + let is_connector_mandate = resp.request.customer_acceptance.is_some() + && matches!( + resp.request.setup_future_usage, + Some(enums::FutureUsage::OffSession) + ); + + let is_legacy_mandate = resp.request.setup_mandate_details.is_some() + && matches!( + resp.request.setup_future_usage, + Some(enums::FutureUsage::OffSession) + ); + + if is_legacy_mandate { + // Mandate is created on the application side and at the connector. + let (payment_method_id, _payment_method_status) = save_payment_call_future.await?; + + let mandate_id = mandate::mandate_procedure( + state, + resp, + &customer_id.clone(), + payment_method_id.clone(), + merchant_connector_id.clone(), + merchant_account.storage_scheme, + ) + .await?; + payment_data.payment_attempt.payment_method_id = payment_method_id; + payment_data.payment_attempt.mandate_id = mandate_id; + Ok(()) + } else if is_connector_mandate { + // The mandate is created on connector's end. + let (payment_method_id, _payment_method_status) = save_payment_call_future.await?; + payment_data.payment_attempt.payment_method_id = payment_method_id; + Ok(()) + } else { + // Save card flow + let save_payment_data = tokenization::SavePaymentMethodData::from(resp); + let merchant_account = merchant_account.clone(); + let key_store = key_store.clone(); + let state = state.clone(); + let customer_id = payment_data.payment_intent.customer_id.clone(); + let profile_id = payment_data.payment_intent.profile_id.clone(); + + let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone(); + let payment_attempt = payment_data.payment_attempt.clone(); + + let amount = resp.request.amount; + let currency = resp.request.currency; + let payment_method_type = resp.request.payment_method_type; + let storage_scheme = merchant_account.clone().storage_scheme; + + logger::info!("Call to save_payment_method in locker"); + let _task_handle = tokio::spawn( + async move { + logger::info!("Starting async call to save_payment_method in locker"); + + let result = Box::pin(tokenization::save_payment_method( + &state, + connector_name, + merchant_connector_id, + save_payment_data, + customer_id, + &merchant_account, + payment_method_type, + &key_store, + Some(amount), + Some(currency), + profile_id, + billing_name, + )) + .await; + + if let Err(err) = result { + logger::error!("Asynchronously saving card in locker failed : {:?}", err); + } else if let Ok((payment_method_id, _pm_status)) = result { + let payment_attempt_update = + storage::PaymentAttemptUpdate::PaymentMethodDetailsUpdate { + payment_method_id, + updated_by: storage_scheme.clone().to_string(), + }; + let respond = state + .store + .update_payment_attempt_with_attempt_id( + payment_attempt, + payment_attempt_update, + storage_scheme, + ) + .await; + if let Err(err) = respond { + logger::error!("Error updating payment attempt: {:?}", err); + }; + } + } + .in_current_span(), + ); + Ok(()) + } + } } #[async_trait] @@ -234,6 +380,28 @@ impl PostUpdateTracker, types::PaymentsSyncData> for )) .await } + + async fn save_pm_and_mandate<'b>( + &self, + state: &AppState, + resp: &types::RouterData, + merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + payment_data: &mut PaymentData, + ) -> CustomResult<(), errors::ApiErrorResponse> + where + F: 'b + Clone + Send + Sync, + { + update_payment_method_status_and_ntid( + state, + payment_data, + resp.status, + resp.response.clone(), + merchant_account.storage_scheme, + ) + .await?; + Ok(()) + } } #[async_trait] @@ -411,6 +579,67 @@ impl PostUpdateTracker, types::SetupMandateRequestDa Ok(payment_data) } + + async fn save_pm_and_mandate<'b>( + &self, + state: &AppState, + resp: &types::RouterData, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + payment_data: &mut PaymentData, + ) -> CustomResult<(), errors::ApiErrorResponse> + where + F: 'b + Clone + Send + Sync, + { + let billing_name = resp + .address + .get_payment_method_billing() + .and_then(|billing_details| billing_details.address.as_ref()) + .and_then(|address| address.get_optional_full_name()); + let save_payment_data = tokenization::SavePaymentMethodData::from(resp); + let customer_id = payment_data.payment_intent.customer_id.clone(); + let profile_id = payment_data.payment_intent.profile_id.clone(); + let connector_name = payment_data + .payment_attempt + .connector + .clone() + .ok_or_else(|| { + logger::error!("Missing required Param connector_name"); + errors::ApiErrorResponse::MissingRequiredField { + field_name: "connector_name", + } + })?; + let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone(); + let (payment_method_id, _payment_method_status) = + Box::pin(tokenization::save_payment_method( + state, + connector_name, + merchant_connector_id.clone(), + save_payment_data, + customer_id.clone(), + merchant_account, + resp.request.payment_method_type, + key_store, + resp.request.amount, + Some(resp.request.currency), + profile_id, + billing_name, + )) + .await?; + + let mandate_id = mandate::mandate_procedure( + state, + resp, + &customer_id, + payment_method_id.clone(), + merchant_connector_id.clone(), + merchant_account.storage_scheme, + ) + .await?; + payment_data.payment_attempt.payment_method_id = payment_method_id; + payment_data.payment_attempt.mandate_id = mandate_id; + Ok(()) + } } #[async_trait] @@ -437,6 +666,28 @@ impl PostUpdateTracker, types::CompleteAuthorizeData )) .await } + + async fn save_pm_and_mandate<'b>( + &self, + state: &AppState, + resp: &types::RouterData, + merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + payment_data: &mut PaymentData, + ) -> CustomResult<(), errors::ApiErrorResponse> + where + F: 'b + Clone + Send + Sync, + { + update_payment_method_status_and_ntid( + state, + payment_data, + resp.status, + resp.response.clone(), + merchant_account.storage_scheme, + ) + .await?; + Ok(()) + } } #[instrument(skip_all)] @@ -587,7 +838,10 @@ async fn payment_response_update_tracker( let payment_attempt_update = storage::PaymentAttemptUpdate::PreprocessingUpdate { status: updated_attempt_status, - payment_method_id: router_data.payment_method_id, + payment_method_id: payment_data + .payment_attempt + .payment_method_id + .clone(), connector_metadata, preprocessing_step_id, connector_transaction_id, @@ -642,7 +896,7 @@ async fn payment_response_update_tracker( metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]); } - let payment_method_id = router_data.payment_method_id.clone(); + let payment_method_id = payment_data.payment_attempt.payment_method_id.clone(); utils::add_apple_pay_payment_status_metrics( router_data.status, @@ -677,10 +931,7 @@ async fn payment_response_update_tracker( .request .get_amount_capturable(&payment_data, updated_attempt_status), payment_method_id, - mandate_id: payment_data - .mandate_id - .clone() - .and_then(|mandate| mandate.mandate_id), + mandate_id: payment_data.payment_attempt.mandate_id.clone(), connector_metadata, payment_token: None, error_code: error_status.clone(), @@ -862,14 +1113,6 @@ async fn payment_response_update_tracker( }, }; - update_payment_method_status_and_ntid( - state, - &mut payment_data, - router_data.status, - router_data.response.clone(), - storage_scheme, - ) - .await?; let m_db = state.clone().store; let m_payment_data_payment_intent = payment_data.payment_intent.clone(); let m_payment_intent_update = payment_intent_update.clone(); diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 5bf6b2e701..304e6edd62 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -29,38 +29,62 @@ use crate::{ utils::{generate_id, OptionExt}, }; +pub struct SavePaymentMethodData { + request: Req, + response: Result, + payment_method_token: Option, + payment_method: PaymentMethod, + attempt_status: common_enums::AttemptStatus, +} + +impl From<&types::RouterData> + for SavePaymentMethodData +{ + fn from(router_data: &types::RouterData) -> Self { + Self { + request: router_data.request.clone(), + response: router_data.response.clone(), + payment_method_token: router_data.payment_method_token.clone(), + payment_method: router_data.payment_method, + attempt_status: router_data.status, + } + } +} + #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] -pub async fn save_payment_method( +pub async fn save_payment_method( state: &AppState, - connector: &api::ConnectorData, - resp: types::RouterData, - maybe_customer: &Option, + connector_name: String, + merchant_connector_id: Option, + save_payment_method_data: SavePaymentMethodData, + customer_id: Option, merchant_account: &domain::MerchantAccount, payment_method_type: Option, key_store: &domain::MerchantKeyStore, amount: Option, currency: Option, profile_id: Option, + billing_name: Option>, ) -> RouterResult<(Option, Option)> where - FData: mandate::MandateBehaviour, + FData: mandate::MandateBehaviour + Clone, { let mut pm_status = None; - match resp.response { + match save_payment_method_data.response { Ok(responses) => { let db = &*state.store; let token_store = state .conf .tokenization .0 - .get(&connector.connector_name.to_string()) + .get(&connector_name.to_string()) .map(|token_filter| token_filter.long_lived_token) .unwrap_or(false); - let network_transaction_id = match responses.clone() { + let network_transaction_id = match &responses { types::PaymentsResponseData::TransactionResponse { network_txn_id, .. } => { - network_txn_id + network_txn_id.clone() } _ => None, }; @@ -82,7 +106,7 @@ where .attach_printable("The pg_agnostic config was not found in the DB")?; if &pg_agnostic.config == "true" - && resp.request.get_setup_future_usage() + && save_payment_method_data.request.get_setup_future_usage() == Some(storage_enums::FutureUsage::OffSession) { Some(network_transaction_id) @@ -95,7 +119,7 @@ where }; let connector_token = if token_store { - let tokens = resp + let tokens = save_payment_method_data .payment_method_token .to_owned() .get_required_value("payment_token")?; @@ -107,17 +131,17 @@ where })? } }; - Some((connector, token)) + Some((connector_name, token)) } else { None }; - let mandate_data_customer_acceptance = resp + let mandate_data_customer_acceptance = save_payment_method_data .request .get_setup_mandate_details() .and_then(|mandate_data| mandate_data.customer_acceptance.clone()); - let customer_acceptance = resp + let customer_acceptance = save_payment_method_data .request .get_customer_acceptance() .or(mandate_data_customer_acceptance.clone().map(From::from)) @@ -139,8 +163,11 @@ where } _ => None, }; - let check_for_mit_mandates = resp.request.get_setup_mandate_details().is_none() - && resp + let check_for_mit_mandates = save_payment_method_data + .request + .get_setup_mandate_details() + .is_none() + && save_payment_method_data .request .get_setup_future_usage() .map(|future_usage| future_usage == storage_enums::FutureUsage::OffSession) @@ -151,7 +178,7 @@ where payment_method_type, amount, currency, - connector.merchant_connector_id.clone(), + merchant_connector_id.clone(), connector_mandate_id.clone(), ) } else { @@ -163,23 +190,16 @@ where .attach_printable("Unable to serialize customer acceptance to value")?; let pm_id = if customer_acceptance.is_some() { - let customer = maybe_customer.to_owned().get_required_value("customer")?; - let billing_name = resp - .address - .get_payment_method_billing() - .and_then(|billing_details| billing_details.address.as_ref()) - .and_then(|address| address.get_optional_full_name()); - let payment_method_create_request = helpers::get_payment_method_create_request( - Some(&resp.request.get_payment_method_data()), - Some(resp.payment_method), + Some(&save_payment_method_data.request.get_payment_method_data()), + Some(save_payment_method_data.payment_method), payment_method_type, - &customer, + &customer_id.clone(), billing_name, ) .await?; + let customer_id = customer_id.to_owned().get_required_value("customer_id")?; let merchant_id = &merchant_account.merchant_id; - let (mut resp, duplication_check) = if !state.conf.locker.locker_enabled { skip_saving_card_in_locker( merchant_account, @@ -187,7 +207,9 @@ where ) .await? } else { - pm_status = Some(common_enums::PaymentMethodStatus::from(resp.status)); + pm_status = Some(common_enums::PaymentMethodStatus::from( + save_payment_method_data.attempt_status, + )); Box::pin(save_in_locker( state, merchant_account, @@ -280,7 +302,7 @@ where payment_method_type, amount, currency, - connector.merchant_connector_id.clone(), + merchant_connector_id.clone(), connector_mandate_id.clone(), )?; @@ -297,7 +319,7 @@ where payment_methods::cards::create_payment_method( db, &payment_method_create_request, - &customer.customer_id, + customer_id.as_str(), &resp.payment_method_id, locker_id, merchant_id, @@ -371,7 +393,7 @@ where payment_method_type, amount, currency, - connector.merchant_connector_id.clone(), + merchant_connector_id.clone(), connector_mandate_id.clone(), )?; @@ -390,7 +412,7 @@ where payment_method_create_request.clone(), key_store, &merchant_account.merchant_id, - &customer.customer_id, + customer_id.as_str(), resp.metadata.clone().map(|val| val.expose()), customer_acceptance, locker_id, @@ -413,7 +435,7 @@ where payment_methods::cards::delete_card_from_locker( state, - &customer.customer_id, + customer_id.as_str(), merchant_id, existing_pm .locker_id @@ -426,7 +448,7 @@ where state, payment_method_create_request, &card, - customer.customer_id.clone(), + customer_id.clone(), merchant_account, api::enums::LockerChoice::HyperswitchCardVault, Some( @@ -516,7 +538,7 @@ where payment_methods::cards::create_payment_method( db, &payment_method_create_request, - &customer.customer_id, + customer_id.as_str(), &resp.payment_method_id, locker_id, merchant_id, @@ -683,7 +705,7 @@ pub async fn save_in_locker( pub fn create_payment_method_metadata( metadata: Option<&pii::SecretSerdeValue>, - connector_token: Option<(&api::ConnectorData, String)>, + connector_token: Option<(String, String)>, ) -> RouterResult> { let mut meta = match metadata { None => serde_json::Map::new(), @@ -698,7 +720,7 @@ pub fn create_payment_method_metadata( }; Ok(connector_token.and_then(|connector_and_token| { meta.insert( - connector_and_token.0.connector_name.to_string(), + connector_and_token.0, serde_json::Value::String(connector_and_token.1), ) })) diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs index 6eda689c8d..1fd25f3ded 100644 --- a/crates/storage_impl/src/payments/payment_attempt.rs +++ b/crates/storage_impl/src/payments/payment_attempt.rs @@ -1433,6 +1433,13 @@ impl DataModelExt for PaymentAttemptUpdate { error_message, updated_by, }, + Self::PaymentMethodDetailsUpdate { + payment_method_id, + updated_by, + } => DieselPaymentAttemptUpdate::PaymentMethodDetailsUpdate { + payment_method_id, + updated_by, + }, Self::ConfirmUpdate { amount, currency, @@ -1807,6 +1814,13 @@ impl DataModelExt for PaymentAttemptUpdate { error_message, updated_by, }, + DieselPaymentAttemptUpdate::PaymentMethodDetailsUpdate { + payment_method_id, + updated_by, + } => Self::PaymentMethodDetailsUpdate { + payment_method_id, + updated_by, + }, DieselPaymentAttemptUpdate::ResponseUpdate { status, connector,