From 8646fcc47d5ae358cf117ad3d454f4380010a5a7 Mon Sep 17 00:00:00 2001 From: Kashif Date: Tue, 4 Mar 2025 19:08:49 +0530 Subject: [PATCH 1/4] feat(payment_link): expose payment link configs for SDK UI rules and payment button --- crates/api_models/src/admin.rs | 16 ++++++++++ crates/api_models/src/payments.rs | 8 +++++ crates/diesel_models/src/business_profile.rs | 4 +++ crates/diesel_models/src/payment_intent.rs | 9 ++++++ crates/hyperswitch_domain_models/src/lib.rs | 12 ++++++++ crates/router/src/core/payment_link.rs | 30 ++++++++++++++++++- .../payment_link_initiate/payment_link.js | 13 ++++++-- .../payment_link_initiator.js | 10 +++++-- .../secure_payment_link_initiator.js | 10 +++++-- .../router/src/core/payments/transformers.rs | 8 +++++ crates/router/src/routes/admin.rs | 8 ++--- crates/router/src/routes/ephemeral_key.rs | 8 ++--- crates/router/src/types/transformers.rs | 8 +++++ 13 files changed, 127 insertions(+), 17 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 5d31ea70994..fcf5dacf615 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -2810,6 +2810,14 @@ pub struct PaymentLinkConfigRequest { pub payment_button_text_colour: Option, /// Custom background colour for the payment link pub background_colour: Option, + /// SDK configuration rules + pub sdk_ui_rules: Option>>, + /// Payment button font weight + pub payment_button_text_weight: Option, + /// Payment button font size + pub payment_button_text_size: Option, + /// Payment button padding + pub payment_button_padding: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, ToSchema)] @@ -2891,6 +2899,14 @@ pub struct PaymentLinkConfig { pub payment_button_text_colour: Option, /// Custom background colour for the payment link pub background_colour: Option, + /// SDK configuration rules + pub sdk_ui_rules: Option>>, + /// Payment button font weight + pub payment_button_text_weight: Option, + /// Payment button font size + pub payment_button_text_size: Option, + /// Payment button padding + pub payment_button_padding: 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 12da310ac61..865400d6259 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -7831,6 +7831,10 @@ pub struct PaymentLinkDetails { pub payment_button_colour: Option, pub payment_button_text_colour: Option, pub background_colour: Option, + pub sdk_ui_rules: Option>>, + pub payment_button_text_weight: Option, + pub payment_button_text_size: Option, + pub payment_button_padding: Option, } #[derive(Debug, serde::Serialize, Clone)] @@ -7846,6 +7850,10 @@ pub struct SecurePaymentLinkDetails { pub payment_button_colour: Option, pub payment_button_text_colour: Option, pub background_colour: Option, + pub sdk_ui_rules: Option>>, + pub payment_button_text_weight: Option, + pub payment_button_text_size: Option, + pub payment_button_padding: Option, } #[derive(Debug, serde::Serialize)] diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 744193bf51e..2692ce36242 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -644,6 +644,10 @@ pub struct PaymentLinkConfigRequest { pub skip_status_screen: Option, pub payment_button_text_colour: Option, pub background_colour: Option, + pub sdk_ui_rules: Option>>, + pub payment_button_text_weight: Option, + pub payment_button_text_size: Option, + pub payment_button_padding: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq)] diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index a2b3c406a2a..86ddf59da74 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -187,6 +187,15 @@ pub struct PaymentLinkConfigRequestForPayments { pub payment_button_text_colour: Option, /// Custom background colour for the payment link pub background_colour: Option, + /// SDK configuration rules + pub sdk_ui_rules: + Option>>, + /// Payment button font weight + pub payment_button_text_weight: Option, + /// Payment button font size + pub payment_button_text_size: Option, + /// Payment button padding + pub payment_button_padding: Option, } common_utils::impl_to_sql_from_sql_json!(PaymentLinkConfigRequestForPayments); diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index c97753a6dc0..3a8bfb20bb3 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -412,6 +412,10 @@ impl ApiModelToDieselModelConvertor skip_status_screen: item.skip_status_screen, background_colour: item.background_colour, payment_button_text_colour: item.payment_button_text_colour, + sdk_ui_rules: item.sdk_ui_rules, + payment_button_text_weight: item.payment_button_text_weight, + payment_button_text_size: item.payment_button_text_size, + payment_button_padding: item.payment_button_padding, } } fn convert_back(self) -> api_models::admin::PaymentLinkConfigRequest { @@ -433,6 +437,10 @@ impl ApiModelToDieselModelConvertor skip_status_screen, background_colour, payment_button_text_colour, + sdk_ui_rules, + payment_button_text_weight, + payment_button_text_size, + payment_button_padding, } = self; api_models::admin::PaymentLinkConfigRequest { theme, @@ -458,6 +466,10 @@ impl ApiModelToDieselModelConvertor skip_status_screen, background_colour, payment_button_text_colour, + sdk_ui_rules, + payment_button_text_weight, + payment_button_text_size, + payment_button_padding, } } } diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index e62d9946416..a2d6b5b0637 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -135,6 +135,10 @@ pub async fn form_payment_link_data( skip_status_screen: None, background_colour: None, payment_button_text_colour: None, + sdk_ui_rules: None, + payment_button_text_weight: None, + payment_button_text_size: None, + payment_button_padding: None, } }; @@ -286,6 +290,10 @@ pub async fn form_payment_link_data( skip_status_screen: payment_link_config.skip_status_screen, background_colour: payment_link_config.background_colour.clone(), payment_button_text_colour: payment_link_config.payment_button_text_colour.clone(), + sdk_ui_rules: payment_link_config.sdk_ui_rules.clone(), + payment_button_text_weight: payment_link_config.payment_button_text_weight.clone(), + payment_button_text_size: payment_link_config.payment_button_text_size.clone(), + payment_button_padding: payment_link_config.payment_button_padding.clone(), }; Ok(( @@ -342,6 +350,10 @@ pub async fn initiate_secure_payment_link_flow( skip_status_screen: payment_link_config.skip_status_screen, background_colour: payment_link_config.background_colour, payment_button_text_colour: payment_link_config.payment_button_text_colour, + sdk_ui_rules: payment_link_config.sdk_ui_rules, + payment_button_text_weight: payment_link_config.payment_button_text_weight, + payment_button_text_size: payment_link_config.payment_button_text_size, + payment_button_padding: payment_link_config.payment_button_padding, }; let js_script = format!( "window.__PAYMENT_DETAILS = {}", @@ -653,6 +665,10 @@ pub fn get_payment_link_config_based_on_priority( skip_status_screen, background_colour, payment_button_text_colour, + sdk_ui_rules, + payment_button_text_weight, + payment_button_text_size, + payment_button_padding, ) = get_payment_link_config_value!( payment_create_link_config, business_theme_configs, @@ -664,7 +680,11 @@ pub fn get_payment_link_config_based_on_priority( (payment_button_colour), (skip_status_screen), (background_colour), - (payment_button_text_colour) + (payment_button_text_colour), + (sdk_ui_rules), + (payment_button_text_weight), + (payment_button_text_size), + (payment_button_padding), ); let payment_link_config = @@ -690,6 +710,10 @@ pub fn get_payment_link_config_based_on_priority( payment_button_colour, background_colour, payment_button_text_colour, + sdk_ui_rules, + payment_button_text_weight, + payment_button_text_size, + payment_button_padding, }; Ok((payment_link_config, domain_name)) @@ -798,6 +822,10 @@ pub async fn get_payment_link_status( skip_status_screen: None, background_colour: None, payment_button_text_colour: None, + sdk_ui_rules: None, + payment_button_text_weight: None, + payment_button_text_size: None, + payment_button_padding: None, } }; 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 811ae799d0f..b5b02349364 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 @@ -310,6 +310,15 @@ function initializeEventListeners(paymentDetails) { var chosenColor = paymentDetails.payment_button_colour || primaryColor; submitButtonNode.style.color = paymentDetails.payment_button_text_colour || invert(chosenColor, true); submitButtonNode.style.backgroundColor = chosenColor; + if (paymentDetails.payment_button_text_weight) { + submitButtonNode.style.fontWeight = paymentDetails.payment_button_text_weight; + } + if (paymentDetails.payment_button_text_size) { + submitButtonNode.style.fontSize = paymentDetails.payment_button_text_size; + } + if (paymentDetails.payment_button_padding) { + submitButtonNode.style.padding = paymentDetails.payment_button_padding; + } } if (hyperCheckoutCartImageNode instanceof HTMLDivElement) { @@ -461,7 +470,7 @@ function handleSubmit(e) { window.top.location.href = url.toString(); } else { redirectToStatus(); - } + } }) .catch(function (error) { console.error("Error confirming payment_intent", error); @@ -801,7 +810,7 @@ function renderBranding(paymentDetails) { * - Renders background image in the payment details section * @param {PaymentDetails} paymentDetails */ -function renderBackgroundImage(paymentDetails) { +function renderBackgroundImage(paymentDetails) { var backgroundImage = paymentDetails.background_image; if (typeof backgroundImage === "object" && backgroundImage !== null) { var paymentDetailsNode = document.getElementById("hyper-checkout-details"); diff --git a/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js b/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js index 6abe5f7d5b2..f37c5a1c034 100644 --- a/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js +++ b/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js @@ -10,7 +10,8 @@ function initializeSDK() { // @ts-ignore var paymentDetails = window.__PAYMENT_DETAILS; - var client_secret = paymentDetails.client_secret; + var clientSecret = paymentDetails.client_secret; + var sdkUiRules = paymentDetails.sdk_ui_rules; var appearance = { variables: { colorPrimary: paymentDetails.theme || "rgb(0, 109, 249)", @@ -24,6 +25,9 @@ function initializeSDK() { colorBackground: "rgb(255, 255, 255)", }, }; + if (typeof sdkUiRules === "object") { + appearance.rules = sdkUiRules; + } // @ts-ignore hyper = window.Hyper(pub_key, { isPreloadEnabled: false, @@ -37,12 +41,12 @@ function initializeSDK() { // @ts-ignore widgets = hyper.widgets({ appearance: appearance, - clientSecret: client_secret, + clientSecret: clientSecret, locale: paymentDetails.locale, }); var type = paymentDetails.sdk_layout === "spaced_accordion" || - paymentDetails.sdk_layout === "accordion" + paymentDetails.sdk_layout === "accordion" ? "accordion" : paymentDetails.sdk_layout; var hideCardNicknameField = paymentDetails.hide_card_nickname_field; diff --git a/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js b/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js index a8a00e4de9d..824dbab8d6f 100644 --- a/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js +++ b/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js @@ -31,7 +31,8 @@ if (!isFramed) { function initializeSDK() { // @ts-ignore var paymentDetails = window.__PAYMENT_DETAILS; - var client_secret = paymentDetails.client_secret; + var clientSecret = paymentDetails.client_secret; + var sdkUiRules = paymentDetails.sdk_ui_rules; var appearance = { variables: { colorPrimary: paymentDetails.theme || "rgb(0, 109, 249)", @@ -45,6 +46,9 @@ if (!isFramed) { colorBackground: "rgb(255, 255, 255)", }, }; + if (typeof sdkUiRules === "object") { + appearance.rules = sdkUiRules; + } // @ts-ignore hyper = window.Hyper(pub_key, { isPreloadEnabled: false, @@ -58,12 +62,12 @@ if (!isFramed) { // @ts-ignore widgets = hyper.widgets({ appearance: appearance, - clientSecret: client_secret, + clientSecret: clientSecret, locale: paymentDetails.locale, }); var type = paymentDetails.sdk_layout === "spaced_accordion" || - paymentDetails.sdk_layout === "accordion" + paymentDetails.sdk_layout === "accordion" ? "accordion" : paymentDetails.sdk_layout; diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 12483aac8e2..a49d11b4551 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -4431,6 +4431,10 @@ impl ForeignFrom skip_status_screen: config.skip_status_screen, background_colour: config.background_colour, payment_button_text_colour: config.payment_button_text_colour, + sdk_ui_rules: config.sdk_ui_rules, + payment_button_text_weight: config.payment_button_text_weight, + payment_button_text_size: config.payment_button_text_size, + payment_button_padding: config.payment_button_padding, } } } @@ -4499,6 +4503,10 @@ impl ForeignFrom skip_status_screen: config.skip_status_screen, background_colour: config.background_colour, payment_button_text_colour: config.payment_button_text_colour, + sdk_ui_rules: config.sdk_ui_rules, + payment_button_text_weight: config.payment_button_text_weight, + payment_button_text_size: config.payment_button_text_size, + payment_button_padding: config.payment_button_padding, } } } diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index 9557aabbe81..a112be46188 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -456,7 +456,7 @@ pub async fn connector_retrieve( let id = path.into_inner(); let payload = web::Json(admin::MerchantConnectorId { id: id.clone() }).into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -477,7 +477,7 @@ pub async fn connector_retrieve( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -491,7 +491,7 @@ pub async fn connector_list( let flow = Flow::MerchantConnectorsList; let profile_id = path.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -507,7 +507,7 @@ pub async fn connector_list( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/ephemeral_key.rs b/crates/router/src/routes/ephemeral_key.rs index 92b5d667daa..64b46eb2f55 100644 --- a/crates/router/src/routes/ephemeral_key.rs +++ b/crates/router/src/routes/ephemeral_key.rs @@ -64,7 +64,7 @@ pub async fn client_secret_create( ) -> HttpResponse { let flow = Flow::EphemeralKeyCreate; let payload = json_payload.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -80,7 +80,7 @@ pub async fn client_secret_create( }, &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -93,7 +93,7 @@ pub async fn client_secret_delete( ) -> HttpResponse { let flow = Flow::EphemeralKeyDelete; let payload = path.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -101,6 +101,6 @@ pub async fn client_secret_delete( |state, _: auth::AuthenticationData, req, _| helpers::delete_client_secret(state, req), &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 514ddbbbb50..e066cff0f21 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -2164,6 +2164,10 @@ impl ForeignFrom payment_button_colour: item.payment_button_colour, background_colour: item.background_colour, payment_button_text_colour: item.payment_button_text_colour, + sdk_ui_rules: item.sdk_ui_rules, + payment_button_text_weight: item.payment_button_text_weight, + payment_button_text_size: item.payment_button_text_size, + payment_button_padding: item.payment_button_padding, } } } @@ -2192,6 +2196,10 @@ impl ForeignFrom payment_button_colour: item.payment_button_colour, background_colour: item.background_colour, payment_button_text_colour: item.payment_button_text_colour, + sdk_ui_rules: item.sdk_ui_rules, + payment_button_text_weight: item.payment_button_text_weight, + payment_button_text_size: item.payment_button_text_size, + payment_button_padding: item.payment_button_padding, } } } From e82cfad1cf15d8c701987c2cb934181af77d0d42 Mon Sep 17 00:00:00 2001 From: Kashif Date: Tue, 4 Mar 2025 19:32:12 +0530 Subject: [PATCH 2/4] chore: fix clippy_v2 --- crates/router/src/routes/verification.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/router/src/routes/verification.rs b/crates/router/src/routes/verification.rs index d89f3c65cbe..31e7638b3ef 100644 --- a/crates/router/src/routes/verification.rs +++ b/crates/router/src/routes/verification.rs @@ -53,7 +53,7 @@ pub async fn retrieve_apple_pay_verified_domains( let merchant_id = ¶ms.merchant_id; let mca_id = ¶ms.merchant_connector_account_id; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -73,6 +73,6 @@ pub async fn retrieve_apple_pay_verified_domains( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } From ee0d3dc9a2cf0e00f2791e734909c20782869867 Mon Sep 17 00:00:00 2001 From: Kashif Date: Wed, 5 Mar 2025 03:52:47 +0530 Subject: [PATCH 3/4] refactor: add payment_link_ui_rules as a config for granular control over payment link design --- api-reference-v2/openapi_spec.json | 126 ++++++++++-------- api-reference/openapi_spec.json | 44 ++++++ crates/api_models/src/admin.rs | 16 +-- crates/api_models/src/payments.rs | 8 +- crates/diesel_models/src/business_profile.rs | 4 +- crates/diesel_models/src/payment_intent.rs | 9 +- crates/hyperswitch_domain_models/src/lib.rs | 12 +- crates/router/src/core/payment_link.rs | 28 +--- .../payment_link_initiate/payment_link.js | 35 +++-- .../payment_link_initiator.js | 2 +- .../secure_payment_link_initiator.js | 2 +- .../router/src/core/payments/transformers.rs | 8 +- crates/router/src/types/transformers.rs | 8 +- 13 files changed, 164 insertions(+), 138 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index cd8b8458364..4ca9f57efb3 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -12587,6 +12587,14 @@ "type": "string", "description": "The url for Qr code given by the connector" }, + "display_text": { + "type": "string", + "nullable": true + }, + "border_color": { + "type": "string", + "nullable": true + }, "type": { "type": "string", "enum": [ @@ -13983,6 +13991,28 @@ "type": "string", "description": "Custom background colour for the payment link", "nullable": true + }, + "sdk_ui_rules": { + "type": "object", + "description": "SDK configuration rules", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "nullable": true + }, + "payment_link_ui_rules": { + "type": "object", + "description": "Payment link configuration rules", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "nullable": true } } }, @@ -14098,6 +14128,28 @@ "type": "string", "description": "Custom background colour for the payment link", "nullable": true + }, + "sdk_ui_rules": { + "type": "object", + "description": "SDK configuration rules", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "nullable": true + }, + "payment_link_ui_rules": { + "type": "object", + "description": "Payment link configuration rules", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "nullable": true } } }, @@ -15301,13 +15353,29 @@ "description": "The return url to which the user should be redirected to", "nullable": true }, - "associated_payment": { + "next_action": { "allOf": [ { - "$ref": "#/components/schemas/PaymentsResponse" + "$ref": "#/components/schemas/NextActionData" } ], "nullable": true + }, + "authentication_details": { + "allOf": [ + { + "$ref": "#/components/schemas/AuthenticationDetails" + } + ], + "nullable": true + }, + "associated_payment_methods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/id_type.GlobalPaymentMethodId" + }, + "description": "The payment method that was created using this payment method session", + "nullable": true } } }, @@ -15542,60 +15610,6 @@ }, "additionalProperties": false }, - "PaymentMethodsSessionResponse": { - "type": "object", - "required": [ - "id", - "customer_id", - "expires_at", - "client_secret" - ], - "properties": { - "id": { - "type": "string", - "example": "12345_pms_01926c58bc6e77c09e809964e72af8c8" - }, - "customer_id": { - "type": "string", - "description": "The customer id for which the payment methods session is to be created", - "example": "12345_cus_01926c58bc6e77c09e809964e72af8c8" - }, - "billing": { - "allOf": [ - { - "$ref": "#/components/schemas/Address" - } - ], - "nullable": true - }, - "psp_tokenization": { - "allOf": [ - { - "$ref": "#/components/schemas/PspTokenization" - } - ], - "nullable": true - }, - "network_tokenization": { - "allOf": [ - { - "$ref": "#/components/schemas/NetworkTokenization" - } - ], - "nullable": true - }, - "expires_at": { - "type": "string", - "format": "date-time", - "description": "The iso timestamp when the session will expire\nTrying to retrieve the session or any operations on the session after this time will result in an error", - "example": "2023-01-18T11:04:09.922Z" - }, - "client_secret": { - "type": "string", - "description": "Client Secret" - } - } - }, "PaymentMethodsSessionUpdateRequest": { "type": "object", "properties": { diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 59791cc7504..d21bc362669 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -16543,6 +16543,28 @@ "type": "string", "description": "Custom background colour for the payment link", "nullable": true + }, + "sdk_ui_rules": { + "type": "object", + "description": "SDK configuration rules", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "nullable": true + }, + "payment_link_ui_rules": { + "type": "object", + "description": "Payment link configuration rules", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "nullable": true } } }, @@ -16658,6 +16680,28 @@ "type": "string", "description": "Custom background colour for the payment link", "nullable": true + }, + "sdk_ui_rules": { + "type": "object", + "description": "SDK configuration rules", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "nullable": true + }, + "payment_link_ui_rules": { + "type": "object", + "description": "Payment link configuration rules", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "nullable": true } } }, diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index fcf5dacf615..d5a4d89910b 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -2812,12 +2812,8 @@ pub struct PaymentLinkConfigRequest { pub background_colour: Option, /// SDK configuration rules pub sdk_ui_rules: Option>>, - /// Payment button font weight - pub payment_button_text_weight: Option, - /// Payment button font size - pub payment_button_text_size: Option, - /// Payment button padding - pub payment_button_padding: Option, + /// Payment link configuration rules + pub payment_link_ui_rules: Option>>, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, ToSchema)] @@ -2901,12 +2897,8 @@ pub struct PaymentLinkConfig { pub background_colour: Option, /// SDK configuration rules pub sdk_ui_rules: Option>>, - /// Payment button font weight - pub payment_button_text_weight: Option, - /// Payment button font size - pub payment_button_text_size: Option, - /// Payment button padding - pub payment_button_padding: Option, + /// Payment link configuration rules + pub payment_link_ui_rules: 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 865400d6259..813408da0ea 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -7832,9 +7832,7 @@ pub struct PaymentLinkDetails { pub payment_button_text_colour: Option, pub background_colour: Option, pub sdk_ui_rules: Option>>, - pub payment_button_text_weight: Option, - pub payment_button_text_size: Option, - pub payment_button_padding: Option, + pub payment_link_ui_rules: Option>>, } #[derive(Debug, serde::Serialize, Clone)] @@ -7851,9 +7849,7 @@ pub struct SecurePaymentLinkDetails { pub payment_button_text_colour: Option, pub background_colour: Option, pub sdk_ui_rules: Option>>, - pub payment_button_text_weight: Option, - pub payment_button_text_size: Option, - pub payment_button_padding: Option, + pub payment_link_ui_rules: Option>>, } #[derive(Debug, serde::Serialize)] diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 2692ce36242..44f32c6eb4b 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -645,9 +645,7 @@ pub struct PaymentLinkConfigRequest { pub payment_button_text_colour: Option, pub background_colour: Option, pub sdk_ui_rules: Option>>, - pub payment_button_text_weight: Option, - pub payment_button_text_size: Option, - pub payment_button_padding: Option, + pub payment_link_ui_rules: Option>>, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq)] diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 86ddf59da74..8845a92ba6a 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -190,12 +190,9 @@ pub struct PaymentLinkConfigRequestForPayments { /// SDK configuration rules pub sdk_ui_rules: Option>>, - /// Payment button font weight - pub payment_button_text_weight: Option, - /// Payment button font size - pub payment_button_text_size: Option, - /// Payment button padding - pub payment_button_padding: Option, + /// Payment link configuration rules + pub payment_link_ui_rules: + Option>>, } common_utils::impl_to_sql_from_sql_json!(PaymentLinkConfigRequestForPayments); diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index 3a8bfb20bb3..2d2a4413afc 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -413,9 +413,7 @@ impl ApiModelToDieselModelConvertor background_colour: item.background_colour, payment_button_text_colour: item.payment_button_text_colour, sdk_ui_rules: item.sdk_ui_rules, - payment_button_text_weight: item.payment_button_text_weight, - payment_button_text_size: item.payment_button_text_size, - payment_button_padding: item.payment_button_padding, + payment_link_ui_rules: item.payment_link_ui_rules, } } fn convert_back(self) -> api_models::admin::PaymentLinkConfigRequest { @@ -438,9 +436,7 @@ impl ApiModelToDieselModelConvertor background_colour, payment_button_text_colour, sdk_ui_rules, - payment_button_text_weight, - payment_button_text_size, - payment_button_padding, + payment_link_ui_rules, } = self; api_models::admin::PaymentLinkConfigRequest { theme, @@ -467,9 +463,7 @@ impl ApiModelToDieselModelConvertor background_colour, payment_button_text_colour, sdk_ui_rules, - payment_button_text_weight, - payment_button_text_size, - payment_button_padding, + payment_link_ui_rules, } } } diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index a2d6b5b0637..292f582df9a 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -136,9 +136,7 @@ pub async fn form_payment_link_data( background_colour: None, payment_button_text_colour: None, sdk_ui_rules: None, - payment_button_text_weight: None, - payment_button_text_size: None, - payment_button_padding: None, + payment_link_ui_rules: None, } }; @@ -291,9 +289,7 @@ pub async fn form_payment_link_data( background_colour: payment_link_config.background_colour.clone(), payment_button_text_colour: payment_link_config.payment_button_text_colour.clone(), sdk_ui_rules: payment_link_config.sdk_ui_rules.clone(), - payment_button_text_weight: payment_link_config.payment_button_text_weight.clone(), - payment_button_text_size: payment_link_config.payment_button_text_size.clone(), - payment_button_padding: payment_link_config.payment_button_padding.clone(), + payment_link_ui_rules: payment_link_config.payment_link_ui_rules.clone(), }; Ok(( @@ -351,9 +347,7 @@ pub async fn initiate_secure_payment_link_flow( background_colour: payment_link_config.background_colour, payment_button_text_colour: payment_link_config.payment_button_text_colour, sdk_ui_rules: payment_link_config.sdk_ui_rules, - payment_button_text_weight: payment_link_config.payment_button_text_weight, - payment_button_text_size: payment_link_config.payment_button_text_size, - payment_button_padding: payment_link_config.payment_button_padding, + payment_link_ui_rules: payment_link_config.payment_link_ui_rules, }; let js_script = format!( "window.__PAYMENT_DETAILS = {}", @@ -666,9 +660,7 @@ pub fn get_payment_link_config_based_on_priority( background_colour, payment_button_text_colour, sdk_ui_rules, - payment_button_text_weight, - payment_button_text_size, - payment_button_padding, + payment_link_ui_rules, ) = get_payment_link_config_value!( payment_create_link_config, business_theme_configs, @@ -682,9 +674,7 @@ pub fn get_payment_link_config_based_on_priority( (background_colour), (payment_button_text_colour), (sdk_ui_rules), - (payment_button_text_weight), - (payment_button_text_size), - (payment_button_padding), + (payment_link_ui_rules), ); let payment_link_config = @@ -711,9 +701,7 @@ pub fn get_payment_link_config_based_on_priority( background_colour, payment_button_text_colour, sdk_ui_rules, - payment_button_text_weight, - payment_button_text_size, - payment_button_padding, + payment_link_ui_rules, }; Ok((payment_link_config, domain_name)) @@ -823,9 +811,7 @@ pub async fn get_payment_link_status( background_colour: None, payment_button_text_colour: None, sdk_ui_rules: None, - payment_button_text_weight: None, - payment_button_text_size: None, - payment_button_padding: None, + payment_link_ui_rules: None, } }; 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 b5b02349364..b8701fc8368 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 @@ -231,8 +231,8 @@ function boot() { link.type = "image/x-icon"; document.head.appendChild(link); } - // Render UI + // Render UI if (paymentDetails.display_sdk_only) { renderSDKHeader(paymentDetails); renderBranding(paymentDetails); @@ -247,7 +247,6 @@ function boot() { renderSDKHeader(paymentDetails); } - // Deal w loaders show("#sdk-spinner"); hide("#page-spinner"); @@ -256,6 +255,12 @@ function boot() { // Add event listeners initializeEventListeners(paymentDetails); + // Update payment link styles + var paymentLinkUiRules = paymentDetails.payment_link_ui_rules; + if (paymentLinkUiRules !== null && typeof paymentLinkUiRules === "object" && Object.getPrototypeOf(paymentLinkUiRules) === Object.prototype) { + updatePaymentLinkUi(paymentLinkUiRules); + } + // Initialize SDK // @ts-ignore if (window.Hyper) { @@ -310,15 +315,6 @@ function initializeEventListeners(paymentDetails) { var chosenColor = paymentDetails.payment_button_colour || primaryColor; submitButtonNode.style.color = paymentDetails.payment_button_text_colour || invert(chosenColor, true); submitButtonNode.style.backgroundColor = chosenColor; - if (paymentDetails.payment_button_text_weight) { - submitButtonNode.style.fontWeight = paymentDetails.payment_button_text_weight; - } - if (paymentDetails.payment_button_text_size) { - submitButtonNode.style.fontSize = paymentDetails.payment_button_text_size; - } - if (paymentDetails.payment_button_padding) { - submitButtonNode.style.padding = paymentDetails.payment_button_padding; - } } if (hyperCheckoutCartImageNode instanceof HTMLDivElement) { @@ -1113,3 +1109,20 @@ function renderSDKHeader(paymentDetails) { sdkHeaderNode.append(sdkHeaderItemNode); } } + +/** + * Trigger - post UI render + * Use - add CSS rules for the payment link + * @param {Object} paymentLinkUiRules + */ +function updatePaymentLinkUi(paymentLinkUiRules) { + Object.keys(paymentLinkUiRules).forEach(function (selector) { + var node = document.querySelector(selector); + if (node instanceof HTMLElement) { + var styles = paymentLinkUiRules[selector]; + Object.keys(styles).forEach(function (property) { + node.style[property] = styles[property]; + }); + } + }) +} \ No newline at end of file diff --git a/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js b/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js index f37c5a1c034..ca6a944baaa 100644 --- a/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js +++ b/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js @@ -25,7 +25,7 @@ function initializeSDK() { colorBackground: "rgb(255, 255, 255)", }, }; - if (typeof sdkUiRules === "object") { + if (sdkUiRules !== null && typeof sdkUiRules === "object" && Object.getPrototypeOf(sdkUiRules) === Object.prototype) { appearance.rules = sdkUiRules; } // @ts-ignore diff --git a/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js b/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js index 824dbab8d6f..9b5a144d29a 100644 --- a/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js +++ b/crates/router/src/core/payment_link/payment_link_initiate/secure_payment_link_initiator.js @@ -46,7 +46,7 @@ if (!isFramed) { colorBackground: "rgb(255, 255, 255)", }, }; - if (typeof sdkUiRules === "object") { + if (sdkUiRules !== null && typeof sdkUiRules === "object" && Object.getPrototypeOf(sdkUiRules) === Object.prototype) { appearance.rules = sdkUiRules; } // @ts-ignore diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index a49d11b4551..eb0f246fdcb 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -4432,9 +4432,7 @@ impl ForeignFrom background_colour: config.background_colour, payment_button_text_colour: config.payment_button_text_colour, sdk_ui_rules: config.sdk_ui_rules, - payment_button_text_weight: config.payment_button_text_weight, - payment_button_text_size: config.payment_button_text_size, - payment_button_padding: config.payment_button_padding, + payment_link_ui_rules: config.payment_link_ui_rules, } } } @@ -4504,9 +4502,7 @@ impl ForeignFrom background_colour: config.background_colour, payment_button_text_colour: config.payment_button_text_colour, sdk_ui_rules: config.sdk_ui_rules, - payment_button_text_weight: config.payment_button_text_weight, - payment_button_text_size: config.payment_button_text_size, - payment_button_padding: config.payment_button_padding, + payment_link_ui_rules: config.payment_link_ui_rules, } } } diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index e066cff0f21..2b563f5644b 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -2165,9 +2165,7 @@ impl ForeignFrom background_colour: item.background_colour, payment_button_text_colour: item.payment_button_text_colour, sdk_ui_rules: item.sdk_ui_rules, - payment_button_text_weight: item.payment_button_text_weight, - payment_button_text_size: item.payment_button_text_size, - payment_button_padding: item.payment_button_padding, + payment_link_ui_rules: item.payment_link_ui_rules, } } } @@ -2197,9 +2195,7 @@ impl ForeignFrom background_colour: item.background_colour, payment_button_text_colour: item.payment_button_text_colour, sdk_ui_rules: item.sdk_ui_rules, - payment_button_text_weight: item.payment_button_text_weight, - payment_button_text_size: item.payment_button_text_size, - payment_button_padding: item.payment_button_padding, + payment_link_ui_rules: item.payment_link_ui_rules, } } } From f233dadcacecd49fd7c4a22a47126523f7aaf5f1 Mon Sep 17 00:00:00 2001 From: Kashif Date: Wed, 5 Mar 2025 15:03:41 +0530 Subject: [PATCH 4/4] chore: add try catch for querySelector --- .../payment_link_initiate/payment_link.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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 b8701fc8368..5b8267c57a9 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 @@ -1117,12 +1117,16 @@ function renderSDKHeader(paymentDetails) { */ function updatePaymentLinkUi(paymentLinkUiRules) { Object.keys(paymentLinkUiRules).forEach(function (selector) { - var node = document.querySelector(selector); - if (node instanceof HTMLElement) { - var styles = paymentLinkUiRules[selector]; - Object.keys(styles).forEach(function (property) { - node.style[property] = styles[property]; - }); + try { + var node = document.querySelector(selector); + if (node instanceof HTMLElement) { + var styles = paymentLinkUiRules[selector]; + Object.keys(styles).forEach(function (property) { + node.style[property] = styles[property]; + }); + } + } catch (error) { + console.error("Failed to apply styles to selector", selector, error); } }) } \ No newline at end of file