Search code examples
wordpresswoocommercecheckoutorderscoupon

Programmatically apply a coupon in WooCommerce 3.0+ for first order made by a customer


I use this code to programmatically apply a coupon in WooCommerce, for first order made by customers. I am needing a function that sees if the customer is new, and if so applies the coupon code.

I am trying to use this within my functions.php of my child theme to no avail.

add_action('woocommerce_after_checkout_validation','check_new_customer_coupon', 0);
function check_new_customer_coupon(){
    global $woocommerce;
    // you might change the name of your coupon
    $new_cust_coupon_code = 'new2022';

    $has_apply_coupon = false;

    foreach ( WC()->cart->get_coupons() as $code => $coupon ) {
        if($code == $new_cust_coupon_code) {
            $has_apply_coupon = true;
        }
    }

    if($has_apply_coupon) {

        if(is_user_logged_in()) {
            $user_id = get_current_user_id();

            // retrieve all orders
            $customer_orders = get_posts( array(
                    'meta_key'    => '_customer_user',
                    'meta_value'  => $user_id,
                    'post_type'   => 'shop_order',
                    'numberposts'=> -1
            ) );

            if(count($customer_orders) > 0) {
                $has_ordered = false;

                $statuses = array('wc-failed', 'wc-cancelled', 'wc-refunded');

                // loop thru orders, if the order is not falled into failed, cancelled or refund then it consider valid
                foreach($customer_orders as $tmp_order) {

                    $order = wc_get_order($tmp_order->ID);
                    if(!in_array($order->get_status(), $statuses)) {
                        $has_ordered = true;
                    }
                }

                // if this customer already ordered, we remove the coupon
                if($has_ordered == true) {
                    WC()->cart->remove_coupon( $new_cust_coupon_code );
                    wc_add_notice( sprintf( "Coupon code: %s is only applicable for new customer." , $new_cust_coupon_code), 'error' );
                    return false;
                }
            } else {
                // customer has no order, so valid to use this coupon
                return true;
            }

        } else {
            // new user is valid
            return true;
        }
    }
}

Only this no longer appears to work in the current WooCommerce version? What am I doing wrong? My WordPress version is 6.0 and WooCommerce is 6.5.1


Solution

  • The code you are using no longer works in current WooCommere versions because it contains outdated code, shortcomings and some superfluous steps:

    • It is not necessary to use get_posts() with multiple arguments, use wc_get_customer_order_count() instead
    • $tmp_order->ID changed to $tmp_order->get_id() since WooCommerce 3
    • Your code does not take guest users into account, so they can manually apply the coupon
    • The coupon is not automatically awarded, your code assumes that the customers have apply this coupon
    • The woocommerce_after_checkout_validation hook contains 2 parameters
    • You don't need to return true\false as it's an action hook not a filter.

    This updated version takes into account:

    • If the coupon been applied, but the customer is NOT logged in, coupon will be removed
    • If the coupon been applied, the customer is logged in, but NOT the first order, the coupon will be removed
    • If the coupon been applied, the customer is logged in and it is the first order, the coupon remains
    • If the coupon NOT been applied, the customer is logged in and it is the first order, the coupon is applied

    So you get:

    function action_woocommerce_after_checkout_validation( $data, $error ) {
        // Change the name to your coupon
        $new_cust_coupon_code = 'coupon1';
    
        // Initialize
        $coupon_been_applied = false;
        $remove_coupon = false;
    
        // Get applied coupons
        $applied_coupons = WC()->cart->get_applied_coupons();
    
        // Has coupon already been applied by the customer
        if ( in_array( $new_cust_coupon_code, $applied_coupons ) ) {
            $coupon_been_applied = true;
    
            // Coupon has been applied, but customer is a guest user
            if ( ! is_user_logged_in() ) {
                $remove_coupon = true;
            }
        }
    
        // Customer is logged in
        if ( is_user_logged_in() ) {    
            // Check if the customer has bought before
            $has_bought_before = wc_get_customer_order_count( get_current_user_id() ) >= 1 ? true : false;
    
            // Coupon been applied, but customer has bought before
            if ( $coupon_been_applied && $has_bought_before ) {
                $remove_coupon = true;
            // NOT been applied AND NOT has bought before
            } elseif ( ! $coupon_been_applied && ! $has_bought_before ) {
                // Apply coupon
                WC()->cart->apply_coupon( $new_cust_coupon_code );
            }
        }
    
        // When true
        if ( $remove_coupon ) {
            // Remove coupon
            WC()->cart->remove_coupon( $new_cust_coupon_code );
    
            // Show message
            $error->add( 'validation', sprintf( __( 'Coupon code: "%s" is only applicable for logged in new customers. So the coupon has been removed', 'woocommerce' ), $new_cust_coupon_code ) );
        }
    }
    add_action( 'woocommerce_after_checkout_validation', 'action_woocommerce_after_checkout_validation', 10, 2 );