Search code examples
phpsqlwoocommercecartdiscount

Add a percentage discount based on customer total purchases sum in Woocommerce


In Woocommerce, I would like to set a percentage discount based on customer total purchases sum. For example if the total purchase sum greater or equal to 200$, customer get 5% discount.

So, I have first part of code for showing the total sum:

function get_customer_total_order() {
    $customer_orders = get_posts( array(
        'numberposts' => - 1,
        'meta_key'    => '_customer_user',
        'meta_value'  => get_current_user_id(),
        'post_type'   => array( 'shop_order' ),
        'post_status' => array( 'wc-completed' )
    ) );

    $total = 0;

    foreach ( $customer_orders as $customer_order ) {
        $order = wc_get_order( $customer_order );
        $total += $order->get_total();
    }

    return $total;
}

And I would like to use my code with the code of this answer:
Progressive discount based on cart total in WooCommerce

It there a way to use them both to set the discount based on total sum of all orders?


Solution

  • There is multiple ways to get customer's total purchases sum:

    1) You could replace your first function using wc_get_customer_total_spent() dedicated Woocommerce function, but this function take all orders paid status ( which are "processing" and "completed").

    2) You could also use this lighter SQL query based on a similar source code for "completed" order status only:

    // Utililty function to get customer's total purchases sum
    function get_customer_total_purchases_sum() {
        $current_user_id = get_current_user_id(); // Current user ID
    
        if( $current_user_id == 0 ) return 0; // we return zero if customer is not logged in
    
        global $wpdb;
    
        // return the SQL query (paid orders sum)
        return $wpdb->get_var("SELECT SUM(pm.meta_value) FROM {$wpdb->prefix}postmeta as pm
        INNER JOIN {$wpdb->prefix}postmeta as pm2 ON pm.post_id = pm2.post_id
        INNER JOIN {$wpdb->prefix}posts as p ON pm.post_id = p.ID
        WHERE p.post_status LIKE 'wc-completed' AND p.post_type LIKE 'shop_order'
        AND pm.meta_key LIKE '_order_total' AND pm2.meta_key LIKE '_customer_user'
        AND pm2.meta_value LIKE '$current_user_id'");
    }
    

    This code goes on function.php file of your active child theme (or theme). Tested and works.

    3) Or you can use your own question function code (but it's heavier). It's up to you.


    The percentage discount (2 ways):

    1) A negative fee:

    The following code will set a percentage discount based on customer's total purchases sum (using my function above):

    // Percentage discount based on customer's total purchases sum
    add_action('woocommerce_cart_calculate_fees', 'customer_purchases_total_sum_percentage_discount', 20, 1 );
    function customer_purchases_total_sum_percentage_discount( $cart ){
        // Only for logged in user
        if ( ( is_admin() && ! defined( 'DOING_AJAX' ) ) || ! is_user_logged_in() )
            return;
    
        ## 1. Get the customer's purchases total sum (and save it in WC sessions to avoid multiple queries
    
        // Check if it's saved in WC_Session
        $purchases_sum = WC()->session->get( 'purchases_sum' ); 
        // If not get it and save it
        if( empty($purchases_sum) ){ 
            // ==> HERE goes the function to get customer's purchases total sum
            $purchases_sum = get_customer_total_purchases_sum();
            // Save it in WC_Session
            WC()->session->set('purchases_sum', $purchases_sum); 
        }
    
        ## 2. Set the discount percentage based on customer's total purchases sum
        if( $purchases_sum >= 200 ){
            $percent =  5; // 5%
        }
    
        if( isset($percent) && $percent > 0){
            $discount = $cart->cart_contents_total * $percent / 100; // discount calculation
            // Set the discount (For discounts (negative fee) the taxes as always included)
            $cart->add_fee( __('Discount', 'woocommerce' ) . " (" . $percent . "%)", -$discount);
        }
    }
    

    This code goes on function.php file of your active child theme (or theme). Tested and works.


    2) Auto adding a coupon code (percentage discount):

    First you need to set a new coupon code (percentage discount type of 5%):

    enter image description here

    Then you will use the following code instead that will auto add a coupon code based on customer's total purchases sum (using my function above):

    add_action( 'woocommerce_before_calculate_totals', 'customer_total_purchases_coupon_discount', 30, 1 );
    function customer_total_purchases_coupon_discount( $cart ) {
        // Only for logged in user
        if ( ( is_admin() && ! defined( 'DOING_AJAX' ) ) || ! is_user_logged_in() )
            return;
    
        if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
            return;
    
        // HERE define your coupon code (in lowercase)
        $coupon_code = 'customersilver';
    
        ## 1. Get the customer's purchases total sum (and save it in WC sessions to avoid multiple queries
    
        // Check if it's saved in WC_Session
        $purchases_sum = WC()->session->get( 'purchases_sum' );
        // If not get it and save it
        if( empty($purchases_sum) ){
            // ==> HERE goes the function to get customer's purchases total sum
            $purchases_sum = get_customer_total_purchases_sum();
            // Save it in WC_Session
            WC()->session->set('purchases_sum', $purchases_sum);
        }
    
        ## 2. Auto applying or removing a coupon code (percentage discount coupon)
    
        // Apply the coupon if there is at least 2 units of "5 Reusable wet"
        if ( ! $cart->has_discount( $coupon_code ) && $purchases_sum >= 200 ) {
            $cart->add_discount( $coupon_code );
        } elseif( $cart->has_discount( $coupon_code ) && $purchases_sum < 200 ) {
            $cart->remove_coupon( $coupon_code );
        }
    }
    

    This code goes on function.php file of your active child theme (or theme). Tested and works.