fix: block duplicate signup when customer has an active membership#963
fix: block duplicate signup when customer has an active membership#963superdav42 wants to merge 1 commit intomainfrom
Conversation
When a logged-in customer with an active subscription goes through the registration form as a new checkout, the gateway replaces the old subscription. Any coupons or custom pricing on the original subscription are lost because the new cart has no discount applied. Add a guard in process_order() that blocks 'new' checkouts when the logged-in user already has an active, trialing, or on-hold membership. The check fires before any customer, membership, or payment records are created so there are no orphaned records when the checkout is blocked. Filterable via wu_allow_duplicate_signup for sites that intentionally permit re-registration (e.g. multi-membership setups).
📝 WalkthroughWalkthroughAn early control-flow check is added to Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
🔨 Build Complete - Ready for Testing!📦 Download Build Artifact (Recommended)Download the zip build, upload to WordPress and test:
🌐 Test in WordPress Playground (Very Experimental)Click the link below to instantly test this PR in your browser - no installation needed! Login credentials: |
There was a problem hiding this comment.
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 `@inc/checkout/class-checkout.php`:
- Around line 788-796: The code returns an admin URL explicitly built with
wu_get_main_site_id(), which forces the main-site domain and can break
mapped-domain auth; change the URL construction to use the current checkout blog
instead (e.g., build the account link with get_admin_url(get_current_blog_id(),
'admin.php?page=account') or call the project’s existing blog-aware account
helper if one exists) so the "manage your existing subscription" link keeps the
user on the same domain when returning the WP_Error in this block (the code
around $allow / get_admin_url(... 'admin.php?page=account') in
class-checkout.php).
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3cef2b63-039b-4646-8d0a-07a79f5d7b6e
📒 Files selected for processing (1)
inc/checkout/class-checkout.php
| if ( ! $allow) { | ||
| $account_url = get_admin_url(wu_get_main_site_id(), 'admin.php?page=account'); | ||
|
|
||
| return new \WP_Error( | ||
| 'duplicate_signup', | ||
| sprintf( | ||
| /* translators: %s is a link to the account page. */ | ||
| __('You already have an active subscription. To manage your existing subscription, <a href="%s">visit your account page</a>. If you need help, please contact support.', 'ultimate-multisite'), | ||
| esc_url($account_url) |
There was a problem hiding this comment.
Use a blog-aware account URL here.
Line 789 always sends the customer to the main site's admin.php?page=account. This file already has to preserve the checkout blog for auth-sensitive links because main-site URLs can miss the user's valid cookie on mapped domains. If that happens here, the "manage your existing subscription" path is broken for the exact users this guard blocks. Build the URL from the current checkout blog, or reuse the existing helper that keeps the customer on the same domain.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@inc/checkout/class-checkout.php` around lines 788 - 796, The code returns an
admin URL explicitly built with wu_get_main_site_id(), which forces the
main-site domain and can break mapped-domain auth; change the URL construction
to use the current checkout blog instead (e.g., build the account link with
get_admin_url(get_current_blog_id(), 'admin.php?page=account') or call the
project’s existing blog-aware account helper if one exists) so the "manage your
existing subscription" link keeps the user on the same domain when returning the
WP_Error in this block (the code around $allow / get_admin_url(...
'admin.php?page=account') in class-checkout.php).
|
Performance Test Results Performance test results for 6ca86b1 are in 🛎️! Note: the numbers in parentheses show the difference to the previous (baseline) test run. Differences below 2% or 0.5 in absolute values are not shown. URL:
|
Summary
process_order()before any customer, membership, or payment records are created — no orphaned records when the checkout is blocked.wu_allow_duplicate_signupfilter for sites that intentionally allow re-registration (e.g. multi-membership setups).Problem
When a customer with an active subscription (e.g. $64/mo with coupon "web35") visits the registration page again and completes a "new" checkout:
process_subscription_created()cancels the old subscription ("Auto-cancelled: Replaced by subscription #XXXXX")The root cause is that logged-in users bypass the "email already in use" check at
maybe_create_customer()(line 1081), and there was no guard against creating a "new" checkout when an active membership already exists.Fix
Added a check in
class-checkout.php::process_order()(after cart type is determined, before any data creation) that returns aWP_Error('duplicate_signup', ...)when all three conditions are met:cart_typeis'new'active,trialing, oron-holdThe error message directs the customer to their account page and suggests contacting support.
How to test
cart_type='upgrade'etc., not'new')add_filter('wu_allow_duplicate_signup', '__return_true')Context
A customer (KursoPro) built a custom workaround ("kp-prevent-duplicate-signup") that hooks into
wu_checkout_after_validateandwoocommerce_checkout_processto block this scenario. However, their UMS-level hook (wu_checkout_after_validate) doesn't actually exist in UMS core — only their WC-level hook was active, and it fires late (after UMS has already created pending records). This fix addresses the issue at the correct level in the checkout pipeline.aidevops.sh v3.13.5 plugin for OpenCode v1.3.17 with gemma4:e4b spent 7d 8h and 43,427 tokens on this as a headless worker.
Summary by CodeRabbit