Repository navigation
Fix - Front-end form inputs show a double border on focus (themegrill/user-registration-pro#1896) - #1548
Conversation
…hemegrill/user-registration-pro#1659) (#1488) * Fix undefined array key warning on subscription cancel with no order (#1659) get_order_by_subscription() returns an empty array when a subscription has no matching order row, so accessing order_type on it threw an undefined array key warning without affecting the outcome (empty() already short-circuited the condition to true). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Guard undefined array key on upgrade-branch subscription cancel $order can be empty array() when the upgrade branch has no matching order row, which trips the same undefined-array-key warning on the switch as the case this PR already fixes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: asheshmagar <ashesh.ido@gmail.com>
* Fix - Guard Membership Groups status column against null post_content JSON. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix - Treat numeric and boolean Membership Group status values as enabled Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…1496) * Fix - Add missing utm_content to remaining free outbound upgrade links Co-authored-by: Cursor <cursoragent@cursor.com> * Fix - Premium template upgrade link now tracks the clicked template Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix - Address Ganesh review for UTM payment attribution and PHPCS. Payment Mollie/Authorize upsells now use payment-settings source/content; doing_it_wrong version reads UR_VERSION; expand multi-key ur_utm_url arrays. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Fix billing emails using member's most-recent order instead of the subscription's own order (#1700)
get_membership_plan_details() and send_cancel_emails() fell back to
MembersOrderRepository::get_member_orders(), which returns the member's
single most-recent order with no subscription filter. For a member with
more than one order, cancellation/expiring/ended/payment emails could
show another order's plan, amount, or transaction ID instead of the one
the email is actually about.
Both now resolve the order via OrdersRepository::get_order_by_subscription(),
scoped to the subscription the email concerns, matching the ownership-
checked lookup the thank-you page already used.
* Trust the caller-supplied order over a re-derived subscription lookup
Payment-success callers (Stripe, PayPal) already resolve and pass the
exact order the receipt is about via $data['order']. The subscription-
scoped fallback added for #1700 ignored it and re-queried for the
subscription's newest order instead, which can differ from the order
the caller supplied if a later order exists on the same subscription
by the time the email builds (e.g. a fast renewal or backfill).
* Validate the caller-supplied order against the resolved subscription
The legacy PayPal IPN handler and the Stripe payment-retry-failed path
build their own order via get_member_orders(), the same member-wide,
unscoped lookup this PR fixes, then pass it through $data['order']
even though both already know the specific subscription. Trusting
$data['order'] unconditionally let their own wrong order back in
ahead of the correct subscription-scoped lookup.
Now the supplied order is only trusted when its subscription_id
matches the resolved subscription; otherwise it falls through to the
subscription-scoped lookup, same as any caller that didn't supply an
order at all.
* Don't inherit an unrelated order when a resolved subscription has none
The member-wide fallback ran whenever no order had been found yet,
even if a subscription ID HAD been resolved but simply had no order of
its own (e.g. get_order_by_subscription() found nothing). That
re-introduced the original bug for that case - a subscription with no
order could inherit another subscription's order via
get_member_orders(). Now the member-wide fallback only runs when no
subscription could be resolved at all; otherwise the order is left
empty.
Also fixed membership_plan_payment_method reading directly from the
caller-supplied $data['order'] instead of the resolved $order - when a
mismatched supplied order gets rejected, this field was still leaking
its payment method through untouched. Hardened three direct
$order['trial_status'] accesses that started producing undefined-
array-key warnings now that $order can legitimately be empty more
often.
* #1700 Fix - admin payment email showing wrong order's payment date
get_membership_plan_details() resolved the correct subscription-scoped
order (per the earlier commits on this branch) but never exposed its
raw date as membership_tags['payment_date'] - the exact key the
{{payment_date}} smart tag already checks for before falling back to
the member's single most-recent order account-wide. Populate it from
the resolved order's created_at so the admin "Payment Received" email
shows the approved order's own date.
Closes themegrill/user-registration-pro#1793.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…rs (#1502) * Fix - Guard Addons newest/oldest sort when released_date is missing. parseDate called .split on undefined and crashed the React Addons page. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix - Combine Addons plan, status, and sort as AND filters. Sort was applied to the full addon list and wiped active status/plan filters. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix CSRF protection for bulk user status actions * test: run security regressions through the QA suite
* Mask payment gateway secret keys with show/hide toggle (#1653) * Add visible focus outline for toggle button and add regression test spec * Fix - Enter key does not toggle payment secret key visibility The #mainform keyup/keypress handler called preventDefault() on every Enter keypress to stop accidental form submits, which also blocked the browser's native Enter-to-click behaviour on the .ur-toggle-password button. Skip the guard when the event target is a button. Closes themegrill/user-registration-pro#1795 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* fix: make Delete User work from the user edit/view screen Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: trigger zip build for QA #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix: drop redundant class causing duplicate delete confirmation Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: trigger zip build for QA #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…1404) Rows are identified by a creation-order id that never renumbers, while user_registration_form_row_ids stores them in visual order. The first row of every form is hardcoded to id 0, so as soon as any later-created row is dragged above it, row 0 leaves index 0. At that point `! empty( $row_ids[ $index ] )` evaluates false for row 0 (both int 0 and the string "0" that the meta actually stores) and the row silently falls back to `$index`, stamping it with another row's id. The multi-step addon then wraps it in whichever part owns that id, and row level conditional logic matches the wrong row for the same reason. Guard on the array instead of the element so a legitimate row id of 0 is kept, matching the pattern already used in class-ur-users-menu.php.
The login_redirect() filter callback for the 'previous-page' redirect option always re-derived the target from wp_get_referer(). For the AJAX login flow, that request's own Referer is always the login page itself (the page that issued the AJAX call), so it silently clobbered the already-correct target that ur_process_login() had computed from the client-captured previous_page field, sending users back to the login page after every successful login instead of their previous page. Prefer previous_page when present, and never resolve to the login page itself -- fall back to My Account instead, matching the guard ur_process_login() already applies. The resolved referer is validated with wp_validate_redirect() before use, since previous_page is client-controlled and this filter also feeds callers (automatic login after registration/SMS OTP) that redirect without validating it themselves. The login-page check itself now compares post IDs via url_to_postid() instead of exact string matching, so query-string or trailing-slash variants of the login URL are still caught. Fixes #1411
…election query (#1465) wums.status = 'failed' can never match: the subscriptions table's status column is enum('active','canceled','expired','trial','pending'), and no code path ever writes 'failed' to it (only the orders table's status enum has 'failed'). Removing the dead branch is a behavioral no-op - only wums.status = 'expired' was ever selectable.
…vior (#1715) (#1491) The setting (urm_enable_no_conflict) is a script-deduplication mode that removes other plugins' captcha scripts site-wide - it never enforces or enables a captcha on any form. Renamed to "Prevent Captcha Conflicts" and rewrote the tooltip to match includes/class-ur-captcha- conflict-manager.php's actual behavior. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…stration page for logged-in members (#1440) * fix: respect shortcode registration page attribute for logged-in members Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: trigger zip build for QA #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix: recognize membership checkout intent by query params, not page identity Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: trigger zip build for QA #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: asheshmagar <ashesh.ido@gmail.com>
* Keep untrusted smart-tag values from executing shortcodes * test: run security regressions through the QA suite
…to logged out visitors (#1434) * #1508 Fix - Login and account pages served a cached logged in notice to logged out visitors * #1508 Fix - Cover registration pre-form message, membership and remaining UR shortcodes * #1508 Fix - Cover membership shortcodes and buy-now block, cut WPML page lookups * #1508 Fix - Only treat registered UR blocks as dynamic content
* fix: skip Stripe key-prefix validation on save when Stripe is being disabled Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: trigger zip build for QA #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: rebuild settings.min.js #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Revert "chore: rebuild settings.min.js #build-zip" Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
import_form() unserializes exported form_post_meta values but then calls sanitize_text_field() on the result unconditionally. WordPress core's sanitize_text_field() returns an empty string for anything that isn't a scalar, so any array/object meta (field lists, condition rules, etc.) was silently replaced with '' on import - no warning, no visible failure, which is why the same template imported cleanly in support's own test. The emptied meta then breaks later, whenever the builder expects an array for that key and gets a string instead. Recurse into arrays/objects and only sanitize their scalar leaves, matching how a plain scalar meta value was already sanitized. A decoded JSON object is normalized back to a plain array rather than left as stdClass, since form meta that was ever a PHP array (e.g. role_based_redirection) is read back elsewhere as an array. Companion fix for themegrill/user-registration-pro#1507, in the shared copy of the same code.
…1436) * fix: exclude pending-cancel subscriptions from renewal reminder query Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: trigger zip build for QA #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix: filter pending-cancel subscriptions in PHP, not a SQL usermeta join Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: trigger zip build for QA #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* fix: add lowest/highest tier anchor labels to upgrade path order list Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: trigger zip build for QA #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix: put highest tier at top and lowest tier at bottom of upgrade path list Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: trigger zip build for QA #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix: reverse upgrade path list for highest-at-top UI Render stored low→high paths as high→low, reverse drag order before path rebuild, and keep dragged DOM on sort so the list does not snap back. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix - Upgrade path anchor styles missing from compiled membership admin CSS Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: remove unreachable payment_approval email type Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * #build-zip --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…mail (#1443) * fix: use passed option to check membership email enabled state Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * #build-zip --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…ree (#1442) * feat: send member/admin notification when a membership downgrades to free Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: trigger zip build for QA #build-zip Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix: resolve previous plan tag and bold plan names in downgrade email Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * #build-zip * fix: gate downgraded-to-free notifications on verified paid-to-free change Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * #build-zip --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
daily_payment_retry_check() retried a failing subscription's payment every day indefinitely - there was no comparison anywhere between the per-user retry counter (urm_is_payment_retrying) and the admin's configured max attempts (user_registration_payment_retry_count), so a subscription could never actually be given up on. That's why the Payment Retry - Final Notice email (payment_retry_cancel) was dead code: nothing ever reached the point where it should be sent. failed_payment_retry_callback() now checks the retry count against the configured max before attempting another retry. Once exhausted, it cancels the subscription via the existing gateway-agnostic SubscriptionRepository::cancel_subscription_by_id() and sends the final-notice email - but only once cancellation actually succeeds, so a gateway-side cancellation failure doesn't tell the member their registration was cancelled when it wasn't. On failure, the retry counter is left as-is so the next daily run retries the cancellation itself. Companion fix for themegrill/user-registration-pro#1603, in the shared copy of the same code.
* Constrain public nonce refresh to supported actions and forms * test: run security regressions through the QA suite
…grill/user-registration-pro#1643) (#1531) * Enhancement - Simplify setup wizard to one membership question (themegrill/user-registration-pro#1643) * Keep the prototype wording for the No option * Record the raw wizard answer (yes, no, later) in the onboarding snapshot * Enhancement - Refine setup wizard welcome copy (themegrill/user-registration-pro#1643) Ask about membership plans rather than memberships, and give each answer a descriptive title so the options read on their own. * Enhancement - Reduce the welcome question to Yes / Not now (themegrill/user-registration-pro#1643) No and Maybe later ran the same flow, so they are merged into one "Not now" answer, and the option descriptions are dropped. The two answers sit side by side and stack on narrow screens. The unselected border is darkened to meet the 3:1 non-text contrast minimum, the focus ring shows only for keyboard focus, and the card wraps Chakra's radio label instead of nesting one label inside another. * Fix - Show a previously saved "Maybe later" as Not now in the setup wizard (themegrill/user-registration-pro#1643) The welcome step now offers only Yes and Not now, so a stored "later" answer from an earlier build matched no card. The saved membership type alone decides the selected card, and the unused "later" setup type is removed. * Enhancement - Drop the membership_interest answer now that the welcome step has two answers (themegrill/user-registration-pro#1643) Yes and Not now map one to one onto paid_membership and normal, so a separate stored answer, its REST argument, snapshot field and regression test add nothing. The wizard saves and reloads from membership_type alone, as it does on develop. * Fix - Meet 3:1 contrast on the welcome cards and keep legacy free_membership sites on the Payment step (themegrill/user-registration-pro#1643) #build-zip * Fix - Drive the welcome card selection from state so it shows without :has(), and add a setup-wizard regression spec (themegrill/user-registration-pro#1643) #build-zip
#1495) * Fix - Recover renewal/expiring reminder emails after a missed cron day Co-authored-by: Cursor <cursoragent@cursor.com> * Fix - Duplicate reminder emails for users with multiple subscriptions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… renewal, and keep trials intact (#1445) * fix: hold membership expiry while Stripe subscription is still past_due Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * #build-zip * fix: only hold expiry for past_due Stripe subscriptions, not unpaid Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * #build-zip * fix: sync local membership when Stripe renewal has no payment_intent Renewal invoices without a PI were skipped entirely, and expiry/retry ignored an still-active Stripe sub — leaving members expired while Stripe kept billing. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: keep trial status on zero-amount invoices and retrieve the Stripe subscription once in the expiry cron Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Fix - Read the Stripe subscription period end from the items on current API versions Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Fix - Keep the member and stop the replaced subscription when a Stripe renewal is declined or paid (#1866, #1867) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Fix - Free the renewal marker and fail the pending order when a Stripe renewal is declined (#1872) A declined renewal left urm_membership_process['renew'] set and its order pending, so every later renewal hit 'Membership renew process already initiated'. The failed-payment branch also unset the upgrade list instead of the renew list. - Clear the renew marker on both decline paths (client-side failure and create_subscription error) and fail the pending order. - Reuse a stale marker in renew_membership() instead of blocking the member. - Keep the one-period cancel_at for manual renewals only, so automatic renewals keep recurring now that $is_renewing is detected. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Fix - Restore spacing in the Stripe customer cleanup Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Fix - Discard the pending member when a guest's Stripe registration payment is declined (#1874) The member was only deleted for the logged-in member or an admin, so a logged-out registrant kept a user with no orders, and the follow-up confirm_payment answered 'Invalid Request.'. Allow the browser whose registration session created the pending member (the check confirm_payment already uses), and show the card error when the pending order is already gone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…ons without invoice.payment_intent (themegrill/user-registration-pro#1823) (#1515) * Fix - Resolve the PaymentIntent of a paid Stripe renewal on API versions without invoice.payment_intent (themegrill/user-registration-pro#1823) * Run the Stripe renewal PaymentIntent regression through the Playwright suite and keep its test doubles lint-clean (themegrill/user-registration-pro#1823) * Document the Invoice test double's __call parameters (themegrill/user-registration-pro#1823)
…hemegrill/user-registration-pro#1884) (#1546) * Fix - Keep the setup wizard answer picked before it finishes loading The Welcome step was interactive while the saved answers were still loading, so a choice made in that window was overwritten when they arrived and Next saved the old answer. The step now shows a spinner until the first load finishes, and step clicks wait for it too. Also restyles the Welcome step's Yes / Not now options to match the plan Type options on the Membership step. Closes themegrill/user-registration-pro#1884 * Fix - Add the regression spec for the setup wizard loading guard Holds the wizard's loading requests and checks the answer cannot be picked until the saved one has loaded, then that Not now saves normal. Fails on develop, where the options are clickable while loading. Refs themegrill/user-registration-pro#1884 --------- Co-authored-by: asheshmagar <ashesh.ido@gmail.com>
* Fix - Cancel membership subscription when a Stripe charge is fully refunded Co-authored-by: Cursor <cursoragent@cursor.com> * Fix - Create Stripe webhook when saving payment keys from setup wizard #build-zip Co-authored-by: Cursor <cursoragent@cursor.com> * Fix - Full Stripe refund not revoking access after a partial refund or on one-time orders Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix - Stripe refund handling no longer fails webhooks or wizard saves, and repeat backfills are a no-op Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: asheshmagar <ashesh.ido@gmail.com>
…megrill/user-registration-pro#1604) (#1539) * Fix content rules not restricting WooCommerce product purchase (themegrill/user-registration-pro#1604) Match product rules against the post object so Post Type targets work, and stop Whole Site targets (enabled or not) from switching product rules off. Covers themegrill/user-registration-pro#1861. * Restrict WooCommerce variations through their parent and map the content-restriction QA area (themegrill/user-registration-pro#1604) Variations are checked against the parent product's rules. The new regression is tagged content-restriction and the module is mapped to that area so the suite selects it. * Read content rules through the request-cached helper in the WooCommerce product check (themegrill/user-registration-pro#1604) * Check a variation's restriction meta on its parent product (themegrill/user-registration-pro#1604) The per-product meta box lives on the parent, so a variation skipped it. Resolve the variation to its parent at both entry points. * Only a matching Access rule grants WooCommerce product access (themegrill/user-registration-pro#1604) A Restrict rule the user does not match was counted as granting access and cancelled other rules that blocked the user. It is now skipped. A variation with no parent is left open instead of falling back to the current post. * Hide the price of a restricted WooCommerce product (themegrill/user-registration-pro#1870) (#1541) * Hide the price of a WooCommerce product the user cannot purchase (themegrill/user-registration-pro#1870) * Skip the restriction check when the price is already empty (themegrill/user-registration-pro#1870) * Test that the price filter is registered (themegrill/user-registration-pro#1870) * Silence the file_get_contents sniff in the price registration check (themegrill/user-registration-pro#1870) * Update the regression wrapper to the new assertion count (themegrill/user-registration-pro#1870) --------- Co-authored-by: asheshmagar <ashesh.ido@gmail.com> --------- Co-authored-by: asheshmagar <ashesh.ido@gmail.com>
…ents (#1882) (#1545) A renewal once saved the invoice list nested inside another list, so the Members view passed an empty amount to number_format() and fataled on PHP 8. Read invoices through ur_get_valid_payment_invoices() in the Members view and My Account, and cast the amount. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…1467, #1469) (#1468) * Fix - Upgrade form breaks when the selected plan's price has trailing zeros (#1467) * Fix - Payment History lists membership payments twice (#1469) * Fix - Prorated upgrade Total throws TypeError for .00-priced plans (#1467) convert_currency_and_calculate_tax() read membershipAmount straight off the data-urm-membership-amount attribute, so a plan stored as "299.00" stayed a string (jQuery's .data() only casts a value that round-trips exactly) and the later .toFixed(2) call threw, leaving Total at 0 with no gateway options for a mid-cycle prorated upgrade. Coerce it with parseFloat(...) || 0, matching the same pattern already used in calculate_total(). Documents the prorated-upgrade path as untestable in the current e2e suite (no seam to seed an active subscription without a real gateway checkout) in .themegrill-qa/knowledge.md, per review on #1468. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* Fix - Form payment View link uses user_id and type=form. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix - Show subscription expiry from existing stored dates when display fields are empty. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix - Form payment View link opens the selected invoice, not always the first Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix - Only show fallback billing or expiry dates when they match renewal status Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: asheshmagar <ashesh.ido@gmail.com>
…sage for a declined Stripe payment (#1874) (#1544) can_discard_pending_member() referenced the AJAX class without its namespace separators, so a declined guest registration fataled with a 500. Also pass the Stripe error message to confirm_payment when the subscription could not be created, so a declined renewal or registration shows the card error instead of 'Invalid Request.' once its pending order has been failed. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: asheshmagar <ashesh.ido@gmail.com>
…ep active members while Stripe retries, and keep the backfill window after a Stripe error (themegrill/user-registration-pro#1888) (#1547) * Fix - Resolve the PaymentIntent of a paid Stripe renewal on API versions without invoice.payment_intent (themegrill/user-registration-pro#1823) * Run the Stripe renewal PaymentIntent regression through the Playwright suite and keep its test doubles lint-clean (themegrill/user-registration-pro#1823) * Document the Invoice test double's __call parameters (themegrill/user-registration-pro#1823) * Fix - Recover paid Stripe renewals in the missed-payment backfill on current API versions, and keep the member's status while Stripe retries a failed invoice (themegrill/user-registration-pro#1888) - The backfill read invoice.subscription and invoice.payment_intent, which Stripe removed in 2025-03-31.basil, so every paid invoice was skipped. It now reads the subscription from parent.subscription_details and resolves the PaymentIntent with the shared invoice lookup, only for subscriptions this site owns. A failed lookup skips that invoice and the run continues. - The invoice lookup now throws to its caller; the webhook keeps logging it and aborting with an error so Stripe redelivers. - invoice.payment_failed no longer moves an active or trial subscription to pending while Stripe still has a retry scheduled (next_payment_attempt). A failure with no retry left still sets pending. * Address review on the Stripe backfill and failed-invoice fix (themegrill/user-registration-pro#1888) - Handle each invoice once per run: invoice.paid and invoice.payment_succeeded list the same invoice, so the second costs no extra Stripe call. - Link the signup invoice to a registration order saved without a PaymentIntent instead of creating a duplicate order, via a new OrdersRepository::get_unlinked_order_by_subscription(). - Keep only active members during Stripe retries; an ended trial whose first charge fails goes to pending as before. - Read the invoice creation time from the same array as the other fields, and update the delayed-start comment that described the old failed-invoice behaviour. * Keep the Stripe backfill window after a Stripe error, and read the webhook subscription from parent on current API versions (themegrill/user-registration-pro#1888) - The missed-payment scheduler moved its shared sync time forward even when the Stripe backfill threw, so one failed run dropped that window's paid renewals for good. Stripe now keeps its own sync time (urm_last_stripe_backfill_sync_time), advanced only after a fully successful run, like PayPal's. A skipped invoice lookup also keeps the window. - handle_webhook() now falls back to parent.subscription_details.subscription, so invoice events from an endpoint on API 2025-03-31.basil or newer no longer stop at NULL_SUBSCRIPTION_ID. * Address review on the Stripe sync-time fix (themegrill/user-registration-pro#1888) - Keep a failed window only for the order backfills (payments, one-time), which match by PaymentIntent. Status, refund and dispute backfills stay on the shared sync time, because replaying their events re-applies stale snapshots. - Also keep the window when a backfilled order cannot be saved or linked, not only when Stripe throws. - Normalize the webhook's subscription ID through extract_stripe_id(). * Address Copilot review on PR #1547 (themegrill/user-registration-pro#1888) - Link a signup invoice only to a Stripe order of the subscription, and only when exactly one unlinked non-trial order exists, so a payment is never written onto a PayPal/bank order or onto a guessed order when an upgrade left a second unlinked one. - Normalize the PaymentIntent read from invoice.payments through extract_stripe_id(), in case Stripe returns it expanded. --------- Co-authored-by: asheshmagar <ashesh.ido@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…/user-registration-pro#1896) Bordered fields already turn the border primary on focus, and the WCAG 2.4.7 outline sat 2px outside it. Remove the gap on text fields, textareas and selects so the border and outline read as one ring; buttons, checkboxes and radios keep their offset. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…l/user-registration-pro#1896) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ck its own test (themegrill/user-registration-pro#1896) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Build for ⬇️ Download user-registration-5.3.0.zip (8.2M) Installs directly via Plugins → Add New → Upload Plugin. A later push only rebuilds this if its commit message includes |
There was a problem hiding this comment.
🟡 Changes recommended
Rebuild the tracked CSS/RTL assets and extend tests to textarea and select controls.
1 open finding
What changed in this PR
Fixes the double focus-ring gap on front-end form controls while preserving focus visibility.
Changes:
- Sets
outline-offset: 0for text-like inputs, textareas, and selects. - Adds end-to-end focus-ring regression coverage.
| File | Summary |
|---|---|
assets/css/user-registration.scss |
Updates focus-outline offsets; generated CSS and RTL assets also need rebuilding. |
tests/e2e/specs/login-forms.spec.ts |
Adds regression coverage; textarea and select cases remain to be covered. |
🧠 Review effort: Lite
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
QA suite — passed ✅All 9 checks passed. 9 passed · 0 failed · 0 skipped · 0 flaky · 176s Automated check — no AI involved. It runs the tests in this branch. |
Resolve the conflict from retargeting #1548 to v5.3.0: the tree is the branch rebased onto v5.3.0, so the diff against it is only the focus ring fix. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
@claudegrill suite |

All Submissions:
Changes proposed in this Pull Request:
Focused text inputs on front-end forms showed two rings: the primary-colour border that the existing
:focusstyles already apply, and the:focus-visibleoutline added in #1535 sitting 2px outside it.The outline stays, since it is what guarantees a visible focus indicator when a form's own styles override the border (WCAG 2.4.7). Only the gap is removed: text-like inputs, textareas and selects now use
outline-offset: 0, so the border and the outline read as one ring. Buttons, submit inputs, checkboxes, radios and other non-text inputs keep the 2px offset, because they have no border to merge with (a submit input's fill is the same colour as the ring).The regression check is a separate e2e test in
login-forms.spec.tswith its own@guards #1896. The existing #1725 test is unchanged.Before (develop) and after, login form, username focused by keyboard:
Closes themegrill/user-registration-pro#1896
How to test the changes in this Pull Request:
Types of changes:
Other information:
Verified in a browser on a local site: login, and registration with text, email, password, textarea, select, radio, checkbox and submit controls. The compiled
user-registration.cssand its RTL copy carry only the new rule, because the QA suite loads the tracked CSS without running Sass; the rest of a full grunt build was left out as unrelated drift. QA suite: 9 of 9 passed on the current head.The branch is rebased onto
v5.3.0and was merged with it to clear the conflict after the base change, so squash-merge it to keepdevelop-only commits out of the release branch.Changelog entry