Skip to content

Revamp/UI ux of payment page#1846

Open
sapayth wants to merge 7 commits intoweDevsOfficial:developfrom
sapayth:revamp/ui_ux_of_payment_page
Open

Revamp/UI ux of payment page#1846
sapayth wants to merge 7 commits intoweDevsOfficial:developfrom
sapayth:revamp/ui_ux_of_payment_page

Conversation

@sapayth
Copy link
Copy Markdown
Member

@sapayth sapayth commented Apr 16, 2026

Closes #935
Related PR #1488

Summary

Redesigns the payment/checkout page with a modern two-column layout: payment methods on the left and an order summary panel on the right. The page is now cleaner and easier to scan, with gateway cards replacing the old plain radio list and a dedicated order summary section showing item details, pricing, and coupon controls in context.

Technical Notes

  • Introduces a new wpuf_billing_data filter — allows developers to modify the billing data array (type, title, subtotal, billing amount) before it's passed to the order summary.

Summary by CodeRabbit

  • New Features

    • Redesigned checkout: billing moved above, responsive two-column layout, card-style payment selectors, improved order summary and coupon apply/remove UI
    • Checkout button now “Continue to Payment” and shows a processing spinner/disabled state during submission
  • Bug Fixes / Tweaks

    • Hidden pro-preview gateways from checkout
    • Stripe shortcut skips redundant validation for smoother flow
    • Updated default bank icon and minor PayPal text tweak
    • Translation template updated with new UI strings

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

Walkthrough

Refactors checkout UI into a two-column gateway/order-summary layout with card-style gateways, moves coupon UI into the order summary, scopes and extends coupon/checkout JS (add remove + submit loading), normalizes billing data and order-summary rendering in PHP, and adds small gateway/default and subscription robustness tweaks.

Changes

Checkout styling & layout

Layer / File(s) Summary
Data shape / selectors
assets/less/frontend-forms.less, assets/css/frontend-forms.css
Introduce .wpuf-checkout-wrap, .wpuf-checkout-columns, .wpuf-gateway-card, .wpuf-order-summary, coupon-related selectors, checkout footer/button states and .wpuf-btn-spinner/@keyframes wpuf-spin; reduce legacy .wpuf-coupon-info-wrap to outer wrapper.
Presentation
assets/css/frontend-forms.css, assets/less/frontend-forms.less
Card-style gateway list, icon/info/label/description layout, large-radio hit area, instruction divider, order-row/label/value/divider, coupon applied/banner layout, full-width CTA and loading styles.
Responsive / compatibility
assets/less/frontend-forms.less, assets/css/frontend-forms.css
Two-column collapse under 768px, centralized #wpuf-payment-gateway .wpuf-instruction rule to replace prior narrow-screen variants.

Checkout & coupon JavaScript

Layer / File(s) Summary
Event wiring / delegation
assets/js/subscriptions-old.js
Delegate coupon handlers to .wpuf-order-summary and add delegation for .wpuf-coupon-remove; wire payment form to new checkoutSubmit handler.
Coupon operations
assets/js/subscriptions-old.js
Scope couponShow/couponApply/couponCancel to nearest .wpuf-order-summary, add `type: $('#wpuf_type').text()
Checkout submit
assets/js/subscriptions-old.js, assets/js/billing-address.js
Add checkoutSubmit() to disable button, show spinner/“Processing…”, set busy state; billing-address submit handler early-returns for stripe (skips form validation/hidden billing population) while other gateways run existing validation.

Payment rendering / Checkout PHP

Layer / File(s) Summary
Gateway list filtration
includes/Free/Free_Loader.php, includes/Frontend/Payment.php
Mark stripe in Free_Loader as is_pro_preview; get_active_gateways() now omits gateways with is_pro_preview.
Billing data shape
includes/Frontend/Payment.php
Build normalized $billing_data (packs/posts), compute subtotal/billing_amount via wpuf_payment_amount, expose hook wpuf_billing_data.
Rendering & wiring
includes/Frontend/Payment.php
Move billing address above new two-column layout (left: gateways; right: order summary), render sanitized gate_instruct_{id} snippet, keep hidden gateway form action hooks, replace prior input submit with <button> labeled “Continue to Payment”, add checkout errors container.
Render helpers
includes/Frontend/Payment.php
Add private render methods for order item, order total, and full order summary and invoke existing compatibility hooks (keep wpuf_before_pack_payment_total).

Gateways & small robustness fixes

Layer / File(s) Summary
Icon filter
Lib/Gateway/Bank_Gateway.php
$this->icon now initialized via apply_filters( 'wpuf_bank_checkout_icon', WPUF_ASSET_URI . '/images/bank.svg' ).
PayPal adjustments
Lib/Gateway/Paypal.php
Default gate_instruct_paypal wording tweak; get_access_token() now returns '' when response lacks access_token; whitespace-only adjustments elsewhere.
Subscription safety
includes/Admin/Subscription.php
Safer $profile_id retrieval using null coalescing when processing recurring pack payments.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

needs: dev review

Poem

🐰 I hopped through styles and lines so neat,
Cards and totals, every row complete,
Coupons hide and then they leap away,
JS spins, PHP guides the pay,
A rabbit cheers — checkout’s ready today!

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The code changes focus on payment page UI redesign and checkout flow improvements, but the linked issue #935 addresses draft post editing accessibility—a fundamentally different problem with no apparent related changes. Review the issue linkage: either relink this PR to the correct payment/checkout issue or add changes that address draft post editing for default post types.
Out of Scope Changes check ⚠️ Warning All changes are focused on payment page UI/UX redesign and checkout flow improvements, which appear disconnected from the linked issue #935 about draft post editing—suggesting potential scope mismatch. Verify the intended scope: confirm whether this PR addresses payment page redesign (in which case relink to appropriate issue) or draft post editing (in which case changes are missing).
Title check ❓ Inconclusive The PR title 'Revamp/UI ux of payment page' is partially related to the changeset, referring to the redesign of the checkout/payment page UI, but does not accurately reflect the linked issue #935 which concerns draft post editing accessibility. Clarify the PR title: either update it to reflect the actual main objective (draft post editing fix) or provide additional context if the payment page redesign is the primary focus.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 PHPStan (2.1.51)

PHPStan was skipped because the sandbox runner could not parse its output.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
assets/js/subscriptions-old.js (2)

154-176: couponRemove duplicates couponCancel logic.

The couponRemove function is nearly identical to couponCancel, with the only difference being that couponRemove doesn't add/remove the spinner class. Consider extracting the shared logic into a helper function to reduce duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/subscriptions-old.js` around lines 154 - 176, The couponRemove
function duplicates couponCancel logic; extract the shared AJAX/post and
DOM-update logic into a new helper (e.g., handleCouponAjax or updateCouponUI)
and have both couponRemove and couponCancel call it; move the common data
construction (action, _wpnonce, pack_id, type) and the success callback that
updates .wpuf-pack-inner, .wpuf-coupon-id-field, .wpuf-coupon-applied, and
.wpuf-copon-show into that helper, while keeping only the spinner add/remove
behavior inside couponCancel.

127-129: Potential whitespace issue with $('#wpuf_type').text().

Using .text() on a div may capture unintended whitespace. Since Payment.php renders these as <div id="wpuf_type" style="display: none"><?php echo 'pack'; ?></div>, this should work, but using .text().trim() would be safer. Alternatively, if the PHP is updated to use hidden inputs as suggested, change to .val().

Safer approach
                     pack_id: self.data('pack_id'),

-                    type: $( '#wpuf_type' ).text() || 'pack'
+                    type: $( '#wpuf_type' ).text().trim() || 'pack'

Apply the same change to lines 164 and 220.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/subscriptions-old.js` around lines 127 - 129, The code uses
$('#wpuf_type').text() when building the payload (type: $('#wpuf_type').text()
|| 'pack'), which can include stray whitespace; change it to
$('#wpuf_type').text().trim() || 'pack' (or to $('#wpuf_type').val() || 'pack'
if you switch the PHP to a hidden input) and apply the same .trim()/.val()
change to the other occurrences of the '#wpuf_type' selector in this file (the
other payload/assignment sites referenced in the review).
includes/Frontend/Payment.php (1)

295-306: Use data attributes instead of hidden divs for JavaScript data passing.

Using hidden <div> elements with text content for passing data to JavaScript is unconventional. Consider using data-* attributes on a relevant element or wp_localize_script() for a more standard approach. This also avoids potential whitespace issues with .text() in JS.

Suggested approach using data attributes
-                                   <?php
-                                   if ( $pack_id ) {
-                                       ?>
-                                       <div id="wpuf_type" style="display: none"><?php echo 'pack'; ?></div>
-                                       <div id="wpuf_id" style="display: none"><?php echo esc_attr( $pack_id ); ?></div>
-                                       <?php
-                                   } elseif ( $post_id ) {
-                                       ?>
-                                       <div id="wpuf_type" style="display: none"><?php echo 'post'; ?></div>
-                                       <div id="wpuf_id" style="display: none"><?php echo esc_attr( $post_id ); ?></div>
-                                       <?php
-                                   }
-                                   ?>
+                                   <input type="hidden" id="wpuf_type" value="<?php echo esc_attr( $pack_id ? 'pack' : 'post' ); ?>">
+                                   <input type="hidden" id="wpuf_id" value="<?php echo esc_attr( $pack_id ? $pack_id : $post_id ); ?>">

Then update the JavaScript to use .val() instead of .text().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@includes/Frontend/Payment.php` around lines 295 - 306, The hidden <div>s with
ids "wpuf_type" and "wpuf_id" in includes/Frontend/Payment.php should be
replaced with data-* attributes (or use wp_localize_script()) on an existing
container element so JS reads values via element.dataset or jQuery .data()/
.val() instead of using .text(); update the code that references
wpuf_type/wpuf_id in your JavaScript to read the new data attributes (or the
localized object) and remove the hidden div outputs in Payment.php (look for the
blocks that echo 'pack'/'post' and esc_attr($pack_id)/esc_attr($post_id)).
assets/css/frontend-forms.css (1)

962-974: Typo: wpuf-copon should be wpuf-coupon.

The class names .wpuf-copon-show, .wpuf-copon-wrap, and .wpuf-copon-cancel contain a typo ("copon" instead of "coupon"). While this is consistent with the JavaScript selectors in subscriptions-old.js, it creates an inconsistent naming convention alongside .wpuf-coupon-field, .wpuf-coupon-applied, etc.

Consider renaming these to use the correct spelling for maintainability, updating both CSS/LESS and JS files together.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/css/frontend-forms.css` around lines 962 - 974, Rename the misspelled
CSS class names (.wpuf-copon-show, .wpuf-copon-wrap, .wpuf-copon-cancel) to the
correct spelling (.wpuf-coupon-show, .wpuf-coupon-wrap, .wpuf-coupon-cancel) in
assets/css/frontend-forms.css and update every corresponding usage in JS (e.g.,
subscriptions-old.js) and any templates that reference those selectors so they
stay consistent with existing classes like .wpuf-coupon-field and
.wpuf-coupon-applied; ensure you replace all occurrences (including event
listeners and querySelector/querySelectorAll strings) and run a quick smoke test
to confirm coupon show/hide and cancel behaviors still work.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@assets/css/frontend-forms.css`:
- Around line 894-897: The CSS rule using the selector body
li:has(input[type="radio"]:checked) .wpuf-gateway-card should use the lowercase
keyword currentcolor (replace currentColor with currentcolor) to satisfy
linting; also add a fallback rule for older browsers that don't support :has by
duplicating the styling using a more widely supported relationship such as
input[type="radio"]:checked ~ .wpuf-gateway-card or input[type="radio"]:checked
+ .wpuf-gateway-card (pick the sibling combinator that matches the markup) so
the same border-color/box-shadow styles apply when the radio is checked without
relying on :has.

In `@assets/js/subscriptions-old.js`:
- Around line 251-256: Replace the hardcoded "Coupon code applied" string in the
orderSummary.append call with a localized value provided by the wpuf_frontend
object; ensure PHP registers that text via wp_localize_script (e.g.,
'coupon_applied_text' => __( 'Coupon code applied', 'wp-user-frontend' )), then
in the JS change the HTML constructed in orderSummary.append (the block that
includes '<span class="wpuf-coupon-applied-label">...') to use
wpuf_frontend.coupon_applied_text while keeping the surrounding markup and the
existing data-pack_id usage on the .wpuf-coupon-remove button.

In `@assets/less/frontend-forms.less`:
- Around line 862-867: The .wpuf-order-summary rule in the LESS source is
missing the background property present in compiled CSS; update the
.wpuf-order-summary selector in assets/less/frontend-forms.less by adding
background: `#f9fafb`; to the rule (alongside border, border-radius, padding) and
recompile the LESS so the generated CSS remains in sync with the source.

In `@includes/Frontend/Payment.php`:
- Around line 168-169: Remove the two unused variable assignments $details_meta
and $currency in the Payment class code: eliminate the call to
wpuf()->subscription->get_details_meta_value() assigned to $details_meta and the
wpuf_get_currency('symbol') call assigned to $currency since neither variable is
used afterward; leaving no side-effectful calls and ensuring no references
remain to $details_meta or $currency elsewhere in the method (search for
$details_meta and $currency to confirm), then run static analysis to verify the
unused-variable warning is resolved.
- Around line 189-194: Remove the unused local variable $pay_per_post and stop
reassigning $current_user to wpuf_get_user(); instead, keep the original
$current_user (from earlier) and, if you need the subscription helper from
wpuf_get_user(), assign that to a new variable name (e.g., $wpuf_user or
$subscription_helper) or call $current_user->subscription()->current_pack()
directly; update references to use the new variable or the original
$current_user to avoid type confusion and redundant assignments (check the block
using get_subs_fallback_cost(), get_pay_per_post_cost(), and current_pack()).

---

Nitpick comments:
In `@assets/css/frontend-forms.css`:
- Around line 962-974: Rename the misspelled CSS class names (.wpuf-copon-show,
.wpuf-copon-wrap, .wpuf-copon-cancel) to the correct spelling
(.wpuf-coupon-show, .wpuf-coupon-wrap, .wpuf-coupon-cancel) in
assets/css/frontend-forms.css and update every corresponding usage in JS (e.g.,
subscriptions-old.js) and any templates that reference those selectors so they
stay consistent with existing classes like .wpuf-coupon-field and
.wpuf-coupon-applied; ensure you replace all occurrences (including event
listeners and querySelector/querySelectorAll strings) and run a quick smoke test
to confirm coupon show/hide and cancel behaviors still work.

In `@assets/js/subscriptions-old.js`:
- Around line 154-176: The couponRemove function duplicates couponCancel logic;
extract the shared AJAX/post and DOM-update logic into a new helper (e.g.,
handleCouponAjax or updateCouponUI) and have both couponRemove and couponCancel
call it; move the common data construction (action, _wpnonce, pack_id, type) and
the success callback that updates .wpuf-pack-inner, .wpuf-coupon-id-field,
.wpuf-coupon-applied, and .wpuf-copon-show into that helper, while keeping only
the spinner add/remove behavior inside couponCancel.
- Around line 127-129: The code uses $('#wpuf_type').text() when building the
payload (type: $('#wpuf_type').text() || 'pack'), which can include stray
whitespace; change it to $('#wpuf_type').text().trim() || 'pack' (or to
$('#wpuf_type').val() || 'pack' if you switch the PHP to a hidden input) and
apply the same .trim()/.val() change to the other occurrences of the
'#wpuf_type' selector in this file (the other payload/assignment sites
referenced in the review).

In `@includes/Frontend/Payment.php`:
- Around line 295-306: The hidden <div>s with ids "wpuf_type" and "wpuf_id" in
includes/Frontend/Payment.php should be replaced with data-* attributes (or use
wp_localize_script()) on an existing container element so JS reads values via
element.dataset or jQuery .data()/ .val() instead of using .text(); update the
code that references wpuf_type/wpuf_id in your JavaScript to read the new data
attributes (or the localized object) and remove the hidden div outputs in
Payment.php (look for the blocks that echo 'pack'/'post' and
esc_attr($pack_id)/esc_attr($post_id)).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5c39228e-bd56-4aae-9c0a-385a7e9c1280

📥 Commits

Reviewing files that changed from the base of the PR and between 9cf91fb and 8090dc4.

📒 Files selected for processing (11)
  • assets/css/admin/form-builder.css
  • assets/css/admin/subscriptions.min.css
  • assets/css/ai-form-builder.css
  • assets/css/ai-form-builder.min.css
  • assets/css/forms-list.min.css
  • assets/css/frontend-forms.css
  • assets/css/frontend-subscriptions.min.css
  • assets/css/frontend/account.css
  • assets/js/subscriptions-old.js
  • assets/less/frontend-forms.less
  • includes/Frontend/Payment.php

Comment thread assets/css/frontend-forms.css Outdated
Comment on lines +894 to +897
body li:has(input[type="radio"]:checked) .wpuf-gateway-card {
border-color: currentColor;
box-shadow: 0 0 0 1px currentColor;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix currentColor casing per linting rules.

Static analysis indicates that currentColor should be currentcolor for consistency with CSS value keyword conventions.

Additionally, the li:has() selector has limited support in older browsers (Safari 15.3-, Firefox 120-). Consider whether fallback styling is needed for older browser support.

Proposed fix for casing
 body li:has(input[type="radio"]:checked) .wpuf-gateway-card {
-  border-color: currentColor;
-  box-shadow: 0 0 0 1px currentColor;
+  border-color: currentcolor;
+  box-shadow: 0 0 0 1px currentcolor;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
body li:has(input[type="radio"]:checked) .wpuf-gateway-card {
border-color: currentColor;
box-shadow: 0 0 0 1px currentColor;
}
body li:has(input[type="radio"]:checked) .wpuf-gateway-card {
border-color: currentcolor;
box-shadow: 0 0 0 1px currentcolor;
}
🧰 Tools
🪛 Stylelint (17.7.0)

[error] 895-895: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)


[error] 896-896: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/css/frontend-forms.css` around lines 894 - 897, The CSS rule using the
selector body li:has(input[type="radio"]:checked) .wpuf-gateway-card should use
the lowercase keyword currentcolor (replace currentColor with currentcolor) to
satisfy linting; also add a fallback rule for older browsers that don't support
:has by duplicating the styling using a more widely supported relationship such
as input[type="radio"]:checked ~ .wpuf-gateway-card or
input[type="radio"]:checked + .wpuf-gateway-card (pick the sibling combinator
that matches the markup) so the same border-color/box-shadow styles apply when
the radio is checked without relying on :has.

Comment on lines +251 to +256
orderSummary.append(
'<div class="wpuf-coupon-applied">' +
'<span class="wpuf-coupon-applied-label">Coupon code applied</span>' +
'<button type="button" class="wpuf-coupon-remove" data-pack_id="' + self.data('pack_id') + '">&times;</button>' +
'</div>'
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded English text should be internationalized.

The string "Coupon code applied" is hardcoded in JavaScript. For proper internationalization, this should come from a localized script variable (e.g., wpuf_frontend.coupon_applied_text).

Suggested fix

Ensure the string is localized in PHP via wp_localize_script():

'coupon_applied_text' => __( 'Coupon code applied', 'wp-user-frontend' ),

Then use it in JS:

                     orderSummary.append(
                         '<div class="wpuf-coupon-applied">' +
-                            '<span class="wpuf-coupon-applied-label">Coupon code applied</span>' +
+                            '<span class="wpuf-coupon-applied-label">' + wpuf_frontend.coupon_applied_text + '</span>' +
                             '<button type="button" class="wpuf-coupon-remove" data-pack_id="' + self.data('pack_id') + '">&times;</button>' +
                         '</div>'
                     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
orderSummary.append(
'<div class="wpuf-coupon-applied">' +
'<span class="wpuf-coupon-applied-label">Coupon code applied</span>' +
'<button type="button" class="wpuf-coupon-remove" data-pack_id="' + self.data('pack_id') + '">&times;</button>' +
'</div>'
);
orderSummary.append(
'<div class="wpuf-coupon-applied">' +
'<span class="wpuf-coupon-applied-label">' + wpuf_frontend.coupon_applied_text + '</span>' +
'<button type="button" class="wpuf-coupon-remove" data-pack_id="' + self.data('pack_id') + '">&times;</button>' +
'</div>'
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/subscriptions-old.js` around lines 251 - 256, Replace the hardcoded
"Coupon code applied" string in the orderSummary.append call with a localized
value provided by the wpuf_frontend object; ensure PHP registers that text via
wp_localize_script (e.g., 'coupon_applied_text' => __( 'Coupon code applied',
'wp-user-frontend' )), then in the JS change the HTML constructed in
orderSummary.append (the block that includes '<span
class="wpuf-coupon-applied-label">...') to use wpuf_frontend.coupon_applied_text
while keeping the surrounding markup and the existing data-pack_id usage on the
.wpuf-coupon-remove button.

Comment on lines +862 to +867
// Order summary card
.wpuf-order-summary {
border: 1px solid #e5e7eb;
border-radius: 12px;
padding: 24px;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing background property in .wpuf-order-summary.

The compiled CSS at line 899 includes background: #f9fafb; for .wpuf-order-summary, but this property is missing in the LESS source. Either the CSS was manually edited (which would be overwritten on next compilation) or there's a sync issue. Add the background to the LESS source.

Proposed fix
 // Order summary card
 .wpuf-order-summary {
+    background: `#f9fafb`;
     border: 1px solid `#e5e7eb`;
     border-radius: 12px;
     padding: 24px;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Order summary card
.wpuf-order-summary {
border: 1px solid #e5e7eb;
border-radius: 12px;
padding: 24px;
}
// Order summary card
.wpuf-order-summary {
background: `#f9fafb`;
border: 1px solid `#e5e7eb`;
border-radius: 12px;
padding: 24px;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/less/frontend-forms.less` around lines 862 - 867, The
.wpuf-order-summary rule in the LESS source is missing the background property
present in compiled CSS; update the .wpuf-order-summary selector in
assets/less/frontend-forms.less by adding background: `#f9fafb`; to the rule
(alongside border, border-radius, padding) and recompile the LESS so the
generated CSS remains in sync with the source.

Comment on lines +168 to +169
$details_meta = wpuf()->subscription->get_details_meta_value();
$currency = wpuf_get_currency( 'symbol' );
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused variables $details_meta and $currency.

These variables are assigned but never used, as flagged by static analysis. They should be removed to improve code clarity.

Proposed fix
                     $pack         = wpuf()->subscription->get_subscription( $pack_id );
-                    $details_meta = wpuf()->subscription->get_details_meta_value();
-                    $currency     = wpuf_get_currency( 'symbol' );

                     $pack_cost      = $pack->meta_value['billing_amount'];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$details_meta = wpuf()->subscription->get_details_meta_value();
$currency = wpuf_get_currency( 'symbol' );
🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 168-168: Avoid unused local variables such as '$details_meta'. (undefined)

(UnusedLocalVariable)


[warning] 169-169: Avoid unused local variables such as '$currency'. (undefined)

(UnusedLocalVariable)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@includes/Frontend/Payment.php` around lines 168 - 169, Remove the two unused
variable assignments $details_meta and $currency in the Payment class code:
eliminate the call to wpuf()->subscription->get_details_meta_value() assigned to
$details_meta and the wpuf_get_currency('symbol') call assigned to $currency
since neither variable is used afterward; leaving no side-effectful calls and
ensuring no references remain to $details_meta or $currency elsewhere in the
method (search for $details_meta and $currency to confirm), then run static
analysis to verify the unused-variable warning is resolved.

Comment thread includes/Frontend/Payment.php
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
includes/Frontend/Payment.php (1)

136-140: ⚠️ Potential issue | 🟠 Major

Default to the first enabled gateway instead of hard-coding PayPal.

If PayPal is disabled, the first render leaves every radio unchecked, and checkout can submit with an empty wpuf_payment_method.

Proposed fix
-            if ( isset( $_REQUEST['wpuf_payment_submit'] ) ) {
-                $selected_gateway = isset( $_REQUEST['wpuf_payment_method'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['wpuf_payment_method'] ) ) : '';
-            } else {
-                $selected_gateway = 'paypal';
-            }
+            $selected_gateway = isset( $_REQUEST['wpuf_payment_method'] )
+                ? sanitize_text_field( wp_unslash( $_REQUEST['wpuf_payment_method'] ) )
+                : '';
+
+            if ( empty( $selected_gateway ) || ! isset( $gateways[ $selected_gateway ] ) ) {
+                $selected_gateway = array_key_first( $gateways );
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@includes/Frontend/Payment.php` around lines 136 - 140, The code currently
hard-codes 'paypal' when no wpuf_payment_method is submitted which leaves radios
unchecked if PayPal is disabled; change the fallback logic for $selected_gateway
to instead detect the first enabled gateway and use its ID. In the else branch
(near the wpuf_payment_submit check) call the existing payment gateway
collection (the function or property that lists gateways in this class, e.g.,
the Payment class method that returns available/enabled gateways) and pick the
first enabled gateway ID to assign to $selected_gateway (with a safe fallback to
an empty string if none found); ensure you use sanitize_text_field/wp_unslash
only for user input (the retrieved gateway ID can be validated against the
gateway list).
♻️ Duplicate comments (1)
assets/less/frontend-forms.less (1)

857-861: ⚠️ Potential issue | 🟡 Minor

Give the order summary card an explicit background.

Right now this card inherits the page background, so it loses contrast on themed checkout pages.

Proposed fix
 .wpuf-order-summary {
+    background: `#f9fafb`;
     border: 1px solid `#e5e7eb`;
     border-radius: 12px;
     padding: 24px;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/less/frontend-forms.less` around lines 857 - 861, The
.wpuf-order-summary block lacks an explicit background so it can inherit page
backgrounds; update the .wpuf-order-summary rule in
assets/less/frontend-forms.less to add an explicit background-color (e.g.
background-color: `#ffffff`; or use your theme variable like `@card-background` or
--card-bg) so the card keeps contrast on themed checkout pages, keeping the
existing border, border-radius and padding intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@includes/Frontend/Payment.php`:
- Around line 136-140: The code currently hard-codes 'paypal' when no
wpuf_payment_method is submitted which leaves radios unchecked if PayPal is
disabled; change the fallback logic for $selected_gateway to instead detect the
first enabled gateway and use its ID. In the else branch (near the
wpuf_payment_submit check) call the existing payment gateway collection (the
function or property that lists gateways in this class, e.g., the Payment class
method that returns available/enabled gateways) and pick the first enabled
gateway ID to assign to $selected_gateway (with a safe fallback to an empty
string if none found); ensure you use sanitize_text_field/wp_unslash only for
user input (the retrieved gateway ID can be validated against the gateway list).

---

Duplicate comments:
In `@assets/less/frontend-forms.less`:
- Around line 857-861: The .wpuf-order-summary block lacks an explicit
background so it can inherit page backgrounds; update the .wpuf-order-summary
rule in assets/less/frontend-forms.less to add an explicit background-color
(e.g. background-color: `#ffffff`; or use your theme variable like
`@card-background` or --card-bg) so the card keeps contrast on themed checkout
pages, keeping the existing border, border-radius and padding intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f96299e8-0184-47e8-a570-5c4db3c5892c

📥 Commits

Reviewing files that changed from the base of the PR and between 8090dc4 and f73afce.

⛔ Files ignored due to path filters (2)
  • assets/images/bank.svg is excluded by !**/*.svg
  • assets/images/credit-card.svg is excluded by !**/*.svg
📒 Files selected for processing (9)
  • Lib/Gateway/Bank_Gateway.php
  • Lib/Gateway/Paypal.php
  • assets/css/frontend-forms.css
  • assets/js/billing-address.js
  • assets/js/subscriptions-old.js
  • assets/less/frontend-forms.less
  • includes/Admin/Subscription.php
  • includes/Free/Free_Loader.php
  • includes/Frontend/Payment.php
✅ Files skipped from review due to trivial changes (2)
  • Lib/Gateway/Bank_Gateway.php
  • Lib/Gateway/Paypal.php
🚧 Files skipped from review as they are similar to previous changes (2)
  • assets/js/subscriptions-old.js
  • assets/css/frontend-forms.css

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@includes/Frontend/Payment.php`:
- Around line 175-223: The code can reach render_order_summary without
$billing_data set when neither $pack_id nor $post_id is truthy; add a guard that
either short-circuits (e.g., return/exit or show an error) before rendering the
form when both $pack_id and $post_id are empty, or initialize a safe default
$billing_data (with keys 'type','id','title','subtotal','billing_amount') before
the if/elseif so subsequent calls like
$this->render_order_summary($billing_data) and helper accesses
($billing_data['title'], ['type'], ['subtotal'], ['billing_amount']) never
trigger warnings; locate the logic around $pack_id/$post_id, the $billing_data
assignment blocks, and the render_order_summary call to implement the guard or
default.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0008cf3e-1530-4be9-9632-c4b897665253

📥 Commits

Reviewing files that changed from the base of the PR and between f73afce and b839aca.

📒 Files selected for processing (3)
  • includes/Admin/Subscription.php
  • includes/Free/Free_Loader.php
  • includes/Frontend/Payment.php
✅ Files skipped from review due to trivial changes (1)
  • includes/Admin/Subscription.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • includes/Free/Free_Loader.php

Comment on lines +175 to +223
if ( $pack_id ) {
$pack = wpuf()->subscription->get_subscription( $pack_id );
$details_meta = wpuf()->subscription->get_details_meta_value();
$currency = wpuf_get_currency( 'symbol' );

$pack_cost = $pack->meta_value['billing_amount'];
$billing_amount = apply_filters( 'wpuf_payment_amount', $pack->meta_value['billing_amount'] );

$order_title = $pack->post_title;

$billing_data = apply_filters( 'wpuf_billing_data', [
'type' => 'pack',
'id' => $pack_id,
'title' => $order_title,
'subtotal' => $pack_cost,
'billing_amount' => $billing_amount,
'recurring_pay' => isset( $pack->meta_value['recurring_pay'] ) ? $pack->meta_value['recurring_pay'] : 'no',
] );
} elseif ( $post_id ) {
$form = new Admin\Forms\Form(
get_post_meta( $post_id, '_wpuf_form_id', true )
);
$force_pack = $form->is_enabled_force_pack();
$pay_per_post = $form->is_enabled_pay_per_post();
$fallback_enabled = $form->is_enabled_fallback_cost();
$fallback_cost = (float) $form->get_subs_fallback_cost();
$pay_per_post_cost = (float) $form->get_pay_per_post_cost();
$current_user = wpuf_get_user();
$current_pack = $current_user->subscription()->current_pack();

if ( $force_pack && ! is_wp_error( $current_pack ) && $fallback_enabled ) {
$post_cost = $fallback_cost;
$billing_amount = apply_filters( 'wpuf_payment_amount', $fallback_cost, $post_id );
} else {
$post_cost = $pay_per_post_cost;
$billing_amount = apply_filters( 'wpuf_payment_amount', $pay_per_post_cost, $post_id );
}

$post_obj = get_post( $post_id );
$order_title = $post_obj ? $post_obj->post_title : __( 'Post Submission', 'wp-user-frontend' );

$billing_data = apply_filters( 'wpuf_billing_data', [
'type' => 'post',
'id' => $post_id,
'title' => $order_title,
'subtotal' => $post_cost,
'billing_amount' => $billing_amount,
] );
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

$billing_data may be undefined when neither pack_id nor post_id is set.

If action=wpuf_pay is reached without a pack_id or post_id (both default to 0 at lines 111-112 and the >0 guard at lines 116-118 only catches the both-set case), neither the if ( $pack_id ) nor elseif ( $post_id ) branch executes, so $billing_data is never defined. It is then dereferenced at line 298 ($this->render_order_summary( $billing_data )) and inside the helpers ($billing_data['title'], ['type'], ['subtotal'], ['billing_amount']), producing PHP warnings and a broken summary render.

Either short-circuit before opening the form when both ids are 0, or initialize a safe default. For example:

🛡️ Suggested guard
             if ( $post_id > 0 && $pack_id > 0 ) {
                 return $content;
             }
+            if ( $post_id <= 0 && $pack_id <= 0 ) {
+                return $content;
+            }
🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 177-177: Avoid unused local variables such as '$details_meta'. (undefined)

(UnusedLocalVariable)


[warning] 178-178: Avoid unused local variables such as '$currency'. (undefined)

(UnusedLocalVariable)


[warning] 198-198: Avoid unused local variables such as '$pay_per_post'. (undefined)

(UnusedLocalVariable)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@includes/Frontend/Payment.php` around lines 175 - 223, The code can reach
render_order_summary without $billing_data set when neither $pack_id nor
$post_id is truthy; add a guard that either short-circuits (e.g., return/exit or
show an error) before rendering the form when both $pack_id and $post_id are
empty, or initialize a safe default $billing_data (with keys
'type','id','title','subtotal','billing_amount') before the if/elseif so
subsequent calls like $this->render_order_summary($billing_data) and helper
accesses ($billing_data['title'], ['type'], ['subtotal'], ['billing_amount'])
never trigger warnings; locate the logic around $pack_id/$post_id, the
$billing_data assignment blocks, and the render_order_summary call to implement
the guard or default.

@sapayth sapayth added needs: testing and removed bug labels May 4, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
assets/js/subscriptions-old.js (1)

115-176: ⚡ Quick win

couponRemove duplicates couponCancel — extract a shared helper.

Both functions POST to wpuf_coupon_cancel and perform identical DOM updates on four targets inside .wpuf-order-summary (.wpuf-pack-inner, .wpuf-coupon-id-field, .wpuf-coupon-applied, .wpuf-coupon-show). The only difference is that couponCancel animates a spinner on the coupon input field while couponRemove has no loading feedback at all — a secondary UX inconsistency that would be resolved by the refactor.

♻️ Proposed refactor — shared `_doCouponCancel` helper
+       _doCouponCancel: function( packId, orderSummary, onDone ) {
+           var data = {
+               action:    'wpuf_coupon_cancel',
+               _wpnonce:  wpuf_frontend.nonce,
+               pack_id:   packId,
+               type:      $( '#wpuf_type' ).text() || 'pack'
+           };
+           $.post( wpuf_frontend.ajaxurl, data, function( res ) {
+               if ( res.success ) {
+                   orderSummary.find( '.wpuf-pack-inner' ).html( res.data.append_data );
+                   orderSummary.find( '.wpuf-coupon-id-field' ).val( '' );
+                   orderSummary.find( '.wpuf-coupon-applied' ).remove();
+                   orderSummary.find( '.wpuf-coupon-show' ).show();
+               }
+               if ( typeof onDone === 'function' ) { onDone( res ); }
+           } );
+       },

         couponCancel: function( e ) {
             e.preventDefault();
             var self         = $( this ),
                 orderSummary = self.closest( '.wpuf-order-summary' ),
                 couponField  = orderSummary.find( 'input.wpuf-coupon-field' );

             couponField.addClass( 'wpuf-coupon-field-spinner' );
-            var data = {
-                action:   'wpuf_coupon_cancel',
-                _wpnonce: wpuf_frontend.nonce,
-                pack_id:  self.data( 'pack_id' ),
-                type:     $( '#wpuf_type' ).text() || 'pack'
-            };
-            $.post( wpuf_frontend.ajaxurl, data, function( res ) {
-                couponField.removeClass( 'wpuf-coupon-field-spinner' );
-                if ( res.success ) {
-                    orderSummary.find( '.wpuf-pack-inner' ).html( res.data.append_data );
-                    orderSummary.find( '.wpuf-coupon-id-field' ).val( '' );
-                    orderSummary.find( '.wpuf-coupon-applied' ).remove();
-                    orderSummary.find( '.wpuf-coupon-show' ).show();
-                }
-            } );
+            subscription._doCouponCancel( self.data( 'pack_id' ), orderSummary, function() {
+                couponField.removeClass( 'wpuf-coupon-field-spinner' );
+            } );
         },

         couponRemove: function( e ) {
             e.preventDefault();
             var self         = $( this ),
                 orderSummary = self.closest( '.wpuf-order-summary' );
-            var data = {
-                action:   'wpuf_coupon_cancel',
-                _wpnonce: wpuf_frontend.nonce,
-                pack_id:  self.data( 'pack_id' ),
-                type:     $( '#wpuf_type' ).text() || 'pack'
-            };
-            $.post( wpuf_frontend.ajaxurl, data, function( res ) {
-                if ( res.success ) {
-                    orderSummary.find( '.wpuf-pack-inner' ).html( res.data.append_data );
-                    orderSummary.find( '.wpuf-coupon-id-field' ).val( '' );
-                    orderSummary.find( '.wpuf-coupon-applied' ).remove();
-                    orderSummary.find( '.wpuf-coupon-show' ).show();
-                }
-            } );
+            subscription._doCouponCancel( self.data( 'pack_id' ), orderSummary );
         },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/subscriptions-old.js` around lines 115 - 176, Both couponCancel and
couponRemove duplicate the same AJAX call and DOM updates; extract a shared
helper (e.g., _doCouponCancel) that accepts the prepared data and the
orderSummary element (or pack_id) and performs the $.post to
'wpuf_coupon_cancel' and the four DOM updates (.wpuf-pack-inner,
.wpuf-coupon-id-field, .wpuf-coupon-applied, .wpuf-coupon-show) on success; then
refactor couponCancel to add/remove the spinner on the coupon input field
before/after calling _doCouponCancel, and refactor couponRemove to call
_doCouponCancel directly so both behaviors reuse the same update logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@assets/css/frontend-forms.css`:
- Around line 854-913: Restore the selected-card visual state by re-adding a
rule that targets the card when its radio is checked (e.g., use the selector
body li:has(input[type="radio"]:checked) .wpuf-gateway-card) and set the same
highlight styles as hover (border-color and optional box-shadow), ensuring you
use the lowercase currentcolor token (not currentColor) if used for color values
to satisfy the linter; if :has() support is a concern, add guidance to toggle an
.is-selected class from JS and target .is-selected .wpuf-gateway-card instead.

In `@assets/js/subscriptions-old.js`:
- Around line 353-356: Replace the hardcoded "Processing…" string in the HTML
snippet that sets the button content with a value from the localized script data
(the key you added via wp_localize_script, e.g. 'processing_text') instead of
the literal English text; update the JS in assets/js/subscriptions-old.js to use
that localized variable (e.g. a property on the global/localized object created
by wp_localize_script) when building the '<span class="wpuf-btn-text">…</span>'
so the label is translatable.
- Around line 376-390: The validation branches in checkoutSubmit do not call
e.preventDefault(), so the form may still submit despite validation errors;
modify the branches that check WP_User_Frontend.validateForm(form) === false and
!wpuf_validate_address() to call e.preventDefault() before calling resetBtn()
and returning, and ensure the handler uses the event parameter (e) passed into
checkoutSubmit; reference WP_User_Frontend.validateForm, wpuf_validate_address,
checkoutSubmit, and resetBtn when making the changes.

In `@Lib/Gateway/Paypal.php`:
- Around line 1595-1597: Guard against division-by-zero when computing
$tax_percentage: if $subtotal is zero (which can happen when
breakdown['item_total'] === 0) skip the division and set $tax_percentage = 0 (or
compute by another safe fallback) instead of performing ($tax_amount /
$subtotal) * 100; update the code around the $tax_amount, $subtotal and
$tax_percentage calculation in Paypal.php to check for $subtotal == 0 (or empty
numeric) and handle that branch before calling number_format()/building the
PayPal payload so you never pass INF/NAN strings to PayPal.

---

Nitpick comments:
In `@assets/js/subscriptions-old.js`:
- Around line 115-176: Both couponCancel and couponRemove duplicate the same
AJAX call and DOM updates; extract a shared helper (e.g., _doCouponCancel) that
accepts the prepared data and the orderSummary element (or pack_id) and performs
the $.post to 'wpuf_coupon_cancel' and the four DOM updates (.wpuf-pack-inner,
.wpuf-coupon-id-field, .wpuf-coupon-applied, .wpuf-coupon-show) on success; then
refactor couponCancel to add/remove the spinner on the coupon input field
before/after calling _doCouponCancel, and refactor couponRemove to call
_doCouponCancel directly so both behaviors reuse the same update logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6aa5f9ed-30b8-43fc-82b0-dfe0a100f026

📥 Commits

Reviewing files that changed from the base of the PR and between b839aca and 76feee2.

⛔ Files ignored due to path filters (6)
  • assets/js/account.min.js is excluded by !**/*.min.js
  • assets/js/ai-form-builder.min.js is excluded by !**/*.min.js
  • assets/js/forms-list.min.js is excluded by !**/*.min.js
  • assets/js/forms-list.min.js.map is excluded by !**/*.map, !**/*.min.js.map
  • assets/js/frontend-subscriptions.min.js is excluded by !**/*.min.js
  • assets/js/subscriptions.min.js is excluded by !**/*.min.js
📒 Files selected for processing (12)
  • Lib/Gateway/Paypal.php
  • assets/css/admin/form-builder.css
  • assets/css/admin/subscriptions.min.css
  • assets/css/ai-form-builder.css
  • assets/css/ai-form-builder.min.css
  • assets/css/forms-list.min.css
  • assets/css/frontend-forms.css
  • assets/css/frontend-subscriptions.min.css
  • assets/css/frontend/account.css
  • assets/js/subscriptions-old.js
  • assets/less/frontend-forms.less
  • languages/wp-user-frontend.pot
✅ Files skipped from review due to trivial changes (1)
  • languages/wp-user-frontend.pot
🚧 Files skipped from review as they are similar to previous changes (1)
  • assets/less/frontend-forms.less

Comment on lines +854 to +913
body .wpuf-gateway-card {
display: block;
border: 2px solid #e5e7eb;
border-radius: 12px;
padding: 16px 20px;
cursor: pointer;
display: inline-block;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
background: #fff;
}
body .wpuf-gateway-card:hover {
border-color: #c7d2fe;
}
body .wpuf-gateway-card .wpuf-gateway-card-main {
display: flex;
align-items: center;
gap: 14px;
}
body .wpuf-gateway-card .wpuf-gateway-icon {
flex-shrink: 0;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
body .wpuf-gateway-card .wpuf-gateway-icon img {
max-width: 36px;
max-height: 36px;
object-fit: contain;
}
body .wpuf-gateway-card .wpuf-gateway-info {
flex: 1;
min-width: 0;
}
body .wpuf-gateway-card .wpuf-gateway-label {
display: block;
font-size: 15px;
font-weight: 600;
color: #1a1a2e;
line-height: 1.3;
}
body .wpuf-gateway-card .wpuf-gateway-desc {
display: block;
font-size: 13px;
color: #6b7280;
line-height: 1.4;
margin-top: 2px;
}
body .wpuf-gateway-card input[type="radio"] {
flex-shrink: 0;
width: 20px;
height: 20px;
margin: 0;
cursor: pointer;
}
body .wpuf-gateway-card .wpuf-payment-instruction {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #f3f4f6;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Gateway card has no visual selected state — add a :checked highlight rule.

.wpuf-gateway-card only defines a hover border (#c7d2fe) but never changes appearance when the radio inside it is selected. A user who picks a gateway gets zero card-level feedback beyond the tiny native radio dot.

The earlier iteration of this PR included a body li:has(input[type="radio"]:checked) .wpuf-gateway-card rule for this purpose. The rule is entirely absent here, likely removed to sidestep the currentColor linting error. It should be restored using currentcolor (lowercase) which satisfies the linter.

✅ Proposed fix — restore the selected-state rule with correct casing
 body .wpuf-gateway-card:hover {
   border-color: `#c7d2fe`;
 }
+body li:has(input[type="radio"]:checked) .wpuf-gateway-card {
+  border-color: currentcolor;
+  box-shadow: 0 0 0 1px currentcolor;
+}

Note on browser support: :has() is unsupported in Firefox < 120 and Safari < 15.4. If support for those browser versions is required, consider toggling an .is-selected class via JS on the li when the radio changes, and targeting that class as a fallback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/css/frontend-forms.css` around lines 854 - 913, Restore the
selected-card visual state by re-adding a rule that targets the card when its
radio is checked (e.g., use the selector body
li:has(input[type="radio"]:checked) .wpuf-gateway-card) and set the same
highlight styles as hover (border-color and optional box-shadow), ensuring you
use the lowercase currentcolor token (not currentColor) if used for color values
to satisfy the linter; if :has() support is a concern, add guidance to toggle an
.is-selected class from JS and target .is-selected .wpuf-gateway-card instead.

Comment on lines +353 to +356
.html(
'<span class="wpuf-btn-spinner"></span>' +
'<span class="wpuf-btn-text">Processing&hellip;</span>'
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Hardcoded "Processing…" text needs to be localized.

The loading label is a hardcoded English string injected into the DOM. Like "Coupon code applied" (flagged separately), it should be provided via wp_localize_script.

🌐 Proposed fix

In PHP (wp_localize_script call):

'processing_text' => __( 'Processing...', 'wp-user-frontend' ),

In JS:

-              '<span class="wpuf-btn-text">Processing&hellip;</span>'
+              '<span class="wpuf-btn-text">' + ( wpuf_frontend.processing_text || 'Processing\u2026' ) + '</span>'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.html(
'<span class="wpuf-btn-spinner"></span>' +
'<span class="wpuf-btn-text">Processing&hellip;</span>'
);
.html(
'<span class="wpuf-btn-spinner"></span>' +
'<span class="wpuf-btn-text">' + ( wpuf_frontend.processing_text || 'Processing\u2026' ) + '</span>'
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/subscriptions-old.js` around lines 353 - 356, Replace the hardcoded
"Processing…" string in the HTML snippet that sets the button content with a
value from the localized script data (the key you added via wp_localize_script,
e.g. 'processing_text') instead of the literal English text; update the JS in
assets/js/subscriptions-old.js to use that localized variable (e.g. a property
on the global/localized object created by wp_localize_script) when building the
'<span class="wpuf-btn-text">…</span>' so the label is translatable.

Comment on lines +376 to +390
// validateForm() returns false on error, serialized string on success.
if ( typeof WP_User_Frontend !== 'undefined' ) {
if ( WP_User_Frontend.validateForm( form ) === false ) {
resetBtn();
return;
}
}

// Billing address validation when the address form is present.
if ( $( '#wpuf-ajax-address-form' ).length && typeof wpuf_validate_address === 'function' ) {
if ( ! wpuf_validate_address() ) {
resetBtn();
return;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check full billing-address.js submit handler for e.preventDefault() position
fd -i 'billing-address.js' --exec grep -n 'preventDefault\|validateForm\|submit' {}

Repository: weDevsOfficial/wp-user-frontend

Length of output: 385


🏁 Script executed:

#!/bin/bash
# Find subscriptions-old.js and examine context around lines 370-395
fd -i 'subscriptions-old.js' -type f | head -1 | xargs -I {} sh -c 'echo "File: {}" && sed -n "360,400p" {} | cat -n | sed "s/^/360: /"'

Repository: weDevsOfficial/wp-user-frontend

Length of output: 246


🏁 Script executed:

#!/bin/bash
# Also search for the checkoutSubmit function definition and how it's attached
fd -i 'subscriptions-old.js' -type f | head -1 | xargs -I {} sh -c 'grep -n "checkoutSubmit\|\.submit(" {} | head -20'

Repository: weDevsOfficial/wp-user-frontend

Length of output: 246


🏁 Script executed:

#!/bin/bash
# Find subscriptions-old.js and examine context around lines 370-395
fd -i 'subscriptions-old.js' -t f | head -1 | xargs -I {} sh -c 'sed -n "360,400p" {} | cat -n'

Repository: weDevsOfficial/wp-user-frontend

Length of output: 1878


🏁 Script executed:

#!/bin/bash
# Search for the checkoutSubmit function definition and attachment
fd -i 'subscriptions-old.js' -t f | head -1 | xargs -I {} sh -c 'grep -n "checkoutSubmit\|\.submit(" {}'

Repository: weDevsOfficial/wp-user-frontend

Length of output: 356


🏁 Script executed:

#!/bin/bash
# Find subscriptions-old.js and get the full checkoutSubmit function
fd -i 'subscriptions-old.js' -t f | head -1 | xargs -I {} sh -c 'sed -n "342,400p" {} | cat -n'

Repository: weDevsOfficial/wp-user-frontend

Length of output: 3002


🏁 Script executed:

#!/bin/bash
# Also check if there are other submit handlers on the same form in subscriptions-old.js
fd -i 'subscriptions-old.js' -t f | head -1 | xargs -I {} sh -c 'grep -n "\.submit\|\.on.*submit" {}'

Repository: weDevsOfficial/wp-user-frontend

Length of output: 312


🏁 Script executed:

#!/bin/bash
# Search for other handlers on wpuf-payment-gateway form and Stripe-related code
fd -i 'subscriptions-old.js' -t f | head -1 | xargs -I {} sh -c 'rg -n "wpuf-payment-gateway|stripe" {} | head -30'

Repository: weDevsOfficial/wp-user-frontend

Length of output: 278


🏁 Script executed:

#!/bin/bash
# Check if there are other submit handlers or Stripe handlers in the entire assets/js directory
rg -l "wpuf-payment-gateway|stripe" assets/js/ | head -10

Repository: weDevsOfficial/wp-user-frontend

Length of output: 301


🏁 Script executed:

#!/bin/bash
# Search for all submission paths to the payment gateway form in all JS files
rg -n "\.submit\(\)|\.trigger.*submit" assets/js/ -t js

Repository: weDevsOfficial/wp-user-frontend

Length of output: 50388


🏁 Script executed:

#!/bin/bash
# Check if there's a Stripe Pro handler or any other handler that might prevent submission
rg -l "stripe|Stripe" assets/js/ --type js | xargs -I {} sh -c 'echo "=== {} ===" && rg -n "preventDefault|submit" {}'

Repository: weDevsOfficial/wp-user-frontend

Length of output: 40314


Add e.preventDefault() to validation failure branches in checkoutSubmit to prevent form submission after errors.

The function receives the event object but omits e.preventDefault() in both validation failure branches, allowing the form to POST despite displaying validation errors. In jQuery, return alone does not prevent the default form submission behavior.

🐛 Proposed fix
         if ( typeof WP_User_Frontend !== 'undefined' ) {
             if ( WP_User_Frontend.validateForm( form ) === false ) {
+                e.preventDefault();
                 resetBtn();
                 return;
             }
         }

         if ( $( '#wpuf-ajax-address-form' ).length && typeof wpuf_validate_address === 'function' ) {
             if ( ! wpuf_validate_address() ) {
+                e.preventDefault();
                 resetBtn();
                 return;
             }
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// validateForm() returns false on error, serialized string on success.
if ( typeof WP_User_Frontend !== 'undefined' ) {
if ( WP_User_Frontend.validateForm( form ) === false ) {
resetBtn();
return;
}
}
// Billing address validation when the address form is present.
if ( $( '#wpuf-ajax-address-form' ).length && typeof wpuf_validate_address === 'function' ) {
if ( ! wpuf_validate_address() ) {
resetBtn();
return;
}
}
// validateForm() returns false on error, serialized string on success.
if ( typeof WP_User_Frontend !== 'undefined' ) {
if ( WP_User_Frontend.validateForm( form ) === false ) {
e.preventDefault();
resetBtn();
return;
}
}
// Billing address validation when the address form is present.
if ( $( '#wpuf-ajax-address-form' ).length && typeof wpuf_validate_address === 'function' ) {
if ( ! wpuf_validate_address() ) {
e.preventDefault();
resetBtn();
return;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/subscriptions-old.js` around lines 376 - 390, The validation
branches in checkoutSubmit do not call e.preventDefault(), so the form may still
submit despite validation errors; modify the branches that check
WP_User_Frontend.validateForm(form) === false and !wpuf_validate_address() to
call e.preventDefault() before calling resetBtn() and returning, and ensure the
handler uses the event parameter (e) passed into checkoutSubmit; reference
WP_User_Frontend.validateForm, wpuf_validate_address, checkoutSubmit, and
resetBtn when making the changes.

Comment thread Lib/Gateway/Paypal.php
Comment on lines +1595 to 1597

// Calculate tax percentage
$tax_percentage = ( $tax_amount / $subtotal ) * 100;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Division-by-zero when item_total is explicitly 0 — subscription creation silently fails.

?? (null-coalescing) only falls back to the right operand when the left is null or undefined. An extension that sets breakdown['item_total'] = 0 (e.g., fully-discounted item with a non-zero tax) will leave $subtotal = 0, making line 1597 a float-division-by-zero. PHP returns INF/NAN, number_format() turns that into a garbage string, and PayPal rejects the subscription payload.

🐛 Proposed fix
-                $subtotal = $payment_data['breakdown']['item_total'] ?? $payment_data['amount'];
-
-                // Calculate tax percentage
-                $tax_percentage = ( $tax_amount / $subtotal ) * 100;
-
-                // Add plan override with tax
-                $subscription_data['plan'] = [
-                    'taxes' => [
-                        'percentage' => number_format( $tax_percentage, 2, '.', '' ),
-                        'inclusive' => false,
-                    ],
-                ];
+                $subtotal = ! empty( $payment_data['breakdown']['item_total'] )
+                    ? (float) $payment_data['breakdown']['item_total']
+                    : (float) $payment_data['amount'];
+
+                // Calculate tax percentage only when subtotal is non-zero.
+                if ( $subtotal > 0 ) {
+                    $tax_percentage = ( $tax_amount / $subtotal ) * 100;
+
+                    // Add plan override with tax
+                    $subscription_data['plan'] = [
+                        'taxes' => [
+                            'percentage' => number_format( $tax_percentage, 2, '.', '' ),
+                            'inclusive' => false,
+                        ],
+                    ];
+                }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Lib/Gateway/Paypal.php` around lines 1595 - 1597, Guard against
division-by-zero when computing $tax_percentage: if $subtotal is zero (which can
happen when breakdown['item_total'] === 0) skip the division and set
$tax_percentage = 0 (or compute by another safe fallback) instead of performing
($tax_amount / $subtotal) * 100; update the code around the $tax_amount,
$subtotal and $tax_percentage calculation in Paypal.php to check for $subtotal
== 0 (or empty numeric) and handle that branch before calling
number_format()/building the PayPal payload so you never pass INF/NAN strings to
PayPal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants