Skip to content

Commit

Permalink
refactor(core): add locker config to enable or disable locker (#3352)
Browse files Browse the repository at this point in the history
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
Aprabhat19 and hyperswitch-bot[bot] authored Jan 18, 2024
1 parent 059e866 commit bd5356e
Show file tree
Hide file tree
Showing 23 changed files with 505 additions and 98 deletions.
1 change: 1 addition & 0 deletions config/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ host_rs = "" # Rust Locker host
mock_locker = true # Emulate a locker locally using Postgres
basilisk_host = "" # Basilisk host
locker_signing_key_id = "1" # Key_id to sign basilisk hs locker
locker_enabled = true # Boolean to enable or disable saving cards in locker

[delayed_session_response]
connectors_with_delayed_session_response = "trustpay,payme" # List of connectors which has delayed session response
Expand Down
2 changes: 2 additions & 0 deletions config/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ host = ""
host_rs = ""
mock_locker = true
basilisk_host = ""
locker_enabled = true


[forex_api]
call_delay = 21600
Expand Down
1 change: 1 addition & 0 deletions config/docker_compose.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ host = ""
host_rs = ""
mock_locker = true
basilisk_host = ""
locker_enabled = true

[jwekey]
vault_encryption_key = ""
Expand Down
11 changes: 11 additions & 0 deletions crates/api_models/src/mandates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub struct MandateResponse {
pub payment_method_id: String,
/// The payment method
pub payment_method: String,
/// The payment method type
pub payment_method_type: Option<String>,
/// The card details for mandate
pub card: Option<MandateCardDetails>,
/// Details about the customer’s acceptance
Expand Down Expand Up @@ -66,6 +68,15 @@ pub struct MandateCardDetails {
#[schema(value_type = Option<String>)]
/// A unique identifier alias to identify a particular card
pub card_fingerprint: Option<Secret<String>>,
/// The first 6 digits of card
pub card_isin: Option<String>,
/// The bank that issued the card
pub card_issuer: Option<String>,
/// The network that facilitates payment card transactions
#[schema(value_type = Option<CardNetwork>)]
pub card_network: Option<api_enums::CardNetwork>,
/// The type of the payment card
pub card_type: Option<String>,
}

#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
Expand Down
41 changes: 41 additions & 0 deletions crates/api_models/src/payment_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ pub struct CardDetail {
/// Card Holder's Nick Name
#[schema(value_type = Option<String>,example = "John Doe")]
pub nick_name: Option<masking::Secret<String>>,

/// Card Issuing Country
pub card_issuing_country: Option<String>,

/// Card's Network
#[schema(value_type = Option<CardNetwork>)]
pub card_network: Option<api_enums::CardNetwork>,

/// Issuer Bank for Card
pub card_issuer: Option<String>,

/// Card Type
pub card_type: Option<String>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
Expand Down Expand Up @@ -177,6 +190,12 @@ pub struct CardDetailsPaymentMethod {
pub expiry_year: Option<masking::Secret<String>>,
pub nick_name: Option<masking::Secret<String>>,
pub card_holder_name: Option<masking::Secret<String>>,
pub card_isin: Option<String>,
pub card_issuer: Option<String>,
pub card_network: Option<api_enums::CardNetwork>,
pub card_type: Option<String>,
#[serde(default = "saved_in_locker_default")]
pub saved_to_locker: bool,
}

#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
Expand Down Expand Up @@ -227,6 +246,18 @@ pub struct CardDetailFromLocker {

#[schema(value_type=Option<String>)]
pub nick_name: Option<masking::Secret<String>>,

#[schema(value_type = Option<CardNetwork>)]
pub card_network: Option<api_enums::CardNetwork>,

pub card_isin: Option<String>,
pub card_issuer: Option<String>,
pub card_type: Option<String>,
pub saved_to_locker: bool,
}

fn saved_in_locker_default() -> bool {
true
}

impl From<CardDetailsPaymentMethod> for CardDetailFromLocker {
Expand All @@ -242,6 +273,11 @@ impl From<CardDetailsPaymentMethod> for CardDetailFromLocker {
card_holder_name: item.card_holder_name,
card_fingerprint: None,
nick_name: item.nick_name,
card_isin: item.card_isin,
card_issuer: item.card_issuer,
card_network: item.card_network,
card_type: item.card_type,
saved_to_locker: item.saved_to_locker,
}
}
}
Expand All @@ -255,6 +291,11 @@ impl From<CardDetailFromLocker> for CardDetailsPaymentMethod {
expiry_year: item.expiry_year,
nick_name: item.nick_name,
card_holder_name: item.card_holder_name,
card_isin: item.card_isin,
card_issuer: item.card_issuer,
card_network: item.card_network,
card_type: item.card_type,
saved_to_locker: item.saved_to_locker,
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/router/src/configs/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ impl Default for super::settings::Locker {
mock_locker: true,
basilisk_host: "localhost".into(),
locker_signing_key_id: "1".into(),
//true or false
locker_enabled: true,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/router/src/configs/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ pub struct Locker {
pub mock_locker: bool,
pub basilisk_host: String,
pub locker_signing_key_id: String,
pub locker_enabled: bool,
}

#[derive(Debug, Deserialize, Clone)]
Expand Down
4 changes: 4 additions & 0 deletions crates/router/src/core/locker_migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ pub async fn call_to_locker(
card_exp_year: card.card_exp_year,
card_holder_name: card.name_on_card,
nick_name: card.nick_name.map(masking::Secret::new),
card_issuing_country: None,
card_network: None,
card_issuer: None,
card_type: None,
};

let pm_create = api::PaymentMethodCreate {
Expand Down
18 changes: 11 additions & 7 deletions crates/router/src/core/mandate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::{
pub async fn get_mandate(
state: AppState,
merchant_account: domain::MerchantAccount,
key_store: domain::MerchantKeyStore,
req: mandates::MandateId,
) -> RouterResponse<mandates::MandateResponse> {
let mandate = state
Expand All @@ -42,7 +43,7 @@ pub async fn get_mandate(
.await
.to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
Ok(services::ApplicationResponse::Json(
mandates::MandateResponse::from_db_mandate(&state, mandate).await?,
mandates::MandateResponse::from_db_mandate(&state, key_store, mandate).await?,
))
}

Expand Down Expand Up @@ -202,6 +203,7 @@ pub async fn update_connector_mandate_id(
pub async fn get_customer_mandates(
state: AppState,
merchant_account: domain::MerchantAccount,
key_store: domain::MerchantKeyStore,
req: customers::CustomerId,
) -> RouterResponse<Vec<mandates::MandateResponse>> {
let mandates = state
Expand All @@ -221,7 +223,10 @@ pub async fn get_customer_mandates(
} else {
let mut response_vec = Vec::with_capacity(mandates.len());
for mandate in mandates {
response_vec.push(mandates::MandateResponse::from_db_mandate(&state, mandate).await?);
response_vec.push(
mandates::MandateResponse::from_db_mandate(&state, key_store.clone(), mandate)
.await?,
);
}
Ok(services::ApplicationResponse::Json(response_vec))
}
Expand Down Expand Up @@ -383,6 +388,7 @@ where
pub async fn retrieve_mandates_list(
state: AppState,
merchant_account: domain::MerchantAccount,
key_store: domain::MerchantKeyStore,
constraints: api_models::mandates::MandateListConstraints,
) -> RouterResponse<Vec<api_models::mandates::MandateResponse>> {
let mandates = state
Expand All @@ -392,11 +398,9 @@ pub async fn retrieve_mandates_list(
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to retrieve mandates")?;
let mandates_list = future::try_join_all(
mandates
.into_iter()
.map(|mandate| mandates::MandateResponse::from_db_mandate(&state, mandate)),
)
let mandates_list = future::try_join_all(mandates.into_iter().map(|mandate| {
mandates::MandateResponse::from_db_mandate(&state, key_store.clone(), mandate)
}))
.await?;
Ok(services::ApplicationResponse::Json(mandates_list))
}
Expand Down
82 changes: 65 additions & 17 deletions crates/router/src/core/payment_methods/cards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ pub async fn add_card_hs(
req,
&merchant_account.merchant_id,
);

Ok((
payment_method_resp,
store_card_payload.duplicate.unwrap_or(false),
Expand Down Expand Up @@ -2508,11 +2509,19 @@ pub async fn list_customer_payment_method(
let parent_payment_method_token = generate_id(consts::ID_LENGTH, "token");

let (card, pmd, hyperswitch_token_data) = match pm.payment_method {
enums::PaymentMethod::Card => (
Some(get_card_details(&pm, key, state).await?),
None,
PaymentTokenData::permanent_card(pm.payment_method_id.clone()),
),
enums::PaymentMethod::Card => {
let card_details = get_card_details_with_locker_fallback(&pm, key, state).await?;

if card_details.is_some() {
(
card_details,
None,
PaymentTokenData::permanent_card(pm.payment_method_id.clone()),
)
} else {
continue;
}
}

#[cfg(feature = "payouts")]
enums::PaymentMethod::BankTransfer => {
Expand Down Expand Up @@ -2571,6 +2580,7 @@ pub async fn list_customer_payment_method(
};

//Need validation for enabled payment method ,querying MCA

let pma = api::CustomerPaymentMethod {
payment_token: parent_payment_method_token.to_owned(),
customer_id: pm.customer_id,
Expand Down Expand Up @@ -2700,7 +2710,38 @@ pub async fn list_customer_payment_method(
Ok(services::ApplicationResponse::Json(response))
}

async fn get_card_details(
pub async fn get_card_details_with_locker_fallback(
pm: &payment_method::PaymentMethod,
key: &[u8],
state: &routes::AppState,
) -> errors::RouterResult<Option<api::CardDetailFromLocker>> {
let card_decrypted =
decrypt::<serde_json::Value, masking::WithType>(pm.payment_method_data.clone(), key)
.await
.change_context(errors::StorageError::DecryptionError)
.attach_printable("unable to decrypt card details")
.ok()
.flatten()
.map(|x| x.into_inner().expose())
.and_then(|v| serde_json::from_value::<PaymentMethodsData>(v).ok())
.and_then(|pmd| match pmd {
PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)),
_ => None,
});

Ok(if let Some(mut crd) = card_decrypted {
if crd.saved_to_locker {
crd.scheme = pm.scheme.clone();
Some(crd)
} else {
None
}
} else {
Some(get_card_details_from_locker(state, pm).await?)
})
}

pub async fn get_card_details_without_locker_fallback(
pm: &payment_method::PaymentMethod,
key: &[u8],
state: &routes::AppState,
Expand Down Expand Up @@ -2979,25 +3020,32 @@ impl TempLockerCardSupport {
pub async fn retrieve_payment_method(
state: routes::AppState,
pm: api::PaymentMethodId,
key_store: domain::MerchantKeyStore,
) -> errors::RouterResponse<api::PaymentMethodResponse> {
let db = state.store.as_ref();
let pm = db
.find_payment_method(&pm.payment_method_id)
.await
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

let key = key_store.key.peek();
let card = if pm.payment_method == enums::PaymentMethod::Card {
let card = get_card_from_locker(
&state,
&pm.customer_id,
&pm.merchant_id,
&pm.payment_method_id,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error getting card from card vault")?;
let card_detail = payment_methods::get_card_detail(&pm, card)
let card_detail = if state.conf.locker.locker_enabled {
let card = get_card_from_locker(
&state,
&pm.customer_id,
&pm.merchant_id,
&pm.payment_method_id,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while getting card details from locker")?;
.attach_printable("Error getting card from card vault")?;
payment_methods::get_card_detail(&pm, card)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while getting card details from locker")?
} else {
get_card_details_without_locker_fallback(&pm, key, &state).await?
};
Some(card_detail)
} else {
None
Expand Down
Loading

0 comments on commit bd5356e

Please sign in to comment.