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:
Amisha Prabhat
2024-01-18 18:10:21 +05:30
committed by GitHub
parent 059e86607d
commit bd5356e7e7
23 changed files with 505 additions and 98 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()
}