Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions api-reference-v2/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -9770,8 +9770,7 @@
"description": "The identifier for payment_attempt"
},
"amount": {
"type": "string",
"description": "The dispute amount"
"$ref": "#/components/schemas/StringMinorUnit"
},
"currency": {
"$ref": "#/components/schemas/Currency"
Expand Down Expand Up @@ -22819,6 +22818,10 @@
"propertyName": "type"
}
},
"StringMinorUnit": {
"type": "string",
"description": "Connector specific types to send"
},
"StripeChargeResponseData": {
"type": "object",
"description": "Fee information to be charged on the payment being collected via Stripe",
Expand Down
7 changes: 5 additions & 2 deletions api-reference/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -11907,8 +11907,7 @@
"description": "The identifier for payment_attempt"
},
"amount": {
"type": "string",
"description": "The dispute amount"
"$ref": "#/components/schemas/StringMinorUnit"
},
"currency": {
"$ref": "#/components/schemas/Currency"
Expand Down Expand Up @@ -27172,6 +27171,10 @@
"propertyName": "type"
}
},
"StringMinorUnit": {
"type": "string",
"description": "Connector specific types to send"
},
"StripeChargeResponseData": {
"type": "object",
"description": "Fee information to be charged on the payment being collected via Stripe",
Expand Down
4 changes: 2 additions & 2 deletions crates/api_models/src/disputes.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;

use common_utils::types::TimeRange;
use common_utils::types::{StringMinorUnit, TimeRange};
use masking::{Deserialize, Serialize};
use serde::de::Error;
use time::PrimitiveDateTime;
Expand All @@ -19,7 +19,7 @@ pub struct DisputeResponse {
/// The identifier for payment_attempt
pub attempt_id: String,
/// The dispute amount
pub amount: String,
pub amount: StringMinorUnit,
/// The three-letter ISO currency code
#[schema(value_type = Currency)]
pub currency: Currency,
Expand Down
54 changes: 53 additions & 1 deletion crates/common_utils/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,20 @@ impl Sum for MinorUnit {
}

/// Connector specific types to send
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq)]
#[derive(
Default,
Debug,
serde::Deserialize,
AsExpression,
serde::Serialize,
Clone,
PartialEq,
Eq,
Hash,
ToSchema,
PartialOrd,
)]
#[diesel(sql_type = sql_types::Text)]
pub struct StringMinorUnit(String);

impl StringMinorUnit {
Expand All @@ -544,6 +557,45 @@ impl StringMinorUnit {
}
}

impl Display for StringMinorUnit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

impl<DB> FromSql<sql_types::Text, DB> for StringMinorUnit
where
DB: Backend,
String: FromSql<sql_types::Text, DB>,
{
fn from_sql(value: DB::RawValue<'_>) -> deserialize::Result<Self> {
let val = String::from_sql(value)?;
Ok(Self(val))
}
}

impl<DB> ToSql<sql_types::Text, DB> for StringMinorUnit
where
DB: Backend,
String: ToSql<sql_types::Text, DB>,
{
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result {
self.0.to_sql(out)
}
}

impl<DB> Queryable<sql_types::Text, DB> for StringMinorUnit
where
DB: Backend,
Self: FromSql<sql_types::Text, DB>,
{
type Row = Self;

fn build(row: Self::Row) -> deserialize::Result<Self> {
Ok(row)
}
}

/// Connector specific types to send
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)]
pub struct FloatMajorUnit(f64);
Expand Down
13 changes: 8 additions & 5 deletions crates/diesel_models/src/dispute.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use common_utils::custom_serde;
use common_utils::{
custom_serde,
types::{MinorUnit, StringMinorUnit},
};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use masking::Secret;
use serde::Serialize;
Expand All @@ -11,7 +14,7 @@ use crate::{enums as storage_enums, schema::dispute};
#[serde(deny_unknown_fields)]
pub struct DisputeNew {
pub dispute_id: String,
pub amount: String,
pub amount: StringMinorUnit,
pub currency: String,
pub dispute_stage: storage_enums::DisputeStage,
pub dispute_status: storage_enums::DisputeStatus,
Expand All @@ -29,7 +32,7 @@ pub struct DisputeNew {
pub evidence: Option<Secret<serde_json::Value>>,
pub profile_id: Option<common_utils::id_type::ProfileId>,
pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
pub dispute_amount: i64,
pub dispute_amount: MinorUnit,
pub organization_id: common_utils::id_type::OrganizationId,
pub dispute_currency: Option<storage_enums::Currency>,
}
Expand All @@ -38,7 +41,7 @@ pub struct DisputeNew {
#[diesel(table_name = dispute, primary_key(dispute_id), check_for_backend(diesel::pg::Pg))]
pub struct Dispute {
pub dispute_id: String,
pub amount: String,
pub amount: StringMinorUnit,
pub currency: String,
pub dispute_stage: storage_enums::DisputeStage,
pub dispute_status: storage_enums::DisputeStatus,
Expand All @@ -60,7 +63,7 @@ pub struct Dispute {
pub evidence: Secret<serde_json::Value>,
pub profile_id: Option<common_utils::id_type::ProfileId>,
pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
pub dispute_amount: i64,
pub dispute_amount: MinorUnit,
pub organization_id: common_utils::id_type::OrganizationId,
pub dispute_currency: Option<storage_enums::Currency>,
}
Expand Down
33 changes: 22 additions & 11 deletions crates/hyperswitch_connectors/src/connectors/adyen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use common_utils::{
errors::CustomResult,
ext_traits::{ByteSliceExt, OptionExt},
request::{Method, Request, RequestBuilder, RequestContent},
types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
types::{
AmountConvertor, MinorUnit, MinorUnitForConnector, StringMinorUnit,
StringMinorUnitForConnector,
},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
Expand Down Expand Up @@ -86,7 +89,7 @@ use crate::{
SubmitEvidenceRouterData,
},
utils::{
self as connector_utils, convert_payment_authorize_router_response,
convert_amount, convert_payment_authorize_router_response,
convert_setup_mandate_router_data_to_authorize_router_data, is_mandate_supported,
ForeignTryFrom, PaymentMethodDataType,
},
Expand All @@ -96,12 +99,14 @@ const ADYEN_API_VERSION: &str = "v68";
#[derive(Clone)]
pub struct Adyen {
amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
amount_converter_webhooks: &'static (dyn AmountConvertor<Output = StringMinorUnit> + Sync),
}

impl Adyen {
pub const fn new() -> &'static Self {
&Self {
amount_converter: &MinorUnitForConnector,
amount_converter_webhooks: &StringMinorUnitForConnector,
}
}
}
Expand Down Expand Up @@ -466,7 +471,7 @@ impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsRespons
convert_setup_mandate_router_data_to_authorize_router_data(req),
));

let amount = connector_utils::convert_amount(
let amount = convert_amount(
self.amount_converter,
authorize_req.request.minor_amount,
authorize_req.request.currency,
Expand Down Expand Up @@ -583,7 +588,7 @@ impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> fo
req: &PaymentsCaptureRouterData,
_connectors: &Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount_to_capture = connector_utils::convert_amount(
let amount_to_capture = convert_amount(
self.amount_converter,
req.request.minor_amount_to_capture,
req.request.currency,
Expand Down Expand Up @@ -849,7 +854,7 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
req: &PaymentsAuthorizeRouterData,
_connectors: &Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount = connector_utils::convert_amount(
let amount = convert_amount(
self.amount_converter,
req.request.minor_amount,
req.request.currency,
Expand Down Expand Up @@ -1012,7 +1017,7 @@ impl ConnectorIntegration<PreProcessing, PaymentsPreProcessingData, PaymentsResp
})?,
};

let amount = connector_utils::convert_amount(self.amount_converter, amount, currency)?;
let amount = convert_amount(self.amount_converter, amount, currency)?;

if response.balance.currency != currency || response.balance.value < amount {
Ok(RouterData {
Expand Down Expand Up @@ -1297,7 +1302,7 @@ impl ConnectorIntegration<PoCreate, PayoutsData, PayoutsResponseData> for Adyen
req: &PayoutsRouterData<PoCreate>,
_connectors: &Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount = connector_utils::convert_amount(
let amount = convert_amount(
self.amount_converter,
req.request.minor_amount,
req.request.destination_currency,
Expand Down Expand Up @@ -1395,7 +1400,7 @@ impl ConnectorIntegration<PoEligibility, PayoutsData, PayoutsResponseData> for A
req: &PayoutsRouterData<PoEligibility>,
_connectors: &Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount = connector_utils::convert_amount(
let amount = convert_amount(
self.amount_converter,
req.request.minor_amount,
req.request.destination_currency,
Expand Down Expand Up @@ -1522,7 +1527,7 @@ impl ConnectorIntegration<PoFulfill, PayoutsData, PayoutsResponseData> for Adyen
req: &PayoutsRouterData<PoFulfill>,
_connectors: &Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount = connector_utils::convert_amount(
let amount = convert_amount(
self.amount_converter,
req.request.minor_amount,
req.request.destination_currency,
Expand Down Expand Up @@ -1627,7 +1632,7 @@ impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Adyen {
req: &RefundsRouterData<Execute>,
_connectors: &Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let refund_amount = connector_utils::convert_amount(
let refund_amount = convert_amount(
self.amount_converter,
req.request.minor_refund_amount,
req.request.currency,
Expand Down Expand Up @@ -1875,8 +1880,14 @@ impl IncomingWebhook for Adyen {
) -> CustomResult<disputes::DisputePayload, errors::ConnectorError> {
let notif = get_webhook_object_from_body(request.body)
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

let amount = convert_amount(
self.amount_converter_webhooks,
notif.amount.value,
notif.amount.currency,
)?;
Ok(disputes::DisputePayload {
amount: notif.amount.value.to_string(),
amount,
currency: notif.amount.currency,
dispute_stage: api_models::enums::DisputeStage::from(notif.event_code.clone()),
connector_dispute_id: notif.psp_reference,
Expand Down
26 changes: 21 additions & 5 deletions crates/hyperswitch_connectors/src/connectors/airwallex.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub mod transformers;

use std::{fmt::Debug, sync::LazyLock};
use std::sync::LazyLock;

use api_models::webhooks::IncomingWebhookEvent;
use common_enums::{enums, CallConnectorAction, PaymentAction};
Expand All @@ -9,6 +9,7 @@ use common_utils::{
errors::CustomResult,
ext_traits::{ByteSliceExt, BytesExt, ValueExt},
request::{Method, Request, RequestBuilder, RequestContent},
types::{AmountConvertor, StringMinorUnit, StringMinorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
Expand Down Expand Up @@ -53,11 +54,21 @@ use transformers as airwallex;
use crate::{
constants::headers,
types::{RefreshTokenRouterData, ResponseRouterData},
utils::{AccessTokenRequestInfo, RefundsRequestData},
utils::{convert_amount, AccessTokenRequestInfo, RefundsRequestData},
};

#[derive(Debug, Clone)]
pub struct Airwallex;
#[derive(Clone)]
pub struct Airwallex {
amount_converter: &'static (dyn AmountConvertor<Output = StringMinorUnit> + Sync),
}

impl Airwallex {
pub const fn new() -> &'static Self {
&Self {
amount_converter: &StringMinorUnitForConnector,
}
}
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Airwallex
where
Expand Down Expand Up @@ -1064,8 +1075,13 @@ impl IncomingWebhook for Airwallex {
.object
.parse_value("AirwallexDisputeObject")
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
let amount = convert_amount(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do it for other flows aswell

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no other flow in Airwallex.rs is converting amount

self.amount_converter,
dispute_details.dispute_amount,
dispute_details.dispute_currency,
)?;
Ok(DisputePayload {
amount: dispute_details.dispute_amount.to_string(),
amount,
currency: dispute_details.dispute_currency,
dispute_stage: api_models::enums::DisputeStage::from(dispute_details.stage.clone()),
connector_dispute_id: dispute_details.dispute_id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use common_enums::enums;
use common_utils::{errors::ParsingError, pii::IpAddress, request::Method};
use common_utils::{errors::ParsingError, pii::IpAddress, request::Method, types::MinorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
payment_method_data::{PaymentMethodData, WalletData},
Expand Down Expand Up @@ -913,7 +913,7 @@ pub struct AirwallexObjectData {
#[derive(Debug, Deserialize)]
pub struct AirwallexDisputeObject {
pub payment_intent_id: String,
pub dispute_amount: i64,
pub dispute_amount: MinorUnit,
pub dispute_currency: enums::Currency,
pub stage: AirwallexDisputeStage,
pub dispute_id: String,
Expand Down
Loading
Loading