feat(payments): add amount and connector id filter in list (#4354)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Apoorv Dixit
2024-04-18 19:14:35 +05:30
committed by GitHub
parent 4feda8f890
commit 53e5307c3c
11 changed files with 95 additions and 30 deletions

View File

@ -3394,6 +3394,8 @@ pub struct PaymentListFilterConstraints {
pub limit: u32, pub limit: u32,
/// The starting point within a list of objects /// The starting point within a list of objects
pub offset: Option<u32>, pub offset: Option<u32>,
/// The amount to filter payments list
pub amount_filter: Option<AmountFilter>,
/// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc). /// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc).
#[serde(flatten)] #[serde(flatten)]
pub time_range: Option<TimeRange>, pub time_range: Option<TimeRange>,
@ -3409,6 +3411,8 @@ pub struct PaymentListFilterConstraints {
pub payment_method_type: Option<Vec<enums::PaymentMethodType>>, pub payment_method_type: Option<Vec<enums::PaymentMethodType>>,
/// The list of authentication types to filter payments list /// The list of authentication types to filter payments list
pub authentication_type: Option<Vec<enums::AuthenticationType>>, pub authentication_type: Option<Vec<enums::AuthenticationType>>,
/// The list of merchant connector ids to filter payments list for selected label
pub merchant_connector_id: Option<Vec<String>>,
} }
#[derive(Clone, Debug, serde::Serialize)] #[derive(Clone, Debug, serde::Serialize)]
pub struct PaymentListFilters { pub struct PaymentListFilters {
@ -3440,6 +3444,12 @@ pub struct PaymentListFiltersV2 {
pub authentication_type: Vec<enums::AuthenticationType>, pub authentication_type: Vec<enums::AuthenticationType>,
} }
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct AmountFilter {
pub start_amount: Option<i64>,
pub end_amount: Option<i64>,
}
#[derive( #[derive(
Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, ToSchema, Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, ToSchema,
)] )]

View File

@ -22,7 +22,7 @@ pub static FRM_CONFIGS_EG: &str = r#"
/// Maximum limit for payments list get api /// Maximum limit for payments list get api
pub const PAYMENTS_LIST_MAX_LIMIT_V1: u32 = 100; pub const PAYMENTS_LIST_MAX_LIMIT_V1: u32 = 100;
/// Maximum limit for payments list post api with filters /// Maximum limit for payments list post api with filters
pub const PAYMENTS_LIST_MAX_LIMIT_V2: u32 = 20; pub const PAYMENTS_LIST_MAX_LIMIT_V2: u32 = 50;
/// Default limit for payments list API /// Default limit for payments list API
pub fn default_payments_list_limit() -> u32 { pub fn default_payments_list_limit() -> u32 {
10 10

View File

@ -99,6 +99,7 @@ pub trait PaymentAttemptInterface {
payment_method: Option<Vec<storage_enums::PaymentMethod>>, payment_method: Option<Vec<storage_enums::PaymentMethod>>,
payment_method_type: Option<Vec<storage_enums::PaymentMethodType>>, payment_method_type: Option<Vec<storage_enums::PaymentMethodType>>,
authentication_type: Option<Vec<storage_enums::AuthenticationType>>, authentication_type: Option<Vec<storage_enums::AuthenticationType>>,
merchant_connector_id: Option<Vec<String>>,
storage_scheme: storage_enums::MerchantStorageScheme, storage_scheme: storage_enums::MerchantStorageScheme,
) -> error_stack::Result<i64, errors::StorageError>; ) -> error_stack::Result<i64, errors::StorageError>;
} }

View File

@ -430,12 +430,14 @@ pub struct PaymentIntentListParams {
pub offset: u32, pub offset: u32,
pub starting_at: Option<PrimitiveDateTime>, pub starting_at: Option<PrimitiveDateTime>,
pub ending_at: Option<PrimitiveDateTime>, pub ending_at: Option<PrimitiveDateTime>,
pub amount_filter: Option<api_models::payments::AmountFilter>,
pub connector: Option<Vec<api_models::enums::Connector>>, pub connector: Option<Vec<api_models::enums::Connector>>,
pub currency: Option<Vec<storage_enums::Currency>>, pub currency: Option<Vec<storage_enums::Currency>>,
pub status: Option<Vec<storage_enums::IntentStatus>>, pub status: Option<Vec<storage_enums::IntentStatus>>,
pub payment_method: Option<Vec<storage_enums::PaymentMethod>>, pub payment_method: Option<Vec<storage_enums::PaymentMethod>>,
pub payment_method_type: Option<Vec<storage_enums::PaymentMethodType>>, pub payment_method_type: Option<Vec<storage_enums::PaymentMethodType>>,
pub authentication_type: Option<Vec<storage_enums::AuthenticationType>>, pub authentication_type: Option<Vec<storage_enums::AuthenticationType>>,
pub merchant_connector_id: Option<Vec<String>>,
pub profile_id: Option<String>, pub profile_id: Option<String>,
pub customer_id: Option<String>, pub customer_id: Option<String>,
pub starting_after_id: Option<String>, pub starting_after_id: Option<String>,
@ -449,12 +451,14 @@ impl From<api_models::payments::PaymentListConstraints> for PaymentIntentFetchCo
offset: 0, offset: 0,
starting_at: value.created_gte.or(value.created_gt).or(value.created), starting_at: value.created_gte.or(value.created_gt).or(value.created),
ending_at: value.created_lte.or(value.created_lt).or(value.created), ending_at: value.created_lte.or(value.created_lt).or(value.created),
amount_filter: None,
connector: None, connector: None,
currency: None, currency: None,
status: None, status: None,
payment_method: None, payment_method: None,
payment_method_type: None, payment_method_type: None,
authentication_type: None, authentication_type: None,
merchant_connector_id: None,
profile_id: None, profile_id: None,
customer_id: value.customer_id, customer_id: value.customer_id,
starting_after_id: value.starting_after, starting_after_id: value.starting_after,
@ -470,12 +474,14 @@ impl From<api_models::payments::TimeRange> for PaymentIntentFetchConstraints {
offset: 0, offset: 0,
starting_at: Some(value.start_time), starting_at: Some(value.start_time),
ending_at: value.end_time, ending_at: value.end_time,
amount_filter: None,
connector: None, connector: None,
currency: None, currency: None,
status: None, status: None,
payment_method: None, payment_method: None,
payment_method_type: None, payment_method_type: None,
authentication_type: None, authentication_type: None,
merchant_connector_id: None,
profile_id: None, profile_id: None,
customer_id: None, customer_id: None,
starting_after_id: None, starting_after_id: None,
@ -494,12 +500,14 @@ impl From<api_models::payments::PaymentListFilterConstraints> for PaymentIntentF
offset: value.offset.unwrap_or_default(), offset: value.offset.unwrap_or_default(),
starting_at: value.time_range.map(|t| t.start_time), starting_at: value.time_range.map(|t| t.start_time),
ending_at: value.time_range.and_then(|t| t.end_time), ending_at: value.time_range.and_then(|t| t.end_time),
amount_filter: value.amount_filter,
connector: value.connector, connector: value.connector,
currency: value.currency, currency: value.currency,
status: value.status, status: value.status,
payment_method: value.payment_method, payment_method: value.payment_method,
payment_method_type: value.payment_method_type, payment_method_type: value.payment_method_type,
authentication_type: value.authentication_type, authentication_type: value.authentication_type,
merchant_connector_id: value.merchant_connector_id,
profile_id: value.profile_id, profile_id: value.profile_id,
customer_id: value.customer_id, customer_id: value.customer_id,
starting_after_id: None, starting_after_id: None,

View File

@ -309,6 +309,8 @@ impl PaymentAttempt {
filter_authentication_type, filter_authentication_type,
)) ))
} }
#[allow(clippy::too_many_arguments)]
pub async fn get_total_count_of_attempts( pub async fn get_total_count_of_attempts(
conn: &PgPooledConn, conn: &PgPooledConn,
merchant_id: &str, merchant_id: &str,
@ -317,6 +319,7 @@ impl PaymentAttempt {
payment_method: Option<Vec<enums::PaymentMethod>>, payment_method: Option<Vec<enums::PaymentMethod>>,
payment_method_type: Option<Vec<enums::PaymentMethodType>>, payment_method_type: Option<Vec<enums::PaymentMethodType>>,
authentication_type: Option<Vec<enums::AuthenticationType>>, authentication_type: Option<Vec<enums::AuthenticationType>>,
merchant_connector_id: Option<Vec<String>>,
) -> StorageResult<i64> { ) -> StorageResult<i64> {
let mut filter = <Self as HasTable>::table() let mut filter = <Self as HasTable>::table()
.count() .count()
@ -324,19 +327,22 @@ impl PaymentAttempt {
.filter(dsl::attempt_id.eq_any(active_attempt_ids.to_owned())) .filter(dsl::attempt_id.eq_any(active_attempt_ids.to_owned()))
.into_boxed(); .into_boxed();
if let Some(connector) = connector.clone() { if let Some(connector) = connector {
filter = filter.filter(dsl::connector.eq_any(connector)); filter = filter.filter(dsl::connector.eq_any(connector));
} }
if let Some(payment_method) = payment_method.clone() { if let Some(payment_method) = payment_method {
filter = filter.filter(dsl::payment_method.eq_any(payment_method)); filter = filter.filter(dsl::payment_method.eq_any(payment_method));
} }
if let Some(payment_method_type) = payment_method_type.clone() { if let Some(payment_method_type) = payment_method_type {
filter = filter.filter(dsl::payment_method_type.eq_any(payment_method_type)); filter = filter.filter(dsl::payment_method_type.eq_any(payment_method_type));
} }
if let Some(authentication_type) = authentication_type.clone() { if let Some(authentication_type) = authentication_type {
filter = filter.filter(dsl::authentication_type.eq_any(authentication_type)); filter = filter.filter(dsl::authentication_type.eq_any(authentication_type));
} }
if let Some(merchant_connector_id) = merchant_connector_id {
filter = filter.filter(dsl::merchant_connector_id.eq_any(merchant_connector_id))
}
router_env::logger::debug!(query = %debug_query::<Pg, _>(&filter).to_string()); router_env::logger::debug!(query = %debug_query::<Pg, _>(&filter).to_string());
db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>( db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(

View File

@ -2579,6 +2579,7 @@ pub async fn apply_filters_on_payments(
constraints.payment_method, constraints.payment_method,
constraints.payment_method_type, constraints.payment_method_type,
constraints.authentication_type, constraints.authentication_type,
constraints.merchant_connector_id,
merchant.storage_scheme, merchant.storage_scheme,
) )
.await .await

View File

@ -1258,6 +1258,7 @@ impl PaymentAttemptInterface for KafkaStore {
payment_method: Option<Vec<common_enums::PaymentMethod>>, payment_method: Option<Vec<common_enums::PaymentMethod>>,
payment_method_type: Option<Vec<common_enums::PaymentMethodType>>, payment_method_type: Option<Vec<common_enums::PaymentMethodType>>,
authentication_type: Option<Vec<common_enums::AuthenticationType>>, authentication_type: Option<Vec<common_enums::AuthenticationType>>,
merchant_connector_id: Option<Vec<String>>,
storage_scheme: MerchantStorageScheme, storage_scheme: MerchantStorageScheme,
) -> CustomResult<i64, errors::DataStorageError> { ) -> CustomResult<i64, errors::DataStorageError> {
self.diesel_store self.diesel_store
@ -1268,6 +1269,7 @@ impl PaymentAttemptInterface for KafkaStore {
payment_method, payment_method,
payment_method_type, payment_method_type,
authentication_type, authentication_type,
merchant_connector_id,
storage_scheme, storage_scheme,
) )
.await .await

View File

@ -922,14 +922,10 @@ pub async fn payments_list_by_filter(
state, state,
&req, &req,
payload, payload,
|state, auth, req, _| { |state, auth: auth::AuthenticationData, req, _| {
payments::apply_filters_on_payments(state, auth.merchant_account, req) payments::apply_filters_on_payments(state, auth.merchant_account, req)
}, },
auth::auth_type( &auth::JWTAuth(Permission::PaymentRead),
&auth::ApiKeyAuth,
&auth::JWTAuth(Permission::PaymentRead),
req.headers(),
),
api_locking::LockAction::NotApplicable, api_locking::LockAction::NotApplicable,
) )
.await .await
@ -948,12 +944,10 @@ pub async fn get_filters_for_payments(
state, state,
&req, &req,
payload, payload,
|state, auth, req, _| payments::get_filters_for_payments(state, auth.merchant_account, req), |state, auth: auth::AuthenticationData, req, _| {
auth::auth_type( payments::get_filters_for_payments(state, auth.merchant_account, req)
&auth::ApiKeyAuth, },
&auth::JWTAuth(Permission::PaymentRead), &auth::JWTAuth(Permission::PaymentRead),
req.headers(),
),
api_locking::LockAction::NotApplicable, api_locking::LockAction::NotApplicable,
) )
.await .await
@ -971,12 +965,10 @@ pub async fn get_payment_filters(
state, state,
&req, &req,
(), (),
|state, auth, _, _| payments::get_payment_filters(state, auth.merchant_account), |state, auth: auth::AuthenticationData, _, _| {
auth::auth_type( payments::get_payment_filters(state, auth.merchant_account)
&auth::ApiKeyAuth, },
&auth::JWTAuth(Permission::PaymentRead), &auth::JWTAuth(Permission::PaymentRead),
req.headers(),
),
api_locking::LockAction::NotApplicable, api_locking::LockAction::NotApplicable,
) )
.await .await

View File

@ -42,6 +42,7 @@ impl PaymentAttemptInterface for MockDb {
_payment_method: Option<Vec<PaymentMethod>>, _payment_method: Option<Vec<PaymentMethod>>,
_payment_method_type: Option<Vec<PaymentMethodType>>, _payment_method_type: Option<Vec<PaymentMethodType>>,
_authentication_type: Option<Vec<AuthenticationType>>, _authentication_type: Option<Vec<AuthenticationType>>,
_merchanat_connector_id: Option<Vec<String>>,
_storage_scheme: storage_enums::MerchantStorageScheme, _storage_scheme: storage_enums::MerchantStorageScheme,
) -> CustomResult<i64, StorageError> { ) -> CustomResult<i64, StorageError> {
Err(StorageError::MockDbError)? Err(StorageError::MockDbError)?

View File

@ -292,6 +292,7 @@ impl<T: DatabaseStore> PaymentAttemptInterface for RouterStore<T> {
payment_method: Option<Vec<PaymentMethod>>, payment_method: Option<Vec<PaymentMethod>>,
payment_method_type: Option<Vec<PaymentMethodType>>, payment_method_type: Option<Vec<PaymentMethodType>>,
authentication_type: Option<Vec<AuthenticationType>>, authentication_type: Option<Vec<AuthenticationType>>,
merchant_connector_id: Option<Vec<String>>,
_storage_scheme: MerchantStorageScheme, _storage_scheme: MerchantStorageScheme,
) -> CustomResult<i64, errors::StorageError> { ) -> CustomResult<i64, errors::StorageError> {
let conn = self let conn = self
@ -314,6 +315,7 @@ impl<T: DatabaseStore> PaymentAttemptInterface for RouterStore<T> {
payment_method, payment_method,
payment_method_type, payment_method_type,
authentication_type, authentication_type,
merchant_connector_id,
) )
.await .await
.map_err(|er| { .map_err(|er| {
@ -1021,6 +1023,7 @@ impl<T: DatabaseStore> PaymentAttemptInterface for KVRouterStore<T> {
payment_method: Option<Vec<PaymentMethod>>, payment_method: Option<Vec<PaymentMethod>>,
payment_method_type: Option<Vec<PaymentMethodType>>, payment_method_type: Option<Vec<PaymentMethodType>>,
authentication_type: Option<Vec<AuthenticationType>>, authentication_type: Option<Vec<AuthenticationType>>,
merchant_connector_id: Option<Vec<String>>,
storage_scheme: MerchantStorageScheme, storage_scheme: MerchantStorageScheme,
) -> CustomResult<i64, errors::StorageError> { ) -> CustomResult<i64, errors::StorageError> {
self.router_store self.router_store
@ -1031,6 +1034,7 @@ impl<T: DatabaseStore> PaymentAttemptInterface for KVRouterStore<T> {
payment_method, payment_method,
payment_method_type, payment_method_type,
authentication_type, authentication_type,
merchant_connector_id,
storage_scheme, storage_scheme,
) )
.await .await

View File

@ -1,5 +1,9 @@
#[cfg(feature = "olap")] #[cfg(feature = "olap")]
use api_models::payments::AmountFilter;
#[cfg(feature = "olap")]
use async_bb8_diesel::{AsyncConnection, AsyncRunQueryDsl}; use async_bb8_diesel::{AsyncConnection, AsyncRunQueryDsl};
#[cfg(feature = "olap")]
use common_utils::errors::ReportSwitchExt;
use common_utils::{date_time, ext_traits::Encode}; use common_utils::{date_time, ext_traits::Encode};
#[cfg(feature = "olap")] #[cfg(feature = "olap")]
use data_models::payments::payment_intent::PaymentIntentFetchConstraints; use data_models::payments::payment_intent::PaymentIntentFetchConstraints;
@ -549,8 +553,6 @@ impl<T: DatabaseStore> PaymentIntentInterface for crate::RouterStore<T> {
constraints: &PaymentIntentFetchConstraints, constraints: &PaymentIntentFetchConstraints,
storage_scheme: MerchantStorageScheme, storage_scheme: MerchantStorageScheme,
) -> error_stack::Result<Vec<(PaymentIntent, PaymentAttempt)>, StorageError> { ) -> error_stack::Result<Vec<(PaymentIntent, PaymentAttempt)>, StorageError> {
use common_utils::errors::ReportSwitchExt;
let conn = connection::pg_connection_read(self).await.switch()?; let conn = connection::pg_connection_read(self).await.switch()?;
let conn = async_bb8_diesel::Connection::as_async_conn(&conn); let conn = async_bb8_diesel::Connection::as_async_conn(&conn);
let mut query = DieselPaymentIntent::table() let mut query = DieselPaymentIntent::table()
@ -615,9 +617,26 @@ impl<T: DatabaseStore> PaymentIntentInterface for crate::RouterStore<T> {
query = query.offset(params.offset.into()); query = query.offset(params.offset.into());
if let Some(currency) = &params.currency { query = match params.amount_filter {
query = query.filter(pi_dsl::currency.eq_any(currency.clone())); Some(AmountFilter {
} start_amount: Some(start),
end_amount: Some(end),
}) => query.filter(pi_dsl::amount.between(start, end)),
Some(AmountFilter {
start_amount: Some(start),
end_amount: None,
}) => query.filter(pi_dsl::amount.ge(start)),
Some(AmountFilter {
start_amount: None,
end_amount: Some(end),
}) => query.filter(pi_dsl::amount.le(end)),
_ => query,
};
query = match &params.currency {
Some(currency) => query.filter(pi_dsl::currency.eq_any(currency.clone())),
None => query,
};
let connectors = params let connectors = params
.connector .connector
@ -653,6 +672,13 @@ impl<T: DatabaseStore> PaymentIntentInterface for crate::RouterStore<T> {
None => query, None => query,
}; };
query = match &params.merchant_connector_id {
Some(merchant_connector_id) => query.filter(
pa_dsl::merchant_connector_id.eq_any(merchant_connector_id.clone()),
),
None => query,
};
query query
} }
}; };
@ -690,8 +716,6 @@ impl<T: DatabaseStore> PaymentIntentInterface for crate::RouterStore<T> {
constraints: &PaymentIntentFetchConstraints, constraints: &PaymentIntentFetchConstraints,
_storage_scheme: MerchantStorageScheme, _storage_scheme: MerchantStorageScheme,
) -> error_stack::Result<Vec<String>, StorageError> { ) -> error_stack::Result<Vec<String>, StorageError> {
use common_utils::errors::ReportSwitchExt;
let conn = connection::pg_connection_read(self).await.switch()?; let conn = connection::pg_connection_read(self).await.switch()?;
let conn = async_bb8_diesel::Connection::as_async_conn(&conn); let conn = async_bb8_diesel::Connection::as_async_conn(&conn);
let mut query = DieselPaymentIntent::table() let mut query = DieselPaymentIntent::table()
@ -722,6 +746,22 @@ impl<T: DatabaseStore> PaymentIntentInterface for crate::RouterStore<T> {
None => query, None => query,
}; };
query = match params.amount_filter {
Some(AmountFilter {
start_amount: Some(start),
end_amount: Some(end),
}) => query.filter(pi_dsl::amount.between(start, end)),
Some(AmountFilter {
start_amount: Some(start),
end_amount: None,
}) => query.filter(pi_dsl::amount.ge(start)),
Some(AmountFilter {
start_amount: None,
end_amount: Some(end),
}) => query.filter(pi_dsl::amount.le(end)),
_ => query,
};
query = match &params.currency { query = match &params.currency {
Some(currency) => query.filter(pi_dsl::currency.eq_any(currency.clone())), Some(currency) => query.filter(pi_dsl::currency.eq_any(currency.clone())),
None => query, None => query,