diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 3c2c811766..2af9ccb87d 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -10805,7 +10805,10 @@ "nullable": true }, "transaction_details": { - "type": "string", + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentLinkTransactionDetails" + }, "description": "Dynamic details related to merchant to be rendered in payment link", "nullable": true } @@ -10857,7 +10860,10 @@ "nullable": true }, "transaction_details": { - "type": "object", + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentLinkTransactionDetails" + }, "description": "Dynamic details related to merchant to be rendered in payment link", "nullable": true } @@ -10908,6 +10914,35 @@ "expired" ] }, + "PaymentLinkTransactionDetails": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string", + "description": "Key for the transaction details", + "example": "Policy-Number", + "maxLength": 255 + }, + "value": { + "type": "string", + "description": "Value for the transaction details", + "example": "297472368473924", + "maxLength": 255 + }, + "ui_configuration": { + "allOf": [ + { + "$ref": "#/components/schemas/TransactionDetailsUiConfiguration" + } + ], + "nullable": true + } + } + }, "PaymentListConstraints": { "type": "object", "properties": { @@ -18094,6 +18129,32 @@ "TouchNGoRedirection": { "type": "object" }, + "TransactionDetailsUiConfiguration": { + "type": "object", + "properties": { + "position": { + "type": "integer", + "format": "int32", + "description": "Position of the key-value pair in the UI", + "example": 5, + "nullable": true + }, + "is_key_bold": { + "type": "boolean", + "description": "Whether the key should be bold", + "default": false, + "example": true, + "nullable": true + }, + "is_value_bold": { + "type": "boolean", + "description": "Whether the value should be bold", + "default": false, + "example": true, + "nullable": true + } + } + }, "TransactionStatus": { "type": "string", "description": "Indicates the transaction status", diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index f7c19ef94b..8bc958c619 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -14803,7 +14803,10 @@ "nullable": true }, "transaction_details": { - "type": "string", + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentLinkTransactionDetails" + }, "description": "Dynamic details related to merchant to be rendered in payment link", "nullable": true } @@ -14855,7 +14858,10 @@ "nullable": true }, "transaction_details": { - "type": "object", + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentLinkTransactionDetails" + }, "description": "Dynamic details related to merchant to be rendered in payment link", "nullable": true } @@ -14906,6 +14912,35 @@ "expired" ] }, + "PaymentLinkTransactionDetails": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string", + "description": "Key for the transaction details", + "example": "Policy-Number", + "maxLength": 255 + }, + "value": { + "type": "string", + "description": "Value for the transaction details", + "example": "297472368473924", + "maxLength": 255 + }, + "ui_configuration": { + "allOf": [ + { + "$ref": "#/components/schemas/TransactionDetailsUiConfiguration" + } + ], + "nullable": true + } + } + }, "PaymentListConstraints": { "type": "object", "properties": { @@ -22543,6 +22578,32 @@ "TouchNGoRedirection": { "type": "object" }, + "TransactionDetailsUiConfiguration": { + "type": "object", + "properties": { + "position": { + "type": "integer", + "format": "int32", + "description": "Position of the key-value pair in the UI", + "example": 5, + "nullable": true + }, + "is_key_bold": { + "type": "boolean", + "description": "Whether the key should be bold", + "default": false, + "example": true, + "nullable": true + }, + "is_value_bold": { + "type": "boolean", + "description": "Whether the value should be bold", + "default": false, + "example": true, + "nullable": true + } + } + }, "TransactionStatus": { "type": "string", "description": "Indicates the transaction status", diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 2b185d202e..b27a279b96 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -9,7 +9,6 @@ use common_utils::{ }; #[cfg(feature = "v1")] use common_utils::{crypto::OptionalEncryptableName, ext_traits::ValueExt}; -use indexmap::IndexMap; #[cfg(feature = "v2")] use masking::ExposeInterface; use masking::Secret; @@ -2584,8 +2583,32 @@ pub struct PaymentLinkConfigRequest { #[schema(default = false, example = true)] pub enabled_saved_payment_method: Option, /// Dynamic details related to merchant to be rendered in payment link - #[schema(value_type = Option, example = r#"{ "value1": "some-value", "value2": "some-value" }"#)] - pub transaction_details: Option>, + pub transaction_details: Option>, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, ToSchema)] +pub struct PaymentLinkTransactionDetails { + /// Key for the transaction details + #[schema(value_type = String, max_length = 255, example = "Policy-Number")] + pub key: String, + /// Value for the transaction details + #[schema(value_type = String, max_length = 255, example = "297472368473924")] + pub value: String, + /// UI configuration for the transaction details + pub ui_configuration: Option, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, ToSchema)] +pub struct TransactionDetailsUiConfiguration { + /// Position of the key-value pair in the UI + #[schema(value_type = Option, example = 5)] + pub position: Option, + /// Whether the key should be bold + #[schema(default = false, example = true)] + pub is_key_bold: Option, + /// Whether the value should be bold + #[schema(default = false, example = true)] + pub is_value_bold: Option, } #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, ToSchema)] @@ -2605,7 +2628,7 @@ pub struct PaymentLinkConfig { /// A list of allowed domains (glob patterns) where this link can be embedded / opened from pub allowed_domains: Option>, /// Dynamic details related to merchant to be rendered in payment link - pub transaction_details: Option, + pub transaction_details: Option>, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 9514f6185f..d09df597d8 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -5616,7 +5616,7 @@ pub struct PaymentLinkDetails { pub sdk_layout: String, pub display_sdk_only: bool, pub locale: Option, - pub transaction_details: Option, + pub transaction_details: Option>, } #[derive(Debug, serde::Serialize, Clone)] @@ -5642,7 +5642,7 @@ pub struct PaymentLinkStatusDetails { pub theme: String, pub return_url: String, pub locale: Option, - pub transaction_details: Option, + pub transaction_details: Option>, pub unified_code: Option, pub unified_message: Option, } diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index f59fedf5c4..299e70dc4c 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -89,9 +89,6 @@ pub const DEFAULT_ENABLE_SAVED_PAYMENT_METHOD: bool = false; /// Default allowed domains for payment links pub const DEFAULT_ALLOWED_DOMAINS: Option> = None; -/// Default merchant details for payment links -pub const DEFAULT_TRANSACTION_DETAILS: Option = None; - /// Default ttl for Extended card info in redis (in seconds) pub const DEFAULT_TTL_FOR_EXTENDED_CARD_INFO: u16 = 15 * 60; diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 2926f47af9..4724f448e4 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -285,6 +285,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::admin::BusinessPaymentLinkConfig, api_models::admin::PaymentLinkConfigRequest, api_models::admin::PaymentLinkConfig, + api_models::admin::PaymentLinkTransactionDetails, + api_models::admin::TransactionDetailsUiConfiguration, api_models::disputes::DisputeResponse, api_models::disputes::DisputeResponsePaymentsRetrieve, api_models::gsm::GsmCreateRequest, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index 25eab97d3e..0debe22db0 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -203,6 +203,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::admin::BusinessPaymentLinkConfig, api_models::admin::PaymentLinkConfigRequest, api_models::admin::PaymentLinkConfig, + api_models::admin::PaymentLinkTransactionDetails, + api_models::admin::TransactionDetailsUiConfiguration, api_models::disputes::DisputeResponse, api_models::disputes::DisputeResponsePaymentsRetrieve, api_models::gsm::GsmCreateRequest, diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index c56f495647..7919b991d8 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -9,7 +9,6 @@ use common_utils::{ DEFAULT_ALLOWED_DOMAINS, DEFAULT_BACKGROUND_COLOR, DEFAULT_DISPLAY_SDK_ONLY, DEFAULT_ENABLE_SAVED_PAYMENT_METHOD, DEFAULT_LOCALE, DEFAULT_MERCHANT_LOGO, DEFAULT_PRODUCT_IMG, DEFAULT_SDK_LAYOUT, DEFAULT_SESSION_EXPIRY, - DEFAULT_TRANSACTION_DETAILS, }, ext_traits::{AsyncExt, OptionExt, ValueExt}, types::{AmountConvertor, MinorUnit, StringMajorUnitForCore}, @@ -114,7 +113,7 @@ pub async fn form_payment_link_data( display_sdk_only: DEFAULT_DISPLAY_SDK_ONLY, enabled_saved_payment_method: DEFAULT_ENABLE_SAVED_PAYMENT_METHOD, allowed_domains: DEFAULT_ALLOWED_DOMAINS, - transaction_details: DEFAULT_TRANSACTION_DETAILS, + transaction_details: None, } }; @@ -616,24 +615,8 @@ pub fn get_payment_link_config_based_on_priority( display_sdk_only, enabled_saved_payment_method, allowed_domains, - transaction_details: payment_create_link_config.and_then(|payment_link_config| { - payment_link_config - .theme_config - .transaction_details - .and_then(|transaction_details| { - match serde_json::to_string(&transaction_details).change_context( - errors::ApiErrorResponse::InvalidDataValue { - field_name: "transaction_details", - }, - ) { - Ok(details) => Some(details), - Err(err) => { - logger::error!("Failed to serialize transaction details: {:?}", err); - None - } - } - }) - }), + transaction_details: payment_create_link_config + .and_then(|payment_link_config| payment_link_config.theme_config.transaction_details), }; Ok((payment_link_config, domain_name)) @@ -721,7 +704,7 @@ pub async fn get_payment_link_status( display_sdk_only: DEFAULT_DISPLAY_SDK_ONLY, enabled_saved_payment_method: DEFAULT_ENABLE_SAVED_PAYMENT_METHOD, allowed_domains: DEFAULT_ALLOWED_DOMAINS, - transaction_details: DEFAULT_TRANSACTION_DETAILS, + transaction_details: None, } }; diff --git a/crates/router/src/core/payment_link/payment_link_initiate/payment_link.css b/crates/router/src/core/payment_link/payment_link_initiate/payment_link.css index d560060d88..92b150fa13 100644 --- a/crates/router/src/core/payment_link/payment_link_initiate/payment_link.css +++ b/crates/router/src/core/payment_link/payment_link_initiate/payment_link.css @@ -96,6 +96,10 @@ body { #hyper-checkout-payment-merchant-dynamic-details { margin: 20px 20px 10px 20px; + overflow-y: scroll; + max-width: 35vw; + max-height: 10vh; + min-height: 80px; } .hyper-checkout-payment-horizontal-line { @@ -714,6 +718,10 @@ body { flex-flow: column; flex-direction: column-reverse; margin: 0; + max-width: 100%; + overflow-y: scroll; + max-height: 10vh; + min-height: 80px; } .hyper-checkout-payment-horizontal-line { diff --git a/crates/router/src/core/payment_link/payment_link_initiate/payment_link.js b/crates/router/src/core/payment_link/payment_link_initiate/payment_link.js index db6a1d7176..ee2357c669 100644 --- a/crates/router/src/core/payment_link/payment_link_initiate/payment_link.js +++ b/crates/router/src/core/payment_link/payment_link_initiate/payment_link.js @@ -617,26 +617,57 @@ function renderDynamicMerchantDetails(paymentDetails) { } function appendMerchantDetails(paymentDetails, merchantDynamicDetails) { - if (!(typeof paymentDetails.transaction_details === "string" && paymentDetails.transaction_details.length > 0)) { + if ( + !( + typeof paymentDetails.transaction_details === "object" && + Object.keys(paymentDetails.transaction_details).length > 0 + ) + ) { return; } try { - let merchantDetailsObject = JSON.parse(paymentDetails.transaction_details); + let merchantDetailsObject = paymentDetails.transaction_details; + // sort the merchant details based on the position + // if position is null, then it will be shown at the end + merchantDetailsObject.sort((a, b) => { + if (a.ui_configuration === null || a.ui_configuration.position === null) + return 1; + if (b.ui_configuration === null || b.ui_configuration.position === null) + return -1; - if (Object.keys(merchantDetailsObject).length > 0) { + if (typeof a.ui_configuration.position === "number" && typeof b.ui_configuration.position === "number") { + return a.ui_configuration.position - b.ui_configuration.position; + } + else return 0; + }); + + if (merchantDetailsObject.length > 0) { // render a horizontal line above dynamic merchant details - var horizontalLineContainer = document.getElementById("hyper-checkout-payment-horizontal-line-container"); + var horizontalLineContainer = document.getElementById( + "hyper-checkout-payment-horizontal-line-container", + ); var horizontalLine = document.createElement("hr"); horizontalLine.className = "hyper-checkout-payment-horizontal-line"; horizontalLineContainer.append(horizontalLine); // max number of items to show in the merchant details - let maxItemsInDetails = 5; - for (var key in merchantDetailsObject) { + let maxItemsInDetails = 50; + for (var item of merchantDetailsObject) { var merchantData = document.createElement("div"); merchantData.className = "hyper-checkout-payment-merchant-dynamic-data"; - merchantData.innerHTML = key+": "+merchantDetailsObject[key].bold(); + // make the key and value bold if specified in the ui_configuration + var key = item.ui_configuration + ? item.ui_configuration.is_key_bold + ? item.key.bold() + : item.key + : item.key; + var value = item.ui_configuration + ? item.ui_configuration.is_value_bold + ? item.value.bold() + : item.value + : item.value; + merchantData.innerHTML = key + " : " + value; merchantDynamicDetails.append(merchantData); if (--maxItemsInDetails === 0) { @@ -667,7 +698,7 @@ function renderCart(paymentDetails) { var MAX_ITEMS_VISIBLE_AFTER_COLLAPSE = paymentDetails.max_items_visible_after_collapse; var yourCartText = document.createElement("span"); - var yourCartText = document.getElementById('your-cart-text'); + var yourCartText = document.getElementById("your-cart-text"); if (yourCartText) { yourCartText.textContent = translations.yourCart; }