refactor(connector): [CRYPTOPAY] amount conversion framework added (#4928)

Co-authored-by: Sahkal Poddar <sahkal.poddar@juspay.in>
Co-authored-by: Deepanshu Bansal <deepanshu.bansal@Deepanshu-Bansal-K3PYF02LFW.local>
This commit is contained in:
DEEPANSHU BANSAL
2024-06-10 17:19:44 +05:30
committed by GitHub
parent 7085a46271
commit adb9b11f69
4 changed files with 59 additions and 48 deletions

View File

@ -1,13 +1,12 @@
pub mod transformers; pub mod transformers;
use std::fmt::Debug;
use base64::Engine; use base64::Engine;
use common_utils::{ use common_utils::{
crypto::{self, GenerateDigest, SignMessage}, crypto::{self, GenerateDigest, SignMessage},
date_time, date_time,
ext_traits::ByteSliceExt, ext_traits::ByteSliceExt,
request::RequestContent, request::RequestContent,
types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
}; };
use error_stack::ResultExt; use error_stack::ResultExt;
use hex::encode; use hex::encode;
@ -36,8 +35,18 @@ use crate::{
utils::BytesExt, utils::BytesExt,
}; };
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct Cryptopay; pub struct Cryptopay {
amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}
impl Cryptopay {
pub fn new() -> &'static Self {
&Self {
amount_converter: &StringMajorUnitForConnector,
}
}
}
impl api::Payment for Cryptopay {} impl api::Payment for Cryptopay {}
impl api::PaymentSession for Cryptopay {} impl api::PaymentSession for Cryptopay {}
@ -123,7 +132,7 @@ where
(headers::DATE.to_string(), date.into()), (headers::DATE.to_string(), date.into()),
( (
headers::CONTENT_TYPE.to_string(), headers::CONTENT_TYPE.to_string(),
Self.get_content_type().to_string().into(), self.get_content_type().to_string().into(),
), ),
]; ];
Ok(headers) Ok(headers)
@ -244,12 +253,12 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
req: &types::PaymentsAuthorizeRouterData, req: &types::PaymentsAuthorizeRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = cryptopay::CryptopayRouterData::try_from(( let amount = utils::convert_amount(
&self.get_currency_unit(), self.amount_converter,
req.request.minor_amount,
req.request.currency, req.request.currency,
req.request.amount, )?;
req, let connector_router_data = cryptopay::CryptopayRouterData::from((amount, req));
))?;
let connector_req = cryptopay::CryptopayPaymentsRequest::try_from(&connector_router_data)?; let connector_req = cryptopay::CryptopayPaymentsRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }
@ -288,13 +297,21 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response)); event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response); router_env::logger::info!(connector_response=?response);
let capture_amount_in_minor_units = match response.data.price_amount {
Some(ref amount) => Some(utils::convert_back(
self.amount_converter,
amount.clone(),
data.request.currency,
)?),
None => None,
};
types::RouterData::foreign_try_from(( types::RouterData::foreign_try_from((
types::ResponseRouterData { types::ResponseRouterData {
response, response,
data: data.clone(), data: data.clone(),
http_code: res.status_code, http_code: res.status_code,
}, },
data.request.currency, capture_amount_in_minor_units,
)) ))
} }
@ -375,13 +392,21 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response)); event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response); router_env::logger::info!(connector_response=?response);
let capture_amount_in_minor_units = match response.data.price_amount {
Some(ref amount) => Some(utils::convert_back(
self.amount_converter,
amount.clone(),
data.request.currency,
)?),
None => None,
};
types::RouterData::foreign_try_from(( types::RouterData::foreign_try_from((
types::ResponseRouterData { types::ResponseRouterData {
response, response,
data: data.clone(), data: data.clone(),
http_code: res.status_code, http_code: res.status_code,
}, },
data.request.currency, capture_amount_in_minor_units,
)) ))
} }

View File

@ -1,10 +1,11 @@
use common_utils::pii; use common_utils::{
use error_stack::ResultExt; pii,
types::{MinorUnit, StringMajorUnit},
};
use masking::Secret; use masking::Secret;
use reqwest::Url; use reqwest::Url;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::utils as connector_utils;
use crate::{ use crate::{
connector::utils::{self, is_payment_failure, CryptoData, PaymentsAuthorizeRequestData}, connector::utils::{self, is_payment_failure, CryptoData, PaymentsAuthorizeRequestData},
consts, consts,
@ -15,31 +16,22 @@ use crate::{
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct CryptopayRouterData<T> { pub struct CryptopayRouterData<T> {
pub amount: String, pub amount: StringMajorUnit,
pub router_data: T, pub router_data: T,
} }
impl<T> TryFrom<(&types::api::CurrencyUnit, enums::Currency, i64, T)> for CryptopayRouterData<T> { impl<T> From<(StringMajorUnit, T)> for CryptopayRouterData<T> {
type Error = error_stack::Report<errors::ConnectorError>; fn from((amount, item): (StringMajorUnit, T)) -> Self {
fn try_from( Self {
(currency_unit, currency, amount, item): (
&types::api::CurrencyUnit,
enums::Currency,
i64,
T,
),
) -> Result<Self, Self::Error> {
let amount = utils::get_amount_as_string(currency_unit, amount, currency)?;
Ok(Self {
amount, amount,
router_data: item, router_data: item,
}) }
} }
} }
#[derive(Default, Debug, Serialize)] #[derive(Default, Debug, Serialize)]
pub struct CryptopayPaymentsRequest { pub struct CryptopayPaymentsRequest {
price_amount: String, price_amount: StringMajorUnit,
price_currency: enums::Currency, price_currency: enums::Currency,
pay_currency: String, pay_currency: String,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@ -62,7 +54,7 @@ impl TryFrom<&CryptopayRouterData<&types::PaymentsAuthorizeRouterData>>
domain::PaymentMethodData::Crypto(ref cryptodata) => { domain::PaymentMethodData::Crypto(ref cryptodata) => {
let pay_currency = cryptodata.get_pay_currency()?; let pay_currency = cryptodata.get_pay_currency()?;
Ok(Self { Ok(Self {
price_amount: item.amount.to_owned(), price_amount: item.amount.clone(),
price_currency: item.router_data.request.currency, price_currency: item.router_data.request.currency,
pay_currency, pay_currency,
network: cryptodata.network.to_owned(), network: cryptodata.network.to_owned(),
@ -140,20 +132,20 @@ impl From<CryptopayPaymentStatus> for enums::AttemptStatus {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct CryptopayPaymentsResponse { pub struct CryptopayPaymentsResponse {
data: CryptopayPaymentResponseData, pub data: CryptopayPaymentResponseData,
} }
impl<F, T> impl<F, T>
ForeignTryFrom<( ForeignTryFrom<(
types::ResponseRouterData<F, CryptopayPaymentsResponse, T, types::PaymentsResponseData>, types::ResponseRouterData<F, CryptopayPaymentsResponse, T, types::PaymentsResponseData>,
diesel_models::enums::Currency, Option<MinorUnit>,
)> for types::RouterData<F, T, types::PaymentsResponseData> )> for types::RouterData<F, T, types::PaymentsResponseData>
{ {
type Error = error_stack::Report<errors::ConnectorError>; type Error = error_stack::Report<errors::ConnectorError>;
fn foreign_try_from( fn foreign_try_from(
(item, currency): ( (item, amount_captured_in_minor_units): (
types::ResponseRouterData<F, CryptopayPaymentsResponse, T, types::PaymentsResponseData>, types::ResponseRouterData<F, CryptopayPaymentsResponse, T, types::PaymentsResponseData>,
diesel_models::enums::Currency, Option<MinorUnit>,
), ),
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
let status = enums::AttemptStatus::from(item.response.data.status.clone()); let status = enums::AttemptStatus::from(item.response.data.status.clone());
@ -196,15 +188,9 @@ impl<F, T>
charge_id: None, charge_id: None,
}) })
}; };
match amount_captured_in_minor_units {
match item.response.data.price_amount { Some(minor_amount) => {
Some(price_amount) => { let amount_captured = Some(minor_amount.get_amount_as_i64());
let amount_captured = Some(
connector_utils::to_currency_lower_unit(price_amount, currency)?
.parse::<i64>()
.change_context(errors::ConnectorError::ParsingFailed)?,
);
Ok(Self { Ok(Self {
status, status,
response, response,
@ -243,9 +229,9 @@ pub struct CryptopayPaymentResponseData {
pub address: Option<Secret<String>>, pub address: Option<Secret<String>>,
pub network: Option<String>, pub network: Option<String>,
pub uri: Option<String>, pub uri: Option<String>,
pub price_amount: Option<String>, pub price_amount: Option<StringMajorUnit>,
pub price_currency: Option<String>, pub price_currency: Option<String>,
pub pay_amount: Option<String>, pub pay_amount: Option<StringMajorUnit>,
pub pay_currency: Option<String>, pub pay_currency: Option<String>,
pub fee: Option<String>, pub fee: Option<String>,
pub fee_currency: Option<String>, pub fee_currency: Option<String>,

View File

@ -329,7 +329,7 @@ impl ConnectorData {
enums::Connector::Cashtocode => Ok(Box::new(&connector::Cashtocode)), enums::Connector::Cashtocode => Ok(Box::new(&connector::Cashtocode)),
enums::Connector::Checkout => Ok(Box::new(&connector::Checkout)), enums::Connector::Checkout => Ok(Box::new(&connector::Checkout)),
enums::Connector::Coinbase => Ok(Box::new(&connector::Coinbase)), enums::Connector::Coinbase => Ok(Box::new(&connector::Coinbase)),
enums::Connector::Cryptopay => Ok(Box::new(&connector::Cryptopay)), enums::Connector::Cryptopay => Ok(Box::new(connector::Cryptopay::new())),
enums::Connector::Cybersource => Ok(Box::new(&connector::Cybersource)), enums::Connector::Cybersource => Ok(Box::new(&connector::Cybersource)),
enums::Connector::Dlocal => Ok(Box::new(&connector::Dlocal)), enums::Connector::Dlocal => Ok(Box::new(&connector::Dlocal)),
#[cfg(feature = "dummy_connector")] #[cfg(feature = "dummy_connector")]

View File

@ -13,7 +13,7 @@ impl utils::Connector for CryptopayTest {
fn get_data(&self) -> api::ConnectorData { fn get_data(&self) -> api::ConnectorData {
use router::connector::Cryptopay; use router::connector::Cryptopay;
api::ConnectorData { api::ConnectorData {
connector: Box::new(&Cryptopay), connector: Box::new(Cryptopay::new()),
connector_name: types::Connector::Cryptopay, connector_name: types::Connector::Cryptopay,
get_token: api::GetToken::Connector, get_token: api::GetToken::Connector,
merchant_connector_id: None, merchant_connector_id: None,