Add to cart in WooCommerce with PHP or JavaScript

Often when building your WooCommerce store you run into having to add to cart in unfamiliar ways.

This may happen when you have a custom offer page, for example. Or you might just want to avoid having a regular, long, checkout funnel. Some stores just don’t need all the options a regular store does and, usually, the easier it is for customers to purchase, the better.

Add a product to cart programmatically using JavaScript

The JavaScript approach might be more common because it’s often connected to custom pop-ups, menu items and such. There are also plugins which allow you to add your custom callbacks when certain elements are clicked.

Here’s a straightforward approach extracted from the actual WooCommerce function:

You can add this to your theme’s footer for example ( just before the closing body tag </body> ) or include it in another JavaScript file which is already loaded. Alternatively you can use the Insert Headers and Footers plugin.

One thing to keep in mind is that if you include the code inside another function it won’t be globally accessible anymore.

The cool part of this is that if you have a properly-built theme the cart will also update without having to reload the page.

Things to note:

  • The function makes an Ajax call so it’s not instant ( synchronous ). Even though it feels like that on a good connection.
  • If you want to add visual feedback you’ll need to modify the $.post call to add a callback.
  • Read the comments in the code and remove the parts which aren’t necessary for you.
  • If you want specific help, ask in the comments! I’m happy to help if I can.

Making sure the function works

If you want to quickly test this function, after adding it to your site, just open the console and call it directly. I’ve set up a quick example site using WooCommerce’s default theme Storefront here: https://wcplayground.mircian.com/

To see the function in action, go to the link above, open the Developer console, write m_wc_add_to_cart( 42 ) and press Enter. You’ll notice that the top right cart in the header is immediately updated.

You’ll also be able to use this for variations, if you know the variation id. Try using the id 66 in the example above and check out the cart.

If the function doesn’t work in your setup, make sure the function is available globally. Also check if wc_add_to_cart_params is available, otherwise the function won’t do anything.

Add to cart programmatically using PHP

It’s easier to modify the cart on the PHP side. For most setups you’ll still need to call this from the frontend using an Ajax call or submitting a form.

A couple of concepts to keep in mind for this:

  • WooCommerce interactions should be made using the WC() function which should be available anywhere. This allows you to access the WooCommerce instance while ensuring future compatibility of your code.
  • You won’t be able to use all functionality if you don’t use the right hook. A safe bet is to use woocommerce_init which ensures all components were loaded.

With that in mind, adding to the cart is just a matter of calling:

WC()->cart->add_to_cart( $product_id, $quantity );

You can even leave out the quantity as it will default to 1.

Similar to the JavaScript code, the function is flexible and allows you to use a variation id directly. This means that if you know your variation id is 45, for example, you can call directly WC()->cart->add_to_cart( 45 ); and the right variation will be added.

Photo by rawpixel on Unsplash

Privately test & configure a WooCommerce payment gateway

Given its importance, adding a new payment method to a live WooCommerce website can be a troublesome task for a developer. In this article I’m going to describe what a payment gateway is, how you can use it and most importantly how to enable a new gateway in production, safely. Our main concern is testing a gateway in production without exposing its potential issues to customers.

What is a payment gateway?

WooCommerce uses payment gateways to capture different types of payment from your customers. Some involve manual interaction, like check payments or bank transfer, which need to be confirmed by a shop manager/administrator. Others, use an external provider which handles credit card payments, which is what I’ll be covering here in this article.

So you might say that a WooCommerce payment gateway is your shop’s connection to a safe payment process handled externally.

There are many options and each one has its specific options, so adding a new option might prove problematic.

Picking a payment gateway

When looking for a payment gateway I think there are 3 main features which you have to consider:

  • Commission rate. Payment processors, who handle the actual transaction and make sure the data is safe, need to make money also. That’s why they will take a small commission from each transaction although some processors also ask for a monthly fee or might have different plans depending on the size of your business.
  • Trust. You want your customers to feel safe when they make a purchase on your website. There’s no point to having a low commission rate if people don’t place the orders. That’s why sometimes with a local audience it’s ok to use a local payment processor. But, with a global audience you might want to use something recognisable around the globe.
  • Refund & dispute policy. I’m not saying you should be trying to trick your customers but sometimes you might have complaints from customers who were not paying attention. Look out for increased fees in case of refunds. Also, make sure to check which actions might block your account as that means you won’t be receiving any payments.

Adding a new payment gateway

In WooCommerce, installing a new gateway is as easy as installing a WordPress plugin & configuring that plugin to use the credentials from the payment gateway you’ve chosen.

For most gateways you’ll find the configuration panel in WordPress admin under WooCommerce > Settings > Checkout. After setting up the desired credentials make sure to also check the option at the top to activate the gateway.

Setting up these credentials properly is essential as you’ll want to avoid:

  • Customers who took time to find the right product and filled in all their info but aren’t able to pay you. That’s one customer which will be hard to get back on your website.
  • Issues with redirects and warnings about a SSL certificate not being set up properly.Customers not seeing the payment confirmation screen which would leave them wondering if the payment got through successfully.

How to privately test your new payment gateway

Part of the issues described above can be avoided with a gateway properly setup and configured. Most gateways offer ways to test them. A test account is a copy of the actual payment processing but using “ dummy” card data to capture payments which don’t involve real money. This way you can go through the process of checkout without having to spend money.

The process is great for setting up a gateway & making sure all your pages work as intended.

For most gateways the “live” or “production” implementation requires an SSL certificate which might not be available on a staging website.

Code

Payment gateway IDs: how to find them and how to hide them (when you don’t need them)

As you see in the code you can have one or multiple gateways hidden by id. You can find a gateway’s id by going to WooCommerce > Settings in your WordPress admin. In the Settings screen choose the Gateways tab and at the bottom you will see a table with all the available gateways and their id.

When you are done with the testing simply remove or comment out the code and you’ll be good to go having a safely configured & tested new gateway for WooCommerce.

Removing a gateway

If, for any reason, you want to remove a payment gateway – either temporarily or because you’re switching to a new one, the first thing you want to do is disable it.

You can easily do that by navigating to WooCommerce > Settings > Checkout, choosing the gateway from the links at the top. On that screen uncheck the option to have the gateway activated and save your settings.

Now you are safe to also remove the gateway’s WordPress plugin if you want. You can do so by going to the Plugins menu in the WordPress administration area, deactivate & delete the plugin.

Final thoughts

Providing your users a safe and clear payment method is very important because it can be a real deal-breaker. That’s why I think you should always test all the potential pitfalls outline here in a controlled environment before going live with your new payment gateway.

Proper testing of a gateway and really any tiny thing that might affect it, makes it easier for you to relax without getting complaints from clients or customers that money ( an important part of their business! ) isn’t getting through.

Photo by Aidan Bartos on Unsplash

Extending WooCommerce admin product search to use custom fields

This post comes as a follow-up to the wp-admin posts search article. In that article, I was mentioning altering that code to be used for WooCommerce admin product search would be simple!

Turns out, from the comments, that it’s not so straight forward with the way WooCommerce handles search in the latest versions so I found an alternate way to do that.

The objective here is to enable users to search by a meta value, set for products or product variations.

Why the previous version doesn’t work

That’s pretty simple to spot once you look at the way things run. WooCommerce uses a Data Store for its post types ( products, orders, etc ) and the search is also done using a custom function. In this case it’s called ‘search_products’ and it does a custom query which basically returns an array of ids to be used for the results. The query looks like this:

$search_results = $wpdb->get_results(
   // phpcs:disable
   $wpdb->prepare(
      "SELECT DISTINCT posts.ID as product_id, posts.post_parent as parent_id FROM {$wpdb->posts} posts
      LEFT JOIN {$wpdb->postmeta} postmeta ON posts.ID = postmeta.post_id
      $type_join
      WHERE (
         posts.post_title LIKE %s
         OR posts.post_excerpt LIKE %s
         OR posts.post_content LIKE %s
         OR (
            postmeta.meta_key = '_sku' AND postmeta.meta_value LIKE %s
         )
      )
      AND posts.post_type IN ('" . implode( "','", $post_types ) . "')
      $status_where
      $type_where
      ORDER BY posts.post_parent ASC, posts.post_title ASC",
      $like_term,
      $like_term,
      $like_term,
      $like_term
   )
   // phpcs:enable
);

In this query, WooCommerce searches for the search term in the product title, description and the SKU.

The SKU search is interesting as it’s also stored as a meta. We can copy that to use the same structure.

The solution

A quick solution to this is to alter the query later and append the results to the “post__in” variable of the query args. Place this code in your theme’s functions.php or in a custom plugin.

Update: there was a mistake in the initial version, no new array was needed on line 25.

Update #2: Ray asked in the comments and I added a second example which includes the option to search for multiple, comma separated, SKUs at the same time.

This will work and add the results to the admin.

There’s one thing I would look further into. And that’s an attempt to replace the first query done by WooCommerce with a query which also includes the custom meta key we are looking for. This way, you’d end up with one query instead of 2 before the main query.

I don’t expect performance issues though, and this definitely is more update-proof as it doesn’t remove any WooCommerce filters which might change in time.

 

WooCommerce variation data in SearchWP results

SearchWP is, in my opinion, the best solution when it comes to search in your WordPress website. Besides providing a great user experience by actually returning good results, it’s well written and allows some nice extensions.

The situation which I’m going to provide a solution for is this: you have a WooCommerce website with variable products. Those products have some custom characteristics which you want to allow your users to search by.

Let’s go with an even more specific example. Let’s say the products are books, and each book has an ISBN. They are variable maybe in edition.

And you want to allow your customers to search by ISBN.

Indexing the data with SearchWP

If you’re not aware, SearchWP’s system is based on an index. The index goes through all the items/posts in the background and creates a structure which makes searching more efficient. But at the same time it allows you to set priorities for each info.

The first step to index the data properly is to make SearchWP aware of it. In order to do that we’ll need a bit of code added to either functions.php in your current theme or small custom plugin. I always recommend a custom plugin because in case you make a typo the plugin will be deactivated and you won’t kill the whole website.

In order to index the data, I used the hook “searchwp_extra_metadata“. This hook allows you to add extra data to be indexed for each post.

Now for the actual code:

As you can see, this is pretty straight-forward and give you great power on what data to associate to each post. You could also use this for example to associate relevant posts to one another or even data from taxonomies.

Resetting the index

Just adding the code above won’t do anything. You’ll need to go to the WordPress admin, navigate to Settings > Search WP. Select the post type you wish to change ( in our case Products ) and at the bottom click on “Add Custom Field”.

If the code is working properly, you will see the newly added key in that list ( in our case “m_product_isbns” ). Select that field, set its priority and save the settings.

Next, go to the “Advanced” tab and click on Reset Index. When the index finishes re-indexing the data, you’ll have the data properly attached to the products.

Adding custom commissions to WC Vendors

Let’s say for some reason you want to add custom commissions to WC Vendors. The main thing you’ll need is the filter “wcv_commission_rate“.

The way this filter works is that it allows you to add on top of the calculated commission by also passing along the product price.

Use cases & code

Let’s say for example that you want your vendors to cover the credit card commissions. You’d have to check the gateway used for checkout on that specific order and update the commission.

You can see the code below:

Please note that the gateway may not approve of this practice! Always check their terms before doing this.

Another nice use case would be to give vendors different commissions based on the order location. So let’s say that you want to give them more of the sale for countries in which you have less sales.

In this case you’d have something among the lines of:

Notice that in this case, the commission gets added to the initial commission so the vendor actually gets MORE.

Be careful though – if you are going to set options for this, don’t allow users to go over 100%. I would also add a check directly in the function above in such a case.

Another nice use case I just thought of would be to check if the customer is new on the website and give the vendor a bonus like – no commission for the first purchase!

Let me know in the comments if that would be a good idea for a plugin.

Attempting to speed up WooCommerce Subscriptions admin

If you use WooCommerce along with their WooCommerce Subscriptions plugin you will probably notice the site becomes slow after a number of subscribers. As I wrote in a previous article, there are some tricks that may make it faster.

In this article I will present some code which might help with speeding up some pages which use lots of queries.

User’s active subscriptions count

If you ever used the Query Monitor plugin you probably noticed that on the Users list, even when loading 20 users WordPress does many queries ( hundreds ). With WooCommerce Subscriptions enabled there is one query which is especially resource expensive. That’s the query used to populate the “Active subscriber” column. You can go ahead and remove that if you don’t use it because you can also filter the users by the “subscriber” role. So here’s the code to do that:

// remove the subscription column in users table
remove_filter( 'manage_users_columns', 'WC_Subscriptions_Admin::add_user_columns', 11 );
remove_filter( 'manage_users_custom_column', 'WC_Subscriptions_Admin::user_column_values', 11 );

Comments count

Another very expensive query which I found might slow your website down if you have lots of WooCommerce orders ( which when using subscriptions is highly probable ) is the comments count. As you probably know the WooCommerce order notes are stored as comments, on average you have at least 2 notes for each order which results in a lot of comments.

You can prevent that count from taking place in the admin using the code below:

if ( is_admin() ) {
    add_filter( 'wp_count_comments', function( $counts, $post_id ) {
        if ( $post_id )
            return $counts;
        return (object) array(
            'approved'        => 0,
            'spam'            => 0,
            'trash'           => 0,
            'total_comments'  => 0,
            'moderated'       => 0,
            'post-trashed'    => 0,
        );
    }, 10, 2 );
}
// If running WooCommerce, remove their filter so that nothing funky goes down
remove_filter( 'wp_count_comments', array( 'WC_Comments', 'wp_count_comments' ), 10 );

Another quick improvement if you have lots of “processing” orders is to remove the count that shows up in the admin menu ( which is being called on every screen obviously ).

// Remove order count from admin menu
add_filter( 'woocommerce_include_processing_order_count_in_menu', '__return_false' );

WooCommerce subscriptions speed up from external plugin

If you are running a WooCommerce subscriptions website you might avoid front-end delays by using proper caching. But in the admin you will still encounter issues with large databases and not only.

Using the Query monitor plugin I found that the class used by WooCommerce subscriptions to check for deprecated hooks is actually doubling the loading times for some of the pages. The class “WCS_Dynamic_Hook_Deprecator” is adding an action on the “all” hook. The “all” hook should never be used unless really needed ( this is a real case where this is needed ).

Removing the action

The main problem when trying to remove the action is that it is adding using an anonymous function in a class instance:

add_filter( 'all', array( &$this, 'check_for_deprecated_hooks' ) );

As explained in this awesome WordPress Stackexchange answer, because the function is anonymous WP generates a name for it using the _wp_filter_build_unique_id function. This means we can find the hook and remove it using the $GLOBALS variable.

It’s much easier than it sounds when using the function below ( from the same answer mentioned above ):

if ( ! function_exists( 'remove_anonymous_object_filter' ) )
{
   /**
    * Remove an anonymous object filter when a class uses $this
    *
    * @param  string $tag    Hook name.
    * @param  string $class  Class name
    * @param  string $method Method name
    * @return void
    */
   function remove_anonymous_object_filter( $tag, $class, $method )
   {
      $filters = $GLOBALS['wp_filter'][ $tag ];
      if ( empty ( $filters ) )
      {
         return;
      }
      foreach ( $filters as $priority => $filter )
      {
         foreach ( $filter as $identifier => $function )
         {
            if ( is_array( $function)
                 and is_a( $function['function'][0], $class )
                     and $method === $function['function'][1]
            )
            {
               remove_filter(
                  $tag,
                  array ( $function['function'][0], $method ),
                  $priority
               );
            }
         }
      }
   }
}

Add this as a stand-alone plugin or in your theme’s “functions.php” file. Afterwards you can remove the WooCommerce Subscriptions hook using:

// remove the hook deprecator check to speed up woocommerce subscriptions
remove_anonymous_object_filter('all', 'WCS_Dynamic_Hook_Deprecator', 'check_for_deprecated_hooks');

Obviously you can use this function for any other situation in which a class assigns an anonymous function to a hook.

WooCommerce Ajax add to cart custom redirect

If you want to redirect to a custom url after adding a product to cart you can achieve that pretty easily if you don’t have the Ajax functionality enabled. An example of that can be seen here.

What if you use Ajax

Using Ajax that’s a little bit harder to achieve but you can do that using by “fake” throwing an error as that is being handled directly by WooCommerce.

In the WooCommerce add-to-cart.js file you will see this piece of code:

if ( response.error && response.product_url ) {
   window.location = response.product_url;
   return;
}

You can use this to manipulate the behavior by hooking into “woocommerce_ajax_added_to_cart”.

add_action('woocommerce_ajax_added_to_cart', 'm_custom_redirect');
function m_custom_redirect( $product_id ) {
    // add your check if certain product should trigger the redirect
    if ( $product_id == 34 ) {
        // custom redirect url
        $custom_redirect_url = get_permalink(55);
        $data = array(
            'error'       => true,
            'product_url' => $custom_redirect_url
        );
        wp_send_json( $data );
        exit;
    }
}

This can be used in a variety of situations in which you want to suggest another product after a product has been added to the cart.

Hooking into WooCommerce’s checkout JS events

Trying to prevent WooCommerce to submit the checkout form in order to manipulate the data submitted, I found that the plugin uses the jQuery “triggerHandler” function. That’s cool because you can use it to check for your custom data for example.

The actual use

Say you want to validate the form using WooCommerce’s Ajax calls, but prevent the actual order submission so you can add extra steps ( like presenting some up-sell offers ). In this case you can hook with js in a manner similar to the one below:

var checkout_form = $( 'form.checkout' );
checkout_form.on( 'checkout_place_order', function() {
    // do your custom stuff
    checkout_form.append('<input type="hidden" name="m_prevent_submit" value="1">');
// return true to continue the submission or false to prevent it return true; 
});

What you can do next is: allow the validation to go through just before the order is added and trigger a custom notice.

You’ll need to hook into “woocommerce_after_checkout_validation” and check if the hidden input we added previously is present. If it is and no error was previously recorded you can trigger the error.

add_action('woocommerce_after_checkout_validation', 'm_prevent_submission');
function m_prevent_submission($posted) {
    if ( isset($_POST['m_prevent_submit']) && wc_notice_count( 'error' ) == 0 ) {
           wc_add_notice( __( "custom_notice", 'm_example' ), 'error');
// change the data in $posted here
       } 
   }

Afterwards, in Javascript you can check for your custom error using a function like:

$( document.body ).on( 'checkout_error', function() {
    var error_text = $('.woocommerce-error').find('li').first().text();
    if ( error_text=='custom_notice' ) {
        // do your custom js stuff
    }
});

The same logic can be applied for example to changing the cart redirect for specific products, more on that on a future post.