fix(connector): convert cents to dollar before sending to connector (#699)

Co-authored-by: Jagan Elavarasan <jaganelavarasan@gmail.com>
Co-authored-by: Arjun Karthik <m.arjunkarthik@gmail.com>
Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
SamraatBansal
2023-03-01 23:53:10 +05:30
committed by GitHub
parent 7bd2008ae0
commit 3e88319222
6 changed files with 121 additions and 45 deletions

View File

@ -4,6 +4,7 @@ use url::Url;
use uuid::Uuid;
use crate::{
connector::utils,
core::errors,
pii::{self, Secret},
services,
@ -14,7 +15,7 @@ use crate::{
pub struct AirwallexIntentRequest {
// Unique ID to be sent for each transaction/operation request to the connector
request_id: String,
amount: i64,
amount: String,
currency: enums::Currency,
//ID created in merchant's order system that corresponds to this PaymentIntent.
merchant_order_id: String,
@ -26,7 +27,7 @@ impl TryFrom<&types::PaymentsAuthorizeSessionTokenRouterData> for AirwallexInten
) -> Result<Self, Self::Error> {
Ok(Self {
request_id: Uuid::new_v4().to_string(),
amount: item.request.amount,
amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?,
currency: item.request.currency,
merchant_order_id: item.payment_id.clone(),
})
@ -145,7 +146,7 @@ impl<F, T> TryFrom<types::ResponseRouterData<F, AirwallexAuthUpdateResponse, T,
pub struct AirwallexPaymentsCaptureRequest {
// Unique ID to be sent for each transaction/operation request to the connector
request_id: String,
amount: Option<i64>,
amount: Option<String>,
}
impl TryFrom<&types::PaymentsCaptureRouterData> for AirwallexPaymentsCaptureRequest {
@ -153,7 +154,13 @@ impl TryFrom<&types::PaymentsCaptureRouterData> for AirwallexPaymentsCaptureRequ
fn try_from(item: &types::PaymentsCaptureRouterData) -> Result<Self, Self::Error> {
Ok(Self {
request_id: Uuid::new_v4().to_string(),
amount: item.request.amount_to_capture,
amount: match item.request.amount_to_capture {
Some(_a) => Some(utils::to_currency_base_unit(
item.request.amount,
item.request.currency,
)?),
_ => None,
},
})
}
}
@ -224,7 +231,7 @@ pub struct AirwallexPaymentsResponse {
status: AirwallexPaymentStatus,
//Unique identifier for the PaymentIntent
id: String,
amount: Option<i64>,
amount: Option<f32>,
//ID of the PaymentConsent related to this PaymentIntent
payment_consent_id: Option<String>,
next_action: Option<AirwallexPaymentsNextAction>,
@ -277,18 +284,21 @@ impl<F, T>
pub struct AirwallexRefundRequest {
// Unique ID to be sent for each transaction/operation request to the connector
request_id: String,
amount: Option<i64>,
amount: Option<String>,
reason: Option<String>,
//Identifier for the PaymentIntent for which Refund is requested
payment_intent_id: String,
}
impl<F> TryFrom<&types::RefundsRouterData<F>> for AirwallexRefundRequest {
type Error = error_stack::Report<errors::ParsingError>;
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
Ok(Self {
request_id: Uuid::new_v4().to_string(),
amount: Some(item.request.refund_amount),
amount: Some(utils::to_currency_base_unit(
item.request.refund_amount,
item.request.currency,
)?),
reason: item.request.reason.clone(),
payment_intent_id: item.request.connector_transaction_id.clone(),
})
@ -320,7 +330,7 @@ impl From<RefundStatus> for enums::RefundStatus {
pub struct RefundResponse {
//A unique number that tags a credit or debit card transaction when it goes from the merchant's bank through to the cardholder's bank.
acquirer_reference_number: String,
amount: i64,
amount: f32,
//Unique identifier for the Refund
id: String,
status: RefundStatus,

View File

@ -3,7 +3,7 @@ use common_utils::ext_traits::ValueExt;
use error_stack::ResultExt;
use masking::{Deserialize, Serialize};
use crate::{core::errors, types, utils::OptionExt};
use crate::{connector::utils, core::errors, types, utils::OptionExt};
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
@ -134,7 +134,10 @@ impl<F>
let amount_info = AmountInfo {
label: metadata.payment_request_data.label,
label_type: "final".to_string(),
amount: (item.data.request.amount / 100).to_string(),
amount: utils::to_currency_base_unit(
item.data.request.amount,
item.data.request.currency,
)?,
};
let payment_request = PaymentRequest {

View File

@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use crate::{
connector::utils,
core::errors,
pii::{self, Secret},
types::{self, api, storage::enums, transformers::ForeignTryFrom},
@ -9,11 +10,10 @@ use crate::{
#[derive(Debug, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct BluesnapPaymentsRequest {
amount: i64,
amount: String,
#[serde(flatten)]
payment_method: PaymentMethodDetails,
currency: enums::Currency,
soft_descriptor: Option<String>,
card_transaction_type: BluesnapTxnType,
}
@ -51,10 +51,9 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsRequest {
)),
}?;
Ok(Self {
amount: item.request.amount,
amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?,
payment_method,
currency: item.request.currency,
soft_descriptor: item.description.clone(),
card_transaction_type: auth_mode,
})
}
@ -84,7 +83,7 @@ impl TryFrom<&types::PaymentsCancelRouterData> for BluesnapVoidRequest {
pub struct BluesnapCaptureRequest {
card_transaction_type: BluesnapTxnType,
transaction_id: String,
amount: Option<i64>,
amount: Option<String>,
}
impl TryFrom<&types::PaymentsCaptureRouterData> for BluesnapCaptureRequest {
@ -92,10 +91,14 @@ impl TryFrom<&types::PaymentsCaptureRouterData> for BluesnapCaptureRequest {
fn try_from(item: &types::PaymentsCaptureRouterData) -> Result<Self, Self::Error> {
let card_transaction_type = BluesnapTxnType::Capture;
let transaction_id = item.request.connector_transaction_id.to_string();
let amount = utils::to_currency_base_unit_from_optional_amount(
item.request.amount_to_capture,
item.request.currency,
)?;
Ok(Self {
card_transaction_type,
transaction_id,
amount: item.request.amount_to_capture,
amount: Some(amount),
})
}
}
@ -232,7 +235,7 @@ impl<F, T>
#[derive(Default, Debug, Eq, PartialEq, Serialize)]
pub struct BluesnapRefundRequest {
amount: Option<i64>,
amount: Option<String>,
reason: Option<String>,
}
@ -241,7 +244,10 @@ impl<F> TryFrom<&types::RefundsRouterData<F>> for BluesnapRefundRequest {
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
Ok(Self {
reason: item.request.reason.clone(),
amount: Some(item.request.refund_amount),
amount: Some(utils::to_currency_base_unit(
item.request.refund_amount,
item.request.currency,
)?),
})
}
}

View File

@ -4,6 +4,7 @@ use masking::Secret;
use serde::{Deserialize, Serialize};
use crate::{
connector::utils,
consts,
core::errors,
types::{self, api, storage::enums},
@ -91,7 +92,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BraintreePaymentsRequest {
Some(enums::CaptureMethod::Automatic) | None
);
let amount = item.request.amount.to_string();
let amount = utils::to_currency_base_unit(item.request.amount, item.request.currency)?;
let device_data = DeviceData {};
let options = PaymentOptions {
submit_for_settlement,

View File

@ -596,30 +596,42 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, NuveiPaymentsRespons
fn try_from(
item: types::RefundsResponseRouterData<api::Execute, NuveiPaymentsResponse>,
) -> Result<Self, Self::Error> {
let refund_status = item
.response
let response = item.response;
let http_code = item.http_code;
let refund_status = response
.transaction_status
.clone()
.map(|a| a.into())
.unwrap_or_else(|| enums::RefundStatus::Failure);
let refund_response = match item.response.status {
.unwrap_or(enums::RefundStatus::Failure);
let refund_response = match response.status {
NuveiPaymentStatus::Error => Err(types::ErrorResponse {
code: item
.response
code: response
.err_code
.map(|c| c.to_string())
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: item
.response
message: response
.reason
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: None,
status_code: item.http_code,
status_code: http_code,
}),
_ => match response.transaction_status {
Some(NuveiTransactionStatus::Error) => Err(types::ErrorResponse {
code: response
.gw_error_code
.map(|c| c.to_string())
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: response
.gw_error_reason
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: None,
status_code: http_code,
}),
_ => Ok(types::RefundsResponseData {
connector_refund_id: item.response.transaction_id.ok_or(errors::ParsingError)?,
connector_refund_id: response.transaction_id.ok_or(errors::ParsingError)?,
refund_status,
}),
},
};
Ok(Self {
response: refund_response,
@ -635,30 +647,42 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, NuveiPaymentsResponse>
fn try_from(
item: types::RefundsResponseRouterData<api::RSync, NuveiPaymentsResponse>,
) -> Result<Self, Self::Error> {
let refund_status = item
.response
let response = item.response;
let http_code = item.http_code;
let refund_status = response
.transaction_status
.clone()
.map(|a| a.into())
.unwrap_or(enums::RefundStatus::Failure);
let refund_response = match item.response.status {
let refund_response = match response.status {
NuveiPaymentStatus::Error => Err(types::ErrorResponse {
code: item
.response
code: response
.err_code
.map(|c| c.to_string())
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: item
.response
message: response
.reason
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: None,
status_code: item.http_code,
status_code: http_code,
}),
_ => match response.transaction_status {
Some(NuveiTransactionStatus::Error) => Err(types::ErrorResponse {
code: response
.gw_error_code
.map(|c| c.to_string())
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: response
.gw_error_reason
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: None,
status_code: http_code,
}),
_ => Ok(types::RefundsResponseData {
connector_refund_id: item.response.transaction_id.ok_or(errors::ParsingError)?,
connector_refund_id: response.transaction_id.ok_or(errors::ParsingError)?,
refund_status,
}),
},
};
Ok(Self {
response: refund_response,

View File

@ -323,3 +323,35 @@ pub fn get_header_key_value<'a>(
errors::ConnectorError::WebhookSourceVerificationFailed
))?
}
pub fn to_currency_base_unit_from_optional_amount(
amount: Option<i64>,
currency: storage_models::enums::Currency,
) -> Result<String, error_stack::Report<errors::ConnectorError>> {
match amount {
Some(a) => to_currency_base_unit(a, currency),
_ => Err(errors::ConnectorError::MissingRequiredField {
field_name: "amount",
}
.into()),
}
}
pub fn to_currency_base_unit(
amount: i64,
currency: storage_models::enums::Currency,
) -> Result<String, error_stack::Report<errors::ConnectorError>> {
let amount_u32 = u32::try_from(amount)
.into_report()
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
match currency {
storage_models::enums::Currency::JPY | storage_models::enums::Currency::KRW => {
Ok(amount.to_string())
}
storage_models::enums::Currency::BHD
| storage_models::enums::Currency::JOD
| storage_models::enums::Currency::KWD
| storage_models::enums::Currency::OMR => Ok((f64::from(amount_u32) / 1000.0).to_string()),
_ => Ok((f64::from(amount_u32) / 100.0).to_string()),
}
}