Easy Digital Downloads Limit One Per Customer

If you are using Easy Digital Downloads, there may be times where you need to limit the purchase of a particular download on your site. There are plugins like the Purchase Limit extension, which allows you to globally limit the purchase of certain products. But what if you need to have Easy Digital Downloads limit one per customer?

As far as I know, there isn’t a plugin that lets you do this. But it isn’t that difficult to set up either.

Set up the checkout error message

We need to be able to let the customer know why they won’t be able to check out if they have a certain download in their cart.

add_action( 'edd_before_purchase_form', 'sd_set_purchase_limit_error' );
function sd_set_purchase_limit_error(){
	$user_id 		= get_current_user_id(); // Get the current customer's ID
	$downloads 		= edd_get_users_purchased_products( $user_id ); // Get all of the products the customer has purchased in the past
	$can_checkout 		= true; // Allow checkout for now
	$in_cart 		= edd_item_in_cart( '57' ); // Check to see if our download is in the customer's cart. Change 57 to your download's ID.
 
	if ( $in_cart == true ){ // If the item isn't in the customer's cart we don't need to go any further.
		if ( ! empty( $downloads ) ){ // If the customer hasn't purchased anything before we don't need to go any further.
			foreach ( $downloads as $download ){ // Loop through each product the customer has purchased before.
				switch ( $download->ID ) {
					case '57': // If the customer has purchased the product we want to limit one per customer set an error message. Again change 57 to your product's ID.
						edd_set_error( 'already_purchased', apply_filters( 'edd_pc_error_message', __( 'You have already purchased the one-per-customer plugin. There is a limit of one per customer. You can log out and purchase with a different account, or remove it from your cart with the link above.' ) ) );
						break;
 
					default:
						edd_unset_error( 'already_purchased' );
						break;
				}
			}
 
			edd_print_errors(); // Display the errors on the checkout page.
		}
	}
}

Actually prevent checkout

So far all we have done is display an error message if the customer has purchased the download before. This won’t actually prevent them from going through the checkout process though.

Next, we need to use the edd_can_checkout filter to let the checkout process know that the customer can’t checkout while this product is still in their cart.

add_filter( 'edd_can_checkout', 'sd_limit_purchase' );
function sd_limit_purchase(){
	$user_id 		= get_current_user_id(); // Get the current customer's ID
	$downloads 		= edd_get_users_purchased_products( $user_id );// Get all of the products the customer has purchased in the past
	$can_checkout 		= true; // Allow checkout for now
	$in_cart 		= edd_item_in_cart( '57' ); // Check to see if our download is in the customer's cart. Change 57 to your download's ID.
 
	if ( $in_cart == true ){ // If the item isn't in the customer's cart we don't need to go any further.
		if ( ! empty( $downloads ) ){ // If the customer hasn't purchased anything before we don't need to go any further.
			foreach ( $downloads as $download ){ // Loop through each product the customer has purchased before.
				switch ( $download->ID ) {
					case '57': // If the customer has purchased the product we want to limit one per customer set an error message. Again change 57 to your product's ID.
						$can_checkout = false;
						break;
 
					default:
						$can_checkout = true;
						break;
				}
			}
		}
	}
	return $can_checkout;
}

And that’s it!

Now if your customer has purchased a certain product before that you want to have Easy Digital Downloads limit one per customer, they will see an error message and the checkout fields will be removed from the checkout page.

25 Comments

  1. Matt Whtieley on February 27, 2018 at 8:19 am

    Great little tutorial. I was able to modify this to use it globally to limit all downloads to one per person for a specific client case. This is the only tutorial on the web addressing this which is crazy. Thanks again!

    Matt

    • Scott DeLuzio on February 27, 2018 at 9:06 am

      Thanks Matt! I’m glad it helped.

    • Adrianne on May 11, 2018 at 3:19 pm

      Matt – I’m intrigued to know how you did this as all of my files are single purchase as well and it would be so much easier to have it set globally.

      Also Scott – I modified the code slightly to block checkout and leave the nag message simultaneously. I also added a variable for the name of the download so the customer knew exactly which download to remove before moving on.

  2. Alberto on March 20, 2018 at 6:17 am

    Thankyou very much, but I have a question can we use multiple downloads id instead of the example 57?

  3. […] thanks to Scott Deluzio for having crafted the majority of this lil’ snippet to modify the behavior of Easy Digital […]

  4. morteza on July 18, 2018 at 10:22 am

    Hi
    Thank you for your awesome post.
    It seems new updates of this plugin doesn’t support EDD frontend submissions & Email tags….

    There are several images and words about that features in this page (http://wpweb.co.in/documents/edd-purchase-limit/), but only in this page! and this plugin not support FES and Email tags for me.
    Do you have any idea?

    Regard

    • Scott DeLuzio on October 1, 2018 at 1:56 pm

      Sorry, I haven’t tested this with FES or Email Tags.

  5. Joel Acevedo on September 29, 2018 at 7:41 am

    Hi,

    Thank you very much for this, I’m curious if it would be possible to check all the downloads under a specific category, so every time I add a new download to that category I don’t have to edit this code?

    Thanks in advance.

    • Scott DeLuzio on October 1, 2018 at 11:23 am

      This is a somewhat tricky one. Currently Easy Digital Downloads uses custom post types for their downloads. That makes it fairly simple to use WP_QUERY to list all posts from the custom post type “download” in the taxonomy (category) you want. You can then loop through each post (download) that is returned, and feed the download ID to the code provided above.
      With that said, EDD version 3.0 is coming out (no official release date has been announced yet). From what I understand, that version will be moving away from using custom post types and will instead have downloads in their own database table. While this will improve performance, it will make the WP_QUERY I mentioned not work anymore. I don’t know exactly how it will work in v3.0 so I can’t say for sure what code you would need…yet.

  6. Harvinder Singh on December 10, 2018 at 2:07 pm

    nice article, I have one more question,

    how can I set a limit so that customer can add only one product at a time in a cart?
    For example, if someone clicks on product A and after that go back and then click on product B, then plugin must show product B and remove the product A from cart automatically.

    thanks

  7. michael on May 21, 2019 at 11:05 am

    hi,

    is it possible to limit 1 trial on 1st purchase, after that user payment will get deducted immediately.

    currently if we add a trial based product, the edd give trial on each checkout, so a same user can get multiple trials,

    • Scott DeLuzio on May 21, 2019 at 11:38 am

      I don’t know if there is a way to limit trial purchases. As far as I can tell, a “trial” status isn’t stored with the list of the customer’s previously purchased products. There might be a way to do it, but it would need some digging to figure out.

  8. Jesús on October 17, 2019 at 4:47 am

    Hi Scott,

    This snippet works fine, thanks! but i need change it for subscriptions.

    I would like to can purchase again a “download id” (subscription) if the subscription is not active ( cancelled or expired)

    Is it possible? How i can modify snippet? Thanks!

    • Scott DeLuzio on October 17, 2019 at 11:34 am

      You would need to add your own logic to the function to check if the subscription is cancelled or expired. Then instead of blocking the purchase, allow it in that instance.

      Take a look at the Recurring Payments documentation on getting subscriber information. You’ll also need to read up on the EDD_Subscription object.

      While I haven’t tested it, you probably would use something like this:
      $subscriber->get_subscriptions( 123, array( 'cancelled', 'expired' ) ); //Valid statuses for the array are 'pending', 'active', 'cancelled', 'expired', 'failing', 'completed' so adjust to your needs

  9. Jesús on October 18, 2019 at 2:05 am

    Hi Scott,

    Thanks for your response, Im tried it adding this code but is not working for me, my knowledge from php is very poor, can you help me, please?, the code would be working for id= 913, 915 and 917:

    add_filter( ‘edd_can_checkout’, ‘sd_limit_purchase’ );
    function sd_limit_purchase(){
    $user_id = get_current_user_id(); // Get the current customer’s ID
    $downloads = edd_get_users_purchased_products( $user_id );// Get all of the products the customer has purchased in the past
    $can_checkout = true; // Allow checkout for now
    $in_cart = edd_item_in_cart( ‘913’ ); // Check to see if our download is in the customer’s cart. Change 913 to your download’s ID.
    $subscriber->get_subscriptions( 123, array( ‘cancelled’, ‘expired’ ) );

    if ( $in_cart == true ){ // If the item isn’t in the customer’s cart we don’t need to go any further.
    if ( ! empty( $downloads ) ){ // If the customer hasn’t purchased anything before we don’t need to go any further.
    foreach ( $downloads as $download ){ // Loop through each product the customer has purchased before.
    switch ( $download->ID ) {
    case ‘913’: // If the customer has purchased the product we want to limit one per customer set an error message. Again change 913 to your product’s ID.
    if (! ($subscriber = ‘cancelled’))
    $can_checkout = false;
    break;

    if (! ($subscriber = ‘expired’))
    $can_checkout = false;
    break;

    default:
    $can_checkout = true;
    break;
    }
    }
    }
    }
    return $can_checkout;

    • Scott DeLuzio on October 18, 2019 at 2:52 pm

      Hey Jesus,
      I think this should get you closer. Note I haven’t tested it myself, but the logic seems right.

      add_filter( 'edd_can_checkout', 'sd_limit_purchase' );
      function sd_limit_purchase(){
      	$user_id = get_current_user_id(); // Get the current customer's ID
      	$downloads = edd_get_users_purchased_products( $user_id );// Get all of the products the customer has purchased in the past
      	$can_checkout = true; // Allow checkout for now
       
      	$subscriber = new EDD_Subscription;
       
       
      	if ( $in_cart == true ){ // If the item isn't in the customer's cart we don't need to go any further.
      		if ( ! empty( $downloads ) ){ // If the customer hasn't purchased anything before we don't need to go any further.
      			foreach ( $downloads as $download ){ // Loop through each product the customer has purchased before.
      				switch ( $download->ID ) {
      					case '913': // If the customer has purchased the product we want to limit one per customer set an error message. Again change 913 to your product's ID.
      						$in_cart = edd_item_in_cart( '913' ); // Check to see if our download is in the customer's cart. Change 913 to your download's ID.
      						if( $in_cart ){
      							// If the subscription isn't cancelled or expired the customer shouldn't be able to checkout
      							if( !$subscriber->get_subscriptions( 913, array( 'cancelled', 'expired' ) ) ){
      								$can_checkout = false;
      							}
      						}
      						break;
      					case '915': // If the customer has purchased the product we want to limit one per customer set an error message. Again change 915 to your product's ID.
      						$in_cart = edd_item_in_cart( '915' ); // Check to see if our download is in the customer's cart. Change 915 to your download's ID.
      						if( $in_cart ){
      							// If the subscription isn't cancelled or expired the customer shouldn't be able to checkout
      							if( !$subscriber->get_subscriptions( 915, array( 'cancelled', 'expired' ) ) ){
      								$can_checkout = false;
      							}
      						}
      						break;
      					case '917': // If the customer has purchased the product we want to limit one per customer set an error message. Again change 917 to your product's ID.
      						$in_cart = edd_item_in_cart( '917' ); // Check to see if our download is in the customer's cart. Change 917 to your download's ID.
      						if( $in_cart ){
      							// If the subscription isn't cancelled or expired the customer shouldn't be able to checkout
      							if( !$subscriber->get_subscriptions( 917, array( 'cancelled', 'expired' ) ) ){
      								$can_checkout = false;
      							}
      						}
      						break;
      					default:
      						$can_checkout = true;
      						break;
      				}
      			}
      		}
      	}
      	return $can_checkout;
      }
  10. Jesús on October 21, 2019 at 12:48 am

    Thanks Scott, i’m tested your code snippet but is not working for me.

    I have an user with 2 sucriptions:

    1 active and 1 cancelled and i can purchase again all suscriptions.

    I will try yo explain better my web:

    I have 3 types of suscription:

    1 Montly (id 913)
    2º Quaterly (id 915)
    3º Yearly (917)

    If the user have a montly active suscription, the user will can purchase only quaterly or yearly, and it will be perfect if could be possible to display an advertisment message that he have an active montly suscription.

    If the user have a quaterly active suscription, the user will can purchase only montly or yearly, and it will perfect if coul be possible to display an advertisment message that he have an active quaterly suscription.

    If the user have a yearly active suscription, the user will can purchase only montly or quaterly, and it will perfect if coul be possible to display an advertisment message that he have an active yearle suscription.

    If the user have a cancelled or expired montly suscription, he will can purchase montly suscription too again.

    If the user have a cancelled or expired quaterly suscription, he will can purchase quaterly suscription too again.

    If the user have a cancelled or expired yearly suscription, he will can purchase yearly suscription too again.

    If the user have an active montly suscription, he can’t purchase montly suscription again and it display a message error.

    If the user have an active quaterly suscription, he can’t purchase quaterly suscription again and it display a message error.

    If the user have an active yearly suscription, he can’t purchase quaterly suscription again and it display a message error.

    I would be very grateful if you could help me, I would pay you if necessary. Can we talk in private? Thanks again!

    You can check my website: https://desa2.fundaciontransparenciayopinion.org/colabora

    • Scott DeLuzio on October 21, 2019 at 8:05 am

      I can’t really dig into this much more than I have already. I’d suggest hiring a developer who can make these customizations for you. I wouldn’t think it would take much to get done. I suggest Codeable for custom development work. They should be able to help you out with this.

  11. Bilgi on April 11, 2021 at 8:54 am

    Hi,

    For free downloads, I want it to download a maximum of 5 times in 24 hours.

    I want to allow 5 or less downloads within 24 hours from your email and IP address.

    How can I do that? How can I check the IP address, mail address and whether it has been in the last 24 hours? Any help would make me happy. I would love to know if there are any hooks.

    Good work, thanks for all…

    • Scott DeLuzio on April 12, 2021 at 10:02 am

      I think that would probably require quite a bit of custom coding to work. It is most likely possible, but I don’t know the best way to go about it, especially with the upcoming EDD 3.0 release, which introduces some major changes to the way that data will be stored in the back end. It might be best to reach out to EDD support to see if they have any suggestions. They may have answered this question for another customer already, so it may be an easy answer for them.

Leave a Comment





This site uses Akismet to reduce spam. Learn how your comment data is processed.