WP Learner
WP Learner

Reputation: 830

Allow customers to buy only one product from defined WooCommerce product category

I want to let customers buy only one product from a defined category. All products are priced as 0 (zero). Only delivery fee is charged.

I referred to the same scenario and tried this code. But this code fails for not logged in users. When I try multiple purchases I have no matter of buying. Want to limit after the first successful attempt of a purchase. I am using the child theme functions.php file.

Note: customers do not need to register in the website before doing a purchase.

add_filter('woocommerce_add_to_cart_validation','filter_add_to_cart_validation',20, 2);
function filter_add_to_cart_validation($valid, $product_id){
    $current_user = wp_get_current_user();
    if ( wc_customer_bought_product( $current_user->user_email, $current_user->ID, $product_id) && has_term( array('free-giveaway'), 'product_cat', $product_id ) ) {
        wc_add_notice( __( 'You already bought an item. Let others to buy as well.', 'woocommerce' ), 'error' );
        $valid = false;
    }
    return $valid;
}

Upvotes: 1

Views: 1023

Answers (1)

7uc1f3r
7uc1f3r

Reputation: 29614

Explanation/comments related to the answer:

  • The has_term() WordPress function is used to check the product category
  • It is better to only apply these kinds of checks (has bought before) for logged in users, since usually no data is known from customers. However, it is possible that you can obtain the e-mail address via WC_Customer. (this will not always suffice, hence my comment to only apply this to logged in users)
  • Note: the has_bought() function is copied and pasted from Checking if customer has already bought something in WooCommerce answer code

So you get:

function filter_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = null, $variations = null ) {
    // Set (multiple) categories
    $categories = array ( 'free-giveaway', 'categorie-1' );
    
    // If passed & has category
    if ( $passed && has_term( $categories, 'product_cat', $product_id ) ) {
        // Initialize
        $value = '';

        // User logged in
        if ( is_user_logged_in() ) {
            // Get the current user's ID 
            $value = get_current_user_id();
        } else {
            // Get billing_email
            $value = WC()->customer->get_billing_email();

            // When empty
            if ( empty ( $value ) ) {
                // Get account email
                $value = WC()->customer->get_email();   
            }
        }

        // NOT empty
        if ( ! empty ( $value ) ) {
            if ( has_bought( $value ) ) {
                // Display an error message
                wc_add_notice( __( 'My custom error message', 'woocommerce' ), 'error' );

                // False
                $passed = false;
            }
        }       
    }

    return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'filter_woocommerce_add_to_cart_validation', 10, 5 );

// Based partially on wc_customer_bought_product(), will return a boolean value based on orders count (false for O orders and true when there is at least one paid order)
function has_bought( $value = 0 ) {
    if ( ! is_user_logged_in() && $value === 0 ) {
        return false;
    }

    global $wpdb;
    
    // Based on user ID (registered users)
    if ( is_numeric( $value ) ) { 
        $meta_key   = '_customer_user';
        $meta_value = $value == 0 ? (int) get_current_user_id() : (int) $value;
    } 
    // Based on billing email (Guest users)
    else { 
        $meta_key   = '_billing_email';
        $meta_value = sanitize_email( $value );
    }
    
    $paid_order_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );

    $count = $wpdb->get_var( $wpdb->prepare("
        SELECT COUNT(p.ID) FROM {$wpdb->prefix}posts AS p
        INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
        WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $paid_order_statuses ) . "' )
        AND p.post_type LIKE 'shop_order'
        AND pm.meta_key = '%s'
        AND pm.meta_value = %s
        LIMIT 1
    ", $meta_key, $meta_value ) );

    // Return a boolean value based on orders count
    return $count > 0;
}

Upvotes: -1

Related Questions