mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
refactor(core): add locker config to enable or disable locker (#3352)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
@ -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?,
|
||||
))
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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))
|
||||
}
|
||||
@ -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
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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),
|
||||
@ -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 => {
|
||||
@ -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,
|
||||
@ -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,
|
||||
@ -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
|
||||
|
||||
@ -352,18 +352,26 @@ pub fn mk_add_card_response_hs(
|
||||
req: api::PaymentMethodCreate,
|
||||
merchant_id: &str,
|
||||
) -> api::PaymentMethodResponse {
|
||||
let mut card_number = card.card_number.peek().to_owned();
|
||||
let card_number = card.card_number.clone();
|
||||
let last4_digits = card_number.clone().get_last4();
|
||||
let card_isin = card_number.get_card_isin();
|
||||
|
||||
let card = api::CardDetailFromLocker {
|
||||
scheme: None,
|
||||
last4_digits: Some(card_number.split_off(card_number.len() - 4)),
|
||||
issuer_country: None, // [#256] bin mapping
|
||||
card_number: Some(card.card_number),
|
||||
expiry_month: Some(card.card_exp_month),
|
||||
expiry_year: Some(card.card_exp_year),
|
||||
card_token: None, // [#256]
|
||||
card_fingerprint: None, // fingerprint not send by basilisk-hs need to have this feature in case we need it in future
|
||||
card_holder_name: card.card_holder_name,
|
||||
nick_name: card.nick_name,
|
||||
last4_digits: Some(last4_digits),
|
||||
issuer_country: None,
|
||||
card_number: Some(card.card_number.clone()),
|
||||
expiry_month: Some(card.card_exp_month.clone()),
|
||||
expiry_year: Some(card.card_exp_year.clone()),
|
||||
card_token: None,
|
||||
card_fingerprint: None,
|
||||
card_holder_name: card.card_holder_name.clone(),
|
||||
nick_name: card.nick_name.clone(),
|
||||
card_isin: Some(card_isin),
|
||||
card_issuer: card.card_issuer,
|
||||
card_network: card.card_network,
|
||||
card_type: card.card_type,
|
||||
saved_to_locker: true,
|
||||
};
|
||||
api::PaymentMethodResponse {
|
||||
merchant_id: merchant_id.to_owned(),
|
||||
@ -399,6 +407,11 @@ pub fn mk_add_card_response(
|
||||
card_fingerprint: Some(response.card_fingerprint),
|
||||
card_holder_name: card.card_holder_name,
|
||||
nick_name: card.nick_name,
|
||||
card_isin: None,
|
||||
card_issuer: None,
|
||||
card_network: None,
|
||||
card_type: None,
|
||||
saved_to_locker: true,
|
||||
};
|
||||
api::PaymentMethodResponse {
|
||||
merchant_id: merchant_id.to_owned(),
|
||||
@ -597,6 +610,8 @@ pub fn get_card_detail(
|
||||
) -> CustomResult<api::CardDetailFromLocker, errors::VaultError> {
|
||||
let card_number = response.card_number;
|
||||
let mut last4_digits = card_number.peek().to_owned();
|
||||
//fetch form card bin
|
||||
|
||||
let card_detail = api::CardDetailFromLocker {
|
||||
scheme: pm.scheme.to_owned(),
|
||||
issuer_country: pm.issuer_country.clone(),
|
||||
@ -608,6 +623,11 @@ pub fn get_card_detail(
|
||||
card_fingerprint: None,
|
||||
card_holder_name: response.name_on_card,
|
||||
nick_name: response.nick_name.map(masking::Secret::new),
|
||||
card_isin: None,
|
||||
card_issuer: None,
|
||||
card_network: None,
|
||||
card_type: None,
|
||||
saved_to_locker: true,
|
||||
};
|
||||
Ok(card_detail)
|
||||
}
|
||||
|
||||
@ -92,7 +92,9 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
||||
|
||||
metrics::PAYMENT_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics
|
||||
|
||||
if resp.request.setup_mandate_details.clone().is_some() {
|
||||
let is_mandate = resp.request.setup_mandate_details.is_some();
|
||||
|
||||
if is_mandate {
|
||||
let payment_method_id = Box::pin(tokenization::save_payment_method(
|
||||
state,
|
||||
connector,
|
||||
|
||||
@ -208,6 +208,7 @@ impl types::SetupMandateRouterData {
|
||||
.to_setup_mandate_failed_response()?;
|
||||
|
||||
let payment_method_type = self.request.payment_method_type;
|
||||
|
||||
let pm_id = Box::pin(tokenization::save_payment_method(
|
||||
state,
|
||||
connector,
|
||||
|
||||
@ -509,16 +509,23 @@ 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, merchant_key_store)
|
||||
.await?;
|
||||
if state.conf.locker.locker_enabled {
|
||||
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 {
|
||||
Err(report!(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message:
|
||||
"payment method in request does not match previously provided payment \
|
||||
method information"
|
||||
method information"
|
||||
.into()
|
||||
}))?
|
||||
}
|
||||
@ -971,7 +978,6 @@ pub fn payment_intent_status_fsm(
|
||||
None => storage_enums::IntentStatus::RequiresPaymentMethod,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn add_domain_task_to_pt<Op>(
|
||||
operation: &Op,
|
||||
state: &AppState,
|
||||
@ -1050,6 +1056,10 @@ pub(crate) async fn get_payment_method_create_request(
|
||||
card_exp_year: card.card_exp_year.clone(),
|
||||
card_holder_name: card.card_holder_name.clone(),
|
||||
nick_name: card.nick_name.clone(),
|
||||
card_issuing_country: card.card_issuing_country.clone(),
|
||||
card_network: card.card_network.clone(),
|
||||
card_issuer: card.card_issuer.clone(),
|
||||
card_type: card.card_type.clone(),
|
||||
};
|
||||
let customer_id = customer.customer_id.clone();
|
||||
let payment_method_request = api::PaymentMethodCreate {
|
||||
@ -3359,21 +3369,23 @@ pub async fn get_additional_payment_data(
|
||||
},
|
||||
))
|
||||
});
|
||||
card_info.unwrap_or(api_models::payments::AdditionalPaymentData::Card(Box::new(
|
||||
api_models::payments::AdditionalCardInfo {
|
||||
card_issuer: None,
|
||||
card_network: None,
|
||||
bank_code: None,
|
||||
card_type: None,
|
||||
card_issuing_country: None,
|
||||
last4,
|
||||
card_isin,
|
||||
card_extended_bin,
|
||||
card_exp_month: Some(card_data.card_exp_month.clone()),
|
||||
card_exp_year: Some(card_data.card_exp_year.clone()),
|
||||
card_holder_name: card_data.card_holder_name.clone(),
|
||||
},
|
||||
)))
|
||||
card_info.unwrap_or_else(|| {
|
||||
api_models::payments::AdditionalPaymentData::Card(Box::new(
|
||||
api_models::payments::AdditionalCardInfo {
|
||||
card_issuer: None,
|
||||
card_network: None,
|
||||
bank_code: None,
|
||||
card_type: None,
|
||||
card_issuing_country: None,
|
||||
last4,
|
||||
card_isin,
|
||||
card_extended_bin,
|
||||
card_exp_month: Some(card_data.card_exp_month.clone()),
|
||||
card_exp_year: Some(card_data.card_exp_year.clone()),
|
||||
card_holder_name: card_data.card_holder_name.clone(),
|
||||
},
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
api_models::payments::PaymentMethodData::BankRedirect(bank_redirect_data) => {
|
||||
|
||||
@ -14,7 +14,7 @@ use crate::{
|
||||
services,
|
||||
types::{
|
||||
self,
|
||||
api::{self, CardDetailsPaymentMethod, PaymentMethodCreateExt},
|
||||
api::{self, CardDetailFromLocker, CardDetailsPaymentMethod, PaymentMethodCreateExt},
|
||||
domain,
|
||||
storage::enums as storage_enums,
|
||||
},
|
||||
@ -74,12 +74,21 @@ where
|
||||
.await?;
|
||||
let merchant_id = &merchant_account.merchant_id;
|
||||
|
||||
let locker_response = save_in_locker(
|
||||
state,
|
||||
merchant_account,
|
||||
payment_method_create_request.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
let locker_response = if !state.conf.locker.locker_enabled {
|
||||
skip_saving_card_in_locker(
|
||||
merchant_account,
|
||||
payment_method_create_request.to_owned(),
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
save_in_locker(
|
||||
state,
|
||||
merchant_account,
|
||||
payment_method_create_request.to_owned(),
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
let is_duplicate = locker_response.1;
|
||||
|
||||
let pm_card_details = locker_response.0.card.as_ref().map(|card| {
|
||||
@ -168,6 +177,85 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn skip_saving_card_in_locker(
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
payment_method_request: api::PaymentMethodCreate,
|
||||
) -> RouterResult<(api_models::payment_methods::PaymentMethodResponse, bool)> {
|
||||
let merchant_id = &merchant_account.merchant_id;
|
||||
let customer_id = payment_method_request
|
||||
.clone()
|
||||
.customer_id
|
||||
.clone()
|
||||
.get_required_value("customer_id")?;
|
||||
let payment_method_id = common_utils::generate_id(crate::consts::ID_LENGTH, "pm");
|
||||
|
||||
let last4_digits = payment_method_request
|
||||
.card
|
||||
.clone()
|
||||
.map(|c| c.card_number.get_last4());
|
||||
|
||||
let card_isin = payment_method_request
|
||||
.card
|
||||
.clone()
|
||||
.map(|c: api_models::payment_methods::CardDetail| c.card_number.get_card_isin());
|
||||
|
||||
match payment_method_request.card.clone() {
|
||||
Some(card) => {
|
||||
let card_detail = CardDetailFromLocker {
|
||||
scheme: None,
|
||||
issuer_country: card.card_issuing_country.clone(),
|
||||
last4_digits: last4_digits.clone(),
|
||||
card_number: None,
|
||||
expiry_month: Some(card.card_exp_month.clone()),
|
||||
expiry_year: Some(card.card_exp_year),
|
||||
card_token: None,
|
||||
card_holder_name: card.card_holder_name.clone(),
|
||||
card_fingerprint: None,
|
||||
nick_name: None,
|
||||
card_isin: card_isin.clone(),
|
||||
card_issuer: card.card_issuer.clone(),
|
||||
card_network: card.card_network.clone(),
|
||||
card_type: card.card_type.clone(),
|
||||
saved_to_locker: false,
|
||||
};
|
||||
let pm_resp = api::PaymentMethodResponse {
|
||||
merchant_id: merchant_id.to_string(),
|
||||
customer_id: Some(customer_id),
|
||||
payment_method_id,
|
||||
payment_method: payment_method_request.payment_method,
|
||||
payment_method_type: payment_method_request.payment_method_type,
|
||||
card: Some(card_detail),
|
||||
recurring_enabled: false,
|
||||
installment_payment_enabled: false,
|
||||
payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]),
|
||||
metadata: None,
|
||||
created: Some(common_utils::date_time::now()),
|
||||
bank_transfer: None,
|
||||
};
|
||||
|
||||
Ok((pm_resp, false))
|
||||
}
|
||||
None => {
|
||||
let pm_id = common_utils::generate_id(crate::consts::ID_LENGTH, "pm");
|
||||
let payment_method_response = api::PaymentMethodResponse {
|
||||
merchant_id: merchant_id.to_string(),
|
||||
customer_id: Some(customer_id),
|
||||
payment_method_id: pm_id,
|
||||
payment_method: payment_method_request.payment_method,
|
||||
payment_method_type: payment_method_request.payment_method_type,
|
||||
card: None,
|
||||
metadata: None,
|
||||
created: Some(common_utils::date_time::now()),
|
||||
recurring_enabled: false,
|
||||
installment_payment_enabled: false,
|
||||
payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]),
|
||||
bank_transfer: None,
|
||||
};
|
||||
Ok((payment_method_response, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn save_in_locker(
|
||||
state: &AppState,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use common_utils::{
|
||||
errors::CustomResult,
|
||||
ext_traits::{StringExt, ValueExt},
|
||||
ext_traits::{AsyncExt, StringExt, ValueExt},
|
||||
};
|
||||
use diesel_models::encryption::Encryption;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
@ -19,6 +19,7 @@ use crate::{
|
||||
},
|
||||
db::StorageInterface,
|
||||
routes::AppState,
|
||||
services,
|
||||
types::{
|
||||
api::{self, enums as api_enums},
|
||||
domain::{
|
||||
@ -184,6 +185,10 @@ pub async fn save_payout_data_to_locker(
|
||||
card_exp_month: card.expiry_month.to_owned(),
|
||||
card_exp_year: card.expiry_year.to_owned(),
|
||||
nick_name: None,
|
||||
card_issuing_country: None,
|
||||
card_network: None,
|
||||
card_issuer: None,
|
||||
card_type: None,
|
||||
};
|
||||
let payload = StoreLockerReq::LockerCard(StoreCardReq {
|
||||
merchant_id: &merchant_account.merchant_id,
|
||||
@ -267,20 +272,65 @@ pub async fn save_payout_data_to_locker(
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error updating payouts in saved payout method")?;
|
||||
|
||||
let pm_data = api::payment_methods::PaymentMethodsData::Card(
|
||||
api::payment_methods::CardDetailsPaymentMethod {
|
||||
last4_digits: card_details
|
||||
.as_ref()
|
||||
.map(|c| c.card_number.clone().get_last4()),
|
||||
issuer_country: None,
|
||||
expiry_month: card_details.as_ref().map(|c| c.card_exp_month.clone()),
|
||||
expiry_year: card_details.as_ref().map(|c| c.card_exp_year.clone()),
|
||||
nick_name: card_details.as_ref().and_then(|c| c.nick_name.clone()),
|
||||
card_holder_name: card_details
|
||||
.as_ref()
|
||||
.and_then(|c| c.card_holder_name.clone()),
|
||||
},
|
||||
);
|
||||
// fetch card info from db
|
||||
let card_isin = card_details
|
||||
.as_ref()
|
||||
.map(|c| c.card_number.clone().get_card_isin());
|
||||
|
||||
let pm_data = card_isin
|
||||
.clone()
|
||||
.async_and_then(|card_isin| async move {
|
||||
db.get_card_info(&card_isin)
|
||||
.await
|
||||
.map_err(|error| services::logger::warn!(card_info_error=?error))
|
||||
.ok()
|
||||
})
|
||||
.await
|
||||
.flatten()
|
||||
.map(|card_info| {
|
||||
api::payment_methods::PaymentMethodsData::Card(
|
||||
api::payment_methods::CardDetailsPaymentMethod {
|
||||
last4_digits: card_details
|
||||
.as_ref()
|
||||
.map(|c| c.card_number.clone().get_last4()),
|
||||
issuer_country: card_info.card_issuing_country,
|
||||
expiry_month: card_details.as_ref().map(|c| c.card_exp_month.clone()),
|
||||
expiry_year: card_details.as_ref().map(|c| c.card_exp_year.clone()),
|
||||
nick_name: card_details.as_ref().and_then(|c| c.nick_name.clone()),
|
||||
card_holder_name: card_details
|
||||
.as_ref()
|
||||
.and_then(|c| c.card_holder_name.clone()),
|
||||
|
||||
card_isin: card_isin.clone(),
|
||||
card_issuer: card_info.card_issuer,
|
||||
card_network: card_info.card_network,
|
||||
card_type: card_info.card_type,
|
||||
saved_to_locker: true,
|
||||
},
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
api::payment_methods::PaymentMethodsData::Card(
|
||||
api::payment_methods::CardDetailsPaymentMethod {
|
||||
last4_digits: card_details
|
||||
.as_ref()
|
||||
.map(|c| c.card_number.clone().get_last4()),
|
||||
issuer_country: None,
|
||||
expiry_month: card_details.as_ref().map(|c| c.card_exp_month.clone()),
|
||||
expiry_year: card_details.as_ref().map(|c| c.card_exp_year.clone()),
|
||||
nick_name: card_details.as_ref().and_then(|c| c.nick_name.clone()),
|
||||
card_holder_name: card_details
|
||||
.as_ref()
|
||||
.and_then(|c| c.card_holder_name.clone()),
|
||||
|
||||
card_isin: card_isin.clone(),
|
||||
card_issuer: None,
|
||||
card_network: None,
|
||||
card_type: None,
|
||||
saved_to_locker: true,
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
let card_details_encrypted =
|
||||
cards::create_encrypted_payment_method_data(key_store, Some(pm_data)).await;
|
||||
|
||||
@ -421,6 +421,7 @@ pub async fn mandates_incoming_webhook_flow<W: types::OutgoingWebhookType>(
|
||||
state: AppState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
business_profile: diesel_models::business_profile::BusinessProfile,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
webhook_details: api::IncomingWebhookDetails,
|
||||
source_verified: bool,
|
||||
event_type: api_models::webhooks::IncomingWebhookEvent,
|
||||
@ -464,8 +465,12 @@ pub async fn mandates_incoming_webhook_flow<W: types::OutgoingWebhookType>(
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
|
||||
let mandates_response = Box::new(
|
||||
api::mandates::MandateResponse::from_db_mandate(&state, updated_mandate.clone())
|
||||
.await?,
|
||||
api::mandates::MandateResponse::from_db_mandate(
|
||||
&state,
|
||||
key_store,
|
||||
updated_mandate.clone(),
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
let event_type: Option<enums::EventType> = updated_mandate.mandate_status.foreign_into();
|
||||
if let Some(outgoing_event_type) = event_type {
|
||||
@ -1237,6 +1242,7 @@ pub async fn webhooks_core<W: types::OutgoingWebhookType, Ctx: PaymentMethodRetr
|
||||
state.clone(),
|
||||
merchant_account,
|
||||
business_profile,
|
||||
key_store,
|
||||
webhook_details,
|
||||
source_verified,
|
||||
event_type,
|
||||
|
||||
@ -226,7 +226,12 @@ pub async fn get_customer_mandates(
|
||||
&req,
|
||||
customer_id,
|
||||
|state, auth, req| {
|
||||
crate::core::mandate::get_customer_mandates(state, auth.merchant_account, req)
|
||||
crate::core::mandate::get_customer_mandates(
|
||||
state,
|
||||
auth.merchant_account,
|
||||
auth.key_store,
|
||||
req,
|
||||
)
|
||||
},
|
||||
auth::auth_type(
|
||||
&auth::ApiKeyAuth,
|
||||
|
||||
@ -41,7 +41,7 @@ pub async fn get_mandate(
|
||||
state,
|
||||
&req,
|
||||
mandate_id,
|
||||
|state, auth, req| mandate::get_mandate(state, auth.merchant_account, req),
|
||||
|state, auth, req| mandate::get_mandate(state, auth.merchant_account, auth.key_store, req),
|
||||
&auth::ApiKeyAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
)
|
||||
@ -123,7 +123,9 @@ pub async fn retrieve_mandates_list(
|
||||
state,
|
||||
&req,
|
||||
payload,
|
||||
|state, auth, req| mandate::retrieve_mandates_list(state, auth.merchant_account, req),
|
||||
|state, auth, req| {
|
||||
mandate::retrieve_mandates_list(state, auth.merchant_account, auth.key_store, req)
|
||||
},
|
||||
auth::auth_type(
|
||||
&auth::ApiKeyAuth,
|
||||
&auth::JWTAuth(Permission::MandateRead),
|
||||
|
||||
@ -242,7 +242,7 @@ pub async fn payment_method_retrieve_api(
|
||||
state,
|
||||
&req,
|
||||
payload,
|
||||
|state, _auth, pm| cards::retrieve_payment_method(state, pm),
|
||||
|state, auth, pm| cards::retrieve_payment_method(state, pm, auth.key_store),
|
||||
&auth::ApiKeyAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use api_models::mandates;
|
||||
pub use api_models::mandates::{MandateId, MandateResponse, MandateRevokedResponse};
|
||||
use error_stack::ResultExt;
|
||||
use masking::PeekInterface;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
@ -11,7 +12,7 @@ use crate::{
|
||||
newtype,
|
||||
routes::AppState,
|
||||
types::{
|
||||
api,
|
||||
api, domain,
|
||||
storage::{self, enums as storage_enums},
|
||||
},
|
||||
};
|
||||
@ -23,12 +24,20 @@ newtype!(
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub(crate) trait MandateResponseExt: Sized {
|
||||
async fn from_db_mandate(state: &AppState, mandate: storage::Mandate) -> RouterResult<Self>;
|
||||
async fn from_db_mandate(
|
||||
state: &AppState,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
mandate: storage::Mandate,
|
||||
) -> RouterResult<Self>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MandateResponseExt for MandateResponse {
|
||||
async fn from_db_mandate(state: &AppState, mandate: storage::Mandate) -> RouterResult<Self> {
|
||||
async fn from_db_mandate(
|
||||
state: &AppState,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
mandate: storage::Mandate,
|
||||
) -> RouterResult<Self> {
|
||||
let db = &*state.store;
|
||||
let payment_method = db
|
||||
.find_payment_method(&mandate.payment_method_id)
|
||||
@ -36,21 +45,35 @@ impl MandateResponseExt for MandateResponse {
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
|
||||
|
||||
let card = if payment_method.payment_method == storage_enums::PaymentMethod::Card {
|
||||
let card = payment_methods::cards::get_card_from_locker(
|
||||
state,
|
||||
&payment_method.customer_id,
|
||||
&payment_method.merchant_id,
|
||||
&payment_method.payment_method_id,
|
||||
)
|
||||
.await?;
|
||||
let card_detail = payment_methods::transformers::get_card_detail(&payment_method, card)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed while getting card details")?;
|
||||
Some(MandateCardDetails::from(card_detail).into_inner())
|
||||
// if locker is disabled , decrypt the payment method data
|
||||
let card_details = if state.conf.locker.locker_enabled {
|
||||
let card = payment_methods::cards::get_card_from_locker(
|
||||
state,
|
||||
&payment_method.customer_id,
|
||||
&payment_method.merchant_id,
|
||||
&payment_method.payment_method_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
payment_methods::transformers::get_card_detail(&payment_method, card)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed while getting card details")?
|
||||
} else {
|
||||
payment_methods::cards::get_card_details_without_locker_fallback(
|
||||
&payment_method,
|
||||
key_store.key.get_inner().peek(),
|
||||
state,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
Some(MandateCardDetails::from(card_details).into_inner())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payment_method_type = payment_method
|
||||
.payment_method_type
|
||||
.map(|pmt| pmt.to_string());
|
||||
Ok(Self {
|
||||
mandate_id: mandate.mandate_id,
|
||||
customer_acceptance: Some(api::payments::CustomerAcceptance {
|
||||
@ -68,6 +91,7 @@ impl MandateResponseExt for MandateResponse {
|
||||
card,
|
||||
status: mandate.mandate_status,
|
||||
payment_method: payment_method.payment_method.to_string(),
|
||||
payment_method_type,
|
||||
payment_method_id: mandate.payment_method_id,
|
||||
})
|
||||
}
|
||||
@ -84,6 +108,10 @@ impl From<api::payment_methods::CardDetailFromLocker> for MandateCardDetails {
|
||||
scheme: card_details_from_locker.scheme,
|
||||
issuer_country: card_details_from_locker.issuer_country,
|
||||
card_fingerprint: card_details_from_locker.card_fingerprint,
|
||||
card_isin: card_details_from_locker.card_isin,
|
||||
card_issuer: card_details_from_locker.card_issuer,
|
||||
card_network: card_details_from_locker.card_network,
|
||||
card_type: card_details_from_locker.card_type,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user