A client reached out to me recently with an interesting problem. They wanted to:
- Register guests as new users (easy)
- Allow existing users to checkout as guests (less easy)
The client already had code set up to connect these guest account purchases to the existing account based on billing email. The code worked, but only if guest checkout wasn’t enabled. They needed to get past the restriction of forced login by WordPress/WooCommerce when an existing user checked out, as well as register new guests as they purchased items.
Registering guests as new users is simple: hide the Create New Account? checkbox via CSS and set $_POST[createaccount] = 1 in the backend on submit. This ensures that we don’t need to use some hacky JS to set the value, and can depend on the server to do what we need.
Allowing existing users to check out as guests presents a more interesting problem. WooCommerce has no way of doing this out of the box, and what I could find on StackOverflow said it’s impossible without editing WC Core.
I had “Allow customers to place orders without an account” and “Allow customers to log into an existing account during checkout” checked in Admin > WooCommerce > Settings > Accounts & Privacy. If I tried to checkout as a guest with an email that already existed, I ran into “That account already exists.” Well, shit.
Between this StackOverflow answer and a string search in Woo, I discovered that there was no way to do anything within the process_customer function. If somebody is a brand new user, everything flows smoothly because process_customer creates the user via wc_create_new_customer. If that somebody is a current user, the function throws an error and tells the user to log in.
Then I stumbled on the solution to all of my problems: wp_set_current_user
The function allows you to set the current user for performing various actions without logging that user in. The process_checkout function calls process_customer to either create a new user or get the current one and do a couple of things (like update user meta with new data).
I was set! Just check to see if a user has posted a billing_email value on init and call wp_set_current_user!
Then I ran into another snag: If you set the user in this manner when you submit the form, the nonce verification fails. No matter how hard you try, you cannot checkout. Dammit!
More investigation triggered a lightbulb: we can set the user AFTER the nonce verification via the woocommerce_before_checkout_process hook (below).
Ok, so now we can verify the nonce and set the user based on the email address. We still need to include registering new users into the mix.
The solution:
function yourplugin_allow_checkout_as_guest() {
// Ensure we are checking out (vs updating a profile, for ex) and have a billing email
if (isset($_REQUEST['woocommerce-process-checkout-nonce']) && isset($_POST['billing_email'])) {
$user_email = $_POST['billing_email'];
$user = get_user_by('email', $user_email);
if ( !empty($user->ID) ) {
// If we have a user, we need to set the user so that
// WooCommerce has a user and doesn't think it needs
// to create another one for that user's email
wp_set_current_user($user->ID);
} else {
// If we have no user, we let WooCommerce know that we need to create a user
$_POST['createaccount'] = 1;
}
}
}
add_action('woocommerce_before_checkout_process', 'yourplugin_allow_checkout_as_guest');
If we have woocommerce-process-checkout-nonce, we know for a fact that we’re in the checkout process.
If we have billing_email, we know it is somebody who isn’t logged in.
So, we can now properly tell WooCommerce that this order belongs to an existing user, or to create a new one, without logging in at all!
Not gonna lie, I was pretty excited to figure this out. Many solutions can be Googled and applied pretty easily, but this one took some detective work.