feat(payment_methods): delete payment method using payment token (#258)

Co-authored-by: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com>
Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
Manoj Ghorela
2023-01-03 11:11:23 +05:30
committed by GitHub
parent 4b4a9967b3
commit f42ea8d4a4
9 changed files with 99 additions and 41 deletions

View File

@ -285,4 +285,5 @@ pub struct TokenizedCardValue2 {
pub card_fingerprint: Option<String>,
pub external_id: Option<String>,
pub customer_id: Option<String>,
pub payment_method_id: Option<String>,
}

View File

@ -155,7 +155,7 @@ pub async fn add_card(
response
} else {
let card_id = generate_id(consts::ID_LENGTH, "card");
mock_add_card(db, &card_id, &card, None).await?
mock_add_card(db, &card_id, &card, None, None).await?
};
if let Some(false) = response.duplicate {
@ -187,6 +187,7 @@ pub async fn mock_add_card(
card_id: &str,
card: &api::CardDetail,
card_cvc: Option<String>,
payment_method_id: Option<String>,
) -> errors::CustomResult<payment_methods::AddCardResponse, errors::CardVaultError> {
let locker_mock_up = storage::LockerMockUpNew {
card_id: card_id.to_string(),
@ -198,6 +199,7 @@ pub async fn mock_add_card(
card_exp_year: card.card_exp_year.peek().to_string(),
card_exp_month: card.card_exp_month.peek().to_string(),
card_cvc,
payment_method_id,
};
let response = db
@ -231,7 +233,9 @@ pub async fn mock_get_card<'a>(
.await
.change_context(errors::CardVaultError::FetchCardFailed)?;
let add_card_response = payment_methods::AddCardResponse {
card_id: locker_mock_up.card_id,
card_id: locker_mock_up
.payment_method_id
.unwrap_or(locker_mock_up.card_id),
external_id: locker_mock_up.external_id,
card_fingerprint: locker_mock_up.card_fingerprint.into(),
card_global_fingerprint: locker_mock_up.card_global_fingerprint.into(),
@ -563,7 +567,7 @@ pub async fn get_lookup_key_from_locker(
.attach_printable("Get Card Details Failed")?;
let card = card_detail.clone();
let resp =
BasiliskCardSupport::create_payment_method_data_in_locker(state, payment_token, card)
BasiliskCardSupport::create_payment_method_data_in_locker(state, payment_token, card, pm)
.await?;
Ok(resp)
}
@ -576,6 +580,7 @@ impl BasiliskCardSupport {
state: &routes::AppState,
payment_token: &str,
card: api::CardDetailFromLocker,
pm: &storage::PaymentMethod,
) -> errors::RouterResult<api::CardDetailFromLocker> {
let card_number = card
.card_number
@ -604,7 +609,13 @@ impl BasiliskCardSupport {
card_holder_name: Some(card_holder_name.into()),
};
let db = &*state.store;
mock_add_card(db, payment_token, &card_detail, None)
mock_add_card(
db,
payment_token,
&card_detail,
None,
Some(pm.payment_method_id.to_string()),
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Add Card Failed")?;
@ -619,6 +630,7 @@ impl BasiliskCardSupport {
state: &routes::AppState,
payment_token: &str,
card: api::CardDetailFromLocker,
pm: &storage::PaymentMethod,
) -> errors::RouterResult<api::CardDetailFromLocker> {
let card_number = card
.card_number
@ -656,7 +668,13 @@ impl BasiliskCardSupport {
)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error getting Value1 for locker")?;
let value2 = payment_methods::mk_card_value2(None, Some(card_fingerprint), None, None)
let value2 = payment_methods::mk_card_value2(
None,
Some(card_fingerprint),
None,
Some(pm.customer_id.to_string()),
Some(pm.payment_method_id.to_string()),
)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error getting Value2 for locker")?;
create_tokenize(state, value1, Some(value2), payment_token.to_string()).await?;
@ -738,11 +756,23 @@ pub async fn delete_payment_method(
merchant_account: storage::MerchantAccount,
pm: api::PaymentMethodId,
) -> errors::RouterResponse<api::DeletePaymentMethodResponse> {
let (_, value2) =
helpers::Vault::get_payment_method_data_from_locker(state, &pm.payment_method_id).await?;
let payment_method_id = value2.map_or(
Err(errors::ApiErrorResponse::PaymentMethodNotFound),
|pm_value2| {
pm_value2
.payment_method_id
.map_or(Err(errors::ApiErrorResponse::PaymentMethodNotFound), |x| {
Ok(x)
})
},
)?;
let pm = state
.store
.delete_payment_method_by_merchant_id_payment_method_id(
&merchant_account.merchant_id,
&pm.payment_method_id,
&payment_method_id,
)
.await
.map_err(|error| {
@ -750,7 +780,7 @@ pub async fn delete_payment_method(
})?;
if pm.payment_method == enums::PaymentMethodType::Card {
let response = delete_card(state, &pm.merchant_id, &pm.payment_method_id).await?;
let response = delete_card(state, &pm.merchant_id, &payment_method_id).await?;
if response.status == "success" {
print!("Card From locker deleted Successfully")
} else {

View File

@ -235,12 +235,14 @@ pub fn mk_card_value2(
card_fingerprint: Option<String>,
external_id: Option<String>,
customer_id: Option<String>,
payment_method_id: Option<String>,
) -> CustomResult<String, errors::CardVaultError> {
let value2 = api::TokenizedCardValue2 {
card_security_code,
card_fingerprint,
external_id,
customer_id,
payment_method_id,
};
let value2_req = utils::Encode::<api::TokenizedCardValue2>::encode_to_string_of_json(&value2)
.change_context(errors::CardVaultError::FetchCardFailed)?;

View File

@ -727,7 +727,7 @@ pub async fn make_pm_data<'a, F: Clone, R>(
(_, Some(token)) => Ok::<_, error_stack::Report<errors::ApiErrorResponse>>(
if payment_method_type == Some(storage_enums::PaymentMethodType::Card) {
// TODO: Handle token expiry
let pm = Vault::get_payment_method_data_from_locker(state, token).await?;
let (pm, _) = Vault::get_payment_method_data_from_locker(state, token).await?;
let updated_pm = match (pm.clone(), card_cvc) {
(Some(api::PaymentMethod::Card(card)), Some(card_cvc)) => {
let mut updated_card = card;
@ -764,7 +764,7 @@ impl Vault {
pub async fn get_payment_method_data_from_locker(
state: &AppState,
lookup_key: &str,
) -> RouterResult<Option<api::PaymentMethod>> {
) -> RouterResult<(Option<api::PaymentMethod>, Option<api::TokenizedCardValue2>)> {
let (resp, card_cvc) = cards::mock_get_card(&*state.store, lookup_key)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
@ -782,14 +782,21 @@ impl Vault {
.expose_option()
.get_required_value("expiry_year")?;
let card_holder_name = card.name_on_card.expose_option().unwrap_or_default();
let card = api::PaymentMethod::Card(api::CCard {
let pm = api::PaymentMethod::Card(api::CCard {
card_number: card_number.into(),
card_exp_month: card_exp_month.into(),
card_exp_year: card_exp_year.into(),
card_holder_name: card_holder_name.into(),
card_cvc: card_cvc.unwrap_or_default().into(),
});
Ok(Some(card))
let value2 = api::TokenizedCardValue2 {
card_security_code: None,
card_fingerprint: None,
external_id: None,
customer_id: None,
payment_method_id: Some(card.card_id),
};
Ok((Some(pm), Some(value2)))
}
#[instrument(skip_all)]
@ -805,7 +812,13 @@ impl Vault {
card_holder_name: Some(card.card_holder_name.clone()),
};
let db = &*state.store;
cards::mock_add_card(db, txn_id, &card_detail, Some(card.card_cvc.peek().clone()))
cards::mock_add_card(
db,
txn_id,
&card_detail,
Some(card.card_cvc.peek().clone()),
None,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Add Card Failed")?;
@ -836,32 +849,27 @@ impl Vault {
pub async fn get_payment_method_data_from_locker(
state: &AppState,
lookup_key: &str,
) -> RouterResult<Option<api::PaymentMethod>> {
) -> RouterResult<(Option<api::PaymentMethod>, Option<api::TokenizedCardValue2>)> {
let de_tokenize = cards::get_tokenized_data(state, lookup_key, true).await?;
let value1: api::TokenizedCardValue1 = de_tokenize
.value1
.parse_struct("TokenizedCardValue1")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error parsing TokenizedCardValue1")?;
let value2 = de_tokenize.value2;
let card_cvc = if value2.is_empty() {
//mandatory field in api contract (when querying from legacy locker we don't get cvv), cvv handling needs to done
"".to_string()
} else {
let tk_value2: api::TokenizedCardValue2 = value2
let value2: api::TokenizedCardValue2 = de_tokenize
.value2
.parse_struct("TokenizedCardValue2")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error parsing TokenizedCardValue2")?;
tk_value2.card_security_code.unwrap_or_default()
};
let card = api::PaymentMethod::Card(api::CCard {
card_number: value1.card_number.into(),
card_exp_month: value1.exp_month.into(),
card_exp_year: value1.exp_year.into(),
card_holder_name: value1.name_on_card.unwrap_or_default().into(),
card_cvc: card_cvc.into(),
card_cvc: value2.card_security_code.clone().unwrap_or_default().into(),
});
Ok(Some(card))
Ok((Some(card), Some(value2)))
}
#[instrument(skip_all)]
@ -881,8 +889,13 @@ impl Vault {
)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error getting Value1 for locker")?;
let value2 =
transformers::mk_card_value2(Some(card.card_cvc.peek().clone()), None, None, None)
let value2 = transformers::mk_card_value2(
Some(card.card_cvc.peek().clone()),
None,
None,
None,
None,
)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error getting Value12 for locker")?;
cards::create_tokenize(state, value1, Some(value2), txn_id.to_string()).await

View File

@ -52,9 +52,12 @@ pub async fn construct_refund_router_data<'a, F>(
let payment_method_data = match payment_method_data.cloned() {
Some(v) => v,
None => {
helpers::Vault::get_payment_method_data_from_locker(state, &payment_attempt.attempt_id)
.await?
.get_required_value("payment_method_data")?
let (pm, _) = helpers::Vault::get_payment_method_data_from_locker(
state,
&payment_attempt.attempt_id,
)
.await?;
pm.get_required_value("payment_method_data")?
}
};

View File

@ -19,6 +19,7 @@ pub struct LockerMockUp {
pub customer_id: Option<String>,
pub duplicate: Option<bool>,
pub card_cvc: Option<String>,
pub payment_method_id: Option<String>,
}
#[derive(Clone, Debug, Default, Insertable, router_derive::DebugAsDisplay)]
@ -33,4 +34,5 @@ pub struct LockerMockUpNew {
pub card_exp_year: String,
pub card_exp_month: String,
pub card_cvc: Option<String>,
pub payment_method_id: Option<String>,
}

View File

@ -108,6 +108,7 @@ diesel::table! {
customer_id -> Nullable<Varchar>,
duplicate -> Nullable<Bool>,
card_cvc -> Nullable<Varchar>,
payment_method_id -> Nullable<Varchar>,
}
}

View File

@ -0,0 +1,3 @@
-- This file should undo anything in `up.sql`
ALTER TABLE locker_mock_up
DROP COLUMN payment_method_id;

View File

@ -0,0 +1,3 @@
-- Your SQL goes here
ALTER TABLE locker_mock_up
ADD COLUMN payment_method_id VARCHAR(64);