mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-31 01:57:45 +08:00
refactor(payment_methods): allow deletion of default payment method for a customer if only one pm exists (#4027)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -52,5 +52,5 @@ pub struct CustomerUpdateInternal {
|
|||||||
pub modified_at: Option<PrimitiveDateTime>,
|
pub modified_at: Option<PrimitiveDateTime>,
|
||||||
pub connector_customer: Option<serde_json::Value>,
|
pub connector_customer: Option<serde_json::Value>,
|
||||||
pub address_id: Option<String>,
|
pub address_id: Option<String>,
|
||||||
pub default_payment_method_id: Option<String>,
|
pub default_payment_method_id: Option<Option<String>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,7 @@ pub mod db_metrics {
|
|||||||
DeleteWithResult,
|
DeleteWithResult,
|
||||||
UpdateWithResults,
|
UpdateWithResults,
|
||||||
UpdateOne,
|
UpdateOne,
|
||||||
|
Count,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods, Table};
|
use async_bb8_diesel::AsyncRunQueryDsl;
|
||||||
|
use diesel::{
|
||||||
|
associations::HasTable, debug_query, pg::Pg, BoolExpressionMethods, ExpressionMethods,
|
||||||
|
QueryDsl, Table,
|
||||||
|
};
|
||||||
|
use error_stack::{IntoReport, ResultExt};
|
||||||
|
|
||||||
use super::generics;
|
use super::generics;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -96,6 +101,34 @@ impl PaymentMethod {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_count_by_customer_id_merchant_id_status(
|
||||||
|
conn: &PgPooledConn,
|
||||||
|
customer_id: &str,
|
||||||
|
merchant_id: &str,
|
||||||
|
status: common_enums::PaymentMethodStatus,
|
||||||
|
) -> StorageResult<i64> {
|
||||||
|
let filter = <Self as HasTable>::table()
|
||||||
|
.count()
|
||||||
|
.filter(
|
||||||
|
dsl::customer_id
|
||||||
|
.eq(customer_id.to_owned())
|
||||||
|
.and(dsl::merchant_id.eq(merchant_id.to_owned()))
|
||||||
|
.and(dsl::status.eq(status.to_owned())),
|
||||||
|
)
|
||||||
|
.into_boxed();
|
||||||
|
|
||||||
|
router_env::logger::debug!(query = %debug_query::<Pg, _>(&filter).to_string());
|
||||||
|
|
||||||
|
generics::db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(
|
||||||
|
filter.get_result_async::<i64>(conn),
|
||||||
|
generics::db_metrics::DatabaseOperation::Count,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::DatabaseError::Others)
|
||||||
|
.attach_printable("Failed to get a count of payment methods")
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn find_by_customer_id_merchant_id_status(
|
pub async fn find_by_customer_id_merchant_id_status(
|
||||||
conn: &PgPooledConn,
|
conn: &PgPooledConn,
|
||||||
customer_id: &str,
|
customer_id: &str,
|
||||||
|
|||||||
@ -3201,7 +3201,7 @@ pub async fn set_default_payment_method(
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let customer_update = CustomerUpdate::UpdateDefaultPaymentMethod {
|
let customer_update = CustomerUpdate::UpdateDefaultPaymentMethod {
|
||||||
default_payment_method_id: Some(payment_method_id.to_owned()),
|
default_payment_method_id: Some(Some(payment_method_id.to_owned())),
|
||||||
};
|
};
|
||||||
|
|
||||||
// update the db with the default payment method id
|
// update the db with the default payment method id
|
||||||
@ -3450,6 +3450,16 @@ pub async fn delete_payment_method(
|
|||||||
.await
|
.await
|
||||||
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
|
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
|
||||||
|
|
||||||
|
let payment_methods_count = db
|
||||||
|
.get_payment_method_count_by_customer_id_merchant_id_status(
|
||||||
|
&key.customer_id,
|
||||||
|
&merchant_account.merchant_id,
|
||||||
|
api_enums::PaymentMethodStatus::Active,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Failed to get a count of payment methods for a customer")?;
|
||||||
|
|
||||||
let customer = db
|
let customer = db
|
||||||
.find_customer_by_customer_id_merchant_id(
|
.find_customer_by_customer_id_merchant_id(
|
||||||
&key.customer_id,
|
&key.customer_id,
|
||||||
@ -3461,7 +3471,8 @@ pub async fn delete_payment_method(
|
|||||||
.attach_printable("Customer not found for the payment method")?;
|
.attach_printable("Customer not found for the payment method")?;
|
||||||
|
|
||||||
utils::when(
|
utils::when(
|
||||||
customer.default_payment_method_id.as_ref() == Some(&pm_id.payment_method_id),
|
customer.default_payment_method_id.as_ref() == Some(&pm_id.payment_method_id)
|
||||||
|
&& payment_methods_count > 1,
|
||||||
|| Err(errors::ApiErrorResponse::PaymentMethodDeleteFailed),
|
|| Err(errors::ApiErrorResponse::PaymentMethodDeleteFailed),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -3489,6 +3500,22 @@ pub async fn delete_payment_method(
|
|||||||
.await
|
.await
|
||||||
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
|
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
|
||||||
|
|
||||||
|
if customer.default_payment_method_id.as_ref() == Some(&pm_id.payment_method_id) {
|
||||||
|
let customer_update = CustomerUpdate::UpdateDefaultPaymentMethod {
|
||||||
|
default_payment_method_id: Some(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
db.update_customer_by_customer_id_merchant_id(
|
||||||
|
key.customer_id,
|
||||||
|
key.merchant_id,
|
||||||
|
customer_update,
|
||||||
|
&key_store,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Failed to update the default payment method id for the customer")?;
|
||||||
|
};
|
||||||
|
|
||||||
Ok(services::ApplicationResponse::Json(
|
Ok(services::ApplicationResponse::Json(
|
||||||
api::PaymentMethodDeleteResponse {
|
api::PaymentMethodDeleteResponse {
|
||||||
payment_method_id: key.payment_method_id,
|
payment_method_id: key.payment_method_id,
|
||||||
|
|||||||
@ -1299,6 +1299,21 @@ impl PaymentMethodInterface for KafkaStore {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_payment_method_count_by_customer_id_merchant_id_status(
|
||||||
|
&self,
|
||||||
|
customer_id: &str,
|
||||||
|
merchant_id: &str,
|
||||||
|
status: common_enums::PaymentMethodStatus,
|
||||||
|
) -> CustomResult<i64, errors::StorageError> {
|
||||||
|
self.diesel_store
|
||||||
|
.get_payment_method_count_by_customer_id_merchant_id_status(
|
||||||
|
customer_id,
|
||||||
|
merchant_id,
|
||||||
|
status,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn find_payment_method_by_locker_id(
|
async fn find_payment_method_by_locker_id(
|
||||||
&self,
|
&self,
|
||||||
locker_id: &str,
|
locker_id: &str,
|
||||||
|
|||||||
@ -36,6 +36,13 @@ pub trait PaymentMethodInterface {
|
|||||||
limit: Option<i64>,
|
limit: Option<i64>,
|
||||||
) -> CustomResult<Vec<storage::PaymentMethod>, errors::StorageError>;
|
) -> CustomResult<Vec<storage::PaymentMethod>, errors::StorageError>;
|
||||||
|
|
||||||
|
async fn get_payment_method_count_by_customer_id_merchant_id_status(
|
||||||
|
&self,
|
||||||
|
customer_id: &str,
|
||||||
|
merchant_id: &str,
|
||||||
|
status: common_enums::PaymentMethodStatus,
|
||||||
|
) -> CustomResult<i64, errors::StorageError>;
|
||||||
|
|
||||||
async fn insert_payment_method(
|
async fn insert_payment_method(
|
||||||
&self,
|
&self,
|
||||||
payment_method_new: storage::PaymentMethodNew,
|
payment_method_new: storage::PaymentMethodNew,
|
||||||
@ -80,6 +87,25 @@ impl PaymentMethodInterface for Store {
|
|||||||
.into_report()
|
.into_report()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
async fn get_payment_method_count_by_customer_id_merchant_id_status(
|
||||||
|
&self,
|
||||||
|
customer_id: &str,
|
||||||
|
merchant_id: &str,
|
||||||
|
status: common_enums::PaymentMethodStatus,
|
||||||
|
) -> CustomResult<i64, errors::StorageError> {
|
||||||
|
let conn = connection::pg_connection_read(self).await?;
|
||||||
|
storage::PaymentMethod::get_count_by_customer_id_merchant_id_status(
|
||||||
|
&conn,
|
||||||
|
customer_id,
|
||||||
|
merchant_id,
|
||||||
|
status,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
.into_report()
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
async fn insert_payment_method(
|
async fn insert_payment_method(
|
||||||
&self,
|
&self,
|
||||||
@ -204,6 +230,27 @@ impl PaymentMethodInterface for MockDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_payment_method_count_by_customer_id_merchant_id_status(
|
||||||
|
&self,
|
||||||
|
customer_id: &str,
|
||||||
|
merchant_id: &str,
|
||||||
|
status: common_enums::PaymentMethodStatus,
|
||||||
|
) -> CustomResult<i64, errors::StorageError> {
|
||||||
|
let payment_methods = self.payment_methods.lock().await;
|
||||||
|
let count = payment_methods
|
||||||
|
.iter()
|
||||||
|
.filter(|pm| {
|
||||||
|
pm.customer_id == customer_id
|
||||||
|
&& pm.merchant_id == merchant_id
|
||||||
|
&& pm.status == status
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
count
|
||||||
|
.try_into()
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::StorageError::MockDbError)
|
||||||
|
}
|
||||||
|
|
||||||
async fn insert_payment_method(
|
async fn insert_payment_method(
|
||||||
&self,
|
&self,
|
||||||
payment_method_new: storage::PaymentMethodNew,
|
payment_method_new: storage::PaymentMethodNew,
|
||||||
|
|||||||
@ -118,7 +118,7 @@ pub enum CustomerUpdate {
|
|||||||
connector_customer: Option<serde_json::Value>,
|
connector_customer: Option<serde_json::Value>,
|
||||||
},
|
},
|
||||||
UpdateDefaultPaymentMethod {
|
UpdateDefaultPaymentMethod {
|
||||||
default_payment_method_id: Option<String>,
|
default_payment_method_id: Option<Option<String>>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user