mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
fix: payment link styling for dynamic classes (#8273)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -1,4 +1,6 @@
|
||||
pub mod validator;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use actix_web::http::header;
|
||||
use api_models::{
|
||||
admin::PaymentLinkConfig,
|
||||
@ -142,6 +144,7 @@ pub async fn form_payment_link_data(
|
||||
payment_form_label_type: None,
|
||||
show_card_terms: None,
|
||||
is_setup_mandate_flow: None,
|
||||
color_icon_card_cvc_error: None,
|
||||
}
|
||||
};
|
||||
|
||||
@ -316,13 +319,13 @@ 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_link_ui_rules: payment_link_config.payment_link_ui_rules.clone(),
|
||||
status: payment_intent.status,
|
||||
enable_button_only_on_form_ready: payment_link_config.enable_button_only_on_form_ready,
|
||||
payment_form_header_text: payment_link_config.payment_form_header_text.clone(),
|
||||
payment_form_label_type: payment_link_config.payment_form_label_type,
|
||||
show_card_terms: payment_link_config.show_card_terms,
|
||||
is_setup_mandate_flow: payment_link_config.is_setup_mandate_flow,
|
||||
color_icon_card_cvc_error: payment_link_config.color_icon_card_cvc_error.clone(),
|
||||
capture_method: payment_attempt.capture_method,
|
||||
setup_future_usage_applied: payment_attempt.setup_future_usage_applied,
|
||||
};
|
||||
@ -350,7 +353,7 @@ pub async fn initiate_secure_payment_link_flow(
|
||||
&payment_link_config,
|
||||
)?;
|
||||
|
||||
let css_script = get_color_scheme_css(&payment_link_config);
|
||||
let css_script = get_payment_link_css_script(&payment_link_config)?;
|
||||
|
||||
match payment_link_details {
|
||||
PaymentLinkData::PaymentLinkStatusDetails(ref status_details) => {
|
||||
@ -380,12 +383,12 @@ 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_link_ui_rules: payment_link_config.payment_link_ui_rules,
|
||||
enable_button_only_on_form_ready: payment_link_config
|
||||
.enable_button_only_on_form_ready,
|
||||
payment_form_header_text: payment_link_config.payment_form_header_text,
|
||||
payment_form_label_type: payment_link_config.payment_form_label_type,
|
||||
show_card_terms: payment_link_config.show_card_terms,
|
||||
color_icon_card_cvc_error: payment_link_config.color_icon_card_cvc_error,
|
||||
};
|
||||
let payment_details_str = serde_json::to_string(&secure_payment_link_details)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
@ -446,7 +449,7 @@ pub async fn initiate_payment_link_flow(
|
||||
let (_, payment_details, payment_link_config) =
|
||||
form_payment_link_data(&state, merchant_context, merchant_id, payment_id).await?;
|
||||
|
||||
let css_script = get_color_scheme_css(&payment_link_config);
|
||||
let css_script = get_payment_link_css_script(&payment_link_config)?;
|
||||
let js_script = get_js_script(&payment_details)?;
|
||||
|
||||
match payment_details {
|
||||
@ -494,6 +497,85 @@ fn get_js_script(payment_details: &PaymentLinkData) -> RouterResult<String> {
|
||||
Ok(format!("window.__PAYMENT_DETAILS = '{url_encoded_str}';"))
|
||||
}
|
||||
|
||||
fn camel_to_kebab(s: &str) -> String {
|
||||
let mut result = String::new();
|
||||
if s.is_empty() {
|
||||
return result;
|
||||
}
|
||||
|
||||
let chars: Vec<char> = s.chars().collect();
|
||||
|
||||
for (i, &ch) in chars.iter().enumerate() {
|
||||
if ch.is_uppercase() {
|
||||
let should_add_dash = i > 0
|
||||
&& (chars.get(i - 1).map(|c| c.is_lowercase()).unwrap_or(false)
|
||||
|| (i + 1 < chars.len()
|
||||
&& chars.get(i + 1).map(|c| c.is_lowercase()).unwrap_or(false)
|
||||
&& chars.get(i - 1).map(|c| c.is_uppercase()).unwrap_or(false)));
|
||||
|
||||
if should_add_dash {
|
||||
result.push('-');
|
||||
}
|
||||
result.push(ch.to_ascii_lowercase());
|
||||
} else {
|
||||
result.push(ch);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn generate_dynamic_css(
|
||||
rules: &HashMap<String, HashMap<String, String>>,
|
||||
) -> Result<String, errors::ApiErrorResponse> {
|
||||
if rules.is_empty() {
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
||||
let mut css_string = String::new();
|
||||
css_string.push_str("/* Dynamically Injected UI Rules */\n");
|
||||
|
||||
for (selector, styles_map) in rules {
|
||||
if selector.trim().is_empty() {
|
||||
return Err(errors::ApiErrorResponse::InvalidRequestData {
|
||||
message: "CSS selector cannot be empty.".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
css_string.push_str(selector);
|
||||
css_string.push_str(" {\n");
|
||||
|
||||
for (prop_camel_case, css_value) in styles_map {
|
||||
let css_property = camel_to_kebab(prop_camel_case);
|
||||
|
||||
css_string.push_str(" ");
|
||||
css_string.push_str(&css_property);
|
||||
css_string.push_str(": ");
|
||||
css_string.push_str(css_value);
|
||||
css_string.push_str(";\n");
|
||||
}
|
||||
css_string.push_str("}\n");
|
||||
}
|
||||
Ok(css_string)
|
||||
}
|
||||
|
||||
fn get_payment_link_css_script(
|
||||
payment_link_config: &PaymentLinkConfig,
|
||||
) -> Result<String, errors::ApiErrorResponse> {
|
||||
let custom_rules_css_option = payment_link_config
|
||||
.payment_link_ui_rules
|
||||
.as_ref()
|
||||
.map(generate_dynamic_css)
|
||||
.transpose()?;
|
||||
|
||||
let color_scheme_css = get_color_scheme_css(payment_link_config);
|
||||
|
||||
if let Some(custom_rules_css) = custom_rules_css_option {
|
||||
Ok(format!("{}\n{}", color_scheme_css, custom_rules_css))
|
||||
} else {
|
||||
Ok(color_scheme_css)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_color_scheme_css(payment_link_config: &PaymentLinkConfig) -> String {
|
||||
let background_primary_color = payment_link_config
|
||||
.background_colour
|
||||
@ -706,6 +788,7 @@ pub fn get_payment_link_config_based_on_priority(
|
||||
payment_form_label_type,
|
||||
show_card_terms,
|
||||
is_setup_mandate_flow,
|
||||
color_icon_card_cvc_error,
|
||||
) = get_payment_link_config_value!(
|
||||
payment_create_link_config,
|
||||
business_theme_configs,
|
||||
@ -724,6 +807,7 @@ pub fn get_payment_link_config_based_on_priority(
|
||||
(payment_form_label_type),
|
||||
(show_card_terms),
|
||||
(is_setup_mandate_flow),
|
||||
(color_icon_card_cvc_error),
|
||||
);
|
||||
|
||||
let payment_link_config =
|
||||
@ -756,6 +840,7 @@ pub fn get_payment_link_config_based_on_priority(
|
||||
payment_form_label_type,
|
||||
show_card_terms,
|
||||
is_setup_mandate_flow,
|
||||
color_icon_card_cvc_error,
|
||||
};
|
||||
|
||||
Ok((payment_link_config, domain_name))
|
||||
@ -870,6 +955,7 @@ pub async fn get_payment_link_status(
|
||||
payment_form_label_type: None,
|
||||
show_card_terms: None,
|
||||
is_setup_mandate_flow: None,
|
||||
color_icon_card_cvc_error: None,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -653,7 +653,7 @@ body {
|
||||
}
|
||||
|
||||
#submit.not-ready {
|
||||
background-color: #C2C2C2 !important;
|
||||
background-color: #C2C2C2;
|
||||
}
|
||||
|
||||
#submit-spinner {
|
||||
|
||||
@ -291,12 +291,6 @@ function boot() {
|
||||
// Add event listeners
|
||||
initializeEventListeners(paymentDetails);
|
||||
|
||||
// Update payment link styles
|
||||
var paymentLinkUiRules = paymentDetails.payment_link_ui_rules;
|
||||
if (isObject(paymentLinkUiRules)) {
|
||||
updatePaymentLinkUi(paymentLinkUiRules);
|
||||
}
|
||||
|
||||
// Initialize SDK
|
||||
// @ts-ignore
|
||||
if (window.Hyper) {
|
||||
@ -358,11 +352,11 @@ function initializeEventListeners(paymentDetails) {
|
||||
if (payNowButtonText) {
|
||||
if (paymentDetails.payment_button_text) {
|
||||
payNowButtonText.textContent = paymentDetails.payment_button_text;
|
||||
} else if (paymentDetails.is_setup_mandate_flow || (paymentDetails.amount==="0.00" && paymentDetails.setup_future_usage_applied ==="off_session")) {
|
||||
} else if (paymentDetails.is_setup_mandate_flow || (paymentDetails.amount === "0.00" && paymentDetails.setup_future_usage_applied === "off_session")) {
|
||||
payNowButtonText.textContent = translations.addPaymentMethod;
|
||||
} else {
|
||||
payNowButtonText.textContent = capture_type === "manual" ? translations.authorizePayment: translations.payNow;
|
||||
|
||||
payNowButtonText.textContent = capture_type === "manual" ? translations.authorizePayment : translations.payNow;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1214,25 +1208,4 @@ function renderSDKHeader(paymentDetails) {
|
||||
// sdkHeaderNode.append(sdkHeaderLogoNode);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -14,6 +14,7 @@ function initializeSDK() {
|
||||
var clientSecret = paymentDetails.client_secret;
|
||||
var sdkUiRules = paymentDetails.sdk_ui_rules;
|
||||
var labelType = paymentDetails.payment_form_label_type;
|
||||
var colorIconCardCvcError = paymentDetails.color_icon_card_cvc_error;
|
||||
var appearance = {
|
||||
variables: {
|
||||
colorPrimary: paymentDetails.theme || "rgb(0, 109, 249)",
|
||||
@ -33,6 +34,9 @@ function initializeSDK() {
|
||||
if (labelType !== null && typeof labelType === "string") {
|
||||
appearance.labels = labelType;
|
||||
}
|
||||
if (colorIconCardCvcError !== null && typeof colorIconCardCvcError === "string") {
|
||||
appearance.variables.colorIconCardCvcError = colorIconCardCvcError;
|
||||
}
|
||||
// @ts-ignore
|
||||
hyper = window.Hyper(pub_key, {
|
||||
isPreloadEnabled: false,
|
||||
|
||||
@ -34,6 +34,7 @@ if (!isFramed) {
|
||||
var clientSecret = paymentDetails.client_secret;
|
||||
var sdkUiRules = paymentDetails.sdk_ui_rules;
|
||||
var labelType = paymentDetails.payment_form_label_type;
|
||||
var colorIconCardCvcError = paymentDetails.color_icon_card_cvc_error;
|
||||
var appearance = {
|
||||
variables: {
|
||||
colorPrimary: paymentDetails.theme || "rgb(0, 109, 249)",
|
||||
@ -53,6 +54,9 @@ if (!isFramed) {
|
||||
if (labelType !== null && typeof labelType === "string") {
|
||||
appearance.labels = labelType;
|
||||
}
|
||||
if (colorIconCardCvcError !== null && typeof colorIconCardCvcError === "string") {
|
||||
appearance.variables.colorIconCardCvcError = colorIconCardCvcError;
|
||||
}
|
||||
// @ts-ignore
|
||||
hyper = window.Hyper(pub_key, {
|
||||
isPreloadEnabled: false,
|
||||
|
||||
@ -5017,6 +5017,7 @@ impl ForeignFrom<api_models::admin::PaymentLinkConfigRequest>
|
||||
payment_form_label_type: config.payment_form_label_type,
|
||||
show_card_terms: config.show_card_terms,
|
||||
is_setup_mandate_flow: config.is_setup_mandate_flow,
|
||||
color_icon_card_cvc_error: config.color_icon_card_cvc_error,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5092,6 +5093,7 @@ impl ForeignFrom<diesel_models::PaymentLinkConfigRequestForPayments>
|
||||
payment_form_label_type: config.payment_form_label_type,
|
||||
show_card_terms: config.show_card_terms,
|
||||
is_setup_mandate_flow: config.is_setup_mandate_flow,
|
||||
color_icon_card_cvc_error: config.color_icon_card_cvc_error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2242,6 +2242,7 @@ impl ForeignFrom<api_models::admin::PaymentLinkConfigRequest>
|
||||
payment_form_label_type: item.payment_form_label_type,
|
||||
show_card_terms: item.show_card_terms,
|
||||
is_setup_mandate_flow: item.is_setup_mandate_flow,
|
||||
color_icon_card_cvc_error: item.color_icon_card_cvc_error,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2277,6 +2278,7 @@ impl ForeignFrom<diesel_models::business_profile::PaymentLinkConfigRequest>
|
||||
payment_form_label_type: item.payment_form_label_type,
|
||||
show_card_terms: item.show_card_terms,
|
||||
is_setup_mandate_flow: item.is_setup_mandate_flow,
|
||||
color_icon_card_cvc_error: item.color_icon_card_cvc_error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user