Checking If Customer Has Already Bought Something in Woocommerce

Checking if customer has already bought something in WooCommerce

Update 2020: New updated improved, light and faster version HERE that handle also guests from billing email



Yes it is possible creating a conditional function that return true when a customer has already at least one order with status completed.

Here is the code for this conditional function:

function has_bought() {
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => 1, // one order is enough
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed', // Only orders with "completed" status
'fields' => 'ids', // Return Ids "completed"
) );

// return "true" when customer has already at least one order (false if not)
return count($customer_orders) > 0 ? true : false;
}

This code is tested and works.

This code goes in function.php file of your active child theme or theme, or in a plugin php file.


USAGE (as a condition):

  • You can use it in some WooCommerce templates that you will have previously copied to your active child theme or theme.
  • In your theme php files.
  • In plugin php files.
  • Any php function or WordPress/WooCommerce.

References

  • Template Structure + Overriding Templates via a Theme
  • Check if a user has purchased specific products in WooCommerce

Check if customer has purchased something and add product to cart in WooCommerce

The code below uses the custom function has_bought(). It auto add to cart a different product for new customers and confirmed customers:

add_action( 'template_redirect', 'add_product_to_cart_conditionally' );
function add_product_to_cart_conditionally() {
if ( is_admin() ) return; // Exit

// Below define the product Id to be added:
$product_A = 37; // <== For new customers that have not purchased a product before (and guests)
$product_B = 53; // <== For confirmed customers that have purchased a product before

$product_id = has_bought() ? $product_B : $product_A;

// If cart is empty
if( WC()->cart->is_empty() ) {
WC()->cart->add_to_cart( $product_id ); // Add the product
}
// If cart is not empty
else {
// Loop through cart items (check cart items)
foreach ( WC()->cart->get_cart() as $item ) {
// Check if the product is already in cart
if ( $item['product_id'] == $product_id ) {
return; // Exit if the product is in cart
}
}
// The product is not in cart: We add it
WC()->cart->add_to_cart( $product_id );
}
}

Code goes in functions.php file of the active child theme (or active theme). Tested and works.

Related: Checking if customer has already bought something in WooCommerce

Check if a user/guest has purchased specific products in WooCommerce

Lighter and improved code version in HERE that handle multiple product IDs

Updated (compatibility for Woocommerce 3+)

Yes it's possible, writing a conditional function that returns "true" if current customer has already bought specifics defined products IDs. This code goes on function.php file of your active child theme or theme.

Here is the conditional function:

function has_bought_items() {
$bought = false;

// Set HERE ine the array your specific target product IDs
$prod_arr = array( '21', '67' );

// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed' // Only orders with status "completed"
) );
foreach ( $customer_orders as $customer_order ) {
// Updated compatibility with WooCommerce 3+
$order_id = method_exists( $order, 'get_id' ) ? $order->get_id() : $order->id;
$order = wc_get_order( $customer_order );

// Iterating through each current customer products bought in the order
foreach ($order->get_items() as $item) {
// WC 3+ compatibility
if ( version_compare( WC_VERSION, '3.0', '<' ) )
$product_id = $item['product_id'];
else
$product_id = $item->get_product_id();

// Your condition related to your 2 specific products Ids
if ( in_array( $product_id, $prod_arr ) )
$bought = true;
}
}
// return "true" if one the specifics products have been bought before by customer
return $bought;
}

This code is tested and works.


USAGE:

For example, you can use it in some WooCommerce templates that you will have previously copied to your active child theme or theme:

  • The template for Shop page concerning add-to-cart button is loop/add-to-cart.php.
  • The templates for Product pages concerning add-to-cart button are in single-product/add-to-cart folder depending on your product types.

Here is an example that you could use in those templates (above):

// Replace the numbers by your special restricted products IDs
$restricted_products = array( '20', '32', '75' );

// compatibility with WC +3
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;

// customer has NOT already bought a specific product for this restricted products
if ( !has_bought_items() && in_array( $product_id, $restricted_products ) ) {

// Displaying an INACTIVE add-to-cart button (With a custom text, style and without the link).
// (AND optionally) an explicit message for example.

// ALL OTHER PRODUCTS OR RESTRICTED PRODUCTS IF COSTUMER HAS ALREADY BOUGHT SPECIAL PRODUCTS
} else {

// place for normal Add-To-Cart button code here
}

And here the complete applied example to add-to-cart button template on Shop page:

<?php
/**
* Loop Add to Cart
*
* This template can be overridden by copying it to yourtheme/woocommerce/loop/add-to-cart.php.
*
* HOWEVER, on occasion WooCommerce will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes
* @package WooCommerce/Templates
* @version 2.5.0
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

global $product;

// Replace the numbers by your special restricted products IDs
$restricted_products = array( '37', '53', '70' );

// compatibility with WC +3
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;

if ( !has_bought_items() && in_array( $product_id, $restricted_products ) ) {

echo '<a class="button greyed_button">' . __("Disabled", "your_theme_slug") . '</a>';
echo '<br><span class="greyed_button-message">' . __("Your message goes here…", "your_theme_slug") . '</span>';

} else {

echo apply_filters( 'woocommerce_loop_add_to_cart_link',
sprintf( '<a rel="nofollow" href="%s" data-quantity="%s" data-product_id="%s" data-product_sku="%s" class="%s">%s</a>',
esc_url( $product->add_to_cart_url() ),
esc_attr( isset( $quantity ) ? $quantity : 1 ),
esc_attr( $product_id ),
esc_attr( $product->get_sku() ),
esc_attr( isset( $class ) ? $class : 'button' ),
esc_html( $product->add_to_cart_text() )
),
$product );

}

You will style the inactive button with greyed_button class in the style.css file of your active child theme or theme. Same thing for the message with greyed_button-message class.

Woocommerce: Checking if successful purchase was from a new or returning customer

The following code should work for returning customer as well as new customer irrespective of a change in billing email address. This should also work for a new customer registering while checking out.

add_action('woocommerce_thankyou', 'is_returning_customer', 10, 1);

function is_returning_customer($order_id)
{
if (!$order_id) {
return;
}
if(is_user_logged_in()) {
$order_status = array('wc-on-hold', 'wc-processing', 'wc-completed');
$customer_id = get_current_user_id();
$customer_orders=get_posts( array(
'meta_key' => '_customer_user',
'meta_value' => $customer_id,
'post_type' => 'shop_order',
'post_status' => $order_status,
'numberposts' => -1
)
);
if(count($customer_orders)>1) {
//returning customer
} else {
//new customer
}
}
}

Check on the checkout page,if Logged In User Has Already Purchased a Product

I presume that your cart (review order part) on checkout page have many products and you want to show Already Purchased text somewhere near product name.

You can do this by overriding template. copy file from wp-plugins/woocommerce/templates/checkout/review-order.php into your active theme (preferably child theme) here you can see how to create child theme (https://developer.wordpress.org/themes/advanced-topics/child-themes/). Now create woocommerce folder in your active theme if not exist, then create checkout folder if not exist and paste that file. Open that file and you can see the Cart loop around line no 31.

add code before that loop as follow:

if( is_user_logged_in() ) {
$user = wp_get_current_user();
}

Now, update loop code bit as follow:

foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );

if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_checkout_cart_item_visible', true, $cart_item, $cart_item_key ) ) {
?>
<tr class="<?php echo esc_attr( apply_filters( 'woocommerce_cart_item_class', 'cart_item', $cart_item, $cart_item_key ) ); ?>">
<td class="product-name">
<?php echo apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . ' '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo apply_filters( 'woocommerce_checkout_cart_item_quantity', ' <strong class="product-quantity">' . sprintf( '× %s', $cart_item['quantity'] ) . '</strong>', $cart_item, $cart_item_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo wc_get_formatted_cart_item_data( $cart_item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php
// Here is your code -- Start
if( is_user_logged_in() && wc_customer_bought_product( $user->user_email, $user->ID, $_product->get_id() ) ) {
echo apply_filters( 'woocommerce_checkout_cart_alredy_bought', '<div class="user-bought">' . sprintf( "Hi %s you already purchased in the past.", $user->first_name ) . '</div>' );
}
// Here is your code -- End
?>
</td>
<td class="product-total">
<?php echo apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</td>
</tr>
<?php
}
}

This should be working for you.

Display a custom text if user has already bought the current product In Woocommerce

If you want to target only Orders with a complete status, you can use the following custom conditional function based on wc_customer_bought_product() source code:

// Utility function to check if a customer has bought a product (Order with "completed" status only)
function customer_has_bought_product( $product_id, $user_id = 0 ) {
global $wpdb;
$customer_id = $user_id == 0 || $user_id == '' ? get_current_user_id() : $user_id;
$status = 'wc-completed';

if( ! $customer_id )
return false;

// Count the number of products
$count = $wpdb->get_var( "
SELECT COUNT(woim.meta_value) FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS woi ON p.ID = woi.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim ON woi.order_item_id = woim.order_item_id
WHERE p.post_status = '$status'
AND pm.meta_key = '_customer_user'
AND pm.meta_value = $customer_id
AND woim.meta_key IN ('_product_id','_variation_id')
AND woim.meta_value = $product_id
" );

// Return a boolean value if count is higher than 0
return $count > 0 ? true : false;
}

add_action( 'woocommerce_before_single_product_summary', 'woo_review_discount_message');
function woo_review_discount_message() {
global $product;

if ( customer_has_bought_product( $product->get_id() ) && ! $product->is_type('variable') ) {
$user = wp_get_current_user();
echo '<div class="user-bought"><span style="color:#CA364D;font-weight:bold;font-size:18px;"><i class="wishlist-icon icon-heart-o"></i></span> Hi ' . $user->first_name . '! Please write a review below.</a></div>';
}
}

Code goes in function.php file of the active child theme (or active theme). Tested and works.

If customer is not logged in nothing will be displayed.

Like Woocommerce function wc_customer_bought_product(), this will not work on variable products single pages.

WooCommerce - Check if user purchased a product in the past 60 days

Here is a conditional function partially based on the built-in woocommerce function wc_customer_bought_product source code query:

There is an 3 optional argument $user_id, $product_ids and $days:

  • $user_id will allow you to specify a defined user ID (when is not used for current logged in user);
  • $product_ids (string or an array) will allow to specify defined product Ids to check
  • $dayswill allow you to specify the number of days to search for (or the period if you prefer)

The code function:

function has_bought_multi( $user_id = 0,  $product_ids = 0, $days = 0 ) {
global $wpdb;
$customer_id = $user_id == 0 || $user_id == '' ? get_current_user_id() : $user_id;
$statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
$date = date('Y-m-d H:i:s', strtotime("-$days day") );

if ( is_array( $product_ids ) )
$product_ids = implode(',', $product_ids);

if ( $product_ids != ( 0 || '' ) )
$query_line = "AND woim.meta_value IN ($product_ids)";
else
$query_line = "AND woim.meta_value != 0";

// Count the number of products
$product_count_query = $wpdb->get_col( "
SELECT COUNT(woim.meta_value) FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS woi ON p.ID = woi.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim ON woi.order_item_id = woim.order_item_id
WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
AND p.post_date > '$date'
AND pm.meta_key = '_customer_user'
AND pm.meta_value = $customer_id
AND woim.meta_key IN ( '_product_id', '_variation_id' )
$query_line
" );
// Set the count in a string
$count = reset($product_count_query);

// Return a boolean value if count is higher than 0
return $count > 0 ? true : false;
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

This code is tested on Woocommerce 3+ and works.

Similar answer: Check if a customer has purchased a specific products in WooCommerce


USAGE EXAMPLE (Customer is logged in):

Detecting if current user has bought your product id # 514 in past 60 days:

if( has_bought_multi( '', 514, 60 ) ){
echo "<p>Customer has bought product id # 514 in past 60 days</p>";
// do something
} else {
echo "<p>Customer <strong>HAS NOT</strong> bought product id # 514 in past 60 days</p>";
// do something else
}


Related Topics



Leave a reply



Submit