diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 717a908b1f..5efebb14f8 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3355,7 +3355,7 @@ pub struct PaymentLinkInitiateRequest { #[derive(Debug, serde::Serialize)] pub struct PaymentLinkDetails { - pub amount: i64, + pub amount: String, pub currency: api_enums::Currency, pub pub_key: String, pub client_secret: String, @@ -3365,7 +3365,7 @@ pub struct PaymentLinkDetails { pub merchant_logo: String, pub return_url: String, pub merchant_name: String, - pub order_details: Option>, + pub order_details: Option>, pub max_items_visible_after_collapse: i8, pub sdk_theme: Option, } @@ -3423,3 +3423,17 @@ pub struct PaymentLinkListResponse { // The list of payment link response objects pub data: Vec, } + +#[derive(Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize, Clone, ToSchema)] +pub struct OrderDetailsWithStringAmount { + /// Name of the product that is being purchased + #[schema(max_length = 255, example = "shirt")] + pub product_name: String, + /// The quantity of the product to be purchased + #[schema(example = 1)] + pub quantity: u16, + /// the amount per quantity of product + pub amount: String, + /// Product Image link + pub product_img_link: Option, +} diff --git a/crates/router/src/compatibility/stripe/errors.rs b/crates/router/src/compatibility/stripe/errors.rs index cf49b1aad2..5963110c63 100644 --- a/crates/router/src/compatibility/stripe/errors.rs +++ b/crates/router/src/compatibility/stripe/errors.rs @@ -241,6 +241,8 @@ pub enum StripeErrorCode { LockTimeout, #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "Merchant connector account is configured with invalid {config}")] InvalidConnectorConfiguration { config: String }, + #[error(error_type = StripeErrorType::HyperswitchError, code = "HE_01", message = "Failed to convert currency to minor unit")] + CurrencyConversionFailed, // [#216]: https://github.com/juspay/hyperswitch/issues/216 // Implement the remaining stripe error codes @@ -595,6 +597,7 @@ impl From for StripeErrorCode { errors::ApiErrorResponse::InvalidConnectorConfiguration { config } => { Self::InvalidConnectorConfiguration { config } } + errors::ApiErrorResponse::CurrencyConversionFailed => Self::CurrencyConversionFailed, } } } @@ -662,7 +665,8 @@ impl actix_web::ResponseError for StripeErrorCode { | Self::CurrencyNotSupported { .. } | Self::DuplicateCustomer | Self::PaymentMethodUnactivated - | Self::InvalidConnectorConfiguration { .. } => StatusCode::BAD_REQUEST, + | Self::InvalidConnectorConfiguration { .. } + | Self::CurrencyConversionFailed => StatusCode::BAD_REQUEST, Self::RefundFailed | Self::PayoutFailed | Self::PaymentLinkNotFound diff --git a/crates/router/src/core/errors/api_error_response.rs b/crates/router/src/core/errors/api_error_response.rs index ac51c5018d..f94504cf27 100644 --- a/crates/router/src/core/errors/api_error_response.rs +++ b/crates/router/src/core/errors/api_error_response.rs @@ -238,6 +238,8 @@ pub enum ApiErrorResponse { CurrencyNotSupported { message: String }, #[error(error_type = ErrorType::InvalidRequestError, code = "IR_24", message = "Merchant connector account is configured with invalid {config}")] InvalidConnectorConfiguration { config: String }, + #[error(error_type = ErrorType::ValidationError, code = "HE_01", message = "Failed to convert currency to minor unit")] + CurrencyConversionFailed, } impl PTError for ApiErrorResponse { diff --git a/crates/router/src/core/errors/transformers.rs b/crates/router/src/core/errors/transformers.rs index 58eb0213cb..fa9a518579 100644 --- a/crates/router/src/core/errors/transformers.rs +++ b/crates/router/src/core/errors/transformers.rs @@ -270,6 +270,9 @@ impl ErrorSwitch for ApiErrorRespon Self::InvalidConnectorConfiguration {config} => { AER::BadRequest(ApiError::new("IR", 24, format!("Merchant connector account is configured with invalid {config}"), None)) } + Self::CurrencyConversionFailed => { + AER::Unprocessable(ApiError::new("HE", 2, "Failed to convert currency to minor unit", None)) + } } } } diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 07fdf4ae40..81b06f5f9a 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -85,8 +85,6 @@ pub async fn intiate_payment_link_flow( extract_payment_link_config(merchant_account.payment_link_config.clone())? }; - let order_details = validate_order_details(payment_intent.order_details)?; - let return_url = if let Some(payment_create_return_url) = payment_intent.return_url { payment_create_return_url } else { @@ -102,12 +100,16 @@ pub async fn intiate_payment_link_flow( payment_intent.currency, payment_intent.client_secret, )?; + let order_details = validate_order_details(payment_intent.order_details, currency)?; let (default_sdk_theme, default_background_color) = (DEFAULT_SDK_THEME, DEFAULT_BACKGROUND_COLOR); let payment_details = api_models::payments::PaymentLinkDetails { - amount: payment_intent.amount, + amount: currency + .to_currency_base_unit(payment_intent.amount) + .into_report() + .change_context(errors::ApiErrorResponse::CurrencyConversionFailed)?, currency, payment_id: payment_intent.payment_id, merchant_name: payment_link.custom_merchant_name.unwrap_or( @@ -236,8 +238,9 @@ pub fn check_payment_link_status(fulfillment_time: Option) -> fn validate_order_details( order_details: Option>>, + currency: api_models::enums::Currency, ) -> Result< - Option>, + Option>, error_stack::Report, > { let order_details = order_details @@ -256,14 +259,31 @@ fn validate_order_details( }) .transpose()?; - let updated_order_details = order_details.map(|mut order_details| { - for order in order_details.iter_mut() { - if order.product_img_link.is_none() { - order.product_img_link = Some(DEFAULT_PRODUCT_IMG.to_string()); + let updated_order_details = match order_details { + Some(mut order_details) => { + let mut order_details_amount_string_array: Vec< + api_models::payments::OrderDetailsWithStringAmount, + > = Vec::new(); + for order in order_details.iter_mut() { + let mut order_details_amount_string : api_models::payments::OrderDetailsWithStringAmount = Default::default(); + if order.product_img_link.is_none() { + order_details_amount_string.product_img_link = + Some(DEFAULT_PRODUCT_IMG.to_string()) + } else { + order_details_amount_string.product_img_link = order.product_img_link.clone() + }; + order_details_amount_string.amount = currency + .to_currency_base_unit(order.amount) + .into_report() + .change_context(errors::ApiErrorResponse::CurrencyConversionFailed)?; + order_details_amount_string.product_name = order.product_name.clone(); + order_details_amount_string.quantity = order.quantity; + order_details_amount_string_array.push(order_details_amount_string) } + Some(order_details_amount_string_array) } - order_details - }); + None => None, + }; Ok(updated_order_details) }