feat(connector): Add refund and dispute webhooks for Rapyd (#1313)

This commit is contained in:
Shankar Singh C
2023-06-28 19:37:17 +05:30
committed by GitHub
parent 9ea9e5523b
commit db011f3d76
3 changed files with 124 additions and 28 deletions

View File

@ -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)]

View File

@ -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,
})
}
}

View File

@ -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 {