diff --git a/assets/js/modules/membership/frontend/user-registration-membership-frontend.js b/assets/js/modules/membership/frontend/user-registration-membership-frontend.js index 90fea9a53..1d5f6350a 100644 --- a/assets/js/modules/membership/frontend/user-registration-membership-frontend.js +++ b/assets/js/modules/membership/frontend/user-registration-membership-frontend.js @@ -3120,39 +3120,44 @@ $(document).on( "user_registration_frontend_validate_before_form_submit", function (e, $form) { - var stripe_selected = false; - $('input[name="urm_payment_method"]:visible').each( - function () { - if ( - $(this).val() === "stripe" && - $(this).is(":checked") - ) { - stripe_selected = true; - } - } + // Detect Stripe by presence of a visible card container — works + // even when there is only one payment method and no radio button. + var $stripeContainer = $form.find( + ".stripe-input-container" ); - // console.log('[URM Stripe Debug] stripe_selected:', stripe_selected, '| stripe_mode_validated:', stripe_mode_validated, '| elements.card:', !!elements.card, '| elements.stripe:', !!elements.stripe); - if (!stripe_selected) return; + if ( + !$stripeContainer.length || + !$stripeContainer.is(":visible") + ) { + return; + } + + if (stripe_mode_validated) { + return; + } - if (stripe_mode_validated) return; + // Remove any stale error label from a previous attempt. + $form.find("#stripe-errors.user-registration-error").remove(); - var stripeEmpty = $(".ur-frontend-form").find( - ".stripe-input-container .StripeElement--empty" + var stripeEmpty = $stripeContainer.find( + ".StripeElement--empty" ).length; - // console.log('[URM Stripe Debug] elements.card:', !!elements.card, '| StripeElement--empty found:', stripeEmpty); - if ( - !elements || - !elements.stripe || - !elements.card || - stripeEmpty - ) { - // console.log('[URM Stripe Debug] Early return — skipping pre-validation'); + + if (stripeEmpty) { + // Show inline label directly in the form — no dependency on + // stripe_settings or $membership_registration_form. + $stripeContainer.append( + '" + ); return; } - stripe_settings.show_stripe_error( - urmf_data.labels.i18n_validating_stripe_card - ); + // Card has content: run async API validation. + if (!elements || !elements.stripe || !elements.card) { + return; + } elements.stripe .createPaymentMethod({ @@ -3161,9 +3166,9 @@ }) .then(function (pmResult) { if (pmResult.error) { - stripe_settings.show_stripe_error( - pmResult.error.message - ); + $form + .find("#stripe-errors") + .text(pmResult.error.message); return; } @@ -3171,12 +3176,13 @@ urmf_data.ajax_url, { action: "user_registration_membership_validate_stripe_card_mode", - _nonce: urmf_data._nonce, - payment_method_id: pmResult.paymentMethod.id + security: urmf_data._nonce, + payment_method_id: + pmResult.paymentMethod.id }, function (response) { if (response.success) { - $membership_registration_form + $form .find("#stripe-errors") .remove(); validated_stripe_pm_id = @@ -3189,13 +3195,15 @@ $form.submit(); } } else { - stripe_settings.show_stripe_error( - response.data && - response.data.message - ? response.data.message - : urmf_data.labels - .i18n_stripe_mode_error - ); + $form + .find("#stripe-errors") + .text( + response.data && + response.data.message + ? response.data.message + : urmf_data.labels + .i18n_stripe_mode_error + ); } } ); @@ -3853,6 +3861,16 @@ .not(seatInput) .prop("disabled", true); }); + + // If Stripe is already selected on page load, initialize it so elements.card + // is ready before the first submit attempt. + var $initialMethod = $('input[name="urm_payment_method"]:checked'); + if ($initialMethod.length && $initialMethod.val() === "stripe") { + if (!elements || !elements.card) { + $(".stripe-container").removeClass("urm-d-none"); + stripe_settings.init(); + } + } }, validateSwitchCurrency: function (paymentMethod) { var $select = $("#ur-local-currency-switch-currency"); diff --git a/modules/membership/includes/Frontend/Frontend.php b/modules/membership/includes/Frontend/Frontend.php index 11b37656f..ea5af76ff 100644 --- a/modules/membership/includes/Frontend/Frontend.php +++ b/modules/membership/includes/Frontend/Frontend.php @@ -37,12 +37,33 @@ public function __construct() { */ private function init_hooks() { add_action( 'wp_enqueue_membership_scripts', array( $this, 'load_scripts' ), 10, 2 ); + add_action( 'user_registration_enqueue_scripts', array( $this, 'maybe_load_scripts_for_form' ), 10, 2 ); add_action( 'template_redirect', array( $this, 'set_thank_you_transient' ) ); add_action( 'wp_loaded', array( $this, 'clear_upgrade_data' ) ); add_action( 'user_registration_before_register_user_action', array( $this, 'validate_stripe_card_before_register' ), 10, 2 ); } + public function maybe_load_scripts_for_form( $form_data_array, $form_id ) { + if ( wp_script_is( 'user-registration-membership-frontend-script', 'enqueued' ) ) { + return; + } + if ( empty( $form_data_array ) || ! is_iterable( $form_data_array ) ) { + return; + } + foreach ( $form_data_array as $row ) { + foreach ( $row as $grid ) { + foreach ( $grid as $field ) { + $field_key = isset( $field->field_key ) ? $field->field_key : ''; + if ( 'membership' === $field_key ) { + $this->load_scripts(); + return; + } + } + } + } + } + /** * Delete Account insert after helper. * diff --git a/modules/membership/includes/Templates/membership-registration-form.php b/modules/membership/includes/Templates/membership-registration-form.php index 8fc2a43fb..56498c73f 100644 --- a/modules/membership/includes/Templates/membership-registration-form.php +++ b/modules/membership/includes/Templates/membership-registration-form.php @@ -554,9 +554,13 @@ class="ur_membership_frontend_input_container urm-d-none urm_hidden_payment_cont - +
+ +