refactor(connector): add amount framework to payme & Trustpay with googlePay, ApplePay for bluesnap, Noon & Trustpay (#4833)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
Co-authored-by: Hrithikesh <61539176+hrithikesh026@users.noreply.github.com>
Co-authored-by: Narayan Bhat <narayan.bhat@juspay.in>
This commit is contained in:
Sahkal Poddar
2024-06-26 14:44:48 +05:30
committed by GitHub
parent ffe90a4112
commit e69a7bda52
14 changed files with 224 additions and 146 deletions

View File

@ -5052,7 +5052,8 @@
}, },
"amount": { "amount": {
"type": "string", "type": "string",
"description": "The total amount for the payment" "description": "The total amount for the payment in majot unit string (Ex: 38.02)",
"example": "38.02"
} }
} }
}, },
@ -10203,7 +10204,8 @@
}, },
"total_price": { "total_price": {
"type": "string", "type": "string",
"description": "The total price" "description": "The total price",
"example": "38.02"
} }
} }
}, },

View File

@ -11,7 +11,7 @@ use common_utils::{
ext_traits::{ConfigExt, Encode}, ext_traits::{ConfigExt, Encode},
id_type, id_type,
pii::{self, Email}, pii::{self, Email},
types::MinorUnit, types::{MinorUnit, StringMajorUnit},
}; };
use masking::{PeekInterface, Secret}; use masking::{PeekInterface, Secret};
use router_derive::Setter; use router_derive::Setter;
@ -4175,7 +4175,8 @@ pub struct GpayTransactionInfo {
/// The total price status (ex: 'FINAL') /// The total price status (ex: 'FINAL')
pub total_price_status: String, pub total_price_status: String,
/// The total price /// The total price
pub total_price: String, #[schema(value_type = String, example = "38.02")]
pub total_price: StringMajorUnit,
} }
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)] #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
@ -4547,8 +4548,9 @@ pub struct AmountInfo {
/// A value that indicates whether the line item(Ex: total, tax, discount, or grand total) is final or pending. /// A value that indicates whether the line item(Ex: total, tax, discount, or grand total) is final or pending.
#[serde(rename = "type")] #[serde(rename = "type")]
pub total_type: Option<String>, pub total_type: Option<String>,
/// The total amount for the payment /// The total amount for the payment in majot unit string (Ex: 38.02)
pub amount: String, #[schema(value_type = String, example = "38.02")]
pub amount: StringMajorUnit,
} }
#[derive(Debug, Clone, serde::Deserialize)] #[derive(Debug, Clone, serde::Deserialize)]

View File

@ -685,6 +685,9 @@ pub struct PaymentsSessionData {
pub country: Option<common_enums::CountryAlpha2>, pub country: Option<common_enums::CountryAlpha2>,
pub surcharge_details: Option<SurchargeDetails>, pub surcharge_details: Option<SurchargeDetails>,
pub order_details: Option<Vec<api_models::payments::OrderDetailsWithAmount>>, pub order_details: Option<Vec<api_models::payments::OrderDetailsWithAmount>>,
// Minor Unit amount for amount frame work
pub minor_amount: MinorUnit,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -33,6 +33,7 @@ use crate::{
types::{ types::{
self, self,
api::{self, ConnectorCommon, ConnectorCommonExt}, api::{self, ConnectorCommon, ConnectorCommonExt},
transformers::ForeignTryFrom,
ErrorResponse, Response, ErrorResponse, Response,
}, },
utils::BytesExt, utils::BytesExt,
@ -603,11 +604,20 @@ impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::Payme
.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));
types::RouterData::try_from(types::ResponseRouterData { let req_amount = data.request.minor_amount;
response, let req_currency = data.request.currency;
data: data.clone(),
http_code: res.status_code, let apple_pay_amount =
}) connector_utils::convert_amount(self.amount_converter, req_amount, req_currency)?;
types::RouterData::foreign_try_from((
types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
},
apple_pay_amount,
))
} }
fn get_error_response( fn get_error_response(

View File

@ -470,12 +470,18 @@ impl TryFrom<&types::PaymentsSessionRouterData> for BluesnapCreateWalletToken {
} }
} }
impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenResponse>> impl
for types::PaymentsSessionRouterData ForeignTryFrom<(
types::PaymentsSessionResponseRouterData<BluesnapWalletTokenResponse>,
StringMajorUnit,
)> for types::PaymentsSessionRouterData
{ {
type Error = error_stack::Report<errors::ConnectorError>; type Error = error_stack::Report<errors::ConnectorError>;
fn try_from( fn foreign_try_from(
item: types::PaymentsSessionResponseRouterData<BluesnapWalletTokenResponse>, (item, apple_pay_amount): (
types::PaymentsSessionResponseRouterData<BluesnapWalletTokenResponse>,
StringMajorUnit,
),
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
let response = &item.response; let response = &item.response;
@ -532,7 +538,7 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons
total: payments::AmountInfo { total: payments::AmountInfo {
label: payment_request_data.label, label: payment_request_data.label,
total_type: Some("final".to_string()), total_type: Some("final".to_string()),
amount: item.data.request.amount.to_string(), amount: apple_pay_amount,
}, },
merchant_capabilities: Some(payment_request_data.merchant_capabilities), merchant_capabilities: Some(payment_request_data.merchant_capabilities),
supported_networks: Some(payment_request_data.supported_networks), supported_networks: Some(payment_request_data.supported_networks),

View File

@ -1,9 +1,14 @@
pub mod transformers; pub mod transformers;
use std::fmt::Debug;
use api_models::enums::AuthenticationType; use api_models::enums::AuthenticationType;
use common_utils::{crypto, request::RequestContent}; use common_utils::{
crypto,
request::RequestContent,
types::{
AmountConvertor, MinorUnit, MinorUnitForConnector, StringMajorUnit,
StringMajorUnitForConnector,
},
};
use diesel_models::enums; use diesel_models::enums;
use error_stack::{Report, ResultExt}; use error_stack::{Report, ResultExt};
use masking::ExposeInterface; use masking::ExposeInterface;
@ -22,14 +27,30 @@ use crate::{
types::{ types::{
self, self,
api::{self, ConnectorCommon, ConnectorCommonExt}, api::{self, ConnectorCommon, ConnectorCommonExt},
domain, ErrorResponse, Response, domain,
transformers::ForeignTryFrom,
ErrorResponse, Response,
}, },
// transformers::{ForeignFrom, ForeignTryFrom},
utils::{handle_json_response_deserialization_failure, BytesExt}, utils::{handle_json_response_deserialization_failure, BytesExt},
}; };
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct Payme; pub struct Payme {
amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
apple_pay_google_pay_amount_converter:
&'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}
impl Payme {
pub const fn new() -> &'static Self {
&Self {
amount_converter: &MinorUnitForConnector,
apple_pay_google_pay_amount_converter: &StringMajorUnitForConnector,
}
}
}
// dummy commit
impl api::Payment for Payme {} impl api::Payment for Payme {}
impl api::PaymentSession for Payme {} impl api::PaymentSession for Payme {}
impl api::PaymentsCompleteAuthorize for Payme {} impl api::PaymentsCompleteAuthorize for Payme {}
@ -287,10 +308,11 @@ impl
req: &types::PaymentsPreProcessingRouterData, req: &types::PaymentsPreProcessingRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount = req.request.get_amount()?; let req_amount = req.request.get_minor_amount()?;
let currency = req.request.get_currency()?; let req_currency = req.request.get_currency()?;
let connector_router_data = let amount =
payme::PaymeRouterData::try_from((&self.get_currency_unit(), currency, amount, req))?; connector_utils::convert_amount(self.amount_converter, req_amount, req_currency)?;
let connector_router_data = payme::PaymeRouterData::try_from((amount, req))?;
let connector_req = payme::GenerateSaleRequest::try_from(&connector_router_data)?; let connector_req = payme::GenerateSaleRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }
@ -329,14 +351,26 @@ impl
.parse_struct("Payme GenerateSaleResponse") .parse_struct("Payme GenerateSaleResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
let req_amount = data.request.get_minor_amount()?;
let req_currency = data.request.get_currency()?;
let apple_pay_amount = connector_utils::convert_amount(
self.apple_pay_google_pay_amount_converter,
req_amount,
req_currency,
)?;
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);
types::RouterData::try_from(types::ResponseRouterData { types::RouterData::foreign_try_from((
response, types::ResponseRouterData {
data: data.clone(), response,
http_code: res.status_code, data: data.clone(),
}) http_code: res.status_code,
},
apple_pay_amount,
))
} }
fn get_error_response( fn get_error_response(
@ -536,12 +570,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 = payme::PaymeRouterData::try_from(( let amount = connector_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 = payme::PaymeRouterData::try_from((amount, req))?;
))?;
let connector_req = payme::PaymePaymentRequest::try_from(&connector_router_data)?; let connector_req = payme::PaymePaymentRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }
@ -724,12 +758,12 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
req: &types::PaymentsCaptureRouterData, req: &types::PaymentsCaptureRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = payme::PaymeRouterData::try_from(( let amount = connector_utils::convert_amount(
&self.get_currency_unit(), self.amount_converter,
req.request.minor_amount_to_capture,
req.request.currency, req.request.currency,
req.request.amount_to_capture, )?;
req, let connector_router_data = payme::PaymeRouterData::try_from((amount, req))?;
))?;
let connector_req = payme::PaymentCaptureRequest::try_from(&connector_router_data)?; let connector_req = payme::PaymentCaptureRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }
@ -822,20 +856,21 @@ impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsR
req: &types::PaymentsCancelRouterData, req: &types::PaymentsCancelRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount = req let req_amount =
.request req.request
.amount .minor_amount
.ok_or(errors::ConnectorError::MissingRequiredField { .ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "amount", field_name: "amount",
})?; })?;
let currency = let req_currency =
req.request req.request
.currency .currency
.ok_or(errors::ConnectorError::MissingRequiredField { .ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "currency", field_name: "currency",
})?; })?;
let connector_router_data = let amount =
payme::PaymeRouterData::try_from((&self.get_currency_unit(), currency, amount, req))?; connector_utils::convert_amount(self.amount_converter, req_amount, req_currency)?;
let connector_router_data = payme::PaymeRouterData::try_from((amount, req))?;
let connector_req = payme::PaymeVoidRequest::try_from(&connector_router_data)?; let connector_req = payme::PaymeVoidRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }
@ -922,12 +957,12 @@ 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_router_data = payme::PaymeRouterData::try_from(( let amount = connector_utils::convert_amount(
&self.get_currency_unit(), self.amount_converter,
req.request.minor_refund_amount,
req.request.currency, req.request.currency,
req.request.refund_amount, )?;
req, let connector_router_data = payme::PaymeRouterData::try_from((amount, req))?;
))?;
let connector_req = payme::PaymeRefundRequest::try_from(&connector_router_data)?; let connector_req = payme::PaymeRefundRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
} }

View File

@ -1,7 +1,10 @@
use std::collections::HashMap; use std::collections::HashMap;
use api_models::enums::{AuthenticationType, PaymentMethod}; use api_models::enums::{AuthenticationType, PaymentMethod};
use common_utils::pii; use common_utils::{
pii,
types::{MinorUnit, StringMajorUnit},
};
use error_stack::ResultExt; use error_stack::ResultExt;
use masking::{ExposeInterface, Secret}; use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -18,7 +21,10 @@ use crate::{
core::errors, core::errors,
services, services,
types::{ types::{
self, api, domain, domain::PaymentMethodData, storage::enums, transformers::ForeignFrom, self, api, domain,
domain::PaymentMethodData,
storage::enums,
transformers::{ForeignFrom, ForeignTryFrom},
MandateReference, MandateReference,
}, },
unimplemented_payment_method, unimplemented_payment_method,
@ -28,15 +34,13 @@ const LANGUAGE: &str = "en";
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct PaymeRouterData<T> { pub struct PaymeRouterData<T> {
pub amount: i64, pub amount: MinorUnit,
pub router_data: T, pub router_data: T,
} }
impl<T> TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for PaymeRouterData<T> { impl<T> TryFrom<(MinorUnit, T)> for PaymeRouterData<T> {
type Error = error_stack::Report<errors::ConnectorError>; type Error = error_stack::Report<errors::ConnectorError>;
fn try_from( fn try_from((amount, item): (MinorUnit, T)) -> Result<Self, Self::Error> {
(_currency_unit, _currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T),
) -> Result<Self, Self::Error> {
Ok(Self { Ok(Self {
amount, amount,
router_data: item, router_data: item,
@ -57,7 +61,7 @@ pub struct PayRequest {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct MandateRequest { pub struct MandateRequest {
currency: enums::Currency, currency: enums::Currency,
sale_price: i64, sale_price: MinorUnit,
transaction_id: String, transaction_id: String,
product_name: String, product_name: String,
sale_return_url: String, sale_return_url: String,
@ -118,7 +122,7 @@ pub struct CaptureBuyerResponse {
pub struct GenerateSaleRequest { pub struct GenerateSaleRequest {
currency: enums::Currency, currency: enums::Currency,
sale_type: SaleType, sale_type: SaleType,
sale_price: i64, sale_price: MinorUnit,
transaction_id: String, transaction_id: String,
product_name: String, product_name: String,
sale_return_url: String, sale_return_url: String,
@ -473,23 +477,27 @@ impl TryFrom<&types::RefundSyncRouterData> for PaymeQueryTransactionRequest {
} }
impl<F> impl<F>
TryFrom< ForeignTryFrom<(
types::ResponseRouterData< types::ResponseRouterData<
F, F,
GenerateSaleResponse, GenerateSaleResponse,
types::PaymentsPreProcessingData, types::PaymentsPreProcessingData,
types::PaymentsResponseData, types::PaymentsResponseData,
>, >,
> for types::RouterData<F, types::PaymentsPreProcessingData, types::PaymentsResponseData> StringMajorUnit,
)> for types::RouterData<F, types::PaymentsPreProcessingData, types::PaymentsResponseData>
{ {
type Error = error_stack::Report<errors::ConnectorError>; type Error = error_stack::Report<errors::ConnectorError>;
fn try_from( fn foreign_try_from(
item: types::ResponseRouterData< (item, apple_pay_amount): (
F, types::ResponseRouterData<
GenerateSaleResponse, F,
types::PaymentsPreProcessingData, GenerateSaleResponse,
types::PaymentsResponseData, types::PaymentsPreProcessingData,
>, types::PaymentsResponseData,
>,
StringMajorUnit,
),
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
match item.data.payment_method { match item.data.payment_method {
PaymentMethod::Card => { PaymentMethod::Card => {
@ -537,8 +545,6 @@ impl<F>
} }
_ => { _ => {
let currency_code = item.data.request.get_currency()?; let currency_code = item.data.request.get_currency()?;
let amount = item.data.request.get_amount()?;
let amount_in_base_unit = utils::to_currency_base_unit(amount, currency_code)?;
let pmd = item.data.request.payment_method_data.to_owned(); let pmd = item.data.request.payment_method_data.to_owned();
let payme_auth_type = PaymeAuthType::try_from(&item.data.connector_auth_type)?; let payme_auth_type = PaymeAuthType::try_from(&item.data.connector_auth_type)?;
@ -556,7 +562,7 @@ impl<F>
total: api_models::payments::AmountInfo { total: api_models::payments::AmountInfo {
label: "Apple Pay".to_string(), label: "Apple Pay".to_string(),
total_type: None, total_type: None,
amount: amount_in_base_unit, amount: apple_pay_amount,
}, },
merchant_capabilities: None, merchant_capabilities: None,
supported_networks: None, supported_networks: None,
@ -907,7 +913,7 @@ impl<F, T>
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct PaymentCaptureRequest { pub struct PaymentCaptureRequest {
payme_sale_id: String, payme_sale_id: String,
sale_price: i64, sale_price: MinorUnit,
} }
impl TryFrom<&PaymeRouterData<&types::PaymentsCaptureRouterData>> for PaymentCaptureRequest { impl TryFrom<&PaymeRouterData<&types::PaymentsCaptureRouterData>> for PaymentCaptureRequest {
@ -915,7 +921,9 @@ impl TryFrom<&PaymeRouterData<&types::PaymentsCaptureRouterData>> for PaymentCap
fn try_from( fn try_from(
item: &PaymeRouterData<&types::PaymentsCaptureRouterData>, item: &PaymeRouterData<&types::PaymentsCaptureRouterData>,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
if item.router_data.request.amount_to_capture != item.router_data.request.payment_amount { if item.router_data.request.minor_amount_to_capture
!= item.router_data.request.minor_payment_amount
{
Err(errors::ConnectorError::NotSupported { Err(errors::ConnectorError::NotSupported {
message: "Partial Capture".to_string(), message: "Partial Capture".to_string(),
connector: "Payme", connector: "Payme",
@ -932,7 +940,7 @@ impl TryFrom<&PaymeRouterData<&types::PaymentsCaptureRouterData>> for PaymentCap
// Type definition for RefundRequest // Type definition for RefundRequest
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct PaymeRefundRequest { pub struct PaymeRefundRequest {
sale_refund_amount: i64, sale_refund_amount: MinorUnit,
payme_sale_id: String, payme_sale_id: String,
seller_payme_id: Secret<String>, seller_payme_id: Secret<String>,
language: String, language: String,

View File

@ -1,18 +1,21 @@
pub mod transformers; pub mod transformers;
use std::fmt::Debug;
use base64::Engine; use base64::Engine;
use common_utils::{ use common_utils::{
crypto, errors::ReportSwitchExt, ext_traits::ByteSliceExt, request::RequestContent, crypto,
errors::ReportSwitchExt,
ext_traits::ByteSliceExt,
request::RequestContent,
types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
}; };
use error_stack::{Report, ResultExt}; use error_stack::{Report, ResultExt};
use masking::PeekInterface; use masking::PeekInterface;
use transformers as trustpay; use transformers as trustpay;
use super::utils::{ use super::utils::{
collect_and_sort_values_by_removing_signature, get_error_code_error_message_based_on_priority, self as connector_utils, collect_and_sort_values_by_removing_signature,
ConnectorErrorType, ConnectorErrorTypeMapping, PaymentsPreProcessingData, get_error_code_error_message_based_on_priority, ConnectorErrorType, ConnectorErrorTypeMapping,
PaymentsPreProcessingData,
}; };
use crate::{ use crate::{
configs::settings, configs::settings,
@ -36,8 +39,18 @@ use crate::{
utils::{self, BytesExt}, utils::{self, BytesExt},
}; };
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct Trustpay; pub struct Trustpay {
amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}
impl Trustpay {
pub fn new() -> &'static Self {
&Self {
amount_converter: &StringMajorUnitForConnector,
}
}
}
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Trustpay impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Trustpay
where where
@ -462,14 +475,13 @@ impl
req: &types::PaymentsPreProcessingRouterData, req: &types::PaymentsPreProcessingRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let currency = req.request.get_currency()?; let req_currency = req.request.get_currency()?;
let amount = req.request.get_amount()?; let req_amount = req.request.get_minor_amount()?;
let connector_router_data = trustpay::TrustpayRouterData::try_from((
&self.get_currency_unit(), let amount =
currency, connector_utils::convert_amount(self.amount_converter, req_amount, req_currency)?;
amount,
req, let connector_router_data = trustpay::TrustpayRouterData::try_from((amount, req))?;
))?;
let connector_req = let connector_req =
trustpay::TrustpayCreateIntentRequest::try_from(&connector_router_data)?; trustpay::TrustpayCreateIntentRequest::try_from(&connector_router_data)?;
Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))) Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
@ -576,13 +588,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 amount = req.request.amount; let amount = connector_utils::convert_amount(
let connector_router_data = trustpay::TrustpayRouterData::try_from(( self.amount_converter,
&self.get_currency_unit(), req.request.minor_amount,
req.request.currency, req.request.currency,
amount, )?;
req, let connector_router_data = trustpay::TrustpayRouterData::try_from((amount, req))?;
))?;
let connector_req = trustpay::TrustpayPaymentsRequest::try_from(&connector_router_data)?; let connector_req = trustpay::TrustpayPaymentsRequest::try_from(&connector_router_data)?;
match req.payment_method { match req.payment_method {
diesel_models::enums::PaymentMethod::BankRedirect => { diesel_models::enums::PaymentMethod::BankRedirect => {
@ -686,12 +697,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_router_data = trustpay::TrustpayRouterData::try_from(( let amount = connector_utils::convert_amount(
&self.get_currency_unit(), self.amount_converter,
req.request.minor_refund_amount,
req.request.currency, req.request.currency,
req.request.refund_amount, )?;
req,
))?; let connector_router_data = trustpay::TrustpayRouterData::try_from((amount, req))?;
let connector_req = trustpay::TrustpayRefundRequest::try_from(&connector_router_data)?; let connector_req = trustpay::TrustpayRefundRequest::try_from(&connector_router_data)?;
match req.payment_method { match req.payment_method {
diesel_models::enums::PaymentMethod::BankRedirect => { diesel_models::enums::PaymentMethod::BankRedirect => {

View File

@ -3,6 +3,7 @@ use std::collections::HashMap;
use common_utils::{ use common_utils::{
errors::CustomResult, errors::CustomResult,
pii::{self, Email}, pii::{self, Email},
types::StringMajorUnit,
}; };
use error_stack::{report, ResultExt}; use error_stack::{report, ResultExt};
use masking::{ExposeInterface, PeekInterface, Secret}; use masking::{ExposeInterface, PeekInterface, Secret};
@ -24,21 +25,13 @@ type Error = error_stack::Report<errors::ConnectorError>;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct TrustpayRouterData<T> { pub struct TrustpayRouterData<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 TrustpayRouterData<T> { impl<T> TryFrom<(StringMajorUnit, T)> for TrustpayRouterData<T> {
type Error = error_stack::Report<errors::ConnectorError>; type Error = error_stack::Report<errors::ConnectorError>;
fn try_from( fn try_from((amount, item): (StringMajorUnit, T)) -> Result<Self, Self::Error> {
(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 { Ok(Self {
amount, amount,
router_data: item, router_data: item,
@ -97,7 +90,7 @@ pub struct References {
#[derive(Default, Debug, Serialize, Deserialize, Eq, PartialEq, Clone)] #[derive(Default, Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]
pub struct Amount { pub struct Amount {
pub amount: String, pub amount: StringMajorUnit,
pub currency: String, pub currency: String,
} }
@ -147,7 +140,7 @@ pub struct CallbackURLs {
#[derive(Debug, Serialize, PartialEq)] #[derive(Debug, Serialize, PartialEq)]
pub struct PaymentRequestCards { pub struct PaymentRequestCards {
pub amount: String, pub amount: StringMajorUnit,
pub currency: String, pub currency: String,
pub pan: cards::CardNumber, pub pan: cards::CardNumber,
pub cvv: Secret<String>, pub cvv: Secret<String>,
@ -273,7 +266,7 @@ fn get_card_request_data(
item: &types::PaymentsAuthorizeRouterData, item: &types::PaymentsAuthorizeRouterData,
browser_info: &BrowserInformation, browser_info: &BrowserInformation,
params: TrustpayMandatoryParams, params: TrustpayMandatoryParams,
amount: String, amount: StringMajorUnit,
ccard: &domain::payments::Card, ccard: &domain::payments::Card,
return_url: String, return_url: String,
) -> Result<TrustpayPaymentsRequest, Error> { ) -> Result<TrustpayPaymentsRequest, Error> {
@ -353,7 +346,7 @@ fn get_bank_redirection_request_data(
item: &types::PaymentsAuthorizeRouterData, item: &types::PaymentsAuthorizeRouterData,
bank_redirection_data: &domain::BankRedirectData, bank_redirection_data: &domain::BankRedirectData,
params: TrustpayMandatoryParams, params: TrustpayMandatoryParams,
amount: String, amount: StringMajorUnit,
auth: TrustpayAuthType, auth: TrustpayAuthType,
) -> Result<TrustpayPaymentsRequest, error_stack::Report<errors::ConnectorError>> { ) -> Result<TrustpayPaymentsRequest, error_stack::Report<errors::ConnectorError>> {
let pm = TrustpayPaymentMethod::try_from(bank_redirection_data)?; let pm = TrustpayPaymentMethod::try_from(bank_redirection_data)?;
@ -1016,7 +1009,7 @@ impl<F, T> TryFrom<types::ResponseRouterData<F, TrustpayAuthUpdateResponse, T, t
#[derive(Default, Debug, Serialize)] #[derive(Default, Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TrustpayCreateIntentRequest { pub struct TrustpayCreateIntentRequest {
pub amount: String, pub amount: StringMajorUnit,
pub currency: String, pub currency: String,
// If true, Apple Pay will be initialized // If true, Apple Pay will be initialized
pub init_apple_pay: Option<bool>, pub init_apple_pay: Option<bool>,
@ -1084,7 +1077,7 @@ pub struct GooglePayTransactionInfo {
pub country_code: api_models::enums::CountryAlpha2, pub country_code: api_models::enums::CountryAlpha2,
pub currency_code: api_models::enums::Currency, pub currency_code: api_models::enums::Currency,
pub total_price_status: String, pub total_price_status: String,
pub total_price: String, pub total_price: StringMajorUnit,
} }
#[derive(Clone, Default, Debug, Deserialize, Serialize)] #[derive(Clone, Default, Debug, Deserialize, Serialize)]
@ -1155,7 +1148,7 @@ pub struct TrustpayApplePayResponse {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ApplePayTotalInfo { pub struct ApplePayTotalInfo {
pub label: String, pub label: String,
pub amount: String, pub amount: StringMajorUnit,
} }
impl<F> impl<F>
@ -1392,7 +1385,7 @@ impl From<ApplePayTotalInfo> for api_models::payments::AmountInfo {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TrustpayRefundRequestCards { pub struct TrustpayRefundRequestCards {
instance_id: String, instance_id: String,
amount: String, amount: StringMajorUnit,
currency: String, currency: String,
reference: String, reference: String,
} }

View File

@ -1,6 +1,10 @@
use api_models::payments as payment_types; use api_models::payments as payment_types;
use async_trait::async_trait; use async_trait::async_trait;
use common_utils::{ext_traits::ByteSliceExt, request::RequestContent}; use common_utils::{
ext_traits::ByteSliceExt,
request::RequestContent,
types::{AmountConvertor, StringMajorUnitForConnector},
};
use error_stack::{Report, ResultExt}; use error_stack::{Report, ResultExt};
use masking::ExposeInterface; use masking::ExposeInterface;
use router_env::metrics::add_attributes; use router_env::metrics::add_attributes;
@ -403,15 +407,16 @@ fn get_apple_pay_amount_info(
label: &str, label: &str,
session_data: types::PaymentsSessionData, session_data: types::PaymentsSessionData,
) -> RouterResult<payment_types::AmountInfo> { ) -> RouterResult<payment_types::AmountInfo> {
let required_amount_type = StringMajorUnitForConnector;
let apple_pay_amount = required_amount_type
.convert(session_data.minor_amount, session_data.currency)
.change_context(errors::ApiErrorResponse::PreconditionFailed {
message: "Failed to convert amount to string major unit for applePay".to_string(),
})?;
let amount_info = payment_types::AmountInfo { let amount_info = payment_types::AmountInfo {
label: label.to_string(), label: label.to_string(),
total_type: Some("final".to_string()), total_type: Some("final".to_string()),
amount: session_data amount: apple_pay_amount,
.currency
.to_currency_base_unit(session_data.amount)
.change_context(errors::ApiErrorResponse::PreconditionFailed {
message: "Failed to convert currency to base unit".to_string(),
})?,
}; };
Ok(amount_info) Ok(amount_info)
@ -548,22 +553,21 @@ fn create_gpay_session_token(
}, },
) )
.collect(); .collect();
let required_amount_type = StringMajorUnitForConnector;
let google_pay_amount = required_amount_type
.convert(
router_data.request.minor_amount,
router_data.request.currency,
)
.change_context(errors::ApiErrorResponse::PreconditionFailed {
message: "Failed to convert amount to string major unit for googlePay".to_string(),
})?;
let session_data = router_data.request.clone(); let session_data = router_data.request.clone();
let transaction_info = payment_types::GpayTransactionInfo { let transaction_info = payment_types::GpayTransactionInfo {
country_code: session_data.country.unwrap_or_default(), country_code: session_data.country.unwrap_or_default(),
currency_code: router_data.request.currency, currency_code: router_data.request.currency,
total_price_status: "Final".to_string(), total_price_status: "Final".to_string(),
total_price: router_data total_price: google_pay_amount,
.request
.currency
.to_currency_base_unit(router_data.request.amount)
.attach_printable(
"Cannot convert given amount to base currency denomination".to_string(),
)
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "amount",
})?,
}; };
let required_shipping_contact_fields = let required_shipping_contact_fields =

View File

@ -1556,6 +1556,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsSessionD
Ok(Self { Ok(Self {
amount: amount.get_amount_as_i64(), //need to change once we move to connector module amount: amount.get_amount_as_i64(), //need to change once we move to connector module
minor_amount: amount,
currency: payment_data.currency, currency: payment_data.currency,
country: payment_data.address.get_payment_method_billing().and_then( country: payment_data.address.get_payment_method_billing().and_then(
|billing_address| { |billing_address| {

View File

@ -437,7 +437,9 @@ impl ConnectorData {
Ok(ConnectorEnum::Old(Box::new(&connector::Opennode))) Ok(ConnectorEnum::Old(Box::new(&connector::Opennode)))
} }
// "payeezy" => Ok(ConnectorIntegrationEnum::Old(Box::new(&connector::Payeezy)), As psync and rsync are not supported by this connector, it is added as template code for future usage // "payeezy" => Ok(ConnectorIntegrationEnum::Old(Box::new(&connector::Payeezy)), As psync and rsync are not supported by this connector, it is added as template code for future usage
enums::Connector::Payme => Ok(ConnectorEnum::Old(Box::new(&connector::Payme))), enums::Connector::Payme => {
Ok(ConnectorEnum::Old(Box::new(connector::Payme::new())))
}
enums::Connector::Payone => Ok(ConnectorEnum::Old(Box::new(&connector::Payone))), enums::Connector::Payone => Ok(ConnectorEnum::Old(Box::new(&connector::Payone))),
enums::Connector::Payu => Ok(ConnectorEnum::Old(Box::new(&connector::Payu))), enums::Connector::Payu => Ok(ConnectorEnum::Old(Box::new(&connector::Payu))),
enums::Connector::Placetopay => { enums::Connector::Placetopay => {
@ -477,7 +479,7 @@ impl ConnectorData {
} }
enums::Connector::Paypal => Ok(ConnectorEnum::Old(Box::new(&connector::Paypal))), enums::Connector::Paypal => Ok(ConnectorEnum::Old(Box::new(&connector::Paypal))),
enums::Connector::Trustpay => { enums::Connector::Trustpay => {
Ok(ConnectorEnum::Old(Box::new(&connector::Trustpay))) Ok(ConnectorEnum::Old(Box::new(connector::Trustpay::new())))
} }
enums::Connector::Tsys => Ok(ConnectorEnum::Old(Box::new(&connector::Tsys))), enums::Connector::Tsys => Ok(ConnectorEnum::Old(Box::new(&connector::Tsys))),
enums::Connector::Volt => Ok(ConnectorEnum::Old(Box::new(&connector::Volt))), enums::Connector::Volt => Ok(ConnectorEnum::Old(Box::new(&connector::Volt))),

View File

@ -17,7 +17,7 @@ impl utils::Connector for PaymeTest {
fn get_data(&self) -> types::api::ConnectorData { fn get_data(&self) -> types::api::ConnectorData {
use router::connector::Payme; use router::connector::Payme;
utils::construct_connector_data_old( utils::construct_connector_data_old(
Box::new(&Payme), Box::new(Payme::new()),
types::Connector::Payme, types::Connector::Payme,
types::api::GetToken::Connector, types::api::GetToken::Connector,
None, None,

View File

@ -15,7 +15,7 @@ impl utils::Connector for TrustpayTest {
fn get_data(&self) -> api::ConnectorData { fn get_data(&self) -> api::ConnectorData {
use router::connector::Trustpay; use router::connector::Trustpay;
utils::construct_connector_data_old( utils::construct_connector_data_old(
Box::new(&Trustpay), Box::new(Trustpay::new()),
types::Connector::Trustpay, types::Connector::Trustpay,
api::GetToken::Connector, api::GetToken::Connector,
None, None,