I have a client with a WordPress/WooCommerce site. They have a large client that needs to access their site with a generic username and password that is shared. This generic account could be used at the same time. This ended up being a large issue because WooCommerce does its best to save the cart, especially, if the user is logged in. It saves the cart in the wp databases. So when this generic account is logged in on one workstation and adds something to the shopping cart, if another user logs in with the same credentials and accesses the shopping cart, they see the current state the first user’s cart. Both users can add and delete to this cart and generally interfere with each others purchases.
Unfortunately, there is not a quick setting fix for this…or even a quick hook and filter to fix this, but I did find a way. WooCommerce’s website has a request page for this functionality. You should vote for it if this solution interests you. (It had 6 votes when I wrote this post). Link to woothemes.com suggestion box to disable persistent cart.
Otherwise, here is a workaround. I was able to do this WITHOUT altering the woocommerce core files. Also, as I write this, it has only been tested minimally. Use this code at your own risk.
The first step would be to prevent Woocommerce from saving the cart values in the database for a user logged in. Looking through the WC_Cart object code, I found the code where it saves the cart information into the WordPress usermeta database using update_user_meta
with the key value of _woocommerce_persistent_cart
. This saves the cart so that if the user has not completed the order and later if they log back onto the site, their cart will be retrieved. This code is ONLY called if the user is logged into the site when creating the shopping cart. Anonymous users do not get their carts saved in the usermeta database. Unfortunately, there is no hook or filter one can use to prevent this save, but there is a hook right after the persistent cart is saved. So I was able to delete this record right after it creates it.
add_action( 'woocommerce_cart_updated', 'jht_delete_persistent_cart');
function jht_delete_persistent_cart() {
if ( get_current_user_id() ) { //only need to do this when a user is logged in
$wc = WooCommerce::instance();
$wc->cart->persistent_cart_destroy();
}
The woocommerce-cart_updated
action occurs right after the save so this is then called, it checks if the user is logged in and if they are, then grab the WooCommerce object instance and run their code to destroy the persistent cart saved in the database.
Now this doesn’t destroy their current cart in their current session. From what I can gather, this persistent cart is only loaded when the user logs in and there is no cart in the local session which, I believe, the session cart gets destroyed on logout.
All users have a local session that is stored in a cookie by the WC_Session_Handler object. The cookie only stores the customer_id and session_expiration. The WC_Session_Handler object stores the actual cart as an array in the wp_options database. It stores it with the option_name as ‘_wc_session_customerID‘ where customerID is, well, the customer id if the user is logged in. If it’s a non-logged in user, it is a random number. This is another place two users logged in with the same username could have problems. They would both be using the same option_name and thus their carts will interfere again.
The WC_Session_Handler object has a function that generates this customer_id. If the user is logged in, it returns the current user id using WordPress’s function get_current_user_id()
. If the user is not logged in, then it generates a random number to use. What we need is for this function to return a random number no matter what. There are no hooks or filters to hook into. If WooCommerce decides to add the ability to remove persistent carts, I would expect this would be the place to add one. With nothing to hook into here, what can we do without altering this core file. It would be simple to just remove the if statement. I found a place in the woocommerce object where it initiates the instance of the WC_Session_Handler object AND to our advantage, it offers a filter for us to change the object we use for the session handler. This filter is in the woocommerce class init. The filter is woocommerce_session_handler
. Now before you leave this page with the thought, “I don’t what to create a session handler object!” We only have to extend the WC_Session_Handler object and override this single function. In fact, we will copy this function and just remove the if statement.
The ‘class-my-session-handler.php’ looks like the following. It is the same function from WC_Session_Handler; I only removed the if statement.
class My_Session_Handler extends WC_Session_Handler {
public function generate_customer_id() {
require_once( ABSPATH . 'wp-includes/class-phpass.php');
$hasher = new PasswordHash( 8, false );
return md5( $hasher->get_random_bytes( 32 ) );
}
}
Now in the functions.php file we can do the following to have it active.
if(! is_admin()) {
add_action( 'after_setup_theme', 'jht_persistent_cart_fix_init' );
}
function jht_persistent_cart_fix_init() {
if(class_exists('WC_Session_Handler')) {
//load session handler to give all users, even logged in users, a unique _customer_id for the session cookie
require_once 'class-my-session-handler.php';
add_filter( 'woocommerce_session_handler', function () {
return 'My_Session_Handler';
} );
} else {
echo ' WC_Session_Handler was not found so My_Session_Handler not loaded
';
}
}
I’m using WordPress version 4.2.3 and WooCommerce Plugin version 2.3.13.