mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
feat(connector): Add refund and dispute webhooks for Rapyd (#1313)
This commit is contained in:
@ -757,10 +757,10 @@ pub struct CheckoutDisputeWebhookData {
|
||||
pub action_id: Option<String>,
|
||||
pub amount: i32,
|
||||
pub currency: String,
|
||||
#[serde(with = "common_utils::custom_serde::iso8601::option")]
|
||||
#[serde(default, with = "common_utils::custom_serde::iso8601::option")]
|
||||
pub evidence_required_by: Option<PrimitiveDateTime>,
|
||||
pub reason_code: Option<String>,
|
||||
#[serde(with = "common_utils::custom_serde::iso8601::option")]
|
||||
#[serde(default, with = "common_utils::custom_serde::iso8601::option")]
|
||||
pub date: Option<PrimitiveDateTime>,
|
||||
}
|
||||
#[derive(Debug, Deserialize)]
|
||||
@ -768,7 +768,7 @@ pub struct CheckoutDisputeWebhookBody {
|
||||
#[serde(rename = "type")]
|
||||
pub transaction_type: CheckoutTransactionType,
|
||||
pub data: CheckoutDisputeWebhookData,
|
||||
#[serde(with = "common_utils::custom_serde::iso8601::option")]
|
||||
#[serde(default, with = "common_utils::custom_serde::iso8601::option")]
|
||||
pub created_on: Option<PrimitiveDateTime>,
|
||||
}
|
||||
#[derive(Debug, Deserialize, strum::Display, Clone)]
|
||||
|
||||
@ -813,16 +813,23 @@ impl api::IncomingWebhook for Rapyd {
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
|
||||
Ok(match webhook.data {
|
||||
transformers::WebhookData::PaymentData(payment_data) => {
|
||||
transformers::WebhookData::Payment(payment_data) => {
|
||||
api_models::webhooks::ObjectReferenceId::PaymentId(
|
||||
api_models::payments::PaymentIdType::ConnectorTransactionId(payment_data.id),
|
||||
)
|
||||
}
|
||||
transformers::WebhookData::RefundData(refund_data) => {
|
||||
transformers::WebhookData::Refund(refund_data) => {
|
||||
api_models::webhooks::ObjectReferenceId::RefundId(
|
||||
api_models::webhooks::RefundIdType::ConnectorRefundId(refund_data.id),
|
||||
)
|
||||
}
|
||||
transformers::WebhookData::Dispute(dispute_data) => {
|
||||
api_models::webhooks::ObjectReferenceId::PaymentId(
|
||||
api_models::payments::PaymentIdType::ConnectorTransactionId(
|
||||
dispute_data.original_transaction_id,
|
||||
),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -834,8 +841,32 @@ impl api::IncomingWebhook for Rapyd {
|
||||
.body
|
||||
.parse_struct("RapydIncomingWebhook")
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
|
||||
webhook.webhook_type.try_into()
|
||||
Ok(match webhook.webhook_type {
|
||||
rapyd::RapydWebhookObjectEventType::PaymentCompleted
|
||||
| rapyd::RapydWebhookObjectEventType::PaymentCaptured => {
|
||||
api::IncomingWebhookEvent::PaymentIntentSuccess
|
||||
}
|
||||
rapyd::RapydWebhookObjectEventType::PaymentFailed => {
|
||||
api::IncomingWebhookEvent::PaymentIntentFailure
|
||||
}
|
||||
rapyd::RapydWebhookObjectEventType::PaymentRefundFailed
|
||||
| rapyd::RapydWebhookObjectEventType::PaymentRefundRejected => {
|
||||
api::IncomingWebhookEvent::RefundFailure
|
||||
}
|
||||
rapyd::RapydWebhookObjectEventType::RefundCompleted => {
|
||||
api::IncomingWebhookEvent::RefundSuccess
|
||||
}
|
||||
rapyd::RapydWebhookObjectEventType::PaymentDisputeCreated => {
|
||||
api::IncomingWebhookEvent::DisputeOpened
|
||||
}
|
||||
rapyd::RapydWebhookObjectEventType::Unknown => {
|
||||
api::IncomingWebhookEvent::EventNotSupported
|
||||
}
|
||||
rapyd::RapydWebhookObjectEventType::PaymentDisputeUpdated => match webhook.data {
|
||||
rapyd::WebhookData::Dispute(data) => api::IncomingWebhookEvent::from(data.status),
|
||||
_ => api::IncomingWebhookEvent::EventNotSupported,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
@ -846,17 +877,50 @@ impl api::IncomingWebhook for Rapyd {
|
||||
.body
|
||||
.parse_struct("RapydIncomingWebhook")
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
let response = match webhook.data {
|
||||
transformers::WebhookData::PaymentData(payment_data) => {
|
||||
let res_json = match webhook.data {
|
||||
transformers::WebhookData::Payment(payment_data) => {
|
||||
let rapyd_response: transformers::RapydPaymentsResponse = payment_data.into();
|
||||
Ok(rapyd_response)
|
||||
}
|
||||
_ => Err(errors::ConnectorError::WebhookEventTypeNotFound),
|
||||
}?;
|
||||
let res_json =
|
||||
utils::Encode::<transformers::RapydPaymentsResponse>::encode_to_value(&response)
|
||||
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
|
||||
|
||||
utils::Encode::<transformers::RapydPaymentsResponse>::encode_to_value(
|
||||
&rapyd_response,
|
||||
)
|
||||
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?
|
||||
}
|
||||
transformers::WebhookData::Refund(refund_data) => {
|
||||
utils::Encode::<transformers::RefundResponseData>::encode_to_value(&refund_data)
|
||||
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?
|
||||
}
|
||||
transformers::WebhookData::Dispute(dispute_data) => {
|
||||
utils::Encode::<transformers::DisputeResponseData>::encode_to_value(&dispute_data)
|
||||
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?
|
||||
}
|
||||
};
|
||||
Ok(res_json)
|
||||
}
|
||||
|
||||
fn get_dispute_details(
|
||||
&self,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::disputes::DisputePayload, errors::ConnectorError> {
|
||||
let webhook: transformers::RapydIncomingWebhook = request
|
||||
.body
|
||||
.parse_struct("RapydIncomingWebhook")
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
let webhook_dispute_data = match webhook.data {
|
||||
transformers::WebhookData::Dispute(dispute_data) => Ok(dispute_data),
|
||||
_ => Err(errors::ConnectorError::WebhookBodyDecodingFailed),
|
||||
}?;
|
||||
Ok(api::disputes::DisputePayload {
|
||||
amount: webhook_dispute_data.amount.to_string(),
|
||||
currency: webhook_dispute_data.currency.to_string(),
|
||||
dispute_stage: api_models::enums::DisputeStage::Dispute,
|
||||
connector_dispute_id: webhook_dispute_data.token,
|
||||
connector_reason: Some(webhook_dispute_data.dispute_reason_description),
|
||||
connector_reason_code: None,
|
||||
challenge_required_by: webhook_dispute_data.due_date,
|
||||
connector_status: webhook_dispute_data.status.to_string(),
|
||||
created_at: webhook_dispute_data.created_at,
|
||||
updated_at: webhook_dispute_data.updated_at,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::PrimitiveDateTime;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
@ -245,6 +246,23 @@ pub struct ResponseData {
|
||||
pub failure_message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DisputeResponseData {
|
||||
pub id: String,
|
||||
pub amount: i64,
|
||||
pub currency: api_models::enums::Currency,
|
||||
pub token: String,
|
||||
pub dispute_reason_description: String,
|
||||
#[serde(default, with = "common_utils::custom_serde::timestamp::option")]
|
||||
pub due_date: Option<PrimitiveDateTime>,
|
||||
pub status: RapydWebhookDisputeStatus,
|
||||
#[serde(default, with = "common_utils::custom_serde::timestamp::option")]
|
||||
pub created_at: Option<PrimitiveDateTime>,
|
||||
#[serde(default, with = "common_utils::custom_serde::timestamp::option")]
|
||||
pub updated_at: Option<PrimitiveDateTime>,
|
||||
pub original_transaction_id: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
pub struct RapydRefundRequest {
|
||||
pub payment: String,
|
||||
@ -463,21 +481,34 @@ pub enum RapydWebhookObjectEventType {
|
||||
RefundCompleted,
|
||||
PaymentRefundRejected,
|
||||
PaymentRefundFailed,
|
||||
PaymentDisputeCreated,
|
||||
PaymentDisputeUpdated,
|
||||
#[serde(other)]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl TryFrom<RapydWebhookObjectEventType> for api::IncomingWebhookEvent {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(value: RapydWebhookObjectEventType) -> Result<Self, Self::Error> {
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, strum::Display)]
|
||||
pub enum RapydWebhookDisputeStatus {
|
||||
#[serde(rename = "ACT")]
|
||||
Active,
|
||||
#[serde(rename = "RVW")]
|
||||
Review,
|
||||
#[serde(rename = "LOS")]
|
||||
Lose,
|
||||
#[serde(rename = "WIN")]
|
||||
Win,
|
||||
#[serde(other)]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<RapydWebhookDisputeStatus> for api_models::webhooks::IncomingWebhookEvent {
|
||||
fn from(value: RapydWebhookDisputeStatus) -> Self {
|
||||
match value {
|
||||
RapydWebhookObjectEventType::PaymentCompleted => Ok(Self::PaymentIntentSuccess),
|
||||
RapydWebhookObjectEventType::PaymentCaptured => Ok(Self::PaymentIntentSuccess),
|
||||
RapydWebhookObjectEventType::PaymentFailed => Ok(Self::PaymentIntentFailure),
|
||||
RapydWebhookObjectEventType::Unknown
|
||||
| RapydWebhookObjectEventType::RefundCompleted
|
||||
| RapydWebhookObjectEventType::PaymentRefundRejected
|
||||
| RapydWebhookObjectEventType::PaymentRefundFailed => Ok(Self::EventNotSupported),
|
||||
RapydWebhookDisputeStatus::Active => Self::DisputeOpened,
|
||||
RapydWebhookDisputeStatus::Review => Self::DisputeChallenged,
|
||||
RapydWebhookDisputeStatus::Lose => Self::DisputeLost,
|
||||
RapydWebhookDisputeStatus::Win => Self::DisputeWon,
|
||||
RapydWebhookDisputeStatus::Unknown => Self::EventNotSupported,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -485,8 +516,9 @@ impl TryFrom<RapydWebhookObjectEventType> for api::IncomingWebhookEvent {
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum WebhookData {
|
||||
PaymentData(ResponseData),
|
||||
RefundData(RefundResponseData),
|
||||
Payment(ResponseData),
|
||||
Refund(RefundResponseData),
|
||||
Dispute(DisputeResponseData),
|
||||
}
|
||||
|
||||
impl From<ResponseData> for RapydPaymentsResponse {
|
||||
|
||||
Reference in New Issue
Block a user