mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 12:06:56 +08:00
refactor(payment_methods): introduce locker_id column in payment_methods table (#3760)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -33,6 +33,7 @@ pub struct PaymentMethod {
|
||||
pub payment_method_issuer_code: Option<storage_enums::PaymentMethodIssuerCode>,
|
||||
pub metadata: Option<pii::SecretSerdeValue>,
|
||||
pub payment_method_data: Option<Encryption>,
|
||||
pub locker_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Insertable, Queryable, router_derive::DebugAsDisplay)]
|
||||
@ -59,6 +60,7 @@ pub struct PaymentMethodNew {
|
||||
pub last_modified: PrimitiveDateTime,
|
||||
pub metadata: Option<pii::SecretSerdeValue>,
|
||||
pub payment_method_data: Option<Encryption>,
|
||||
pub locker_id: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for PaymentMethodNew {
|
||||
@ -69,6 +71,7 @@ impl Default for PaymentMethodNew {
|
||||
customer_id: String::default(),
|
||||
merchant_id: String::default(),
|
||||
payment_method_id: String::default(),
|
||||
locker_id: Option::default(),
|
||||
payment_method: storage_enums::PaymentMethod::default(),
|
||||
payment_method_type: Option::default(),
|
||||
payment_method_issuer: Option::default(),
|
||||
|
||||
@ -44,6 +44,15 @@ impl PaymentMethod {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
pub async fn find_by_locker_id(conn: &PgPooledConn, locker_id: &str) -> StorageResult<Self> {
|
||||
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
|
||||
conn,
|
||||
dsl::locker_id.eq(locker_id.to_owned()),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
pub async fn find_by_payment_method_id(
|
||||
conn: &PgPooledConn,
|
||||
|
||||
@ -829,6 +829,8 @@ diesel::table! {
|
||||
payment_method_issuer_code -> Nullable<PaymentMethodIssuerCode>,
|
||||
metadata -> Nullable<Json>,
|
||||
payment_method_data -> Nullable<Bytea>,
|
||||
#[max_length = 64]
|
||||
locker_id -> Nullable<Varchar>,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -218,7 +218,7 @@ pub async fn delete_customer(
|
||||
&state,
|
||||
&req.customer_id,
|
||||
&merchant_account.merchant_id,
|
||||
&pm.payment_method_id,
|
||||
pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
|
||||
@ -83,9 +83,13 @@ pub async fn call_to_locker(
|
||||
.into_iter()
|
||||
.filter(|pm| matches!(pm.payment_method, storage_enums::PaymentMethod::Card))
|
||||
{
|
||||
let card =
|
||||
cards::get_card_from_locker(state, customer_id, merchant_id, &pm.payment_method_id)
|
||||
.await;
|
||||
let card = cards::get_card_from_locker(
|
||||
state,
|
||||
customer_id,
|
||||
merchant_id,
|
||||
pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
|
||||
)
|
||||
.await;
|
||||
|
||||
let card = match card {
|
||||
Ok(card) => card,
|
||||
@ -127,7 +131,7 @@ pub async fn call_to_locker(
|
||||
customer_id.to_string(),
|
||||
merchant_account,
|
||||
api_enums::LockerChoice::HyperswitchCardVault,
|
||||
Some(&pm.payment_method_id),
|
||||
Some(pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id)),
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
|
||||
@ -155,7 +155,7 @@ impl PaymentMethodRetrieve for Oss {
|
||||
storage::PaymentTokenData::Permanent(card_token) => {
|
||||
helpers::retrieve_card_with_permanent_token(
|
||||
state,
|
||||
&card_token.token,
|
||||
card_token.locker_id.as_ref().unwrap_or(&card_token.token),
|
||||
payment_intent,
|
||||
card_token_data,
|
||||
)
|
||||
@ -166,7 +166,7 @@ impl PaymentMethodRetrieve for Oss {
|
||||
storage::PaymentTokenData::PermanentCard(card_token) => {
|
||||
helpers::retrieve_card_with_permanent_token(
|
||||
state,
|
||||
&card_token.token,
|
||||
card_token.locker_id.as_ref().unwrap_or(&card_token.token),
|
||||
payment_intent,
|
||||
card_token_data,
|
||||
)
|
||||
|
||||
@ -76,6 +76,7 @@ pub async fn create_payment_method(
|
||||
req: &api::PaymentMethodCreate,
|
||||
customer_id: &str,
|
||||
payment_method_id: &str,
|
||||
locker_id: Option<String>,
|
||||
merchant_id: &str,
|
||||
pm_metadata: Option<serde_json::Value>,
|
||||
payment_method_data: Option<Encryption>,
|
||||
@ -90,6 +91,7 @@ pub async fn create_payment_method(
|
||||
customer_id: customer_id.to_string(),
|
||||
merchant_id: merchant_id.to_string(),
|
||||
payment_method_id: payment_method_id.to_string(),
|
||||
locker_id,
|
||||
payment_method: req.payment_method,
|
||||
payment_method_type: req.payment_method_type,
|
||||
payment_method_issuer: req.payment_method_issuer.clone(),
|
||||
@ -131,6 +133,65 @@ pub fn store_default_payment_method(
|
||||
(payment_method_response, None)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn get_or_insert_payment_method(
|
||||
db: &dyn db::StorageInterface,
|
||||
req: api::PaymentMethodCreate,
|
||||
resp: &mut api::PaymentMethodResponse,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
customer_id: &str,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
) -> errors::RouterResult<diesel_models::PaymentMethod> {
|
||||
let mut payment_method_id = resp.payment_method_id.clone();
|
||||
let mut locker_id = None;
|
||||
let payment_method = {
|
||||
let existing_pm_by_pmid = db.find_payment_method(&payment_method_id).await;
|
||||
|
||||
if let Err(err) = existing_pm_by_pmid {
|
||||
if err.current_context().is_db_not_found() {
|
||||
locker_id = Some(payment_method_id.clone());
|
||||
let existing_pm_by_locker_id = db
|
||||
.find_payment_method_by_locker_id(&payment_method_id)
|
||||
.await;
|
||||
|
||||
match &existing_pm_by_locker_id {
|
||||
Ok(pm) => payment_method_id = pm.payment_method_id.clone(),
|
||||
Err(_) => payment_method_id = generate_id(consts::ID_LENGTH, "pm"),
|
||||
};
|
||||
existing_pm_by_locker_id
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
} else {
|
||||
existing_pm_by_pmid
|
||||
}
|
||||
};
|
||||
resp.payment_method_id = payment_method_id.to_owned();
|
||||
|
||||
match payment_method {
|
||||
Ok(pm) => Ok(pm),
|
||||
Err(err) => {
|
||||
if err.current_context().is_db_not_found() {
|
||||
insert_payment_method(
|
||||
db,
|
||||
resp,
|
||||
req,
|
||||
key_store,
|
||||
&merchant_account.merchant_id,
|
||||
customer_id,
|
||||
resp.metadata.clone().map(|val| val.expose()),
|
||||
locker_id,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
Err(err)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while finding payment method")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn add_payment_method(
|
||||
state: routes::AppState,
|
||||
@ -182,40 +243,41 @@ pub async fn add_payment_method(
|
||||
)),
|
||||
};
|
||||
|
||||
let (resp, duplication_check) = response?;
|
||||
let (mut resp, duplication_check) = response?;
|
||||
|
||||
match duplication_check {
|
||||
Some(duplication_check) => match duplication_check {
|
||||
payment_methods::DataDuplicationCheck::Duplicated => {
|
||||
let existing_pm = db.find_payment_method(&resp.payment_method_id).await;
|
||||
|
||||
if let Err(err) = existing_pm {
|
||||
if err.current_context().is_db_not_found() {
|
||||
insert_payment_method(
|
||||
db,
|
||||
&resp,
|
||||
req,
|
||||
key_store,
|
||||
merchant_id,
|
||||
&customer_id,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
Err(err)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while finding payment method")
|
||||
}?
|
||||
};
|
||||
get_or_insert_payment_method(
|
||||
db,
|
||||
req.clone(),
|
||||
&mut resp,
|
||||
merchant_account,
|
||||
&customer_id,
|
||||
key_store,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
payment_methods::DataDuplicationCheck::MetaDataChanged => {
|
||||
if let Some(card) = req.card.clone() {
|
||||
let existing_pm = get_or_insert_payment_method(
|
||||
db,
|
||||
req.clone(),
|
||||
&mut resp,
|
||||
merchant_account,
|
||||
&customer_id,
|
||||
key_store,
|
||||
)
|
||||
.await?;
|
||||
|
||||
delete_card_from_locker(
|
||||
&state,
|
||||
&customer_id,
|
||||
merchant_id,
|
||||
&resp.payment_method_id,
|
||||
existing_pm
|
||||
.locker_id
|
||||
.as_ref()
|
||||
.unwrap_or(&existing_pm.payment_method_id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -226,7 +288,12 @@ pub async fn add_payment_method(
|
||||
customer_id.clone(),
|
||||
merchant_account,
|
||||
api::enums::LockerChoice::HyperswitchCardVault,
|
||||
Some(&resp.payment_method_id),
|
||||
Some(
|
||||
existing_pm
|
||||
.locker_id
|
||||
.as_ref()
|
||||
.unwrap_or(&existing_pm.payment_method_id),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
@ -243,69 +310,46 @@ pub async fn add_payment_method(
|
||||
.attach_printable("Failed while updating card metadata changes"))?
|
||||
};
|
||||
|
||||
let existing_pm = db.find_payment_method(&resp.payment_method_id).await;
|
||||
match existing_pm {
|
||||
Ok(pm) => {
|
||||
let updated_card = Some(api::CardDetailFromLocker {
|
||||
scheme: None,
|
||||
last4_digits: Some(card.card_number.clone().get_last4()),
|
||||
issuer_country: None,
|
||||
card_number: Some(card.card_number),
|
||||
expiry_month: Some(card.card_exp_month),
|
||||
expiry_year: Some(card.card_exp_year),
|
||||
card_token: None,
|
||||
card_fingerprint: None,
|
||||
card_holder_name: card.card_holder_name,
|
||||
nick_name: card.nick_name,
|
||||
card_network: None,
|
||||
card_isin: None,
|
||||
card_issuer: None,
|
||||
card_type: None,
|
||||
saved_to_locker: true,
|
||||
});
|
||||
let updated_card = Some(api::CardDetailFromLocker {
|
||||
scheme: None,
|
||||
last4_digits: Some(card.card_number.clone().get_last4()),
|
||||
issuer_country: None,
|
||||
card_number: Some(card.card_number),
|
||||
expiry_month: Some(card.card_exp_month),
|
||||
expiry_year: Some(card.card_exp_year),
|
||||
card_token: None,
|
||||
card_fingerprint: None,
|
||||
card_holder_name: card.card_holder_name,
|
||||
nick_name: card.nick_name,
|
||||
card_network: None,
|
||||
card_isin: None,
|
||||
card_issuer: None,
|
||||
card_type: None,
|
||||
saved_to_locker: true,
|
||||
});
|
||||
|
||||
let updated_pmd = updated_card.as_ref().map(|card| {
|
||||
PaymentMethodsData::Card(CardDetailsPaymentMethod::from(
|
||||
card.clone(),
|
||||
))
|
||||
});
|
||||
let pm_data_encrypted =
|
||||
create_encrypted_payment_method_data(key_store, updated_pmd).await;
|
||||
let updated_pmd = updated_card.as_ref().map(|card| {
|
||||
PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone()))
|
||||
});
|
||||
let pm_data_encrypted =
|
||||
create_encrypted_payment_method_data(key_store, updated_pmd).await;
|
||||
|
||||
let pm_update = storage::PaymentMethodUpdate::PaymentMethodDataUpdate {
|
||||
payment_method_data: pm_data_encrypted,
|
||||
};
|
||||
let pm_update = storage::PaymentMethodUpdate::PaymentMethodDataUpdate {
|
||||
payment_method_data: pm_data_encrypted,
|
||||
};
|
||||
|
||||
db.update_payment_method(pm, pm_update)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to add payment method in db")?;
|
||||
}
|
||||
Err(err) => {
|
||||
if err.current_context().is_db_not_found() {
|
||||
insert_payment_method(
|
||||
db,
|
||||
&resp,
|
||||
req,
|
||||
key_store,
|
||||
merchant_id,
|
||||
&customer_id,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
Err(err)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while finding payment method")
|
||||
}?;
|
||||
}
|
||||
}
|
||||
db.update_payment_method(existing_pm, pm_update)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to add payment method in db")?;
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let pm_metadata = resp.metadata.as_ref().map(|data| data.peek());
|
||||
|
||||
let locker_id = Some(resp.payment_method_id);
|
||||
resp.payment_method_id = generate_id(consts::ID_LENGTH, "pm");
|
||||
insert_payment_method(
|
||||
db,
|
||||
&resp,
|
||||
@ -314,14 +358,16 @@ pub async fn add_payment_method(
|
||||
merchant_id,
|
||||
&customer_id,
|
||||
pm_metadata.cloned(),
|
||||
locker_id,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(services::ApplicationResponse::Json(resp))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn insert_payment_method(
|
||||
db: &dyn db::StorageInterface,
|
||||
resp: &api::PaymentMethodResponse,
|
||||
@ -330,7 +376,8 @@ pub async fn insert_payment_method(
|
||||
merchant_id: &str,
|
||||
customer_id: &str,
|
||||
pm_metadata: Option<serde_json::Value>,
|
||||
) -> errors::RouterResult<()> {
|
||||
locker_id: Option<String>,
|
||||
) -> errors::RouterResult<diesel_models::PaymentMethod> {
|
||||
let pm_card_details = resp
|
||||
.card
|
||||
.as_ref()
|
||||
@ -341,14 +388,13 @@ pub async fn insert_payment_method(
|
||||
&req,
|
||||
customer_id,
|
||||
&resp.payment_method_id,
|
||||
locker_id,
|
||||
merchant_id,
|
||||
pm_metadata,
|
||||
pm_data_encrypted,
|
||||
key_store,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
@ -372,7 +418,7 @@ pub async fn update_customer_payment_method(
|
||||
&state,
|
||||
&pm.customer_id,
|
||||
&pm.merchant_id,
|
||||
&pm.payment_method_id,
|
||||
pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
|
||||
)
|
||||
.await?;
|
||||
};
|
||||
@ -915,7 +961,7 @@ pub async fn mock_call_to_locker_hs<'a>(
|
||||
duplication_check: None,
|
||||
};
|
||||
Ok(payment_methods::StoreCardResp {
|
||||
status: "SUCCESS".to_string(),
|
||||
status: "Ok".to_string(),
|
||||
error_code: None,
|
||||
error_message: None,
|
||||
payload: Some(payload),
|
||||
@ -1001,7 +1047,7 @@ pub async fn mock_delete_card_hs<'a>(
|
||||
.await
|
||||
.change_context(errors::VaultError::FetchCardFailed)?;
|
||||
Ok(payment_methods::DeleteCardResp {
|
||||
status: "SUCCESS".to_string(),
|
||||
status: "Ok".to_string(),
|
||||
error_code: None,
|
||||
error_message: None,
|
||||
})
|
||||
@ -1020,7 +1066,7 @@ pub async fn mock_delete_card<'a>(
|
||||
card_id: Some(locker_mock_up.card_id),
|
||||
external_id: Some(locker_mock_up.external_id),
|
||||
card_isin: None,
|
||||
status: "SUCCESS".to_string(),
|
||||
status: "Ok".to_string(),
|
||||
})
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@ -2640,7 +2686,11 @@ pub async fn list_customer_payment_method(
|
||||
(
|
||||
card_details,
|
||||
None,
|
||||
PaymentTokenData::permanent_card(pm.payment_method_id.clone()),
|
||||
PaymentTokenData::permanent_card(
|
||||
Some(pm.payment_method_id.clone()),
|
||||
pm.locker_id.clone().or(Some(pm.payment_method_id.clone())),
|
||||
pm.locker_id.clone().unwrap_or(pm.payment_method_id.clone()),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
continue;
|
||||
@ -2660,7 +2710,7 @@ pub async fn list_customer_payment_method(
|
||||
&token,
|
||||
&pm.customer_id,
|
||||
&pm.merchant_id,
|
||||
&pm.payment_method_id,
|
||||
pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
|
||||
)
|
||||
.await?,
|
||||
),
|
||||
@ -2901,7 +2951,7 @@ pub async fn get_card_details_from_locker(
|
||||
state,
|
||||
&pm.customer_id,
|
||||
&pm.merchant_id,
|
||||
&pm.payment_method_id,
|
||||
pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
@ -3166,7 +3216,7 @@ pub async fn retrieve_payment_method(
|
||||
&state,
|
||||
&pm.customer_id,
|
||||
&pm.merchant_id,
|
||||
&pm.payment_method_id,
|
||||
pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
@ -3216,11 +3266,11 @@ pub async fn delete_payment_method(
|
||||
&state,
|
||||
&key.customer_id,
|
||||
&key.merchant_id,
|
||||
pm_id.payment_method_id.as_str(),
|
||||
key.locker_id.as_ref().unwrap_or(&key.payment_method_id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if response.status == "SUCCESS" {
|
||||
if response.status == "Ok" {
|
||||
logger::info!("Card From locker deleted Successfully!");
|
||||
} else {
|
||||
logger::error!("Error: Deleting Card From Locker!\n{:#?}", response);
|
||||
|
||||
@ -374,46 +374,6 @@ pub fn mk_add_card_response_hs(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mk_add_card_response(
|
||||
card: api::CardDetail,
|
||||
response: AddCardResponse,
|
||||
req: api::PaymentMethodCreate,
|
||||
merchant_id: &str,
|
||||
) -> api::PaymentMethodResponse {
|
||||
let mut card_number = card.card_number.peek().to_owned();
|
||||
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: Some(response.external_id.into()), // [#256]
|
||||
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(),
|
||||
customer_id: req.customer_id,
|
||||
payment_method_id: response.card_id,
|
||||
payment_method: req.payment_method,
|
||||
payment_method_type: req.payment_method_type,
|
||||
bank_transfer: None,
|
||||
card: Some(card),
|
||||
metadata: req.metadata,
|
||||
created: Some(common_utils::date_time::now()),
|
||||
recurring_enabled: false, // [#256]
|
||||
installment_payment_enabled: false, // [#256] Pending on discussion, and not stored in the card locker
|
||||
payment_experience: None, // [#256]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mk_add_card_request(
|
||||
locker: &settings::Locker,
|
||||
card: &api::CardDetail,
|
||||
|
||||
@ -6,6 +6,7 @@ use router_env::{instrument, tracing};
|
||||
|
||||
use super::helpers;
|
||||
use crate::{
|
||||
consts,
|
||||
core::{
|
||||
errors::{self, ConnectorErrorExt, RouterResult, StorageErrorExt},
|
||||
mandate, payment_methods, payments,
|
||||
@ -19,7 +20,7 @@ use crate::{
|
||||
domain,
|
||||
storage::{self, enums as storage_enums},
|
||||
},
|
||||
utils::OptionExt,
|
||||
utils::{generate_id, OptionExt},
|
||||
};
|
||||
|
||||
#[instrument(skip_all)]
|
||||
@ -85,7 +86,7 @@ where
|
||||
.await?;
|
||||
let merchant_id = &merchant_account.merchant_id;
|
||||
|
||||
let locker_response = if !state.conf.locker.locker_enabled {
|
||||
let (mut resp, duplication_check) = if !state.conf.locker.locker_enabled {
|
||||
skip_saving_card_in_locker(
|
||||
merchant_account,
|
||||
payment_method_create_request.to_owned(),
|
||||
@ -100,9 +101,7 @@ where
|
||||
.await?
|
||||
};
|
||||
|
||||
let duplication_check = locker_response.1;
|
||||
|
||||
let pm_card_details = locker_response.0.card.as_ref().map(|card| {
|
||||
let pm_card_details = resp.card.as_ref().map(|card| {
|
||||
api::payment_methods::PaymentMethodsData::Card(CardDetailsPaymentMethod::from(
|
||||
card.clone(),
|
||||
))
|
||||
@ -115,13 +114,44 @@ where
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut payment_method_id = resp.payment_method_id.clone();
|
||||
let mut locker_id = None;
|
||||
|
||||
match duplication_check {
|
||||
Some(duplication_check) => match duplication_check {
|
||||
payment_methods::transformers::DataDuplicationCheck::Duplicated => {
|
||||
let existing_pm = db
|
||||
.find_payment_method(&locker_response.0.payment_method_id)
|
||||
.await;
|
||||
match existing_pm {
|
||||
let payment_method = {
|
||||
let existing_pm_by_pmid =
|
||||
db.find_payment_method(&payment_method_id).await;
|
||||
|
||||
if let Err(err) = existing_pm_by_pmid {
|
||||
if err.current_context().is_db_not_found() {
|
||||
locker_id = Some(payment_method_id.clone());
|
||||
let existing_pm_by_locker_id = db
|
||||
.find_payment_method_by_locker_id(&payment_method_id)
|
||||
.await;
|
||||
|
||||
match &existing_pm_by_locker_id {
|
||||
Ok(pm) => {
|
||||
payment_method_id = pm.payment_method_id.clone()
|
||||
}
|
||||
Err(_) => {
|
||||
payment_method_id =
|
||||
generate_id(consts::ID_LENGTH, "pm")
|
||||
}
|
||||
};
|
||||
existing_pm_by_locker_id
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
} else {
|
||||
existing_pm_by_pmid
|
||||
}
|
||||
};
|
||||
|
||||
resp.payment_method_id = payment_method_id;
|
||||
|
||||
match payment_method {
|
||||
Ok(pm) => {
|
||||
let pm_metadata = create_payment_method_metadata(
|
||||
pm.metadata.as_ref(),
|
||||
@ -146,7 +176,8 @@ where
|
||||
db,
|
||||
&payment_method_create_request,
|
||||
&customer.customer_id,
|
||||
&locker_response.0.payment_method_id,
|
||||
&resp.payment_method_id,
|
||||
locker_id,
|
||||
merchant_id,
|
||||
pm_metadata,
|
||||
pm_data_encrypted,
|
||||
@ -165,22 +196,87 @@ where
|
||||
}
|
||||
payment_methods::transformers::DataDuplicationCheck::MetaDataChanged => {
|
||||
if let Some(card) = payment_method_create_request.card.clone() {
|
||||
let payment_method = {
|
||||
let existing_pm_by_pmid =
|
||||
db.find_payment_method(&payment_method_id).await;
|
||||
|
||||
if let Err(err) = existing_pm_by_pmid {
|
||||
if err.current_context().is_db_not_found() {
|
||||
locker_id = Some(payment_method_id.clone());
|
||||
let existing_pm_by_locker_id = db
|
||||
.find_payment_method_by_locker_id(
|
||||
&payment_method_id,
|
||||
)
|
||||
.await;
|
||||
|
||||
match &existing_pm_by_locker_id {
|
||||
Ok(pm) => {
|
||||
payment_method_id = pm.payment_method_id.clone()
|
||||
}
|
||||
Err(_) => {
|
||||
payment_method_id =
|
||||
generate_id(consts::ID_LENGTH, "pm")
|
||||
}
|
||||
};
|
||||
existing_pm_by_locker_id
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
} else {
|
||||
existing_pm_by_pmid
|
||||
}
|
||||
};
|
||||
|
||||
resp.payment_method_id = payment_method_id;
|
||||
|
||||
let existing_pm =
|
||||
match payment_method {
|
||||
Ok(pm) => Ok(pm),
|
||||
Err(err) => {
|
||||
if err.current_context().is_db_not_found() {
|
||||
payment_methods::cards::insert_payment_method(
|
||||
db,
|
||||
&resp,
|
||||
payment_method_create_request.clone(),
|
||||
key_store,
|
||||
&merchant_account.merchant_id,
|
||||
&customer.customer_id,
|
||||
resp.metadata.clone().map(|val| val.expose()),
|
||||
locker_id,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
Err(err)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while finding payment method")
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
payment_methods::cards::delete_card_from_locker(
|
||||
state,
|
||||
&customer.customer_id,
|
||||
merchant_id,
|
||||
&locker_response.0.payment_method_id,
|
||||
existing_pm
|
||||
.locker_id
|
||||
.as_ref()
|
||||
.unwrap_or(&existing_pm.payment_method_id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let add_card_resp = payment_methods::cards::add_card_hs(
|
||||
state,
|
||||
payment_method_create_request.clone(),
|
||||
payment_method_create_request,
|
||||
&card,
|
||||
customer.customer_id.clone(),
|
||||
merchant_account,
|
||||
api::enums::LockerChoice::HyperswitchCardVault,
|
||||
Some(&locker_response.0.payment_method_id),
|
||||
Some(
|
||||
existing_pm
|
||||
.locker_id
|
||||
.as_ref()
|
||||
.unwrap_or(&existing_pm.payment_method_id),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
@ -188,7 +284,7 @@ where
|
||||
logger::error!(vault_err=?err);
|
||||
db.delete_payment_method_by_merchant_id_payment_method_id(
|
||||
merchant_id,
|
||||
&locker_response.0.payment_method_id,
|
||||
&resp.payment_method_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(
|
||||
@ -201,90 +297,59 @@ where
|
||||
))?
|
||||
};
|
||||
|
||||
let existing_pm = db
|
||||
.find_payment_method(&locker_response.0.payment_method_id)
|
||||
let updated_card = Some(CardDetailFromLocker {
|
||||
scheme: None,
|
||||
last4_digits: Some(card.card_number.clone().get_last4()),
|
||||
issuer_country: None,
|
||||
card_number: Some(card.card_number),
|
||||
expiry_month: Some(card.card_exp_month),
|
||||
expiry_year: Some(card.card_exp_year),
|
||||
card_token: None,
|
||||
card_fingerprint: None,
|
||||
card_holder_name: card.card_holder_name,
|
||||
nick_name: card.nick_name,
|
||||
card_network: None,
|
||||
card_isin: None,
|
||||
card_issuer: None,
|
||||
card_type: None,
|
||||
saved_to_locker: true,
|
||||
});
|
||||
|
||||
let updated_pmd = updated_card.as_ref().map(|card| {
|
||||
PaymentMethodsData::Card(CardDetailsPaymentMethod::from(
|
||||
card.clone(),
|
||||
))
|
||||
});
|
||||
let pm_data_encrypted =
|
||||
payment_methods::cards::create_encrypted_payment_method_data(
|
||||
key_store,
|
||||
updated_pmd,
|
||||
)
|
||||
.await;
|
||||
match existing_pm {
|
||||
Ok(pm) => {
|
||||
let updated_card = Some(CardDetailFromLocker {
|
||||
scheme: None,
|
||||
last4_digits: Some(
|
||||
card.card_number.clone().get_last4(),
|
||||
),
|
||||
issuer_country: None,
|
||||
card_number: Some(card.card_number),
|
||||
expiry_month: Some(card.card_exp_month),
|
||||
expiry_year: Some(card.card_exp_year),
|
||||
card_token: None,
|
||||
card_fingerprint: None,
|
||||
card_holder_name: card.card_holder_name,
|
||||
nick_name: card.nick_name,
|
||||
card_network: None,
|
||||
card_isin: None,
|
||||
card_issuer: None,
|
||||
card_type: None,
|
||||
saved_to_locker: true,
|
||||
});
|
||||
|
||||
let updated_pmd = updated_card.as_ref().map(|card| {
|
||||
PaymentMethodsData::Card(
|
||||
CardDetailsPaymentMethod::from(card.clone()),
|
||||
)
|
||||
});
|
||||
let pm_data_encrypted =
|
||||
payment_methods::cards::create_encrypted_payment_method_data(
|
||||
key_store,
|
||||
updated_pmd,
|
||||
)
|
||||
.await;
|
||||
let pm_update =
|
||||
storage::PaymentMethodUpdate::PaymentMethodDataUpdate {
|
||||
payment_method_data: pm_data_encrypted,
|
||||
};
|
||||
|
||||
let pm_update =
|
||||
storage::PaymentMethodUpdate::PaymentMethodDataUpdate {
|
||||
payment_method_data: pm_data_encrypted,
|
||||
};
|
||||
|
||||
db.update_payment_method(pm, pm_update)
|
||||
.await
|
||||
.change_context(
|
||||
errors::ApiErrorResponse::InternalServerError,
|
||||
)
|
||||
.attach_printable(
|
||||
"Failed to add payment method in db",
|
||||
)?;
|
||||
}
|
||||
Err(err) => {
|
||||
if err.current_context().is_db_not_found() {
|
||||
payment_methods::cards::insert_payment_method(
|
||||
db,
|
||||
&locker_response.0,
|
||||
payment_method_create_request,
|
||||
key_store,
|
||||
merchant_id,
|
||||
&customer.customer_id,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
Err(err)
|
||||
.change_context(
|
||||
errors::ApiErrorResponse::InternalServerError,
|
||||
)
|
||||
.attach_printable(
|
||||
"Error while finding payment method",
|
||||
)
|
||||
}?;
|
||||
}
|
||||
}
|
||||
db.update_payment_method(existing_pm, pm_update)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to add payment method in db")?;
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let pm_metadata = create_payment_method_metadata(None, connector_token)?;
|
||||
|
||||
locker_id = Some(resp.payment_method_id);
|
||||
resp.payment_method_id = generate_id(consts::ID_LENGTH, "pm");
|
||||
payment_methods::cards::create_payment_method(
|
||||
db,
|
||||
&payment_method_create_request,
|
||||
&customer.customer_id,
|
||||
&locker_response.0.payment_method_id,
|
||||
&resp.payment_method_id,
|
||||
locker_id,
|
||||
merchant_id,
|
||||
pm_metadata,
|
||||
pm_data_encrypted,
|
||||
@ -294,7 +359,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
Some(locker_response.0.payment_method_id)
|
||||
Some(resp.payment_method_id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
@ -84,9 +84,11 @@ pub async fn make_payout_method_data<'a>(
|
||||
.attach_printable("failed to deserialize hyperswitch token data")?;
|
||||
|
||||
let payment_token = match payment_token_data {
|
||||
storage::PaymentTokenData::PermanentCard(storage::CardTokenData { token }) => {
|
||||
Some(token)
|
||||
}
|
||||
storage::PaymentTokenData::PermanentCard(storage::CardTokenData {
|
||||
locker_id,
|
||||
token,
|
||||
..
|
||||
}) => locker_id.or(Some(token)),
|
||||
storage::PaymentTokenData::TemporaryGeneric(storage::GenericTokenData {
|
||||
token,
|
||||
}) => Some(token),
|
||||
@ -370,11 +372,13 @@ pub async fn save_payout_data_to_locker(
|
||||
card_network: None,
|
||||
};
|
||||
|
||||
let payment_method_id = common_utils::generate_id(crate::consts::ID_LENGTH, "pm");
|
||||
cards::create_payment_method(
|
||||
db,
|
||||
&payment_method,
|
||||
&payout_attempt.customer_id,
|
||||
&stored_resp.card_reference,
|
||||
&payment_method_id,
|
||||
Some(stored_resp.card_reference),
|
||||
&merchant_account.merchant_id,
|
||||
None,
|
||||
card_details_encrypted,
|
||||
|
||||
@ -1273,6 +1273,15 @@ impl PaymentMethodInterface for KafkaStore {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn find_payment_method_by_locker_id(
|
||||
&self,
|
||||
locker_id: &str,
|
||||
) -> CustomResult<storage::PaymentMethod, errors::StorageError> {
|
||||
self.diesel_store
|
||||
.find_payment_method_by_locker_id(locker_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn insert_payment_method(
|
||||
&self,
|
||||
m: storage::PaymentMethodNew,
|
||||
|
||||
@ -15,6 +15,11 @@ pub trait PaymentMethodInterface {
|
||||
payment_method_id: &str,
|
||||
) -> CustomResult<storage::PaymentMethod, errors::StorageError>;
|
||||
|
||||
async fn find_payment_method_by_locker_id(
|
||||
&self,
|
||||
locker_id: &str,
|
||||
) -> CustomResult<storage::PaymentMethod, errors::StorageError>;
|
||||
|
||||
async fn find_payment_method_by_customer_id_merchant_id_list(
|
||||
&self,
|
||||
customer_id: &str,
|
||||
@ -52,6 +57,17 @@ impl PaymentMethodInterface for Store {
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn find_payment_method_by_locker_id(
|
||||
&self,
|
||||
locker_id: &str,
|
||||
) -> CustomResult<storage::PaymentMethod, errors::StorageError> {
|
||||
let conn = connection::pg_connection_read(self).await?;
|
||||
storage::PaymentMethod::find_by_locker_id(&conn, locker_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn insert_payment_method(
|
||||
&self,
|
||||
payment_method_new: storage::PaymentMethodNew,
|
||||
@ -127,6 +143,25 @@ impl PaymentMethodInterface for MockDb {
|
||||
}
|
||||
}
|
||||
|
||||
async fn find_payment_method_by_locker_id(
|
||||
&self,
|
||||
locker_id: &str,
|
||||
) -> CustomResult<storage::PaymentMethod, errors::StorageError> {
|
||||
let payment_methods = self.payment_methods.lock().await;
|
||||
let payment_method = payment_methods
|
||||
.iter()
|
||||
.find(|pm| pm.locker_id == Some(locker_id.to_string()))
|
||||
.cloned();
|
||||
|
||||
match payment_method {
|
||||
Some(pm) => Ok(pm),
|
||||
None => Err(errors::StorageError::ValueNotFound(
|
||||
"cannot find payment method".to_string(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn insert_payment_method(
|
||||
&self,
|
||||
payment_method_new: storage::PaymentMethodNew,
|
||||
@ -142,6 +177,7 @@ impl PaymentMethodInterface for MockDb {
|
||||
customer_id: payment_method_new.customer_id,
|
||||
merchant_id: payment_method_new.merchant_id,
|
||||
payment_method_id: payment_method_new.payment_method_id,
|
||||
locker_id: payment_method_new.locker_id,
|
||||
accepted_currency: payment_method_new.accepted_currency,
|
||||
scheme: payment_method_new.scheme,
|
||||
token: payment_method_new.token,
|
||||
|
||||
@ -51,7 +51,10 @@ impl MandateResponseExt for MandateResponse {
|
||||
state,
|
||||
&payment_method.customer_id,
|
||||
&payment_method.merchant_id,
|
||||
&payment_method.payment_method_id,
|
||||
payment_method
|
||||
.locker_id
|
||||
.as_ref()
|
||||
.unwrap_or(&payment_method.payment_method_id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@ -13,6 +13,8 @@ pub enum PaymentTokenKind {
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CardTokenData {
|
||||
pub payment_method_id: Option<String>,
|
||||
pub locker_id: Option<String>,
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
@ -34,8 +36,16 @@ pub enum PaymentTokenData {
|
||||
}
|
||||
|
||||
impl PaymentTokenData {
|
||||
pub fn permanent_card(token: String) -> Self {
|
||||
Self::PermanentCard(CardTokenData { token })
|
||||
pub fn permanent_card(
|
||||
payment_method_id: Option<String>,
|
||||
locker_id: Option<String>,
|
||||
token: String,
|
||||
) -> Self {
|
||||
Self::PermanentCard(CardTokenData {
|
||||
payment_method_id,
|
||||
locker_id,
|
||||
token,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn temporary_generic(token: String) -> Self {
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
|
||||
ALTER TABLE payment_methods DROP COLUMN IF EXISTS locker_id;
|
||||
@ -0,0 +1,3 @@
|
||||
-- Your SQL goes here
|
||||
|
||||
ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS locker_id VARCHAR(64) DEFAULT NULL;
|
||||
Reference in New Issue
Block a user