feat(payment-link): add config for enabling form button only when form is complete (#7517)

This commit is contained in:
Kashif
2025-03-17 20:01:07 +05:30
committed by GitHub
parent c702535e91
commit 0be1f878ed
16 changed files with 123 additions and 14 deletions

View File

@ -152,6 +152,9 @@ pub const DEFAULT_DISPLAY_SDK_ONLY: bool = false;
/// Default bool to enable saved payment method
pub const DEFAULT_ENABLE_SAVED_PAYMENT_METHOD: bool = false;
/// [PaymentLink] Default bool for enabling button only when form is ready
pub const DEFAULT_ENABLE_BUTTON_ONLY_ON_FORM_READY: bool = false;
/// Default Merchant Logo Link
pub const DEFAULT_MERCHANT_LOGO: &str =
"https://live.hyperswitch.io/payment-link-assets/Merchant_placeholder.png";

View File

@ -23,8 +23,9 @@ use super::{
use crate::{
consts::{
self, DEFAULT_ALLOWED_DOMAINS, DEFAULT_BACKGROUND_COLOR, DEFAULT_DISPLAY_SDK_ONLY,
DEFAULT_ENABLE_SAVED_PAYMENT_METHOD, DEFAULT_HIDE_CARD_NICKNAME_FIELD,
DEFAULT_MERCHANT_LOGO, DEFAULT_PRODUCT_IMG, DEFAULT_SDK_LAYOUT, DEFAULT_SHOW_CARD_FORM,
DEFAULT_ENABLE_BUTTON_ONLY_ON_FORM_READY, DEFAULT_ENABLE_SAVED_PAYMENT_METHOD,
DEFAULT_HIDE_CARD_NICKNAME_FIELD, DEFAULT_MERCHANT_LOGO, DEFAULT_PRODUCT_IMG,
DEFAULT_SDK_LAYOUT, DEFAULT_SHOW_CARD_FORM,
},
errors::RouterResponse,
get_payment_link_config_value, get_payment_link_config_value_based_on_priority,
@ -137,6 +138,7 @@ pub async fn form_payment_link_data(
payment_button_text_colour: None,
sdk_ui_rules: None,
payment_link_ui_rules: None,
enable_button_only_on_form_ready: DEFAULT_ENABLE_BUTTON_ONLY_ON_FORM_READY,
}
};
@ -290,6 +292,7 @@ pub async fn form_payment_link_data(
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(),
enable_button_only_on_form_ready: payment_link_config.enable_button_only_on_form_ready,
};
Ok((
@ -348,6 +351,8 @@ pub async fn initiate_secure_payment_link_flow(
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,
};
let js_script = format!(
"window.__PAYMENT_DETAILS = {}",
@ -634,6 +639,7 @@ pub fn get_payment_link_config_based_on_priority(
enabled_saved_payment_method,
hide_card_nickname_field,
show_card_form_by_default,
enable_button_only_on_form_ready,
) = get_payment_link_config_value!(
payment_create_link_config,
business_theme_configs,
@ -647,7 +653,11 @@ pub fn get_payment_link_config_based_on_priority(
DEFAULT_ENABLE_SAVED_PAYMENT_METHOD
),
(hide_card_nickname_field, DEFAULT_HIDE_CARD_NICKNAME_FIELD),
(show_card_form_by_default, DEFAULT_SHOW_CARD_FORM)
(show_card_form_by_default, DEFAULT_SHOW_CARD_FORM),
(
enable_button_only_on_form_ready,
DEFAULT_ENABLE_BUTTON_ONLY_ON_FORM_READY
)
);
let (
@ -702,6 +712,7 @@ pub fn get_payment_link_config_based_on_priority(
payment_button_text_colour,
sdk_ui_rules,
payment_link_ui_rules,
enable_button_only_on_form_ready,
};
Ok((payment_link_config, domain_name))
@ -812,6 +823,7 @@ pub async fn get_payment_link_status(
payment_button_text_colour: None,
sdk_ui_rules: None,
payment_link_ui_rules: None,
enable_button_only_on_form_ready: DEFAULT_ENABLE_BUTTON_ONLY_ON_FORM_READY,
}
};

View File

@ -652,6 +652,10 @@ body {
cursor: not-allowed;
}
#submit.not-ready {
background-color: #C2C2C2 !important;
}
#submit-spinner {
width: 28px;
height: 28px;

View File

@ -290,9 +290,9 @@
<div></div>
</div>
</div>
<form id="payment-form" onclick="handleSubmit(); return false;">
<form id="payment-form">
<div id="unified-checkout"></div>
<button id="submit" class="hidden">
<button type="submit" id="submit" class="hidden">
<span id="submit-spinner" class="hidden"></span>
<span id="submit-button-text"></span>
</button>

View File

@ -257,7 +257,7 @@ function boot() {
// Update payment link styles
var paymentLinkUiRules = paymentDetails.payment_link_ui_rules;
if (paymentLinkUiRules !== null && typeof paymentLinkUiRules === "object" && Object.getPrototypeOf(paymentLinkUiRules) === Object.prototype) {
if (isObject(paymentLinkUiRules)) {
updatePaymentLinkUi(paymentLinkUiRules);
}
@ -279,7 +279,18 @@ function boot() {
boot();
/**
* Use - add event listeners for changing UI on screen resize
* Use - checks if a given value is an object
* @param {any} val
* @returns {boolean}
*/
function isObject(val) {
return val !== null && typeof val === "object" && Object.getPrototypeOf(val) === Object.prototype
}
/**
* Use - add event listeners for changing UI on
* - Screen resize
* - Form inputs
* @param {PaymentDetails} paymentDetails
*/
function initializeEventListeners(paymentDetails) {
@ -384,17 +395,58 @@ function initializeEventListeners(paymentDetails) {
// @ts-ignore
window.state.isMobileView = currentWidth <= 1199;
});
var paymentForm = document.getElementById("payment-form");
if (paymentForm instanceof HTMLFormElement) {
paymentForm.addEventListener("submit", function (event) {
event.preventDefault();
handleSubmit(event);
})
}
if (paymentDetails.enable_button_only_on_form_ready) {
handleFormReadyForSubmission();
}
}
function handleFormReadyForSubmission() {
window.addEventListener("message", function (event) {
// Event listener for updating the button rules
if (isObject(event.data) && event.data["isFormReadyForSubmission"] !== null) {
let isFormReadyForSubmission = event.data["isFormReadyForSubmission"];
var submitButtonNode = document.getElementById("submit");
if (submitButtonNode instanceof HTMLButtonElement) {
if (isFormReadyForSubmission === false) {
submitButtonNode.disabled = true;
addClass("#submit", "not-ready");
addClass("#submit", "disabled");
} else if (isFormReadyForSubmission === true) {
submitButtonNode.disabled = false;
removeClass("#submit", "not-ready");
removeClass("#submit", "disabled");
}
}
}
});
}
/**
* Trigger - post mounting SDK
* Use - set relevant classes to elements in the doc for showing SDK
**/
function showSDK(display_sdk_only) {
function showSDK(display_sdk_only, enable_button_only_on_form_ready) {
if (!display_sdk_only) {
show("#hyper-checkout-details");
}
show("#hyper-checkout-sdk");
if (enable_button_only_on_form_ready) {
addClass("#submit", "not-ready");
addClass("#submit", "disabled");
var submitButtonNode = document.getElementById("submit");
if (submitButtonNode instanceof HTMLButtonElement) {
submitButtonNode.disabled = true;
}
}
show("#submit");
show("#unified-checkout");
hide("#sdk-spinner");
@ -426,10 +478,11 @@ function handleSubmit(e) {
// Update button loader
hide("#submit-button-text");
show("#submit-spinner");
addClass("#submit", "processing");
addClass("#submit", "disabled");
var submitButtonNode = document.getElementById("submit");
if (submitButtonNode instanceof HTMLButtonElement) {
submitButtonNode.disabled = true;
submitButtonNode.classList.add("disabled");
}
hyper
@ -472,11 +525,12 @@ function handleSubmit(e) {
console.error("Error confirming payment_intent", error);
})
.finally(() => {
removeClass("#submit", "processing");
hide("#submit-spinner");
show("#submit-button-text");
removeClass("#submit", "disabled");
if (submitButtonNode instanceof HTMLButtonElement) {
submitButtonNode.disabled = false;
submitButtonNode.classList.remove("disabled");
}
});
}

View File

@ -75,7 +75,7 @@ function initializeSDK() {
// @ts-ignore
mountUnifiedCheckout("#unified-checkout");
// @ts-ignore
showSDK(paymentDetails.display_sdk_only);
showSDK(paymentDetails.display_sdk_only, paymentDetails.enable_button_only_on_form_ready);
let shimmer = document.getElementById("payment-details-shimmer");
shimmer.classList.add("reduce-opacity");

View File

@ -98,7 +98,7 @@ if (!isFramed) {
// @ts-ignore
mountUnifiedCheckout("#unified-checkout");
// @ts-ignore
showSDK(paymentDetails.display_sdk_only);
showSDK(paymentDetails.display_sdk_only, paymentDetails.enable_button_only_on_form_ready);
let shimmer = document.getElementById("payment-details-shimmer");
shimmer.classList.add("reduce-opacity");

View File

@ -4532,6 +4532,7 @@ impl ForeignFrom<api_models::admin::PaymentLinkConfigRequest>
payment_button_text_colour: config.payment_button_text_colour,
sdk_ui_rules: config.sdk_ui_rules,
payment_link_ui_rules: config.payment_link_ui_rules,
enable_button_only_on_form_ready: config.enable_button_only_on_form_ready,
}
}
}
@ -4602,6 +4603,7 @@ impl ForeignFrom<diesel_models::PaymentLinkConfigRequestForPayments>
payment_button_text_colour: config.payment_button_text_colour,
sdk_ui_rules: config.sdk_ui_rules,
payment_link_ui_rules: config.payment_link_ui_rules,
enable_button_only_on_form_ready: config.enable_button_only_on_form_ready,
}
}
}

View File

@ -2174,6 +2174,7 @@ impl ForeignFrom<api_models::admin::PaymentLinkConfigRequest>
payment_button_text_colour: item.payment_button_text_colour,
sdk_ui_rules: item.sdk_ui_rules,
payment_link_ui_rules: item.payment_link_ui_rules,
enable_button_only_on_form_ready: item.enable_button_only_on_form_ready,
}
}
}
@ -2204,6 +2205,7 @@ impl ForeignFrom<diesel_models::business_profile::PaymentLinkConfigRequest>
payment_button_text_colour: item.payment_button_text_colour,
sdk_ui_rules: item.sdk_ui_rules,
payment_link_ui_rules: item.payment_link_ui_rules,
enable_button_only_on_form_ready: item.enable_button_only_on_form_ready,
}
}
}