Skip to content

Commit

Permalink
refactor(connector): add amount conversion framework to Riskified (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Sidharth-Singh10 authored Nov 26, 2024
1 parent c9df7b0 commit acb30ef
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 35 deletions.
41 changes: 36 additions & 5 deletions crates/router/src/connector/riskified.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
pub mod transformers;
use std::fmt::Debug;

#[cfg(feature = "frm")]
use base64::Engine;
use common_utils::types::{
AmountConvertor, MinorUnit, StringMajorUnit, StringMajorUnitForConnector,
};
#[cfg(feature = "frm")]
use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent};
#[cfg(feature = "frm")]
Expand All @@ -14,6 +16,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::{
Expand All @@ -35,10 +38,18 @@ use crate::{
utils::BytesExt,
};

#[derive(Debug, Clone)]
pub struct Riskified;
#[derive(Clone)]
pub struct Riskified {
amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}

impl Riskified {
pub fn new() -> &'static Self {
&Self {
amount_converter: &StringMajorUnitForConnector,
}
}

#[cfg(feature = "frm")]
pub fn generate_authorization_signature(
&self,
Expand Down Expand Up @@ -173,7 +184,17 @@ impl
req: &frm_types::FrmCheckoutRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let req_obj = riskified::RiskifiedPaymentsCheckoutRequest::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::RiskifiedPaymentsCheckoutRequest::try_from(&req_data)?;
Ok(RequestContent::Json(Box::new(req_obj)))
}

Expand Down Expand Up @@ -293,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)))
}
}
Expand Down
106 changes: 77 additions & 29 deletions crates/router/src/connector/riskified/transformers/api.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,48 @@
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::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
};
use error_stack::{self, ResultExt};
use masking::Secret;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{
connector::utils::{
AddressDetailsData, FraudCheckCheckoutRequest, FraudCheckTransactionRequest, RouterData,
convert_amount, AddressDetailsData, FraudCheckCheckoutRequest,
FraudCheckTransactionRequest, RouterData,
},
core::{errors, fraud_check::types as core_types},
types::{
self, api, api::Fulfillment, fraud_check as frm_types, storage::enums as storage_enums,
self,
api::{self, Fulfillment},
fraud_check as frm_types,
storage::enums as storage_enums,
ResponseId, ResponseRouterData,
},
};

type Error = error_stack::Report<errors::ConnectorError>;

pub struct RiskifiedRouterData<T> {
pub amount: StringMajorUnit,
pub router_data: T,
amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}

impl<T> From<(StringMajorUnit, T)> for RiskifiedRouterData<T> {
fn from((amount, router_data): (StringMajorUnit, T)) -> Self {
Self {
amount,
router_data,
amount_converter: &StringMajorUnitForConnector,
}
}
}

#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
pub struct RiskifiedPaymentsCheckoutRequest {
order: CheckoutRequest,
Expand All @@ -35,7 +60,7 @@ pub struct CheckoutRequest {
updated_at: PrimitiveDateTime,
gateway: Option<String>,
browser_ip: Option<std::net::IpAddr>,
total_price: i64,
total_price: StringMajorUnit,
total_discounts: i64,
cart_token: String,
referring_site: String,
Expand All @@ -60,13 +85,13 @@ pub struct PaymentDetails {

#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
pub struct ShippingLines {
price: i64,
price: StringMajorUnit,
title: Option<String>,
}

#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
pub struct DiscountCodes {
amount: i64,
amount: StringMajorUnit,
code: Option<String>,
}

Expand Down Expand Up @@ -110,7 +135,7 @@ pub struct OrderAddress {

#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
pub struct LineItem {
price: i64,
price: StringMajorUnit,
quantity: i32,
title: String,
product_type: Option<common_enums::ProductType>,
Expand All @@ -132,9 +157,14 @@ pub struct RiskifiedMetadata {
shipping_lines: Vec<ShippingLines>,
}

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<Self, Self::Error> {
fn try_from(
payment: &RiskifiedRouterData<&frm_types::FrmCheckoutRouterData>,
) -> Result<Self, Self::Error> {
let payment_data = payment.router_data.clone();
let metadata: RiskifiedMetadata = payment_data
.frm_metadata
.clone()
Expand All @@ -148,6 +178,33 @@ impl TryFrom<&frm_types::FrmCheckoutRouterData> for RiskifiedPaymentsCheckoutReq
let billing_address = payment_data.get_billing()?;
let shipping_address = payment_data.get_shipping_address_with_phone_number()?;
let address = payment_data.get_billing_address()?;
let line_items = payment_data
.request
.get_order_details()?
.iter()
.map(|order_detail| {
let price = convert_amount(
payment.amount_converter,
order_detail.amount,
payment_data.request.currency.ok_or_else(|| {
errors::ConnectorError::MissingRequiredField {
field_name: "currency",
}
})?,
)?;

Ok(LineItem {
price,
quantity: i32::from(order_detail.quantity),
title: order_detail.product_name.clone(),
product_type: order_detail.product_type.clone(),
requires_shipping: order_detail.requires_shipping,
product_id: order_detail.product_id.clone(),
category: order_detail.category.clone(),
brand: order_detail.brand.clone(),
})
})
.collect::<Result<Vec<_>, Self::Error>>()?;

Ok(Self {
order: CheckoutRequest {
Expand All @@ -156,23 +213,9 @@ 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: payment.amount.clone(),
cart_token: payment_data.attempt_id.clone(),
line_items: payment_data
.request
.get_order_details()?
.iter()
.map(|order_detail| LineItem {
price: order_detail.amount.get_amount_as_i64(), // This should be changed to MinorUnit when we implement amount conversion for this connector. Additionally, the function get_amount_as_i64() should be avoided in the future.
quantity: i32::from(order_detail.quantity),
title: order_detail.product_name.clone(),
product_type: order_detail.product_type.clone(),
requires_shipping: order_detail.requires_shipping,
product_id: order_detail.product_id.clone(),
category: order_detail.category.clone(),
brand: order_detail.brand.clone(),
})
.collect::<Vec<_>>(),
line_items,
source: Source::DesktopWeb,
billing_address: OrderAddress::try_from(billing_address).ok(),
shipping_address: OrderAddress::try_from(shipping_address).ok(),
Expand Down Expand Up @@ -411,7 +454,7 @@ pub struct SuccessfulTransactionData {
pub struct TransactionDecisionData {
external_status: TransactionStatus,
reason: Option<String>,
amount: i64,
amount: StringMajorUnit,
currency: storage_enums::Currency,
#[serde(with = "common_utils::custom_serde::iso8601")]
decided_at: PrimitiveDateTime,
Expand All @@ -429,16 +472,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<Self, Self::Error> {
fn try_from(
item_data: &RiskifiedRouterData<&frm_types::FrmTransactionRouterData>,
) -> Result<Self, Self::Error> {
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: item.request.amount,
amount: item_data.amount.clone(),
currency: item.request.get_currency()?,
decided_at: common_utils::date_time::now(),
payment_details: [TransactionPaymentDetails {
Expand Down
2 changes: 1 addition & 1 deletion crates/router/src/types/api/fraud_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())))
}
}
}
Expand Down

0 comments on commit acb30ef

Please sign in to comment.