Skip to content

Commit a3183a0

Browse files
feat(core): Create Payout Webhook Flow (#4696)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
1 parent 32f0fae commit a3183a0

File tree

43 files changed

+809
-68
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+809
-68
lines changed

crates/api_models/src/webhooks.rs

+45
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
33
use time::PrimitiveDateTime;
44
use utoipa::ToSchema;
55

6+
#[cfg(feature = "payouts")]
7+
use crate::payouts;
68
use crate::{disputes, enums as api_enums, mandates, payments, refunds};
79

810
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Copy)]
@@ -41,10 +43,26 @@ pub enum IncomingWebhookEvent {
4143
ExternalAuthenticationARes,
4244
FrmApproved,
4345
FrmRejected,
46+
#[cfg(feature = "payouts")]
47+
PayoutSuccess,
48+
#[cfg(feature = "payouts")]
49+
PayoutFailure,
50+
#[cfg(feature = "payouts")]
51+
PayoutProcessing,
52+
#[cfg(feature = "payouts")]
53+
PayoutCancelled,
54+
#[cfg(feature = "payouts")]
55+
PayoutCreated,
56+
#[cfg(feature = "payouts")]
57+
PayoutExpired,
58+
#[cfg(feature = "payouts")]
59+
PayoutReversed,
4460
}
4561

4662
pub enum WebhookFlow {
4763
Payment,
64+
#[cfg(feature = "payouts")]
65+
Payout,
4866
Refund,
4967
Dispute,
5068
Subscription,
@@ -62,6 +80,11 @@ pub enum WebhookResponseTracker {
6280
payment_id: String,
6381
status: common_enums::IntentStatus,
6482
},
83+
#[cfg(feature = "payouts")]
84+
Payout {
85+
payout_id: String,
86+
status: common_enums::PayoutStatus,
87+
},
6588
Refund {
6689
payment_id: String,
6790
refund_id: String,
@@ -86,6 +109,8 @@ impl WebhookResponseTracker {
86109
| Self::Refund { payment_id, .. }
87110
| Self::Dispute { payment_id, .. } => Some(payment_id.to_string()),
88111
Self::NoEffect | Self::Mandate { .. } => None,
112+
#[cfg(feature = "payouts")]
113+
Self::Payout { .. } => None,
89114
}
90115
}
91116
}
@@ -125,6 +150,14 @@ impl From<IncomingWebhookEvent> for WebhookFlow {
125150
IncomingWebhookEvent::FrmApproved | IncomingWebhookEvent::FrmRejected => {
126151
Self::FraudCheck
127152
}
153+
#[cfg(feature = "payouts")]
154+
IncomingWebhookEvent::PayoutSuccess
155+
| IncomingWebhookEvent::PayoutFailure
156+
| IncomingWebhookEvent::PayoutProcessing
157+
| IncomingWebhookEvent::PayoutCancelled
158+
| IncomingWebhookEvent::PayoutCreated
159+
| IncomingWebhookEvent::PayoutExpired
160+
| IncomingWebhookEvent::PayoutReversed => Self::Payout,
128161
}
129162
}
130163
}
@@ -149,12 +182,21 @@ pub enum AuthenticationIdType {
149182
ConnectorAuthenticationId(String),
150183
}
151184

185+
#[cfg(feature = "payouts")]
186+
#[derive(Clone)]
187+
pub enum PayoutIdType {
188+
PayoutAttemptId(String),
189+
ConnectorPayoutId(String),
190+
}
191+
152192
#[derive(Clone)]
153193
pub enum ObjectReferenceId {
154194
PaymentId(payments::PaymentIdType),
155195
RefundId(RefundIdType),
156196
MandateId(MandateIdType),
157197
ExternalAuthenticationID(AuthenticationIdType),
198+
#[cfg(feature = "payouts")]
199+
PayoutId(PayoutIdType),
158200
}
159201

160202
pub struct IncomingWebhookDetails {
@@ -193,6 +235,9 @@ pub enum OutgoingWebhookContent {
193235
DisputeDetails(Box<disputes::DisputeResponse>),
194236
#[schema(value_type = MandateResponse, title = "MandateResponse")]
195237
MandateDetails(Box<mandates::MandateResponse>),
238+
#[cfg(feature = "payouts")]
239+
#[schema(value_type = PayoutCreateResponse, title = "PayoutCreateResponse")]
240+
PayoutDetails(payouts::PayoutCreateResponse),
196241
}
197242

198243
#[derive(Debug, Clone, Serialize)]

crates/common_enums/src/enums.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,8 @@ pub enum EventClass {
10691069
Refunds,
10701070
Disputes,
10711071
Mandates,
1072+
#[cfg(feature = "payouts")]
1073+
Payouts,
10721074
}
10731075

10741076
#[derive(
@@ -1107,6 +1109,13 @@ pub enum EventType {
11071109
DisputeLost,
11081110
MandateActive,
11091111
MandateRevoked,
1112+
PayoutSuccess,
1113+
PayoutFailed,
1114+
PayoutInitiated,
1115+
PayoutProcessing,
1116+
PayoutCancelled,
1117+
PayoutExpired,
1118+
PayoutReversed,
11101119
}
11111120

11121121
#[derive(
@@ -2116,6 +2125,9 @@ pub enum PayoutStatus {
21162125
Success,
21172126
Failed,
21182127
Cancelled,
2128+
Initiated,
2129+
Expired,
2130+
Reversed,
21192131
Pending,
21202132
Ineligible,
21212133
#[default]

crates/diesel_models/src/enums.rs

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ pub enum EventObjectType {
6868
RefundDetails,
6969
DisputeDetails,
7070
MandateDetails,
71+
PayoutDetails,
7172
}
7273

7374
#[derive(

crates/diesel_models/src/payout_attempt.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub struct PayoutAttempt {
1515
pub merchant_id: String,
1616
pub address_id: String,
1717
pub connector: Option<String>,
18-
pub connector_payout_id: String,
18+
pub connector_payout_id: Option<String>,
1919
pub payout_token: Option<String>,
2020
pub status: storage_enums::PayoutStatus,
2121
pub is_eligible: Option<bool>,
@@ -51,7 +51,7 @@ pub struct PayoutAttemptNew {
5151
pub merchant_id: String,
5252
pub address_id: String,
5353
pub connector: Option<String>,
54-
pub connector_payout_id: String,
54+
pub connector_payout_id: Option<String>,
5555
pub payout_token: Option<String>,
5656
pub status: storage_enums::PayoutStatus,
5757
pub is_eligible: Option<bool>,
@@ -71,7 +71,7 @@ pub struct PayoutAttemptNew {
7171
#[derive(Debug, Clone, Serialize, Deserialize)]
7272
pub enum PayoutAttemptUpdate {
7373
StatusUpdate {
74-
connector_payout_id: String,
74+
connector_payout_id: Option<String>,
7575
status: storage_enums::PayoutStatus,
7676
error_message: Option<String>,
7777
error_code: Option<String>,
@@ -138,7 +138,7 @@ impl From<PayoutAttemptUpdate> for PayoutAttemptUpdateInternal {
138138
error_code,
139139
is_eligible,
140140
} => Self {
141-
connector_payout_id: Some(connector_payout_id),
141+
connector_payout_id,
142142
status: Some(status),
143143
error_message,
144144
error_code,
@@ -182,7 +182,7 @@ impl PayoutAttemptUpdate {
182182
} = self.into();
183183
PayoutAttempt {
184184
payout_token: payout_token.or(source.payout_token),
185-
connector_payout_id: connector_payout_id.unwrap_or(source.connector_payout_id),
185+
connector_payout_id: connector_payout_id.or(source.connector_payout_id),
186186
status: status.unwrap_or(source.status),
187187
error_message: error_message.or(source.error_message),
188188
error_code: error_code.or(source.error_code),

crates/diesel_models/src/query/payout_attempt.rs

+14
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,20 @@ impl PayoutAttempt {
8181
.await
8282
}
8383

84+
pub async fn find_by_merchant_id_connector_payout_id(
85+
conn: &PgPooledConn,
86+
merchant_id: &str,
87+
connector_payout_id: &str,
88+
) -> StorageResult<Self> {
89+
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
90+
conn,
91+
dsl::merchant_id
92+
.eq(merchant_id.to_owned())
93+
.and(dsl::connector_payout_id.eq(connector_payout_id.to_owned())),
94+
)
95+
.await
96+
}
97+
8498
pub async fn update_by_merchant_id_payout_id(
8599
conn: &PgPooledConn,
86100
merchant_id: &str,

crates/diesel_models/src/schema.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,7 @@ diesel::table! {
967967
#[max_length = 64]
968968
connector -> Nullable<Varchar>,
969969
#[max_length = 128]
970-
connector_payout_id -> Varchar,
970+
connector_payout_id -> Nullable<Varchar>,
971971
#[max_length = 64]
972972
payout_token -> Nullable<Varchar>,
973973
status -> PayoutStatus,

crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ pub trait PayoutAttemptInterface {
3232
_storage_scheme: MerchantStorageScheme,
3333
) -> error_stack::Result<PayoutAttempt, errors::StorageError>;
3434

35+
async fn find_payout_attempt_by_merchant_id_connector_payout_id(
36+
&self,
37+
_merchant_id: &str,
38+
_connector_payout_id: &str,
39+
_storage_scheme: MerchantStorageScheme,
40+
) -> error_stack::Result<PayoutAttempt, errors::StorageError>;
41+
3542
async fn get_filters_for_payouts(
3643
&self,
3744
payout: &[Payouts],
@@ -56,7 +63,7 @@ pub struct PayoutAttempt {
5663
pub merchant_id: String,
5764
pub address_id: String,
5865
pub connector: Option<String>,
59-
pub connector_payout_id: String,
66+
pub connector_payout_id: Option<String>,
6067
pub payout_token: Option<String>,
6168
pub status: storage_enums::PayoutStatus,
6269
pub is_eligible: Option<bool>,
@@ -81,7 +88,7 @@ pub struct PayoutAttemptNew {
8188
pub merchant_id: String,
8289
pub address_id: String,
8390
pub connector: Option<String>,
84-
pub connector_payout_id: String,
91+
pub connector_payout_id: Option<String>,
8592
pub payout_token: Option<String>,
8693
pub status: storage_enums::PayoutStatus,
8794
pub is_eligible: Option<bool>,
@@ -107,7 +114,7 @@ impl Default for PayoutAttemptNew {
107114
merchant_id: String::default(),
108115
address_id: String::default(),
109116
connector: None,
110-
connector_payout_id: String::default(),
117+
connector_payout_id: Some(String::default()),
111118
payout_token: None,
112119
status: storage_enums::PayoutStatus::default(),
113120
is_eligible: None,
@@ -124,10 +131,10 @@ impl Default for PayoutAttemptNew {
124131
}
125132
}
126133

127-
#[derive(Debug)]
134+
#[derive(Debug, Clone)]
128135
pub enum PayoutAttemptUpdate {
129136
StatusUpdate {
130-
connector_payout_id: String,
137+
connector_payout_id: Option<String>,
131138
status: storage_enums::PayoutStatus,
132139
error_message: Option<String>,
133140
error_code: Option<String>,
@@ -174,7 +181,7 @@ impl From<PayoutAttemptUpdate> for PayoutAttemptUpdateInternal {
174181
error_code,
175182
is_eligible,
176183
} => Self {
177-
connector_payout_id: Some(connector_payout_id),
184+
connector_payout_id,
178185
status: Some(status),
179186
error_message,
180187
error_code,

crates/hyperswitch_domain_models/src/router_response_types.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ pub struct RetrieveFileResponse {
182182
#[derive(Clone, Debug, Default)]
183183
pub struct PayoutsResponseData {
184184
pub status: Option<common_enums::PayoutStatus>,
185-
pub connector_payout_id: String,
185+
pub connector_payout_id: Option<String>,
186186
pub payout_eligible: Option<bool>,
187187
pub should_add_next_step_to_process_tracker: bool,
188188
}

0 commit comments

Comments
 (0)