From 9ca1c0240bb9aeee86b56e7a1991a4b759f95e00 Mon Sep 17 00:00:00 2001 From: Sidharth-Singh10 Date: Thu, 17 Oct 2024 23:02:26 +0530 Subject: [PATCH 1/4] refactor(connector): add amount conversion framework to Riskified --- crates/router/src/connector/riskified.rs | 24 +++++++++-- .../connector/riskified/transformers/api.rs | 43 +++++++++++++------ crates/router/src/types/api/fraud_check.rs | 2 +- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/crates/router/src/connector/riskified.rs b/crates/router/src/connector/riskified.rs index 0f8ebb46134..cf00c1122e5 100644 --- a/crates/router/src/connector/riskified.rs +++ b/crates/router/src/connector/riskified.rs @@ -1,8 +1,8 @@ pub mod transformers; -use std::fmt::Debug; #[cfg(feature = "frm")] use base64::Engine; +use common_utils::types::{AmountConvertor, MinorUnit, MinorUnitForConnector,StringMajorUnit}; #[cfg(feature = "frm")] use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent}; #[cfg(feature = "frm")] @@ -14,6 +14,7 @@ use ring::hmac; #[cfg(feature = "frm")] use transformers as riskified; +use super::utils::convert_amount; #[cfg(feature = "frm")] use super::utils::{self as connector_utils, FrmTransactionRouterDataRequest}; use crate::{ @@ -35,10 +36,19 @@ use crate::{ utils::BytesExt, }; -#[derive(Debug, Clone)] -pub struct Riskified; +#[derive(Clone)] +pub struct Riskified { + amount_converter: &'static (dyn AmountConvertor + Sync), +} impl Riskified { + + pub fn new() -> &'static Self { + &Self { + amount_converter: &MinorUnitForConnector, + } + } + #[cfg(feature = "frm")] pub fn generate_authorization_signature( &self, @@ -173,7 +183,13 @@ impl req: &frm_types::FrmCheckoutRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - let req_obj = riskified::RiskifiedPaymentsCheckoutRequest::try_from(req)?; + let amount = convert_amount( + self.amount_converter, + MinorUnit::new(req.request.amount), + req.request.currency.unwrap(), + )?; + let req_data = riskified::RiskifiedRouterData::from((amount, req)); + let req_obj = riskified::RiskifiedPaymentsCheckoutRequest::try_from(&req_data)?; Ok(RequestContent::Json(Box::new(req_obj))) } diff --git a/crates/router/src/connector/riskified/transformers/api.rs b/crates/router/src/connector/riskified/transformers/api.rs index 2e0ac3b0047..42a2b069536 100644 --- a/crates/router/src/connector/riskified/transformers/api.rs +++ b/crates/router/src/connector/riskified/transformers/api.rs @@ -1,5 +1,5 @@ use api_models::payments::AdditionalPaymentData; -use common_utils::{ext_traits::ValueExt, id_type, pii::Email}; +use common_utils::{ext_traits::ValueExt, id_type, pii::Email, types::MinorUnit}; use error_stack::{self, ResultExt}; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -18,6 +18,21 @@ use crate::{ type Error = error_stack::Report; +#[derive(Debug, Serialize)] +pub struct RiskifiedRouterData { + pub amount: MinorUnit, + pub router_data: T, +} + +impl From<(MinorUnit, T)> for RiskifiedRouterData { + fn from((amount, router_data): (MinorUnit, T)) -> Self { + Self { + amount, + router_data, + } + } +} + #[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)] pub struct RiskifiedPaymentsCheckoutRequest { order: CheckoutRequest, @@ -35,8 +50,8 @@ pub struct CheckoutRequest { updated_at: PrimitiveDateTime, gateway: Option, browser_ip: Option, - total_price: i64, - total_discounts: i64, + total_price: MinorUnit, + total_discounts: MinorUnit, cart_token: String, referring_site: String, line_items: Vec, @@ -60,13 +75,13 @@ pub struct PaymentDetails { #[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)] pub struct ShippingLines { - price: i64, + price: MinorUnit, title: Option, } #[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)] pub struct DiscountCodes { - amount: i64, + amount: MinorUnit, code: Option, } @@ -110,7 +125,7 @@ pub struct OrderAddress { #[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)] pub struct LineItem { - price: i64, + price: MinorUnit, quantity: i32, title: String, product_type: Option, @@ -132,9 +147,11 @@ pub struct RiskifiedMetadata { shipping_lines: Vec, } -impl TryFrom<&frm_types::FrmCheckoutRouterData> for RiskifiedPaymentsCheckoutRequest { +impl TryFrom<&RiskifiedRouterData<&frm_types::FrmCheckoutRouterData>> for RiskifiedPaymentsCheckoutRequest { type Error = Error; - fn try_from(payment_data: &frm_types::FrmCheckoutRouterData) -> Result { + fn try_from(payment: &RiskifiedRouterData<&frm_types::FrmCheckoutRouterData>) -> Result { + + let payment_data = payment.router_data.clone(); let metadata: RiskifiedMetadata = payment_data .frm_metadata .clone() @@ -156,14 +173,14 @@ impl TryFrom<&frm_types::FrmCheckoutRouterData> for RiskifiedPaymentsCheckoutReq created_at: common_utils::date_time::now(), updated_at: common_utils::date_time::now(), gateway: payment_data.request.gateway.clone(), - total_price: payment_data.request.amount, + total_price: MinorUnit::new(payment_data.request.amount), cart_token: payment_data.attempt_id.clone(), line_items: payment_data .request .get_order_details()? .iter() .map(|order_detail| LineItem { - price: order_detail.amount, + price: MinorUnit::new(order_detail.amount), quantity: i32::from(order_detail.quantity), title: order_detail.product_name.clone(), product_type: order_detail.product_type.clone(), @@ -176,7 +193,7 @@ impl TryFrom<&frm_types::FrmCheckoutRouterData> for RiskifiedPaymentsCheckoutReq source: Source::DesktopWeb, billing_address: OrderAddress::try_from(billing_address).ok(), shipping_address: OrderAddress::try_from(shipping_address).ok(), - total_discounts: 0, + total_discounts: MinorUnit::zero(), currency: payment_data.request.currency, referring_site: "hyperswitch.io".to_owned(), discount_codes: Vec::new(), @@ -411,7 +428,7 @@ pub struct SuccessfulTransactionData { pub struct TransactionDecisionData { external_status: TransactionStatus, reason: Option, - amount: i64, + amount: MinorUnit, currency: storage_enums::Currency, #[serde(with = "common_utils::custom_serde::iso8601")] decided_at: PrimitiveDateTime, @@ -438,7 +455,7 @@ impl TryFrom<&frm_types::FrmTransactionRouterData> for TransactionSuccessRequest decision: TransactionDecisionData { external_status: TransactionStatus::Approved, reason: None, - amount: item.request.amount, + amount: MinorUnit::new(item.request.amount), currency: item.request.get_currency()?, decided_at: common_utils::date_time::now(), payment_details: [TransactionPaymentDetails { diff --git a/crates/router/src/types/api/fraud_check.rs b/crates/router/src/types/api/fraud_check.rs index 2d1a42092f4..213aef9cf03 100644 --- a/crates/router/src/types/api/fraud_check.rs +++ b/crates/router/src/types/api/fraud_check.rs @@ -51,7 +51,7 @@ impl FraudCheckConnectorData { Ok(ConnectorEnum::Old(Box::new(&connector::Signifyd))) } enums::FrmConnectors::Riskified => { - Ok(ConnectorEnum::Old(Box::new(&connector::Riskified))) + Ok(ConnectorEnum::Old(Box::new(connector::Riskified::new()))) } } } From 3eda7cf0b98006924dc47e09396b66a8bd0f5ec1 Mon Sep 17 00:00:00 2001 From: Sidharth-Singh10 Date: Thu, 17 Oct 2024 23:27:54 +0530 Subject: [PATCH 2/4] refactor(connector): fix clippy warnings --- crates/router/src/connector/riskified.rs | 11 +++++++---- .../src/connector/riskified/transformers/api.rs | 9 ++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/crates/router/src/connector/riskified.rs b/crates/router/src/connector/riskified.rs index cf00c1122e5..a084b88166b 100644 --- a/crates/router/src/connector/riskified.rs +++ b/crates/router/src/connector/riskified.rs @@ -2,7 +2,7 @@ pub mod transformers; #[cfg(feature = "frm")] use base64::Engine; -use common_utils::types::{AmountConvertor, MinorUnit, MinorUnitForConnector,StringMajorUnit}; +use common_utils::types::{AmountConvertor, MinorUnit, MinorUnitForConnector}; #[cfg(feature = "frm")] use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent}; #[cfg(feature = "frm")] @@ -42,13 +42,12 @@ pub struct Riskified { } impl Riskified { - pub fn new() -> &'static Self { &Self { amount_converter: &MinorUnitForConnector, } } - + #[cfg(feature = "frm")] pub fn generate_authorization_signature( &self, @@ -186,7 +185,11 @@ impl let amount = convert_amount( self.amount_converter, MinorUnit::new(req.request.amount), - req.request.currency.unwrap(), + req.request + .currency + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "currency", + })?, )?; let req_data = riskified::RiskifiedRouterData::from((amount, req)); let req_obj = riskified::RiskifiedPaymentsCheckoutRequest::try_from(&req_data)?; diff --git a/crates/router/src/connector/riskified/transformers/api.rs b/crates/router/src/connector/riskified/transformers/api.rs index 42a2b069536..91ecca2d3a9 100644 --- a/crates/router/src/connector/riskified/transformers/api.rs +++ b/crates/router/src/connector/riskified/transformers/api.rs @@ -147,10 +147,13 @@ pub struct RiskifiedMetadata { shipping_lines: Vec, } -impl TryFrom<&RiskifiedRouterData<&frm_types::FrmCheckoutRouterData>> for RiskifiedPaymentsCheckoutRequest { +impl TryFrom<&RiskifiedRouterData<&frm_types::FrmCheckoutRouterData>> + for RiskifiedPaymentsCheckoutRequest +{ type Error = Error; - fn try_from(payment: &RiskifiedRouterData<&frm_types::FrmCheckoutRouterData>) -> Result { - + fn try_from( + payment: &RiskifiedRouterData<&frm_types::FrmCheckoutRouterData>, + ) -> Result { let payment_data = payment.router_data.clone(); let metadata: RiskifiedMetadata = payment_data .frm_metadata From 78b4bab1bcf348e642a5b6466583c214a64031bd Mon Sep 17 00:00:00 2001 From: Sidharth-Singh10 Date: Wed, 23 Oct 2024 19:21:27 +0530 Subject: [PATCH 3/4] replaced MinorUnit with StringMajorUnit --- crates/common_utils/src/types.rs | 2 +- crates/router/src/connector/riskified.rs | 8 ++++-- .../connector/riskified/transformers/api.rs | 28 +++++++++---------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index 0cad88bfd4f..88d7dd4b4a7 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -553,7 +553,7 @@ pub struct StringMajorUnit(String); impl StringMajorUnit { /// forms a new major unit from amount - fn new(value: String) -> Self { + pub fn new(value: String) -> Self { Self(value) } diff --git a/crates/router/src/connector/riskified.rs b/crates/router/src/connector/riskified.rs index a084b88166b..5b3ae91a439 100644 --- a/crates/router/src/connector/riskified.rs +++ b/crates/router/src/connector/riskified.rs @@ -2,7 +2,9 @@ pub mod transformers; #[cfg(feature = "frm")] use base64::Engine; -use common_utils::types::{AmountConvertor, MinorUnit, MinorUnitForConnector}; +use common_utils::types::{ + AmountConvertor, MinorUnit, StringMajorUnit, StringMajorUnitForConnector, +}; #[cfg(feature = "frm")] use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent}; #[cfg(feature = "frm")] @@ -38,13 +40,13 @@ use crate::{ #[derive(Clone)] pub struct Riskified { - amount_converter: &'static (dyn AmountConvertor + Sync), + amount_converter: &'static (dyn AmountConvertor + Sync), } impl Riskified { pub fn new() -> &'static Self { &Self { - amount_converter: &MinorUnitForConnector, + amount_converter: &StringMajorUnitForConnector, } } diff --git a/crates/router/src/connector/riskified/transformers/api.rs b/crates/router/src/connector/riskified/transformers/api.rs index 91ecca2d3a9..da6fed4ba00 100644 --- a/crates/router/src/connector/riskified/transformers/api.rs +++ b/crates/router/src/connector/riskified/transformers/api.rs @@ -1,5 +1,5 @@ use api_models::payments::AdditionalPaymentData; -use common_utils::{ext_traits::ValueExt, id_type, pii::Email, types::MinorUnit}; +use common_utils::{ext_traits::ValueExt, id_type, pii::Email, types::StringMajorUnit}; use error_stack::{self, ResultExt}; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -20,12 +20,12 @@ type Error = error_stack::Report; #[derive(Debug, Serialize)] pub struct RiskifiedRouterData { - pub amount: MinorUnit, + pub amount: StringMajorUnit, pub router_data: T, } -impl From<(MinorUnit, T)> for RiskifiedRouterData { - fn from((amount, router_data): (MinorUnit, T)) -> Self { +impl From<(StringMajorUnit, T)> for RiskifiedRouterData { + fn from((amount, router_data): (StringMajorUnit, T)) -> Self { Self { amount, router_data, @@ -50,8 +50,8 @@ pub struct CheckoutRequest { updated_at: PrimitiveDateTime, gateway: Option, browser_ip: Option, - total_price: MinorUnit, - total_discounts: MinorUnit, + total_price: StringMajorUnit, + total_discounts: StringMajorUnit, cart_token: String, referring_site: String, line_items: Vec, @@ -75,13 +75,13 @@ pub struct PaymentDetails { #[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)] pub struct ShippingLines { - price: MinorUnit, + price: StringMajorUnit, title: Option, } #[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)] pub struct DiscountCodes { - amount: MinorUnit, + amount: StringMajorUnit, code: Option, } @@ -125,7 +125,7 @@ pub struct OrderAddress { #[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)] pub struct LineItem { - price: MinorUnit, + price: StringMajorUnit, quantity: i32, title: String, product_type: Option, @@ -176,14 +176,14 @@ impl TryFrom<&RiskifiedRouterData<&frm_types::FrmCheckoutRouterData>> created_at: common_utils::date_time::now(), updated_at: common_utils::date_time::now(), gateway: payment_data.request.gateway.clone(), - total_price: MinorUnit::new(payment_data.request.amount), + total_price: StringMajorUnit::new(payment_data.request.amount.to_string()), cart_token: payment_data.attempt_id.clone(), line_items: payment_data .request .get_order_details()? .iter() .map(|order_detail| LineItem { - price: MinorUnit::new(order_detail.amount), + price: StringMajorUnit::new(order_detail.amount.to_string()), quantity: i32::from(order_detail.quantity), title: order_detail.product_name.clone(), product_type: order_detail.product_type.clone(), @@ -196,7 +196,7 @@ impl TryFrom<&RiskifiedRouterData<&frm_types::FrmCheckoutRouterData>> source: Source::DesktopWeb, billing_address: OrderAddress::try_from(billing_address).ok(), shipping_address: OrderAddress::try_from(shipping_address).ok(), - total_discounts: MinorUnit::zero(), + total_discounts: StringMajorUnit::new("0".to_string()), currency: payment_data.request.currency, referring_site: "hyperswitch.io".to_owned(), discount_codes: Vec::new(), @@ -431,7 +431,7 @@ pub struct SuccessfulTransactionData { pub struct TransactionDecisionData { external_status: TransactionStatus, reason: Option, - amount: MinorUnit, + amount: StringMajorUnit, currency: storage_enums::Currency, #[serde(with = "common_utils::custom_serde::iso8601")] decided_at: PrimitiveDateTime, @@ -458,7 +458,7 @@ impl TryFrom<&frm_types::FrmTransactionRouterData> for TransactionSuccessRequest decision: TransactionDecisionData { external_status: TransactionStatus::Approved, reason: None, - amount: MinorUnit::new(item.request.amount), + amount: StringMajorUnit::new(item.request.amount.to_string()), currency: item.request.get_currency()?, decided_at: common_utils::date_time::now(), payment_details: [TransactionPaymentDetails { From 1905c598cef9001c70eaec0990fa0a4e5399d11b Mon Sep 17 00:00:00 2001 From: Sidharth-Singh10 Date: Fri, 25 Oct 2024 13:21:58 +0530 Subject: [PATCH 4/4] maintain consistency in data type conversion --- crates/router/src/connector/riskified.rs | 12 +++++++++++- .../src/connector/riskified/transformers/api.rs | 13 +++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/crates/router/src/connector/riskified.rs b/crates/router/src/connector/riskified.rs index 5b3ae91a439..7d9feec717a 100644 --- a/crates/router/src/connector/riskified.rs +++ b/crates/router/src/connector/riskified.rs @@ -314,7 +314,17 @@ impl Ok(RequestContent::Json(Box::new(req_obj))) } _ => { - let req_obj = riskified::TransactionSuccessRequest::try_from(req)?; + let amount = convert_amount( + self.amount_converter, + MinorUnit::new(req.request.amount), + req.request + .currency + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "currency", + })?, + )?; + let req_data = riskified::RiskifiedRouterData::from((amount, req)); + let req_obj = riskified::TransactionSuccessRequest::try_from(&req_data)?; Ok(RequestContent::Json(Box::new(req_obj))) } } diff --git a/crates/router/src/connector/riskified/transformers/api.rs b/crates/router/src/connector/riskified/transformers/api.rs index da6fed4ba00..b3aed4e18d6 100644 --- a/crates/router/src/connector/riskified/transformers/api.rs +++ b/crates/router/src/connector/riskified/transformers/api.rs @@ -176,7 +176,7 @@ impl TryFrom<&RiskifiedRouterData<&frm_types::FrmCheckoutRouterData>> created_at: common_utils::date_time::now(), updated_at: common_utils::date_time::now(), gateway: payment_data.request.gateway.clone(), - total_price: StringMajorUnit::new(payment_data.request.amount.to_string()), + total_price: payment.amount.clone(), cart_token: payment_data.attempt_id.clone(), line_items: payment_data .request @@ -449,16 +449,21 @@ pub enum TransactionStatus { Approved, } -impl TryFrom<&frm_types::FrmTransactionRouterData> for TransactionSuccessRequest { +impl TryFrom<&RiskifiedRouterData<&frm_types::FrmTransactionRouterData>> + for TransactionSuccessRequest +{ type Error = Error; - fn try_from(item: &frm_types::FrmTransactionRouterData) -> Result { + fn try_from( + item_data: &RiskifiedRouterData<&frm_types::FrmTransactionRouterData>, + ) -> Result { + let item = item_data.router_data.clone(); Ok(Self { order: SuccessfulTransactionData { id: item.attempt_id.clone(), decision: TransactionDecisionData { external_status: TransactionStatus::Approved, reason: None, - amount: StringMajorUnit::new(item.request.amount.to_string()), + amount: item_data.amount.clone(), currency: item.request.get_currency()?, decided_at: common_utils::date_time::now(), payment_details: [TransactionPaymentDetails {