mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(customer_v2): Add customer V2 delete api (#5518)
Co-authored-by: Narayan Bhat <narayan.bhat@juspay.in> Co-authored-by: hrithikesh026 <hrithikesh.vm@juspay.in> Co-authored-by: Prajjwal Kumar <prajjwal.kumar@juspay.in> Co-authored-by: Sanchith Hegde <sanchith.hegde@juspay.in> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Sahkal Poddar <sahkalpoddar@Sahkals-MacBook-Air.local>
This commit is contained in:
@ -1,36 +1,38 @@
|
||||
use api_models::customers::CustomerRequestWithEmail;
|
||||
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
|
||||
use common_utils::{crypto::Encryptable, types::Description};
|
||||
use common_utils::{
|
||||
crypto::Encryptable,
|
||||
errors::ReportSwitchExt,
|
||||
ext_traits::{AsyncExt, OptionExt},
|
||||
id_type, type_name,
|
||||
types::keymanager::{Identifier, KeyManagerState, ToEncryptable},
|
||||
types::{
|
||||
keymanager::{Identifier, KeyManagerState, ToEncryptable},
|
||||
Description,
|
||||
},
|
||||
};
|
||||
use error_stack::{report, ResultExt};
|
||||
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
|
||||
use masking::{Secret, SwitchStrategy};
|
||||
use router_env::{instrument, tracing};
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "customer_v2"))]
|
||||
use crate::core::payment_methods::cards::create_encrypted_data;
|
||||
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
|
||||
use crate::utils::CustomerAddress;
|
||||
use crate::{
|
||||
core::errors::{self, StorageErrorExt},
|
||||
core::{
|
||||
errors::{self, StorageErrorExt},
|
||||
payment_methods::cards,
|
||||
},
|
||||
db::StorageInterface,
|
||||
pii::PeekInterface,
|
||||
routes::SessionState,
|
||||
routes::{metrics, SessionState},
|
||||
services,
|
||||
types::{
|
||||
api::customers,
|
||||
domain::{self, types},
|
||||
storage::{self},
|
||||
storage::{self, enums},
|
||||
transformers::ForeignFrom,
|
||||
},
|
||||
};
|
||||
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
|
||||
use crate::{
|
||||
core::payment_methods::cards, routes::metrics, types::storage::enums, utils::CustomerAddress,
|
||||
};
|
||||
|
||||
pub const REDACTED: &str = "Redacted";
|
||||
|
||||
@ -264,8 +266,8 @@ impl CustomerCreateBridge for customers::CustomerRequest {
|
||||
updated_by: None,
|
||||
default_billing_address: encrypted_customer_billing_address.map(Into::into),
|
||||
default_shipping_address: encrypted_customer_shipping_address.map(Into::into),
|
||||
// status: Some(customer_domain::SoftDeleteStatus::Active)
|
||||
version: hyperswitch_domain_models::consts::API_VERSION,
|
||||
status: common_enums::DeleteStatus::Active,
|
||||
})
|
||||
}
|
||||
|
||||
@ -532,6 +534,178 @@ pub async fn list_customers(
|
||||
Ok(services::ApplicationResponse::Json(customers))
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "customer_v2",
|
||||
feature = "payment_methods_v2"
|
||||
))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn delete_customer(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
req: customers::GlobalId,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
) -> errors::CustomerResponse<customers::CustomerDeleteResponse> {
|
||||
let db = &*state.store;
|
||||
let key_manager_state = &(&state).into();
|
||||
req.fetch_domain_model_and_update_and_generate_delete_customer_response(
|
||||
db,
|
||||
&key_store,
|
||||
&merchant_account,
|
||||
key_manager_state,
|
||||
&state,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "customer_v2",
|
||||
feature = "payment_methods_v2"
|
||||
))]
|
||||
#[async_trait::async_trait]
|
||||
impl CustomerDeleteBridge for customers::GlobalId {
|
||||
async fn fetch_domain_model_and_update_and_generate_delete_customer_response<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn StorageInterface,
|
||||
key_store: &'a domain::MerchantKeyStore,
|
||||
merchant_account: &'a domain::MerchantAccount,
|
||||
key_manager_state: &'a KeyManagerState,
|
||||
state: &'a SessionState,
|
||||
) -> errors::CustomerResponse<customers::CustomerDeleteResponse> {
|
||||
let customer_orig = db
|
||||
.find_customer_by_global_id(
|
||||
key_manager_state,
|
||||
&self.id,
|
||||
merchant_account.get_id(),
|
||||
&key_store,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
|
||||
let merchant_reference_id = customer_orig.merchant_reference_id.clone();
|
||||
|
||||
let customer_mandates = db.find_mandate_by_global_id(&self.id).await.switch()?;
|
||||
|
||||
for mandate in customer_mandates.into_iter() {
|
||||
if mandate.mandate_status == enums::MandateStatus::Active {
|
||||
Err(errors::CustomersErrorResponse::MandateActive)?
|
||||
}
|
||||
}
|
||||
|
||||
match db
|
||||
.find_payment_method_list_by_global_id(key_manager_state, key_store, &self.id, None)
|
||||
.await
|
||||
{
|
||||
// check this in review
|
||||
Ok(customer_payment_methods) => {
|
||||
for pm in customer_payment_methods.into_iter() {
|
||||
if pm.payment_method == Some(enums::PaymentMethod::Card) {
|
||||
cards::delete_card_by_locker_id(
|
||||
&state,
|
||||
&self.id,
|
||||
merchant_account.get_id(),
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
}
|
||||
// No solution as of now, need to discuss this further with payment_method_v2
|
||||
|
||||
// db.delete_payment_method(
|
||||
// key_manager_state,
|
||||
// key_store,
|
||||
// pm,
|
||||
// )
|
||||
// .await
|
||||
// .switch()?;
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
if error.current_context().is_db_not_found() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error)
|
||||
.change_context(errors::CustomersErrorResponse::InternalServerError)
|
||||
.attach_printable(
|
||||
"failed find_payment_method_by_customer_id_merchant_id_list",
|
||||
)
|
||||
}?
|
||||
}
|
||||
};
|
||||
|
||||
let key = key_store.key.get_inner().peek();
|
||||
|
||||
let identifier = Identifier::Merchant(key_store.merchant_id.clone());
|
||||
let redacted_encrypted_value: Encryptable<Secret<_>> = types::crypto_operation(
|
||||
key_manager_state,
|
||||
type_name!(storage::Address),
|
||||
types::CryptoOperation::Encrypt(REDACTED.to_string().into()),
|
||||
identifier.clone(),
|
||||
key,
|
||||
)
|
||||
.await
|
||||
.and_then(|val| val.try_into_operation())
|
||||
.switch()?;
|
||||
|
||||
let redacted_encrypted_email = Encryptable::new(
|
||||
redacted_encrypted_value
|
||||
.clone()
|
||||
.into_inner()
|
||||
.switch_strategy(),
|
||||
redacted_encrypted_value.clone().into_encrypted(),
|
||||
);
|
||||
|
||||
let updated_customer = storage::CustomerUpdate::Update {
|
||||
name: Some(redacted_encrypted_value.clone()),
|
||||
email: Box::new(Some(redacted_encrypted_email)),
|
||||
phone: Box::new(Some(redacted_encrypted_value.clone())),
|
||||
description: Some(Description::new(REDACTED.to_string())),
|
||||
phone_country_code: Some(REDACTED.to_string()),
|
||||
metadata: None,
|
||||
connector_customer: None,
|
||||
default_billing_address: None,
|
||||
default_shipping_address: None,
|
||||
default_payment_method_id: None,
|
||||
status: Some(common_enums::DeleteStatus::Redacted),
|
||||
};
|
||||
|
||||
db.update_customer_by_global_id(
|
||||
key_manager_state,
|
||||
self.id.clone(),
|
||||
customer_orig,
|
||||
merchant_account.get_id(),
|
||||
updated_customer,
|
||||
&key_store,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
|
||||
let response = customers::CustomerDeleteResponse {
|
||||
merchant_reference_id,
|
||||
customer_deleted: true,
|
||||
address_deleted: true,
|
||||
payment_methods_deleted: true,
|
||||
id: self.id.clone(),
|
||||
};
|
||||
metrics::CUSTOMER_REDACTED.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(services::ApplicationResponse::Json(response))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
trait CustomerDeleteBridge {
|
||||
async fn fetch_domain_model_and_update_and_generate_delete_customer_response<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn StorageInterface,
|
||||
key_store: &'a domain::MerchantKeyStore,
|
||||
merchant_account: &'a domain::MerchantAccount,
|
||||
key_manager_state: &'a KeyManagerState,
|
||||
state: &'a SessionState,
|
||||
) -> errors::CustomerResponse<customers::CustomerDeleteResponse>;
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(feature = "customer_v2"),
|
||||
@ -544,174 +718,203 @@ pub async fn delete_customer(
|
||||
req: customers::CustomerId,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
) -> errors::CustomerResponse<customers::CustomerDeleteResponse> {
|
||||
let db = &state.store;
|
||||
let db = &*state.store;
|
||||
let key_manager_state = &(&state).into();
|
||||
let customer_orig = db
|
||||
.find_customer_by_customer_id_merchant_id(
|
||||
req.fetch_domain_model_and_update_and_generate_delete_customer_response(
|
||||
db,
|
||||
&key_store,
|
||||
&merchant_account,
|
||||
key_manager_state,
|
||||
&state,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(feature = "customer_v2"),
|
||||
not(feature = "payment_methods_v2")
|
||||
))]
|
||||
#[async_trait::async_trait]
|
||||
impl CustomerDeleteBridge for customers::CustomerId {
|
||||
async fn fetch_domain_model_and_update_and_generate_delete_customer_response<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn StorageInterface,
|
||||
key_store: &'a domain::MerchantKeyStore,
|
||||
merchant_account: &'a domain::MerchantAccount,
|
||||
key_manager_state: &'a KeyManagerState,
|
||||
state: &'a SessionState,
|
||||
) -> errors::CustomerResponse<customers::CustomerDeleteResponse> {
|
||||
let customer_orig = db
|
||||
.find_customer_by_customer_id_merchant_id(
|
||||
key_manager_state,
|
||||
&self.customer_id,
|
||||
merchant_account.get_id(),
|
||||
key_store,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
|
||||
let customer_mandates = db
|
||||
.find_mandate_by_merchant_id_customer_id(merchant_account.get_id(), &self.customer_id)
|
||||
.await
|
||||
.switch()?;
|
||||
|
||||
for mandate in customer_mandates.into_iter() {
|
||||
if mandate.mandate_status == enums::MandateStatus::Active {
|
||||
Err(errors::CustomersErrorResponse::MandateActive)?
|
||||
}
|
||||
}
|
||||
|
||||
match db
|
||||
.find_payment_method_by_customer_id_merchant_id_list(
|
||||
key_manager_state,
|
||||
key_store,
|
||||
&self.customer_id,
|
||||
merchant_account.get_id(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
// check this in review
|
||||
Ok(customer_payment_methods) => {
|
||||
for pm in customer_payment_methods.into_iter() {
|
||||
if pm.payment_method == Some(enums::PaymentMethod::Card) {
|
||||
cards::delete_card_from_locker(
|
||||
state,
|
||||
&self.customer_id,
|
||||
merchant_account.get_id(),
|
||||
pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
}
|
||||
db.delete_payment_method_by_merchant_id_payment_method_id(
|
||||
key_manager_state,
|
||||
key_store,
|
||||
merchant_account.get_id(),
|
||||
&pm.payment_method_id,
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
if error.current_context().is_db_not_found() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error)
|
||||
.change_context(errors::CustomersErrorResponse::InternalServerError)
|
||||
.attach_printable(
|
||||
"failed find_payment_method_by_customer_id_merchant_id_list",
|
||||
)
|
||||
}?
|
||||
}
|
||||
};
|
||||
|
||||
let key = key_store.key.get_inner().peek();
|
||||
let identifier = Identifier::Merchant(key_store.merchant_id.clone());
|
||||
let redacted_encrypted_value: Encryptable<Secret<_>> = types::crypto_operation(
|
||||
key_manager_state,
|
||||
&req.customer_id,
|
||||
merchant_account.get_id(),
|
||||
&key_store,
|
||||
type_name!(storage::Address),
|
||||
types::CryptoOperation::Encrypt(REDACTED.to_string().into()),
|
||||
identifier.clone(),
|
||||
key,
|
||||
)
|
||||
.await
|
||||
.and_then(|val| val.try_into_operation())
|
||||
.switch()?;
|
||||
|
||||
let redacted_encrypted_email = Encryptable::new(
|
||||
redacted_encrypted_value
|
||||
.clone()
|
||||
.into_inner()
|
||||
.switch_strategy(),
|
||||
redacted_encrypted_value.clone().into_encrypted(),
|
||||
);
|
||||
|
||||
let update_address = storage::AddressUpdate::Update {
|
||||
city: Some(REDACTED.to_string()),
|
||||
country: None,
|
||||
line1: Some(redacted_encrypted_value.clone()),
|
||||
line2: Some(redacted_encrypted_value.clone()),
|
||||
line3: Some(redacted_encrypted_value.clone()),
|
||||
state: Some(redacted_encrypted_value.clone()),
|
||||
zip: Some(redacted_encrypted_value.clone()),
|
||||
first_name: Some(redacted_encrypted_value.clone()),
|
||||
last_name: Some(redacted_encrypted_value.clone()),
|
||||
phone_number: Some(redacted_encrypted_value.clone()),
|
||||
country_code: Some(REDACTED.to_string()),
|
||||
updated_by: merchant_account.storage_scheme.to_string(),
|
||||
email: Some(redacted_encrypted_email),
|
||||
};
|
||||
|
||||
match db
|
||||
.update_address_by_merchant_id_customer_id(
|
||||
key_manager_state,
|
||||
&self.customer_id,
|
||||
merchant_account.get_id(),
|
||||
update_address,
|
||||
key_store,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => {
|
||||
if error.current_context().is_db_not_found() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error)
|
||||
.change_context(errors::CustomersErrorResponse::InternalServerError)
|
||||
.attach_printable("failed update_address_by_merchant_id_customer_id")
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
let updated_customer = storage::CustomerUpdate::Update {
|
||||
name: Some(redacted_encrypted_value.clone()),
|
||||
email: Some(
|
||||
types::crypto_operation(
|
||||
key_manager_state,
|
||||
type_name!(storage::Customer),
|
||||
types::CryptoOperation::Encrypt(REDACTED.to_string().into()),
|
||||
identifier,
|
||||
key,
|
||||
)
|
||||
.await
|
||||
.and_then(|val| val.try_into_operation())
|
||||
.switch()?,
|
||||
),
|
||||
phone: Box::new(Some(redacted_encrypted_value.clone())),
|
||||
description: Some(Description::new(REDACTED.to_string())),
|
||||
phone_country_code: Some(REDACTED.to_string()),
|
||||
metadata: None,
|
||||
connector_customer: None,
|
||||
address_id: None,
|
||||
};
|
||||
|
||||
db.update_customer_by_customer_id_merchant_id(
|
||||
key_manager_state,
|
||||
self.customer_id.clone(),
|
||||
merchant_account.get_id().to_owned(),
|
||||
customer_orig,
|
||||
updated_customer,
|
||||
key_store,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
|
||||
let customer_mandates = db
|
||||
.find_mandate_by_merchant_id_customer_id(merchant_account.get_id(), &req.customer_id)
|
||||
.await
|
||||
.switch()?;
|
||||
|
||||
for mandate in customer_mandates.into_iter() {
|
||||
if mandate.mandate_status == enums::MandateStatus::Active {
|
||||
Err(errors::CustomersErrorResponse::MandateActive)?
|
||||
}
|
||||
let response = customers::CustomerDeleteResponse {
|
||||
customer_id: self.customer_id.clone(),
|
||||
customer_deleted: true,
|
||||
address_deleted: true,
|
||||
payment_methods_deleted: true,
|
||||
};
|
||||
metrics::CUSTOMER_REDACTED.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(services::ApplicationResponse::Json(response))
|
||||
}
|
||||
|
||||
match db
|
||||
.find_payment_method_by_customer_id_merchant_id_list(
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
&req.customer_id,
|
||||
merchant_account.get_id(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
// check this in review
|
||||
Ok(customer_payment_methods) => {
|
||||
for pm in customer_payment_methods.into_iter() {
|
||||
if pm.payment_method == Some(enums::PaymentMethod::Card) {
|
||||
cards::delete_card_from_locker(
|
||||
&state,
|
||||
&req.customer_id,
|
||||
merchant_account.get_id(),
|
||||
pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id),
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
}
|
||||
db.delete_payment_method_by_merchant_id_payment_method_id(
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
merchant_account.get_id(),
|
||||
&pm.payment_method_id,
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
if error.current_context().is_db_not_found() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error)
|
||||
.change_context(errors::CustomersErrorResponse::InternalServerError)
|
||||
.attach_printable("failed find_payment_method_by_customer_id_merchant_id_list")
|
||||
}?
|
||||
}
|
||||
};
|
||||
|
||||
let key = key_store.key.get_inner().peek();
|
||||
let identifier = Identifier::Merchant(key_store.merchant_id.clone());
|
||||
let redacted_encrypted_value: Encryptable<Secret<_>> = types::crypto_operation(
|
||||
key_manager_state,
|
||||
type_name!(storage::Address),
|
||||
types::CryptoOperation::Encrypt(REDACTED.to_string().into()),
|
||||
identifier.clone(),
|
||||
key,
|
||||
)
|
||||
.await
|
||||
.and_then(|val| val.try_into_operation())
|
||||
.switch()?;
|
||||
|
||||
let redacted_encrypted_email = Encryptable::new(
|
||||
redacted_encrypted_value
|
||||
.clone()
|
||||
.into_inner()
|
||||
.switch_strategy(),
|
||||
redacted_encrypted_value.clone().into_encrypted(),
|
||||
);
|
||||
|
||||
let update_address = storage::AddressUpdate::Update {
|
||||
city: Some(REDACTED.to_string()),
|
||||
country: None,
|
||||
line1: Some(redacted_encrypted_value.clone()),
|
||||
line2: Some(redacted_encrypted_value.clone()),
|
||||
line3: Some(redacted_encrypted_value.clone()),
|
||||
state: Some(redacted_encrypted_value.clone()),
|
||||
zip: Some(redacted_encrypted_value.clone()),
|
||||
first_name: Some(redacted_encrypted_value.clone()),
|
||||
last_name: Some(redacted_encrypted_value.clone()),
|
||||
phone_number: Some(redacted_encrypted_value.clone()),
|
||||
country_code: Some(REDACTED.to_string()),
|
||||
updated_by: merchant_account.storage_scheme.to_string(),
|
||||
email: Some(redacted_encrypted_email),
|
||||
};
|
||||
|
||||
match db
|
||||
.update_address_by_merchant_id_customer_id(
|
||||
key_manager_state,
|
||||
&req.customer_id,
|
||||
merchant_account.get_id(),
|
||||
update_address,
|
||||
&key_store,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => {
|
||||
if error.current_context().is_db_not_found() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error)
|
||||
.change_context(errors::CustomersErrorResponse::InternalServerError)
|
||||
.attach_printable("failed update_address_by_merchant_id_customer_id")
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
let updated_customer = storage::CustomerUpdate::Update {
|
||||
name: Some(redacted_encrypted_value.clone()),
|
||||
email: Some(
|
||||
types::crypto_operation(
|
||||
key_manager_state,
|
||||
type_name!(storage::Customer),
|
||||
types::CryptoOperation::Encrypt(REDACTED.to_string().into()),
|
||||
identifier,
|
||||
key,
|
||||
)
|
||||
.await
|
||||
.and_then(|val| val.try_into_operation())
|
||||
.switch()?,
|
||||
),
|
||||
phone: Box::new(Some(redacted_encrypted_value.clone())),
|
||||
description: Some(Description::new(REDACTED.to_string())),
|
||||
phone_country_code: Some(REDACTED.to_string()),
|
||||
metadata: None,
|
||||
connector_customer: None,
|
||||
address_id: None,
|
||||
};
|
||||
db.update_customer_by_customer_id_merchant_id(
|
||||
key_manager_state,
|
||||
req.customer_id.clone(),
|
||||
merchant_account.get_id().to_owned(),
|
||||
customer_orig,
|
||||
updated_customer,
|
||||
&key_store,
|
||||
merchant_account.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.switch()?;
|
||||
|
||||
let response = customers::CustomerDeleteResponse {
|
||||
customer_id: req.customer_id,
|
||||
customer_deleted: true,
|
||||
address_deleted: true,
|
||||
payment_methods_deleted: true,
|
||||
};
|
||||
metrics::CUSTOMER_REDACTED.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(services::ApplicationResponse::Json(response))
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
@ -1072,7 +1275,7 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
|
||||
merchant_account.get_id(),
|
||||
storage::CustomerUpdate::Update {
|
||||
name: encryptable_customer.name,
|
||||
email: encryptable_customer.email,
|
||||
email: Box::new(encryptable_customer.email),
|
||||
phone: Box::new(encryptable_customer.phone),
|
||||
phone_country_code: self.phone_country_code.clone(),
|
||||
metadata: self.metadata.clone(),
|
||||
@ -1081,6 +1284,7 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
|
||||
default_billing_address: encrypted_customer_billing_address.map(Into::into),
|
||||
default_shipping_address: encrypted_customer_shipping_address.map(Into::into),
|
||||
default_payment_method_id: Some(self.default_payment_method_id.clone()),
|
||||
status: None,
|
||||
},
|
||||
key_store,
|
||||
merchant_account.storage_scheme,
|
||||
|
||||
@ -2008,6 +2008,15 @@ pub async fn delete_card_from_locker(
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "customer_v2"))]
|
||||
pub async fn delete_card_by_locker_id(
|
||||
state: &routes::SessionState,
|
||||
id: &String,
|
||||
merchant_id: &id_type::MerchantId,
|
||||
) -> errors::RouterResult<payment_methods::DeleteCardResp> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn add_card_hs(
|
||||
state: &routes::SessionState,
|
||||
@ -2399,6 +2408,18 @@ pub async fn delete_card_from_hs_locker<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
// Need to fix this function while completing v2
|
||||
#[cfg(all(feature = "v2", feature = "customer_v2"))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn delete_card_from_hs_locker_by_global_id<'a>(
|
||||
state: &routes::SessionState,
|
||||
id: &String,
|
||||
merchant_id: &id_type::MerchantId,
|
||||
card_reference: &'a str,
|
||||
) -> errors::RouterResult<payment_methods::DeleteCardResp> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
///Mock api for local testing
|
||||
pub async fn mock_call_to_locker_hs(
|
||||
db: &dyn db::StorageInterface,
|
||||
|
||||
@ -85,6 +85,14 @@ pub struct CardReqBody {
|
||||
pub card_reference: String,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "customer_v2"))]
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct CardReqBodyV2 {
|
||||
pub merchant_id: id_type::MerchantId,
|
||||
pub merchant_customer_id: String, // Not changing this as it might lead to api contract failure
|
||||
pub card_reference: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct RetrieveCardResp {
|
||||
pub status: String,
|
||||
@ -523,6 +531,42 @@ pub async fn mk_delete_card_request_hs(
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
// Need to fix this once we start moving to v2 completion
|
||||
#[cfg(all(feature = "v2", feature = "customer_v2"))]
|
||||
pub async fn mk_delete_card_request_hs_by_id(
|
||||
jwekey: &settings::Jwekey,
|
||||
locker: &settings::Locker,
|
||||
id: &String,
|
||||
merchant_id: &id_type::MerchantId,
|
||||
card_reference: &str,
|
||||
) -> CustomResult<services::Request, errors::VaultError> {
|
||||
let merchant_customer_id = id.to_owned();
|
||||
let card_req_body = CardReqBodyV2 {
|
||||
merchant_id: merchant_id.to_owned(),
|
||||
merchant_customer_id,
|
||||
card_reference: card_reference.to_owned(),
|
||||
};
|
||||
let payload = card_req_body
|
||||
.encode_to_vec()
|
||||
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
||||
|
||||
let private_key = jwekey.vault_private_key.peek().as_bytes();
|
||||
|
||||
let jws = encryption::jws_sign_payload(&payload, &locker.locker_signing_key_id, private_key)
|
||||
.await
|
||||
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
||||
|
||||
let jwe_payload =
|
||||
mk_basilisk_req(jwekey, &jws, api_enums::LockerChoice::HyperswitchCardVault).await?;
|
||||
|
||||
let mut url = locker.host.to_owned();
|
||||
url.push_str("/cards/delete");
|
||||
let mut request = services::Request::new(services::Method::Post, &url);
|
||||
request.add_header(headers::CONTENT_TYPE, "application/json".into());
|
||||
request.set_body(RequestContent::Json(Box::new(jwe_payload)));
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
pub fn mk_delete_card_response(
|
||||
response: DeleteCardResponse,
|
||||
) -> errors::RouterResult<DeleteCardResp> {
|
||||
|
||||
Reference in New Issue
Block a user