mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(router): add payment incoming webhooks support for v2 (#6551)
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>
This commit is contained in:
committed by
GitHub
parent
65bf75a75e
commit
8e9c3ec893
@ -3691,6 +3691,7 @@ pub struct PaymentMethodDataResponseWithBilling {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
#[cfg(feature = "v1")]
|
||||
pub enum PaymentIdType {
|
||||
/// The identifier for payment intent
|
||||
PaymentIntentId(id_type::PaymentId),
|
||||
@ -3702,6 +3703,20 @@ pub enum PaymentIdType {
|
||||
PreprocessingId(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
#[cfg(feature = "v2")]
|
||||
pub enum PaymentIdType {
|
||||
/// The identifier for payment intent
|
||||
PaymentIntentId(id_type::GlobalPaymentId),
|
||||
/// The identifier for connector transaction
|
||||
ConnectorTransactionId(String),
|
||||
/// The identifier for payment attempt
|
||||
PaymentAttemptId(String),
|
||||
/// The identifier for preprocessing step
|
||||
PreprocessingId(String),
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl fmt::Display for PaymentIdType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
@ -3726,6 +3741,7 @@ impl fmt::Display for PaymentIdType {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl Default for PaymentIdType {
|
||||
fn default() -> Self {
|
||||
Self::PaymentIntentId(Default::default())
|
||||
@ -4566,7 +4582,7 @@ pub struct PaymentsRetrieveRequest {
|
||||
|
||||
/// Error details for the payment
|
||||
#[cfg(feature = "v2")]
|
||||
#[derive(Debug, serde::Serialize, ToSchema)]
|
||||
#[derive(Debug, serde::Serialize, Clone, ToSchema)]
|
||||
pub struct ErrorDetails {
|
||||
/// The error code
|
||||
pub code: String,
|
||||
@ -4651,7 +4667,7 @@ pub struct PaymentsConfirmIntentResponse {
|
||||
// TODO: have a separate response for detailed, summarized
|
||||
/// Response for Payment Intent Confirm
|
||||
#[cfg(feature = "v2")]
|
||||
#[derive(Debug, serde::Serialize, ToSchema)]
|
||||
#[derive(Debug, serde::Serialize, Clone, ToSchema)]
|
||||
pub struct PaymentsRetrieveResponse {
|
||||
/// Unique identifier for the payment. This ensures idempotency for multiple payments
|
||||
/// that have been done by a single merchant.
|
||||
@ -6307,6 +6323,85 @@ pub struct FrmMessage {
|
||||
pub frm_error: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2")]
|
||||
mod payment_id_type {
|
||||
use std::{borrow::Cow, fmt};
|
||||
|
||||
use serde::{
|
||||
de::{self, Visitor},
|
||||
Deserializer,
|
||||
};
|
||||
|
||||
use super::PaymentIdType;
|
||||
|
||||
struct PaymentIdVisitor;
|
||||
struct OptionalPaymentIdVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for PaymentIdVisitor {
|
||||
type Value = PaymentIdType;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("payment id")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
common_utils::id_type::GlobalPaymentId::try_from(Cow::Owned(value.to_string()))
|
||||
.map_err(de::Error::custom)
|
||||
.map(PaymentIdType::PaymentIntentId)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Visitor<'de> for OptionalPaymentIdVisitor {
|
||||
type Value = Option<PaymentIdType>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("payment id")
|
||||
}
|
||||
|
||||
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(PaymentIdVisitor).map(Some)
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn deserialize<'a, D>(deserializer: D) -> Result<PaymentIdType, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_any(PaymentIdVisitor)
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize_option<'a, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<PaymentIdType>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_option(OptionalPaymentIdVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
mod payment_id_type {
|
||||
use std::{borrow::Cow, fmt};
|
||||
|
||||
|
||||
@ -76,25 +76,45 @@ pub enum WebhookFlow {
|
||||
#[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,
|
||||
@ -103,6 +123,7 @@ pub enum WebhookResponseTracker {
|
||||
}
|
||||
|
||||
impl WebhookResponseTracker {
|
||||
#[cfg(feature = "v1")]
|
||||
pub fn get_payment_id(&self) -> Option<common_utils::id_type::PaymentId> {
|
||||
match self {
|
||||
Self::Payment { payment_id, .. }
|
||||
@ -113,6 +134,18 @@ impl WebhookResponseTracker {
|
||||
Self::Payout { .. } => 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IncomingWebhookEvent> for WebhookFlow {
|
||||
@ -227,6 +260,7 @@ pub struct OutgoingWebhook {
|
||||
|
||||
#[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>),
|
||||
@ -241,6 +275,23 @@ pub enum OutgoingWebhookContent {
|
||||
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::PaymentsRetrieveResponse>),
|
||||
#[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>,
|
||||
|
||||
Reference in New Issue
Block a user