mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(router): Add Payments - List endpoint for v2 (#7191)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -5252,6 +5252,82 @@ pub async fn list_payments(
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "olap"))]
|
||||
pub async fn list_payments(
|
||||
state: SessionState,
|
||||
merchant: domain::MerchantAccount,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
constraints: api::PaymentListConstraints,
|
||||
) -> RouterResponse<payments_api::PaymentListResponse> {
|
||||
common_utils::metrics::utils::record_operation_time(
|
||||
async {
|
||||
let limit = &constraints.limit;
|
||||
helpers::validate_payment_list_request_for_joins(*limit)?;
|
||||
let db: &dyn StorageInterface = state.store.as_ref();
|
||||
let fetch_constraints = constraints.clone().into();
|
||||
let list: Vec<(storage::PaymentIntent, Option<storage::PaymentAttempt>)> = db
|
||||
.get_filtered_payment_intents_attempt(
|
||||
&(&state).into(),
|
||||
merchant.get_id(),
|
||||
&fetch_constraints,
|
||||
&key_store,
|
||||
merchant.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
|
||||
let data: Vec<api_models::payments::PaymentsListResponseItem> =
|
||||
list.into_iter().map(ForeignFrom::foreign_from).collect();
|
||||
|
||||
let active_attempt_ids = db
|
||||
.get_filtered_active_attempt_ids_for_total_count(
|
||||
merchant.get_id(),
|
||||
&fetch_constraints,
|
||||
merchant.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while retrieving active_attempt_ids for merchant")?;
|
||||
|
||||
let total_count = if constraints.has_no_attempt_filters() {
|
||||
i64::try_from(active_attempt_ids.len())
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while converting from usize to i64")
|
||||
} else {
|
||||
let active_attempt_ids = active_attempt_ids
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
db.get_total_count_of_filtered_payment_attempts(
|
||||
merchant.get_id(),
|
||||
&active_attempt_ids,
|
||||
constraints.connector,
|
||||
constraints.payment_method_type,
|
||||
constraints.payment_method_subtype,
|
||||
constraints.authentication_type,
|
||||
constraints.merchant_connector_id,
|
||||
constraints.card_network,
|
||||
merchant.storage_scheme,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while retrieving total count of payment attempts")
|
||||
}?;
|
||||
|
||||
Ok(services::ApplicationResponse::Json(
|
||||
api_models::payments::PaymentListResponse {
|
||||
count: data.len(),
|
||||
total_count,
|
||||
data,
|
||||
},
|
||||
))
|
||||
},
|
||||
&metrics::PAYMENT_LIST_LATENCY,
|
||||
router_env::metric_attributes!(("merchant_id", merchant.get_id().clone())),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "v1"))]
|
||||
pub async fn apply_filters_on_payments(
|
||||
state: SessionState,
|
||||
|
||||
@ -2774,6 +2774,54 @@ impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::Pay
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2")]
|
||||
impl ForeignFrom<(storage::PaymentIntent, Option<storage::PaymentAttempt>)>
|
||||
for api_models::payments::PaymentsListResponseItem
|
||||
{
|
||||
fn foreign_from((pi, pa): (storage::PaymentIntent, Option<storage::PaymentAttempt>)) -> Self {
|
||||
Self {
|
||||
id: pi.id,
|
||||
merchant_id: pi.merchant_id,
|
||||
profile_id: pi.profile_id,
|
||||
customer_id: pi.customer_id,
|
||||
payment_method_id: pa.as_ref().and_then(|p| p.payment_method_id.clone()),
|
||||
status: pi.status,
|
||||
amount: api_models::payments::PaymentAmountDetailsResponse::foreign_from((
|
||||
&pi.amount_details,
|
||||
pa.as_ref().map(|p| &p.amount_details),
|
||||
)),
|
||||
created: pi.created_at,
|
||||
payment_method_type: pa.as_ref().and_then(|p| p.payment_method_type.into()),
|
||||
payment_method_subtype: pa.as_ref().and_then(|p| p.payment_method_subtype.into()),
|
||||
connector: pa.as_ref().and_then(|p| p.connector.clone()),
|
||||
merchant_connector_id: pa.as_ref().and_then(|p| p.merchant_connector_id.clone()),
|
||||
customer: None,
|
||||
merchant_reference_id: pi.merchant_reference_id,
|
||||
connector_payment_id: pa.as_ref().and_then(|p| p.connector_payment_id.clone()),
|
||||
connector_response_reference_id: pa
|
||||
.as_ref()
|
||||
.and_then(|p| p.connector_response_reference_id.clone()),
|
||||
metadata: pi.metadata,
|
||||
description: pi.description.map(|val| val.get_string_repr().to_string()),
|
||||
authentication_type: pi.authentication_type,
|
||||
capture_method: Some(pi.capture_method),
|
||||
setup_future_usage: Some(pi.setup_future_usage),
|
||||
attempt_count: pi.attempt_count,
|
||||
error: pa
|
||||
.as_ref()
|
||||
.and_then(|p| p.error.as_ref())
|
||||
.map(|e| api_models::payments::ErrorDetails::foreign_from(e.clone())),
|
||||
cancellation_reason: pa.as_ref().and_then(|p| p.cancellation_reason.clone()),
|
||||
order_details: None,
|
||||
return_url: pi.return_url,
|
||||
statement_descriptor: pi.statement_descriptor,
|
||||
allowed_payment_method_types: pi.allowed_payment_method_types,
|
||||
authorization_count: pi.authorization_count,
|
||||
modified_at: pa.as_ref().map(|p| p.modified_at),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl ForeignFrom<ephemeral_key::EphemeralKey> for api::ephemeral_key::EphemeralKeyCreateResponse {
|
||||
fn foreign_from(from: ephemeral_key::EphemeralKey) -> Self {
|
||||
|
||||
@ -1735,6 +1735,34 @@ impl PaymentAttemptInterface for KafkaStore {
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2")]
|
||||
async fn get_total_count_of_filtered_payment_attempts(
|
||||
&self,
|
||||
merchant_id: &id_type::MerchantId,
|
||||
active_attempt_ids: &[String],
|
||||
connector: Option<api_models::enums::Connector>,
|
||||
payment_method_type: Option<common_enums::PaymentMethod>,
|
||||
payment_method_subtype: Option<common_enums::PaymentMethodType>,
|
||||
authentication_type: Option<common_enums::AuthenticationType>,
|
||||
merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
|
||||
card_network: Option<common_enums::CardNetwork>,
|
||||
storage_scheme: MerchantStorageScheme,
|
||||
) -> CustomResult<i64, errors::DataStorageError> {
|
||||
self.diesel_store
|
||||
.get_total_count_of_filtered_payment_attempts(
|
||||
merchant_id,
|
||||
active_attempt_ids,
|
||||
connector,
|
||||
payment_method_type,
|
||||
payment_method_subtype,
|
||||
authentication_type,
|
||||
merchant_connector_id,
|
||||
card_network,
|
||||
storage_scheme,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
async fn find_attempts_by_merchant_id_payment_id(
|
||||
&self,
|
||||
@ -1914,6 +1942,32 @@ impl PaymentIntentInterface for KafkaStore {
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "v2"))]
|
||||
async fn get_filtered_payment_intents_attempt(
|
||||
&self,
|
||||
state: &KeyManagerState,
|
||||
merchant_id: &id_type::MerchantId,
|
||||
constraints: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
storage_scheme: MerchantStorageScheme,
|
||||
) -> CustomResult<
|
||||
Vec<(
|
||||
hyperswitch_domain_models::payments::PaymentIntent,
|
||||
Option<hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt>,
|
||||
)>,
|
||||
errors::DataStorageError,
|
||||
> {
|
||||
self.diesel_store
|
||||
.get_filtered_payment_intents_attempt(
|
||||
state,
|
||||
merchant_id,
|
||||
constraints,
|
||||
key_store,
|
||||
storage_scheme,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "v1"))]
|
||||
async fn get_filtered_active_attempt_ids_for_total_count(
|
||||
&self,
|
||||
@ -1952,6 +2006,21 @@ impl PaymentIntentInterface for KafkaStore {
|
||||
)
|
||||
.await
|
||||
}
|
||||
#[cfg(all(feature = "olap", feature = "v2"))]
|
||||
async fn get_filtered_active_attempt_ids_for_total_count(
|
||||
&self,
|
||||
merchant_id: &id_type::MerchantId,
|
||||
constraints: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints,
|
||||
storage_scheme: MerchantStorageScheme,
|
||||
) -> CustomResult<Vec<Option<String>>, errors::DataStorageError> {
|
||||
self.diesel_store
|
||||
.get_filtered_active_attempt_ids_for_total_count(
|
||||
merchant_id,
|
||||
constraints,
|
||||
storage_scheme,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
||||
@ -573,6 +573,7 @@ impl Payments {
|
||||
web::resource("/create-intent")
|
||||
.route(web::post().to(payments::payments_create_intent)),
|
||||
)
|
||||
.service(web::resource("/list").route(web::get().to(payments::payments_list)))
|
||||
.service(
|
||||
web::resource("/aggregate").route(web::get().to(payments::get_payments_aggregates)),
|
||||
)
|
||||
|
||||
@ -1234,6 +1234,35 @@ pub async fn payments_list(
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(flow = ?Flow::PaymentsList))]
|
||||
#[cfg(all(feature = "olap", feature = "v2"))]
|
||||
pub async fn payments_list(
|
||||
state: web::Data<app::AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
payload: web::Query<payment_types::PaymentListConstraints>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::PaymentsList;
|
||||
let payload = payload.into_inner();
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
payload,
|
||||
|state, auth: auth::AuthenticationData, req, _| {
|
||||
payments::list_payments(state, auth.merchant_account, auth.key_store, req)
|
||||
},
|
||||
auth::auth_type(
|
||||
&auth::HeaderAuth(auth::ApiKeyAuth),
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::MerchantPaymentRead,
|
||||
},
|
||||
req.headers(),
|
||||
),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(flow = ?Flow::PaymentsList))]
|
||||
#[cfg(all(feature = "olap", feature = "v1"))]
|
||||
pub async fn profile_payments_list(
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#[cfg(feature = "v1")]
|
||||
pub use api_models::payments::{PaymentListResponse, PaymentListResponseV2};
|
||||
pub use api_models::payments::{
|
||||
PaymentListFilterConstraints, PaymentListResponse, PaymentListResponseV2,
|
||||
};
|
||||
#[cfg(feature = "v2")]
|
||||
pub use api_models::payments::{
|
||||
PaymentsConfirmIntentRequest, PaymentsCreateIntentRequest, PaymentsIntentResponse,
|
||||
@ -14,9 +16,8 @@ pub use api_models::{
|
||||
CryptoData, CustomerAcceptance, CustomerDetailsResponse, MandateAmountData, MandateData,
|
||||
MandateTransactionType, MandateType, MandateValidationFields, NextActionType,
|
||||
OnlineMandate, OpenBankingSessionToken, PayLaterData, PaymentIdType,
|
||||
PaymentListConstraints, PaymentListFilterConstraints, PaymentListFilters,
|
||||
PaymentListFiltersV2, PaymentMethodData, PaymentMethodDataRequest,
|
||||
PaymentMethodDataResponse, PaymentOp, PaymentRetrieveBody,
|
||||
PaymentListConstraints, PaymentListFilters, PaymentListFiltersV2, PaymentMethodData,
|
||||
PaymentMethodDataRequest, PaymentMethodDataResponse, PaymentOp, PaymentRetrieveBody,
|
||||
PaymentRetrieveBodyWithCredentials, PaymentsAggregateResponse, PaymentsApproveRequest,
|
||||
PaymentsCancelRequest, PaymentsCaptureRequest, PaymentsCompleteAuthorizeRequest,
|
||||
PaymentsDynamicTaxCalculationRequest, PaymentsDynamicTaxCalculationResponse,
|
||||
|
||||
Reference in New Issue
Block a user