Cart and checkout

Available data

  • $cart = kart()->cart();
  • $cart->add($product, $amount, $set, $variant)
  • $cart->allInStock()
  • $cart->canCheckout()
  • $cart->clear()
  • $cart->complete()
  • $cart->count()
  • $cart->delete()
  • $cart->fix()
  • $cart->formattedSubtotal()
  • $cart->has($product, $variant)
  • $cart->holdStock()
  • $cart->lines()
  • $cart->merge($user)
  • $cart->releaseStock($data)
  • $cart->remove($product, $amount, $variant)
  • $cart->save()
  • $cart->subtotal()

List products in cart

<?php foreach (kart()->cart()->lines() as $line) {
    /** @var \Bnomei\Kart\CartLine $line */
    /** @var ProductPage $product */
    $product = $line->product(); ?>
    <li>
        <a href="<?= $product->url() ?>"><?= $product->title() ?></a>
        <?php if ($line->hasStockForQuantity() === false) { ?>
            <span><?= $product->stock(withHold: true) ?> of <?= $line->quantity() ?>x</span>
        <?php } else { ?>
            <span><?= $line->quantity() ?>x</span>
        <?php } ?>
        <span><?= $line->formattedPrice() ?></span>
        <?php /* <span><strong><?= $line->formattedSubtotal() ?></strong></span> */ ?>
        <div>
            <form method="POST" action="<?= $product->add() ?>">
                <button type="submit" onclick="this.disabled=true;this.form.submit();">+</button>
            </form>
            <form method="POST" action="<?= $product->remove() ?>">
                <button onclick="this.disabled=true;this.form.submit();" type="submit">–</button>
            </form>
            <form method="POST" action="<?= $product->later() ?>">
                <button onclick="this.disabled=true;this.form.submit();" type="submit">⊻</button>
            </form>
        </div>
    </li>
<?php } ?>
  • $line->decrement($amount)
  • $line->formattedPrice()
  • $line->formattedSubtotal()
  • $line->hasStockForQuantity()
  • $line->increment($amount)
  • $line->price()
  • $line->product()
  • $line->product()->stock(withHold:, variant:)
  • $line->product()->title()
  • $line->product()->url()
  • $line->quantity()
  • $line->subtotal()

Add product to cart

<?php
/** @var ProductPage $page */
$product ??= $page;

if ($product->inStock()) { ?>
    <form method="POST" action="<?= $product->add() ?>">
        <input type="hidden" name="redirect" value="<?= $redirect ?? $page->url() ?>">
        <button type="submit" onclick="this.disabled=true;this.form.submit();">Add to cart</button>
    </form>
<?php } else { ?>
    <p><mark>Out of stock</mark></p>
<?php }

Remove product from cart

<form method="POST" action="<?= $product->remove() ?>">
    <button onclick="this.disabled=true;this.form.submit();" type="submit">–</button>
</form>

Buy now

<?php
/** @var ProductPage $page */
$product ??= $page;

if ($product->inStock()) { ?>
    <form method="POST" action="<?= $product->buy() ?>">
        <input type="hidden" name="redirect" value="<?= $redirect ?? $page->url() ?>">
        <button type="submit" onclick="this.disabled=true;this.form.submit();">Buy now</button>
    </form>
<?php } else { ?>
    <p><mark>out of stock</mark></p>
<?php }

Stock and hold

<?= $product->stock() ?>
or with stock in hold
<?= $product->stock(withHold: true, variant: $variant) ?>

Product variants

For all HTML forms (or API calls when using a headless setup) you can optionally forward a variant either combined or with each group. You can use $product->variantGroups() to help you build the HTML select-elements.

<form method="POST" action="<?= $product->add() ?>">
    <input type="hidden" name="redirect" value="<?= $redirect ?? $page->url() ?>">
    <input type="hidden" name="variant" value="color:Red,size:M">
    <button type="submit" onclick="this.disabled=true;this.form.submit();">Add to cart</button>
</form>

<form method="POST" action="<?= $product->add() ?>">
    <input type="hidden" name="redirect" value="<?= $redirect ?? $page->url() ?>">
    <select name="color">
        <option value="Red">Red</option>
        <option value="Sky Blue">Sky Blue</option>
    </select>
    <select name="size">
        <option value="S">S</option>
        <option value="M">M</option>
        <option value="L">L</option>
    </select>
    <button type="submit" onclick="this.disabled=true;this.form.submit();">Add to cart</button>
</form>

Checkout

<p><strong><?= kart()->cart()->formattedSubtotal() ?> +tax</strong></p>
<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>

Merging of guest and customer carts

When an anonymous user logs into their account, the cart and wishlist from their anonymous session will be merged with the data saved on their account. On logout, the cart and wishlist of the current session will be cleared.

Tax

The Kirby Kart plugin does not provide a preview of tax calculations because it does not have access to your customers' locations or shipping and billing addresses. However, once your customer proceeds to checkout and reaches the payment processing stage with your provider, they can enter this information and see the correct tax rate applied, or you roll out a custom solution.

If your provider does not support automated tax calculation, you could query the customer for their shipping information at checkout, calculate taxes yourself, and forward the updated line items to the payment provider.

Abandoned carts

The Kirby Kart plugin does not retain a shopping cart beyond the current PHP session. It cannot send a notification to indicate that a cart has been abandoned after a certain period, such as 8 hours. However, once the checkout process is initiated, your provider will be aware of the checkout session that Kirby Kart started, allowing you to track it there. You may also use a customer ID to provide more insight.

You can also use the hooks triggered by the Kart plugin to track events.

JSON-LD

Adding the following snippet to your dedicated checkout view will help search engines better understand the structure of your shop to output a matching scheme.

<?php snippet('kart/checkout-json-ld') ?>
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.