mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	 aa6ebf8aef
			
		
	
	aa6ebf8aef
	
	
	
		
			
			Co-authored-by: Narayan Bhat <narayan.bhat@juspay.in> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: hrithikesh026 <hrithikesh.vm@juspay.in> Co-authored-by: Aishwariyaa Anand <124241367+Aishwariyaa-Anand@users.noreply.github.com> Co-authored-by: Aishwariyaa Anand <aishwariyaa.anand@Aishwariyaa-Anand-C3PGW02T6Y.local>
		
			
				
	
	
		
			385 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			385 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use common_utils::custom_serde;
 | |
| 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)]
 | |
| #[serde(rename_all = "snake_case")]
 | |
| pub enum IncomingWebhookEvent {
 | |
|     /// Authorization + Capture success
 | |
|     PaymentIntentFailure,
 | |
|     /// Authorization + Capture failure
 | |
|     PaymentIntentSuccess,
 | |
|     PaymentIntentProcessing,
 | |
|     PaymentIntentPartiallyFunded,
 | |
|     PaymentIntentCancelled,
 | |
|     PaymentIntentCancelFailure,
 | |
|     PaymentIntentAuthorizationSuccess,
 | |
|     PaymentIntentAuthorizationFailure,
 | |
|     PaymentIntentCaptureSuccess,
 | |
|     PaymentIntentCaptureFailure,
 | |
|     PaymentActionRequired,
 | |
|     EventNotSupported,
 | |
|     SourceChargeable,
 | |
|     SourceTransactionCreated,
 | |
|     RefundFailure,
 | |
|     RefundSuccess,
 | |
|     DisputeOpened,
 | |
|     DisputeExpired,
 | |
|     DisputeAccepted,
 | |
|     DisputeCancelled,
 | |
|     DisputeChallenged,
 | |
|     // dispute has been successfully challenged by the merchant
 | |
|     DisputeWon,
 | |
|     // dispute has been unsuccessfully challenged
 | |
|     DisputeLost,
 | |
|     MandateActive,
 | |
|     MandateRevoked,
 | |
|     EndpointVerification,
 | |
|     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,
 | |
|     #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
 | |
|     RecoveryPaymentFailure,
 | |
|     #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
 | |
|     RecoveryPaymentSuccess,
 | |
|     #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
 | |
|     RecoveryPaymentPending,
 | |
|     #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
 | |
|     RecoveryInvoiceCancel,
 | |
| }
 | |
| 
 | |
| pub enum WebhookFlow {
 | |
|     Payment,
 | |
|     #[cfg(feature = "payouts")]
 | |
|     Payout,
 | |
|     Refund,
 | |
|     Dispute,
 | |
|     Subscription,
 | |
|     ReturnResponse,
 | |
|     BankTransfer,
 | |
|     Mandate,
 | |
|     ExternalAuthentication,
 | |
|     FraudCheck,
 | |
|     #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
 | |
|     Recovery,
 | |
| }
 | |
| 
 | |
| #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
 | |
| /// This enum tells about the affect a webhook had on an object
 | |
| pub enum WebhookResponseTracker {
 | |
|     #[cfg(feature = "v1")]
 | |
|     Payment {
 | |
|         payment_id: common_utils::id_type::PaymentId,
 | |
|         status: common_enums::IntentStatus,
 | |
|     },
 | |
|     #[cfg(feature = "v2")]
 | |
|     Payment {
 | |
|         payment_id: common_utils::id_type::GlobalPaymentId,
 | |
|         status: common_enums::IntentStatus,
 | |
|     },
 | |
|     #[cfg(feature = "payouts")]
 | |
|     Payout {
 | |
|         payout_id: String,
 | |
|         status: common_enums::PayoutStatus,
 | |
|     },
 | |
|     #[cfg(feature = "v1")]
 | |
|     Refund {
 | |
|         payment_id: common_utils::id_type::PaymentId,
 | |
|         refund_id: String,
 | |
|         status: common_enums::RefundStatus,
 | |
|     },
 | |
|     #[cfg(feature = "v2")]
 | |
|     Refund {
 | |
|         payment_id: common_utils::id_type::GlobalPaymentId,
 | |
|         refund_id: String,
 | |
|         status: common_enums::RefundStatus,
 | |
|     },
 | |
|     #[cfg(feature = "v1")]
 | |
|     Dispute {
 | |
|         dispute_id: String,
 | |
|         payment_id: common_utils::id_type::PaymentId,
 | |
|         status: common_enums::DisputeStatus,
 | |
|     },
 | |
|     #[cfg(feature = "v2")]
 | |
|     Dispute {
 | |
|         dispute_id: String,
 | |
|         payment_id: common_utils::id_type::GlobalPaymentId,
 | |
|         status: common_enums::DisputeStatus,
 | |
|     },
 | |
|     Mandate {
 | |
|         mandate_id: String,
 | |
|         status: common_enums::MandateStatus,
 | |
|     },
 | |
|     NoEffect,
 | |
|     Relay {
 | |
|         relay_id: common_utils::id_type::RelayId,
 | |
|         status: common_enums::RelayStatus,
 | |
|     },
 | |
| }
 | |
| 
 | |
| impl WebhookResponseTracker {
 | |
|     #[cfg(feature = "v1")]
 | |
|     pub fn get_payment_id(&self) -> Option<common_utils::id_type::PaymentId> {
 | |
|         match self {
 | |
|             Self::Payment { payment_id, .. }
 | |
|             | Self::Refund { payment_id, .. }
 | |
|             | Self::Dispute { payment_id, .. } => Some(payment_id.to_owned()),
 | |
|             Self::NoEffect | Self::Mandate { .. } => None,
 | |
|             #[cfg(feature = "payouts")]
 | |
|             Self::Payout { .. } => None,
 | |
|             Self::Relay { .. } => None,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[cfg(feature = "v2")]
 | |
|     pub fn get_payment_id(&self) -> Option<common_utils::id_type::GlobalPaymentId> {
 | |
|         match self {
 | |
|             Self::Payment { payment_id, .. }
 | |
|             | Self::Refund { payment_id, .. }
 | |
|             | Self::Dispute { payment_id, .. } => Some(payment_id.to_owned()),
 | |
|             Self::NoEffect | Self::Mandate { .. } => None,
 | |
|             #[cfg(feature = "payouts")]
 | |
|             Self::Payout { .. } => None,
 | |
|             Self::Relay { .. } => None,
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl From<IncomingWebhookEvent> for WebhookFlow {
 | |
|     fn from(evt: IncomingWebhookEvent) -> Self {
 | |
|         match evt {
 | |
|             IncomingWebhookEvent::PaymentIntentFailure
 | |
|             | IncomingWebhookEvent::PaymentIntentSuccess
 | |
|             | IncomingWebhookEvent::PaymentIntentProcessing
 | |
|             | IncomingWebhookEvent::PaymentActionRequired
 | |
|             | IncomingWebhookEvent::PaymentIntentPartiallyFunded
 | |
|             | IncomingWebhookEvent::PaymentIntentCancelled
 | |
|             | IncomingWebhookEvent::PaymentIntentCancelFailure
 | |
|             | IncomingWebhookEvent::PaymentIntentAuthorizationSuccess
 | |
|             | IncomingWebhookEvent::PaymentIntentAuthorizationFailure
 | |
|             | IncomingWebhookEvent::PaymentIntentCaptureSuccess
 | |
|             | IncomingWebhookEvent::PaymentIntentCaptureFailure => Self::Payment,
 | |
|             IncomingWebhookEvent::EventNotSupported => Self::ReturnResponse,
 | |
|             IncomingWebhookEvent::RefundSuccess | IncomingWebhookEvent::RefundFailure => {
 | |
|                 Self::Refund
 | |
|             }
 | |
|             IncomingWebhookEvent::MandateActive | IncomingWebhookEvent::MandateRevoked => {
 | |
|                 Self::Mandate
 | |
|             }
 | |
|             IncomingWebhookEvent::DisputeOpened
 | |
|             | IncomingWebhookEvent::DisputeAccepted
 | |
|             | IncomingWebhookEvent::DisputeExpired
 | |
|             | IncomingWebhookEvent::DisputeCancelled
 | |
|             | IncomingWebhookEvent::DisputeChallenged
 | |
|             | IncomingWebhookEvent::DisputeWon
 | |
|             | IncomingWebhookEvent::DisputeLost => Self::Dispute,
 | |
|             IncomingWebhookEvent::EndpointVerification => Self::ReturnResponse,
 | |
|             IncomingWebhookEvent::SourceChargeable
 | |
|             | IncomingWebhookEvent::SourceTransactionCreated => Self::BankTransfer,
 | |
|             IncomingWebhookEvent::ExternalAuthenticationARes => Self::ExternalAuthentication,
 | |
|             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,
 | |
|             #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
 | |
|             IncomingWebhookEvent::RecoveryInvoiceCancel
 | |
|             | IncomingWebhookEvent::RecoveryPaymentFailure
 | |
|             | IncomingWebhookEvent::RecoveryPaymentPending
 | |
|             | IncomingWebhookEvent::RecoveryPaymentSuccess => Self::Recovery,
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub type MerchantWebhookConfig = std::collections::HashSet<IncomingWebhookEvent>;
 | |
| 
 | |
| #[derive(Clone)]
 | |
| pub enum RefundIdType {
 | |
|     RefundId(String),
 | |
|     ConnectorRefundId(String),
 | |
| }
 | |
| 
 | |
| #[derive(Clone)]
 | |
| pub enum MandateIdType {
 | |
|     MandateId(String),
 | |
|     ConnectorMandateId(String),
 | |
| }
 | |
| 
 | |
| #[derive(Clone)]
 | |
| pub enum AuthenticationIdType {
 | |
|     AuthenticationId(String),
 | |
|     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),
 | |
|     #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
 | |
|     InvoiceId(InvoiceIdType),
 | |
| }
 | |
| 
 | |
| #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
 | |
| #[derive(Clone)]
 | |
| pub enum InvoiceIdType {
 | |
|     ConnectorInvoiceId(String),
 | |
| }
 | |
| 
 | |
| #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
 | |
| impl ObjectReferenceId {
 | |
|     pub fn get_connector_transaction_id_as_string(
 | |
|         self,
 | |
|     ) -> Result<String, common_utils::errors::ValidationError> {
 | |
|         match self {
 | |
|             Self::PaymentId(
 | |
|                 payments::PaymentIdType::ConnectorTransactionId(id)
 | |
|             ) => Ok(id),
 | |
|             Self::PaymentId(_)=>Err(
 | |
|                 common_utils::errors::ValidationError::IncorrectValueProvided {
 | |
|                     field_name: "ConnectorTransactionId variant of PaymentId is required but received otherr variant",
 | |
|                 },
 | |
|             ),
 | |
|             Self::RefundId(_) => Err(
 | |
|                 common_utils::errors::ValidationError::IncorrectValueProvided {
 | |
|                     field_name: "PaymentId is required but received RefundId",
 | |
|                 },
 | |
|             ),
 | |
|             Self::MandateId(_) => Err(
 | |
|                 common_utils::errors::ValidationError::IncorrectValueProvided {
 | |
|                     field_name: "PaymentId is required but received MandateId",
 | |
|                 },
 | |
|             ),
 | |
|             Self::ExternalAuthenticationID(_) => Err(
 | |
|                 common_utils::errors::ValidationError::IncorrectValueProvided {
 | |
|                     field_name: "PaymentId is required but received ExternalAuthenticationID",
 | |
|                 },
 | |
|             ),
 | |
|             #[cfg(feature = "payouts")]
 | |
|             Self::PayoutId(_) => Err(
 | |
|                 common_utils::errors::ValidationError::IncorrectValueProvided {
 | |
|                     field_name: "PaymentId is required but received PayoutId",
 | |
|                 },
 | |
|             ),
 | |
|             Self::InvoiceId(_) => Err(
 | |
|                 common_utils::errors::ValidationError::IncorrectValueProvided {
 | |
|                     field_name: "PaymentId is required but received InvoiceId",
 | |
|                 },
 | |
|             )
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub struct IncomingWebhookDetails {
 | |
|     pub object_reference_id: ObjectReferenceId,
 | |
|     pub resource_object: Vec<u8>,
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Serialize, ToSchema)]
 | |
| pub struct OutgoingWebhook {
 | |
|     /// The merchant id of the merchant
 | |
|     #[schema(value_type = String)]
 | |
|     pub merchant_id: common_utils::id_type::MerchantId,
 | |
| 
 | |
|     /// The unique event id for each webhook
 | |
|     pub event_id: String,
 | |
| 
 | |
|     /// The type of event this webhook corresponds to.
 | |
|     #[schema(value_type = EventType)]
 | |
|     pub event_type: api_enums::EventType,
 | |
| 
 | |
|     /// This is specific to the flow, for ex: it will be `PaymentsResponse` for payments flow
 | |
|     pub content: OutgoingWebhookContent,
 | |
| 
 | |
|     /// The time at which webhook was sent
 | |
|     #[serde(default, with = "custom_serde::iso8601")]
 | |
|     pub timestamp: PrimitiveDateTime,
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Clone, Serialize, ToSchema)]
 | |
| #[serde(tag = "type", content = "object", rename_all = "snake_case")]
 | |
| #[cfg(feature = "v1")]
 | |
| pub enum OutgoingWebhookContent {
 | |
|     #[schema(value_type = PaymentsResponse, title = "PaymentsResponse")]
 | |
|     PaymentDetails(Box<payments::PaymentsResponse>),
 | |
|     #[schema(value_type = RefundResponse, title = "RefundResponse")]
 | |
|     RefundDetails(Box<refunds::RefundResponse>),
 | |
|     #[schema(value_type = DisputeResponse, title = "DisputeResponse")]
 | |
|     DisputeDetails(Box<disputes::DisputeResponse>),
 | |
|     #[schema(value_type = MandateResponse, title = "MandateResponse")]
 | |
|     MandateDetails(Box<mandates::MandateResponse>),
 | |
|     #[cfg(feature = "payouts")]
 | |
|     #[schema(value_type = PayoutCreateResponse, title = "PayoutCreateResponse")]
 | |
|     PayoutDetails(Box<payouts::PayoutCreateResponse>),
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Clone, Serialize, ToSchema)]
 | |
| #[serde(tag = "type", content = "object", rename_all = "snake_case")]
 | |
| #[cfg(feature = "v2")]
 | |
| pub enum OutgoingWebhookContent {
 | |
|     #[schema(value_type = PaymentsResponse, title = "PaymentsResponse")]
 | |
|     PaymentDetails(Box<payments::PaymentsResponse>),
 | |
|     #[schema(value_type = RefundResponse, title = "RefundResponse")]
 | |
|     RefundDetails(Box<refunds::RefundResponse>),
 | |
|     #[schema(value_type = DisputeResponse, title = "DisputeResponse")]
 | |
|     DisputeDetails(Box<disputes::DisputeResponse>),
 | |
|     #[schema(value_type = MandateResponse, title = "MandateResponse")]
 | |
|     MandateDetails(Box<mandates::MandateResponse>),
 | |
|     #[cfg(feature = "payouts")]
 | |
|     #[schema(value_type = PayoutCreateResponse, title = "PayoutCreateResponse")]
 | |
|     PayoutDetails(Box<payouts::PayoutCreateResponse>),
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Clone, Serialize)]
 | |
| pub struct ConnectorWebhookSecrets {
 | |
|     pub secret: Vec<u8>,
 | |
|     pub additional_secret: Option<masking::Secret<String>>,
 | |
| }
 | |
| 
 | |
| #[cfg(all(feature = "v2", feature = "revenue_recovery"))]
 | |
| impl IncomingWebhookEvent {
 | |
|     pub fn is_recovery_transaction_event(&self) -> bool {
 | |
|         matches!(
 | |
|             self,
 | |
|             Self::RecoveryPaymentFailure
 | |
|                 | Self::RecoveryPaymentSuccess
 | |
|                 | Self::RecoveryPaymentPending
 | |
|         )
 | |
|     }
 | |
| }
 |