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:
- First it checks if the customer/user has
_money_spent
metadata. If not, there’s no need to query the database. - 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 arewc_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 - 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:
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! 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.
Hello,
unfortunately the code doesn’t work for me, this line indicates ERROR!
“public function get_total_spent( &$customer )……”
Hi Peter — what does the full error message say?
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
This does not seem to work with guest checkouts. Is there a way to make this work with guest checkout?
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.
Yep, Dan’s explanation is perfect. Though, you have a possible workaround: automatically register guest checkouts (https://www.businessbloomer.com/woocommerce-automatically-register-guest-checkouts), in which case they’ll be counted 🙂