mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
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:
@ -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,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>,
|
||||||
|
|||||||
@ -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")]
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user