SureCart unfulfilled order bubble

Tech Articles | May 14, 2025 | Blog, Coding, SureCart, WooCommerce, Wordpress

There was a recent post in the Sure products Facebook page about a designer/developer who had a client that was migrated from Woo to SureCart and was missing the little unfulfilled order bubble, and was there any native way to replicate this? The item to replicate is shown below

Woocommerce order bubble

There is no native functionality to do just this was the official response and rightly so saying you can see it in one of the copious pages very easily which you can, the one things I have liked about SureCart and continue to dive into is just how great the API and the documents are as well as the support at telling you what you need.

My reply on this question was that this should be possible to replicate easily, as you’d just need to query the unfulfilled orders and if greater than 0, display a bubble with the number next to the menu item. The original poster hadn’t done this sort of thing before, so I said when I get the time I’d take a look as I didn’t think it would take long and I wasn’t wrong in this case.

Background Information

First thing I did was use some of what I already know, we are going to need an API key to query the API I’ve not had much luck getting this out of the database assuming its encrypted (its on my ask support questions if this is possible or to get access to it through a function or filter include. So I would just include this in the code for simplicity.

The next thing I already knew was the SureCart menu system is all built under “sc-dashboard” as a slug. So I used some code to target that and find out what orders are called.


if ( ! defined('SC_DEBUG')   )  define('SC_DEBUG',    true);

function sc_dbg($label, $val){
    if ( SC_DEBUG ) {
        error_log(sprintf('[SC DEBUG] %s: %s', $label, wp_json_encode($val)));
    }
}
add_action( 'admin_menu', function() {
    global $submenu;

    // Dump *all* the top‐level slugs so you can see what SureCart actually uses:
    sc_dbg( 'all_submenu_keys', array_keys( $submenu ) );

    // If you know the real slug, dump that specific submenu:
    $slug = 'sc-dashboard'; // try 'surecart', 'sure-cart', 'sure_cart', etc.
    if ( isset( $submenu[ $slug ] ) ) {
        sc_dbg( 'surecart_submenu', $submenu[ $slug ] );
    } else {
        sc_dbg( 'no_submenu_for_' . $slug, true );
    }
}, 999 );

PHP

This returned a block like the below

SC DEBUG] raw_sc_dashboard_submenu: [["Dashboard","manage_sc_shop_settings","sc-dashboard","Dashboard"],["Orders","edit_sc_orders","sc-orders","Orders"],["Products","edit_sc_products","sc-products","Products"],["Coupons","edit_sc_coupons","sc-coupons","Coupons"],["Licenses","edit_sc_products","sc-licenses","Licenses"],["Subscriptions","edit_sc_subscriptions","sc-subscriptions","Subscriptions"],["Affiliates","edit_sc_affiliates","sc-affiliates","Affiliates"],["Customers","edit_sc_customers","sc-customers","Customers"],["Shop","manage_options","post.php?post=100&action=edit","Shop"],["Checkout","manage_options","post.php?post=97&action=edit","Checkout"],["Cart","manage_options","site-editor.php?postId=surecart%2Fsurecart%2F%2Fcart&postType=wp_template_part&canvas=edit","Cart"],["Customer Area","manage_options","post.php?post=98&action=edit","Customer Area"],["Custom Forms","manage_options","edit.php?post_type=sc_form","Forms"],["Settings","manage_options","sc-settings","Settings"]]	

The Surecart API is really well documented so I also already knew when I responded which API call to use

GET https://api.surecart.com/v1/orders?fulfillment_status[]=processing&limit=1
Authorization: Bearer {API_KEY}
PHP

With some exceptions, we will need to make sure we page through orders as it’s possible on a busy store to have more than one page of unfulfilled orders.

We will also need to cache, so this won’t be live as some stores could have 100s, and we wouldn’t want to call those API calls on every load. I’ve settled for it being refreshed every 5 mins, this can be easily adjusted or removed in the commented code below by removing the transienet

The Code

We aim to add a bubble to the orders tab, with the information based on the queries and key above.

Surecart vs woo with 1 unfulfilled order

So we query the orders looking for unfulfilled orders, one page at a time with up to 100 orders. We could then nest the JSON responses, cache them and return them to display them in a badge in Surecart.

<?php
/**
  * Description: Shows a red badge next to β€œOrders” for processing orders via SureCart’s v1
 * Version:     1.0
 */

// β€”β€”β€” CONFIG β€”β€”β€”
// Replace with your real SureCart API key
if ( ! defined( 'SC_API_KEY' ) ) {
    define( 'SC_API_KEY', 'YOUR API KEY HERE' );
}

// Toggle API debugging (logs only the request URL and HTTP status):
if ( ! defined( 'SC_DEBUG' ) ) {
    define( 'SC_DEBUG', false);
}

// β€”β€”β€” FETCH + CACHE β€”β€”β€”
function sc_get_unfulfilled_count(): int {
    // 1) Try cache
    $cached = get_transient( 'sc_unfulfilled_count' );
    if ( false !== $cached ) {
        return (int) $cached;
    }

    $total = 0;
    $page  = 1;
    $per_page = 100;  // fetch 100 orders per request

    do {
        // build URL with pagination params
        $url = add_query_arg( [
            'fulfillment_status[]' => 'unfulfilled',
            'limit'                => $per_page,
            'page'                 => $page,
        ], 'https://api.surecart.com/v1/orders' );

        if ( SC_DEBUG ) {
            error_log( "[SC DEBUG] Requesting page $page: $url" );
        }

        $resp = wp_remote_get( $url, [
            'headers' => [
                'Authorization' => 'Bearer ' . SC_API_KEY,
                'Accept'        => 'application/json',
            ],
            'timeout' => 5,
        ] );

        if ( is_wp_error( $resp ) ) {
            if ( SC_DEBUG ) {
                error_log( '[SC DEBUG] API error: ' . $resp->get_error_message() );
            }
            break;
        }

        $code = wp_remote_retrieve_response_code( $resp );
        if ( SC_DEBUG ) {
            error_log( "[SC DEBUG] HTTP status for page $page: $code" );
        }
        if ( 200 !== $code ) {
            break;
        }

        $body = wp_remote_retrieve_body( $resp );
        $data = json_decode( $body, true );

        // count how many items on this page
        $count_on_page = isset( $data['data'] ) && is_array( $data['data'] )
            ? count( $data['data'] )
            : 0;

        if ( SC_DEBUG ) {
            error_log( "[SC DEBUG] Page $page returned $count_on_page items" );
        }

        $total += $count_on_page;
        $page++;

    } while ( $count_on_page === $per_page );  // if we got a full page, there might be more

    // cache and return
    set_transient( 'sc_unfulfilled_count', $total, 5 * MINUTE_IN_SECONDS ); //remove if you don't want caching.
    return $total;
}


//
// β€”β€”β€” INJECT BADGE β€”β€”β€”
add_action( 'admin_menu', function() {
    $count = sc_get_unfulfilled_count();
    if ( $count < 1 ) {
        return;
    }

    global $submenu;
    if ( ! isset( $submenu['sc-dashboard'] ) ) {
        return;
    }

    foreach ( $submenu['sc-dashboard'] as & $item ) {
        if ( stripos( $item[0], 'orders' ) !== false ) {
            $item[0] .= sprintf(
                ' <span class="awaiting-mod"><span class="count">%d</span></span>',
                $count
            );
            break;
        }
    }
}, 100 );

// β€”β€”β€” STYLING β€”β€”β€”
add_action( 'admin_head', function() {
    echo '<style>
      .awaiting-mod .count { font-weight:600; padding:0 6px; }
    </style>';
} );
PHP

Now the code above works and displays the image below, but it’s not battle tested, and you are likely to need to adjust caching times and even polling numbers based on your site and server, but it does go to show what is possible with an API call and a few known variables and the reason we know so much is because of how good the documentation is.

Surecart unfulfilled order bubble example

You can view a video on the code and the implementation below:

Support the Author

buy me a coffee
Really Useful Plugin Logo
Wpvideobank temp
Appoligies for any spelling and grammer issue. As a dyslexic i need to rely on tools for this they like me are not perfect but I do try my best