From 9ba7a50c7082639836f47f1da2334d854d324253 Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Mon, 17 Apr 2023 11:08:48 +0530 Subject: [PATCH 1/8] add disputes and refund for rapyd --- crates/router/src/connector/rapyd.rs | 35 +++++++++++++++++++ .../src/connector/rapyd/transformers.rs | 28 +++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/crates/router/src/connector/rapyd.rs b/crates/router/src/connector/rapyd.rs index 05c28efba53..277ef7d9b15 100644 --- a/crates/router/src/connector/rapyd.rs +++ b/crates/router/src/connector/rapyd.rs @@ -791,6 +791,11 @@ impl api::IncomingWebhook for Rapyd { api_models::webhooks::RefundIdType::ConnectorRefundId(refund_data.id), ) } + transformers::WebhookData::DisputeData(dispute_data) => { + api_models::webhooks::ObjectReferenceId::PaymentId( + api_models::payments::PaymentIdType::ConnectorTransactionId(dispute_data.id), + ) + } }) } @@ -819,6 +824,14 @@ impl api::IncomingWebhook for Rapyd { let rapyd_response: transformers::RapydPaymentsResponse = payment_data.into(); Ok(rapyd_response) } + // transformers::WebhookData::RefundData(refund_data) => { + // let rapyd_response: transformers::RapydPaymentsResponse = refund_data.into(); + // Ok(rapyd_response) + // } + // transformers::WebhookData::DisputeData(dispute_data) => { + // let rapyd_response: transformers::RapydPaymentsResponse = dispute_data.into(); + // Ok(rapyd_response) + // } _ => Err(errors::ConnectorError::WebhookEventTypeNotFound), }?; let res_json = @@ -827,4 +840,26 @@ impl api::IncomingWebhook for Rapyd { Ok(res_json) } + + fn get_dispute_details( + &self, + request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + let webhook: transformers::DesputeResponseData = request + .body + .parse_struct("RapydIncomingWebhook") + .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?; + Ok(api::disputes::DisputePayload { + amount: webhook.amount.to_string(), + currency: webhook.currency, + dispute_stage: api_models::enums::DisputeStage::Dispute, + connector_dispute_id: webhook.token, + connector_reason: Some(webhook.dispute_reason_description), + connector_reason_code: None, + challenge_required_by: webhook.due_date, + connector_status: webhook.status, + created_at: Some(webhook.created_at), + updated_at: Some(webhook.updated_at), + }) + } } diff --git a/crates/router/src/connector/rapyd/transformers.rs b/crates/router/src/connector/rapyd/transformers.rs index 4e9de6121bc..c76d5110c53 100644 --- a/crates/router/src/connector/rapyd/transformers.rs +++ b/crates/router/src/connector/rapyd/transformers.rs @@ -237,6 +237,21 @@ pub struct ResponseData { pub failure_message: Option, } +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct DesputeResponseData { + pub id: String, + pub amount: i64, + pub currency: String, + //Stage is None + pub token: String, + pub dispute_reason_description: String, + //Reason code is None + pub due_date: i64, //its in timestamp requires conversion + pub status: String, + pub created_at: String, + pub updated_at: String, +} + #[derive(Default, Debug, Serialize)] pub struct RapydRefundRequest { pub payment: String, @@ -446,6 +461,16 @@ pub enum RapydWebhookObjectEventType { RefundCompleted, PaymentRefundRejected, PaymentRefundFailed, + PaymentDisputeCreated, //DisputeOpened + PaymentDisputeUpdated, +} + +#[derive(Debug, Deserialize)] +pub enum RapydWebhookDisputeStatus { + ACT, //Wen dont have + RVW, //DisputeChallenged + LOS, //DisputeLost + WIN, //DisputeWon } impl TryFrom for api::IncomingWebhookEvent { @@ -455,6 +480,8 @@ impl TryFrom for api::IncomingWebhookEvent { RapydWebhookObjectEventType::PaymentCompleted => Ok(Self::PaymentIntentSuccess), RapydWebhookObjectEventType::PaymentCaptured => Ok(Self::PaymentIntentSuccess), RapydWebhookObjectEventType::PaymentFailed => Ok(Self::PaymentIntentFailure), + RapydWebhookObjectEventType::PaymentRefundFailed => Ok(Self::RefundFailure), + RapydWebhookObjectEventType::RefundCompleted => Ok(Self::RefundSuccess), _ => Err(errors::ConnectorError::WebhookEventTypeNotFound).into_report()?, } } @@ -465,6 +492,7 @@ impl TryFrom for api::IncomingWebhookEvent { pub enum WebhookData { PaymentData(ResponseData), RefundData(RefundResponseData), + DisputeData(DesputeResponseData), } impl From for RapydPaymentsResponse { From b64af15a0eca4327c05965de1e5fa4cb58fd3e43 Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Mon, 22 May 2023 11:15:47 +0530 Subject: [PATCH 2/8] dispute webhooks code changes --- crates/router/src/connector/rapyd.rs | 25 ++++++++-- .../src/connector/rapyd/transformers.rs | 49 ++++++++++++------- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/crates/router/src/connector/rapyd.rs b/crates/router/src/connector/rapyd.rs index 277ef7d9b15..99669ce8c03 100644 --- a/crates/router/src/connector/rapyd.rs +++ b/crates/router/src/connector/rapyd.rs @@ -1,9 +1,11 @@ mod transformers; use std::fmt::Debug; +use api_models::webhooks; use base64::Engine; use common_utils::{date_time, ext_traits::StringExt}; use error_stack::{IntoReport, ResultExt}; +use http::request; use rand::distributions::{Alphanumeric, DistString}; use ring::hmac; use transformers as rapyd; @@ -807,8 +809,23 @@ 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 => api::IncomingWebhookEvent::PaymentIntentFailure, + rapyd::RapydWebhookObjectEventType::PaymentCaptured => api::IncomingWebhookEvent::PaymentIntentSuccess, + rapyd::RapydWebhookObjectEventType::PaymentFailed => api::IncomingWebhookEvent::PaymentIntentFailure, + rapyd::RapydWebhookObjectEventType::PaymentRefundFailed => api::IncomingWebhookEvent::RefundFailure, + rapyd::RapydWebhookObjectEventType::RefundCompleted => api::IncomingWebhookEvent::RefundSuccess, + rapyd::RapydWebhookObjectEventType::PaymentDisputeCreated => api::IncomingWebhookEvent::DisputeOpened, + rapyd::RapydWebhookObjectEventType::PaymentDisputeUpdated => { + match webhook.data { + rapyd::WebhookData::DisputeData(data ) => { + api::IncomingWebhookEvent::try_from(data.status)? + }, + _ => Err(errors::ConnectorError::WebhookEventTypeNotFound)?, + } + }, + _ => Err(errors::ConnectorError::WebhookEventTypeNotFound).into_report()?, + }) } fn get_webhook_resource_object( @@ -856,8 +873,8 @@ impl api::IncomingWebhook for Rapyd { connector_dispute_id: webhook.token, connector_reason: Some(webhook.dispute_reason_description), connector_reason_code: None, - challenge_required_by: webhook.due_date, - connector_status: webhook.status, + challenge_required_by: Some(webhook.due_date.to_string()), + connector_status: webhook.status.to_string(), created_at: Some(webhook.created_at), updated_at: Some(webhook.updated_at), }) diff --git a/crates/router/src/connector/rapyd/transformers.rs b/crates/router/src/connector/rapyd/transformers.rs index c76d5110c53..f8d8e0bf01e 100644 --- a/crates/router/src/connector/rapyd/transformers.rs +++ b/crates/router/src/connector/rapyd/transformers.rs @@ -237,17 +237,15 @@ pub struct ResponseData { pub failure_message: Option, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct DesputeResponseData { pub id: String, pub amount: i64, pub currency: String, - //Stage is None pub token: String, pub dispute_reason_description: String, - //Reason code is None - pub due_date: i64, //its in timestamp requires conversion - pub status: String, + pub due_date: i64, + pub status: RapydWebhookDisputeStatus, pub created_at: String, pub updated_at: String, } @@ -465,25 +463,40 @@ pub enum RapydWebhookObjectEventType { PaymentDisputeUpdated, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Clone, Deserialize, PartialEq, strum::Display)] pub enum RapydWebhookDisputeStatus { - ACT, //Wen dont have + ACT, //DisputeOpened RVW, //DisputeChallenged LOS, //DisputeLost WIN, //DisputeWon } -impl TryFrom for api::IncomingWebhookEvent { - type Error = error_stack::Report; - fn try_from(value: RapydWebhookObjectEventType) -> Result { - match value { - RapydWebhookObjectEventType::PaymentCompleted => Ok(Self::PaymentIntentSuccess), - RapydWebhookObjectEventType::PaymentCaptured => Ok(Self::PaymentIntentSuccess), - RapydWebhookObjectEventType::PaymentFailed => Ok(Self::PaymentIntentFailure), - RapydWebhookObjectEventType::PaymentRefundFailed => Ok(Self::RefundFailure), - RapydWebhookObjectEventType::RefundCompleted => Ok(Self::RefundSuccess), - _ => Err(errors::ConnectorError::WebhookEventTypeNotFound).into_report()?, - } +// impl TryFrom for api::IncomingWebhookEvent { +// type Error = error_stack::Report; +// fn try_from(value: RapydWebhookObjectEventType) -> Result { +// match value { +// RapydWebhookObjectEventType::PaymentCompleted => Ok(Self::PaymentIntentSuccess), +// RapydWebhookObjectEventType::PaymentCaptured => Ok(Self::PaymentIntentSuccess), +// RapydWebhookObjectEventType::PaymentFailed => Ok(Self::PaymentIntentFailure), +// RapydWebhookObjectEventType::PaymentRefundFailed => Ok(Self::RefundFailure), +// RapydWebhookObjectEventType::RefundCompleted => Ok(Self::RefundSuccess), +// RapydWebhookObjectEventType::PaymentDisputeCreated => Ok(Self::DisputeOpened), +// RapydWebhookObjectEventType::PaymentDisputeUpdated => {} +// _ => Err(errors::ConnectorError::WebhookEventTypeNotFound).into_report()?, +// } +// } +// } + +impl TryFrom for api_models::webhooks::IncomingWebhookEvent { + type Error = errors::ConnectorError; + fn try_from(value: RapydWebhookDisputeStatus) -> Result { + Ok(match value { + RapydWebhookDisputeStatus::ACT => Self::DisputeOpened, + RapydWebhookDisputeStatus::RVW => Self::DisputeChallenged, + RapydWebhookDisputeStatus::LOS => Self::DisputeLost, + RapydWebhookDisputeStatus::WIN => Self::DisputeWon, + _ => Err(errors::ConnectorError::WebhookEventTypeNotFound)?, + }) } } From fa4ccc8c3eb368b1fdcd7e90b92f26e795d20398 Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Mon, 22 May 2023 20:18:54 +0530 Subject: [PATCH 3/8] fix webhook status mapping --- crates/router/src/connector/rapyd.rs | 14 +--- .../src/connector/rapyd/transformers.rs | 74 +++++++++---------- 2 files changed, 39 insertions(+), 49 deletions(-) diff --git a/crates/router/src/connector/rapyd.rs b/crates/router/src/connector/rapyd.rs index 99669ce8c03..0a7bfd15f5d 100644 --- a/crates/router/src/connector/rapyd.rs +++ b/crates/router/src/connector/rapyd.rs @@ -1,11 +1,11 @@ mod transformers; use std::fmt::Debug; -use api_models::webhooks; + use base64::Engine; use common_utils::{date_time, ext_traits::StringExt}; use error_stack::{IntoReport, ResultExt}; -use http::request; + use rand::distributions::{Alphanumeric, DistString}; use ring::hmac; use transformers as rapyd; @@ -841,14 +841,6 @@ impl api::IncomingWebhook for Rapyd { let rapyd_response: transformers::RapydPaymentsResponse = payment_data.into(); Ok(rapyd_response) } - // transformers::WebhookData::RefundData(refund_data) => { - // let rapyd_response: transformers::RapydPaymentsResponse = refund_data.into(); - // Ok(rapyd_response) - // } - // transformers::WebhookData::DisputeData(dispute_data) => { - // let rapyd_response: transformers::RapydPaymentsResponse = dispute_data.into(); - // Ok(rapyd_response) - // } _ => Err(errors::ConnectorError::WebhookEventTypeNotFound), }?; let res_json = @@ -873,7 +865,7 @@ impl api::IncomingWebhook for Rapyd { connector_dispute_id: webhook.token, connector_reason: Some(webhook.dispute_reason_description), connector_reason_code: None, - challenge_required_by: Some(webhook.due_date.to_string()), + challenge_required_by: Some(webhook.due_date), connector_status: webhook.status.to_string(), created_at: Some(webhook.created_at), updated_at: Some(webhook.updated_at), diff --git a/crates/router/src/connector/rapyd/transformers.rs b/crates/router/src/connector/rapyd/transformers.rs index 06038af6113..840bde75361 100644 --- a/crates/router/src/connector/rapyd/transformers.rs +++ b/crates/router/src/connector/rapyd/transformers.rs @@ -1,5 +1,6 @@ -use error_stack::{IntoReport, ResultExt}; +use error_stack::{ResultExt, IntoReport}; use serde::{Deserialize, Serialize}; +use time::PrimitiveDateTime; use url::Url; use crate::{ @@ -192,6 +193,8 @@ impl ForeignFrom<(RapydPaymentStatus, NextAction)> for enums::AttemptStatus { ) => Self::Voided, (RapydPaymentStatus::Error, _) => Self::Failure, (RapydPaymentStatus::New, _) => Self::Authorizing, + (RapydPaymentStatus::Active, NextAction::NotApplicable) => todo!(), + (RapydPaymentStatus::Active, NextAction::PendingConfirmation) => todo!(), } } } @@ -217,6 +220,10 @@ pub enum NextAction { ThreedsVerification, #[serde(rename = "pending_capture")] PendingCapture, + #[serde(rename = "not_applicable")] + NotApplicable, + #[serde(rename = "pending_confirmation")] + PendingConfirmation, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -225,7 +232,7 @@ pub struct ResponseData { pub amount: i64, pub status: RapydPaymentStatus, pub next_action: NextAction, - pub redirect_url: Option, + pub redirect_url: Option, pub original_amount: Option, pub is_partial: Option, pub currency_code: Option, @@ -244,10 +251,13 @@ pub struct DesputeResponseData { pub currency: String, pub token: String, pub dispute_reason_description: String, - pub due_date: i64, + #[serde(with = "common_utils::custom_serde::timestamp")] + pub due_date: PrimitiveDateTime, pub status: RapydWebhookDisputeStatus, - pub created_at: String, - pub updated_at: String, + #[serde(with = "common_utils::custom_serde::timestamp")] + pub created_at: PrimitiveDateTime, + #[serde(with = "common_utils::custom_serde::timestamp")] + pub updated_at: PrimitiveDateTime, } #[derive(Default, Debug, Serialize)] @@ -400,12 +410,16 @@ impl }), ), _ => { - let redirection_data = data.redirect_url.as_ref().map(|redirect_url| { - services::RedirectForm::from(( - redirect_url.to_owned(), - services::Method::Get, - )) - }); + let redirection_dat = if !data.redirect_url.as_ref().unwrap().is_empty() { + data.redirect_url.as_ref().map(|url| { + Url::parse(url).into_report().change_context(errors::ConnectorError::FailedToObtainIntegrationUrl) + }).transpose()? + } else { + None + }; + + let redirection_data = redirection_dat.map(|url| services::RedirectForm::from((url, services::Method::Get))); + ( attempt_status, Ok(types::PaymentsResponseData::TransactionResponse { @@ -460,43 +474,27 @@ pub enum RapydWebhookObjectEventType { RefundCompleted, PaymentRefundRejected, PaymentRefundFailed, - PaymentDisputeCreated, //DisputeOpened + PaymentDisputeCreated, PaymentDisputeUpdated, } #[derive(Debug, Clone, Deserialize, PartialEq, strum::Display)] +#[serde(rename_all = "UPPERCASE")] pub enum RapydWebhookDisputeStatus { - ACT, //DisputeOpened - RVW, //DisputeChallenged - LOS, //DisputeLost - WIN, //DisputeWon -} - -// impl TryFrom for api::IncomingWebhookEvent { -// type Error = error_stack::Report; -// fn try_from(value: RapydWebhookObjectEventType) -> Result { -// match value { -// RapydWebhookObjectEventType::PaymentCompleted => Ok(Self::PaymentIntentSuccess), -// RapydWebhookObjectEventType::PaymentCaptured => Ok(Self::PaymentIntentSuccess), -// RapydWebhookObjectEventType::PaymentFailed => Ok(Self::PaymentIntentFailure), -// RapydWebhookObjectEventType::PaymentRefundFailed => Ok(Self::RefundFailure), -// RapydWebhookObjectEventType::RefundCompleted => Ok(Self::RefundSuccess), -// RapydWebhookObjectEventType::PaymentDisputeCreated => Ok(Self::DisputeOpened), -// RapydWebhookObjectEventType::PaymentDisputeUpdated => {} -// _ => Err(errors::ConnectorError::WebhookEventTypeNotFound).into_report()?, -// } -// } -// } + Act, + Rvw, + Los, + Win, +} impl TryFrom for api_models::webhooks::IncomingWebhookEvent { type Error = errors::ConnectorError; fn try_from(value: RapydWebhookDisputeStatus) -> Result { Ok(match value { - RapydWebhookDisputeStatus::ACT => Self::DisputeOpened, - RapydWebhookDisputeStatus::RVW => Self::DisputeChallenged, - RapydWebhookDisputeStatus::LOS => Self::DisputeLost, - RapydWebhookDisputeStatus::WIN => Self::DisputeWon, - _ => Err(errors::ConnectorError::WebhookEventTypeNotFound)?, + RapydWebhookDisputeStatus::Act => Self::DisputeOpened, + RapydWebhookDisputeStatus::Rvw => Self::DisputeChallenged, + RapydWebhookDisputeStatus::Los => Self::DisputeLost, + RapydWebhookDisputeStatus::Win => Self::DisputeWon, }) } } From e3cb959175de5d36dbbd11435b9571a88e4f9b61 Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Tue, 30 May 2023 11:39:33 +0530 Subject: [PATCH 4/8] add disputes and refund webhook for rapyd --- crates/router/src/connector/rapyd.rs | 95 +++++++++++-------- .../src/connector/rapyd/transformers.rs | 66 +++++++------ 2 files changed, 94 insertions(+), 67 deletions(-) diff --git a/crates/router/src/connector/rapyd.rs b/crates/router/src/connector/rapyd.rs index 0a7bfd15f5d..2d87a7eb2c8 100644 --- a/crates/router/src/connector/rapyd.rs +++ b/crates/router/src/connector/rapyd.rs @@ -1,11 +1,9 @@ mod transformers; use std::fmt::Debug; - use base64::Engine; use common_utils::{date_time, ext_traits::StringExt}; use error_stack::{IntoReport, ResultExt}; - use rand::distributions::{Alphanumeric, DistString}; use ring::hmac; use transformers as rapyd; @@ -783,19 +781,21 @@ 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::DisputeData(dispute_data) => { + transformers::WebhookData::Dispute(dispute_data) => { api_models::webhooks::ObjectReferenceId::PaymentId( - api_models::payments::PaymentIdType::ConnectorTransactionId(dispute_data.id), + api_models::payments::PaymentIdType::ConnectorTransactionId( + dispute_data.original_transaction_id, + ), ) } }) @@ -810,21 +810,31 @@ impl api::IncomingWebhook for Rapyd { .parse_struct("RapydIncomingWebhook") .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?; Ok(match webhook.webhook_type { - rapyd::RapydWebhookObjectEventType::PaymentCompleted => api::IncomingWebhookEvent::PaymentIntentFailure, - rapyd::RapydWebhookObjectEventType::PaymentCaptured => api::IncomingWebhookEvent::PaymentIntentSuccess, - rapyd::RapydWebhookObjectEventType::PaymentFailed => api::IncomingWebhookEvent::PaymentIntentFailure, - rapyd::RapydWebhookObjectEventType::PaymentRefundFailed => api::IncomingWebhookEvent::RefundFailure, - rapyd::RapydWebhookObjectEventType::RefundCompleted => api::IncomingWebhookEvent::RefundSuccess, - rapyd::RapydWebhookObjectEventType::PaymentDisputeCreated => api::IncomingWebhookEvent::DisputeOpened, - rapyd::RapydWebhookObjectEventType::PaymentDisputeUpdated => { - match webhook.data { - rapyd::WebhookData::DisputeData(data ) => { - api::IncomingWebhookEvent::try_from(data.status)? - }, - _ => Err(errors::ConnectorError::WebhookEventTypeNotFound)?, + rapyd::RapydWebhookObjectEventType::PaymentCompleted => { + api::IncomingWebhookEvent::PaymentIntentFailure + } + rapyd::RapydWebhookObjectEventType::PaymentCaptured => { + api::IncomingWebhookEvent::PaymentIntentSuccess + } + rapyd::RapydWebhookObjectEventType::PaymentFailed => { + api::IncomingWebhookEvent::PaymentIntentFailure + } + rapyd::RapydWebhookObjectEventType::PaymentRefundFailed => { + api::IncomingWebhookEvent::RefundFailure + } + rapyd::RapydWebhookObjectEventType::RefundCompleted => { + api::IncomingWebhookEvent::RefundSuccess + } + rapyd::RapydWebhookObjectEventType::PaymentDisputeCreated => { + api::IncomingWebhookEvent::DisputeOpened + } + rapyd::RapydWebhookObjectEventType::PaymentDisputeUpdated => match webhook.data { + rapyd::WebhookData::Dispute(data) => { + api::IncomingWebhookEvent::try_from(data.status)? } + _ => Err(errors::ConnectorError::WebhookEventTypeNotFound)?, }, - _ => Err(errors::ConnectorError::WebhookEventTypeNotFound).into_report()?, + _ => Err(errors::ConnectorError::WebhookEventTypeNotFound).into_report()?, }) } @@ -836,17 +846,24 @@ 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::::encode_to_value(&response) - .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?; + utils::Encode::::encode_to_value( + &rapyd_response, + ) + .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)? + } + transformers::WebhookData::Refund(refund_data) => { + utils::Encode::::encode_to_value(&refund_data) + .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)? + } + transformers::WebhookData::Dispute(dispute_data) => { + utils::Encode::::encode_to_value(&dispute_data) + .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)? + } + }; Ok(res_json) } @@ -854,21 +871,25 @@ impl api::IncomingWebhook for Rapyd { &self, request: &api::IncomingWebhookRequestDetails<'_>, ) -> CustomResult { - let webhook: transformers::DesputeResponseData = request + 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::WebhookEventTypeNotFound), + }?; Ok(api::disputes::DisputePayload { - amount: webhook.amount.to_string(), - currency: webhook.currency, + amount: webhook_dispute_data.amount.to_string(), + currency: webhook_dispute_data.currency, dispute_stage: api_models::enums::DisputeStage::Dispute, - connector_dispute_id: webhook.token, - connector_reason: Some(webhook.dispute_reason_description), + connector_dispute_id: webhook_dispute_data.token, + connector_reason: Some(webhook_dispute_data.dispute_reason_description), connector_reason_code: None, - challenge_required_by: Some(webhook.due_date), - connector_status: webhook.status.to_string(), - created_at: Some(webhook.created_at), - updated_at: Some(webhook.updated_at), + challenge_required_by: Some(webhook_dispute_data.due_date), + connector_status: webhook_dispute_data.status.to_string(), + created_at: Some(webhook_dispute_data.created_at), + updated_at: Some(webhook_dispute_data.updated_at), }) } } diff --git a/crates/router/src/connector/rapyd/transformers.rs b/crates/router/src/connector/rapyd/transformers.rs index 840bde75361..5addbcf0d73 100644 --- a/crates/router/src/connector/rapyd/transformers.rs +++ b/crates/router/src/connector/rapyd/transformers.rs @@ -1,4 +1,4 @@ -use error_stack::{ResultExt, IntoReport}; +use error_stack::{IntoReport, ResultExt}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; use url::Url; @@ -181,10 +181,14 @@ impl ForeignFrom<(RapydPaymentStatus, NextAction)> for enums::AttemptStatus { let (status, next_action) = item; match (status, next_action) { (RapydPaymentStatus::Closed, _) => Self::Charged, - (RapydPaymentStatus::Active, NextAction::ThreedsVerification) => { - Self::AuthenticationPending - } - (RapydPaymentStatus::Active, NextAction::PendingCapture) => Self::Authorized, + ( + RapydPaymentStatus::Active, + NextAction::ThreedsVerification | NextAction::PendingConfirmation, + ) => Self::AuthenticationPending, + ( + RapydPaymentStatus::Active, + NextAction::PendingCapture | NextAction::NotApplicable, + ) => Self::Authorized, ( RapydPaymentStatus::CanceledByClientOrBank | RapydPaymentStatus::Expired @@ -193,8 +197,6 @@ impl ForeignFrom<(RapydPaymentStatus, NextAction)> for enums::AttemptStatus { ) => Self::Voided, (RapydPaymentStatus::Error, _) => Self::Failure, (RapydPaymentStatus::New, _) => Self::Authorizing, - (RapydPaymentStatus::Active, NextAction::NotApplicable) => todo!(), - (RapydPaymentStatus::Active, NextAction::PendingConfirmation) => todo!(), } } } @@ -244,20 +246,21 @@ pub struct ResponseData { pub failure_message: Option, } -#[derive(Debug, Clone, Deserialize, PartialEq)] -pub struct DesputeResponseData { +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct DisputeResponseData { pub id: String, pub amount: i64, pub currency: String, pub token: String, pub dispute_reason_description: String, #[serde(with = "common_utils::custom_serde::timestamp")] - pub due_date: PrimitiveDateTime, + pub due_date: PrimitiveDateTime, pub status: RapydWebhookDisputeStatus, #[serde(with = "common_utils::custom_serde::timestamp")] pub created_at: PrimitiveDateTime, #[serde(with = "common_utils::custom_serde::timestamp")] pub updated_at: PrimitiveDateTime, + pub original_transaction_id: String, } #[derive(Default, Debug, Serialize)] @@ -410,16 +413,19 @@ impl }), ), _ => { - let redirection_dat = if !data.redirect_url.as_ref().unwrap().is_empty() { - data.redirect_url.as_ref().map(|url| { - Url::parse(url).into_report().change_context(errors::ConnectorError::FailedToObtainIntegrationUrl) - }).transpose()? - } else { - None - }; - - let redirection_data = redirection_dat.map(|url| services::RedirectForm::from((url, services::Method::Get))); - + let redirction_url = data + .redirect_url + .as_ref() + .filter(|redirect_str| !redirect_str.is_empty()) + .map(|url| { + Url::parse(url).into_report().change_context( + errors::ConnectorError::FailedToObtainIntegrationUrl, + ) + }) + .transpose()?; + + let redirection_data = redirction_url + .map(|url| services::RedirectForm::from((url, services::Method::Get))); ( attempt_status, Ok(types::PaymentsResponseData::TransactionResponse { @@ -474,17 +480,17 @@ pub enum RapydWebhookObjectEventType { RefundCompleted, PaymentRefundRejected, PaymentRefundFailed, - PaymentDisputeCreated, - PaymentDisputeUpdated, + PaymentDisputeCreated, + PaymentDisputeUpdated, } -#[derive(Debug, Clone, Deserialize, PartialEq, strum::Display)] +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, strum::Display)] #[serde(rename_all = "UPPERCASE")] pub enum RapydWebhookDisputeStatus { - Act, - Rvw, - Los, - Win, + Act, + Rvw, + Los, + Win, } impl TryFrom for api_models::webhooks::IncomingWebhookEvent { @@ -502,9 +508,9 @@ impl TryFrom for api_models::webhooks::IncomingWebhoo #[derive(Debug, Deserialize)] #[serde(untagged)] pub enum WebhookData { - PaymentData(ResponseData), - RefundData(RefundResponseData), - DisputeData(DesputeResponseData), + Payment(ResponseData), + Refund(RefundResponseData), + Dispute(DisputeResponseData), } impl From for RapydPaymentsResponse { From 2863d4cef1beb0a326de2e6d21f337d64712d028 Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Tue, 30 May 2023 12:01:28 +0530 Subject: [PATCH 5/8] remove cargo lock file --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd55c2dc31b..3690b937999 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3058,7 +3058,7 @@ dependencies = [ [[package]] name = "opentelemetry" version = "0.18.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" +source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" dependencies = [ "opentelemetry_api", "opentelemetry_sdk", @@ -3067,7 +3067,7 @@ dependencies = [ [[package]] name = "opentelemetry-otlp" version = "0.11.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" +source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" dependencies = [ "async-trait", "futures", @@ -3084,7 +3084,7 @@ dependencies = [ [[package]] name = "opentelemetry-proto" version = "0.1.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" +source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" dependencies = [ "futures", "futures-util", @@ -3096,7 +3096,7 @@ dependencies = [ [[package]] name = "opentelemetry_api" version = "0.18.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" +source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" dependencies = [ "fnv", "futures-channel", @@ -3111,7 +3111,7 @@ dependencies = [ [[package]] name = "opentelemetry_sdk" version = "0.18.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" +source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658" dependencies = [ "async-trait", "crossbeam-channel", From a17b18521132981bd3e8498d3d2c9a57f525cf96 Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Wed, 31 May 2023 14:32:38 +0530 Subject: [PATCH 6/8] address pr comments --- crates/router/src/connector/rapyd.rs | 8 +++----- .../src/connector/rapyd/transformers.rs | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/router/src/connector/rapyd.rs b/crates/router/src/connector/rapyd.rs index 2d87a7eb2c8..e85e2d34ec2 100644 --- a/crates/router/src/connector/rapyd.rs +++ b/crates/router/src/connector/rapyd.rs @@ -810,10 +810,8 @@ impl api::IncomingWebhook for Rapyd { .parse_struct("RapydIncomingWebhook") .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?; Ok(match webhook.webhook_type { - rapyd::RapydWebhookObjectEventType::PaymentCompleted => { - api::IncomingWebhookEvent::PaymentIntentFailure - } - rapyd::RapydWebhookObjectEventType::PaymentCaptured => { + rapyd::RapydWebhookObjectEventType::PaymentCompleted + | rapyd::RapydWebhookObjectEventType::PaymentCaptured => { api::IncomingWebhookEvent::PaymentIntentSuccess } rapyd::RapydWebhookObjectEventType::PaymentFailed => { @@ -881,7 +879,7 @@ impl api::IncomingWebhook for Rapyd { }?; Ok(api::disputes::DisputePayload { amount: webhook_dispute_data.amount.to_string(), - currency: webhook_dispute_data.currency, + 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), diff --git a/crates/router/src/connector/rapyd/transformers.rs b/crates/router/src/connector/rapyd/transformers.rs index 957db7e7a89..bbabd988829 100644 --- a/crates/router/src/connector/rapyd/transformers.rs +++ b/crates/router/src/connector/rapyd/transformers.rs @@ -250,7 +250,7 @@ pub struct ResponseData { pub struct DisputeResponseData { pub id: String, pub amount: i64, - pub currency: String, + pub currency: api_models::enums::Currency, pub token: String, pub dispute_reason_description: String, #[serde(with = "common_utils::custom_serde::timestamp")] @@ -486,11 +486,14 @@ pub enum RapydWebhookObjectEventType { } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, strum::Display)] -#[serde(rename_all = "UPPERCASE")] pub enum RapydWebhookDisputeStatus { - Act, - Rvw, - Los, + #[serde(rename = "ACT")] + Active, + #[serde(rename = "RVW")] + Review, + #[serde(rename = "LOS")] + Lose, + #[serde(rename = "WIN")] Win, } @@ -498,9 +501,9 @@ impl TryFrom for api_models::webhooks::IncomingWebhoo type Error = errors::ConnectorError; fn try_from(value: RapydWebhookDisputeStatus) -> Result { Ok(match value { - RapydWebhookDisputeStatus::Act => Self::DisputeOpened, - RapydWebhookDisputeStatus::Rvw => Self::DisputeChallenged, - RapydWebhookDisputeStatus::Los => Self::DisputeLost, + RapydWebhookDisputeStatus::Active => Self::DisputeOpened, + RapydWebhookDisputeStatus::Review => Self::DisputeChallenged, + RapydWebhookDisputeStatus::Lose => Self::DisputeLost, RapydWebhookDisputeStatus::Win => Self::DisputeWon, }) } From 0baf1f34c1db502fee913d276eafa5242c93a4c8 Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Wed, 31 May 2023 18:58:04 +0530 Subject: [PATCH 7/8] address pr comments --- .../src/connector/checkout/transformers.rs | 6 +++--- crates/router/src/connector/rapyd.rs | 12 +++++------ .../src/connector/rapyd/transformers.rs | 21 +++++++++---------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/crates/router/src/connector/checkout/transformers.rs b/crates/router/src/connector/checkout/transformers.rs index 35b599717ea..e102e031c5d 100644 --- a/crates/router/src/connector/checkout/transformers.rs +++ b/crates/router/src/connector/checkout/transformers.rs @@ -756,10 +756,10 @@ pub struct CheckoutDisputeWebhookData { pub action_id: Option, 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, pub reason_code: Option, - #[serde(with = "common_utils::custom_serde::iso8601::option")] + #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub date: Option, } #[derive(Debug, Deserialize)] @@ -767,7 +767,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, } #[derive(Debug, Deserialize, strum::Display, Clone)] diff --git a/crates/router/src/connector/rapyd.rs b/crates/router/src/connector/rapyd.rs index e85e2d34ec2..85d63a96d49 100644 --- a/crates/router/src/connector/rapyd.rs +++ b/crates/router/src/connector/rapyd.rs @@ -827,9 +827,7 @@ impl api::IncomingWebhook for Rapyd { api::IncomingWebhookEvent::DisputeOpened } rapyd::RapydWebhookObjectEventType::PaymentDisputeUpdated => match webhook.data { - rapyd::WebhookData::Dispute(data) => { - api::IncomingWebhookEvent::try_from(data.status)? - } + rapyd::WebhookData::Dispute(data) => api::IncomingWebhookEvent::from(data.status), _ => Err(errors::ConnectorError::WebhookEventTypeNotFound)?, }, _ => Err(errors::ConnectorError::WebhookEventTypeNotFound).into_report()?, @@ -875,7 +873,7 @@ impl api::IncomingWebhook for Rapyd { .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?; let webhook_dispute_data = match webhook.data { transformers::WebhookData::Dispute(dispute_data) => Ok(dispute_data), - _ => Err(errors::ConnectorError::WebhookEventTypeNotFound), + _ => Err(errors::ConnectorError::WebhookBodyDecodingFailed), }?; Ok(api::disputes::DisputePayload { amount: webhook_dispute_data.amount.to_string(), @@ -884,10 +882,10 @@ impl api::IncomingWebhook for Rapyd { connector_dispute_id: webhook_dispute_data.token, connector_reason: Some(webhook_dispute_data.dispute_reason_description), connector_reason_code: None, - challenge_required_by: Some(webhook_dispute_data.due_date), + challenge_required_by: webhook_dispute_data.due_date, connector_status: webhook_dispute_data.status.to_string(), - created_at: Some(webhook_dispute_data.created_at), - updated_at: Some(webhook_dispute_data.updated_at), + created_at: webhook_dispute_data.created_at, + updated_at: webhook_dispute_data.updated_at, }) } } diff --git a/crates/router/src/connector/rapyd/transformers.rs b/crates/router/src/connector/rapyd/transformers.rs index bbabd988829..12eb71a5281 100644 --- a/crates/router/src/connector/rapyd/transformers.rs +++ b/crates/router/src/connector/rapyd/transformers.rs @@ -253,13 +253,13 @@ pub struct DisputeResponseData { pub currency: api_models::enums::Currency, pub token: String, pub dispute_reason_description: String, - #[serde(with = "common_utils::custom_serde::timestamp")] - pub due_date: PrimitiveDateTime, + #[serde(default, with = "common_utils::custom_serde::timestamp::option")] + pub due_date: Option, pub status: RapydWebhookDisputeStatus, - #[serde(with = "common_utils::custom_serde::timestamp")] - pub created_at: PrimitiveDateTime, - #[serde(with = "common_utils::custom_serde::timestamp")] - pub updated_at: PrimitiveDateTime, + #[serde(default, with = "common_utils::custom_serde::timestamp::option")] + pub created_at: Option, + #[serde(default, with = "common_utils::custom_serde::timestamp::option")] + pub updated_at: Option, pub original_transaction_id: String, } @@ -497,15 +497,14 @@ pub enum RapydWebhookDisputeStatus { Win, } -impl TryFrom for api_models::webhooks::IncomingWebhookEvent { - type Error = errors::ConnectorError; - fn try_from(value: RapydWebhookDisputeStatus) -> Result { - Ok(match value { +impl From for api_models::webhooks::IncomingWebhookEvent { + fn from(value: RapydWebhookDisputeStatus) -> Self { + match value { RapydWebhookDisputeStatus::Active => Self::DisputeOpened, RapydWebhookDisputeStatus::Review => Self::DisputeChallenged, RapydWebhookDisputeStatus::Lose => Self::DisputeLost, RapydWebhookDisputeStatus::Win => Self::DisputeWon, - }) + } } } From 72425d0d8be789b7251a3a7fcbe12cec2fd47f3e Mon Sep 17 00:00:00 2001 From: Shankar Singh C Date: Mon, 12 Jun 2023 12:43:52 +0530 Subject: [PATCH 8/8] address pr comments --- crates/router/src/connector/rapyd.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/router/src/connector/rapyd.rs b/crates/router/src/connector/rapyd.rs index e8adbf60dd6..26a6536fa37 100644 --- a/crates/router/src/connector/rapyd.rs +++ b/crates/router/src/connector/rapyd.rs @@ -835,7 +835,8 @@ impl api::IncomingWebhook for Rapyd { rapyd::RapydWebhookObjectEventType::PaymentFailed => { api::IncomingWebhookEvent::PaymentIntentFailure } - rapyd::RapydWebhookObjectEventType::PaymentRefundFailed => { + rapyd::RapydWebhookObjectEventType::PaymentRefundFailed + | rapyd::RapydWebhookObjectEventType::PaymentRefundRejected => { api::IncomingWebhookEvent::RefundFailure } rapyd::RapydWebhookObjectEventType::RefundCompleted => { @@ -844,8 +845,7 @@ impl api::IncomingWebhook for Rapyd { rapyd::RapydWebhookObjectEventType::PaymentDisputeCreated => { api::IncomingWebhookEvent::DisputeOpened } - rapyd::RapydWebhookObjectEventType::Unknown - | rapyd::RapydWebhookObjectEventType::PaymentRefundRejected => { + rapyd::RapydWebhookObjectEventType::Unknown => { api::IncomingWebhookEvent::EventNotSupported } rapyd::RapydWebhookObjectEventType::PaymentDisputeUpdated => match webhook.data {