Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions inc/checkout/class-checkout.php
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,90 @@ public function process_order() {
*/
$this->type = $cart->get_cart_type();

/*
* Block duplicate signup for logged-in users with active memberships.
*
* When a customer with an active subscription goes through the
* registration form again as a "new" checkout (rather than
* upgrade/downgrade), the new subscription replaces the old one
* and any applied coupons or custom pricing are lost.
*
* This guard fires early — before any customer, membership, or
* payment records are created — so there are no orphaned records
* to clean up when the checkout is blocked.
*
* Filterable via `wu_allow_duplicate_signup` for sites that
* intentionally permit re-registration (e.g. multi-membership).
*
* @since 2.5.1
*/
if ('new' === $this->type && is_user_logged_in()) {
$existing_customer = wu_get_current_customer();

if ($existing_customer) {
$active_memberships = wu_get_memberships([
'customer_id' => $existing_customer->get_id(),
'status__in' => [
Membership_Status::ACTIVE,
Membership_Status::TRIALING,
Membership_Status::ON_HOLD,
],
'number' => 1,
]);

if ( ! empty($active_memberships)) {
$existing_membership = reset($active_memberships);

/**
* Filters whether to allow a duplicate signup when the
* customer already has an active membership.
*
* Return `true` to allow the checkout to proceed. The
* default is `false` (block the signup).
*
* @since 2.5.1
*
* @param bool $allow Whether to allow the duplicate signup.
* @param \WP_Ultimo\Models\Membership $membership The existing active membership.
* @param \WP_Ultimo\Models\Customer $customer The current customer.
* @param \WP_Ultimo\Checkout\Cart $cart The cart being processed.
*/
$allow = apply_filters(
'wu_allow_duplicate_signup',
false,
$existing_membership,
$existing_customer,
$cart
);

if ( ! $allow) {
/*
* Build the account URL on the customer's own subsite
* rather than the main network site. The "account" admin
* page lives in each subsite's wp-admin. Using
* wu_get_main_site_id() would force the main-site domain
* which breaks when domain mapping is active.
*
* Falls back to the main site only if the membership has
* no published sites yet (pending site scenario).
*/
$membership_sites = $existing_membership->get_sites();
$account_blog_id = ! empty($membership_sites) ? $membership_sites[0]->get_id() : wu_get_main_site_id();
$account_url = get_admin_url($account_blog_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)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)
);
}
}
}
}

/*
* Gets the gateway object we want to use.
*
Expand Down
Loading