Skip to content

Commit fb5f0e6

Browse files
feat(payouts): implement list and filter APIs (#3651)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Kashif <[email protected]>
1 parent 4fb7c6e commit fb5f0e6

File tree

26 files changed

+1615
-25
lines changed

26 files changed

+1615
-25
lines changed

crates/api_models/src/events/payouts.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use common_utils::events::{ApiEventMetric, ApiEventsType};
22

33
use crate::payouts::{
4-
PayoutActionRequest, PayoutCreateRequest, PayoutCreateResponse, PayoutRetrieveRequest,
4+
PayoutActionRequest, PayoutCreateRequest, PayoutCreateResponse, PayoutListConstraints,
5+
PayoutListFilterConstraints, PayoutListFilters, PayoutListResponse, PayoutRetrieveRequest,
56
};
67

78
impl ApiEventMetric for PayoutRetrieveRequest {
@@ -27,3 +28,27 @@ impl ApiEventMetric for PayoutActionRequest {
2728
Some(ApiEventsType::Payout)
2829
}
2930
}
31+
32+
impl ApiEventMetric for PayoutListConstraints {
33+
fn get_api_event_type(&self) -> Option<ApiEventsType> {
34+
Some(ApiEventsType::Payout)
35+
}
36+
}
37+
38+
impl ApiEventMetric for PayoutListFilterConstraints {
39+
fn get_api_event_type(&self) -> Option<ApiEventsType> {
40+
Some(ApiEventsType::Payout)
41+
}
42+
}
43+
44+
impl ApiEventMetric for PayoutListResponse {
45+
fn get_api_event_type(&self) -> Option<ApiEventsType> {
46+
Some(ApiEventsType::Payout)
47+
}
48+
}
49+
50+
impl ApiEventMetric for PayoutListFilters {
51+
fn get_api_event_type(&self) -> Option<ApiEventsType> {
52+
Some(ApiEventsType::Payout)
53+
}
54+
}

crates/api_models/src/payments.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::{collections::HashMap, fmt, num::NonZeroI64};
22

33
use cards::CardNumber;
44
use common_utils::{
5+
consts::default_payments_list_limit,
56
crypto,
67
ext_traits::Encode,
78
pii::{self, Email},
@@ -3197,7 +3198,7 @@ pub struct PaymentListConstraints {
31973198

31983199
/// limit on the number of objects to return
31993200
#[schema(default = 10, maximum = 100)]
3200-
#[serde(default = "default_limit")]
3201+
#[serde(default = "default_payments_list_limit")]
32013202
pub limit: u32,
32023203

32033204
/// The time at which payment is created
@@ -3283,7 +3284,7 @@ pub struct PaymentListFilterConstraints {
32833284
/// The identifier for customer
32843285
pub customer_id: Option<String>,
32853286
/// The limit on the number of objects. The default limit is 10 and max limit is 20
3286-
#[serde(default = "default_limit")]
3287+
#[serde(default = "default_payments_list_limit")]
32873288
pub limit: u32,
32883289
/// The starting point within a list of objects
32893290
pub offset: Option<u32>,
@@ -3353,10 +3354,6 @@ pub struct VerifyResponse {
33533354
pub error_message: Option<String>,
33543355
}
33553356

3356-
fn default_limit() -> u32 {
3357-
10
3358-
}
3359-
33603357
#[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
33613358
pub struct PaymentsRedirectionResponse {
33623359
pub redirect_url: String,

crates/api_models/src/payouts.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use cards::CardNumber;
22
use common_utils::{
3+
consts::default_payouts_list_limit,
34
crypto,
45
pii::{self, Email},
56
};
67
use masking::Secret;
78
use serde::{Deserialize, Serialize};
9+
use time::PrimitiveDateTime;
810
use utoipa::ToSchema;
911

1012
use crate::{enums as api_enums, payments};
@@ -393,6 +395,54 @@ pub struct PayoutCreateResponse {
393395

394396
/// The business profile that is associated with this payment
395397
pub profile_id: String,
398+
399+
/// Time when the payout was created
400+
#[schema(example = "2022-09-10T10:11:12Z")]
401+
#[serde(with = "common_utils::custom_serde::iso8601::option")]
402+
pub created: Option<PrimitiveDateTime>,
403+
404+
/// List of attempts
405+
#[schema(value_type = Option<Vec<PayoutAttemptResponse>>)]
406+
#[serde(skip_serializing_if = "Option::is_none")]
407+
pub attempts: Option<Vec<PayoutAttemptResponse>>,
408+
}
409+
410+
#[derive(
411+
Default, Debug, serde::Serialize, Clone, PartialEq, ToSchema, router_derive::PolymorphicSchema,
412+
)]
413+
pub struct PayoutAttemptResponse {
414+
/// Unique identifier for the attempt
415+
pub attempt_id: String,
416+
/// The status of the attempt
417+
#[schema(value_type = PayoutStatus, example = "failed")]
418+
pub status: api_enums::PayoutStatus,
419+
/// The payout attempt amount. Amount for the payout in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,
420+
pub amount: i64,
421+
/// The currency of the amount of the payout attempt
422+
#[schema(value_type = Option<Currency>, example = "USD")]
423+
pub currency: Option<api_enums::Currency>,
424+
/// The connector used for the payout
425+
pub connector: Option<String>,
426+
/// Connector's error code in case of failures
427+
pub error_code: Option<String>,
428+
/// Connector's error message in case of failures
429+
pub error_message: Option<String>,
430+
/// The payout method that was used
431+
#[schema(value_type = Option<PayoutType>, example = "bank")]
432+
pub payment_method: Option<api_enums::PayoutType>,
433+
/// Payment Method Type
434+
#[schema(value_type = Option<PaymentMethodType>, example = "bacs")]
435+
pub payout_method_type: Option<api_enums::PaymentMethodType>,
436+
/// A unique identifier for a payout provided by the connector
437+
pub connector_transaction_id: Option<String>,
438+
/// If the payout was cancelled the reason provided here
439+
pub cancellation_reason: Option<String>,
440+
/// Provide a reference to a stored payout method
441+
pub payout_token: Option<String>,
442+
/// error code unified across the connectors is received here if there was an error while calling connector
443+
pub unified_code: Option<String>,
444+
/// error message unified across the connectors is received here if there was an error while calling connector
445+
pub unified_message: Option<String>,
396446
}
397447

398448
#[derive(Default, Debug, Clone, Deserialize, ToSchema)]
@@ -430,3 +480,96 @@ pub struct PayoutActionRequest {
430480
)]
431481
pub payout_id: String,
432482
}
483+
484+
#[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)]
485+
#[serde(deny_unknown_fields)]
486+
pub struct PayoutListConstraints {
487+
/// The identifier for customer
488+
#[schema(example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
489+
pub customer_id: Option<String>,
490+
491+
/// A cursor for use in pagination, fetch the next list after some object
492+
#[schema(example = "pay_fafa124123")]
493+
pub starting_after: Option<String>,
494+
495+
/// A cursor for use in pagination, fetch the previous list before some object
496+
#[schema(example = "pay_fafa124123")]
497+
pub ending_before: Option<String>,
498+
499+
/// limit on the number of objects to return
500+
#[schema(default = 10, maximum = 100)]
501+
#[serde(default = "default_payouts_list_limit")]
502+
pub limit: u32,
503+
504+
/// The time at which payout is created
505+
#[schema(example = "2022-09-10T10:11:12Z")]
506+
#[serde(default, with = "common_utils::custom_serde::iso8601::option")]
507+
pub created: Option<PrimitiveDateTime>,
508+
509+
/// 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).
510+
#[serde(flatten)]
511+
#[schema(value_type = Option<TimeRange>)]
512+
pub time_range: Option<payments::TimeRange>,
513+
}
514+
515+
#[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)]
516+
#[serde(deny_unknown_fields)]
517+
pub struct PayoutListFilterConstraints {
518+
/// The identifier for payout
519+
#[schema(
520+
value_type = Option<String>,
521+
min_length = 30,
522+
max_length = 30,
523+
example = "payout_mbabizu24mvu3mela5njyhpit4"
524+
)]
525+
pub payout_id: Option<String>,
526+
/// The identifier for business profile
527+
pub profile_id: Option<String>,
528+
/// The identifier for customer
529+
#[schema(example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
530+
pub customer_id: Option<String>,
531+
/// The limit on the number of objects. The default limit is 10 and max limit is 20
532+
#[serde(default = "default_payouts_list_limit")]
533+
pub limit: u32,
534+
/// The starting point within a list of objects
535+
pub offset: Option<u32>,
536+
/// 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).
537+
#[serde(flatten)]
538+
#[schema(value_type = Option<TimeRange>)]
539+
pub time_range: Option<payments::TimeRange>,
540+
/// The list of connectors to filter payouts list
541+
#[schema(value_type = Option<Vec<PayoutConnectors>>, max_length = 255, example = json!(["wise", "adyen"]))]
542+
pub connector: Option<Vec<api_enums::PayoutConnectors>>,
543+
/// The list of currencies to filter payouts list
544+
#[schema(value_type = Currency, example = "USD")]
545+
pub currency: Option<Vec<api_enums::Currency>>,
546+
/// The list of payout status to filter payouts list
547+
#[schema(value_type = Option<Vec<PayoutStatus>>, example = json!(["pending", "failed"]))]
548+
pub status: Option<Vec<api_enums::PayoutStatus>>,
549+
/// The list of payout methods to filter payouts list
550+
#[schema(value_type = Option<Vec<PayoutType>>, example = json!(["bank", "card"]))]
551+
pub payout_method: Option<Vec<common_enums::PayoutType>>,
552+
/// Type of recipient
553+
#[schema(value_type = PayoutEntityType, example = "Individual")]
554+
pub entity_type: Option<common_enums::PayoutEntityType>,
555+
}
556+
557+
#[derive(Clone, Debug, serde::Serialize, ToSchema)]
558+
pub struct PayoutListResponse {
559+
/// The number of payouts included in the list
560+
pub size: usize,
561+
// The list of payouts response objects
562+
pub data: Vec<PayoutCreateResponse>,
563+
}
564+
565+
#[derive(Clone, Debug, serde::Serialize)]
566+
pub struct PayoutListFilters {
567+
/// The list of available connector filters
568+
pub connector: Vec<api_enums::PayoutConnectors>,
569+
/// The list of available currency filters
570+
pub currency: Vec<common_enums::Currency>,
571+
/// The list of available payment status filters
572+
pub status: Vec<common_enums::PayoutStatus>,
573+
/// The list of available payment method filters
574+
pub payout_method: Vec<common_enums::PayoutType>,
575+
}

crates/common_enums/src/enums.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2028,6 +2028,7 @@ pub enum CanadaStatesAbbreviation {
20282028
Debug,
20292029
Default,
20302030
Eq,
2031+
Hash,
20312032
PartialEq,
20322033
ToSchema,
20332034
serde::Deserialize,

crates/common_utils/src/consts.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,23 @@ pub static FRM_CONFIGS_EG: &str = r#"
2323
pub const PAYMENTS_LIST_MAX_LIMIT_V1: u32 = 100;
2424
/// Maximum limit for payments list post api with filters
2525
pub const PAYMENTS_LIST_MAX_LIMIT_V2: u32 = 20;
26+
/// Default limit for payments list API
27+
pub fn default_payments_list_limit() -> u32 {
28+
10
29+
}
2630

2731
/// Maximum limit for payment link list get api
2832
pub const PAYMENTS_LINK_LIST_LIMIT: u32 = 100;
2933

34+
/// Maximum limit for payouts list get api
35+
pub const PAYOUTS_LIST_MAX_LIMIT_GET: u32 = 100;
36+
/// Maximum limit for payouts list post api
37+
pub const PAYOUTS_LIST_MAX_LIMIT_POST: u32 = 20;
38+
/// Default limit for payouts list API
39+
pub fn default_payouts_list_limit() -> u32 {
40+
10
41+
}
42+
3043
/// surcharge percentage maximum precision length
3144
pub const SURCHARGE_PERCENTAGE_PRECISION_LENGTH: u8 = 2;
3245

crates/data_models/src/payouts.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,100 @@
11
pub mod payout_attempt;
22
#[allow(clippy::module_inception)]
33
pub mod payouts;
4+
5+
use common_enums as storage_enums;
6+
use common_utils::consts;
7+
use time::PrimitiveDateTime;
8+
9+
pub enum PayoutFetchConstraints {
10+
Single { payout_id: String },
11+
List(Box<PayoutListParams>),
12+
}
13+
14+
pub struct PayoutListParams {
15+
pub offset: u32,
16+
pub starting_at: Option<PrimitiveDateTime>,
17+
pub ending_at: Option<PrimitiveDateTime>,
18+
pub connector: Option<Vec<api_models::enums::PayoutConnectors>>,
19+
pub currency: Option<Vec<storage_enums::Currency>>,
20+
pub status: Option<Vec<storage_enums::PayoutStatus>>,
21+
pub payout_method: Option<Vec<common_enums::PayoutType>>,
22+
pub profile_id: Option<String>,
23+
pub customer_id: Option<String>,
24+
pub starting_after_id: Option<String>,
25+
pub ending_before_id: Option<String>,
26+
pub entity_type: Option<common_enums::PayoutEntityType>,
27+
pub limit: Option<u32>,
28+
}
29+
30+
impl From<api_models::payouts::PayoutListConstraints> for PayoutFetchConstraints {
31+
fn from(value: api_models::payouts::PayoutListConstraints) -> Self {
32+
Self::List(Box::new(PayoutListParams {
33+
offset: 0,
34+
starting_at: value
35+
.time_range
36+
.map_or(value.created, |t| Some(t.start_time)),
37+
ending_at: value.time_range.and_then(|t| t.end_time),
38+
connector: None,
39+
currency: None,
40+
status: None,
41+
payout_method: None,
42+
profile_id: None,
43+
customer_id: value.customer_id,
44+
starting_after_id: value.starting_after,
45+
ending_before_id: value.ending_before,
46+
entity_type: None,
47+
limit: Some(std::cmp::min(
48+
value.limit,
49+
consts::PAYOUTS_LIST_MAX_LIMIT_GET,
50+
)),
51+
}))
52+
}
53+
}
54+
55+
impl From<api_models::payments::TimeRange> for PayoutFetchConstraints {
56+
fn from(value: api_models::payments::TimeRange) -> Self {
57+
Self::List(Box::new(PayoutListParams {
58+
offset: 0,
59+
starting_at: Some(value.start_time),
60+
ending_at: value.end_time,
61+
connector: None,
62+
currency: None,
63+
status: None,
64+
payout_method: None,
65+
profile_id: None,
66+
customer_id: None,
67+
starting_after_id: None,
68+
ending_before_id: None,
69+
entity_type: None,
70+
limit: None,
71+
}))
72+
}
73+
}
74+
75+
impl From<api_models::payouts::PayoutListFilterConstraints> for PayoutFetchConstraints {
76+
fn from(value: api_models::payouts::PayoutListFilterConstraints) -> Self {
77+
if let Some(payout_id) = value.payout_id {
78+
Self::Single { payout_id }
79+
} else {
80+
Self::List(Box::new(PayoutListParams {
81+
offset: value.offset.unwrap_or_default(),
82+
starting_at: value.time_range.map(|t| t.start_time),
83+
ending_at: value.time_range.and_then(|t| t.end_time),
84+
connector: value.connector,
85+
currency: value.currency,
86+
status: value.status,
87+
payout_method: value.payout_method,
88+
profile_id: value.profile_id,
89+
customer_id: value.customer_id,
90+
starting_after_id: None,
91+
ending_before_id: None,
92+
entity_type: value.entity_type,
93+
limit: Some(std::cmp::min(
94+
value.limit,
95+
consts::PAYOUTS_LIST_MAX_LIMIT_POST,
96+
)),
97+
}))
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)