WooCommerce Function of the Week: wc_get_customer_total_spent

WooCommerce is full of unknown gems for developers. For example, there are some core functions that calculate pretty complex data, and these functions are mainly “public” so that WooCommerce developers can reuse them for their own purposes.

Today, let’s take a look at the wc_get_customer_total_spent( $user_id ) WooCommerce PHP function.

As you can guess by its name, this function returns the total amount spent by a given customer identified by their user ID. There is no need to loop through user orders to add up totals and return a final sum — WooCommerce already has you covered.

So, let’s take a deeper look at the syntax, meaning, and usage of wc_get_customer_total_spent.

How does it work?

Function source code and description

You can find the wc_get_customer_total_spent function under woocommerce\includes\wc-user-functions.php. Clearly, it’s a function that gets a user ID and returns the total spent by that user as a string:

/**
 * Get total spent by customer.
 *
 * @param  int $user_id User ID.
 * @return string
 */
function wc_get_customer_total_spent( $user_id ) {
	$customer = new WC_Customer( $user_id );
	return $customer->get_total_spent();
}

The first question that comes to mind is which order statuses are counted toward the total amount spent?

The answer might be inside the get_total_spent function in woocommerce\includes\class-wc-customer.php:

/**
 * Return how much money this customer has spent.
 *
 * @return float
 */
public function get_total_spent() {
	return $this->data_store->get_total_spent( $this );
}

Actually, we also need to look at WC_Customer_Data_Store::get_total_spent() in woocommerce/includes/data-stores/class-wc-customer-data-store.php:

/**
 * Return how much money this customer has spent.
 *
 * @since 3.0.0
 * @param WC_Customer $customer Customer object.
 * @return float
 */
public function get_total_spent( &$customer ) {
	$spent = apply_filters(
		'woocommerce_customer_get_total_spent',
		get_user_meta( $customer->get_id(), '_money_spent', true ),
		$customer
	);
	if ( '' === $spent ) {
		global $wpdb;
		$statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
		$spent    = $wpdb->get_var(
			// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
			apply_filters(
				'woocommerce_customer_get_total_spent_query',
				"SELECT SUM(meta2.meta_value)
				FROM $wpdb->posts as posts
				LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
				LEFT JOIN {$wpdb->postmeta} AS meta2 ON posts.ID = meta2.post_id
				WHERE   meta.meta_key       = '_customer_user'
				AND     meta.meta_value     = '" . esc_sql( $customer->get_id() ) . "'
				AND     posts.post_type     = 'shop_order'
				AND     posts.post_status   IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
				AND     meta2.meta_key      = '_order_total'",
				$customer
			)
			// phpcs:enable
		);
		if ( ! $spent ) {
			$spent = 0;
		}
		update_user_meta( $customer->get_id(), '_money_spent', $spent );
	}
	return wc_format_decimal( $spent, 2 );
}

Finally, we have the full picture of how the wc_get_customer_total_spent function works:

  1. First it checks if the customer/user has _money_spent metadata. If not, there’s no need to query the database.
  2. If _money_spent is null, the function queries the database for all orders placed by the customer ID. The only order statuses to be counted are wc_get_is_paid_statuses ones. If you do a file search for that function, you’ll see that by default it only includes completed and processing order statuses, unless you’ve added custom order statuses to the paid statuses array
  3. After the database call, the total sum is calculated and stored it in the _money_spent customer meta, so that next time the function can run faster by avoiding yet another query.

An interesting observation: get_total_spent returns a floating-point number with two decimals by default (unless you specify otherwise via the WooCommerce settings or a PHP filter hook), while wc_get_customer_total_spent declares that the total is returned as a string. That’s a mystery! Probably a mistake, too, as it should definitely be a float.

What is it good for?

Function usage

Clearly, wc_get_customer_total_spent can be really useful for a store with an active and established customer base.

A good use case for wc_get_customer_total_spent that immediately comes to mind is a VIP section dedicated to customers who have spent more than a given amount. You could display thank you messages, special offers, unlock benefits, and create targeted VIP marketing messages.

A good place to display a VIP status or message is the WooCommerce “My Account dashboard page” and header notification bar for logged-in customers. Then your VIP customers will see a special notice just for them when they log in.

Here’s an example:

/**
 * @snippet       Show Notice If Customer Spent > $500 @ My Account
 * @how-to        Get CustomizeWoo.com FREE
 * @author        Rodolfo Melogli
 * @compatible    WooCommerce 6
 * @donate $9     https://businessbloomer.com/bloomer-armada/
 */
  
add_action( 'woocommerce_account_dashboard', 'bbloomer_my_account_notice_if_customer_spent' );
function bbloomer_my_account_notice_if_customer_spent() {
   $user_id = get_current_user_id();
   if ( wc_get_customer_total_spent( $user_id ) > 500 ) {
      echo '<div class="woocommerce-message"><a class="button" href="/shop/vip">Browse VIP-only products</a> Congratulations, you are a VIP member!</div>';
   }
}

Here’s how I use it on Business Bloomer:

If the customer has spent more than $500, this message will show in the My Account > Dashboard page

Limitations and Further Questions

WooCommerce by default has a single currency, so wc_get_customer_total_spent may not be usable with multi-currency stores without a special workaround. Have you encountered this situation and come up with a solution? Let us know in the comments.

Do the Woo

Do the Woo! Get the Podcast. Subscribe to the Newsletter.

Learn from others. Grow your business. Connect with a community of like-minded developers and builders who freelance, run agencies, and build really cool products and sites in the WooCommerce ecosystem.

Similar Posts

6 Comments

  1. Hello,
    unfortunately the code doesn’t work for me, this line indicates ERROR!
    “public function get_total_spent( &$customer )……”

    1. Hey Peter, if I understand well you misunderstood the “public function get_total_spent( &$customer )……” function meaning. That’s the function present at WooCommerce core and should not be used for development purposes.

      On the other hand, look at the wc_get_customer_total_spent( $user_id ) function, and use that within a plugin or a snippet, and magic will happen

    1. It can’t work with guest checkout because guests aren’t logged in. As a result, you (or rather WooCommerce) can’t identify the guest with an existing user account and its previous purchase history.

      Guest checkout is often used by people who do not have an account and do not wish to have one on your site for privacy or some other reason. If they become repeat customers, it’s more likely they will establish a user account. But until then, all you can do is look at how many guest purchases you’ve had and maybe guess (by IP logging) how many people are buying from you that way, and what the total revenue is from “guests.”

      If you have a lot of guest checkouts taking place, maybe you should look at the reasons for that — is it difficult or unclear how to create an account? Maybe adding some instructions and incentives would help — explain the benefit to the customer, offer them a reward (like ad discount code/coupon) for creating an account.

Comments are closed.