Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: limit purchase of a network membership #169

Merged
merged 4 commits into from
Dec 20, 2024
Merged
Changes from 3 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
62 changes: 58 additions & 4 deletions includes/woocommerce-memberships/class-limit-purchase.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,36 @@ class Limit_Purchase {
public static function init() {
add_filter( 'woocommerce_subscription_is_purchasable', [ __CLASS__, 'restrict_network_subscriptions' ], 10, 2 );
add_filter( 'woocommerce_cart_product_cannot_be_purchased_message', [ __CLASS__, 'woocommerce_cart_product_cannot_be_purchased_message' ], 10, 2 );

// Also limit purchase for logged out users, inferring their IDs from the email.
add_action( 'woocommerce_after_checkout_validation', [ __CLASS__, 'validate_network_subscription' ], 10, 2 );
}

/**
* Restricts subscription purchasing from a network-synchronized plan to one.
* Restricts subscription purchasing from a network-synchronized plan to one for logged in readers.
*
* @param bool $purchasable Whether the subscription product is purchasable.
* @param \WC_Product_Subscription|\WC_Product_Subscription_Variation $subscription_product The subscription product.
* @return bool
*/
public static function restrict_network_subscriptions( $purchasable, $subscription_product ) {
if ( ! is_user_logged_in() ) {
return $purchasable;
}
return self::get_network_equivalent_subscription_for_current_user( $subscription_product ) ? false : $purchasable;
}

/**
* Given a product, check if the current user has an active subscription in another site that gives access to the same membership.
* Given a product, check if the user has an active subscription in another site that gives access to the same membership.
*
* @param \WC_Product $product Product data.
* @param int|null $user_id User ID, defaults to the current user.
*/
private static function get_network_equivalent_subscription_for_current_user( \WC_Product $product ) {
$user_id = get_current_user_id();
private static function get_network_equivalent_subscription_for_current_user( \WC_Product $product, $user_id = null ) {
if ( is_null( $user_id ) ) {
$user_id = self::get_user_id_from_email();
}

if ( ! $user_id ) {
return;
}
Expand Down Expand Up @@ -105,4 +115,48 @@ public static function woocommerce_cart_product_cannot_be_purchased_message( $me
}
return $message;
}

/**
* Get user from email.
*
* @return false|int User ID if found by email address, false otherwise.
*/
private static function get_user_id_from_email() {
$billing_email = filter_input( INPUT_POST, 'billing_email', FILTER_SANITIZE_EMAIL );
if ( $billing_email ) {
$customer = \get_user_by( 'email', $billing_email );
if ( $customer ) {
return $customer->ID;
}
}
return false;
}

/**
* Validate network subscription for logged out readers.
*
* @param array $data Checkout data.
* @param WC_Errors $errors Checkout errors.
*/
public static function validate_network_subscription( $data, $errors ) {
if ( is_user_logged_in() || ! function_exists( 'WC' ) ) {
return;
}
$id_from_email = self::get_user_id_from_email();
if ( $id_from_email ) {
$cart_items = WC()->cart->get_cart();
foreach ( $cart_items as $cart_item ) {
$product = $cart_item['data'];
$network_active_subscription = self::get_network_equivalent_subscription_for_current_user( $product, $id_from_email );
if ( $network_active_subscription ) {
$error_message = sprintf(
/* translators: %s: Site URL */
__( "You can't buy this subscription because you already have it active on %s", 'newspack-network' ),
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm fine with this updated error message, but just wanted to share why I removed the site name initially. Since this is for logged out readers, its possible for someone to input another persons email address here, in which case they'd learn about where another person's subscription exists.

This is probably not a big deal, but wanted to share just in case.

$network_active_subscription['site']
);
$errors->add( 'network_subscription', $error_message );
}
}
}
}
}