Skip to content

Instantly share code, notes, and snippets.

@bekarice
Last active September 12, 2024 16:37
Show Gist options
  • Save bekarice/a2da034d37dbc64d41be to your computer and use it in GitHub Desktop.
Save bekarice/a2da034d37dbc64d41be to your computer and use it in GitHub Desktop.
Disables Repeat Purchase for any WooCommerce product
<?php
/**
* Disables repeat purchase for products / variations
*
* @param bool $purchasable true if product can be purchased
* @param \WC_Product $product the WooCommerce product
* @return bool $purchasable the updated is_purchasable check
*/
function sv_disable_repeat_purchase( $purchasable, $product ) {
// Don't run on parents of variations,
// function will already check variations separately
if ( $product->is_type( 'variable' ) ) {
return $purchasable;
}
// Get the ID for the current product (passed in)
$product_id = $product->get_id();
// return false if the customer has bought the product / variation
if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $product_id ) ) {
$purchasable = false;
}
return $purchasable;
}
add_filter( 'woocommerce_is_purchasable', 'sv_disable_repeat_purchase', 10, 2 );
/**
* Shows a "purchase disabled" message to the customer
*/
function sv_purchase_disabled_message() {
global $product; // get the current product to see if it has been purchased
if ( $product->is_type( 'variable' ) ) {
foreach ( $product->get_children() as $variation_id ) {
// Render the purchase restricted message if it has been purchased
if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $variation_id ) ) {
sv_render_variation_non_purchasable_message( $product, $variation_id );
}
}
} else {
if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $product->get_id() ) ) {
echo '<div class="woocommerce"><div class="woocommerce-info wc-nonpurchasable-message">You\'ve already purchased this product! It can only be purchased once.</div></div>';
}
}
}
add_action( 'woocommerce_single_product_summary', 'sv_purchase_disabled_message', 31 );
/**
* Generates a "purchase disabled" message to the customer for specific variations
*
* @param \WC_Product $product the WooCommerce product
* @param int $no_repeats_id the id of the non-purchasable product
*/
function sv_render_variation_non_purchasable_message( $product, $no_repeats_id ) {
// Double-check we're looking at a variable product
if ( $product->is_type( 'variable' ) && $product->has_child() ) {
$variation_purchasable = true;
foreach ( $product->get_available_variations() as $variation ) {
// only show this message for non-purchasable variations matching our ID
if ( $no_repeats_id === $variation['variation_id'] ) {
$variation_purchasable = false;
echo '<div class="woocommerce"><div class="woocommerce-info wc-nonpurchasable-message js-variation-' . sanitize_html_class( $variation['variation_id'] ) . '">You\'ve already purchased this product! It can only be purchased once.</div></div>';
}
}
}
if ( ! $variation_purchasable ) {
wc_enqueue_js("
jQuery('.variations_form')
.on( 'woocommerce_variation_select_change', function( event ) {
jQuery('.wc-nonpurchasable-message').hide();
})
.on( 'found_variation', function( event, variation ) {
jQuery('.wc-nonpurchasable-message').hide();
if ( ! variation.is_purchasable ) {
jQuery( '.wc-nonpurchasable-message.js-variation-' + variation.variation_id ).show();
}
})
.find( '.variations select' ).change();
");
}
}
@joshrogersdesign
Copy link

Hey Beka!

This code is awesome - thank you for your hard work. My only issue is that I want to allow my client's customers to re-purchase only one product category. In this case it's a category called "gift-cards" (id=470). So far I have tried to just simply isolate a product id to be allowed for repurchase. But even this has its bugs:
if ( $product_id = 43026) {
return $purchasable;
}

Would you be able to help me with the category conditionality?

@pbpraveen1988
Copy link

Hey Beka,

I am new to woo-commerce can you instruct me where to put the above code to run the site perfectly .

I already purchased the woo-commerce membership plugin and installed it. and I restricted a downloadable product to count according to membership and put amount of product is free because i restricted them with membership so user already paid for membership so he can easily get that product as free and after that he can download but download count is 3 (for example) so he download one so the remaining is 2 now but if he can purchase again then the download count start again from 3 , hope you understand my requirement.

That would be great helpful
Thank You

@Copylefter
Copy link

Wordpress in debug mode give me notice "Notice: Trying to get property of non-object" to line if ( wc_customer_bought_product( get_current_user()->user_email, get_current_user_id(), $product_id ) ) { $purchasable = false; }

@mikeeman2000
Copy link

This is great. Any way to add a time limit? Say limit purchase every 30 days?

@mcpat-it
Copy link

mcpat-it commented Apr 8, 2017

Great code but I have a problem with wpml (multilanguage) which is not detecting the purchase in another language, I solved the problem with small adaption:

/**
 * Disables repeat purchase for products / variations
 * 
 * @param bool $purchasable true if product can be purchased
 * @param \WC_Product $product the WooCommerce product
 * @return bool $purchasable the updated is_purchasable check
 */
function sv_disable_repeat_purchase( $purchasable, $product ) {
	// Don't run on parents of variations,
	// function will already check variations separately
	if ( $product->is_type( 'variable' ) ) {
		return $purchasable;
	}
	
	// Get the ID for the current product (passed in)
	$product_id = $product->is_type( 'variation' ) ? $product->variation_id : $product->id; 
    
    // return false if the customer has bought the product / variation
    // https://wpml.org/forums/topic/get-the-translated-products-id-from-default-id/
	global $sitepress;
	$translated_ids = Array();
	if(!isset($sitepress)) return;
	$post_id = $product->id; // Your original product ID
	$trid = $sitepress->get_element_trid($post_id, 'post_product');
	$translations = $sitepress->get_element_translations($trid, 'product');
	foreach( $translations as $lang=>$translation){
		$translated_ids[] = $translation->element_id;
	}
    //http://stackoverflow.com/questions/27306828/how-to-use-wc-customer-bought-product-function-to-check-if-customer-bought-produ
	foreach($translated_ids as $item):
    	if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $item) ) {
    		$purchasable = false;
    		break;
    	}
    endforeach; 
    
    // Double-check for variations: if parent is not purchasable, then variation is not
    if ( $purchasable && $product->is_type( 'variation' ) ) {
        $purchasable = $product->parent->is_purchasable();
    }
    
    return $purchasable;
}
add_filter( 'woocommerce_is_purchasable', 'sv_disable_repeat_purchase', 10, 2 );


/**
 * Shows a "purchase disabled" message to the customer
 */
function sv_purchase_disabled_message() {
	
	// Get the current product to see if it has been purchased
	global $product;
	
	if ( $product->is_type( 'variable' ) ) {
		
		foreach ( $product->get_children() as $variation_id ) {
			// Render the purchase restricted message if it has been purchased
			if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $variation_id ) ) {
				sv_render_variation_non_purchasable_message( $product, $variation_id );
			}
		}
		
	} else {
		// https://wpml.org/forums/topic/get-the-translated-products-id-from-default-id/
		global $sitepress;
		$translated_ids = Array();
		if(!isset($sitepress)) return;
		$post_id = $product->id; //34; // Your original product ID
		$trid = $sitepress->get_element_trid($post_id, 'post_product');
		$translations = $sitepress->get_element_translations($trid, 'product');
		foreach( $translations as $lang=>$translation){
			$translated_ids[] = $translation->element_id;
		}
		foreach($translated_ids as $item):
    		if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $item) ) {
    			echo '<div class="woocommerce"><div class="woocommerce-info wc-nonpurchasable-message">You\'ve already purchased this product! It can only be purchased once.</div></div>';
    			break;
    		}
    	endforeach; 
	}
}
add_action( 'woocommerce_single_product_summary', 'sv_purchase_disabled_message', 31 );


/**
 * Generates a "purchase disabled" message to the customer for specific variations
 * 
 * @param \WC_Product $product the WooCommerce product
 * @param int $no_repeats_id the id of the non-purchasable product
 */
function sv_render_variation_non_purchasable_message( $product, $no_repeats_id ) {
	
	// Double-check we're looking at a variable product
	if ( $product->is_type( 'variable' ) && $product->has_child() ) {
		
		$variation_purchasable = true;
		
		foreach ( $product->get_available_variations() as $variation ) {
			
			// only show this message for non-purchasable variations matching our ID
			if ( $no_repeats_id === $variation['variation_id'] ) {
				$variation_purchasable = false;	
				echo '<div class="woocommerce"><div class="woocommerce-info wc-nonpurchasable-message js-variation-' . sanitize_html_class( $variation['variation_id'] ) . '">You\'ve already purchased this product! It can only be purchased once.</div></div>';
			}
		}
	}
		
	if ( ! $variation_purchasable ) {
		wc_enqueue_js("
			jQuery('.variations_form')
				.on( 'woocommerce_variation_select_change', function( event ) {
					jQuery('.wc-nonpurchasable-message').hide();
				})
				.on( 'found_variation', function( event, variation ) {
					jQuery('.wc-nonpurchasable-message').hide();
					if ( ! variation.is_purchasable ) {
						jQuery( '.wc-nonpurchasable-message.js-variation-' + variation.variation_id ).show();
					}
				})
			.find( '.variations select' ).change();
		");
	}
}

@mrjikey
Copy link

mrjikey commented Feb 12, 2018

where should i upload this file?

@rjdougan
Copy link

How should this code be modified to disable a variable product regardless of which variation is selected on the initial purchase?

@oempire
Copy link

oempire commented Feb 4, 2020

Getting error showing in PHP log after implementing this

[04-Feb-2020 15:47:17 UTC] id was called incorrectly. Product properties should not be accessed directly. Backtrace: require('wp-blog-header.php'), wp, WP->main, WP->parse_request, do_action_ref_array('parse_request'), WP_Hook->do_action, WP_Hook->apply_filters, rest_api_loaded, WP_REST_Server->serve_request, WP_REST_Server->check_authentication, apply_filters('rest_authentication_errors'), WP_Hook->apply_filters, Automattic\WooCommerce\Blocks\RestApi::maybe_init_cart_session, WC_Cart->get_cart, WC_Cart_Session->get_cart_from_session, WC_Product->is_purchasable, apply_filters('woocommerce_is_purchasable'), WP_Hook->apply_filters, sv_disable_repeat_purchase, WC_Abstract_Legacy_Product->__get, wc_doing_it_wrong. This message was added in version 3.0.

@crossworx
Copy link

where should i upload this file?

Did you get an answer on this?

@starzakp
Copy link

Hi Beka,
This code is awesome. Thank you!!

I would like to apply this code only to DOWNLOADABLE products. Or if easier to just one Product Category. Could you help with that?

All the best!

@ivan006
Copy link

ivan006 commented Nov 4, 2021

PHP Notice: variation_id was called incorrectly. Product properties should not be accessed directly

Great code but I have a problem with wpml (multilanguage) which is not detecting the purchase in another language, I solved the problem with small adaption:

/**
 * Disables repeat purchase for products / variations
 * 
 * @param bool $purchasable true if product can be purchased
 * @param \WC_Product $product the WooCommerce product
 * @return bool $purchasable the updated is_purchasable check
 */
function sv_disable_repeat_purchase( $purchasable, $product ) {
	// Don't run on parents of variations,
	// function will already check variations separately
	if ( $product->is_type( 'variable' ) ) {
		return $purchasable;
	}
	
	// Get the ID for the current product (passed in)
	$product_id = $product->is_type( 'variation' ) ? $product->variation_id : $product->id; 
    
    // return false if the customer has bought the product / variation
    // https://wpml.org/forums/topic/get-the-translated-products-id-from-default-id/
	global $sitepress;
	$translated_ids = Array();
	if(!isset($sitepress)) return;
	$post_id = $product->id; // Your original product ID
	$trid = $sitepress->get_element_trid($post_id, 'post_product');
	$translations = $sitepress->get_element_translations($trid, 'product');
	foreach( $translations as $lang=>$translation){
		$translated_ids[] = $translation->element_id;
	}
    //http://stackoverflow.com/questions/27306828/how-to-use-wc-customer-bought-product-function-to-check-if-customer-bought-produ
	foreach($translated_ids as $item):
    	if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $item) ) {
    		$purchasable = false;
    		break;
    	}
    endforeach; 
    
    // Double-check for variations: if parent is not purchasable, then variation is not
    if ( $purchasable && $product->is_type( 'variation' ) ) {
        $purchasable = $product->parent->is_purchasable();
    }
    
    return $purchasable;
}
add_filter( 'woocommerce_is_purchasable', 'sv_disable_repeat_purchase', 10, 2 );


/**
 * Shows a "purchase disabled" message to the customer
 */
function sv_purchase_disabled_message() {
	
	// Get the current product to see if it has been purchased
	global $product;
	
	if ( $product->is_type( 'variable' ) ) {
		
		foreach ( $product->get_children() as $variation_id ) {
			// Render the purchase restricted message if it has been purchased
			if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $variation_id ) ) {
				sv_render_variation_non_purchasable_message( $product, $variation_id );
			}
		}
		
	} else {
		// https://wpml.org/forums/topic/get-the-translated-products-id-from-default-id/
		global $sitepress;
		$translated_ids = Array();
		if(!isset($sitepress)) return;
		$post_id = $product->id; //34; // Your original product ID
		$trid = $sitepress->get_element_trid($post_id, 'post_product');
		$translations = $sitepress->get_element_translations($trid, 'product');
		foreach( $translations as $lang=>$translation){
			$translated_ids[] = $translation->element_id;
		}
		foreach($translated_ids as $item):
    		if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $item) ) {
    			echo '<div class="woocommerce"><div class="woocommerce-info wc-nonpurchasable-message">You\'ve already purchased this product! It can only be purchased once.</div></div>';
    			break;
    		}
    	endforeach; 
	}
}
add_action( 'woocommerce_single_product_summary', 'sv_purchase_disabled_message', 31 );


/**
 * Generates a "purchase disabled" message to the customer for specific variations
 * 
 * @param \WC_Product $product the WooCommerce product
 * @param int $no_repeats_id the id of the non-purchasable product
 */
function sv_render_variation_non_purchasable_message( $product, $no_repeats_id ) {
	
	// Double-check we're looking at a variable product
	if ( $product->is_type( 'variable' ) && $product->has_child() ) {
		
		$variation_purchasable = true;
		
		foreach ( $product->get_available_variations() as $variation ) {
			
			// only show this message for non-purchasable variations matching our ID
			if ( $no_repeats_id === $variation['variation_id'] ) {
				$variation_purchasable = false;	
				echo '<div class="woocommerce"><div class="woocommerce-info wc-nonpurchasable-message js-variation-' . sanitize_html_class( $variation['variation_id'] ) . '">You\'ve already purchased this product! It can only be purchased once.</div></div>';
			}
		}
	}
		
	if ( ! $variation_purchasable ) {
		wc_enqueue_js("
			jQuery('.variations_form')
				.on( 'woocommerce_variation_select_change', function( event ) {
					jQuery('.wc-nonpurchasable-message').hide();
				})
				.on( 'found_variation', function( event, variation ) {
					jQuery('.wc-nonpurchasable-message').hide();
					if ( ! variation.is_purchasable ) {
						jQuery( '.wc-nonpurchasable-message.js-variation-' + variation.variation_id ).show();
					}
				})
			.find( '.variations select' ).change();
		");
	}
}

PHP Notice: variation_id was called incorrectly. Product properties should not be accessed directly

@Picpool
Copy link

Picpool commented Feb 12, 2022

Hello , it is great , is there a way to include for processing orders , a,nd not only completed ?
Thanks

@mdarab
Copy link

mdarab commented May 7, 2022

Hello,

I would like to show pop-up when click add to cart button to confirm with yes or no repeat purchase.

If Yes button clicked the customer can continue his purchase and if the No button clicked, no products are added to the cart.

What should I do?

Thank you in advanced

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