Custom Order Processing Plugin for WooCommerce

WooCommerce, built as a plugin for WordPress, is an incredibly powerful software tool on its own. The front end is well done, and you will find it familiar if you’ve used WordPress before. But when you want to do something beyond the essential functionality, it can be more than a little frustrating to figure out the right way to do it. Multiple versions, deprecated functions, and a new REST API that makes things simpler, but way less convenient, can make searching the web for answers take forever.

I was recently tasked with building a custom plugin to integrate a WooCommerce shop (let’s call the shop Roadrunner Books) with a fulfillment house (we’ll call them Acme Fulfillment). Acme had a bare-bones API and no pre-existing WooCommerce integration, so my job was to:

  1. Intercept a paid WooCommerce order on Roadrunner’s website
  2. Determine if the product(s) purchased needed to be sent to Acme for shipping
  3. Send the products and the shipping details to Acme

This sounds fairly simple, but add to the mix that RoadRunner only needed to send certain items to Acme (just books) and all eBooks and other products were fulfilled elsewhere. Plus, their store utilized Variable products and Product Bundles. And, the response from Acme’s API needed to be saved to WooCommerce so Customer Service representatives could see the Acme Order Numbers.

Step 1: Build the Plugin

Making a WordPress plugin is not as intimidating as it seems. You need a few required elements, and the rest is just like any other custom WordPress code that you might put into the functions.php file. Plugins don’t actually have to have a front-end in the WordPress Dashboard or be listed in the Plugin shop. Custom plugins are just a cleaner, simpler way to include custom code into your WordPress website.

Start With a Unique Name

If you use a name that already exists in the WordPress plugin shop, your plugin will trigger automatic updates from THAT plugin, overwriting your plugin if someone clicks “update.” To avoid this, prefix your plugin’s name with a sensible term or initials. In my case, I will use “rd” for Roadrunner, and name my plugin “rd-order-fulfillment” so I can easily tell its purpose.

Create a Folder and File With That Unique Name

Add the Required Header

You can also a bit of security to your plugin with this line of code, to keep folks from traversing your plugin with bots:

Install Your Custom Plugin

If your plugin code created above is in the wp-content/plugins directory, you should be able to log into your store and see your new plugin listed. Activate it so that you can test using it.

Step 2: Catch the Orders with a WooCommerce Hook

Now that we have a plugin, we should have it do some stuff, right? Since we want to run our plugin when someone successfully completes an order, we need to use a WooCommerce hook to catch the process. There are many points in the order process we can hook into, but we want to grab data when a payment is complete: we don’t want to ship prodto ucts unless we have accepted payment from our customer.

The hook will call our function, and from there we can access the order object’s details. Here are some fields we will need to tell Acme where to send the products, and how to ship them:

Step 3: Working with Variable Products and Bundles

Acme needs product SKU values to fulfill orders. In some stores, that’s as simple as $sku = $product->get_sku();, but RoadRunner utilizes variable products (where there are multiple options for a product, like a paperback and eBook version of one book) and Product Bundles (a custom WooCommerce extension that lets you put groups of products together and offer them for a set price, like 3 books all about the Sonoran Desert as a “Sonoran Study Bundle”). Acme doesn’t know about our variable products or bundles—they only need to know what actual products need to be shipped to a customer. That means we need to find the right SKUs to send.

Annoyingly, the Product Bundles plugin adds our bundles to our order twice: once as the Bundle, and then second as each individual item. You’d think we need to ignore the bundle and just process the individual items, but because we need the details of the bundle as a line item, we actually get the most accurate information from traversing the bundle instead, and then ignoring the individual items as we get to them in the larger loop.

Traversing the bundle

Then we need to check each bundled item for its variation id. If it has a variation_id, then that is the SKU we want to use. Otherwise, the product’s SKU is the right (and only) one.

What about the singleton (non-bundled) products? Those will be processed as the “else” in the if($is_bundle){ call above. We just need to make sure the item is NOT a bundle child (since those are dealt with in the loop above).

Step 4: Filtering Products by a Custom Attribute

Some of our products are not fulfilled by Acme—our eBooks, for instance, are delivered to the customer with their order confirmation page and email—so we do not want to send those product SKUs to Acme. We need to filter our products by an attribute, and we have a custom attribute called “Format” that is perfect for this task.

We have three Formats of products in RoadRunner’s WooCommerce store: Paperback, Hardcover, and eBook. Only the Paperback and Hardcover books should be sent to Acme’s API. We need to access the custom attribute “Format” to determine if we send or not for each product. In our case, since we have to run through the products in two ways (bundled & not-bundled) already, I made the Format check a separate function to be called. If the function returns false, then we know the product is an eBook and we should not send it to Acme.

Function being called on each product:

Function to filter by custom attribute, Format in this case:

Step 5: POST Products to Acme

We could use a regular PHP cURL to send our data to Acme, but WordPress has a nice little function that keeps things neat and tidy. We need to check before we do anything, however, that we actually have products to send to Acme.

Then, with this API, we need to authenticate using custom headers.

Next, we can send our formatted data.

Step 6: Save Acme’s Details in WooCommerce’s Order Object

In the response we get from Acme above, we used the PHP Error Log to see the Acme Order Number from their system for successful POSTs. We should record this value so that if any Customer Service issues arise, we can refer to this Order Number in the WooCommerce Order interface to ask Acme questions. We can use the update_meta_data() function to do this:

Now, in the Order page, our Customer Service reps can easily see the Acme Order ID:

Final Step: Putting it all Together

WooCommerce’s powerful engine can make for a complicated and ever-evolving code base. I hope this tutorial for a custom fulfillment integration helps you better understand the way WooCommerce works on the backend, and maybe saves you some time if this is the kind of project you’re working on right now. Meep meep!

Leave a Reply

Your email address will not be published. Required fields are marked *