mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-03 13:30:39 +08:00
refactor(connector): add amount conversion framework to Shift4 (#6250)
This commit is contained in:
@ -1,8 +1,10 @@
|
|||||||
pub mod transformers;
|
pub mod transformers;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use common_utils::{
|
||||||
|
ext_traits::ByteSliceExt,
|
||||||
use common_utils::{ext_traits::ByteSliceExt, request::RequestContent};
|
request::RequestContent,
|
||||||
|
types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
|
||||||
|
};
|
||||||
use diesel_models::enums;
|
use diesel_models::enums;
|
||||||
use error_stack::{report, ResultExt};
|
use error_stack::{report, ResultExt};
|
||||||
use transformers as shift4;
|
use transformers as shift4;
|
||||||
@ -27,8 +29,18 @@ use crate::{
|
|||||||
utils::BytesExt,
|
utils::BytesExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Shift4;
|
pub struct Shift4 {
|
||||||
|
amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shift4 {
|
||||||
|
pub fn new() -> &'static Self {
|
||||||
|
&Self {
|
||||||
|
amount_converter: &MinorUnitForConnector,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Shift4
|
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Shift4
|
||||||
where
|
where
|
||||||
@ -206,7 +218,13 @@ 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_req = shift4::Shift4PaymentsRequest::try_from(req)?;
|
let amount = connector_utils::convert_amount(
|
||||||
|
self.amount_converter,
|
||||||
|
req.request.minor_amount,
|
||||||
|
req.request.currency,
|
||||||
|
)?;
|
||||||
|
let connector_router_data = shift4::Shift4RouterData::try_from((amount, req))?;
|
||||||
|
let connector_req = shift4::Shift4PaymentsRequest::try_from(&connector_router_data)?;
|
||||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,7 +488,21 @@ impl
|
|||||||
req: &types::PaymentsPreProcessingRouterData,
|
req: &types::PaymentsPreProcessingRouterData,
|
||||||
_connectors: &settings::Connectors,
|
_connectors: &settings::Connectors,
|
||||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||||
let connector_req = shift4::Shift4PaymentsRequest::try_from(req)?;
|
let amount = connector_utils::convert_amount(
|
||||||
|
self.amount_converter,
|
||||||
|
req.request.minor_amount.ok_or_else(|| {
|
||||||
|
errors::ConnectorError::MissingRequiredField {
|
||||||
|
field_name: "minor_amount",
|
||||||
|
}
|
||||||
|
})?,
|
||||||
|
req.request
|
||||||
|
.currency
|
||||||
|
.ok_or_else(|| errors::ConnectorError::MissingRequiredField {
|
||||||
|
field_name: "currency",
|
||||||
|
})?,
|
||||||
|
)?;
|
||||||
|
let connector_router_data = shift4::Shift4RouterData::try_from((amount, req))?;
|
||||||
|
let connector_req = shift4::Shift4PaymentsRequest::try_from(&connector_router_data)?;
|
||||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,7 +591,13 @@ impl
|
|||||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||||
_connectors: &settings::Connectors,
|
_connectors: &settings::Connectors,
|
||||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||||
let connector_req = shift4::Shift4PaymentsRequest::try_from(req)?;
|
let amount = connector_utils::convert_amount(
|
||||||
|
self.amount_converter,
|
||||||
|
req.request.minor_amount,
|
||||||
|
req.request.currency,
|
||||||
|
)?;
|
||||||
|
let connector_router_data = shift4::Shift4RouterData::try_from((amount, req))?;
|
||||||
|
let connector_req = shift4::Shift4PaymentsRequest::try_from(&connector_router_data)?;
|
||||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,7 +679,13 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
|
|||||||
req: &types::RefundsRouterData<api::Execute>,
|
req: &types::RefundsRouterData<api::Execute>,
|
||||||
_connectors: &settings::Connectors,
|
_connectors: &settings::Connectors,
|
||||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||||
let connector_req = shift4::Shift4RefundRequest::try_from(req)?;
|
let amount = connector_utils::convert_amount(
|
||||||
|
self.amount_converter,
|
||||||
|
req.request.minor_refund_amount,
|
||||||
|
req.request.currency,
|
||||||
|
)?;
|
||||||
|
let connector_router_data = shift4::Shift4RouterData::try_from((amount, req))?;
|
||||||
|
let connector_req = shift4::Shift4RefundRequest::try_from(&connector_router_data)?;
|
||||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use api_models::payments;
|
use api_models::payments;
|
||||||
use cards::CardNumber;
|
use cards::CardNumber;
|
||||||
use common_utils::pii::SecretSerdeValue;
|
use common_utils::{pii::SecretSerdeValue, types::MinorUnit};
|
||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
use masking::Secret;
|
use masking::Secret;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -23,11 +23,25 @@ trait Shift4AuthorizePreprocessingCommon {
|
|||||||
fn get_router_return_url(&self) -> Option<String>;
|
fn get_router_return_url(&self) -> Option<String>;
|
||||||
fn get_email_optional(&self) -> Option<pii::Email>;
|
fn get_email_optional(&self) -> Option<pii::Email>;
|
||||||
fn get_complete_authorize_url(&self) -> Option<String>;
|
fn get_complete_authorize_url(&self) -> Option<String>;
|
||||||
fn get_amount_required(&self) -> Result<i64, Error>;
|
|
||||||
fn get_currency_required(&self) -> Result<diesel_models::enums::Currency, Error>;
|
fn get_currency_required(&self) -> Result<diesel_models::enums::Currency, Error>;
|
||||||
fn get_payment_method_data_required(&self) -> Result<domain::PaymentMethodData, Error>;
|
fn get_payment_method_data_required(&self) -> Result<domain::PaymentMethodData, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Shift4RouterData<T> {
|
||||||
|
pub amount: MinorUnit,
|
||||||
|
pub router_data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TryFrom<(MinorUnit, T)> for Shift4RouterData<T> {
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
fn try_from((amount, item): (MinorUnit, T)) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
amount,
|
||||||
|
router_data: item,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Shift4AuthorizePreprocessingCommon for types::PaymentsAuthorizeData {
|
impl Shift4AuthorizePreprocessingCommon for types::PaymentsAuthorizeData {
|
||||||
fn get_email_optional(&self) -> Option<pii::Email> {
|
fn get_email_optional(&self) -> Option<pii::Email> {
|
||||||
self.email.clone()
|
self.email.clone()
|
||||||
@ -37,10 +51,6 @@ impl Shift4AuthorizePreprocessingCommon for types::PaymentsAuthorizeData {
|
|||||||
self.complete_authorize_url.clone()
|
self.complete_authorize_url.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_amount_required(&self) -> Result<i64, error_stack::Report<errors::ConnectorError>> {
|
|
||||||
Ok(self.amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_currency_required(
|
fn get_currency_required(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<diesel_models::enums::Currency, error_stack::Report<errors::ConnectorError>> {
|
) -> Result<diesel_models::enums::Currency, error_stack::Report<errors::ConnectorError>> {
|
||||||
@ -70,10 +80,6 @@ impl Shift4AuthorizePreprocessingCommon for types::PaymentsPreProcessingData {
|
|||||||
self.complete_authorize_url.clone()
|
self.complete_authorize_url.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_amount_required(&self) -> Result<i64, Error> {
|
|
||||||
self.get_amount()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_currency_required(&self) -> Result<diesel_models::enums::Currency, Error> {
|
fn get_currency_required(&self) -> Result<diesel_models::enums::Currency, Error> {
|
||||||
self.get_currency()
|
self.get_currency()
|
||||||
}
|
}
|
||||||
@ -95,7 +101,7 @@ impl Shift4AuthorizePreprocessingCommon for types::PaymentsPreProcessingData {
|
|||||||
}
|
}
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct Shift4PaymentsRequest {
|
pub struct Shift4PaymentsRequest {
|
||||||
amount: String,
|
amount: MinorUnit,
|
||||||
currency: enums::Currency,
|
currency: enums::Currency,
|
||||||
captured: bool,
|
captured: bool,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
@ -195,19 +201,19 @@ pub enum CardPayment {
|
|||||||
CardToken(Secret<String>),
|
CardToken(Secret<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, Req> TryFrom<&types::RouterData<T, Req, types::PaymentsResponseData>>
|
impl<T, Req> TryFrom<&Shift4RouterData<&types::RouterData<T, Req, types::PaymentsResponseData>>>
|
||||||
for Shift4PaymentsRequest
|
for Shift4PaymentsRequest
|
||||||
where
|
where
|
||||||
Req: Shift4AuthorizePreprocessingCommon,
|
Req: Shift4AuthorizePreprocessingCommon,
|
||||||
{
|
{
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
fn try_from(
|
fn try_from(
|
||||||
item: &types::RouterData<T, Req, types::PaymentsResponseData>,
|
item: &Shift4RouterData<&types::RouterData<T, Req, types::PaymentsResponseData>>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
let submit_for_settlement = item.request.is_automatic_capture()?;
|
let submit_for_settlement = item.router_data.request.is_automatic_capture()?;
|
||||||
let amount = item.request.get_amount_required()?.to_string();
|
let amount = item.amount.to_owned();
|
||||||
let currency = item.request.get_currency_required()?;
|
let currency = item.router_data.request.get_currency_required()?;
|
||||||
let payment_method = Shift4PaymentMethod::try_from(item)?;
|
let payment_method = Shift4PaymentMethod::try_from(item.router_data)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
amount,
|
amount,
|
||||||
currency,
|
currency,
|
||||||
@ -437,27 +443,33 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TryFrom<&types::RouterData<T, types::CompleteAuthorizeData, types::PaymentsResponseData>>
|
impl<T>
|
||||||
for Shift4PaymentsRequest
|
TryFrom<
|
||||||
|
&Shift4RouterData<
|
||||||
|
&types::RouterData<T, types::CompleteAuthorizeData, types::PaymentsResponseData>,
|
||||||
|
>,
|
||||||
|
> for Shift4PaymentsRequest
|
||||||
{
|
{
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
fn try_from(
|
fn try_from(
|
||||||
item: &types::RouterData<T, types::CompleteAuthorizeData, types::PaymentsResponseData>,
|
item: &Shift4RouterData<
|
||||||
|
&types::RouterData<T, types::CompleteAuthorizeData, types::PaymentsResponseData>,
|
||||||
|
>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
match &item.request.payment_method_data {
|
match &item.router_data.request.payment_method_data {
|
||||||
Some(domain::PaymentMethodData::Card(_)) => {
|
Some(domain::PaymentMethodData::Card(_)) => {
|
||||||
let card_token: Shift4CardToken =
|
let card_token: Shift4CardToken =
|
||||||
to_connector_meta(item.request.connector_meta.clone())?;
|
to_connector_meta(item.router_data.request.connector_meta.clone())?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
amount: item.request.amount.to_string(),
|
amount: item.amount.to_owned(),
|
||||||
currency: item.request.currency,
|
currency: item.router_data.request.currency,
|
||||||
payment_method: Shift4PaymentMethod::CardsNon3DSRequest(Box::new(
|
payment_method: Shift4PaymentMethod::CardsNon3DSRequest(Box::new(
|
||||||
CardsNon3DSRequest {
|
CardsNon3DSRequest {
|
||||||
card: CardPayment::CardToken(card_token.id),
|
card: CardPayment::CardToken(card_token.id),
|
||||||
description: item.description.clone(),
|
description: item.router_data.description.clone(),
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
captured: item.request.is_auto_capture()?,
|
captured: item.router_data.request.is_auto_capture()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Some(domain::PaymentMethodData::Wallet(_))
|
Some(domain::PaymentMethodData::Wallet(_))
|
||||||
@ -687,7 +699,7 @@ pub struct Token {
|
|||||||
|
|
||||||
#[derive(Default, Debug, Deserialize, Serialize)]
|
#[derive(Default, Debug, Deserialize, Serialize)]
|
||||||
pub struct ThreeDSecureInfo {
|
pub struct ThreeDSecureInfo {
|
||||||
pub amount: i64,
|
pub amount: MinorUnit,
|
||||||
pub currency: String,
|
pub currency: String,
|
||||||
pub enrolled: bool,
|
pub enrolled: bool,
|
||||||
#[serde(rename = "liabilityShift")]
|
#[serde(rename = "liabilityShift")]
|
||||||
@ -814,15 +826,17 @@ impl<T, F>
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Shift4RefundRequest {
|
pub struct Shift4RefundRequest {
|
||||||
charge_id: String,
|
charge_id: String,
|
||||||
amount: i64,
|
amount: MinorUnit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for Shift4RefundRequest {
|
impl<F> TryFrom<&Shift4RouterData<&types::RefundsRouterData<F>>> for Shift4RefundRequest {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
fn try_from(
|
||||||
|
item: &Shift4RouterData<&types::RefundsRouterData<F>>,
|
||||||
|
) -> Result<Self, Self::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
charge_id: item.request.connector_transaction_id.clone(),
|
charge_id: item.router_data.request.connector_transaction_id.clone(),
|
||||||
amount: item.request.refund_amount,
|
amount: item.amount.to_owned(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -470,7 +470,9 @@ impl ConnectorData {
|
|||||||
Ok(ConnectorEnum::Old(Box::new(connector::Razorpay::new())))
|
Ok(ConnectorEnum::Old(Box::new(connector::Razorpay::new())))
|
||||||
}
|
}
|
||||||
enums::Connector::Rapyd => Ok(ConnectorEnum::Old(Box::new(&connector::Rapyd))),
|
enums::Connector::Rapyd => Ok(ConnectorEnum::Old(Box::new(&connector::Rapyd))),
|
||||||
enums::Connector::Shift4 => Ok(ConnectorEnum::Old(Box::new(&connector::Shift4))),
|
enums::Connector::Shift4 => {
|
||||||
|
Ok(ConnectorEnum::Old(Box::new(connector::Shift4::new())))
|
||||||
|
}
|
||||||
enums::Connector::Square => Ok(ConnectorEnum::Old(Box::new(&connector::Square))),
|
enums::Connector::Square => Ok(ConnectorEnum::Old(Box::new(&connector::Square))),
|
||||||
enums::Connector::Stax => Ok(ConnectorEnum::Old(Box::new(&connector::Stax))),
|
enums::Connector::Stax => Ok(ConnectorEnum::Old(Box::new(&connector::Stax))),
|
||||||
enums::Connector::Stripe => {
|
enums::Connector::Stripe => {
|
||||||
|
|||||||
@ -15,7 +15,7 @@ impl utils::Connector for Shift4Test {
|
|||||||
fn get_data(&self) -> types::api::ConnectorData {
|
fn get_data(&self) -> types::api::ConnectorData {
|
||||||
use router::connector::Shift4;
|
use router::connector::Shift4;
|
||||||
utils::construct_connector_data_old(
|
utils::construct_connector_data_old(
|
||||||
Box::new(&Shift4),
|
Box::new(Shift4::new()),
|
||||||
types::Connector::Shift4,
|
types::Connector::Shift4,
|
||||||
types::api::GetToken::Connector,
|
types::api::GetToken::Connector,
|
||||||
None,
|
None,
|
||||||
|
|||||||
Reference in New Issue
Block a user