diff --git a/crates/api_models/src/disputes.rs b/crates/api_models/src/disputes.rs index 7629eeee876..1b04e792278 100644 --- a/crates/api_models/src/disputes.rs +++ b/crates/api_models/src/disputes.rs @@ -104,6 +104,8 @@ pub struct DisputeEvidenceBlock { pub struct DisputeListConstraints { /// limit on the number of objects to return pub limit: Option, + /// The identifier for business profile + pub profile_id: Option, /// status of the dispute pub dispute_status: Option, /// stage of the dispute diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 31fa00cef9a..5f99e050f67 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1999,10 +1999,13 @@ pub struct PaymentListResponseV2 { } #[derive(Clone, Debug, serde::Deserialize)] -#[serde(deny_unknown_fields)] pub struct PaymentListFilterConstraints { /// The identifier for payment pub payment_id: Option, + /// The identifier for business profile + pub profile_id: Option, + /// The identifier for customer + pub customer_id: Option, /// The limit on the number of objects. The default limit is 10 and max limit is 20 #[serde(default = "default_limit")] pub limit: u32, @@ -2015,10 +2018,14 @@ pub struct PaymentListFilterConstraints { pub connector: Option>, /// The list of currencies to filter payments list pub currency: Option>, - /// The list of payment statuses to filter payments list + /// The list of payment status to filter payments list pub status: Option>, /// The list of payment methods to filter payments list - pub payment_methods: Option>, + pub payment_method: Option>, + /// The list of payment method types to filter payments list + pub payment_method_type: Option>, + /// The list of authentication types to filter payments list + pub authentication_type: Option>, } #[derive(Clone, Debug, serde::Serialize)] pub struct PaymentListFilters { @@ -2030,6 +2037,10 @@ pub struct PaymentListFilters { pub status: Vec, /// The list of available payment method filters pub payment_method: Vec, + /// The list of available payment method types + pub payment_method_type: Vec, + /// The list of available authentication types + pub authentication_type: Vec, } #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] diff --git a/crates/api_models/src/refunds.rs b/crates/api_models/src/refunds.rs index 8914e00826a..489c5dd1c43 100644 --- a/crates/api_models/src/refunds.rs +++ b/crates/api_models/src/refunds.rs @@ -132,6 +132,8 @@ pub struct RefundListRequest { pub payment_id: Option, /// The identifier for the refund pub refund_id: Option, + /// The identifier for business profile + pub profile_id: Option, /// Limit on the number of objects to return pub limit: Option, /// The starting point within a list of objects diff --git a/crates/data_models/src/payments/payment_attempt.rs b/crates/data_models/src/payments/payment_attempt.rs index ad877ab01e8..a372a77f064 100644 --- a/crates/data_models/src/payments/payment_attempt.rs +++ b/crates/data_models/src/payments/payment_attempt.rs @@ -79,12 +79,15 @@ pub trait PaymentAttemptInterface { storage_scheme: MerchantStorageScheme, ) -> error_stack::Result; + #[allow(clippy::too_many_arguments)] async fn get_total_count_of_filtered_payment_attempts( &self, merchant_id: &str, active_attempt_ids: &[String], connector: Option>, - payment_methods: Option>, + payment_method: Option>, + payment_method_type: Option>, + authentication_type: Option>, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result; } @@ -147,6 +150,8 @@ pub struct PaymentListFilters { pub currency: Vec, pub status: Vec, pub payment_method: Vec, + pub payment_method_type: Vec, + pub authentication_type: Vec, } #[derive(Clone, Debug, Default, Serialize, Deserialize)] diff --git a/crates/data_models/src/payments/payment_intent.rs b/crates/data_models/src/payments/payment_intent.rs index 26c244782a5..b3bf8af8c36 100644 --- a/crates/data_models/src/payments/payment_intent.rs +++ b/crates/data_models/src/payments/payment_intent.rs @@ -391,57 +391,66 @@ impl From for PaymentIntentUpdateInternal { } pub enum PaymentIntentFetchConstraints { - Single { - payment_intent_id: String, - }, - List { - offset: u32, - starting_at: Option, - ending_at: Option, - connector: Option>, - currency: Option>, - status: Option>, - payment_methods: Option>, - customer_id: Option, - starting_after_id: Option, - ending_before_id: Option, - limit: Option, - }, + Single { payment_intent_id: String }, + List(Box), +} + +pub struct PaymentIntentListParams { + pub offset: u32, + pub starting_at: Option, + pub ending_at: Option, + pub connector: Option>, + pub currency: Option>, + pub status: Option>, + pub payment_method: Option>, + pub payment_method_type: Option>, + pub authentication_type: Option>, + pub profile_id: Option, + pub customer_id: Option, + pub starting_after_id: Option, + pub ending_before_id: Option, + pub limit: Option, } impl From for PaymentIntentFetchConstraints { fn from(value: api_models::payments::PaymentListConstraints) -> Self { - Self::List { + Self::List(Box::new(PaymentIntentListParams { offset: 0, starting_at: value.created_gte.or(value.created_gt).or(value.created), ending_at: value.created_lte.or(value.created_lt).or(value.created), connector: None, currency: None, status: None, - payment_methods: None, + payment_method: None, + payment_method_type: None, + authentication_type: None, + profile_id: None, customer_id: value.customer_id, starting_after_id: value.starting_after, ending_before_id: value.ending_before, limit: Some(std::cmp::min(value.limit, PAYMENTS_LIST_MAX_LIMIT_V1)), - } + })) } } impl From for PaymentIntentFetchConstraints { fn from(value: api_models::payments::TimeRange) -> Self { - Self::List { + Self::List(Box::new(PaymentIntentListParams { offset: 0, starting_at: Some(value.start_time), ending_at: value.end_time, connector: None, currency: None, status: None, - payment_methods: None, + payment_method: None, + payment_method_type: None, + authentication_type: None, + profile_id: None, customer_id: None, starting_after_id: None, ending_before_id: None, limit: None, - } + })) } } @@ -450,19 +459,22 @@ impl From for PaymentIntentF if let Some(payment_intent_id) = value.payment_id { Self::Single { payment_intent_id } } else { - Self::List { + Self::List(Box::new(PaymentIntentListParams { offset: value.offset.unwrap_or_default(), starting_at: value.time_range.map(|t| t.start_time), ending_at: value.time_range.and_then(|t| t.end_time), connector: value.connector, currency: value.currency, status: value.status, - payment_methods: value.payment_methods, - customer_id: None, + payment_method: value.payment_method, + payment_method_type: value.payment_method_type, + authentication_type: value.authentication_type, + profile_id: value.profile_id, + customer_id: value.customer_id, starting_after_id: None, ending_before_id: None, limit: Some(std::cmp::min(value.limit, PAYMENTS_LIST_MAX_LIMIT_V2)), - } + })) } } } diff --git a/crates/diesel_models/src/query/payment_attempt.rs b/crates/diesel_models/src/query/payment_attempt.rs index c22151d3803..8e2fc729dbf 100644 --- a/crates/diesel_models/src/query/payment_attempt.rs +++ b/crates/diesel_models/src/query/payment_attempt.rs @@ -216,6 +216,8 @@ impl PaymentAttempt { Vec, Vec, Vec, + Vec, + Vec, )> { let active_attempts: Vec = pi .iter() @@ -272,11 +274,39 @@ impl PaymentAttempt { .flatten() .collect::>(); + let filter_payment_method_type = filter + .clone() + .select(dsl::payment_method_type) + .distinct() + .get_results_async::>(conn) + .await + .into_report() + .change_context(DatabaseError::Others) + .attach_printable("Error filtering records by payment method type")? + .into_iter() + .flatten() + .collect::>(); + + let filter_authentication_type = filter + .clone() + .select(dsl::authentication_type) + .distinct() + .get_results_async::>(conn) + .await + .into_report() + .change_context(DatabaseError::Others) + .attach_printable("Error filtering records by authentication type")? + .into_iter() + .flatten() + .collect::>(); + Ok(( filter_connector, filter_currency, intent_status, filter_payment_method, + filter_payment_method_type, + filter_authentication_type, )) } pub async fn get_total_count_of_attempts( @@ -285,6 +315,8 @@ impl PaymentAttempt { active_attempt_ids: &[String], connector: Option>, payment_method: Option>, + payment_method_type: Option>, + authentication_type: Option>, ) -> StorageResult { let mut filter = ::table() .count() @@ -299,6 +331,12 @@ impl PaymentAttempt { if let Some(payment_method) = payment_method.clone() { filter = filter.filter(dsl::payment_method.eq_any(payment_method)); } + if let Some(payment_method_type) = payment_method_type.clone() { + filter = filter.filter(dsl::payment_method_type.eq_any(payment_method_type)); + } + if let Some(authentication_type) = authentication_type.clone() { + filter = filter.filter(dsl::authentication_type.eq_any(authentication_type)); + } router_env::logger::debug!(query = %debug_query::(&filter).to_string()); db_metrics::track_database_call::<::Table, _, _>( diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 708e20d2050..1b6d93596f1 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -1651,7 +1651,9 @@ pub async fn apply_filters_on_payments( &merchant.merchant_id, &active_attempt_ids, constraints.connector, - constraints.payment_methods, + constraints.payment_method, + constraints.payment_method_type, + constraints.authentication_type, merchant.storage_scheme, ) .await @@ -1698,6 +1700,8 @@ pub async fn get_filters_for_payments( currency: filters.currency, status: filters.status, payment_method: filters.payment_method, + payment_method_type: filters.payment_method_type, + authentication_type: filters.authentication_type, }, )) } diff --git a/crates/router/src/db/dispute.rs b/crates/router/src/db/dispute.rs index 71f47872c09..42fb85ad16d 100644 --- a/crates/router/src/db/dispute.rs +++ b/crates/router/src/db/dispute.rs @@ -562,6 +562,7 @@ mod tests { received_time_gt: None, received_time_lte: None, received_time_gte: None, + profile_id: None, }, ) .await diff --git a/crates/router/src/db/refund.rs b/crates/router/src/db/refund.rs index 93227d71846..ccbd6a5b39d 100644 --- a/crates/router/src/db/refund.rs +++ b/crates/router/src/db/refund.rs @@ -902,6 +902,7 @@ impl RefundInterface for MockDb { .clone() .map_or(true, |id| id == refund.refund_id) }) + .filter(|refund| refund_details.profile_id == refund.profile_id) .filter(|refund| { refund.created_at >= refund_details.time_range.map_or( @@ -1025,6 +1026,7 @@ impl RefundInterface for MockDb { .clone() .map_or(true, |id| id == refund.refund_id) }) + .filter(|refund| refund_details.profile_id == refund.profile_id) .filter(|refund| { refund.created_at >= refund_details.time_range.map_or( diff --git a/crates/router/src/types/storage/dispute.rs b/crates/router/src/types/storage/dispute.rs index 7b66df6b05f..84a1f748bee 100644 --- a/crates/router/src/types/storage/dispute.rs +++ b/crates/router/src/types/storage/dispute.rs @@ -28,6 +28,9 @@ impl DisputeDbExt for Dispute { .order(dsl::modified_at.desc()) .into_boxed(); + if let Some(profile_id) = dispute_list_constraints.profile_id { + filter = filter.filter(dsl::profile_id.eq(profile_id)); + } if let Some(received_time) = dispute_list_constraints.received_time { filter = filter.filter(dsl::created_at.eq(received_time)); } diff --git a/crates/router/src/types/storage/refund.rs b/crates/router/src/types/storage/refund.rs index 17d94c6b678..bdfa8dc5b5f 100644 --- a/crates/router/src/types/storage/refund.rs +++ b/crates/router/src/types/storage/refund.rs @@ -67,6 +67,17 @@ impl RefundDbExt for Refund { filter = filter.limit(limit).offset(offset); } }; + match &refund_list_details.profile_id { + Some(profile_id) => { + filter = filter + .filter(dsl::profile_id.eq(profile_id.to_owned())) + .limit(limit) + .offset(offset); + } + None => { + filter = filter.limit(limit).offset(offset); + } + }; if let Some(time_range) = refund_list_details.time_range { filter = filter.filter(dsl::created_at.ge(time_range.start_time)); @@ -175,6 +186,9 @@ impl RefundDbExt for Refund { if let Some(ref_id) = &refund_list_details.refund_id { filter = filter.filter(dsl::refund_id.eq(ref_id.to_owned())); } + if let Some(profile_id) = &refund_list_details.profile_id { + filter = filter.filter(dsl::profile_id.eq(profile_id.to_owned())); + } if let Some(time_range) = refund_list_details.time_range { filter = filter.filter(dsl::created_at.ge(time_range.start_time)); diff --git a/crates/storage_impl/src/mock_db/payment_attempt.rs b/crates/storage_impl/src/mock_db/payment_attempt.rs index cd1e2f29b4c..e74de71c852 100644 --- a/crates/storage_impl/src/mock_db/payment_attempt.rs +++ b/crates/storage_impl/src/mock_db/payment_attempt.rs @@ -1,4 +1,4 @@ -use api_models::enums::{Connector, PaymentMethod}; +use api_models::enums::{AuthenticationType, Connector, PaymentMethod, PaymentMethodType}; use common_utils::errors::CustomResult; use data_models::{ errors::StorageError, @@ -39,7 +39,9 @@ impl PaymentAttemptInterface for MockDb { _merchant_id: &str, _active_attempt_ids: &[String], _connector: Option>, - _payment_methods: Option>, + _payment_method: Option>, + _payment_method_type: Option>, + _authentication_type: Option>, _storage_scheme: MerchantStorageScheme, ) -> CustomResult { Err(StorageError::MockDbError)? diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs index 954da5d8c04..e4d3973918e 100644 --- a/crates/storage_impl/src/payments/payment_attempt.rs +++ b/crates/storage_impl/src/payments/payment_attempt.rs @@ -1,4 +1,4 @@ -use api_models::enums::{Connector, PaymentMethod}; +use api_models::enums::{AuthenticationType, Connector, PaymentMethod, PaymentMethodType}; use common_utils::errors::CustomResult; use data_models::{ errors, @@ -176,11 +176,20 @@ impl PaymentAttemptInterface for RouterStore { er.change_context(new_err) }) .map( - |(connector, currency, status, payment_method)| PaymentListFilters { + |( connector, currency, status, payment_method, + payment_method_type, + authentication_type, + )| PaymentListFilters { + connector, + currency, + status, + payment_method, + payment_method_type, + authentication_type, }, ) } @@ -248,7 +257,9 @@ impl PaymentAttemptInterface for RouterStore { merchant_id: &str, active_attempt_ids: &[String], connector: Option>, - payment_methods: Option>, + payment_method: Option>, + payment_method_type: Option>, + authentication_type: Option>, _storage_scheme: MerchantStorageScheme, ) -> CustomResult { let conn = self @@ -269,7 +280,9 @@ impl PaymentAttemptInterface for RouterStore { merchant_id, active_attempt_ids, connector_strings, - payment_methods, + payment_method, + payment_method_type, + authentication_type, ) .await .map_err(|er| { @@ -826,7 +839,9 @@ impl PaymentAttemptInterface for KVRouterStore { merchant_id: &str, active_attempt_ids: &[String], connector: Option>, - payment_methods: Option>, + payment_method: Option>, + payment_method_type: Option>, + authentication_type: Option>, storage_scheme: MerchantStorageScheme, ) -> CustomResult { self.router_store @@ -834,7 +849,9 @@ impl PaymentAttemptInterface for KVRouterStore { merchant_id, active_attempt_ids, connector, - payment_methods, + payment_method, + payment_method_type, + authentication_type, storage_scheme, ) .await diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index b365c6a3781..8352158be05 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -366,29 +366,20 @@ impl PaymentIntentInterface for crate::RouterStore { PaymentIntentFetchConstraints::Single { payment_intent_id } => { query = query.filter(pi_dsl::payment_id.eq(payment_intent_id.to_owned())); } - PaymentIntentFetchConstraints::List { - offset, - starting_at, - ending_at, - connector: _, - currency, - status, - payment_methods: _, - customer_id, - starting_after_id, - ending_before_id, - limit, - } => { - if let Some(limit) = limit { - query = query.limit((*limit).into()); - }; + PaymentIntentFetchConstraints::List(params) => { + if let Some(limit) = params.limit { + query = query.limit(limit.into()); + } - if let Some(customer_id) = customer_id { + if let Some(customer_id) = ¶ms.customer_id { query = query.filter(pi_dsl::customer_id.eq(customer_id.clone())); } + if let Some(profile_id) = ¶ms.profile_id { + query = query.filter(pi_dsl::profile_id.eq(profile_id.clone())); + } - query = match (starting_at, starting_after_id) { - (Some(starting_at), _) => query.filter(pi_dsl::created_at.ge(*starting_at)), + query = match (params.starting_at, ¶ms.starting_after_id) { + (Some(starting_at), _) => query.filter(pi_dsl::created_at.ge(starting_at)), (None, Some(starting_after_id)) => { // TODO: Fetch partial columns for this query since we only need some columns let starting_at = self @@ -404,8 +395,8 @@ impl PaymentIntentInterface for crate::RouterStore { (None, None) => query, }; - query = match (ending_at, ending_before_id) { - (Some(ending_at), _) => query.filter(pi_dsl::created_at.le(*ending_at)), + query = match (params.ending_at, ¶ms.ending_before_id) { + (Some(ending_at), _) => query.filter(pi_dsl::created_at.le(ending_at)), (None, Some(ending_before_id)) => { // TODO: Fetch partial columns for this query since we only need some columns let ending_at = self @@ -420,17 +411,26 @@ impl PaymentIntentInterface for crate::RouterStore { } (None, None) => query, }; - query = query.offset((*offset).into()); - query = match currency { + query = query.offset(params.offset.into()); + + query = match ¶ms.currency { Some(currency) => query.filter(pi_dsl::currency.eq_any(currency.clone())), None => query, }; - query = match status { + query = match ¶ms.status { Some(status) => query.filter(pi_dsl::status.eq_any(status.clone())), None => query, }; + + if let Some(currency) = ¶ms.currency { + query = query.filter(pi_dsl::currency.eq_any(currency.clone())); + } + + if let Some(status) = ¶ms.status { + query = query.filter(pi_dsl::status.eq_any(status.clone())); + } } } @@ -490,29 +490,21 @@ impl PaymentIntentInterface for crate::RouterStore { PaymentIntentFetchConstraints::Single { payment_intent_id } => { query.filter(pi_dsl::payment_id.eq(payment_intent_id.to_owned())) } - PaymentIntentFetchConstraints::List { - offset, - starting_at, - ending_at, - connector, - currency, - status, - payment_methods, - customer_id, - starting_after_id, - ending_before_id, - limit, - } => { - if let Some(limit) = limit { - query = query.limit((*limit).into()); + PaymentIntentFetchConstraints::List(params) => { + if let Some(limit) = params.limit { + query = query.limit(limit.into()); } - if let Some(customer_id) = customer_id { + if let Some(customer_id) = ¶ms.customer_id { query = query.filter(pi_dsl::customer_id.eq(customer_id.clone())); } - query = match (starting_at, starting_after_id) { - (Some(starting_at), _) => query.filter(pi_dsl::created_at.ge(*starting_at)), + if let Some(profile_id) = ¶ms.profile_id { + query = query.filter(pi_dsl::profile_id.eq(profile_id.clone())); + } + + query = match (params.starting_at, ¶ms.starting_after_id) { + (Some(starting_at), _) => query.filter(pi_dsl::created_at.ge(starting_at)), (None, Some(starting_after_id)) => { // TODO: Fetch partial columns for this query since we only need some columns let starting_at = self @@ -528,8 +520,8 @@ impl PaymentIntentInterface for crate::RouterStore { (None, None) => query, }; - query = match (ending_at, ending_before_id) { - (Some(ending_at), _) => query.filter(pi_dsl::created_at.le(*ending_at)), + query = match (params.ending_at, ¶ms.ending_before_id) { + (Some(ending_at), _) => query.filter(pi_dsl::created_at.le(ending_at)), (None, Some(ending_before_id)) => { // TODO: Fetch partial columns for this query since we only need some columns let ending_at = self @@ -545,14 +537,14 @@ impl PaymentIntentInterface for crate::RouterStore { (None, None) => query, }; - query = query.offset((*offset).into()); + query = query.offset(params.offset.into()); - query = match currency { - Some(currency) => query.filter(pi_dsl::currency.eq_any(currency.clone())), - None => query, - }; + if let Some(currency) = ¶ms.currency { + query = query.filter(pi_dsl::currency.eq_any(currency.clone())); + } - let connectors = connector + let connectors = params + .connector .as_ref() .map(|c| c.iter().map(|c| c.to_string()).collect::>()); @@ -561,18 +553,30 @@ impl PaymentIntentInterface for crate::RouterStore { None => query, }; - query = match status { + query = match ¶ms.status { Some(status) => query.filter(pi_dsl::status.eq_any(status.clone())), None => query, }; - query = match payment_methods { - Some(payment_methods) => { - query.filter(pa_dsl::payment_method.eq_any(payment_methods.clone())) + query = match ¶ms.payment_method { + Some(payment_method) => { + query.filter(pa_dsl::payment_method.eq_any(payment_method.clone())) } None => query, }; + query = match ¶ms.payment_method_type { + Some(payment_method_type) => query + .filter(pa_dsl::payment_method_type.eq_any(payment_method_type.clone())), + None => query, + }; + + query = match ¶ms.authentication_type { + Some(authentication_type) => query + .filter(pa_dsl::authentication_type.eq_any(authentication_type.clone())), + None => query, + }; + query } }; @@ -620,34 +624,30 @@ impl PaymentIntentInterface for crate::RouterStore { PaymentIntentFetchConstraints::Single { payment_intent_id } => { query.filter(pi_dsl::payment_id.eq(payment_intent_id.to_owned())) } - PaymentIntentFetchConstraints::List { - starting_at, - ending_at, - currency, - status, - customer_id, - .. - } => { - if let Some(customer_id) = customer_id { + PaymentIntentFetchConstraints::List(params) => { + if let Some(customer_id) = ¶ms.customer_id { query = query.filter(pi_dsl::customer_id.eq(customer_id.clone())); } + if let Some(profile_id) = ¶ms.profile_id { + query = query.filter(pi_dsl::profile_id.eq(profile_id.clone())); + } - query = match starting_at { - Some(starting_at) => query.filter(pi_dsl::created_at.ge(*starting_at)), + query = match params.starting_at { + Some(starting_at) => query.filter(pi_dsl::created_at.ge(starting_at)), None => query, }; - query = match ending_at { - Some(ending_at) => query.filter(pi_dsl::created_at.le(*ending_at)), + query = match params.ending_at { + Some(ending_at) => query.filter(pi_dsl::created_at.le(ending_at)), None => query, }; - query = match currency { + query = match ¶ms.currency { Some(currency) => query.filter(pi_dsl::currency.eq_any(currency.clone())), None => query, }; - query = match status { + query = match ¶ms.status { Some(status) => query.filter(pi_dsl::status.eq_any(status.clone())), None => query, }; diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index efa7ebb32cf..7f1647d1132 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -10184,6 +10184,11 @@ "description": "The identifier for the refund", "nullable": true }, + "profile_id": { + "type": "string", + "description": "The identifier for business profile", + "nullable": true + }, "limit": { "type": "integer", "format": "int64",