diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/conventional-commit-check.yml similarity index 89% rename from .github/workflows/pr-title-check.yml rename to .github/workflows/conventional-commit-check.yml index 167be295443b..5fd25e9332d1 100644 --- a/.github/workflows/pr-title-check.yml +++ b/.github/workflows/conventional-commit-check.yml @@ -1,4 +1,4 @@ -name: PR Title Checks +name: Conventional Commit Message Check on: # This is a dangerous event trigger as it causes the workflow to run in the @@ -35,21 +35,6 @@ env: CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse jobs: - typos: - name: Spell check PR title - runs-on: ubuntu-latest - steps: - - name: Store PR title in a file - shell: bash - env: - TITLE: ${{ github.event.pull_request.title }} - run: echo $TITLE > pr_title.txt - - - name: Spell check - uses: crate-ci/typos@master - with: - files: ./pr_title.txt - pr_title_check: name: Verify PR title follows conventional commit standards runs-on: ubuntu-latest diff --git a/.github/workflows/pr-title-spell-check.yml b/.github/workflows/pr-title-spell-check.yml new file mode 100644 index 000000000000..6ab6f184739d --- /dev/null +++ b/.github/workflows/pr-title-spell-check.yml @@ -0,0 +1,27 @@ +name: PR Title Spell Check + +on: + pull_request: + types: + - opened + - edited + - synchronize + +jobs: + typos: + name: Spell check PR title + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Store PR title in a file + shell: bash + env: + TITLE: ${{ github.event.pull_request.title }} + run: echo $TITLE > pr_title.txt + + - name: Spell check + uses: crate-ci/typos@master + with: + files: ./pr_title.txt diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index 2f2563ee3976..7b20c3865d15 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -49,3 +49,6 @@ pub(crate) const MERCHANT_ID_FIELD_EXTENSION_ID: &str = "1.2.840.113635.100.6.32 pub(crate) const METRICS_HOST_TAG_NAME: &str = "host"; pub const MAX_ROUTING_CONFIGS_PER_MERCHANT: usize = 100; pub const ROUTING_CONFIG_ID_LENGTH: usize = 10; + +pub const LOCKER_REDIS_PREFIX: &str = "LOCKER_PM_TOKEN"; +pub const LOCKER_REDIS_EXPIRY_SECONDS: u32 = 60 * 15; // 15 minutes diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 422c3fa19881..b19b381af507 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -13,7 +13,10 @@ use diesel_models::enums; use crate::{ core::{errors::RouterResult, payments::helpers}, routes::AppState, - types::api::{self, payments}, + types::{ + api::{self, payments}, + domain, + }, }; pub struct Oss; @@ -25,6 +28,7 @@ pub trait PaymentMethodRetrieve { state: &AppState, payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(Option, Option)>; } @@ -35,6 +39,7 @@ impl PaymentMethodRetrieve for Oss { state: &AppState, payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(Option, Option)> { match pm_data { pm_opt @ Some(pm @ api::PaymentMethodData::Card(_)) => { @@ -44,6 +49,7 @@ impl PaymentMethodRetrieve for Oss { payment_intent, enums::PaymentMethod::Card, pm, + merchant_key_store, ) .await?; @@ -64,6 +70,7 @@ impl PaymentMethodRetrieve for Oss { payment_intent, enums::PaymentMethod::BankTransfer, pm, + merchant_key_store, ) .await?; @@ -76,6 +83,7 @@ impl PaymentMethodRetrieve for Oss { payment_intent, enums::PaymentMethod::Wallet, pm, + merchant_key_store, ) .await?; @@ -88,6 +96,7 @@ impl PaymentMethodRetrieve for Oss { payment_intent, enums::PaymentMethod::BankRedirect, pm, + merchant_key_store, ) .await?; diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 417b030f5494..234323f0179a 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2009,7 +2009,7 @@ pub async fn list_customer_payment_method( let hyperswitch_token = generate_id(consts::ID_LENGTH, "token"); let card = if pm.payment_method == enums::PaymentMethod::Card { - get_card_details(&pm, key, state, &hyperswitch_token).await? + get_card_details(&pm, key, state, &hyperswitch_token, &key_store).await? } else { None }; @@ -2104,6 +2104,7 @@ async fn get_card_details( key: &[u8], state: &routes::AppState, hyperswitch_token: &str, + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult> { let mut _card_decrypted = decrypt::(pm.payment_method_data.clone(), key) @@ -2120,7 +2121,7 @@ async fn get_card_details( }); Ok(Some( - get_lookup_key_from_locker(state, hyperswitch_token, pm).await?, + get_lookup_key_from_locker(state, hyperswitch_token, pm, key_store).await?, )) } @@ -2128,6 +2129,7 @@ pub async fn get_lookup_key_from_locker( state: &routes::AppState, payment_token: &str, pm: &storage::PaymentMethod, + merchant_key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult { let card = get_card_from_locker( state, @@ -2142,9 +2144,15 @@ pub async fn get_lookup_key_from_locker( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Get Card Details Failed")?; let card = card_detail.clone(); - let resp = - BasiliskCardSupport::create_payment_method_data_in_locker(state, payment_token, card, pm) - .await?; + + let resp = TempLockerCardSupport::create_payment_method_data_in_temp_locker( + state, + payment_token, + card, + pm, + merchant_key_store, + ) + .await?; Ok(resp) } @@ -2177,6 +2185,7 @@ pub async fn get_lookup_key_for_payout_method( Some(payout_token.to_string()), &pm_parsed, Some(pm.customer_id.to_owned()), + key_store, ) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -2190,110 +2199,16 @@ pub async fn get_lookup_key_for_payout_method( } } -pub struct BasiliskCardSupport; +pub struct TempLockerCardSupport; -#[cfg(not(feature = "basilisk"))] -impl BasiliskCardSupport { - async fn create_payment_method_data_in_locker( - state: &routes::AppState, - payment_token: &str, - card: api::CardDetailFromLocker, - pm: &storage::PaymentMethod, - ) -> errors::RouterResult { - let card_number = card.card_number.clone().get_required_value("card_number")?; - let card_exp_month = card - .expiry_month - .clone() - .expose_option() - .get_required_value("expiry_month")?; - let card_exp_year = card - .expiry_year - .clone() - .expose_option() - .get_required_value("expiry_year")?; - let card_holder_name = card - .card_holder_name - .clone() - .expose_option() - .unwrap_or_default(); - let value1 = payment_methods::mk_card_value1( - card_number, - card_exp_year, - card_exp_month, - Some(card_holder_name), - None, - None, - None, - ) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value1 for locker")?; - let value2 = payment_methods::mk_card_value2( - None, - None, - None, - Some(pm.customer_id.to_string()), - Some(pm.payment_method_id.to_string()), - ) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value2 for locker")?; - - let value1 = vault::VaultPaymentMethod::Card(value1); - let value2 = vault::VaultPaymentMethod::Card(value2); - - let value1 = utils::Encode::::encode_to_string_of_json(&value1) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Wrapped value1 construction failed when saving card to locker")?; - - let value2 = utils::Encode::::encode_to_string_of_json(&value2) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Wrapped value2 construction failed when saving card to locker")?; - - let db_value = vault::MockTokenizeDBValue { value1, value2 }; - - let value_string = - utils::Encode::::encode_to_string_of_json(&db_value) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "Mock tokenize value construction failed when saving card to locker", - )?; - - let db = &*state.store; - - let already_present = db.find_config_by_key(payment_token).await; - - if already_present.is_err() { - let config = storage::ConfigNew { - key: payment_token.to_string(), - config: value_string, - }; - - db.insert_config(config) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization save to db failed")?; - } else { - let config_update = storage::ConfigUpdate::Update { - config: Some(value_string), - }; - - db.update_config_by_key(payment_token, config_update) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization db update failed")?; - } - - Ok(card) - } -} - -#[cfg(feature = "basilisk")] -impl BasiliskCardSupport { +impl TempLockerCardSupport { #[instrument(skip_all)] - async fn create_payment_method_data_in_locker( + async fn create_payment_method_data_in_temp_locker( state: &routes::AppState, payment_token: &str, card: api::CardDetailFromLocker, pm: &storage::PaymentMethod, + merchant_key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult { let card_number = card.card_number.clone().get_required_value("card_number")?; let card_exp_month = card @@ -2343,8 +2258,14 @@ impl BasiliskCardSupport { .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Wrapped value2 construction failed when saving card to locker")?; - let lookup_key = - vault::create_tokenize(state, value1, Some(value2), payment_token.to_string()).await?; + let lookup_key = vault::create_tokenize( + state, + value1, + Some(value2), + payment_token.to_string(), + merchant_key_store.key.get_inner(), + ) + .await?; vault::add_delete_tokenized_data_task( &*state.store, &lookup_key, diff --git a/crates/router/src/core/payment_methods/vault.rs b/crates/router/src/core/payment_methods/vault.rs index d16269deb9b2..5ad78c9d730e 100644 --- a/crates/router/src/core/payment_methods/vault.rs +++ b/crates/router/src/core/payment_methods/vault.rs @@ -1,34 +1,30 @@ -use common_utils::generate_id_with_default_len; -#[cfg(feature = "basilisk")] -use error_stack::report; -use error_stack::{IntoReport, ResultExt}; +use common_utils::{ + crypto::{DecodeMessage, EncodeMessage, GcmAes256}, + ext_traits::BytesExt, + generate_id_with_default_len, +}; +use error_stack::{report, IntoReport, ResultExt}; #[cfg(feature = "basilisk")] use josekit::jwe; use masking::PeekInterface; use router_env::{instrument, tracing}; -#[cfg(feature = "basilisk")] use scheduler::{types::process_data, utils as process_tracker_utils}; -#[cfg(feature = "basilisk")] -use crate::routes::metrics; #[cfg(feature = "payouts")] use crate::types::api::payouts; use crate::{ - configs::settings, + consts, core::errors::{self, CustomResult, RouterResult}, - logger, routes, + db, logger, routes, + routes::metrics, types::{ - api, - storage::{self, enums}, + api, domain, + storage::{self, enums, ProcessTrackerExt}, }, utils::{self, StringExt}, }; #[cfg(feature = "basilisk")] -use crate::{core::payment_methods::transformers as payment_methods, services, utils::BytesExt}; -#[cfg(feature = "basilisk")] -use crate::{db, types::storage::ProcessTrackerExt}; - -#[cfg(feature = "basilisk")] +use crate::{core::payment_methods::transformers as payment_methods, services, settings}; const VAULT_SERVICE_NAME: &str = "CARD"; #[cfg(feature = "basilisk")] const VAULT_VERSION: &str = "0"; @@ -622,196 +618,15 @@ pub struct MockTokenizeDBValue { pub struct Vault; -#[cfg(not(feature = "basilisk"))] impl Vault { #[instrument(skip_all)] pub async fn get_payment_method_data_from_locker( state: &routes::AppState, lookup_key: &str, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(Option, SupplementaryVaultData)> { - let config = state - .store - .find_config_by_key(lookup_key) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Could not find payment method in vault")?; - - let tokenize_value: MockTokenizeDBValue = config - .config - .parse_struct("MockTokenizeDBValue") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to deserialize Mock tokenize db value")?; - - let (payment_method, supp_data) = - api::PaymentMethodData::from_values(tokenize_value.value1, tokenize_value.value2) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error parsing Payment Method from Values")?; - - Ok((Some(payment_method), supp_data)) - } - - #[cfg(feature = "payouts")] - #[instrument(skip_all)] - pub async fn get_payout_method_data_from_temporary_locker( - state: &routes::AppState, - lookup_key: &str, - ) -> RouterResult<(Option, SupplementaryVaultData)> { - let config = state - .store - .find_config_by_key(lookup_key) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Could not find payment method in vault")?; - - let tokenize_value: MockTokenizeDBValue = config - .config - .parse_struct("MockTokenizeDBValue") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to deserialize Mock tokenize db value")?; - - let (payout_method, supp_data) = - api::PayoutMethodData::from_values(tokenize_value.value1, tokenize_value.value2) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error parsing Payout Method from Values")?; - - Ok((Some(payout_method), supp_data)) - } - - #[cfg(feature = "payouts")] - #[instrument(skip_all)] - pub async fn store_payout_method_data_in_locker( - state: &routes::AppState, - token_id: Option, - payout_method: &api::PayoutMethodData, - customer_id: Option, - ) -> RouterResult { - let value1 = payout_method - .get_value1(customer_id.clone()) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value1 for locker")?; - - let value2 = payout_method - .get_value2(customer_id) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value2 for locker")?; - - let lookup_key = token_id.unwrap_or_else(|| generate_id_with_default_len("token")); - - let db_value = MockTokenizeDBValue { value1, value2 }; - - let value_string = - utils::Encode::::encode_to_string_of_json(&db_value) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to encode payout method as mock tokenize db value")?; - - let already_present = state.store.find_config_by_key(&lookup_key).await; - - if already_present.is_err() { - let config = storage::ConfigNew { - key: lookup_key.clone(), - config: value_string, - }; - - state - .store - .insert_config(config) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization save to db failed insert")?; - } else { - let config_update = storage::ConfigUpdate::Update { - config: Some(value_string), - }; - state - .store - .update_config_by_key(&lookup_key, config_update) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization save to db failed update")?; - } - - Ok(lookup_key) - } - - #[instrument(skip_all)] - pub async fn store_payment_method_data_in_locker( - state: &routes::AppState, - token_id: Option, - payment_method: &api::PaymentMethodData, - customer_id: Option, - _pm: enums::PaymentMethod, - ) -> RouterResult { - let value1 = payment_method - .get_value1(customer_id.clone()) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value1 for locker")?; - - let value2 = payment_method - .get_value2(customer_id) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value12 for locker")?; - - let lookup_key = token_id.unwrap_or_else(|| generate_id_with_default_len("token")); - - let db_value = MockTokenizeDBValue { value1, value2 }; - - let value_string = - utils::Encode::::encode_to_string_of_json(&db_value) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to encode payment method as mock tokenize db value")?; - - let already_present = state.store.find_config_by_key(&lookup_key).await; - - if already_present.is_err() { - let config = storage::ConfigNew { - key: lookup_key.clone(), - config: value_string, - }; - - state - .store - .insert_config(config) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization save to db failed insert")?; - } else { - let config_update = storage::ConfigUpdate::Update { - config: Some(value_string), - }; - state - .store - .update_config_by_key(&lookup_key, config_update) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization save to db failed update")?; - } - - Ok(lookup_key) - } - - #[instrument(skip_all)] - pub async fn delete_locker_payment_method_by_lookup_key( - state: &routes::AppState, - lookup_key: &Option, - ) { - let db = &*state.store; - if let Some(id) = lookup_key { - match db.delete_config_by_key(id).await { - Ok(_) => logger::info!("Card Deleted from locker mock up"), - Err(err) => logger::error!("Err: Card Delete from locker Failed : {}", err), - } - } - } -} - -#[cfg(feature = "basilisk")] -impl Vault { - #[instrument(skip_all)] - pub async fn get_payment_method_data_from_locker( - state: &routes::AppState, - lookup_key: &str, - ) -> RouterResult<(Option, SupplementaryVaultData)> { - let de_tokenize = get_tokenized_data(state, lookup_key, true).await?; + let de_tokenize = + get_tokenized_data(state, lookup_key, true, merchant_key_store.key.get_inner()).await?; let (payment_method, customer_id) = api::PaymentMethodData::from_values(de_tokenize.value1, de_tokenize.value2) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -827,6 +642,7 @@ impl Vault { payment_method: &api::PaymentMethodData, customer_id: Option, pm: enums::PaymentMethod, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult { let value1 = payment_method .get_value1(customer_id.clone()) @@ -840,7 +656,14 @@ impl Vault { let lookup_key = token_id.unwrap_or_else(|| generate_id_with_default_len("token")); - let lookup_key = create_tokenize(state, value1, Some(value2), lookup_key).await?; + let lookup_key = create_tokenize( + state, + value1, + Some(value2), + lookup_key, + merchant_key_store.key.get_inner(), + ) + .await?; add_delete_tokenized_data_task(&*state.store, &lookup_key, pm).await?; metrics::TOKENIZED_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]); Ok(lookup_key) @@ -851,8 +674,10 @@ impl Vault { pub async fn get_payout_method_data_from_temporary_locker( state: &routes::AppState, lookup_key: &str, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(Option, SupplementaryVaultData)> { - let de_tokenize = get_tokenized_data(state, lookup_key, true).await?; + let de_tokenize = + get_tokenized_data(state, lookup_key, true, merchant_key_store.key.get_inner()).await?; let (payout_method, supp_data) = api::PayoutMethodData::from_values(de_tokenize.value1, de_tokenize.value2) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -868,6 +693,7 @@ impl Vault { token_id: Option, payout_method: &api::PayoutMethodData, customer_id: Option, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult { let value1 = payout_method .get_value1(customer_id.clone()) @@ -881,7 +707,14 @@ impl Vault { let lookup_key = token_id.unwrap_or_else(|| generate_id_with_default_len("token")); - let lookup_key = create_tokenize(state, value1, Some(value2), lookup_key).await?; + let lookup_key = create_tokenize( + state, + value1, + Some(value2), + lookup_key, + merchant_key_store.key.get_inner(), + ) + .await?; // add_delete_tokenized_data_task(&*state.store, &lookup_key, pm).await?; // scheduler_metrics::TOKENIZED_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]); Ok(lookup_key) @@ -893,31 +726,334 @@ impl Vault { lookup_key: &Option, ) { if let Some(lookup_key) = lookup_key { - let delete_resp = delete_tokenized_data(state, lookup_key).await; - match delete_resp { - Ok(resp) => { - if resp == "Ok" { - logger::info!("Card From locker deleted Successfully") - } else { - logger::error!("Error: Deleting Card From Locker : {:?}", resp) - } - } - Err(err) => logger::error!("Err: Deleting Card From Locker : {:?}", err), - } + delete_tokenized_data(state, lookup_key) + .await + .map(|_| logger::info!("Card From locker deleted Successfully")) + .map_err(|err| logger::error!("Error: Deleting Card From Redis Locker : {:?}", err)) + .ok(); } } } //------------------------------------------------TokenizeService------------------------------------------------ -pub fn get_key_id(keys: &settings::Jwekey) -> &str { - let key_identifier = "1"; // [#46]: Fetch this value from redis or external sources - if key_identifier == "1" { - &keys.locker_key_identifier1 - } else { - &keys.locker_key_identifier2 + +#[inline(always)] +fn get_redis_locker_key(lookup_key: &str) -> String { + format!("{}_{}", consts::LOCKER_REDIS_PREFIX, lookup_key) +} + +#[instrument(skip(state, value1, value2))] +pub async fn create_tokenize( + state: &routes::AppState, + value1: String, + value2: Option, + lookup_key: String, + encryption_key: &masking::Secret>, +) -> RouterResult { + let redis_key = get_redis_locker_key(lookup_key.as_str()); + let func = || async { + metrics::CREATED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); + + let payload_to_be_encrypted = api::TokenizePayloadRequest { + value1: value1.clone(), + value2: value2.clone().unwrap_or_default(), + lookup_key: lookup_key.clone(), + service_name: VAULT_SERVICE_NAME.to_string(), + }; + + let payload = utils::Encode::::encode_to_string_of_json( + &payload_to_be_encrypted, + ) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + let encrypted_payload = GcmAes256 + .encode_message(encryption_key.peek().as_ref(), payload.as_bytes()) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to encode redis temp locker data")?; + + let redis_conn = state + .store + .get_redis_conn() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to get redis connection")?; + + redis_conn + .set_key_if_not_exists_with_expiry( + redis_key.as_str(), + bytes::Bytes::from(encrypted_payload), + Some(i64::from(consts::LOCKER_REDIS_EXPIRY_SECONDS)), + ) + .await + .map(|_| lookup_key.clone()) + .map_err(|err| { + metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); + err + }) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error from redis locker") + }; + + match func().await { + Ok(s) => { + logger::info!( + "Insert payload in redis locker successful with lookup key: {:?}", + redis_key + ); + Ok(s) + } + Err(err) => { + logger::error!("Redis Temp locker Failed: {:?}", err); + + #[cfg(feature = "basilisk")] + return old_create_tokenize(state, value1, value2, lookup_key).await; + + #[cfg(not(feature = "basilisk"))] + Err(err) + } + } +} + +#[instrument(skip(state))] +pub async fn get_tokenized_data( + state: &routes::AppState, + lookup_key: &str, + _should_get_value2: bool, + encryption_key: &masking::Secret>, +) -> RouterResult { + let redis_key = get_redis_locker_key(lookup_key); + let func = || async { + metrics::GET_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); + + let redis_conn = state + .store + .get_redis_conn() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to get redis connection")?; + + let response = redis_conn.get_key::(redis_key.as_str()).await; + + match response { + Ok(resp) => { + let decrypted_payload = GcmAes256 + .decode_message( + encryption_key.peek().as_ref(), + masking::Secret::new(resp.into()), + ) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to decode redis temp locker data")?; + + let get_response: api::TokenizePayloadRequest = + bytes::Bytes::from(decrypted_payload) + .parse_struct("TokenizePayloadRequest") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Error getting TokenizePayloadRequest from tokenize response", + )?; + + Ok(get_response) + } + Err(err) => { + metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); + Err(err).change_context(errors::ApiErrorResponse::UnprocessableEntity { + message: "Token is invalid or expired".into(), + }) + } + } + }; + + match func().await { + Ok(s) => { + logger::info!( + "Fetch payload in redis locker successful with lookup key: {:?}", + redis_key + ); + Ok(s) + } + Err(err) => { + logger::error!("Redis Temp locker Failed: {:?}", err); + + #[cfg(feature = "basilisk")] + return old_get_tokenized_data(state, lookup_key, _should_get_value2).await; + + #[cfg(not(feature = "basilisk"))] + Err(err) + } + } +} + +#[instrument(skip(state))] +pub async fn delete_tokenized_data(state: &routes::AppState, lookup_key: &str) -> RouterResult<()> { + let redis_key = get_redis_locker_key(lookup_key); + let func = || async { + metrics::DELETED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); + + let redis_conn = state + .store + .get_redis_conn() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to get redis connection")?; + + let response = redis_conn.delete_key(redis_key.as_str()).await; + + match response { + Ok(redis_interface::DelReply::KeyDeleted) => Ok(()), + Ok(redis_interface::DelReply::KeyNotDeleted) => { + Err(errors::ApiErrorResponse::InternalServerError) + .into_report() + .attach_printable("Token invalid or expired") + } + Err(err) => { + metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); + Err(errors::ApiErrorResponse::InternalServerError) + .into_report() + .attach_printable_lazy(|| { + format!("Failed to delete from redis locker: {err:?}") + }) + } + } + }; + match func().await { + Ok(s) => { + logger::info!( + "Delete payload in redis locker successful with lookup key: {:?}", + redis_key + ); + Ok(s) + } + Err(err) => { + logger::error!("Redis Temp locker Failed: {:?}", err); + + #[cfg(feature = "basilisk")] + return old_delete_tokenized_data(state, lookup_key).await; + + #[cfg(not(feature = "basilisk"))] + Err(err) + } + } +} + +// ********************************************** PROCESS TRACKER ********************************************** + +pub async fn add_delete_tokenized_data_task( + db: &dyn db::StorageInterface, + lookup_key: &str, + pm: enums::PaymentMethod, +) -> RouterResult<()> { + let runner = "DELETE_TOKENIZE_DATA_WORKFLOW"; + let current_time = common_utils::date_time::now(); + let tracking_data = serde_json::to_value(storage::TokenizeCoreWorkflow { + lookup_key: lookup_key.to_owned(), + pm, + }) + .into_report() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| format!("unable to convert into value {lookup_key:?}"))?; + + let schedule_time = get_delete_tokenize_schedule_time(db, &pm, 0).await; + + let process_tracker_entry = storage::ProcessTrackerNew { + id: format!("{runner}_{lookup_key}"), + name: Some(String::from(runner)), + tag: vec![String::from("BASILISK-V3")], + runner: Some(String::from(runner)), + retry_count: 0, + schedule_time, + rule: String::new(), + tracking_data, + business_status: String::from("Pending"), + status: enums::ProcessTrackerStatus::New, + event: vec![], + created_at: current_time, + updated_at: current_time, + }; + let response = db.insert_process(process_tracker_entry).await; + response.map(|_| ()).or_else(|err| { + if err.current_context().is_db_unique_violation() { + Ok(()) + } else { + Err(report!(errors::ApiErrorResponse::InternalServerError)) + } + }) +} + +pub async fn start_tokenize_data_workflow( + state: &routes::AppState, + tokenize_tracker: &storage::ProcessTracker, +) -> Result<(), errors::ProcessTrackerError> { + let db = &*state.store; + let delete_tokenize_data = serde_json::from_value::( + tokenize_tracker.tracking_data.clone(), + ) + .into_report() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| { + format!( + "unable to convert into DeleteTokenizeByTokenRequest {:?}", + tokenize_tracker.tracking_data + ) + })?; + + match delete_tokenized_data(state, &delete_tokenize_data.lookup_key).await { + Ok(()) => { + logger::info!("Card From locker deleted Successfully"); + //mark task as finished + let id = tokenize_tracker.id.clone(); + tokenize_tracker + .clone() + .finish_with_status(db.as_scheduler(), format!("COMPLETED_BY_PT_{id}")) + .await?; + } + Err(err) => { + logger::error!("Err: Deleting Card From Locker : {:?}", err); + retry_delete_tokenize(db, &delete_tokenize_data.pm, tokenize_tracker.to_owned()) + .await?; + metrics::RETRIED_DELETE_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]); + } } + Ok(()) +} + +pub async fn get_delete_tokenize_schedule_time( + db: &dyn db::StorageInterface, + pm: &enums::PaymentMethod, + retry_count: i32, +) -> Option { + let redis_mapping = db::get_and_deserialize_key( + db, + &format!("pt_mapping_delete_{pm}_tokenize_data"), + "PaymentMethodsPTMapping", + ) + .await; + let mapping = match redis_mapping { + Ok(x) => x, + Err(err) => { + logger::info!("Redis Mapping Error: {}", err); + process_data::PaymentMethodsPTMapping::default() + } + }; + let time_delta = process_tracker_utils::get_pm_schedule_time(mapping, pm, retry_count + 1); + + process_tracker_utils::get_time_from_delta(time_delta) } +pub async fn retry_delete_tokenize( + db: &dyn db::StorageInterface, + pm: &enums::PaymentMethod, + pt: storage::ProcessTracker, +) -> Result<(), errors::ProcessTrackerError> { + let schedule_time = get_delete_tokenize_schedule_time(db, pm, pt.retry_count).await; + + match schedule_time { + Some(s_time) => pt.retry(db.as_scheduler(), s_time).await, + None => { + pt.finish_with_status(db.as_scheduler(), "RETRIES_EXCEEDED".to_string()) + .await + } + } +} + +// Fallback logic of old temp locker needs to be removed later + #[cfg(feature = "basilisk")] async fn get_locker_jwe_keys( keys: &settings::ActiveKmsSecrets, @@ -936,13 +1072,13 @@ async fn get_locker_jwe_keys( } #[cfg(feature = "basilisk")] -pub async fn create_tokenize( +#[instrument(skip(state, value1, value2))] +pub async fn old_create_tokenize( state: &routes::AppState, value1: String, value2: Option, lookup_key: String, ) -> RouterResult { - metrics::CREATED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); let payload_to_be_encrypted = api::TokenizePayloadRequest { value1, value2: value2.unwrap_or_default(), @@ -1017,7 +1153,7 @@ pub async fn create_tokenize( } #[cfg(feature = "basilisk")] -pub async fn get_tokenized_data( +pub async fn old_get_tokenized_data( state: &routes::AppState, lookup_key: &str, should_get_value2: bool, @@ -1096,10 +1232,10 @@ pub async fn get_tokenized_data( } #[cfg(feature = "basilisk")] -pub async fn delete_tokenized_data( +pub async fn old_delete_tokenized_data( state: &routes::AppState, lookup_key: &str, -) -> RouterResult { +) -> RouterResult<()> { metrics::DELETED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); let payload_to_be_encrypted = api::DeleteTokenizeByTokenRequest { lookup_key: lookup_key.to_string(), @@ -1136,11 +1272,11 @@ pub async fn delete_tokenized_data( .attach_printable("Error while making /tokenize/delete/token call to the locker")?; match response { Ok(r) => { - let delete_response = std::str::from_utf8(&r.response) + let _delete_response = std::str::from_utf8(&r.response) .into_report() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Decoding Failed for basilisk delete response")?; - Ok(delete_response.to_string()) + Ok(()) } Err(err) => { metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); @@ -1151,133 +1287,12 @@ pub async fn delete_tokenized_data( } } -// ********************************************** PROCESS TRACKER ********************************************** #[cfg(feature = "basilisk")] -pub async fn add_delete_tokenized_data_task( - db: &dyn db::StorageInterface, - lookup_key: &str, - pm: enums::PaymentMethod, -) -> RouterResult<()> { - let runner = "DELETE_TOKENIZE_DATA_WORKFLOW"; - let current_time = common_utils::date_time::now(); - let tracking_data = serde_json::to_value(storage::TokenizeCoreWorkflow { - lookup_key: lookup_key.to_owned(), - pm, - }) - .into_report() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable_lazy(|| format!("unable to convert into value {lookup_key:?}"))?; - - let schedule_time = get_delete_tokenize_schedule_time(db, &pm, 0).await; - - let process_tracker_entry = storage::ProcessTrackerNew { - id: format!("{runner}_{lookup_key}"), - name: Some(String::from(runner)), - tag: vec![String::from("BASILISK-V3")], - runner: Some(String::from(runner)), - retry_count: 0, - schedule_time, - rule: String::new(), - tracking_data, - business_status: String::from("Pending"), - status: enums::ProcessTrackerStatus::New, - event: vec![], - created_at: current_time, - updated_at: current_time, - }; - let response = db.insert_process(process_tracker_entry).await; - response.map(|_| ()).or_else(|err| { - if err.current_context().is_db_unique_violation() { - Ok(()) - } else { - Err(report!(errors::ApiErrorResponse::InternalServerError)) - } - }) -} - -#[cfg(feature = "basilisk")] -pub async fn start_tokenize_data_workflow( - state: &routes::AppState, - tokenize_tracker: &storage::ProcessTracker, -) -> Result<(), errors::ProcessTrackerError> { - let db = &*state.store; - let delete_tokenize_data = serde_json::from_value::( - tokenize_tracker.tracking_data.clone(), - ) - .into_report() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable_lazy(|| { - format!( - "unable to convert into DeleteTokenizeByTokenRequest {:?}", - tokenize_tracker.tracking_data - ) - })?; - - let delete_resp = delete_tokenized_data(state, &delete_tokenize_data.lookup_key).await; - match delete_resp { - Ok(resp) => { - if resp == "Ok" { - logger::info!("Card From locker deleted Successfully"); - //mark task as finished - let id = tokenize_tracker.id.clone(); - tokenize_tracker - .clone() - .finish_with_status(db.as_scheduler(), format!("COMPLETED_BY_PT_{id}")) - .await?; - } else { - logger::error!("Error: Deleting Card From Locker : {:?}", resp); - retry_delete_tokenize(db, &delete_tokenize_data.pm, tokenize_tracker.to_owned()) - .await?; - metrics::RETRIED_DELETE_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]); - } - } - Err(err) => { - logger::error!("Err: Deleting Card From Locker : {:?}", err); - retry_delete_tokenize(db, &delete_tokenize_data.pm, tokenize_tracker.to_owned()) - .await?; - metrics::RETRIED_DELETE_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]); - } - } - Ok(()) -} - -#[cfg(feature = "basilisk")] -pub async fn get_delete_tokenize_schedule_time( - db: &dyn db::StorageInterface, - pm: &enums::PaymentMethod, - retry_count: i32, -) -> Option { - let redis_mapping = db::get_and_deserialize_key( - db, - &format!("pt_mapping_delete_{pm}_tokenize_data"), - "PaymentMethodsPTMapping", - ) - .await; - let mapping = match redis_mapping { - Ok(x) => x, - Err(err) => { - logger::info!("Redis Mapping Error: {}", err); - process_data::PaymentMethodsPTMapping::default() - } - }; - let time_delta = process_tracker_utils::get_pm_schedule_time(mapping, pm, retry_count + 1); - - process_tracker_utils::get_time_from_delta(time_delta) -} - -#[cfg(feature = "basilisk")] -pub async fn retry_delete_tokenize( - db: &dyn db::StorageInterface, - pm: &enums::PaymentMethod, - pt: storage::ProcessTracker, -) -> Result<(), errors::ProcessTrackerError> { - let schedule_time = get_delete_tokenize_schedule_time(db, pm, pt.retry_count).await; - - match schedule_time { - Some(s_time) => pt.retry(db.as_scheduler(), s_time).await, - None => { - pt.finish_with_status(db.as_scheduler(), "RETRIES_EXCEEDED".to_string()) - .await - } +pub fn get_key_id(keys: &settings::Jwekey) -> &str { + let key_identifier = "1"; // [#46]: Fetch this value from redis or external sources + if key_identifier == "1" { + &keys.locker_key_identifier1 + } else { + &keys.locker_key_identifier2 } } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 586126467e18..98ab158e7935 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -153,6 +153,7 @@ where &operation, &mut payment_data, &validate_result, + &key_store, ) .await?; @@ -717,6 +718,7 @@ where payment_data, validate_result, &merchant_connector_account, + key_store, ) .await?; @@ -1399,6 +1401,7 @@ pub async fn get_connector_tokenization_action_when_confirm_true( payment_data: &mut PaymentData, validate_result: &operations::ValidateResult<'_>, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(PaymentData, TokenizationAction)> where F: Send + Clone, @@ -1461,7 +1464,12 @@ where TokenizationAction::TokenizeInRouter => { let (_operation, payment_method_data) = operation .to_domain()? - .make_pm_data(state, payment_data, validate_result.storage_scheme) + .make_pm_data( + state, + payment_data, + validate_result.storage_scheme, + merchant_key_store, + ) .await?; payment_data.payment_method_data = payment_method_data; TokenizationAction::SkipConnectorTokenization @@ -1471,7 +1479,12 @@ where TokenizationAction::TokenizeInConnectorAndRouter => { let (_operation, payment_method_data) = operation .to_domain()? - .make_pm_data(state, payment_data, validate_result.storage_scheme) + .make_pm_data( + state, + payment_data, + validate_result.storage_scheme, + merchant_key_store, + ) .await?; payment_data.payment_method_data = payment_method_data; @@ -1507,6 +1520,7 @@ pub async fn tokenize_in_router_when_confirm_false( operation: &BoxedOperation<'_, F, Req, Ctx>, payment_data: &mut PaymentData, validate_result: &operations::ValidateResult<'_>, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult> where F: Send + Clone, @@ -1516,7 +1530,12 @@ where let payment_data = if !is_operation_confirm(operation) { let (_operation, payment_method_data) = operation .to_domain()? - .make_pm_data(state, payment_data, validate_result.storage_scheme) + .make_pm_data( + state, + payment_data, + validate_result.storage_scheme, + merchant_key_store, + ) .await?; payment_data.payment_method_data = payment_method_data; payment_data diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 2c77184b9bd4..e27fe54c0ed0 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -95,37 +95,56 @@ impl Feature for types::PaymentsAu metrics::PAYMENT_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics - let save_payment_result = tokenization::save_payment_method( - state, - connector, - resp.to_owned(), - maybe_customer, - merchant_account, - self.request.payment_method_type, - key_store, - ) - .await; - - let pm_id = match save_payment_result { - Ok(payment_method_id) => Ok(payment_method_id), - Err(error) => { - if resp.request.setup_mandate_details.clone().is_some() { - Err(error) - } else { - logger::error!(save_payment_method_error=?error); - Ok(None) + if resp.request.setup_mandate_details.clone().is_some() { + let payment_method_id = tokenization::save_payment_method( + state, + connector, + resp.to_owned(), + maybe_customer, + merchant_account, + self.request.payment_method_type, + key_store, + ) + .await?; + Ok(mandate::mandate_procedure( + state, + resp, + maybe_customer, + payment_method_id, + connector.merchant_connector_id.clone(), + ) + .await?) + } else { + let connector = connector.clone(); + let response = resp.clone(); + let maybe_customer = maybe_customer.clone(); + let merchant_account = merchant_account.clone(); + let key_store = key_store.clone(); + let state = state.clone(); + + logger::info!("Initiating async call to save_payment_method in locker"); + + tokio::spawn(async move { + logger::info!("Starting async call to save_payment_method in locker"); + + let result = tokenization::save_payment_method( + &state, + &connector, + response, + &maybe_customer, + &merchant_account, + self.request.payment_method_type, + &key_store, + ) + .await; + + if let Err(err) = result { + logger::error!("Asynchronously saving card in locker failed : {:?}", err); } - } - }?; + }); - Ok(mandate::mandate_procedure( - state, - resp, - maybe_customer, - pm_id, - connector.merchant_connector_id.clone(), - ) - .await?) + Ok(resp) + } } else { Ok(self.clone()) } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index f42e4985380c..fd9fd7361da3 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -399,6 +399,7 @@ pub async fn get_token_pm_type_mandate_details( request: &api::PaymentsRequest, mandate_type: Option, merchant_account: &domain::MerchantAccount, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( Option, Option, @@ -427,7 +428,13 @@ pub async fn get_token_pm_type_mandate_details( recurring_mandate_payment_data, payment_method_type_, mandate_connector, - ) = get_token_for_recurring_mandate(state, request, merchant_account).await?; + ) = get_token_for_recurring_mandate( + state, + request, + merchant_account, + merchant_key_store, + ) + .await?; Ok(( token_, payment_method_, @@ -452,6 +459,7 @@ pub async fn get_token_for_recurring_mandate( state: &AppState, req: &api::PaymentsRequest, merchant_account: &domain::MerchantAccount, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( Option, Option, @@ -501,7 +509,9 @@ pub async fn get_token_for_recurring_mandate( }; if let diesel_models::enums::PaymentMethod::Card = payment_method.payment_method { - let _ = cards::get_lookup_key_from_locker(state, &token, &payment_method).await?; + let _ = + cards::get_lookup_key_from_locker(state, &token, &payment_method, merchant_key_store) + .await?; if let Some(payment_method_from_request) = req.payment_method { let pm: storage_enums::PaymentMethod = payment_method_from_request; if pm != payment_method.payment_method { @@ -1320,6 +1330,7 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>( operation: BoxedOperation<'a, F, R, Ctx>, state: &'a AppState, payment_data: &mut PaymentData, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, R, Ctx>, Option, @@ -1373,6 +1384,7 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>( let (pm, supplementary_data) = vault::Vault::get_payment_method_data_from_locker( state, &hyperswitch_token, + merchant_key_store, ) .await .attach_printable( @@ -1402,6 +1414,7 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>( &updated_pm, payment_data.payment_intent.customer_id.to_owned(), enums::PaymentMethod::Card, + merchant_key_store, ) .await?; Some(updated_pm) @@ -1442,6 +1455,7 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>( state, &payment_data.payment_intent, &payment_data.payment_attempt, + merchant_key_store, ) .await?; @@ -1461,6 +1475,7 @@ pub async fn store_in_vault_and_generate_ppmt( payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, payment_method: enums::PaymentMethod, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult { let router_token = vault::Vault::store_payment_method_data_in_locker( state, @@ -1468,6 +1483,7 @@ pub async fn store_in_vault_and_generate_ppmt( payment_method_data, payment_intent.customer_id.to_owned(), payment_method, + merchant_key_store, ) .await?; let parent_payment_method_token = generate_id(consts::ID_LENGTH, "token"); @@ -1491,6 +1507,7 @@ pub async fn store_payment_method_data_in_vault( payment_intent: &PaymentIntent, payment_method: enums::PaymentMethod, payment_method_data: &api::PaymentMethodData, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult> { if should_store_payment_method_data_in_vault( &state.conf.temp_locker_enable_config, @@ -1503,6 +1520,7 @@ pub async fn store_payment_method_data_in_vault( payment_intent, payment_attempt, payment_method, + merchant_key_store, ) .await?; diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index d198cd562a79..ad747ac2792a 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -123,6 +123,7 @@ pub trait Domain: Send + Sync { state: &'a AppState, payment_data: &mut PaymentData, storage_scheme: enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, R, Ctx>, Option, @@ -233,11 +234,12 @@ where state: &'a AppState, payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRetrieveRequest, Ctx>, Option, )> { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } } @@ -282,6 +284,7 @@ where _state: &'a AppState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCaptureRequest, Ctx>, Option, @@ -343,6 +346,7 @@ where _state: &'a AppState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCancelRequest, Ctx>, Option, @@ -394,6 +398,7 @@ where _state: &'a AppState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRejectRequest, Ctx>, Option, diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 16bb84f69ddb..4cd1bae04dee 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -88,6 +88,7 @@ impl request, mandate_type.clone(), merchant_account, + key_store, ) .await?; @@ -299,12 +300,13 @@ impl Domain, _storage_scheme: storage_enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { let (op, payment_method_data) = - helpers::make_pm_data(Box::new(self), state, payment_data).await?; + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await?; utils::when(payment_method_data.is_none(), || { Err(errors::ApiErrorResponse::PaymentMethodNotFound) diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index a2f5292a37f7..4e87b3869431 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -87,6 +87,7 @@ impl request, mandate_type.clone(), merchant_account, + key_store, ) .await?; @@ -294,12 +295,13 @@ impl Domain, _storage_scheme: storage_enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { let (op, payment_method_data) = - helpers::make_pm_data(Box::new(self), state, payment_data).await?; + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await?; Ok((op, payment_method_data)) } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 8842963990b6..5de281a5e63c 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -69,6 +69,7 @@ impl request, mandate_type.clone(), merchant_account, + key_store, ); let (mut payment_intent, mandate_details) = @@ -423,12 +424,13 @@ impl Domain, _storage_scheme: storage_enums::MerchantStorageScheme, + key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { let (op, payment_method_data) = - helpers::make_pm_data(Box::new(self), state, payment_data).await?; + helpers::make_pm_data(Box::new(self), state, payment_data, key_store).await?; utils::when(payment_method_data.is_none(), || { Err(errors::ApiErrorResponse::PaymentMethodNotFound) diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index f3b777534bf7..909f4d456530 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -107,6 +107,7 @@ impl request, mandate_type, merchant_account, + merchant_key_store, ) .await?; @@ -353,11 +354,12 @@ impl Domain, _storage_scheme: enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } #[instrument(skip_all)] diff --git a/crates/router/src/core/payments/operations/payment_method_validate.rs b/crates/router/src/core/payments/operations/payment_method_validate.rs index 6d97f7b66cd1..33f6c23c8363 100644 --- a/crates/router/src/core/payments/operations/payment_method_validate.rs +++ b/crates/router/src/core/payments/operations/payment_method_validate.rs @@ -297,11 +297,12 @@ where state: &'a AppState, payment_data: &mut PaymentData, _storage_scheme: storage_enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::VerifyRequest, Ctx>, Option, )> { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } async fn get_connector<'a>( diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index cf16a053592b..354c62648bb3 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -318,6 +318,7 @@ where _state: &'b AppState, _payment_data: &mut PaymentData, _storage_scheme: storage_enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'b, F, api::PaymentsSessionRequest, Ctx>, Option, diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index 227e7e2f90db..e9fa301bf07b 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -282,6 +282,7 @@ where state: &'a AppState, payment_data: &mut PaymentData, _storage_scheme: storage_enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsStartRequest, Ctx>, Option, @@ -293,7 +294,7 @@ where .map(|connector_name| connector_name == *"bluesnap".to_string()) .unwrap_or(false) { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } else { Ok((Box::new(self), None)) } diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index d20830d9bc6b..96aac6c9d79b 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -95,11 +95,12 @@ impl Domain, _storage_scheme: enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } #[instrument(skip_all)] diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index d0b17b5d460d..622d09754396 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -106,6 +106,7 @@ impl request, mandate_type.clone(), merchant_account, + key_store, ) .await?; @@ -394,11 +395,12 @@ impl Domain, _storage_scheme: storage_enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } #[instrument(skip_all)] diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index f7831465e1ce..794180e2112e 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -1,6 +1,7 @@ use common_utils::{ext_traits::ValueExt, pii}; use error_stack::{report, ResultExt}; use masking::ExposeInterface; +use router_env::{instrument, tracing}; use super::helpers; use crate::{ @@ -20,6 +21,7 @@ use crate::{ utils::OptionExt, }; +#[instrument(skip_all)] pub async fn save_payment_method( state: &AppState, connector: &api::ConnectorData, diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index ddb2a017e35a..f1136a35a65a 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -112,7 +112,7 @@ where // Validate create request let (payout_id, payout_method_data) = - validator::validate_create_request(&state, &merchant_account, &req).await?; + validator::validate_create_request(&state, &merchant_account, &req, &key_store).await?; // Create DB entries let mut payout_data = payout_create_db_entries( @@ -403,6 +403,7 @@ pub async fn payouts_fulfill_core( &payout_attempt.merchant_id, &payout_attempt.payout_id, Some(&payout_data.payouts.payout_type), + &key_store, ) .await? .get_required_value("payout_method_data")?, @@ -458,6 +459,7 @@ pub async fn call_connector_payout( &payout_attempt.merchant_id, &payout_attempt.payout_id, Some(&payouts.payout_type), + key_store, ) .await? .get_required_value("payout_method_data")?, diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 9890cd9d5efd..39079ea36cd6 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -28,6 +28,7 @@ use crate::{ utils::{self, OptionExt}, }; +#[allow(clippy::too_many_arguments)] pub async fn make_payout_method_data<'a>( state: &'a AppState, payout_method_data: Option<&api::PayoutMethodData>, @@ -36,6 +37,7 @@ pub async fn make_payout_method_data<'a>( merchant_id: &str, payout_id: &str, payout_type: Option<&api_enums::PayoutType>, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult> { let db = &*state.store; let hyperswitch_token = if let Some(payout_token) = payout_token { @@ -67,14 +69,16 @@ pub async fn make_payout_method_data<'a>( match (payout_method_data.to_owned(), hyperswitch_token) { // Get operation (None, Some(payout_token)) => { - let (pm, supplementary_data) = vault::Vault::get_payout_method_data_from_temporary_locker( - state, - &payout_token, - ) - .await - .attach_printable( - "Payout method for given token not found or there was a problem fetching it", - )?; + let (pm, supplementary_data) = + vault::Vault::get_payout_method_data_from_temporary_locker( + state, + &payout_token, + merchant_key_store, + ) + .await + .attach_printable( + "Payout method for given token not found or there was a problem fetching it", + )?; utils::when( supplementary_data .customer_id @@ -93,6 +97,7 @@ pub async fn make_payout_method_data<'a>( payout_token.to_owned(), payout_method, Some(customer_id.to_owned()), + merchant_key_store, ) .await?; diff --git a/crates/router/src/core/payouts/validator.rs b/crates/router/src/core/payouts/validator.rs index c815d91e41dd..3793ee523dc3 100644 --- a/crates/router/src/core/payouts/validator.rs +++ b/crates/router/src/core/payouts/validator.rs @@ -57,6 +57,7 @@ pub async fn validate_create_request( state: &AppState, merchant_account: &domain::MerchantAccount, req: &payouts::PayoutCreateRequest, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(String, Option)> { let merchant_id = &merchant_account.merchant_id; @@ -103,6 +104,7 @@ pub async fn validate_create_request( &merchant_account.merchant_id, payout_id.as_ref(), req.payout_type.as_ref(), + merchant_key_store, ) .await? } diff --git a/crates/router/src/types/api/payment_methods.rs b/crates/router/src/types/api/payment_methods.rs index e5bf1d8dd1bf..5acb66b5068e 100644 --- a/crates/router/src/types/api/payment_methods.rs +++ b/crates/router/src/types/api/payment_methods.rs @@ -1,12 +1,11 @@ use api_models::enums as api_enums; pub use api_models::payment_methods::{ CardDetail, CardDetailFromLocker, CardDetailsPaymentMethod, CustomerPaymentMethod, - CustomerPaymentMethodsListResponse, DeleteTokenizeByDateRequest, DeleteTokenizeByTokenRequest, - GetTokenizePayloadRequest, GetTokenizePayloadResponse, PaymentMethodCreate, - PaymentMethodDeleteResponse, PaymentMethodId, PaymentMethodList, PaymentMethodListRequest, - PaymentMethodListResponse, PaymentMethodResponse, PaymentMethodUpdate, PaymentMethodsData, - TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2, - TokenizedWalletValue1, TokenizedWalletValue2, + CustomerPaymentMethodsListResponse, DeleteTokenizeByTokenRequest, GetTokenizePayloadRequest, + GetTokenizePayloadResponse, PaymentMethodCreate, PaymentMethodDeleteResponse, PaymentMethodId, + PaymentMethodList, PaymentMethodListRequest, PaymentMethodListResponse, PaymentMethodResponse, + PaymentMethodUpdate, PaymentMethodsData, TokenizePayloadEncrypted, TokenizePayloadRequest, + TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, }; use error_stack::report; diff --git a/crates/router/src/workflows/tokenized_data.rs b/crates/router/src/workflows/tokenized_data.rs index 2f5d33173276..0674982f92fe 100644 --- a/crates/router/src/workflows/tokenized_data.rs +++ b/crates/router/src/workflows/tokenized_data.rs @@ -1,14 +1,13 @@ use scheduler::consumer::workflows::ProcessTrackerWorkflow; -#[cfg(feature = "basilisk")] -use crate::core::payment_methods::vault; -use crate::{errors, logger::error, routes::AppState, types::storage}; +use crate::{ + core::payment_methods::vault, errors, logger::error, routes::AppState, types::storage, +}; pub struct DeleteTokenizeDataWorkflow; #[async_trait::async_trait] impl ProcessTrackerWorkflow for DeleteTokenizeDataWorkflow { - #[cfg(feature = "basilisk")] async fn execute_workflow<'a>( &'a self, state: &'a AppState, @@ -17,15 +16,6 @@ impl ProcessTrackerWorkflow for DeleteTokenizeDataWorkflow { Ok(vault::start_tokenize_data_workflow(state, &process).await?) } - #[cfg(not(feature = "basilisk"))] - async fn execute_workflow<'a>( - &'a self, - _state: &'a AppState, - _process: storage::ProcessTracker, - ) -> Result<(), errors::ProcessTrackerError> { - Ok(()) - } - async fn error_handler<'a>( &'a self, _state: &'a AppState,