Skip to content

Commit

Permalink
refactor(connector): add amount framework to payme & Trustpay with go…
Browse files Browse the repository at this point in the history
…oglePay, ApplePay for bluesnap, Noon & Trustpay (#4833)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
Co-authored-by: Hrithikesh <[email protected]>
Co-authored-by: Narayan Bhat <[email protected]>
  • Loading branch information
4 people committed Jun 26, 2024
1 parent ffe90a4 commit e69a7bd
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 146 deletions.
6 changes: 4 additions & 2 deletions api-reference/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -5052,7 +5052,8 @@
},
"amount": {
"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"
}
}
},
Expand Down Expand Up @@ -10203,7 +10204,8 @@
},
"total_price": {
"type": "string",
"description": "The total price"
"description": "The total price",
"example": "38.02"
}
}
},
Expand Down
10 changes: 6 additions & 4 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use common_utils::{
ext_traits::{ConfigExt, Encode},
id_type,
pii::{self, Email},
types::MinorUnit,
types::{MinorUnit, StringMajorUnit},
};
use masking::{PeekInterface, Secret};
use router_derive::Setter;
Expand Down Expand Up @@ -4175,7 +4175,8 @@ pub struct GpayTransactionInfo {
/// The total price status (ex: 'FINAL')
pub total_price_status: String,
/// 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)]
Expand Down Expand Up @@ -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.
#[serde(rename = "type")]
pub total_type: Option<String>,
/// The total amount for the payment
pub amount: String,
/// The total amount for the payment in majot unit string (Ex: 38.02)
#[schema(value_type = String, example = "38.02")]
pub amount: StringMajorUnit,
}

#[derive(Debug, Clone, serde::Deserialize)]
Expand Down
3 changes: 3 additions & 0 deletions crates/hyperswitch_domain_models/src/router_request_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,9 @@ pub struct PaymentsSessionData {
pub country: Option<common_enums::CountryAlpha2>,
pub surcharge_details: Option<SurchargeDetails>,
pub order_details: Option<Vec<api_models::payments::OrderDetailsWithAmount>>,

// Minor Unit amount for amount frame work
pub minor_amount: MinorUnit,
}

#[derive(Debug, Clone)]
Expand Down
20 changes: 15 additions & 5 deletions crates/router/src/connector/bluesnap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::{
types::{
self,
api::{self, ConnectorCommon, ConnectorCommonExt},
transformers::ForeignTryFrom,
ErrorResponse, Response,
},
utils::BytesExt,
Expand Down Expand Up @@ -603,11 +604,20 @@ impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::Payme
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response));

types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
let req_amount = data.request.minor_amount;
let req_currency = data.request.currency;

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(
Expand Down
16 changes: 11 additions & 5 deletions crates/router/src/connector/bluesnap/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,12 +470,18 @@ impl TryFrom<&types::PaymentsSessionRouterData> for BluesnapCreateWalletToken {
}
}

impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenResponse>>
for types::PaymentsSessionRouterData
impl
ForeignTryFrom<(
types::PaymentsSessionResponseRouterData<BluesnapWalletTokenResponse>,
StringMajorUnit,
)> for types::PaymentsSessionRouterData
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::PaymentsSessionResponseRouterData<BluesnapWalletTokenResponse>,
fn foreign_try_from(
(item, apple_pay_amount): (
types::PaymentsSessionResponseRouterData<BluesnapWalletTokenResponse>,
StringMajorUnit,
),
) -> Result<Self, Self::Error> {
let response = &item.response;

Expand Down Expand Up @@ -532,7 +538,7 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons
total: payments::AmountInfo {
label: payment_request_data.label,
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),
supported_networks: Some(payment_request_data.supported_networks),
Expand Down
113 changes: 74 additions & 39 deletions crates/router/src/connector/payme.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
pub mod transformers;

use std::fmt::Debug;

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 error_stack::{Report, ResultExt};
use masking::ExposeInterface;
Expand All @@ -22,14 +27,30 @@ use crate::{
types::{
self,
api::{self, ConnectorCommon, ConnectorCommonExt},
domain, ErrorResponse, Response,
domain,
transformers::ForeignTryFrom,
ErrorResponse, Response,
},
// transformers::{ForeignFrom, ForeignTryFrom},
utils::{handle_json_response_deserialization_failure, BytesExt},
};

#[derive(Debug, Clone)]
pub struct Payme;
#[derive(Clone)]
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::PaymentSession for Payme {}
impl api::PaymentsCompleteAuthorize for Payme {}
Expand Down Expand Up @@ -287,10 +308,11 @@ impl
req: &types::PaymentsPreProcessingRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount = req.request.get_amount()?;
let currency = req.request.get_currency()?;
let connector_router_data =
payme::PaymeRouterData::try_from((&self.get_currency_unit(), currency, amount, req))?;
let req_amount = req.request.get_minor_amount()?;
let req_currency = req.request.get_currency()?;
let amount =
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)?;
Ok(RequestContent::Json(Box::new(connector_req)))
}
Expand Down Expand Up @@ -329,14 +351,26 @@ impl
.parse_struct("Payme GenerateSaleResponse")
.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));
router_env::logger::info!(connector_response=?response);

types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
types::RouterData::foreign_try_from((
types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
},
apple_pay_amount,
))
}

fn get_error_response(
Expand Down Expand Up @@ -536,12 +570,12 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
req: &types::PaymentsAuthorizeRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = payme::PaymeRouterData::try_from((
&self.get_currency_unit(),
let amount = connector_utils::convert_amount(
self.amount_converter,
req.request.minor_amount,
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)?;
Ok(RequestContent::Json(Box::new(connector_req)))
}
Expand Down Expand Up @@ -724,12 +758,12 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
req: &types::PaymentsCaptureRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = payme::PaymeRouterData::try_from((
&self.get_currency_unit(),
let amount = connector_utils::convert_amount(
self.amount_converter,
req.request.minor_amount_to_capture,
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)?;
Ok(RequestContent::Json(Box::new(connector_req)))
}
Expand Down Expand Up @@ -822,20 +856,21 @@ impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsR
req: &types::PaymentsCancelRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount = req
.request
.amount
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "amount",
})?;
let currency =
let req_amount =
req.request
.minor_amount
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "amount",
})?;
let req_currency =
req.request
.currency
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "currency",
})?;
let connector_router_data =
payme::PaymeRouterData::try_from((&self.get_currency_unit(), currency, amount, req))?;
let amount =
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)?;
Ok(RequestContent::Json(Box::new(connector_req)))
}
Expand Down Expand Up @@ -922,12 +957,12 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
req: &types::RefundsRouterData<api::Execute>,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = payme::PaymeRouterData::try_from((
&self.get_currency_unit(),
let amount = connector_utils::convert_amount(
self.amount_converter,
req.request.minor_refund_amount,
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)?;
Ok(RequestContent::Json(Box::new(connector_req)))
}
Expand Down
Loading

0 comments on commit e69a7bd

Please sign in to comment.