diff --git a/crates/hyperswitch_connectors/src/connectors/xendit.rs b/crates/hyperswitch_connectors/src/connectors/xendit.rs index 8d857c7251..ff84224a65 100644 --- a/crates/hyperswitch_connectors/src/connectors/xendit.rs +++ b/crates/hyperswitch_connectors/src/connectors/xendit.rs @@ -57,6 +57,7 @@ use transformers::{self as xendit, XenditEventType, XenditWebhookEvent}; use crate::{ constants::headers, types::ResponseRouterData, + utils as connector_utils, utils::{self, PaymentMethodDataType, RefundsRequestData}, }; @@ -301,14 +302,27 @@ impl ConnectorIntegration for Xen event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { - let response: xendit::XenditResponse = - res.response - .parse_struct("xendit XenditResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: xendit::XenditResponse = res + .response + .clone() + .parse_struct("xendit XenditResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + let response_integrity_object = match response.clone() { + xendit::XenditResponse::Payment(p) => connector_utils::get_sync_integrity_object( + self.amount_converter, + p.amount, + p.currency.to_string().clone(), + ), + xendit::XenditResponse::Webhook(p) => connector_utils::get_sync_integrity_object( + self.amount_converter, + p.data.amount, + p.data.currency.to_string().clone(), + ), + }; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); - RouterData::try_from(ResponseRouterData { + let new_router_data = RouterData::try_from(ResponseRouterData { response, data: data.clone(), http_code: res.status_code, + }); + new_router_data.and_then(|mut router_data| { + let integrity_object = response_integrity_object?; + router_data.request.integrity_object = Some(integrity_object); + Ok(router_data) }) - .change_context(errors::ConnectorError::ResponseHandlingFailed) } } @@ -599,15 +631,26 @@ impl ConnectorIntegration fo .parse_struct("Xendit PaymentsResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response_integrity_object = connector_utils::get_capture_integrity_object( + self.amount_converter, + Some(response.amount), + response.currency.to_string().clone(), + )?; + event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); - RouterData::try_from(ResponseRouterData { + let new_router_data = RouterData::try_from(ResponseRouterData { response, data: data.clone(), http_code: res.status_code, }) - .change_context(errors::ConnectorError::ResponseHandlingFailed) + .change_context(errors::ConnectorError::ResponseHandlingFailed); + + new_router_data.map(|mut router_data| { + router_data.request.integrity_object = Some(response_integrity_object); + router_data + }) } fn get_error_response( @@ -695,15 +738,27 @@ impl ConnectorIntegration for Xendit .parse_struct("xendit RefundResponse") .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let response_integrity_object = connector_utils::get_refund_integrity_object( + self.amount_converter, + response.amount, + response.currency.to_string().clone(), + )?; + event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); - RouterData::try_from(ResponseRouterData { + let new_router_data = RouterData::try_from(ResponseRouterData { response, data: data.clone(), http_code: res.status_code, - }) - .change_context(errors::ConnectorError::ResponseHandlingFailed) + }); + + new_router_data + .map(|mut router_data| { + router_data.request.integrity_object = Some(response_integrity_object); + router_data + }) + .change_context(errors::ConnectorError::ResponseHandlingFailed) } fn get_error_response( @@ -772,19 +827,32 @@ impl ConnectorIntegration for Xendit { event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { - let response: xendit::RefundResponse = - res.response - .parse_struct("xendit RefundResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: xendit::RefundResponse = res + .response + .clone() + .parse_struct("xendit RefundResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + let response_integrity_object = connector_utils::get_refund_integrity_object( + self.amount_converter, + response.amount, + response.currency.to_string().clone(), + )?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); - RouterData::try_from(ResponseRouterData { + let new_router_data = RouterData::try_from(ResponseRouterData { response, data: data.clone(), http_code: res.status_code, - }) - .change_context(errors::ConnectorError::ResponseHandlingFailed) + }); + + new_router_data + .map(|mut router_data| { + router_data.request.integrity_object = Some(response_integrity_object); + router_data + }) + .change_context(errors::ConnectorError::ResponseHandlingFailed) } fn get_error_response( diff --git a/crates/hyperswitch_connectors/src/connectors/xendit/transformers.rs b/crates/hyperswitch_connectors/src/connectors/xendit/transformers.rs index 32f6781599..7b649289ea 100644 --- a/crates/hyperswitch_connectors/src/connectors/xendit/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/xendit/transformers.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use cards::CardNumber; -use common_enums::enums; +use common_enums::{enums, Currency}; use common_utils::{pii, request::Method, types::FloatMajorUnit}; use hyperswitch_domain_models::{ payment_method_data::PaymentMethodData, @@ -65,7 +65,7 @@ pub struct CardPaymentRequest { #[derive(Serialize, Deserialize, Debug)] pub struct MandatePaymentRequest { pub amount: FloatMajorUnit, - pub currency: common_enums::Currency, + pub currency: Currency, pub capture_method: String, pub payment_method_id: Secret, pub channel_properties: ChannelProperties, @@ -81,7 +81,7 @@ pub struct XenditPaymentsCaptureRequest { #[derive(Serialize, Deserialize, Debug)] pub struct XenditPaymentsRequest { pub amount: FloatMajorUnit, - pub currency: common_enums::Currency, + pub currency: Currency, pub capture_method: String, #[serde(skip_serializing_if = "Option::is_none")] pub payment_method: Option, @@ -97,7 +97,7 @@ pub struct XenditSplitRoute { pub flat_amount: Option, #[serde(skip_serializing_if = "Option::is_none")] pub percent_amount: Option, - pub currency: enums::Currency, + pub currency: Currency, pub destination_account_id: String, pub reference_id: String, } @@ -164,7 +164,7 @@ pub enum PaymentStatus { AwaitingCapture, Verified, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] #[serde(untagged)] pub enum XenditResponse { Payment(XenditPaymentResponse), @@ -178,6 +178,8 @@ pub struct XenditPaymentResponse { pub payment_method: PaymentMethodInfo, pub failure_code: Option, pub reference_id: Secret, + pub amount: FloatMajorUnit, + pub currency: Currency, } fn map_payment_response_to_attempt_status( @@ -502,7 +504,7 @@ impl common_utils::types::AmountConvertor::convert_back( &required_conversion_type, amount, - item.data.request.currency.unwrap_or(enums::Currency::USD), + item.data.request.currency.unwrap_or(Currency::USD), ) .map_err(|_| { errors::ConnectorError::RequestEncodingFailedWithReason( @@ -660,7 +662,7 @@ impl TryFrom<&PaymentsPreProcessingRouterData> for XenditSplitRequestData { common_utils::types::AmountConvertor::convert( &required_conversion_type, amount, - item.request.currency.unwrap_or(enums::Currency::USD), + item.request.currency.unwrap_or(Currency::USD), ) .map_err(|_| { errors::ConnectorError::RequestEncodingFailedWithReason( @@ -739,8 +741,10 @@ impl From for enums::RefundStatus { //TODO: Fill the struct with respective fields #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RefundResponse { - id: String, - status: RefundStatus, + pub id: String, + pub status: RefundStatus, + pub amount: FloatMajorUnit, + pub currency: String, } impl TryFrom> for RefundsRouterData { @@ -777,20 +781,22 @@ impl TryFrom> for RefundsRouter pub struct XenditMetadata { pub for_user_id: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct XenditWebhookEvent { pub event: XenditEventType, pub data: EventDetails, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct EventDetails { pub id: String, pub payment_request_id: Option, + pub amount: FloatMajorUnit, + pub currency: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub enum XenditEventType { #[serde(rename = "payment.succeeded")] PaymentSucceeded, diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index 04c5cb531f..80c9f63ca4 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -6309,6 +6309,74 @@ pub fn get_card_details( } } +pub fn get_authorise_integrity_object( + amount_convertor: &dyn AmountConvertor, + amount: T, + currency: String, +) -> Result> { + let currency_enum = enums::Currency::from_str(currency.to_uppercase().as_str()) + .change_context(errors::ConnectorError::ParsingFailed)?; + + let amount_in_minor_unit = + convert_back_amount_to_minor_units(amount_convertor, amount, currency_enum)?; + + Ok(AuthoriseIntegrityObject { + amount: amount_in_minor_unit, + currency: currency_enum, + }) +} + +pub fn get_sync_integrity_object( + amount_convertor: &dyn AmountConvertor, + amount: T, + currency: String, +) -> Result> { + let currency_enum = enums::Currency::from_str(currency.to_uppercase().as_str()) + .change_context(errors::ConnectorError::ParsingFailed)?; + let amount_in_minor_unit = + convert_back_amount_to_minor_units(amount_convertor, amount, currency_enum)?; + + Ok(SyncIntegrityObject { + amount: Some(amount_in_minor_unit), + currency: Some(currency_enum), + }) +} + +pub fn get_capture_integrity_object( + amount_convertor: &dyn AmountConvertor, + capture_amount: Option, + currency: String, +) -> Result> { + let currency_enum = enums::Currency::from_str(currency.to_uppercase().as_str()) + .change_context(errors::ConnectorError::ParsingFailed)?; + + let capture_amount_in_minor_unit = capture_amount + .map(|amount| convert_back_amount_to_minor_units(amount_convertor, amount, currency_enum)) + .transpose()?; + + Ok(CaptureIntegrityObject { + capture_amount: capture_amount_in_minor_unit, + currency: currency_enum, + }) +} + +pub fn get_refund_integrity_object( + amount_convertor: &dyn AmountConvertor, + refund_amount: T, + currency: String, +) -> Result> { + let currency_enum = enums::Currency::from_str(currency.to_uppercase().as_str()) + .change_context(errors::ConnectorError::ParsingFailed)?; + + let refund_amount_in_minor_unit = + convert_back_amount_to_minor_units(amount_convertor, refund_amount, currency_enum)?; + + Ok(RefundIntegrityObject { + currency: currency_enum, + refund_amount: refund_amount_in_minor_unit, + }) +} + #[cfg(feature = "frm")] pub trait FraudCheckSaleRequest { fn get_order_details(&self) -> Result, Error>; @@ -6366,71 +6434,3 @@ impl SplitPaymentData for SetupMandateRequestData { None } } - -pub fn get_refund_integrity_object( - amount_convertor: &dyn AmountConvertor, - refund_amount: T, - currency: String, -) -> Result> { - let currency_enum = enums::Currency::from_str(currency.to_uppercase().as_str()) - .change_context(errors::ConnectorError::ParsingFailed)?; - - let refund_amount_in_minor_unit = - convert_back_amount_to_minor_units(amount_convertor, refund_amount, currency_enum)?; - - Ok(RefundIntegrityObject { - currency: currency_enum, - refund_amount: refund_amount_in_minor_unit, - }) -} - -pub fn get_capture_integrity_object( - amount_convertor: &dyn AmountConvertor, - capture_amount: Option, - currency: String, -) -> Result> { - let currency_enum = enums::Currency::from_str(currency.to_uppercase().as_str()) - .change_context(errors::ConnectorError::ParsingFailed)?; - - let capture_amount_in_minor_unit = capture_amount - .map(|amount| convert_back_amount_to_minor_units(amount_convertor, amount, currency_enum)) - .transpose()?; - - Ok(CaptureIntegrityObject { - capture_amount: capture_amount_in_minor_unit, - currency: currency_enum, - }) -} - -pub fn get_sync_integrity_object( - amount_convertor: &dyn AmountConvertor, - amount: T, - currency: String, -) -> Result> { - let currency_enum = enums::Currency::from_str(currency.to_uppercase().as_str()) - .change_context(errors::ConnectorError::ParsingFailed)?; - let amount_in_minor_unit = - convert_back_amount_to_minor_units(amount_convertor, amount, currency_enum)?; - - Ok(SyncIntegrityObject { - amount: Some(amount_in_minor_unit), - currency: Some(currency_enum), - }) -} - -pub fn get_authorise_integrity_object( - amount_convertor: &dyn AmountConvertor, - amount: T, - currency: String, -) -> Result> { - let currency_enum = enums::Currency::from_str(currency.to_uppercase().as_str()) - .change_context(errors::ConnectorError::ParsingFailed)?; - - let amount_in_minor_unit = - convert_back_amount_to_minor_units(amount_convertor, amount, currency_enum)?; - - Ok(AuthoriseIntegrityObject { - amount: amount_in_minor_unit, - currency: currency_enum, - }) -} diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 7a0912e47d..e2db6a379c 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -2750,7 +2750,6 @@ pub fn convert_back_amount_to_minor_units( .convert_back(amount, currency) .change_context(errors::ConnectorError::AmountConversionFailed) } - pub trait NetworkTokenData { fn get_card_issuer(&self) -> Result; fn get_expiry_year_4_digit(&self) -> Secret;