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:
Chethan Rao
2024-02-27 13:33:43 +05:30
committed by GitHub
parent 1c6913be74
commit 385622678f
16 changed files with 395 additions and 234 deletions

View File

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

View File

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

View File

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

View File

@ -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()?;

View File

@ -83,8 +83,12 @@ 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)
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 {
@ -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)

View File

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

View File

@ -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(
get_or_insert_payment_method(
db,
&resp,
req,
key_store,
merchant_id,
req.clone(),
&mut resp,
merchant_account,
&customer_id,
None,
key_store,
)
.await
} else {
Err(err)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error while finding payment method")
}?
};
.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,9 +310,6 @@ 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()),
@ -265,9 +329,7 @@ pub async fn add_payment_method(
});
let updated_pmd = updated_card.as_ref().map(|card| {
PaymentMethodsData::Card(CardDetailsPaymentMethod::from(
card.clone(),
))
PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone()))
});
let pm_data_encrypted =
create_encrypted_payment_method_data(key_store, updated_pmd).await;
@ -276,36 +338,18 @@ pub async fn add_payment_method(
payment_method_data: pm_data_encrypted,
};
db.update_payment_method(pm, pm_update)
db.update_payment_method(existing_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")
}?;
}
}
}
}
},
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);

View File

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

View File

@ -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)
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 {
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,16 +297,9 @@ where
))?
};
let existing_pm = db
.find_payment_method(&locker_response.0.payment_method_id)
.await;
match existing_pm {
Ok(pm) => {
let updated_card = Some(CardDetailFromLocker {
scheme: None,
last4_digits: Some(
card.card_number.clone().get_last4(),
),
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),
@ -227,9 +316,9 @@ where
});
let updated_pmd = updated_card.as_ref().map(|card| {
PaymentMethodsData::Card(
CardDetailsPaymentMethod::from(card.clone()),
)
PaymentMethodsData::Card(CardDetailsPaymentMethod::from(
card.clone(),
))
});
let pm_data_encrypted =
payment_methods::cards::create_encrypted_payment_method_data(
@ -243,48 +332,24 @@ where
payment_method_data: pm_data_encrypted,
};
db.update_payment_method(pm, pm_update)
db.update_payment_method(existing_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",
)
}?;
}
}
.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
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
-- This file should undo anything in `up.sql`
ALTER TABLE payment_methods DROP COLUMN IF EXISTS locker_id;

View File

@ -0,0 +1,3 @@
-- Your SQL goes here
ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS locker_id VARCHAR(64) DEFAULT NULL;