feat(core): use redis as temp locker instead of basilisk (#2789)

This commit is contained in:
Abhishek Marrivagu
2023-11-08 13:12:28 +05:30
committed by GitHub
parent 9cc8b93070
commit 6678689265
23 changed files with 553 additions and 530 deletions

View File

@ -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

View File

@ -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<payments::PaymentMethodData>, Option<String>)>;
}
@ -35,6 +39,7 @@ impl PaymentMethodRetrieve for Oss {
state: &AppState,
payment_intent: &PaymentIntent,
payment_attempt: &PaymentAttempt,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(Option<payments::PaymentMethodData>, Option<String>)> {
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?;

View File

@ -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<Option<api::CardDetailFromLocker>> {
let mut _card_decrypted =
decrypt::<serde_json::Value, masking::WithType>(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<api::CardDetailFromLocker> {
let card = get_card_from_locker(
state,
@ -2142,8 +2144,14 @@ 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)
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<api::CardDetailFromLocker> {
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::<vault::VaultPaymentMethod>::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::<vault::VaultPaymentMethod>::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::<vault::MockTokenizeDBValue>::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<api::CardDetailFromLocker> {
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,

View File

@ -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<api::PaymentMethodData>, 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<api::PayoutMethodData>, 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<String>,
payout_method: &api::PayoutMethodData,
customer_id: Option<String>,
) -> RouterResult<String> {
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::<MockTokenizeDBValue>::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<String>,
payment_method: &api::PaymentMethodData,
customer_id: Option<String>,
_pm: enums::PaymentMethod,
) -> RouterResult<String> {
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::<MockTokenizeDBValue>::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<String>,
) {
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<api::PaymentMethodData>, 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<String>,
pm: enums::PaymentMethod,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<String> {
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<api::PayoutMethodData>, 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<String>,
payout_method: &api::PayoutMethodData,
customer_id: Option<String>,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<String> {
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<String>,
) {
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<String>,
lookup_key: String,
encryption_key: &masking::Secret<Vec<u8>>,
) -> RouterResult<String> {
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::<api::TokenizePayloadRequest>::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<Vec<u8>>,
) -> RouterResult<api::TokenizePayloadRequest> {
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::<bytes::Bytes>(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::<storage::TokenizeCoreWorkflow>(
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<time::PrimitiveDateTime> {
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<String>,
lookup_key: String,
) -> RouterResult<String> {
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<String> {
) -> 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(())
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 {
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::<storage::TokenizeCoreWorkflow>(
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<time::PrimitiveDateTime> {
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
}
&keys.locker_key_identifier2
}
}

View File

@ -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<F, Req, Ctx>(
payment_data: &mut PaymentData<F>,
validate_result: &operations::ValidateResult<'_>,
merchant_connector_account: &helpers::MerchantConnectorAccountType,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(PaymentData<F>, 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<F, Req, Ctx>(
operation: &BoxedOperation<'_, F, Req, Ctx>,
payment_data: &mut PaymentData<F>,
validate_result: &operations::ValidateResult<'_>,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<PaymentData<F>>
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

View File

@ -95,7 +95,8 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
metrics::PAYMENT_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics
let save_payment_result = tokenization::save_payment_method(
if resp.request.setup_mandate_details.clone().is_some() {
let payment_method_id = tokenization::save_payment_method(
state,
connector,
resp.to_owned(),
@ -104,28 +105,46 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
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)
}
}
}?;
.await?;
Ok(mandate::mandate_procedure(
state,
resp,
maybe_customer,
pm_id,
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(resp)
}
} else {
Ok(self.clone())
}

View File

@ -399,6 +399,7 @@ pub async fn get_token_pm_type_mandate_details(
request: &api::PaymentsRequest,
mandate_type: Option<api::MandateTransactionType>,
merchant_account: &domain::MerchantAccount,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
Option<String>,
Option<storage_enums::PaymentMethod>,
@ -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<String>,
Option<storage_enums::PaymentMethod>,
@ -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<F>,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, R, Ctx>,
Option<api::PaymentMethodData>,
@ -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<String> {
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<Option<String>> {
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?;

View File

@ -123,6 +123,7 @@ pub trait Domain<F: Clone, R, Ctx: PaymentMethodRetrieve>: Send + Sync {
state: &'a AppState,
payment_data: &mut PaymentData<F>,
storage_scheme: enums::MerchantStorageScheme,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, R, Ctx>,
Option<api::PaymentMethodData>,
@ -233,11 +234,12 @@ where
state: &'a AppState,
payment_data: &mut PaymentData<F>,
_storage_scheme: enums::MerchantStorageScheme,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsRetrieveRequest, Ctx>,
Option<api::PaymentMethodData>,
)> {
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<F>,
_storage_scheme: enums::MerchantStorageScheme,
_merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsCaptureRequest, Ctx>,
Option<api::PaymentMethodData>,
@ -343,6 +346,7 @@ where
_state: &'a AppState,
_payment_data: &mut PaymentData<F>,
_storage_scheme: enums::MerchantStorageScheme,
_merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsCancelRequest, Ctx>,
Option<api::PaymentMethodData>,
@ -394,6 +398,7 @@ where
_state: &'a AppState,
_payment_data: &mut PaymentData<F>,
_storage_scheme: enums::MerchantStorageScheme,
_merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsRejectRequest, Ctx>,
Option<api::PaymentMethodData>,

View File

@ -88,6 +88,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
request,
mandate_type.clone(),
merchant_account,
key_store,
)
.await?;
@ -299,12 +300,13 @@ impl<F: Clone + Send, Ctx: PaymentMethodRetrieve> Domain<F, api::PaymentsRequest
state: &'a AppState,
payment_data: &mut PaymentData<F>,
_storage_scheme: storage_enums::MerchantStorageScheme,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsRequest, Ctx>,
Option<api::PaymentMethodData>,
)> {
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)

View File

@ -87,6 +87,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
request,
mandate_type.clone(),
merchant_account,
key_store,
)
.await?;
@ -294,12 +295,13 @@ impl<F: Clone + Send, Ctx: PaymentMethodRetrieve> Domain<F, api::PaymentsRequest
state: &'a AppState,
payment_data: &mut PaymentData<F>,
_storage_scheme: storage_enums::MerchantStorageScheme,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsRequest, Ctx>,
Option<api::PaymentMethodData>,
)> {
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))
}

View File

@ -69,6 +69,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
request,
mandate_type.clone(),
merchant_account,
key_store,
);
let (mut payment_intent, mandate_details) =
@ -423,12 +424,13 @@ impl<F: Clone + Send, Ctx: PaymentMethodRetrieve> Domain<F, api::PaymentsRequest
state: &'a AppState,
payment_data: &mut PaymentData<F>,
_storage_scheme: storage_enums::MerchantStorageScheme,
key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsRequest, Ctx>,
Option<api::PaymentMethodData>,
)> {
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)

View File

@ -107,6 +107,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
request,
mandate_type,
merchant_account,
merchant_key_store,
)
.await?;
@ -353,11 +354,12 @@ impl<F: Clone + Send, Ctx: PaymentMethodRetrieve> Domain<F, api::PaymentsRequest
state: &'a AppState,
payment_data: &mut PaymentData<F>,
_storage_scheme: enums::MerchantStorageScheme,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsRequest, Ctx>,
Option<api::PaymentMethodData>,
)> {
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)]

View File

@ -297,11 +297,12 @@ where
state: &'a AppState,
payment_data: &mut PaymentData<F>,
_storage_scheme: storage_enums::MerchantStorageScheme,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::VerifyRequest, Ctx>,
Option<api::PaymentMethodData>,
)> {
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>(

View File

@ -318,6 +318,7 @@ where
_state: &'b AppState,
_payment_data: &mut PaymentData<F>,
_storage_scheme: storage_enums::MerchantStorageScheme,
_merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'b, F, api::PaymentsSessionRequest, Ctx>,
Option<api::PaymentMethodData>,

View File

@ -282,6 +282,7 @@ where
state: &'a AppState,
payment_data: &mut PaymentData<F>,
_storage_scheme: storage_enums::MerchantStorageScheme,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsStartRequest, Ctx>,
Option<api::PaymentMethodData>,
@ -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))
}

View File

@ -95,11 +95,12 @@ impl<F: Clone + Send, Ctx: PaymentMethodRetrieve> Domain<F, api::PaymentsRequest
state: &'a AppState,
payment_data: &mut PaymentData<F>,
_storage_scheme: enums::MerchantStorageScheme,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsRequest, Ctx>,
Option<api::PaymentMethodData>,
)> {
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)]

View File

@ -106,6 +106,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
request,
mandate_type.clone(),
merchant_account,
key_store,
)
.await?;
@ -394,11 +395,12 @@ impl<F: Clone + Send, Ctx: PaymentMethodRetrieve> Domain<F, api::PaymentsRequest
state: &'a AppState,
payment_data: &mut PaymentData<F>,
_storage_scheme: storage_enums::MerchantStorageScheme,
merchant_key_store: &domain::MerchantKeyStore,
) -> RouterResult<(
BoxedOperation<'a, F, api::PaymentsRequest, Ctx>,
Option<api::PaymentMethodData>,
)> {
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)]

View File

@ -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<F: Clone, FData>(
state: &AppState,
connector: &api::ConnectorData,

View File

@ -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")?,

View File

@ -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<Option<api::PayoutMethodData>> {
let db = &*state.store;
let hyperswitch_token = if let Some(payout_token) = payout_token {
@ -67,9 +69,11 @@ 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(
let (pm, supplementary_data) =
vault::Vault::get_payout_method_data_from_temporary_locker(
state,
&payout_token,
merchant_key_store,
)
.await
.attach_printable(
@ -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?;

View File

@ -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<payouts::PayoutMethodData>)> {
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?
}

View File

@ -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;

View File

@ -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<AppState> for DeleteTokenizeDataWorkflow {
#[cfg(feature = "basilisk")]
async fn execute_workflow<'a>(
&'a self,
state: &'a AppState,
@ -17,15 +16,6 @@ impl ProcessTrackerWorkflow<AppState> 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,