mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 12:06:56 +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 action_id: Option<String>,
|
||||||
pub amount: i32,
|
pub amount: i32,
|
||||||
pub currency: String,
|
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 evidence_required_by: Option<PrimitiveDateTime>,
|
||||||
pub reason_code: Option<String>,
|
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>,
|
pub date: Option<PrimitiveDateTime>,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
@ -768,7 +768,7 @@ pub struct CheckoutDisputeWebhookBody {
|
|||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub transaction_type: CheckoutTransactionType,
|
pub transaction_type: CheckoutTransactionType,
|
||||||
pub data: CheckoutDisputeWebhookData,
|
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>,
|
pub created_on: Option<PrimitiveDateTime>,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Deserialize, strum::Display, Clone)]
|
#[derive(Debug, Deserialize, strum::Display, Clone)]
|
||||||
|
|||||||
@ -813,16 +813,23 @@ impl api::IncomingWebhook for Rapyd {
|
|||||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||||
|
|
||||||
Ok(match webhook.data {
|
Ok(match webhook.data {
|
||||||
transformers::WebhookData::PaymentData(payment_data) => {
|
transformers::WebhookData::Payment(payment_data) => {
|
||||||
api_models::webhooks::ObjectReferenceId::PaymentId(
|
api_models::webhooks::ObjectReferenceId::PaymentId(
|
||||||
api_models::payments::PaymentIdType::ConnectorTransactionId(payment_data.id),
|
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::ObjectReferenceId::RefundId(
|
||||||
api_models::webhooks::RefundIdType::ConnectorRefundId(refund_data.id),
|
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
|
.body
|
||||||
.parse_struct("RapydIncomingWebhook")
|
.parse_struct("RapydIncomingWebhook")
|
||||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||||
|
Ok(match webhook.webhook_type {
|
||||||
webhook.webhook_type.try_into()
|
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(
|
fn get_webhook_resource_object(
|
||||||
@ -846,17 +877,50 @@ impl api::IncomingWebhook for Rapyd {
|
|||||||
.body
|
.body
|
||||||
.parse_struct("RapydIncomingWebhook")
|
.parse_struct("RapydIncomingWebhook")
|
||||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||||
let response = match webhook.data {
|
let res_json = match webhook.data {
|
||||||
transformers::WebhookData::PaymentData(payment_data) => {
|
transformers::WebhookData::Payment(payment_data) => {
|
||||||
let rapyd_response: transformers::RapydPaymentsResponse = payment_data.into();
|
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)
|
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 error_stack::{IntoReport, ResultExt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use time::PrimitiveDateTime;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -245,6 +246,23 @@ pub struct ResponseData {
|
|||||||
pub failure_message: Option<String>,
|
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)]
|
#[derive(Default, Debug, Serialize)]
|
||||||
pub struct RapydRefundRequest {
|
pub struct RapydRefundRequest {
|
||||||
pub payment: String,
|
pub payment: String,
|
||||||
@ -463,21 +481,34 @@ pub enum RapydWebhookObjectEventType {
|
|||||||
RefundCompleted,
|
RefundCompleted,
|
||||||
PaymentRefundRejected,
|
PaymentRefundRejected,
|
||||||
PaymentRefundFailed,
|
PaymentRefundFailed,
|
||||||
|
PaymentDisputeCreated,
|
||||||
|
PaymentDisputeUpdated,
|
||||||
#[serde(other)]
|
#[serde(other)]
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<RapydWebhookObjectEventType> for api::IncomingWebhookEvent {
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, strum::Display)]
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
pub enum RapydWebhookDisputeStatus {
|
||||||
fn try_from(value: RapydWebhookObjectEventType) -> Result<Self, Self::Error> {
|
#[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 {
|
match value {
|
||||||
RapydWebhookObjectEventType::PaymentCompleted => Ok(Self::PaymentIntentSuccess),
|
RapydWebhookDisputeStatus::Active => Self::DisputeOpened,
|
||||||
RapydWebhookObjectEventType::PaymentCaptured => Ok(Self::PaymentIntentSuccess),
|
RapydWebhookDisputeStatus::Review => Self::DisputeChallenged,
|
||||||
RapydWebhookObjectEventType::PaymentFailed => Ok(Self::PaymentIntentFailure),
|
RapydWebhookDisputeStatus::Lose => Self::DisputeLost,
|
||||||
RapydWebhookObjectEventType::Unknown
|
RapydWebhookDisputeStatus::Win => Self::DisputeWon,
|
||||||
| RapydWebhookObjectEventType::RefundCompleted
|
RapydWebhookDisputeStatus::Unknown => Self::EventNotSupported,
|
||||||
| RapydWebhookObjectEventType::PaymentRefundRejected
|
|
||||||
| RapydWebhookObjectEventType::PaymentRefundFailed => Ok(Self::EventNotSupported),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -485,8 +516,9 @@ impl TryFrom<RapydWebhookObjectEventType> for api::IncomingWebhookEvent {
|
|||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum WebhookData {
|
pub enum WebhookData {
|
||||||
PaymentData(ResponseData),
|
Payment(ResponseData),
|
||||||
RefundData(RefundResponseData),
|
Refund(RefundResponseData),
|
||||||
|
Dispute(DisputeResponseData),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ResponseData> for RapydPaymentsResponse {
|
impl From<ResponseData> for RapydPaymentsResponse {
|
||||||
|
|||||||
Reference in New Issue
Block a user