mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(router): get filters for payments (#1600)
This commit is contained in:
@ -1265,6 +1265,38 @@ pub async fn list_payments(
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
pub async fn get_filters_for_payments(
|
||||
db: &dyn StorageInterface,
|
||||
merchant: domain::MerchantAccount,
|
||||
time_range: api::TimeRange,
|
||||
) -> RouterResponse<api::PaymentListFilters> {
|
||||
use crate::types::transformers::ForeignFrom;
|
||||
|
||||
let pi = db
|
||||
.filter_payment_intents_by_time_range_constraints(
|
||||
&merchant.merchant_id,
|
||||
&time_range,
|
||||
merchant.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
|
||||
|
||||
let filters = db
|
||||
.get_filters_for_payments(
|
||||
&pi,
|
||||
&merchant.merchant_id,
|
||||
// since OLAP doesn't have KV. Force to get the data from PSQL.
|
||||
storage_enums::MerchantStorageScheme::PostgresOnly,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
|
||||
|
||||
let filters: api::PaymentListFilters = ForeignFrom::foreign_from(filters);
|
||||
|
||||
Ok(services::ApplicationResponse::Json(filters))
|
||||
}
|
||||
|
||||
pub async fn add_process_sync_task(
|
||||
db: &dyn StorageInterface,
|
||||
payment_attempt: &storage::PaymentAttempt,
|
||||
|
||||
@ -4,7 +4,7 @@ use api_models::payments::OrderDetailsWithAmount;
|
||||
use common_utils::fp_utils;
|
||||
use error_stack::ResultExt;
|
||||
use router_env::{instrument, tracing};
|
||||
use storage_models::ephemeral_key;
|
||||
use storage_models::{ephemeral_key, payment_attempt::PaymentListFilters};
|
||||
|
||||
use super::{flows::Feature, PaymentAddress, PaymentData};
|
||||
use crate::{
|
||||
@ -599,6 +599,29 @@ impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::Pay
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<PaymentListFilters> for api_models::payments::PaymentListFilters {
|
||||
fn foreign_from(item: PaymentListFilters) -> Self {
|
||||
Self {
|
||||
connector: item.connector,
|
||||
currency: item
|
||||
.currency
|
||||
.into_iter()
|
||||
.map(ForeignInto::foreign_into)
|
||||
.collect(),
|
||||
status: item
|
||||
.status
|
||||
.into_iter()
|
||||
.map(ForeignInto::foreign_into)
|
||||
.collect(),
|
||||
payment_method: item
|
||||
.payment_method
|
||||
.into_iter()
|
||||
.map(ForeignInto::foreign_into)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<ephemeral_key::EphemeralKey> for api::ephemeral_key::EphemeralKeyCreateResponse {
|
||||
fn foreign_from(from: ephemeral_key::EphemeralKey) -> Self {
|
||||
Self {
|
||||
|
||||
@ -62,6 +62,13 @@ pub trait PaymentAttemptInterface {
|
||||
merchant_id: &str,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<types::PaymentAttempt, errors::StorageError>;
|
||||
|
||||
async fn get_filters_for_payments(
|
||||
&self,
|
||||
pi: &[storage_models::payment_intent::PaymentIntent],
|
||||
merchant_id: &str,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<storage_models::payment_attempt::PaymentListFilters, errors::StorageError>;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "kv_store"))]
|
||||
@ -177,6 +184,20 @@ mod storage {
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn get_filters_for_payments(
|
||||
&self,
|
||||
pi: &[storage_models::payment_intent::PaymentIntent],
|
||||
merchant_id: &str,
|
||||
_storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<storage_models::payment_attempt::PaymentListFilters, errors::StorageError>
|
||||
{
|
||||
let conn = connection::pg_connection_read(self).await?;
|
||||
PaymentAttempt::get_filters_for_payments(&conn, pi, merchant_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn find_payment_attempt_by_preprocessing_id_merchant_id(
|
||||
&self,
|
||||
preprocessing_id: &str,
|
||||
@ -224,6 +245,16 @@ impl PaymentAttemptInterface for MockDb {
|
||||
Err(errors::StorageError::MockDbError)?
|
||||
}
|
||||
|
||||
async fn get_filters_for_payments(
|
||||
&self,
|
||||
_pi: &[storage_models::payment_intent::PaymentIntent],
|
||||
_merchant_id: &str,
|
||||
_storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<storage_models::payment_attempt::PaymentListFilters, errors::StorageError>
|
||||
{
|
||||
Err(errors::StorageError::MockDbError)?
|
||||
}
|
||||
|
||||
async fn find_payment_attempt_by_attempt_id_merchant_id(
|
||||
&self,
|
||||
_attempt_id: &str,
|
||||
@ -798,6 +829,20 @@ mod storage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_filters_for_payments(
|
||||
&self,
|
||||
pi: &[storage_models::payment_intent::PaymentIntent],
|
||||
merchant_id: &str,
|
||||
_storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<storage_models::payment_attempt::PaymentListFilters, errors::StorageError>
|
||||
{
|
||||
let conn = connection::pg_connection_read(self).await?;
|
||||
PaymentAttempt::get_filters_for_payments(&conn, pi, merchant_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@ -35,6 +35,14 @@ pub trait PaymentIntentInterface {
|
||||
pc: &api::PaymentListConstraints,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<Vec<types::PaymentIntent>, errors::StorageError>;
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
async fn filter_payment_intents_by_time_range_constraints(
|
||||
&self,
|
||||
merchant_id: &str,
|
||||
time_range: &api::TimeRange,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<Vec<types::PaymentIntent>, errors::StorageError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "kv_store")]
|
||||
@ -237,6 +245,25 @@ mod storage {
|
||||
.into_report()
|
||||
}
|
||||
|
||||
enums::MerchantStorageScheme::RedisKv => Err(errors::StorageError::KVError.into()),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "olap")]
|
||||
async fn filter_payment_intents_by_time_range_constraints(
|
||||
&self,
|
||||
merchant_id: &str,
|
||||
time_range: &api::TimeRange,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<Vec<PaymentIntent>, errors::StorageError> {
|
||||
match storage_scheme {
|
||||
enums::MerchantStorageScheme::PostgresOnly => {
|
||||
let conn = connection::pg_connection_read(self).await?;
|
||||
PaymentIntent::filter_by_time_constraints(&conn, merchant_id, time_range)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
enums::MerchantStorageScheme::RedisKv => Err(errors::StorageError::KVError.into()),
|
||||
}
|
||||
}
|
||||
@ -307,6 +334,19 @@ mod storage {
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
#[cfg(feature = "olap")]
|
||||
async fn filter_payment_intents_by_time_range_constraints(
|
||||
&self,
|
||||
merchant_id: &str,
|
||||
time_range: &api::TimeRange,
|
||||
_storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<Vec<PaymentIntent>, errors::StorageError> {
|
||||
let conn = connection::pg_connection_read(self).await?;
|
||||
PaymentIntent::filter_by_time_constraints(&conn, merchant_id, time_range)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,6 +362,16 @@ impl PaymentIntentInterface for MockDb {
|
||||
// [#172]: Implement function for `MockDb`
|
||||
Err(errors::StorageError::MockDbError)?
|
||||
}
|
||||
#[cfg(feature = "olap")]
|
||||
async fn filter_payment_intents_by_time_range_constraints(
|
||||
&self,
|
||||
_merchant_id: &str,
|
||||
_time_range: &api::TimeRange,
|
||||
_storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<Vec<types::PaymentIntent>, errors::StorageError> {
|
||||
// [#172]: Implement function for `MockDb`
|
||||
Err(errors::StorageError::MockDbError)?
|
||||
}
|
||||
|
||||
#[allow(clippy::panic)]
|
||||
async fn insert_payment_intent(
|
||||
|
||||
@ -147,7 +147,9 @@ impl Payments {
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
{
|
||||
route = route.service(web::resource("/list").route(web::get().to(payments_list)));
|
||||
route = route
|
||||
.service(web::resource("/list").route(web::get().to(payments_list)))
|
||||
.service(web::resource("/filter").route(web::post().to(get_filters_for_payments)))
|
||||
}
|
||||
#[cfg(feature = "oltp")]
|
||||
{
|
||||
|
||||
@ -692,7 +692,6 @@ pub async fn payments_cancel(
|
||||
)]
|
||||
#[instrument(skip_all, fields(flow = ?Flow::PaymentsList))]
|
||||
#[cfg(feature = "olap")]
|
||||
// #[get("/list")]
|
||||
pub async fn payments_list(
|
||||
state: web::Data<app::AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
@ -711,6 +710,28 @@ pub async fn payments_list(
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(flow = ?Flow::PaymentsList))]
|
||||
#[cfg(feature = "olap")]
|
||||
pub async fn get_filters_for_payments(
|
||||
state: web::Data<app::AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
payload: web::Json<payment_types::TimeRange>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::PaymentsList;
|
||||
let payload = payload.into_inner();
|
||||
api::server_wrap(
|
||||
flow,
|
||||
state.get_ref(),
|
||||
&req,
|
||||
payload,
|
||||
|state, auth, req| {
|
||||
payments::get_filters_for_payments(&*state.store, auth.merchant_account, req)
|
||||
},
|
||||
&auth::ApiKeyAuth,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn authorize_verify_select<Op>(
|
||||
operation: Op,
|
||||
state: &app::AppState,
|
||||
|
||||
@ -2,12 +2,13 @@ pub use api_models::payments::{
|
||||
AcceptanceType, Address, AddressDetails, Amount, AuthenticationForStartResponse, Card,
|
||||
CryptoData, CustomerAcceptance, MandateData, MandateTransactionType, MandateType,
|
||||
MandateValidationFields, NextActionType, OnlineMandate, PayLaterData, PaymentIdType,
|
||||
PaymentListConstraints, PaymentListResponse, PaymentMethodData, PaymentMethodDataResponse,
|
||||
PaymentOp, PaymentRetrieveBody, PaymentRetrieveBodyWithCredentials, PaymentsCancelRequest,
|
||||
PaymentsCaptureRequest, PaymentsRedirectRequest, PaymentsRedirectionResponse, PaymentsRequest,
|
||||
PaymentsResponse, PaymentsResponseForm, PaymentsRetrieveRequest, PaymentsSessionRequest,
|
||||
PaymentsSessionResponse, PaymentsStartRequest, PgRedirectResponse, PhoneDetails,
|
||||
RedirectionResponse, SessionToken, UrlDetails, VerifyRequest, VerifyResponse, WalletData,
|
||||
PaymentListConstraints, PaymentListFilters, PaymentListResponse, PaymentMethodData,
|
||||
PaymentMethodDataResponse, PaymentOp, PaymentRetrieveBody, PaymentRetrieveBodyWithCredentials,
|
||||
PaymentsCancelRequest, PaymentsCaptureRequest, PaymentsRedirectRequest,
|
||||
PaymentsRedirectionResponse, PaymentsRequest, PaymentsResponse, PaymentsResponseForm,
|
||||
PaymentsRetrieveRequest, PaymentsSessionRequest, PaymentsSessionResponse, PaymentsStartRequest,
|
||||
PgRedirectResponse, PhoneDetails, RedirectionResponse, SessionToken, TimeRange, UrlDetails,
|
||||
VerifyRequest, VerifyResponse, WalletData,
|
||||
};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::PeekInterface;
|
||||
|
||||
@ -22,6 +22,12 @@ pub trait PaymentIntentDbExt: Sized {
|
||||
merchant_id: &str,
|
||||
pc: &api::PaymentListConstraints,
|
||||
) -> CustomResult<Vec<Self>, errors::DatabaseError>;
|
||||
|
||||
async fn filter_by_time_constraints(
|
||||
conn: &PgPooledConn,
|
||||
merchant_id: &str,
|
||||
pc: &api::TimeRange,
|
||||
) -> CustomResult<Vec<Self>, errors::DatabaseError>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@ -85,4 +91,34 @@ impl PaymentIntentDbExt for PaymentIntent {
|
||||
.change_context(errors::DatabaseError::NotFound)
|
||||
.attach_printable_lazy(|| "Error filtering records by predicate")
|
||||
}
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
async fn filter_by_time_constraints(
|
||||
conn: &PgPooledConn,
|
||||
merchant_id: &str,
|
||||
time_range: &api::TimeRange,
|
||||
) -> CustomResult<Vec<Self>, errors::DatabaseError> {
|
||||
let start_time = time_range.start_time;
|
||||
let end_time = time_range
|
||||
.end_time
|
||||
.unwrap_or_else(common_utils::date_time::now);
|
||||
|
||||
//[#350]: Replace this with Boxable Expression and pass it into generic filter
|
||||
// when https://github.com/rust-lang/rust/issues/52662 becomes stable
|
||||
let mut filter = <Self as HasTable>::table()
|
||||
.filter(dsl::merchant_id.eq(merchant_id.to_owned()))
|
||||
.order(dsl::modified_at.desc())
|
||||
.into_boxed();
|
||||
|
||||
filter = filter.filter(dsl::created_at.ge(start_time));
|
||||
|
||||
filter = filter.filter(dsl::created_at.le(end_time));
|
||||
|
||||
filter
|
||||
.get_results_async(conn)
|
||||
.await
|
||||
.into_report()
|
||||
.change_context(errors::DatabaseError::Others)
|
||||
.attach_printable("Error filtering records by time range")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user