Slack App for WooCommerce Reports: A Slack Slash Command That Goes Beyond Real-Time Events For Your WordPress Store

#

In the early days of SpinupWP we used Baremetrics for Stripe metrics and I noticed the SpinupWP team using Baremetrics’ handy Slack commands to get quick and easy reporting insights directly from a Slack channel.

Baremetrics Slack command output

Slack has slash commands which allow you to perform tasks just by typing /something. For example /away sets your status to away saving you a couple of clicks. You can add your own slash commands to do pretty much anything. As the Slack documentation says:

These commands are the starting point for complex workflows, integrations with external services, or even just simple message responses. They’re the knock at your app’s front door that could be the start of a great conversation.

This got me thinking, I’d love to see reporting from WooCommerce about our plugin sales be made available in the same way as Baremetrics does for Stripe data.

So why not learn something new and create a custom slack command?

What’s Currently Available

There are a lot of integrations available for Woo, including one that they’ve built themselves. However, all of the ones I’ve seen only give you real-time notifications of Woo events (new sale, etc.).

Not very helpful when you’ve got a very active store, nor does it help with looking at sales trends, which is what I love about the Baremetrics integration.

So, building from scratch it is.

How to Do It

As a proof of concept, I’m going to create a command that will generate and return a total of sales from the WooCommerce store on deliciousbrains.com, that sells our plugins WP Migrate DB Pro, WP Offload Media, and WP Offload SES. For now, the command will only be able to ask for a total for the current week, month or year.

Before we start, if you want to follow along and get this up and running, you will need a WordPress site running a WooCommerce store, as well as a Slack workspace where you have access to create and install an app.

There are a few parts to this process that I’ll walk you through, but the general concept with Slash Commands is the command and other data is sent from your workspace to an endpoint for the app and that then returns a message to Slack to be displayed to the user.

Creating the Slack App

The first thing to do is to create a Slack app to register the new Slash command and handle the communication of data.

Creating a Slack App modal

Once you’ve given it a name and chosen which workspace it should live in, you will need to choose what the app will do. This is where we want to select ‘Slash Command’

Select Slack App as a Slash Command

Then we need to fill out all the details of the command –

  • Command: is what users will type to invoke the command
  • Request URL: is the URL that will be sent a request with the command and any further text when a user enters the command in Slack (more on that later)
  • Short Description: will be shown in the command list or autocomplete box when the command is typed
  • Usage Hint: if the command takes parameters then you can specify them here. In this case, the period of sales can be specified

Slack Command details modal

Now we have an app set up, we need to create the code to live at the ‘Request URL’ for the app, to receive the command data, crunch our WooCommerce sales numbers and return the total as a message to Slack.

Responding to the Command

When a slash command is invoked, Slack sends an HTTP POST to the Request URL you specified above. This request contains a data payload describing the source command and who invoked it.

The Request URL I’ve defined for the app is an endpoint on deliciousbrains.com, which is of course a WordPress site running our WooCommerce store. We need some custom code on the site to respond to this request, in the form of an mu-plugin file. Because the request sent by Slack is an HTTP POST with certain headers and data in the Body, I didn’t want to mess around using a custom request handler. Instead, it seemed like a great opportunity to use the WordPress REST API.

It turns out registering a new POST endpoint with a callback function is really easy.

add_action( 'rest_api_init', function () {
    register_rest_route( 'dbi', 'slash-woo/', array(
        'methods'  => 'POST',
        'callback' => 'slack_woo_command',
    ) );
} );

Inside the callback function slack_woo_command I need to perform a couple of checks on the WP_REST_Request object passed to the function. As Slack sends the actual command name with the request I’m checking this is /woo as I would expect. (this is useful if you are using the same endpoint for different commands). I’m then checking the WooCommerce plugin is activated, as this command will be useless without it.

function slack_woo_command( $request ) {
    $command = filter_input( INPUT_POST, 'command', FILTER_SANITIZE_STRING );
    if ( empty( $command ) || '/woo' !== $command ) {
        return false;
    }

    if ( ! function_exists( 'WC') ) {
        return false;
    }
}

I need to then validate that this request has actually come from Slack via our Slack app so we can authorize it, and not from another source.

if ( ! defined( 'DBI_SLACK_SECRET' ) || ! verify_slack_signed_secret( $request, DBI_SLACK_SECRET ) ) {
    return false;
}

We need to add the Signing Secret from the Slack App Credentials pane to a constant in the site’s wp-config.php file. This is then used to verify against the signed secret passed in the request, using this function:

function verify_slack_signed_secret( $request, $secret ) {
    $version = 'v0';
    $body    = $request->get_body();

    $timestamp = $request->get_header( 'X-Slack-Request-Timestamp' );
    if ( $timestamp > time() + 300 ) {
        return false;
    }

    $sig_basestring  = "{$version}:{$timestamp}:{$body}";
    $hash            = hash_hmac( 'sha256', $sig_basestring, $secret );
    $local_signature = "{$version}={$hash}";

    $remote_signature = $request->get_header( 'X-Slack-Signature' );
    if ( $remote_signature !== $local_signature ) {
        return false;
    }

    return true;
}

As I mentioned earlier, this command can specify which period of sales it wants the total for. For example, /woo month will just get the total of sales for the current month. This extra text after the command name is passed in the request body, so I need to grab it if it is supplied, check it against a whitelist then convert it to a format WooCommerce will recognize:

$interval = 'week';

$interval_whitelist = array( 'week', 'month', 'year' );

$text = filter_input( INPUT_POST, 'text', FILTER_SANITIZE_STRING );
if ( $text ) {
    $parts = explode( ' ', $text );
    if ( in_array( trim( $parts[0] ), $interval_whitelist ) ) {
        $interval = $parts[0];
    }
}

if ( 'week' === $interval ) {
    $interval = '7day'; // Backwards compatibility with WooCommerce’s reporting code
}

Next, I need to get WooCommerce to generate the sales total for me, for that period. Luckily, there are some reporting classes that exist so I don’t need to worry about understanding the Woo data model and executing MySQL queries. It’s always a good idea to use plugin APIs where possible, as underlying code and data models can change often.

Here’s my very simple function to retrieve the sales total I need:

function get_woo_orders_total( $interval ) {
    include_once( WC()->plugin_path() . '/includes/admin/reports/class-wc-admin-report.php' );
    include_once( WC()->plugin_path() . '/includes/admin/reports/class-wc-report-sales-by-date.php' );

    $report = new WC_Report_Sales_By_Date();

    $report->calculate_current_range( $interval );

    $report_data = $report->get_report_data();

    return $report_data->net_sales;
}

Then all I need to do is form a message with the total and return it at the end of my request callback:

$total = get_woo_orders_total( $interval );

return array(
    'text' => sprintf( '*Sales Report* This %s: *%s*', ucfirst( $interval_name ), html_entity_decode( get_woocommerce_currency_symbol()) . number_format( $total ) ),
);

Here’s the complete mu-plugin code which you can install on your site and connect to a Slack App you have created yourself, using the signing secret defined in the constant.

Take a look at what the command output looks like in Slack (the staging version of our site doesn’t have sales for this month):

Slack Slash Command output

It’s using the default image Slack assigns to apps, but that can easily be customized in the app settings. There’s a lot more that can be done to make this command even better.

Improvements

This is a very simple proof of concept but it could be taken much further to turn it into a really useful command. I would like to add support for filtering by plugin, eg. /woo month wp-migrate-db-pro. This would just be some extra command text parsing and using the plugin slug as an argument in the WooCommerce report query.

Baremetrics gives you the previous period data and the difference between them, color coding increases and decreases, as well as a celebratory emoji if the numbers have increased by a certain amount. It would be great to add the same insight, with extra styling to the message and wouldn’t take too much extra work.

If we wanted to take this app further and release it to other WooCommerce users then we would need to distribute the Slack app and allow other users to install it using OAuth. If that’s something you’re interested in us doing, let us know in the comments.

Wrapping Up

I really enjoyed creating my first Slack App, it gave me a good excuse to use the REST API and dive into WooCommerce’s reporting classes, as well as producing a helpful Slash Command to present sales reporting right from within Slack, without having to login to our ecommerce site and perform a bunch of clicks.

Have you created a Slack app or Slash Command before? Have you got any tips or tricks? Let us know in the comments below.

About the Author

Iain Poulson

Iain is a WordPress and PHP developer from England. He builds free and premium plugins, as well as occasionally blogging about WordPress. Moonlights as a PhpStorm evangelist.