Preventing Backorders on Product Variants in Sparkpay using PHP

Discontinued Product with Variants on Sparkpay
If both the Discontinued and Enable variant level inventory boxes are checked, this probably won’t work the way you want it to.

Sparkpay (formerly Americommerce) has an option to prevent backorders on the products you list on your store.  Unfortunately, it has some flaws. For example:

Let’s say one of your items is a t-shirt with 3 sizes: small, medium, and large.  You only have a limited number of each size available, and don’t want to oversell them.  In order to prevent this, you check the Discontinued box on the inventory page. This will change the product status to discontinued once the inventory reaches zero.

The problem with this method is that it is based on product inventory, not variant inventory.  In other words, if you have 3 large, no medium and no small shirts, the medium and small shirts will still show as available to purchase until the total of all 3 variants reaches zero.  This also means that if you sell 3 of the sizes you don’t have, the large shirts will become unavailable as well, even though they are still in stock.

For stores that sell items with limited availability, Sparkpay’s limited ability to prevent overselling of those items can cause a lot of unnecessary customer service problems.  Fortunately, there is a way to correct the issue of product vs. variant inventory using the Sparkpay REST API and a little PHP.

In order for this process to work, however, you’ll need to have 2 order statuses for new orders.  For this example, I’ll be using “Submitted” and “Pending Processing”.  Submitted will be the default status for new orders, and the scripts we’ll be using will look for orders with this status.  Once those orders have been checked, their order status will be changed to Pending Processing.

You will also need to create an access token with, at a minimum, permission to view and change order data, and to view and change catalog data.  Instructions for doing so can be found in the Sparkpay documentation here:

While the following scripts will not prevent overselling altogether, they will help to minimize it.  In other words, we can’t prevent someone from adding 5 small t-shirts to their order when we only have 4, but we can prevent the next person from ordering more.

So let’s get started!  Below is the class we’ll use to connect to the Sparkpay REST API.  Keep in mind, this was originally written when Sparkpay was still Americommerce – hence, all the “AC” prefixes.

Our class contains 3 methods: a constructor, sendGetRequest, and sendPutRequest.  Get and Put are the only 2 request types we’ll be sending, but you could easily add a sendPostRequest method if you needed to send a Post request.

The constructor assigns the access token to the class property token.  You can set this via the constant at the top of the script, or you can pass a token into the constructor which will override the default token set in the constant.  If you plan on using this class for several API functions, and want each function to use a separate token, passing the token to the constructor is the way to go.

Next is the sendGetRequest method, which takes up to 3 parameters.  The first, $resource, is used to identify the resource you wish to access.  This will generally be set to either products or orders.  Next is the $query parameter, which should receive an array containing key=>value pairs for any search criteria in the request.  These array elements will be url-encoded and added as a querystring to the url.  The last parameter, $fields, is used to specify which fields you wish to be returned in your request, and should be passed as an array containing valid field names.

The sendPutRequest method also takes 3 parameters.  However, unlike the sendGetRequest method, which will work with only the resource specified, this method requires all 3 parameters.  The first parameter, $resource, specifies the resource you’re trying to update (products, orders, etc.).  The next parameter is the identifier for the specific object within that resource, and $data holds an array containing the data we will specify to update the object.

You may notice in both the sendGetRequest and sendPutRequest methods, I’ve used a couple of not-so-common elements of PHP, namely the sleep function and the dreaded goto operator.  These come into play in the event of a 429 error, which means that we’ve exceeded our limit on the number of requests sent to the API within a given time frame.  In the case of Sparkpay, this time frame is 10 seconds.  I’ve set the sleep function for 11 seconds for good measure, but you could probably reduce it to 10 without issue.  After the sleep function, I’ve used the goto operator to ensure that we’re sending the same request as the one that timed out.

So that’s our connector.  Now, on to the backorder blocker!  Here’s the class for that:

Here, we instantiate the Ac_rest class, and assign the object to the ACRest property.  From there, everything is done via the runDiscontinuedCheck method.  The logic of this method is fairly straightforward and can be explained like this:

  1. Collect the order id’s of all new orders (indicated by their order status).
  2. Collect the product and variant id’s of all items in those orders that are marked as discontinued and have a quantity on hand equal to or less than zero.
  3. Update the status of those products and/or variants to a discontinued status so that they can no longer be added to a customer’s cart.
  4. Update the status of the new orders so they are not collected the next time the script runs.

There are 3 lines in this class where your statuses may differ from what I’ve used.  The first is line 19, where the status for new orders is specified.  If you’re using something other than Submitted for your status, simply change the parameter passed to the getOrderStatusByName method here.

The second place where your order status may differ is on line 43.  This is the status you’ll use for orders that have been checked for discontinued items.  This way, they won’t be pulled into the process the next time.

The third, and least likely place you’ll need to change the status is on line 130.  This line retrieves the discontinued product status. If you’re using something other than Discontinued (the Sparkpay default) for your discontinued products, you’ll change that here.

The Ac_rest and Backorder_blocker classes contain all the functionality you’ll need to stop excessive backorders from occurring, and the process is launched with just a few quick lines of code as shown in the file below:

Once you have all 3 files available on a public web server, there’s just one more step.  You’ll need to set up a rule in Sparkpay to call Update_discontinued_variants.php whenever a new order is placed or changed.  The instructions for setting up a new order rule can be found here:

For this order rule, you’ll want to use the following settings:

  1. In the “Respond To” section, check the boxes for “A new order is placed or created” and “An existing order is updated”.
  2. For Conditions, you want to test if the order status is equal to “Submitted” or whatever status you used for new orders.
  3. Under Actions, you’ll select “Request Url”, and then enter the Url to call the Update_discontinued_variants.php script.

That’s it!  Now, whenever a new order is placed or updated (and set to a Submitted status), the script will be called to check if any of the items in the order are sold out and should be discontinued.