Stock

Unlimited quantity by default

Unless you have disabled the management of Stocks using the bnomei.kart.stocks.enabled configuration settings, the Kirby Kart plugin will create a content page to manage stocks. It also expects a subpage for each Stock-to-Product relationship.

The plugin assumes unlimited availability for each product without an explicitly defined stock amount and does not track stock changes.

Product Variants

You can also define stock for product variants. The stock for variants coexists with the product's main stock. If a variant has no stock definition, the plugin will fall back to the product's main stock when fulfilling an order. The product page has a method to retrieve the stock for each variant.

Cart and checkout

Your customers can add items in or out of stock to their carts.
Items that go out of stock while in their cart are not removed.
You can check if all items in the cart have stock available before starting the checkout flow with kart()->cart()->canCheckout() like this...

<form method="POST" action="<?= kart()->urls()->cart_checkout() ?>">
    <?php // TODO: You should add an invisible CAPTCHA here, like... ?>
    <?php // snippet('kart/turnstile-form')?>
    <input type="hidden" name="redirect" value="<?= $page?->url() ?>">
    <button type="submit" onclick="this.disabled=true;this.form.submit();" <?= kart()->cart()->canCheckout() === false ? 'disabled' : '' ?>>Checkout</button>
</form>

Wishlist

Your customers can add items in or out of stock to their wishlists.
Items that go out of stock while on their wishlist are not removed.

Stock management in Panel

Within the Kirby CMS Panel, you can create new Stock-to-Product relationships (aka stock pages) and update the stock of existing ones. It will also show when each stock page was last updated. This timestamp might not perfectly correlate to the timestamp of an order since, by default, the Kirby Kart plugin uses a locking queue.

Orders will NOT put stock in hold by default

You can check for enough stock when starting the checkout flow, see above.

Since all successful orders eventually update the stock, even if there is insufficient stock, a successful order processed after that time might still cause the stock to drop below 0. You can enable reserving stock with the bnomei.kart.stocks.hold = 10 config setting, with a number in minutes or false. The Kirby Kart plugin will reserve/hold product quantities when the checkout process begins in 10 minutes or until the order is cancelled/completed. The hold is only enabled for registered and logged-in users.

The hold is not a perfect solution; it just prevents the worst-case scenarios and enables "flash sales" on a first-come, first-served basis.

DANGER: Unless you enforce a maximum quantity per product in the cart, someone could hold all your stock hostage! Kart defaults to bnomei.kart.orders.order.maxapo = 10, and you can set a value per product. Most providers have an additional limit for the number of line items in an order, which is reflected in the Kirby Kart plugin with bnomei.kart.orders.order.maxlpo = 10.

Updating stock with Locking Queue

Kirby is a flat-file CMS. Within each HTTP request, the required content files are read, and in the case of the stock pages, possibly updated as a result of completing an order. This imposes a challenge when handling concurrent HTTP requests.

Concurrent requests occur when a later request starts while another request has not yet finished. This is usually a very, very short amount of time, but depending on the amount of traffic your website is experiencing and how performant it is in general, it could be anything between a few milliseconds and multiple seconds.

In the case of Kirby, this will not lead to race conditions or processes waiting on each other, but simply to the later-processed request overwriting the result of the earlier one.

Let's see this in action:

  • Product A has a stock amount of 7.Customer #1 adds 2x Product A to the cart, starts the checkout and fills out their credentials.
  • Customer #2 clicks a "Buy Now" button for 1x Product A and is sent to the provider checkout and fills out their credentials.
  • Both Customer #1 and #2 complete the payment at roughly the same time. The provider sends two HTTP requests which both are processed by the Kart plugin and they want to change the stock of Product A.
  • Request #1 reads the stock amount for Product A as 7 and wants to write 7-2.
  • Request #2 reads the stock amount for Product A as 7 and wants to write 7-1.
  • Let's assume #1 finishes before #2 it will write 5 but that will get overwritten soon after with the result of #2's 6. Or the other way round with #2 first 6 and #1 later resulting in a stock of 5.

As you can see, neither result is correct; it should have been 7-2-1=4. The only solution for handling truly concurrent requests is to put the changes in a locking queue and process them independently of the original requests while strictly enforcing that each job in the queue can only be read and processed synchronously.

The implementation of the Kirby Kart plugin ensures the stocks get updated correctly even under a very high server load.

ATTENTION: The locking queue tracks stock in high-traffic HTTP request scenarios, not between order initiation and completion.

Performance

The Kirby Kart plugin caches the stock of all products and their variants for quick retrieval. This cache gets flushed when any stock gets updated.

Screenshots

Kirby Kart is not affiliated with the developers of Kirby CMS. We are merely standing on the shoulder of giants.
© 2025 Bruno Meilick All rights reserved.