From a62882a18903f12d161480b2925049c106e7ad8e Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Sun, 23 Jun 2024 20:30:52 +0530 Subject: [PATCH 1/8] feat(router): updated last_used_at field for apple pay and google pay for CITs --- .../router/src/core/payments/tokenization.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 64fe526f0c0..56321697745 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -515,7 +515,7 @@ where } }, None => { - let customer_saved_pm_id_option = if payment_method_type + let customer_saved_pm_option = if payment_method_type == Some(api_models::enums::PaymentMethodType::ApplePay) || payment_method_type == Some(api_models::enums::PaymentMethodType::GooglePay) @@ -534,7 +534,7 @@ where .find(|payment_method| { payment_method.payment_method_type == payment_method_type }) - .map(|pm| pm.payment_method_id.clone())), + .map(|pm| pm.clone())), Err(error) => { if error.current_context().is_db_not_found() { Ok(None) @@ -553,8 +553,18 @@ where Ok(None) }?; - if let Some(customer_saved_pm_id) = customer_saved_pm_id_option { - resp.payment_method_id = customer_saved_pm_id; + if let Some(customer_saved_pm) = customer_saved_pm_option { + payment_methods::cards::update_last_used_at( + &customer_saved_pm, + state, + merchant_account.storage_scheme, + ) + .await + .map_err(|e| { + logger::error!("Failed to update last used at: {:?}", e); + }) + .ok(); + resp.payment_method_id = customer_saved_pm.payment_method_id; } else { let pm_metadata = create_payment_method_metadata(None, connector_token)?; From f0b506ca517e4d8ac6d6fe51860ebb998642f84e Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Sun, 23 Jun 2024 21:20:12 +0530 Subject: [PATCH 2/8] fix clippy errors --- crates/router/src/core/payments/tokenization.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 56321697745..1a6f044cb0f 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -534,7 +534,7 @@ where .find(|payment_method| { payment_method.payment_method_type == payment_method_type }) - .map(|pm| pm.clone())), + .cloned()), Err(error) => { if error.current_context().is_db_not_found() { Ok(None) From 6b58f257faf4f12546fb49804b92de56a2c44a51 Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Mon, 24 Jun 2024 16:59:27 +0530 Subject: [PATCH 3/8] populate payment_method_info for saved apple or google pay --- crates/router/src/core/mandate/helpers.rs | 8 ++-- crates/router/src/core/payments/helpers.rs | 48 +++++++++++++++++++ .../router/src/core/payments/tokenization.rs | 18 ++----- 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/crates/router/src/core/mandate/helpers.rs b/crates/router/src/core/mandate/helpers.rs index 7fb0cfa28af..2bb981d03e8 100644 --- a/crates/router/src/core/mandate/helpers.rs +++ b/crates/router/src/core/mandate/helpers.rs @@ -70,9 +70,11 @@ pub fn get_mandate_type( Ok(Some(api::MandateTransactionType::NewMandateTransaction)) } - (_, _, Some(enums::FutureUsage::OffSession), _, Some(_)) | (_, Some(_), _, _, _) => Ok( - Some(api::MandateTransactionType::RecurringMandateTransaction), - ), + (_, _, Some(enums::FutureUsage::OffSession), _, Some(_)) + | (_, Some(_), _, _, _) + | (_, _, Some(enums::FutureUsage::OffSession), _, _) => Ok(Some( + api::MandateTransactionType::RecurringMandateTransaction, + )), _ => Ok(None), } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 9802860ef95..d050b17654b 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -533,6 +533,54 @@ pub async fn get_token_pm_type_mandate_details( mandate_generic_data.mandate_connector, mandate_generic_data.payment_method_info, ) + } else if request.payment_method_type + == Some(api_models::enums::PaymentMethodType::ApplePay) + || request.payment_method_type + == Some(api_models::enums::PaymentMethodType::GooglePay) + { + let customer_saved_pm_option = match state + .store + .find_payment_method_by_customer_id_merchant_id_list( + &request + .customer_id + .clone() + .get_required_value("customer_id")?, + merchant_account.merchant_id.as_str(), + None, + ) + .await + { + Ok(customer_payment_methods) => Ok(customer_payment_methods + .iter() + .find(|payment_method| { + payment_method.payment_method_type + == request.payment_method_type + }) + .cloned()), + Err(error) => { + if error.current_context().is_db_not_found() { + Ok(None) + } else { + Err(error) + .change_context( + errors::ApiErrorResponse::InternalServerError, + ) + .attach_printable( + "failed to find payment methods for a customer", + ) + } + } + }?; + + ( + None, + request.payment_method, + request.payment_method_type, + None, + None, + None, + customer_saved_pm_option, + ) } else { ( request.payment_token.to_owned(), diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 1a6f044cb0f..64fe526f0c0 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -515,7 +515,7 @@ where } }, None => { - let customer_saved_pm_option = if payment_method_type + let customer_saved_pm_id_option = if payment_method_type == Some(api_models::enums::PaymentMethodType::ApplePay) || payment_method_type == Some(api_models::enums::PaymentMethodType::GooglePay) @@ -534,7 +534,7 @@ where .find(|payment_method| { payment_method.payment_method_type == payment_method_type }) - .cloned()), + .map(|pm| pm.payment_method_id.clone())), Err(error) => { if error.current_context().is_db_not_found() { Ok(None) @@ -553,18 +553,8 @@ where Ok(None) }?; - if let Some(customer_saved_pm) = customer_saved_pm_option { - payment_methods::cards::update_last_used_at( - &customer_saved_pm, - state, - merchant_account.storage_scheme, - ) - .await - .map_err(|e| { - logger::error!("Failed to update last used at: {:?}", e); - }) - .ok(); - resp.payment_method_id = customer_saved_pm.payment_method_id; + if let Some(customer_saved_pm_id) = customer_saved_pm_id_option { + resp.payment_method_id = customer_saved_pm_id; } else { let pm_metadata = create_payment_method_metadata(None, connector_token)?; From 72071c4df5e7768febb09a3a3c07cff995a6ccfc Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Tue, 25 Jun 2024 16:32:28 +0530 Subject: [PATCH 4/8] consider guest customer --- crates/router/src/core/payments/helpers.rs | 91 ++++++++++++---------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index d050b17654b..e9df375ab6e 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -538,49 +538,58 @@ pub async fn get_token_pm_type_mandate_details( || request.payment_method_type == Some(api_models::enums::PaymentMethodType::GooglePay) { - let customer_saved_pm_option = match state - .store - .find_payment_method_by_customer_id_merchant_id_list( - &request - .customer_id - .clone() - .get_required_value("customer_id")?, - merchant_account.merchant_id.as_str(), - None, - ) - .await - { - Ok(customer_payment_methods) => Ok(customer_payment_methods - .iter() - .find(|payment_method| { - payment_method.payment_method_type - == request.payment_method_type - }) - .cloned()), - Err(error) => { - if error.current_context().is_db_not_found() { - Ok(None) - } else { - Err(error) - .change_context( - errors::ApiErrorResponse::InternalServerError, - ) - .attach_printable( - "failed to find payment methods for a customer", - ) + if let Some(customer_id) = &request.customer_id { + let customer_saved_pm_option = match state + .store + .find_payment_method_by_customer_id_merchant_id_list( + customer_id, + merchant_account.merchant_id.as_str(), + None, + ) + .await + { + Ok(customer_payment_methods) => Ok(customer_payment_methods + .iter() + .find(|payment_method| { + payment_method.payment_method_type + == request.payment_method_type + }) + .cloned()), + Err(error) => { + if error.current_context().is_db_not_found() { + Ok(None) + } else { + Err(error) + .change_context( + errors::ApiErrorResponse::InternalServerError, + ) + .attach_printable( + "failed to find payment methods for a customer", + ) + } } - } - }?; + }?; - ( - None, - request.payment_method, - request.payment_method_type, - None, - None, - None, - customer_saved_pm_option, - ) + ( + None, + request.payment_method, + request.payment_method_type, + None, + None, + None, + customer_saved_pm_option, + ) + } else { + ( + None, + request.payment_method, + request.payment_method_type, + None, + None, + None, + None, + ) + } } else { ( request.payment_token.to_owned(), From 936faceabf441f676fb3320f5dd0777fc0f9397f Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Sun, 23 Jun 2024 20:30:52 +0530 Subject: [PATCH 5/8] feat(router): updated last_used_at field for apple pay and google pay for CITs --- .../router/src/core/payments/tokenization.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 64fe526f0c0..56321697745 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -515,7 +515,7 @@ where } }, None => { - let customer_saved_pm_id_option = if payment_method_type + let customer_saved_pm_option = if payment_method_type == Some(api_models::enums::PaymentMethodType::ApplePay) || payment_method_type == Some(api_models::enums::PaymentMethodType::GooglePay) @@ -534,7 +534,7 @@ where .find(|payment_method| { payment_method.payment_method_type == payment_method_type }) - .map(|pm| pm.payment_method_id.clone())), + .map(|pm| pm.clone())), Err(error) => { if error.current_context().is_db_not_found() { Ok(None) @@ -553,8 +553,18 @@ where Ok(None) }?; - if let Some(customer_saved_pm_id) = customer_saved_pm_id_option { - resp.payment_method_id = customer_saved_pm_id; + if let Some(customer_saved_pm) = customer_saved_pm_option { + payment_methods::cards::update_last_used_at( + &customer_saved_pm, + state, + merchant_account.storage_scheme, + ) + .await + .map_err(|e| { + logger::error!("Failed to update last used at: {:?}", e); + }) + .ok(); + resp.payment_method_id = customer_saved_pm.payment_method_id; } else { let pm_metadata = create_payment_method_metadata(None, connector_token)?; From 04853156660b28b85ad47afd5b87af41a1afa2f4 Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Sun, 23 Jun 2024 21:20:12 +0530 Subject: [PATCH 6/8] fix clippy errors --- crates/router/src/core/payments/tokenization.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 56321697745..1a6f044cb0f 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -534,7 +534,7 @@ where .find(|payment_method| { payment_method.payment_method_type == payment_method_type }) - .map(|pm| pm.clone())), + .cloned()), Err(error) => { if error.current_context().is_db_not_found() { Ok(None) From 0dc6f8a426c681d061b9f40e234f0ceef290976d Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Tue, 25 Jun 2024 17:17:26 +0530 Subject: [PATCH 7/8] fix(router): update last used when the customer acceptence is passed in the recurring payment --- .../src/core/payments/operations/payment_response.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index b7a1ffb7c5d..68c9d776027 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -114,9 +114,14 @@ impl PostUpdateTracker, types::PaymentsAuthor .get_payment_method_billing() .and_then(|billing_details| billing_details.address.as_ref()) .and_then(|address| address.get_optional_full_name()); + let mut should_avoid_saving = false; if let Some(payment_method_info) = &payment_data.payment_method_info { if payment_data.payment_intent.off_session.is_none() && resp.response.is_ok() { + should_avoid_saving = resp.request.payment_method_type + == Some(enums::PaymentMethodType::ApplePay) + || resp.request.payment_method_type + == Some(enums::PaymentMethodType::GooglePay); payment_methods::cards::update_last_used_at( payment_method_info, state, @@ -179,6 +184,12 @@ impl PostUpdateTracker, types::PaymentsAuthor let (payment_method_id, _payment_method_status) = save_payment_call_future.await?; payment_data.payment_attempt.payment_method_id = payment_method_id; Ok(()) + } else if should_avoid_saving { + if let Some(pm_info) = &payment_data.payment_method_info { + payment_data.payment_attempt.payment_method_id = + Some(pm_info.payment_method_id.clone()); + }; + Ok(()) } else { // Save card flow let save_payment_data = tokenization::SavePaymentMethodData::from(resp); From 31121a6502a34c9510165d517284effed3f2db18 Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Wed, 26 Jun 2024 13:39:04 +0530 Subject: [PATCH 8/8] add update last used during the card duplication checks --- crates/diesel_models/src/payment_method.rs | 30 +++++++++++++++-- .../router/src/core/payment_methods/cards.rs | 25 +++++++++++--- .../router/src/core/payments/tokenization.rs | 33 +++++++------------ 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index d9c7a66a6b2..16e8ddeb7d5 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -96,8 +96,13 @@ pub struct TokenizeCoreWorkflow { #[derive(Debug, Serialize, Deserialize)] pub enum PaymentMethodUpdate { - MetadataUpdate { + MetadataUpdateAndLastUsed { metadata: Option, + last_used_at: PrimitiveDateTime, + }, + UpdatePaymentMethodDataAndLastUsed { + payment_method_data: Option, + last_used_at: PrimitiveDateTime, }, PaymentMethodDataUpdate { payment_method_data: Option, @@ -191,10 +196,13 @@ impl PaymentMethodUpdateInternal { impl From for PaymentMethodUpdateInternal { fn from(payment_method_update: PaymentMethodUpdate) -> Self { match payment_method_update { - PaymentMethodUpdate::MetadataUpdate { metadata } => Self { + PaymentMethodUpdate::MetadataUpdateAndLastUsed { + metadata, + last_used_at, + } => Self { metadata, payment_method_data: None, - last_used_at: None, + last_used_at: Some(last_used_at), network_transaction_id: None, status: None, locker_id: None, @@ -232,6 +240,22 @@ impl From for PaymentMethodUpdateInternal { payment_method_issuer: None, payment_method_type: None, }, + PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed { + payment_method_data, + last_used_at, + } => Self { + metadata: None, + payment_method_data, + last_used_at: Some(last_used_at), + network_transaction_id: None, + status: None, + locker_id: None, + payment_method: None, + connector_mandate_details: None, + updated_by: None, + payment_method_issuer: None, + payment_method_type: None, + }, PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate { network_transaction_id, status, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 204d7eb2439..35e5e884c73 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -1378,14 +1378,31 @@ pub async fn call_to_locker_hs<'a>( Ok(stored_card) } -pub async fn update_payment_method( +pub async fn update_payment_method_metadata_and_last_used( db: &dyn db::StorageInterface, pm: payment_method::PaymentMethod, - pm_metadata: serde_json::Value, + pm_metadata: Option, + storage_scheme: MerchantStorageScheme, +) -> errors::CustomResult<(), errors::VaultError> { + let pm_update = payment_method::PaymentMethodUpdate::MetadataUpdateAndLastUsed { + metadata: pm_metadata, + last_used_at: common_utils::date_time::now(), + }; + db.update_payment_method(pm, pm_update, storage_scheme) + .await + .change_context(errors::VaultError::UpdateInPaymentMethodDataTableFailed)?; + Ok(()) +} + +pub async fn update_payment_method_and_last_used( + db: &dyn db::StorageInterface, + pm: payment_method::PaymentMethod, + payment_method_update: Option, storage_scheme: MerchantStorageScheme, ) -> errors::CustomResult<(), errors::VaultError> { - let pm_update = payment_method::PaymentMethodUpdate::MetadataUpdate { - metadata: Some(pm_metadata), + let pm_update = payment_method::PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed { + payment_method_data: payment_method_update, + last_used_at: common_utils::date_time::now(), }; db.update_payment_method(pm, pm_update, storage_scheme) .await diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 1a6f044cb0f..649bf900bed 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -269,20 +269,15 @@ where pm.metadata.as_ref(), connector_token, )?; - if let Some(metadata) = pm_metadata { - payment_methods::cards::update_payment_method( - db, - pm.clone(), - metadata, - merchant_account.storage_scheme, - ) - .await - .change_context( - errors::ApiErrorResponse::InternalServerError, - ) - .attach_printable("Failed to add payment method in db")?; - }; - // update if its a off-session mit payment + payment_methods::cards::update_payment_method_metadata_and_last_used( + db, + pm.clone(), + pm_metadata, + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to add payment method in db")?; if check_for_mit_mandates { let connector_mandate_details = update_connector_mandate_details_in_payment_method( @@ -498,14 +493,10 @@ where ) .await; - let pm_update = - storage::PaymentMethodUpdate::PaymentMethodDataUpdate { - payment_method_data: pm_data_encrypted, - }; - - db.update_payment_method( + payment_methods::cards::update_payment_method_and_last_used( + db, existing_pm, - pm_update, + pm_data_encrypted, merchant_account.storage_scheme, ) .await