Skip to content

Commit

Permalink
feat(core): Create Payout Webhook Flow (#4696)
Browse files Browse the repository at this point in the history
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
Sakilmostak and hyperswitch-bot[bot] authored Jun 5, 2024
1 parent 32f0fae commit a3183a0
Show file tree
Hide file tree
Showing 43 changed files with 809 additions and 68 deletions.
45 changes: 45 additions & 0 deletions crates/api_models/src/webhooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use utoipa::ToSchema;

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

#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Copy)]
Expand Down Expand Up @@ -41,10 +43,26 @@ pub enum IncomingWebhookEvent {
ExternalAuthenticationARes,
FrmApproved,
FrmRejected,
#[cfg(feature = "payouts")]
PayoutSuccess,
#[cfg(feature = "payouts")]
PayoutFailure,
#[cfg(feature = "payouts")]
PayoutProcessing,
#[cfg(feature = "payouts")]
PayoutCancelled,
#[cfg(feature = "payouts")]
PayoutCreated,
#[cfg(feature = "payouts")]
PayoutExpired,
#[cfg(feature = "payouts")]
PayoutReversed,
}

pub enum WebhookFlow {
Payment,
#[cfg(feature = "payouts")]
Payout,
Refund,
Dispute,
Subscription,
Expand All @@ -62,6 +80,11 @@ pub enum WebhookResponseTracker {
payment_id: String,
status: common_enums::IntentStatus,
},
#[cfg(feature = "payouts")]
Payout {
payout_id: String,
status: common_enums::PayoutStatus,
},
Refund {
payment_id: String,
refund_id: String,
Expand All @@ -86,6 +109,8 @@ impl WebhookResponseTracker {
| Self::Refund { payment_id, .. }
| Self::Dispute { payment_id, .. } => Some(payment_id.to_string()),
Self::NoEffect | Self::Mandate { .. } => None,
#[cfg(feature = "payouts")]
Self::Payout { .. } => None,
}
}
}
Expand Down Expand Up @@ -125,6 +150,14 @@ impl From<IncomingWebhookEvent> for WebhookFlow {
IncomingWebhookEvent::FrmApproved | IncomingWebhookEvent::FrmRejected => {
Self::FraudCheck
}
#[cfg(feature = "payouts")]
IncomingWebhookEvent::PayoutSuccess
| IncomingWebhookEvent::PayoutFailure
| IncomingWebhookEvent::PayoutProcessing
| IncomingWebhookEvent::PayoutCancelled
| IncomingWebhookEvent::PayoutCreated
| IncomingWebhookEvent::PayoutExpired
| IncomingWebhookEvent::PayoutReversed => Self::Payout,
}
}
}
Expand All @@ -149,12 +182,21 @@ pub enum AuthenticationIdType {
ConnectorAuthenticationId(String),
}

#[cfg(feature = "payouts")]
#[derive(Clone)]
pub enum PayoutIdType {
PayoutAttemptId(String),
ConnectorPayoutId(String),
}

#[derive(Clone)]
pub enum ObjectReferenceId {
PaymentId(payments::PaymentIdType),
RefundId(RefundIdType),
MandateId(MandateIdType),
ExternalAuthenticationID(AuthenticationIdType),
#[cfg(feature = "payouts")]
PayoutId(PayoutIdType),
}

pub struct IncomingWebhookDetails {
Expand Down Expand Up @@ -193,6 +235,9 @@ pub enum OutgoingWebhookContent {
DisputeDetails(Box<disputes::DisputeResponse>),
#[schema(value_type = MandateResponse, title = "MandateResponse")]
MandateDetails(Box<mandates::MandateResponse>),
#[cfg(feature = "payouts")]
#[schema(value_type = PayoutCreateResponse, title = "PayoutCreateResponse")]
PayoutDetails(payouts::PayoutCreateResponse),
}

#[derive(Debug, Clone, Serialize)]
Expand Down
12 changes: 12 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,8 @@ pub enum EventClass {
Refunds,
Disputes,
Mandates,
#[cfg(feature = "payouts")]
Payouts,
}

#[derive(
Expand Down Expand Up @@ -1107,6 +1109,13 @@ pub enum EventType {
DisputeLost,
MandateActive,
MandateRevoked,
PayoutSuccess,
PayoutFailed,
PayoutInitiated,
PayoutProcessing,
PayoutCancelled,
PayoutExpired,
PayoutReversed,
}

#[derive(
Expand Down Expand Up @@ -2116,6 +2125,9 @@ pub enum PayoutStatus {
Success,
Failed,
Cancelled,
Initiated,
Expired,
Reversed,
Pending,
Ineligible,
#[default]
Expand Down
1 change: 1 addition & 0 deletions crates/diesel_models/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub enum EventObjectType {
RefundDetails,
DisputeDetails,
MandateDetails,
PayoutDetails,
}

#[derive(
Expand Down
10 changes: 5 additions & 5 deletions crates/diesel_models/src/payout_attempt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct PayoutAttempt {
pub merchant_id: String,
pub address_id: String,
pub connector: Option<String>,
pub connector_payout_id: String,
pub connector_payout_id: Option<String>,
pub payout_token: Option<String>,
pub status: storage_enums::PayoutStatus,
pub is_eligible: Option<bool>,
Expand Down Expand Up @@ -51,7 +51,7 @@ pub struct PayoutAttemptNew {
pub merchant_id: String,
pub address_id: String,
pub connector: Option<String>,
pub connector_payout_id: String,
pub connector_payout_id: Option<String>,
pub payout_token: Option<String>,
pub status: storage_enums::PayoutStatus,
pub is_eligible: Option<bool>,
Expand All @@ -71,7 +71,7 @@ pub struct PayoutAttemptNew {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PayoutAttemptUpdate {
StatusUpdate {
connector_payout_id: String,
connector_payout_id: Option<String>,
status: storage_enums::PayoutStatus,
error_message: Option<String>,
error_code: Option<String>,
Expand Down Expand Up @@ -138,7 +138,7 @@ impl From<PayoutAttemptUpdate> for PayoutAttemptUpdateInternal {
error_code,
is_eligible,
} => Self {
connector_payout_id: Some(connector_payout_id),
connector_payout_id,
status: Some(status),
error_message,
error_code,
Expand Down Expand Up @@ -182,7 +182,7 @@ impl PayoutAttemptUpdate {
} = self.into();
PayoutAttempt {
payout_token: payout_token.or(source.payout_token),
connector_payout_id: connector_payout_id.unwrap_or(source.connector_payout_id),
connector_payout_id: connector_payout_id.or(source.connector_payout_id),
status: status.unwrap_or(source.status),
error_message: error_message.or(source.error_message),
error_code: error_code.or(source.error_code),
Expand Down
14 changes: 14 additions & 0 deletions crates/diesel_models/src/query/payout_attempt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ impl PayoutAttempt {
.await
}

pub async fn find_by_merchant_id_connector_payout_id(
conn: &PgPooledConn,
merchant_id: &str,
connector_payout_id: &str,
) -> StorageResult<Self> {
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
conn,
dsl::merchant_id
.eq(merchant_id.to_owned())
.and(dsl::connector_payout_id.eq(connector_payout_id.to_owned())),
)
.await
}

pub async fn update_by_merchant_id_payout_id(
conn: &PgPooledConn,
merchant_id: &str,
Expand Down
2 changes: 1 addition & 1 deletion crates/diesel_models/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,7 @@ diesel::table! {
#[max_length = 64]
connector -> Nullable<Varchar>,
#[max_length = 128]
connector_payout_id -> Varchar,
connector_payout_id -> Nullable<Varchar>,
#[max_length = 64]
payout_token -> Nullable<Varchar>,
status -> PayoutStatus,
Expand Down
19 changes: 13 additions & 6 deletions crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ pub trait PayoutAttemptInterface {
_storage_scheme: MerchantStorageScheme,
) -> error_stack::Result<PayoutAttempt, errors::StorageError>;

async fn find_payout_attempt_by_merchant_id_connector_payout_id(
&self,
_merchant_id: &str,
_connector_payout_id: &str,
_storage_scheme: MerchantStorageScheme,
) -> error_stack::Result<PayoutAttempt, errors::StorageError>;

async fn get_filters_for_payouts(
&self,
payout: &[Payouts],
Expand All @@ -56,7 +63,7 @@ pub struct PayoutAttempt {
pub merchant_id: String,
pub address_id: String,
pub connector: Option<String>,
pub connector_payout_id: String,
pub connector_payout_id: Option<String>,
pub payout_token: Option<String>,
pub status: storage_enums::PayoutStatus,
pub is_eligible: Option<bool>,
Expand All @@ -81,7 +88,7 @@ pub struct PayoutAttemptNew {
pub merchant_id: String,
pub address_id: String,
pub connector: Option<String>,
pub connector_payout_id: String,
pub connector_payout_id: Option<String>,
pub payout_token: Option<String>,
pub status: storage_enums::PayoutStatus,
pub is_eligible: Option<bool>,
Expand All @@ -107,7 +114,7 @@ impl Default for PayoutAttemptNew {
merchant_id: String::default(),
address_id: String::default(),
connector: None,
connector_payout_id: String::default(),
connector_payout_id: Some(String::default()),
payout_token: None,
status: storage_enums::PayoutStatus::default(),
is_eligible: None,
Expand All @@ -124,10 +131,10 @@ impl Default for PayoutAttemptNew {
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum PayoutAttemptUpdate {
StatusUpdate {
connector_payout_id: String,
connector_payout_id: Option<String>,
status: storage_enums::PayoutStatus,
error_message: Option<String>,
error_code: Option<String>,
Expand Down Expand Up @@ -174,7 +181,7 @@ impl From<PayoutAttemptUpdate> for PayoutAttemptUpdateInternal {
error_code,
is_eligible,
} => Self {
connector_payout_id: Some(connector_payout_id),
connector_payout_id,
status: Some(status),
error_message,
error_code,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ pub struct RetrieveFileResponse {
#[derive(Clone, Debug, Default)]
pub struct PayoutsResponseData {
pub status: Option<common_enums::PayoutStatus>,
pub connector_payout_id: String,
pub connector_payout_id: Option<String>,
pub payout_eligible: Option<bool>,
pub should_add_next_step_to_process_tracker: bool,
}
Expand Down
Loading

0 comments on commit a3183a0

Please sign in to comment.