From 5c379524ecc6560b211f4f3b21f3abb315f32b0a Mon Sep 17 00:00:00 2001 From: Aniket Burman Date: Tue, 18 Mar 2025 12:29:06 +0530 Subject: [PATCH 01/12] Record back --- .../src/connectors/recurly.rs | 105 +++++++++++++++++- .../src/connectors/recurly/transformers.rs | 89 +++++++++++++++ .../src/default_implementations.rs | 2 +- .../src/router_flow_types/revenue_recovery.rs | 1 + 4 files changed, 194 insertions(+), 3 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/recurly.rs b/crates/hyperswitch_connectors/src/connectors/recurly.rs index c16810a6ab7..9dd50fc56eb 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly.rs @@ -40,7 +40,11 @@ use masking::{ExposeInterface, Mask}; use transformers as recurly; use crate::{constants::headers, types::ResponseRouterData, utils}; - +use hyperswitch_domain_models::types::RevenueRecoveryRecordBackRouterData; +use hyperswitch_domain_models::router_flow_types::RecoveryRecordBack; +use hyperswitch_domain_models::router_request_types::revenue_recovery::RevenueRecoveryRecordBackRequest; +use hyperswitch_domain_models::router_response_types::revenue_recovery::RevenueRecoveryRecordBackResponse; +use crate::connectors::recurly::transformers::RecurlyRecordStatus; #[derive(Clone)] pub struct Recurly { amount_converter: &'static (dyn AmountConvertor + Sync), @@ -66,7 +70,8 @@ impl api::Refund for Recurly {} impl api::RefundExecute for Recurly {} impl api::RefundSync for Recurly {} impl api::PaymentToken for Recurly {} - +#[cfg(all(feature = "v2", feature = "revenue_recovery"))] +impl api::revenue_recovery::RevenueRecoveryRecordBack for Recurly {} impl ConnectorIntegration for Recurly { @@ -541,6 +546,102 @@ impl ConnectorIntegration for Recurly { } } +impl + ConnectorIntegration< + RecoveryRecordBack, + RevenueRecoveryRecordBackRequest, + RevenueRecoveryRecordBackResponse, + > for Recurly +{ + fn get_headers( + &self, + req: &RevenueRecoveryRecordBackRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + fn get_url( + &self, + req: &RevenueRecoveryRecordBackRouterData, + connectors: &Connectors, + ) -> CustomResult { + let invoice_id = req + .request + .merchant_reference_id + .get_string_repr() + .to_string(); + + let status = RecurlyRecordStatus::try_from(req.request.attempt_status) + .map_err(|_| errors::ConnectorError::NotSupported { + message: "Invalid attempt status for Recurly".to_string(), + connector: "recurly", + })?; + + + let status_endpoint = match status { + RecurlyRecordStatus::Success => "mark_successful", + RecurlyRecordStatus::Failure => "mark_failed", + }; + + Ok(format!( + "{}/invoices/{}/{}", + self.base_url(connectors), + invoice_id, + status_endpoint + )) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn build_request( + &self, + req: &RevenueRecoveryRecordBackRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Put) + .url(&types::RevenueRecoveryRecordBackType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::RevenueRecoveryRecordBackType::get_headers( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &RevenueRecoveryRecordBackRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: recurly::RecurlyRecordbackResponse = res + .response + .parse_struct("recurly RecurlyRecordbackResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + #[async_trait::async_trait] impl webhooks::IncomingWebhook for Recurly { fn get_webhook_object_reference_id( diff --git a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs index bc62985edb3..6c22ae3b428 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs @@ -16,6 +16,10 @@ use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, utils::PaymentsAuthorizeRequestData, }; +use hyperswitch_domain_models::router_flow_types::RecoveryRecordBack; +use hyperswitch_domain_models::router_request_types::revenue_recovery::RevenueRecoveryRecordBackRequest; +use hyperswitch_domain_models::router_response_types::revenue_recovery::RevenueRecoveryRecordBackResponse; +use hyperswitch_domain_models::types::RevenueRecoveryRecordBackRouterData; //TODO: Fill the struct with respective fields pub struct RecurlyRouterData { @@ -226,3 +230,88 @@ pub struct RecurlyErrorResponse { pub message: String, pub reason: Option, } + +#[derive(Debug, Serialize, Clone, Copy)] +#[serde(rename_all = "snake_case")] +pub enum RecurlyRecordStatus { + Success, + Failure, +} + +#[cfg(all(feature = "v2", feature = "revenue_recovery"))] +impl TryFrom for RecurlyRecordStatus { + type Error = error_stack::Report; + fn try_from(status: enums::AttemptStatus) -> Result { + match status { + enums::AttemptStatus::Charged + | enums::AttemptStatus::PartialCharged + | enums::AttemptStatus::PartialChargedAndChargeable => Ok(Self::Success), + enums::AttemptStatus::Failure + | enums::AttemptStatus::CaptureFailed + | enums::AttemptStatus::RouterDeclined => Ok(Self::Failure), + enums::AttemptStatus::AuthenticationFailed + | enums::AttemptStatus::Started + | enums::AttemptStatus::AuthenticationPending + | enums::AttemptStatus::AuthenticationSuccessful + | enums::AttemptStatus::Authorized + | enums::AttemptStatus::AuthorizationFailed + | enums::AttemptStatus::Authorizing + | enums::AttemptStatus::CodInitiated + | enums::AttemptStatus::Voided + | enums::AttemptStatus::VoidInitiated + | enums::AttemptStatus::CaptureInitiated + | enums::AttemptStatus::VoidFailed + | enums::AttemptStatus::AutoRefunded + | enums::AttemptStatus::Unresolved + | enums::AttemptStatus::Pending + | enums::AttemptStatus::PaymentMethodAwaited + | enums::AttemptStatus::ConfirmationAwaited + | enums::AttemptStatus::DeviceDataCollectionPending => { + Err(errors::ConnectorError::NotSupported { + message: "Record back flow is only supported for terminal status".to_string(), + connector: "recurly", + } + .into()) + } + } + } +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct RecurlyRecordbackResponse { + pub invoice: RecurlyRecordbackInvoice, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct RecurlyRecordbackInvoice { + pub id: common_utils::id_type::PaymentReferenceId, +} + +impl + TryFrom< + ResponseRouterData< + RecoveryRecordBack, + RecurlyRecordbackResponse, + RevenueRecoveryRecordBackRequest, + RevenueRecoveryRecordBackResponse, + >, + > for RevenueRecoveryRecordBackRouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData< + RecoveryRecordBack, + RecurlyRecordbackResponse, + RevenueRecoveryRecordBackRequest, + RevenueRecoveryRecordBackResponse, + >, + ) -> Result { + let merchant_reference_id = item.response.invoice.id; + Ok(Self { + response: Ok(RevenueRecoveryRecordBackResponse { + merchant_reference_id, + }), + ..item.data + }) + } +} \ No newline at end of file diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs index aef839b4364..b7f37470ac1 100644 --- a/crates/hyperswitch_connectors/src/default_implementations.rs +++ b/crates/hyperswitch_connectors/src/default_implementations.rs @@ -3678,7 +3678,7 @@ default_imp_for_revenue_recovery_record_back!( connectors::Placetopay, connectors::Rapyd, connectors::Razorpay, - connectors::Recurly, + // connectors::Recurly, connectors::Redsys, connectors::Shift4, connectors::Stax, diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/revenue_recovery.rs b/crates/hyperswitch_domain_models/src/router_flow_types/revenue_recovery.rs index c6583811bca..18908bfa676 100644 --- a/crates/hyperswitch_domain_models/src/router_flow_types/revenue_recovery.rs +++ b/crates/hyperswitch_domain_models/src/router_flow_types/revenue_recovery.rs @@ -1,2 +1,3 @@ pub struct GetAdditionalRevenueRecoveryDetails; +#[derive(Clone)] pub struct RecoveryRecordBack; From 020c72f69da8c70f854890b3feb6ba0041572756 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 07:58:08 +0000 Subject: [PATCH 02/12] chore: run formatter --- .../src/connectors/recurly.rs | 38 ++++++++++--------- .../src/connectors/recurly/transformers.rs | 20 +++++----- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/recurly.rs b/crates/hyperswitch_connectors/src/connectors/recurly.rs index 8dc73c95c60..61b5da8c5e5 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly.rs @@ -12,16 +12,21 @@ use hyperswitch_domain_models::{ access_token_auth::AccessTokenAuth, payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void}, refunds::{Execute, RSync}, + RecoveryRecordBack, }, router_request_types::{ - AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData, - PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, - RefundsData, SetupMandateRequestData, + revenue_recovery::RevenueRecoveryRecordBackRequest, AccessTokenRequestData, + PaymentMethodTokenizationData, PaymentsAuthorizeData, PaymentsCancelData, + PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, RefundsData, + SetupMandateRequestData, + }, + router_response_types::{ + revenue_recovery::RevenueRecoveryRecordBackResponse, PaymentsResponseData, + RefundsResponseData, }, - router_response_types::{PaymentsResponseData, RefundsResponseData}, types::{ PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, - RefundSyncRouterData, RefundsRouterData, + RefundSyncRouterData, RefundsRouterData, RevenueRecoveryRecordBackRouterData, }, }; use hyperswitch_interfaces::{ @@ -39,14 +44,11 @@ use masking::{ExposeInterface, Mask}; use transformers as recurly; use crate::{ - connectors::recurly::transformers::RecurlyWebhookBody, constants::headers, - types::ResponseRouterData, utils, + connectors::recurly::transformers::{RecurlyRecordStatus, RecurlyWebhookBody}, + constants::headers, + types::ResponseRouterData, + utils, }; -use hyperswitch_domain_models::types::RevenueRecoveryRecordBackRouterData; -use hyperswitch_domain_models::router_flow_types::RecoveryRecordBack; -use hyperswitch_domain_models::router_request_types::revenue_recovery::RevenueRecoveryRecordBackRequest; -use hyperswitch_domain_models::router_response_types::revenue_recovery::RevenueRecoveryRecordBackResponse; -use crate::connectors::recurly::transformers::RecurlyRecordStatus; #[derive(Clone)] pub struct Recurly { amount_converter: &'static (dyn AmountConvertor + Sync), @@ -590,17 +592,17 @@ impl .get_string_repr() .to_string(); - let status = RecurlyRecordStatus::try_from(req.request.attempt_status) - .map_err(|_| errors::ConnectorError::NotSupported { + let status = RecurlyRecordStatus::try_from(req.request.attempt_status).map_err(|_| { + errors::ConnectorError::NotSupported { message: "Invalid attempt status for Recurly".to_string(), connector: "recurly", - })?; - - + } + })?; + let status_endpoint = match status { RecurlyRecordStatus::Success => "mark_successful", RecurlyRecordStatus::Failure => "mark_failed", - }; + }; Ok(format!( "{}/invoices/{}/{}", diff --git a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs index 2188cc8cd7c..224c4ab5f30 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs @@ -4,10 +4,16 @@ use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::PaymentMethodData, router_data::{ConnectorAuthType, RouterData}, - router_flow_types::refunds::{Execute, RSync}, - router_request_types::ResponseId, - router_response_types::{PaymentsResponseData, RefundsResponseData}, - types::{PaymentsAuthorizeRouterData, RefundsRouterData}, + router_flow_types::{ + refunds::{Execute, RSync}, + RecoveryRecordBack, + }, + router_request_types::{revenue_recovery::RevenueRecoveryRecordBackRequest, ResponseId}, + router_response_types::{ + revenue_recovery::RevenueRecoveryRecordBackResponse, PaymentsResponseData, + RefundsResponseData, + }, + types::{PaymentsAuthorizeRouterData, RefundsRouterData, RevenueRecoveryRecordBackRouterData}, }; use hyperswitch_interfaces::errors; use masking::Secret; @@ -17,10 +23,6 @@ use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, utils::PaymentsAuthorizeRequestData, }; -use hyperswitch_domain_models::router_flow_types::RecoveryRecordBack; -use hyperswitch_domain_models::router_request_types::revenue_recovery::RevenueRecoveryRecordBackRequest; -use hyperswitch_domain_models::router_response_types::revenue_recovery::RevenueRecoveryRecordBackResponse; -use hyperswitch_domain_models::types::RevenueRecoveryRecordBackRouterData; //TODO: Fill the struct with respective fields pub struct RecurlyRouterData { @@ -339,4 +341,4 @@ impl ..item.data }) } -} \ No newline at end of file +} From 1da3cad3d261c22ca00c37b198521b18d1a1f668 Mon Sep 17 00:00:00 2001 From: Aniket Burman <93077964+aniketburman014@users.noreply.github.com> Date: Thu, 20 Mar 2025 14:06:17 +0530 Subject: [PATCH 03/12] Update revenue_recovery.rs --- .../src/router_flow_types/revenue_recovery.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/revenue_recovery.rs b/crates/hyperswitch_domain_models/src/router_flow_types/revenue_recovery.rs index f0b714db712..b73ce533508 100644 --- a/crates/hyperswitch_domain_models/src/router_flow_types/revenue_recovery.rs +++ b/crates/hyperswitch_domain_models/src/router_flow_types/revenue_recovery.rs @@ -1,4 +1,3 @@ pub struct GetAdditionalRevenueRecoveryDetails; - #[derive(Debug, Clone)] pub struct RecoveryRecordBack; From ff4c0c8e75ebe39e58a25c20abbc49448dd19108 Mon Sep 17 00:00:00 2001 From: Aniket Burman Date: Tue, 18 Mar 2025 14:05:36 +0530 Subject: [PATCH 04/12] Minor --- .../src/connectors/recurly.rs | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/recurly.rs b/crates/hyperswitch_connectors/src/connectors/recurly.rs index bb3122f389c..ca1dbd07384 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly.rs @@ -29,6 +29,7 @@ use hyperswitch_domain_models::{ RefundSyncRouterData, RefundsRouterData, RevenueRecoveryRecordBackRouterData, }, }; +use masking::PeekInterface; use hyperswitch_interfaces::{ api::{ self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications, @@ -144,12 +145,15 @@ impl ConnectorCommon for Recurly { ) -> CustomResult)>, errors::ConnectorError> { let auth = recurly::RecurlyAuthType::try_from(auth_type) .change_context(errors::ConnectorError::FailedToObtainAuthType)?; - Ok(vec![( - headers::AUTHORIZATION.to_string(), - auth.api_key.expose().into_masked(), - )]) - } - + Ok(vec![ + ( + headers::AUTHORIZATION.to_string(), + format!("Basic {}", base64::encode(auth.api_key.peek())).into_masked(), + ), + ( + headers::ACCEPT.to_string(), + "application/vnd.recurly.v2021-02-25".to_string().into_masked(), + ), fn build_error_response( &self, res: Response, @@ -588,11 +592,12 @@ impl req: &RevenueRecoveryRecordBackRouterData, connectors: &Connectors, ) -> CustomResult { - let invoice_id = req - .request - .merchant_reference_id - .get_string_repr() - .to_string(); + // let invoice_id = req + // .request + // .merchant_reference_id + // .get_string_repr() + // .to_string(); + let invoice_id = "wlfucc9o7715".to_string(); let status = RecurlyRecordStatus::try_from(req.request.attempt_status).map_err(|_| { errors::ConnectorError::NotSupported { From 9b54909413c3b4a78fe42e1c50e4b287893492da Mon Sep 17 00:00:00 2001 From: Aniket Burman Date: Thu, 20 Mar 2025 19:14:38 +0530 Subject: [PATCH 05/12] record back implementation --- .../src/connectors/recurly.rs | 69 ++++++++++--------- .../src/connectors/recurly/transformers.rs | 30 ++++---- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/recurly.rs b/crates/hyperswitch_connectors/src/connectors/recurly.rs index ca1dbd07384..ef309ee576f 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly.rs @@ -12,24 +12,30 @@ use hyperswitch_domain_models::{ access_token_auth::AccessTokenAuth, payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void}, refunds::{Execute, RSync}, - RecoveryRecordBack, }, router_request_types::{ - revenue_recovery::RevenueRecoveryRecordBackRequest, AccessTokenRequestData, + AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, RefundsData, SetupMandateRequestData, }, - router_response_types::{ - revenue_recovery::RevenueRecoveryRecordBackResponse, PaymentsResponseData, + router_response_types::{PaymentsResponseData, RefundsResponseData, }, types::{ PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, - RefundSyncRouterData, RefundsRouterData, RevenueRecoveryRecordBackRouterData, + RefundSyncRouterData, RefundsRouterData, }, }; -use masking::PeekInterface; +#[cfg(all(feature = "v2", feature = "revenue_recovery"))] +use hyperswitch_domain_models::{ + router_flow_types::RecoveryRecordBack, + router_request_types::revenue_recovery::RevenueRecoveryRecordBackRequest, + router_response_types::revenue_recovery::RevenueRecoveryRecordBackResponse, + types::RevenueRecoveryRecordBackRouterData, +}; +#[cfg(all(feature = "v2", feature = "revenue_recovery"))] +use crate::connectors::recurly::transformers::RecurlyRecordStatus; use hyperswitch_interfaces::{ api::{ self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications, @@ -41,11 +47,12 @@ use hyperswitch_interfaces::{ types::{self, Response}, webhooks, }; -use masking::{ExposeInterface, Mask}; -use transformers as recurly; +use masking::Mask; +use masking::ExposeInterface; +use transformers as recurly; +use crate::connectors::recurly::transformers::RecurlyWebhookBody; use crate::{ - connectors::recurly::transformers::{RecurlyRecordStatus, RecurlyWebhookBody}, constants::headers, types::ResponseRouterData, utils, @@ -145,15 +152,11 @@ impl ConnectorCommon for Recurly { ) -> CustomResult)>, errors::ConnectorError> { let auth = recurly::RecurlyAuthType::try_from(auth_type) .change_context(errors::ConnectorError::FailedToObtainAuthType)?; - Ok(vec![ - ( - headers::AUTHORIZATION.to_string(), - format!("Basic {}", base64::encode(auth.api_key.peek())).into_masked(), - ), - ( - headers::ACCEPT.to_string(), - "application/vnd.recurly.v2021-02-25".to_string().into_masked(), - ), + Ok(vec![( + headers::AUTHORIZATION.to_string(), + auth.api_key.expose().into_masked(), + )]) + } fn build_error_response( &self, res: Response, @@ -572,7 +575,7 @@ impl ConnectorIntegration for Recurly { self.build_error_response(res, event_builder) } } - +#[cfg(all(feature = "v2", feature = "revenue_recovery"))] impl ConnectorIntegration< RecoveryRecordBack, @@ -592,24 +595,23 @@ impl req: &RevenueRecoveryRecordBackRouterData, connectors: &Connectors, ) -> CustomResult { - // let invoice_id = req - // .request - // .merchant_reference_id - // .get_string_repr() - // .to_string(); - let invoice_id = "wlfucc9o7715".to_string(); - - let status = RecurlyRecordStatus::try_from(req.request.attempt_status).map_err(|_| { - errors::ConnectorError::NotSupported { - message: "Invalid attempt status for Recurly".to_string(), - connector: "recurly", - } + let invoice_id = req + .request + .merchant_reference_id + .get_string_repr() + .to_string(); + + let status = RecurlyRecordStatus::try_from(req.request.attempt_status) + .map_err(|_| errors::ConnectorError::NotSupported { + message: "Invalid attempt status for Recurly".to_string(), + connector: "recurly", })?; - + + let status_endpoint = match status { RecurlyRecordStatus::Success => "mark_successful", RecurlyRecordStatus::Failure => "mark_failed", - }; + }; Ok(format!( "{}/invoices/{}/{}", @@ -638,6 +640,7 @@ impl .headers(types::RevenueRecoveryRecordBackType::get_headers( self, req, connectors, )?) + .header("Content-Length", "0") .build(), )) } diff --git a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs index 224c4ab5f30..5b4adabf2e7 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs @@ -4,26 +4,27 @@ use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::PaymentMethodData, router_data::{ConnectorAuthType, RouterData}, - router_flow_types::{ - refunds::{Execute, RSync}, - RecoveryRecordBack, - }, - router_request_types::{revenue_recovery::RevenueRecoveryRecordBackRequest, ResponseId}, - router_response_types::{ - revenue_recovery::RevenueRecoveryRecordBackResponse, PaymentsResponseData, + router_flow_types::refunds::{Execute, RSync}, + router_request_types::ResponseId, + router_response_types::{ PaymentsResponseData, RefundsResponseData, }, - types::{PaymentsAuthorizeRouterData, RefundsRouterData, RevenueRecoveryRecordBackRouterData}, + types::{PaymentsAuthorizeRouterData, RefundsRouterData,}, }; use hyperswitch_interfaces::errors; use masking::Secret; use serde::{Deserialize, Serialize}; - +#[cfg(all(feature = "v2", feature = "revenue_recovery"))] +use hyperswitch_domain_models::{ + router_flow_types::RecoveryRecordBack, + router_request_types::revenue_recovery::RevenueRecoveryRecordBackRequest, + router_response_types::revenue_recovery::RevenueRecoveryRecordBackResponse, + types::RevenueRecoveryRecordBackRouterData, +}; use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, utils::PaymentsAuthorizeRequestData, }; - //TODO: Fill the struct with respective fields pub struct RecurlyRouterData { pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. @@ -306,14 +307,11 @@ impl TryFrom for RecurlyRecordStatus { #[derive(Debug, Deserialize, Serialize, Clone)] pub struct RecurlyRecordbackResponse { - pub invoice: RecurlyRecordbackInvoice, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct RecurlyRecordbackInvoice { + // inovice id pub id: common_utils::id_type::PaymentReferenceId, } +#[cfg(all(feature = "v2", feature = "revenue_recovery"))] impl TryFrom< ResponseRouterData< @@ -333,7 +331,7 @@ impl RevenueRecoveryRecordBackResponse, >, ) -> Result { - let merchant_reference_id = item.response.invoice.id; + let merchant_reference_id = item.response.id; Ok(Self { response: Ok(RevenueRecoveryRecordBackResponse { merchant_reference_id, From c8f62d880f3d9f0c474a29e2a34fcf988a629475 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 13:49:07 +0000 Subject: [PATCH 06/12] chore: run formatter --- .../src/connectors/recurly.rs | 40 ++++++++----------- .../src/connectors/recurly/transformers.rs | 13 +++--- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/recurly.rs b/crates/hyperswitch_connectors/src/connectors/recurly.rs index ef309ee576f..c8ea947bbc1 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly.rs @@ -14,14 +14,11 @@ use hyperswitch_domain_models::{ refunds::{Execute, RSync}, }, router_request_types::{ - AccessTokenRequestData, - PaymentMethodTokenizationData, PaymentsAuthorizeData, PaymentsCancelData, - PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, RefundsData, - SetupMandateRequestData, - }, - router_response_types::{PaymentsResponseData, - RefundsResponseData, + AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData, + PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, + RefundsData, SetupMandateRequestData, }, + router_response_types::{PaymentsResponseData, RefundsResponseData}, types::{ PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData, @@ -34,8 +31,6 @@ use hyperswitch_domain_models::{ router_response_types::revenue_recovery::RevenueRecoveryRecordBackResponse, types::RevenueRecoveryRecordBackRouterData, }; -#[cfg(all(feature = "v2", feature = "revenue_recovery"))] -use crate::connectors::recurly::transformers::RecurlyRecordStatus; use hyperswitch_interfaces::{ api::{ self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications, @@ -47,15 +42,14 @@ use hyperswitch_interfaces::{ types::{self, Response}, webhooks, }; -use masking::Mask; -use masking::ExposeInterface; - +use masking::{ExposeInterface, Mask}; use transformers as recurly; -use crate::connectors::recurly::transformers::RecurlyWebhookBody; + +#[cfg(all(feature = "v2", feature = "revenue_recovery"))] +use crate::connectors::recurly::transformers::RecurlyRecordStatus; use crate::{ - constants::headers, - types::ResponseRouterData, - utils, + connectors::recurly::transformers::RecurlyWebhookBody, constants::headers, + types::ResponseRouterData, utils, }; #[derive(Clone)] pub struct Recurly { @@ -601,17 +595,17 @@ impl .get_string_repr() .to_string(); - let status = RecurlyRecordStatus::try_from(req.request.attempt_status) - .map_err(|_| errors::ConnectorError::NotSupported { - message: "Invalid attempt status for Recurly".to_string(), - connector: "recurly", + let status = RecurlyRecordStatus::try_from(req.request.attempt_status).map_err(|_| { + errors::ConnectorError::NotSupported { + message: "Invalid attempt status for Recurly".to_string(), + connector: "recurly", + } })?; - - + let status_endpoint = match status { RecurlyRecordStatus::Success => "mark_successful", RecurlyRecordStatus::Failure => "mark_failed", - }; + }; Ok(format!( "{}/invoices/{}/{}", diff --git a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs index 5b4adabf2e7..ad4af171116 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs @@ -6,14 +6,9 @@ use hyperswitch_domain_models::{ router_data::{ConnectorAuthType, RouterData}, router_flow_types::refunds::{Execute, RSync}, router_request_types::ResponseId, - router_response_types::{ PaymentsResponseData, - RefundsResponseData, - }, - types::{PaymentsAuthorizeRouterData, RefundsRouterData,}, + router_response_types::{PaymentsResponseData, RefundsResponseData}, + types::{PaymentsAuthorizeRouterData, RefundsRouterData}, }; -use hyperswitch_interfaces::errors; -use masking::Secret; -use serde::{Deserialize, Serialize}; #[cfg(all(feature = "v2", feature = "revenue_recovery"))] use hyperswitch_domain_models::{ router_flow_types::RecoveryRecordBack, @@ -21,6 +16,10 @@ use hyperswitch_domain_models::{ router_response_types::revenue_recovery::RevenueRecoveryRecordBackResponse, types::RevenueRecoveryRecordBackRouterData, }; +use hyperswitch_interfaces::errors; +use masking::Secret; +use serde::{Deserialize, Serialize}; + use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, utils::PaymentsAuthorizeRequestData, From 2859f0b14598aeba59319a5350e5b9ec1c11347f Mon Sep 17 00:00:00 2001 From: Aniket Burman Date: Thu, 20 Mar 2025 19:19:50 +0530 Subject: [PATCH 07/12] removed recurly from default_imp_for_revenue_recovery_record_back --- crates/hyperswitch_connectors/src/default_implementations.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs index 3d77f629822..3e7bfc79161 100644 --- a/crates/hyperswitch_connectors/src/default_implementations.rs +++ b/crates/hyperswitch_connectors/src/default_implementations.rs @@ -3740,7 +3740,6 @@ default_imp_for_revenue_recovery_record_back!( connectors::Placetopay, connectors::Rapyd, connectors::Razorpay, - // connectors::Recurly, connectors::Redsys, connectors::Shift4, connectors::Stax, From 9e45de9759caf920ad145468d9382feec69a0b41 Mon Sep 17 00:00:00 2001 From: Aniket Burman Date: Thu, 20 Mar 2025 19:22:59 +0530 Subject: [PATCH 08/12] Spaces --- crates/hyperswitch_connectors/src/connectors/recurly.rs | 2 ++ .../src/connectors/recurly/transformers.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/crates/hyperswitch_connectors/src/connectors/recurly.rs b/crates/hyperswitch_connectors/src/connectors/recurly.rs index c8ea947bbc1..6b1b4c34637 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly.rs @@ -51,6 +51,7 @@ use crate::{ connectors::recurly::transformers::RecurlyWebhookBody, constants::headers, types::ResponseRouterData, utils, }; + #[derive(Clone)] pub struct Recurly { amount_converter: &'static (dyn AmountConvertor + Sync), @@ -151,6 +152,7 @@ impl ConnectorCommon for Recurly { auth.api_key.expose().into_masked(), )]) } + fn build_error_response( &self, res: Response, diff --git a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs index ad4af171116..499f5c1c63f 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs @@ -24,6 +24,7 @@ use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, utils::PaymentsAuthorizeRequestData, }; + //TODO: Fill the struct with respective fields pub struct RecurlyRouterData { pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. From 630d69d79c8e84dac37624787bc608490485486b Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 13:55:20 +0000 Subject: [PATCH 09/12] chore: run formatter --- crates/hyperswitch_connectors/src/connectors/recurly.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hyperswitch_connectors/src/connectors/recurly.rs b/crates/hyperswitch_connectors/src/connectors/recurly.rs index 6b1b4c34637..dfce3920cce 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly.rs @@ -152,7 +152,7 @@ impl ConnectorCommon for Recurly { auth.api_key.expose().into_masked(), )]) } - + fn build_error_response( &self, res: Response, From 6664a5c37ddf249775a9875f2ad391813abf802f Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 14:01:33 +0000 Subject: [PATCH 10/12] docs(openapi): re-generate OpenAPI specification --- api-reference-v2/openapi_spec.json | 9 +++++++++ api-reference/openapi_spec.json | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 0ae089c5ea0..0d10ad5cbd9 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -8365,10 +8365,12 @@ "CAD", "CDF", "CHF", + "CLF", "CLP", "CNY", "COP", "CRC", + "CUC", "CUP", "CVE", "CZK", @@ -8464,6 +8466,7 @@ "SOS", "SRD", "SSP", + "STD", "STN", "SVC", "SYP", @@ -9969,6 +9972,12 @@ } } }, + { + "type": "string", + "enum": [ + "user_social_security_number" + ] + }, { "type": "string", "enum": [ diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index fa4ce7ae5eb..c4c18f6738f 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -10493,10 +10493,12 @@ "CAD", "CDF", "CHF", + "CLF", "CLP", "CNY", "COP", "CRC", + "CUC", "CUP", "CVE", "CZK", @@ -10592,6 +10594,7 @@ "SOS", "SRD", "SSP", + "STD", "STN", "SVC", "SYP", @@ -12170,6 +12173,12 @@ } } }, + { + "type": "string", + "enum": [ + "user_social_security_number" + ] + }, { "type": "string", "enum": [ From c906778664a0fcce86e4f6cf44cc2a7e268cb561 Mon Sep 17 00:00:00 2001 From: Aniket Burman Date: Thu, 20 Mar 2025 20:17:33 +0530 Subject: [PATCH 11/12] minor changes in recurly.rs --- .../src/connectors/recurly.rs | 23 ++++++++----------- .../src/connectors/recurly/transformers.rs | 4 ++-- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/recurly.rs b/crates/hyperswitch_connectors/src/connectors/recurly.rs index dfce3920cce..ccfd9313487 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly.rs @@ -51,6 +51,10 @@ use crate::{ connectors::recurly::transformers::RecurlyWebhookBody, constants::headers, types::ResponseRouterData, utils, }; +#[cfg(all(feature = "v2", feature = "revenue_recovery"))] +const STATUS_SUCCESSFUL_ENDPOINT: &str = "mark_successful"; +#[cfg(all(feature = "v2", feature = "revenue_recovery"))] +const STATUS_FAILED_ENDPOINT: &str = "mark_failed"; #[derive(Clone)] pub struct Recurly { @@ -597,23 +601,16 @@ impl .get_string_repr() .to_string(); - let status = RecurlyRecordStatus::try_from(req.request.attempt_status).map_err(|_| { - errors::ConnectorError::NotSupported { - message: "Invalid attempt status for Recurly".to_string(), - connector: "recurly", - } - })?; + let status = RecurlyRecordStatus::try_from(req.request.attempt_status)?; let status_endpoint = match status { - RecurlyRecordStatus::Success => "mark_successful", - RecurlyRecordStatus::Failure => "mark_failed", - }; + RecurlyRecordStatus::Success => STATUS_SUCCESSFUL_ENDPOINT, + RecurlyRecordStatus::Failure => STATUS_FAILED_ENDPOINT, + }; Ok(format!( - "{}/invoices/{}/{}", - self.base_url(connectors), - invoice_id, - status_endpoint + "{}/invoices/{invoice_id}/{status_endpoint}", + self.base_url(connectors) )) } diff --git a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs index 499f5c1c63f..426dc3f83be 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs @@ -1,5 +1,5 @@ use common_enums::enums; -use common_utils::{errors::CustomResult, ext_traits::ByteSliceExt, types::StringMinorUnit}; +use common_utils::{id_type, errors::CustomResult, ext_traits::ByteSliceExt, types::StringMinorUnit}; use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::PaymentMethodData, @@ -308,7 +308,7 @@ impl TryFrom for RecurlyRecordStatus { #[derive(Debug, Deserialize, Serialize, Clone)] pub struct RecurlyRecordbackResponse { // inovice id - pub id: common_utils::id_type::PaymentReferenceId, + pub id: id_type::PaymentReferenceId, } #[cfg(all(feature = "v2", feature = "revenue_recovery"))] From a09908dac3607eb8f0adc9e741e16f5cdff73032 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 14:52:52 +0000 Subject: [PATCH 12/12] chore: run formatter --- crates/hyperswitch_connectors/src/connectors/recurly.rs | 2 +- .../src/connectors/recurly/transformers.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/recurly.rs b/crates/hyperswitch_connectors/src/connectors/recurly.rs index ccfd9313487..e094e24e23a 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly.rs @@ -606,7 +606,7 @@ impl let status_endpoint = match status { RecurlyRecordStatus::Success => STATUS_SUCCESSFUL_ENDPOINT, RecurlyRecordStatus::Failure => STATUS_FAILED_ENDPOINT, - }; + }; Ok(format!( "{}/invoices/{invoice_id}/{status_endpoint}", diff --git a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs index 426dc3f83be..23a0daa45b4 100644 --- a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs @@ -1,5 +1,7 @@ use common_enums::enums; -use common_utils::{id_type, errors::CustomResult, ext_traits::ByteSliceExt, types::StringMinorUnit}; +use common_utils::{ + errors::CustomResult, ext_traits::ByteSliceExt, id_type, types::StringMinorUnit, +}; use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::PaymentMethodData,