{
    "version": "https://jsonfeed.org/version/1.1",
    "title": "KahunaCart news",
    "description": "KahunaCart release notes, feature updates and development news.",
    "home_page_url": "https://kahunacart.rhuk.net/news",
    "feed_url": "https://kahunacart.rhuk.net/news.json",
    "language": "en",
    "items": [
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-1-6-0",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-1-6-0",
            "title": "KahunaCart 1.6.0: Core block architecture improves theming",
            "summary": "The big change in KahunaCart 1.6 is under the hood. Every storefront page is now made of blocks, and a theme only overrides the ones it wants to change. We built nine themes on it while we were at it. Three of them come free with every license and the other six are business themes you can buy, and all nine have a live demo you can poke around in.",
            "content_html": "<p>I'm excited to announce <strong>KahunaCart 1.6.0</strong>! It's a big one, with 49 changes (21 new features, 17 improvements and 11 fixes), and <strong>1.6.1</strong> followed later the same day with one more. The changelog only tells half the story though, because most of my time on this release actually went into themes.</p>\n<p>Up until now, if you wanted your store to look different, you had to copy KahunaCart's templates into your theme and edit them there. That's fine until the next release fixes something in checkout and your theme keeps serving its own old copy of the page. Our own <strong>Kahuna</strong> theme had twelve of these copied templates, and I was going through them by hand every time we released. I really didn't want everyone else who builds a theme to be stuck doing the same thing.</p>\n<h2 id=\"storefront-pages-built-from-blocks\">Storefront pages built from blocks<a href=\"#storefront-pages-built-from-blocks\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>With 1.6, every storefront page is split up into named <strong>blocks</strong>. That covers checkout, the cart, the product page, the order page, product listings, categories and search. Each region of a page gets its own block, and a layout block on each page arranges those regions into columns. Your theme's checkout template now just extends KahunaCart's and overrides the blocks you actually want to change. Everything else still comes from KahunaCart, so when we fix something in a later release, your store picks it up without you touching the theme.</p>\n<p>The little pieces that show up all over the store have moved into their own files too: the <strong>product card</strong>, the <strong>cart line</strong>, the <strong>receipt line</strong> and the <strong>delivery method</strong>. Change the product card once and every card in the store follows, even the ones the filters load in on the fly.</p>\n<p>Themes also get some new settings (type, card shadows, the gap between cards, the focus ring, and the size card pictures get cropped to), plus a handful of helpers for things a theme used to have to figure out on its own:</p>\n<ul>\n<li>Whether a product card can add straight to the cart</li>\n<li>A category's picture at any size</li>\n<li>A product's releases and a pre-order's date</li>\n<li>A customer's downloads</li>\n</ul>\n<p>If you've already got a theme with copied templates, don't worry. It still works exactly like it did before.</p>\n<p>If you build themes, check out <a href=\"/docs/store/theming/theme-blocks\"><strong>Theme blocks</strong></a> for the full list. There's a test in KahunaCart that checks that list against the actual templates, so it won't drift out of date.</p>\n<h2 id=\"three-free-themes\">Three free themes<a href=\"#three-free-themes\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Once the blocks were in place, we built three themes on them, and all three are <strong>free with every KahunaCart license</strong>:</p>\n<ul>\n<li><strong><a href=\"/add-ons/themes/kahuna\">Kahuna</a></strong> is the warm, friendly theme you've seen on the demo store, rebuilt on blocks.</li>\n<li><strong><a href=\"/add-ons/themes/slate\">Slate</a></strong> is crisp and square, with sharp lines and plenty of white space.</li>\n<li><strong><a href=\"/add-ons/themes/pebble\">Pebble</a></strong> is soft and rounded, with pill buttons and gentle shadows.</li>\n</ul>\n<figure>\n<a href=\"/news/kahunacart-1-6-0/free-themes.webp\" data-lightbox hx-boost=\"false\"><img src=\"/news/kahunacart-1-6-0/free-themes.webp\" alt=\"The home page of the KahunaCart demo store in Kahuna, Slate and Pebble, side by side\" width=\"2892\" height=\"612\"></a>\n<figcaption>The same demo store in Kahuna, Slate and Pebble.</figcaption>\n</figure>\n<p>Each one has full light and dark designs, and they come with the extra pages a real shop needs: a home page, an about page, a journal and bookings. Install them from the admin with the license key you already have, or from the command line with <code>bin/gpm install slate</code>. You don't need to buy anything, and you can switch between them whenever you feel like it.</p>\n<p>Here's the same product page in all three. Slate and Pebble don't copy a single KahunaCart template to get there, they just extend them. Pretty cool, right?</p>\n<figure>\n<a href=\"/news/kahunacart-1-6-0/one-product-three-themes.webp\" data-lightbox hx-boost=\"false\"><img src=\"/news/kahunacart-1-6-0/one-product-three-themes.webp\" alt=\"The Reef Runner Tee product page in Kahuna, Slate and Pebble\" width=\"2892\" height=\"612\"></a>\n<figcaption>One product page in three themes, with nothing copied.</figcaption>\n</figure>\n<h3 id=\"home-pages-made-of-modules\">Home pages made of modules<a href=\"#home-pages-made-of-modules\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h3>\n<p>The home and about pages in every theme are now built from <strong>modules</strong>. That's things like a hero, a row of categories, featured products, a band of text with a picture, and whatever else the theme offers. You add them in the admin, drag them into the order you want and switch them off when you don't need them, with picture and icon pickers where they make sense. No more editing a template just to move a section around.</p>\n<figure>\n<a href=\"/news/kahunacart-1-6-0/home-modules.webp\" data-lightbox hx-boost=\"false\"><img src=\"/news/kahunacart-1-6-0/home-modules.webp\" alt=\"The modules of a home page listed in the Grav admin, with a publish switch and a drag handle on each\" width=\"1404\" height=\"507\"></a>\n<figcaption>A modular home page in the admin. Drag to reorder, or switch a module off with one click.</figcaption>\n</figure>\n<p>The module list in the page editor is new in <strong>Admin 2.1.40</strong> and <strong>API 1.0.57</strong>, which went out this week as well. They also bring icon sets, so a theme can put its own icons in the icon picker.</p>\n<h2 id=\"six-business-themes\">Six business themes<a href=\"#six-business-themes\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Building the free themes on blocks went so smoothly that we just kept going, and ended up with <strong>six business themes</strong>. Each demo is set up as an example business with its own photography, fonts and colors, and a home page that puts its add-on to work right there on the page, so you can try it the way a visitor would.</p>\n<figure>\n<a href=\"/news/kahunacart-1-6-0/business-themes.webp\" data-lightbox hx-boost=\"false\"><img src=\"/news/kahunacart-1-6-0/business-themes.webp\" alt=\"The home pages of Vellum, Boulder, Aegean, Graphene, Zoom and Encore\" width=\"2892\" height=\"1224\"></a>\n<figcaption>Vellum, Boulder and Aegean on top. Graphene, Zoom and Encore below.</figcaption>\n</figure>\n<ul>\n<li><strong><a href=\"/add-ons/themes/vellum\">Vellum</a></strong> is calm and photo-led, with black and white photo bands. Its demo is a hair salon taking bookings for cuts and color.</li>\n<li><strong><a href=\"/add-ons/themes/boulder\">Boulder</a></strong> is dark and loud, with big type and a booking panel right over the hero. Its demo rents out mountain bikes by the half day, and sells them too.</li>\n<li><strong><a href=\"/add-ons/themes/aegean\">Aegean</a></strong> is big photos and calm serif type, with a date picker on the home page. Its demo is a holiday house taking nightly stays.</li>\n<li><strong><a href=\"/add-ons/themes/graphene\">Graphene</a></strong> is dark with a mint accent and a terminal-style changelog. Its demo is a software studio selling apps with license keys.</li>\n<li><strong><a href=\"/add-ons/themes/zoom\">Zoom</a></strong> is bright and fun, all electric blue, with the day laid out in lanes. Its demo is a pet shop that does grooming.</li>\n<li><strong><a href=\"/add-ons/themes/encore\">Encore</a></strong> is colorful and joyful, with a preview player and the tour dates on the home page. Its demo is a band selling records, downloads and printed-to-order shirts.</li>\n</ul>\n<p>Those businesses are only examples though, and nothing in a theme ties it to the one in its demo. Vellum would work just as well for a florist or a studio, and Encore suits anyone with records, merch and dates to sell. Swap in your own words, pictures and colors. Every demo has a color panel, so you can try a few out before you buy.</p>\n<p>Each business theme is <strong>$99</strong>, a one-time purchase that goes onto your KahunaCart license key just like any other add-on, and updates are included with your site renewal. If you don't already have the add-ons a demo uses, there's a bundle for each one for more savings:</p>\n<ul>\n<li><strong>Vellum + Bookings</strong>, <strong>Boulder + Bookings</strong> and <strong>Zoom + Bookings</strong>: $209 each</li>\n<li><strong>Aegean + Bookings + Rentals</strong>: $279</li>\n<li><strong>Graphene + License Keys</strong>: $189</li>\n<li><strong>Encore + Print on Demand + Newsletters</strong>: $319</li>\n<li><strong>All six business themes</strong>: $399</li>\n</ul>\n<p>Every theme installs through the <strong>Grav package manager</strong> with your key, but you can also grab each one as a <strong>ready-to-run store</strong>. That's a complete Grav site with the theme, KahunaCart and the store setup from its demo (the catalog, services, staff, hours and rates), minus the demo's customers and orders. Your key gets written into the package when you download it, and you'll find them in the <strong>Downloads</strong> tab of your <a href=\"/shop/account/downloads\">KahunaCart account</a>.</p>\n<p>The packages are also a great way to learn how to set up KahunaCart. Open one up and you can see exactly how its products, options and pages are put together. And if you'd rather not start from scratch, it's a good place to begin.</p>\n<p>If your key doesn't cover one of the add-ons a demo uses, that add-on is left out of your package. Any module that needs it will tell you it's missing, and you can install the add-on later or just switch that module off.</p>\n<h2 id=\"new-demos\">New demos<a href=\"#new-demos\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>We've also rebuilt <a href=\"https://demo.kahunacart.com/\">demo.kahunacart.com</a> from scratch. It now opens on a landing page that lists every demo, with notes at each stop on what to try and how to sign in.</p>\n<figure>\n<a href=\"/news/kahunacart-1-6-0/demo-tour.webp\" data-lightbox hx-boost=\"false\"><img src=\"/news/kahunacart-1-6-0/demo-tour.webp\" alt=\"The demo.kahunacart.com landing page with six tour stops: the surf shop, the admin, bookings, the front desk, Point of Sale and the nine themes\" width=\"2160\" height=\"863\"></a>\n<figcaption>demo.kahunacart.com: take the tour, or just start clicking.</figcaption>\n</figure>\n<ul>\n<li><strong>The surf shop</strong> is still our core demo store, now living at <a href=\"https://demo.kahunacart.com/store\">/store</a>, with a bar across the top that lets you flip between Kahuna, Slate and Pebble as you browse.</li>\n<li><strong>The admin</strong> lets you open every screen, and nothing you change gets saved.</li>\n<li><strong>Bookings</strong>, the <strong>front desk</strong> and <strong>Point of Sale</strong> each get a demo of their own.</li>\n<li><strong>Each business theme</strong> has its own complete demo store: <a href=\"https://demo.kahunacart.com/vellum/\">/vellum</a>, <a href=\"https://demo.kahunacart.com/boulder/\">/boulder</a>, <a href=\"https://demo.kahunacart.com/aegean/\">/aegean</a>, <a href=\"https://demo.kahunacart.com/graphene/\">/graphene</a>, <a href=\"https://demo.kahunacart.com/zoom/\">/zoom</a> and <a href=\"https://demo.kahunacart.com/encore/\">/encore</a>. Book a haircut, rent a bike or pre-order a record.</li>\n</ul>\n<p>The whole site resets every hour, nothing is charged and no emails go out, so go ahead and click on anything you like.</p>\n<h2 id=\"also-in-1-6\">Also in 1.6<a href=\"#also-in-1-6\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>A couple of these came straight out of building Encore. A band wants to sell a record as an LP, a CD and a download, and it wants to start selling before the release date. KahunaCart couldn't do either of those before. Now it can.</p>\n<p><strong>Pre-orders.</strong> Give a product an <strong>Out on</strong> date and it sells as a pre-order until that day. The button changes to <strong>Pre-order</strong> with the date beside it, and the cart, receipt and emails all say when it's out. Payment and stock work the way they always have, so a limited pressing can still sell out. Download links are handed out at purchase but only open on release day, and on the day every waiting buyer gets an \"It's out\" email with their links. After that it's an ordinary product again, and you don't have to change a thing. See <a href=\"/docs/store/products#pre-orders\">Pre-orders</a>.</p>\n<p><strong>Digital variants.</strong> Tick <strong>Digital</strong> on any variant of a physical product and that variant won't ship anything or ask for an address. So a record in amber vinyl, black vinyl, CD, cassette and download can finally be one product. Physical products can carry files now too, which means you can give every LP buyer the download, or only the buyers of one variant. See <a href=\"/docs/selling/digital-products#digital-variants\">Digital variants</a>.</p>\n<p><strong>Whole Amounts.</strong> Flip one switch and the storefront shows <code>$55</code> instead of <code>$55.00</code>. Prices with cents keep them, and prices shown side by side always use the same format, so you'll never get <code>$55.00</code> sitting next to <code>$49</code>. Invoices, emails and reports still show full amounts.</p>\n<p><strong>Cart, bag or basket.</strong> Pick the word your shop uses and every shopper-facing string follows along, from the buy button right through to \"Your bag is empty.\"</p>\n<p><strong>Coupons from an ad.</strong> Put an ad's campaign tag on a coupon and anyone who arrives from that ad gets the code applied for them. You don't have to edit an ad that's already running and send it back through review just to add a code. See <a href=\"/docs/money/coupons#codes-that-apply-from-an-ad-campaign\">Codes that apply from an ad campaign</a>.</p>\n<p>And a bunch of smaller goodies:</p>\n<ul>\n<li><strong>Gotify notifications</strong> for store owners who run their own <a href=\"https://gotify.net\">Gotify</a> server, and the channel picker is now a set of cards.</li>\n<li><strong>Account tabs are hidden when a customer has nothing in them</strong>, so someone buying from a software store doesn't see an empty Bookings tab.</li>\n<li><strong>Rating a product is now a row of stars</strong> that light up as you pick, and it still works from the keyboard.</li>\n<li><strong>Dates shoppers see follow your store's language</strong>: month first in US English, day first in British English.</li>\n<li><strong>Deposits are clear from the bag right through to the invoice</strong>, showing what's due today, what's left and when it's due.</li>\n<li><strong>Payment methods and collection points show as cards at checkout</strong>, and picking a collection point hides the delivery methods.</li>\n<li><strong>1.6.1 adds review stars to product cards</strong> for any theme that wants them, using your approved reviews. Zoom already shows them.</li>\n</ul>\n<h2 id=\"add-ons\">Add-ons<a href=\"#add-ons\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<ul>\n<li><strong>Subscriptions</strong> 1.1.10, <strong>Bookings</strong> 1.1.12 and <strong>Licenses</strong> hide their account tabs when a customer has nothing in them. Bookings and <strong>Rentals</strong> 1.0.8 also fill in a signed-in customer's name, email and phone on the booking form.</li>\n<li><strong>Licenses</strong> 1.1.4 writes the right key into a theme's ready-to-run package, and leaves out any add-on that key doesn't cover.</li>\n<li>Every add-on now ships with its own <code>LICENSE</code> file and links to the <a href=\"/license\">KahunaCart License</a>.</li>\n</ul>\n<p>Update from the admin or with <code>bin/gpm update</code>, then give Slate or Pebble a spin on your own store. I'd love to see what you build with the blocks, so come and show me on <a href=\"https://discord.gg/vMs3UTEAg4\"><strong>Discord</strong></a>!</p>\n<p>— Andy</p>",
            "date_published": "2026-10-23T08:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.6.0"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-point-of-sale-available",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-point-of-sale-available",
            "title": "KahunaCart Point of Sale",
            "summary": "Our new Point of Sale add-on turns a tablet, a laptop or a phone into a till on your own site. Take cash or a card on a Stripe, Square or SumUp reader, take returns, and count the drawer at closing. Every sale is an ordinary KahunaCart order, on the same stock as your website.",
            "content_html": "<p>Plenty of shops don't only sell online. There's a stall at the Saturday market, a counter at the front of a studio, a table at a convention, and a website as well. That usually means two systems: a till app with its own stock count and its own list of sales, and the online store. And somebody has to make the two agree at the end of the week. I wanted one stock and one set of orders for all of it. <strong>KahunaCart Point of Sale</strong> is available today.</p>\n<h2 id=\"a-till-on-your-own-site\">A till on your own site<a href=\"#a-till-on-your-own-site\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>POS runs at <code>/pos</code> on your own site, behind your site's own sign-in. Open it on a tablet, add it to the home screen, and it opens full screen like an app. It works on a laptop too, and on a phone in a pinch.</p>\n<p>Every sale it rings up is an ordinary <strong>KahunaCart</strong> order. It takes stock from the same count as your website, uses your prices, sales, coupons and customer groups, and turns up in your reports next to everything else you sell. Tax is worked out at the address of the place you're selling from.</p>\n<p>Scan a barcode, or tap a product on the favourites grid. Give a staff discount with a reason when a box is dented. Attach a customer if they want their store credit or an emailed receipt. Park a sale while someone runs back to the car for their wallet.</p>\n<h2 id=\"cash-cards-or-some-of-each\">Cash, cards, or some of each<a href=\"#cash-cards-or-some-of-each\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The Pay screen shows what's owed in big type and a button for each way to pay. <strong>Cash</strong> shows the change large. <strong>Cards</strong> go to a card reader: a <strong>Stripe</strong> WisePOS E or Reader S700, a <strong>Square Terminal</strong> or a <strong>SumUp Solo</strong>, each through that provider's add-on and your own account with it. The till sends the amount, the customer taps, and the payment lands on the order.</p>\n<p>You can split a sale any way you like: put fifty on the card and take the rest in cash. There's also store credit, gift cards, and a <strong>pay link</strong> drawn as a QR code for the customer to pay from their own phone.</p>\n<p>I care a lot about the money being right, so the till never guesses. A double tap or a dropped connection records one payment, not two. And if a reader goes quiet halfway through, the till shows the payment as checking and keeps asking the provider until it knows.</p>\n<p>No reader yet? The test provider has <strong>simulated readers</strong>, so you can rehearse a whole day of sales before you buy any hardware.</p>\n<h2 id=\"returns-at-the-counter\">Returns at the counter<a href=\"#returns-at-the-counter\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Every till receipt carries its order number as a barcode. Scan it, pick what's coming back, and the till shows exactly what it will refund and to what before you press the button. Refund to the card, in cash from the drawer, or as store credit. Anything damaged is refunded without going back into stock. And a customer who bought from your website can bring it back to the shop too.</p>\n<h2 id=\"one-tablet-a-few-people\">One tablet, a few people<a href=\"#one-tablet-a-few-people\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Most counters have one tablet and a few people behind it. So the tablet stays signed in, and whoever is serving taps their name and types a <strong>PIN</strong>. Everything they do is recorded under their name and checked against their own permissions. Someone who can't give refunds can't refund, even on the manager's tablet. When it's needed, the manager types their PIN right there to approve that one return or that one big discount, and the approval is written on the order.</p>\n<h2 id=\"closing-time\">Closing time<a href=\"#closing-time\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Open a shift with a float. Record cash going out for the bank or coming in from it. At closing, count the drawer by notes and coins. The till asks for your count first and only then shows what should be there, so nobody counts towards a number. Then it freezes the <strong>Z report</strong>: sales, payments by type, refunds, discounts, takings per person, and the drawer sums. If the drawer is out by more than your limit, you get a message.</p>\n<p>All of it lives in a new <strong>Point of sale</strong> section in your KahunaCart admin, with every register, every shift and its report, and sales reports by register, staff, location or way of paying.</p>\n<h2 id=\"bookings-and-balances\">Bookings and balances<a href=\"#bookings-and-balances\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>If you run <strong>Bookings</strong>, a booking that still owes money now has a <strong>Take payment</strong> button on the front desk. It opens the till with that order ready to pay. When it's paid, <strong>Book next visit</strong> takes you straight back to the desk to book the customer's next appointment. The till's menu can take what any order still owes, such as an order placed online and paid in the shop.</p>\n<h2 id=\"what-it-doesn-t-do-yet\">What it doesn't do yet<a href=\"#what-it-doesn-t-do-yet\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>I'd rather you hear this from me than find out at the market.</p>\n<ul>\n<li><strong>No offline mode.</strong> The till needs a connection to your shop. When the connection drops it says so and sells nothing it can't record. Put the tablet on mobile data if the stall's wifi is shaky.</li>\n<li><strong>No cash drawer kick or customer display.</strong> Receipts print through the browser's own print dialog, to any printer the device can use, laid out for 80 mm paper. The till doesn't send commands to a printer, a drawer or a second screen.</li>\n<li><strong>Not every reader.</strong> Bluetooth and USB readers and Tap to Pay on a phone aren't supported yet.</li>\n</ul>\n<p>The <a href=\"/docs/point-of-sale/hardware\">hardware page</a> has the full list.</p>\n<h2 id=\"get-started\">Get started<a href=\"#get-started\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>POS needs <strong>KahunaCart 1.5.7 or later</strong>, the Login and API plugins, and a pickup location for each place you sell. It's a one-time purchase for your site, and updates are covered by your site's single renewal. Card readers come from your payment provider, and so do the card fees. The add-on doesn't add one.</p>\n<p>Add it to your licence in the <a href=\"/shop/product/kahunacart-point-of-sale\">store</a>, install it through Grav's plugin manager, and follow the <a href=\"/docs/point-of-sale/getting-started\">getting started guide</a>. The <a href=\"/add-ons/point-of-sale\">add-on page</a> has screenshots of every screen.</p>\n<p>If you sell anywhere besides your website, I'd love to hear how the till works for you. Come and tell us on <a href=\"https://discord.gg/vMs3UTEAg4\">Discord</a>.</p>\n<p>Andy</p>",
            "date_published": "2026-10-14T16:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.0.0"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-affiliates-available",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-affiliates-available",
            "title": "KahunaCart Affiliates",
            "summary": "Our new Affiliates add-on lets bloggers, podcast hosts and happy customers sell for you. Each partner gets a link, a coupon code or both, their sales are credited to them, and one batch pays everyone at the end of the month. No outside service, no spreadsheet.",
            "content_html": "<p>When we moved Grav Premium onto KahunaCart last month, one of the things we carried across was an affiliate deal: a cookie, a welcome banner and a discount code for a hosting partner. It turned out nobody had ever used it, and when we looked at why, the answer was simple. There was nothing behind it. No commission was ever worked out, nothing tracked what the partner had earned, and paying them would have meant a spreadsheet. The usual fix is a monthly fee to an outside service that only sees part of what happened to the order. We'd rather it lived in the store, next to the orders and the refunds. So we built it into KahunaCart. <strong>KahunaCart Affiliates</strong> is available today.</p>\n<h2 id=\"links-coupons-or-both\">Links, coupons, or both<a href=\"#links-coupons-or-both\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Every partner gets a link to your store, a coupon code, or both. The link is your store's address with <code>?ref=</code> and their code on the end. It works on every page, and partners can turn any page of your site into a link of their own. The coupon is an ordinary KahunaCart coupon, so it credits the partner on every order that uses it, whether the customer clicked anything or not.</p>\n<p>That second part matters more than it sounds. A podcast host reads out a code, and the listener types it in a week later on their phone. There's no click to track, and with most affiliate tools that sale is lost. Here it's credited. A partner like that doesn't even need an account on your store: they share their code, you pay them by PayPal or by hand, and they can get a monthly statement by email.</p>\n<p>The referral cookie is listed with the Consent plugin as a marketing cookie. A visitor who says no is remembered for that visit only, or not at all in strict mode, where a coupon is the only way to credit them. The <a href=\"/docs/affiliates/tracking\">tracking docs</a> explain which to pick. The store keeps a count of clicks per partner per day, and nothing about the visitor.</p>\n<h2 id=\"commissions-that-follow-the-order\">Commissions that follow the order<a href=\"#commissions-that-follow-the-order\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Set one rate for the whole program, then give a partner, a category or a single product its own, as a percent or a flat amount. A commission is worked out on what the customer paid for the goods, after discounts. Tax is left out, and so is shipping unless you choose to pay on it.</p>\n<p>If you sell subscriptions with the <a href=\"/add-ons/subscriptions\">Subscriptions</a> add-on, partners earn again when a customer they referred renews. Pay on every renewal, a set number of them, or none.</p>\n<p>Each commission waits out your refund window before it can be paid, 30 days by default. A refund takes back exactly what the refunded items earned. If the partner was already paid, the refund writes a negative amount that comes off their next payout, so nobody has to chase anyone for money.</p>\n<h2 id=\"one-batch-pays-everyone\">One batch pays everyone<a href=\"#one-batch-pays-everyone\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>At the end of the month, build a batch. The preview lists everyone who's owed enough and says why anyone is left out. Partners choose how they're paid:</p>\n<ul>\n<li><strong>Store credit</strong> goes straight into their credit account, ready to spend in your store. Add a bonus for choosing it if you like, since credit costs you no fees.</li>\n<li><strong>PayPal Payouts</strong> sends the money from your own PayPal account. The add-on has its own PayPal connection, so you don't need to take PayPal at checkout.</li>\n<li><strong>By hand</strong>, you pay outside the store and mark each one paid. The batch gives you a CSV for your bank, a PayPal Payouts upload file and a Wise batch file.</li>\n</ul>\n<p>The add-on never holds anyone's money. Every amount is worked out again when the batch is built, so a refund that came in that morning is counted.</p>\n<h2 id=\"tax-details-for-year-end\">Tax details for year end<a href=\"#tax-details-for-year-end\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Partners fill in a W-9 or a W-8BEN in their account. At year end you get each partner's totals, a flag on US partners who reached the 1099-NEC threshold, a CSV in the layout of the IRS IRIS upload, and a statement PDF for each partner. Full tax ID numbers are stored only when your server has its own key, and they're encrypted with it. Without one, a W-9 keeps the last four digits and the signed PDF.</p>\n<p>To be clear about where this stops: KahunaCart collects and totals this information. It doesn't file anything or give tax advice. It gives you the numbers to hand to whoever does.</p>\n<h2 id=\"apply-without-an-account\">Apply without an account<a href=\"#apply-without-an-account\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>A blogger who has never bought from you shouldn't have to make an account just to ask about your program. So anyone can apply from a public form: their name, email, where they'll share your link and how they'd like to be paid. They confirm their email address, and the application waits for you. Nothing else happens until you decide.</p>\n<p>Approve it and KahunaCart makes their customer account, with a link in the welcome email to choose a password. Reject it and nothing is made at all, so spam applications never turn into accounts. If the email already belongs to a customer, approving attaches the partner to that account instead. Customers who are signed in get the same form filled in for them. Each application tells you which of these it is, so you always know whether approving makes a new account.</p>\n<p>You can also pick a customer group for your partners. Everyone you approve goes in it, and anyone who leaves the program comes out of it. Filter <strong>Customers</strong> by that group to see your partners, or lock a page to it for brand assets and news that only partners should see.</p>\n<h2 id=\"partners-help-themselves\">Partners help themselves<a href=\"#partners-help-themselves\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Partners get an <strong>Affiliate</strong> tab in their customer account. They can apply to join, copy their link, see what's waiting and what's been paid, choose how they're paid and fill in their tax form, all without writing to you. They see the order number and date for each commission, never your customer's name or email. You can add banners and text for them to copy, and put a \"Join our affiliate program\" block on any page with your rate and terms.</p>\n<h2 id=\"also-in-the-core\">Also in the core<a href=\"#also-in-the-core\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p><strong>KahunaCart 1.5.2</strong> came out alongside it, with the store credit movement that pays a partner in credit, and two fixes to merging a store bundle into a running store. <strong>1.5.1</strong> came out a few minutes before it: orders paid through Polar, or Stripe under Managed Payments, now show the tax the provider charged on the receipt, the invoice and the customer's order history, so the total matches what the customer actually paid.</p>\n<h2 id=\"get-started\">Get started<a href=\"#get-started\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Affiliates needs <strong>KahunaCart 1.5.2 or later</strong> and the store's job worker running. It's <strong>$149</strong>, a one-time purchase for your site, and updates are covered by your site's single renewal.</p>\n<p>Add it to your licence in the <a href=\"/shop/product/kahunacart-affiliates\">store</a>, install it through Grav's plugin manager, and follow the <a href=\"/docs/affiliates/overview\">overview and setup guide</a>.</p>\n<p>If you run a program with it, or you've been putting one off because of the paperwork, I'd love to hear how it goes. Come and tell us on <a href=\"https://discord.gg/vMs3UTEAg4\">Discord</a>.</p>\n<p>Andy</p>",
            "date_published": "2026-10-12T12:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.0.1"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-1-5-0",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-1-5-0",
            "title": "KahunaCart 1.5.0: Take your whole store with you",
            "summary": "Take your store with you. Export everything, or just the parts you pick, into one file you can restore on another KahunaCart site.",
            "content_html": "<p><strong>KahunaCart 1.5.0</strong> is out, with 16 changes: 11 new features, 3 improvements and 2 fixes.</p>\n<p>It started with a question from a store owner. Their shop lived at <code>/shop</code> inside a bigger Grav site, and they wanted it on its own site at its own address. The honest answer was \"copy the files and the database, then delete everything that isn't the shop\". That works. But it's clumsy, and I didn't like that it was the only answer. So 1.5.0 is about your store being yours to take wherever you want.</p>\n<h2 id=\"export-the-whole-store\">Export the whole store<a href=\"#export-the-whole-store\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>There's a new <strong>Import &amp; Export</strong> screen. The <strong>Export</strong> tab lists everything your store holds, and how many of each:</p>\n<ul>\n<li>your settings, plus the webhooks and notification settings that connect the store to other services</li>\n<li>customers, their addresses and customer groups</li>\n<li>products, categories, pictures and digital files</li>\n<li>coupons, sales, orders, store credit and reviews</li>\n<li>the data of any add-on that offers it</li>\n</ul>\n<p><strong>Everything</strong> is ticked to start with. Untick what you don't want, press <strong>Export</strong>, and you get one zip file. Inside is your data, plus copies you can read without KahunaCart: the catalog as a text file, and your customers and orders as CSV files that open in any spreadsheet. I like that part a lot. Even if you never restore a bundle, you have your customers and orders in a form you can use anywhere.</p>\n<p>Customer passwords and the keys your store uses to talk to payment providers stay out unless you tick them. If you include the keys, along with saved cards and webhook secrets, they're locked with a passphrase you choose. Every export is kept in <strong>History</strong>, with who ran it and what it held.</p>\n<h2 id=\"restore-it-on-another-site\">Restore it on another site<a href=\"#restore-it-on-another-site\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>A bundle restores in one of two ways.</p>\n<p><strong>Full restore</strong> puts the store into a fresh KahunaCart install with the same order numbers, the same customers and the same orders. If you included passwords, customers sign in with the ones they already have. If you included the keys, saved cards and payment providers keep working too. The one thing that never comes across is whether the store is open, so the new site keeps its own setting.</p>\n<p><strong>Merge</strong> brings a bundle into a store that is already running. It matches products by the last part of their web address, customers by email and orders by number. It never deletes anything, and running the same bundle twice changes nothing.</p>\n<p>Both start with a dry run that shows what would happen, and both end with a report. And a restore sends nothing: no emails, no webhooks, no order events. Restored orders are history, not new sales.</p>\n<p><code>bin/plugin kahunacart export --bundle store.zip</code> and <code>bin/plugin kahunacart import store.zip --full</code> do the same from the command line, which is the way to go for a big store.</p>\n<h2 id=\"pause-the-shop-with-one-switch\">Pause the shop with one switch<a href=\"#pause-the-shop-with-one-switch\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Moving a shop needs a pause, and until now KahunaCart didn't really have one. Turning the cart off left buy buttons around that went nowhere.</p>\n<p>1.5.0 adds a <strong>store status</strong>: <strong>Open</strong>, <strong>Coming soon</strong> or <strong>Paused</strong>, with a message of your own. While the shop isn't open, every page shows a banner with your message and nobody can start a new purchase. That goes for shoppers on the site and for apps and AI shopping agents connected to it. Product pages stay up, with \"Coming soon\" or \"Paused\" where the buy button was.</p>\n<p>Everything else keeps working. Customers can sign in, see their orders, download what they bought and pay what they owe on an order that already exists. Subscriptions renew. Payments in progress still go through.</p>\n<p>You can still buy, though. Signed-in staff see the store working, with a banner saying only staff can buy, so you can place a test order before you open. <strong>Coming soon</strong> also asks search engines not to list the shop's pages. <strong>Paused</strong> leaves your search listings alone.</p>\n<p>Switch it from the dashboard, the General tab of the settings, or <code>bin/plugin kahunacart store --status=paused</code>. It's handy for more than moving, too. Holidays, a stocktake, or a launch that isn't ready yet.</p>\n<h2 id=\"move-your-shop-to-its-own-site\">Move your shop to its own site<a href=\"#move-your-shop-to-its-own-site\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>This is the job that started it all. Pause the old shop, export everything, and restore it on the new site. The restore report then gives you a block of redirects to paste into the old site's settings. Every old shop address goes to the same page on the new site, because a restore keeps each product's address.</p>\n<p>The docs walk through the whole move step by step, including the part people forget: stopping the old store right after the final export, so nothing lands on a site you're about to switch off. See <a href=\"/docs/operations/export-and-restore#moving-your-shop-to-its-own-site\">Moving your shop to its own site</a>.</p>\n<h2 id=\"the-shop-as-your-home-page\">The shop as your home page<a href=\"#the-shop-as-your-home-page\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>In KahunaCart's settings, set <strong>Shop Base Route</strong> to <code>/</code> and the shop is your home page. The product listing is at <code>/</code>, the cart at <code>/cart</code> and products at addresses like <code>/product/reef-runner-tee</code>. Your blog, <code>/about</code> and the admin carry on as before.</p>\n<p>If you already have a page at an address the shop uses, like <code>/cart</code>, the shop takes it. <code>bin/plugin kahunacart check</code> lists any of those, so run it before you switch. See <a href=\"/docs/store/storefront#mounting-the-shop-at-the-site-root\">Making the shop your home page</a>.</p>\n<h2 id=\"also-in-this-release\">Also in this release<a href=\"#also-in-this-release\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<ul>\n<li><strong>Export… on Products and Customers</strong> downloads a catalog file, a products CSV or a customers CSV that the importer reads back as it is.</li>\n<li><strong>A catalog import's dry run names the pictures this store doesn't have</strong>, before anything is written.</li>\n<li><strong>A catalog file with no products imports</strong> when it carries categories, attributes, manufacturers or custom fields.</li>\n</ul>\n<h2 id=\"fixes\">Fixes<a href=\"#fixes\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<ul>\n<li>A shop at the site root no longer doubles its slashes in links, redirects and emails.</li>\n<li><strong>Back to shop</strong> on the set-password and payment pages works when Grav is installed in a folder.</li>\n</ul>\n<h2 id=\"add-ons\">Add-ons<a href=\"#add-ons\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<ul>\n<li><strong>Bookings</strong> 1.1.8 takes no new bookings from shoppers while the store is coming soon or paused. Signed-in staff can still book.</li>\n<li><strong>Subscriptions</strong> 1.1.8, <strong>Licenses</strong> 1.0.17, <strong>Rentals</strong> 1.0.7, <strong>Newsletters</strong> 1.1.11, <strong>Polar</strong> 1.1.11 and <strong>Bookings</strong> work with the shop as your home page.</li>\n<li>The <strong>Kahuna</strong> theme 1.0.15 shows the store status banner.</li>\n</ul>\n<p>Update through the admin or with <code>bin/gpm update</code>, and come and tell me how it goes on <a href=\"https://discord.gg/vMs3UTEAg4\">Discord</a>.</p>",
            "date_published": "2026-10-11T17:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.5.0"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-print-on-demand-available",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-print-on-demand-available",
            "title": "KahunaCart Print on Demand",
            "summary": "Our new Print on Demand add-on connects Printful, Printify and Gelato to your store. They print it, pack it and post it. You never touch the stock, and by default every order waits for your OK before anything is printed or charged.",
            "content_html": "<p>A couple of KahunaCart customers asked for Printful this month. One of them was moving a PrestaShop store across, where some products came from Printful's own plugin and the rest went to different printers by SKU range. Nobody wanted to hold stock, and nobody wanted a second shop just for the printed stuff. So we built it into KahunaCart. <strong>KahunaCart Print on Demand</strong> is available today.</p>\n<h2 id=\"how-it-works\">How it works<a href=\"#how-it-works\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>You design a shirt, a mug or a poster in <strong>Printful</strong>, <strong>Printify</strong> or <strong>Gelato</strong>, the same way you would for any other shop. Then you import it into KahunaCart. Its sizes and colours become options, its pictures come across, and each variant keeps what the printer charges you for it, so you set your own price and can see your margin.</p>\n<p>A customer buys it from your store and pays you, through the same cart and checkout as everything else you sell. The order goes to the printer, the printer makes it and posts it, and the tracking number comes back onto the order. The customer sees it on their receipt and in their account, and gets the usual shipped email.</p>\n<p>You can connect all three printers at once. Each product is made by one of them, so checkout never has to choose.</p>\n<h2 id=\"nothing-is-printed-until-you-say-so\">Nothing is printed until you say so<a href=\"#nothing-is-printed-until-you-say-so\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>This is the part I care about most. Sending an order to a printer spends your money, and the first few orders through any new setup are the ones most likely to be wrong. So every printer starts in <strong>review mode</strong>. A paid order becomes a free draft at the printer and waits on the order screen with what it will cost you and what you'll make on it, until you press <strong>Send to Printful for $18.40</strong>.</p>\n<p>Once a few have gone through cleanly, switch that printer to <strong>automatic</strong> and paid orders go straight to production. A refunded or cancelled sale is checked again before anything is sent.</p>\n<p>Printify needs one extra step. A Printify shop sends orders to production by itself after 24 hours unless you set it to <strong>Manual order approval</strong>, and its API can't tell us which way yours is set. So KahunaCart asks you to confirm it before it will switch Printify on.</p>\n<h2 id=\"your-own-files-on-their-blanks\">Your own files on their blanks<a href=\"#your-own-files-on-their-blanks\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Not everything you sell has to be designed in the printer's dashboard. If you keep your own artwork, pick the printer's blank for each variant, say a Bella + Canvas 3001 tee in black or an A3 matte poster, and upload a print file for each print area. The store checks each file against the size the printer wants and warns you when it would print soft.</p>\n<p>That's also how a big catalogue moves across. <strong>Assign by SKU</strong> links every variant whose SKU matches a rule, so 7000 to 7999 can go to Printful tees and 8000 to 8999 to Gelato posters. There's a dry run that shows what would change before anything does, and an undo for the whole run. A spreadsheet of links works too, in and out.</p>\n<h2 id=\"their-shipping-at-checkout\">Their shipping at checkout<a href=\"#their-shipping-at-checkout\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Each printer adds its own delivery options to checkout, priced live for the customer's address, so what the customer pays for postage matches what the printer charges you. A cart with a printed shirt and a mug you post yourself is two parcels, each with the right options. If a printer can't give a price, your own shipping methods carry on as before, so checkout never ends up with nothing to choose.</p>\n<h2 id=\"which-printer\">Which printer?<a href=\"#which-printer\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<ul>\n<li><strong>Printful</strong> is the most complete. Clothing and accessories, embroidery, your own designs or your own files on their blanks.</li>\n<li><strong>Printify</strong> has a huge catalogue from lots of print shops, and it's the easy choice if your designs are already there.</li>\n<li><strong>Gelato</strong> prints close to the customer, from clothing and mugs to wall art and cards, from your own files or a Gelato template.</li>\n</ul>\n<p>The <a href=\"/docs/print-on-demand/overview\">overview</a> has a side-by-side table, and each printer has its own setup page in the docs.</p>\n<h2 id=\"look-around-first\">Look around first<a href=\"#look-around-first\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The add-on comes with a pretend printer called <strong>Kahuna Print Co.</strong> Switch it on for a test store and you get three products, invented postage, and orders that move from made to shipped to delivered on a clock, so every screen has something on it before you sign up anywhere. The <a href=\"/docs/print-on-demand/demo-printer\">demo printer page</a> explains it.</p>\n<p>It's running on the <a href=\"https://demo.kahunacart.com/shop/product/kelp-bay-surf-tee\">live demo</a> too. Put the surf tee in the cart and checkout offers the printer's Standard and Express postage. Then open the <a href=\"https://demo.kahunacart.com/admin/plugin/kahunacart#/section/print-on-demand\">admin</a> to see its orders being made and shipped, with the cost and margin on each. The sign-in details are on the demo's login page, nothing is charged, and everything is wiped on the hour.</p>\n<h2 id=\"also-new-in-the-core\">Also new in the core<a href=\"#also-new-in-the-core\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p><strong>KahunaCart 1.4.7</strong> came out alongside it, with a few things worth knowing about:</p>\n<ul>\n<li><strong>Batch actions on variants.</strong> Tick the variants you want in the product editor and change or delete just those. Leftovers from an old set of options can all go in one go.</li>\n<li><strong>Merge two customers.</strong> When someone has bought under two email addresses, you can merge them, or they can do it themselves from their account. Their orders, licences and history move across, and the old address still signs in.</li>\n<li><strong>Other email addresses.</strong> Customers can add a second address to their own account.</li>\n<li><strong>Shipping fixes.</strong> The fallback shipping method you nominate is now actually used when a carrier can't quote, and a product type that holds no stock is never stock-counted.</li>\n</ul>\n<h2 id=\"get-started\">Get started<a href=\"#get-started\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Print on Demand needs <strong>KahunaCart 1.4.7 or later</strong>, the store's job worker running, and a public https address so the printers can send tracking updates. It's <strong>$149</strong>, a one-time purchase for your site, and updates are covered by your site's single renewal. You'll need your own account with Printful, Printify or Gelato. They bill you for each order they make and its postage, and the add-on doesn't add a fee on top.</p>\n<p>Add it to your licence in the <a href=\"/shop/product/kahunacart-print-on-demand\">store</a>, install it through Grav's plugin manager, and follow the <a href=\"/docs/print-on-demand/getting-started\">getting started guide</a>.</p>\n<p>If you sell merch, prints or anything else a printer could make for you, I'd love to hear how this works for you. Come and tell us on <a href=\"https://discord.gg/vMs3UTEAg4\">Discord</a>.</p>\n<p>Andy</p>",
            "date_published": "2026-10-11T12:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.0.0"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-1-4-0",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-1-4-0",
            "title": "KahunaCart 1.4.0: Sell in more than one currency",
            "summary": "One store, several currencies: you type a price in each one, shoppers browse and pay in their own, and every order stays in the currency it was placed in.",
            "content_html": "<p><strong>KahunaCart 1.4.0</strong> is out, with 41 changes: 17 new features, 11 improvements and 13 fixes. The headline is the one people have asked for most since launch: selling in more than one currency.</p>\n<h2 id=\"one-store-several-currencies\">One store, several currencies<a href=\"#one-store-several-currencies\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Until now a KahunaCart store sold in one currency. If you wanted euros and pounds as well as dollars, you ran three stores or you didn't do it.</p>\n<p>With 1.4.0 you keep one base currency and list the others you want to sell in under <strong>Configuration → General → Selling in More Than One Currency</strong>. Then you type a price for each one. A shopper in Germany browses in euros, pays in euros and gets a receipt in euros. Your accounts in dollars stay exactly as they were.</p>\n<p><strong>Nothing is converted.</strong> KahunaCart doesn't look up exchange rates or work a price out for you. You decide that the tee is €29 and £25, not whatever $32 happens to be today. A product with no price in a currency simply isn't sold in it, and the product page says \"Not available in EUR\" instead of guessing. The one exception is free: a product with a base price of 0 is free in every currency.</p>\n<p>You can type the prices wherever you already work: in the product editor, with bulk edit, in a CSV file or in the catalogue file. Every currency gets its own column, and JPY gets no decimals while KWD gets three.</p>\n<h2 id=\"everything-with-a-price-follows-along\">Everything with a price follows along<a href=\"#everything-with-a-price-follows-along\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>A price is only one of the amounts a store charges. Coupons, sales, price lists, quantity tiers, order minimums and maximums, payment method fees, shipping methods, collection point fees and store credit all take an amount per currency now. There is one rule for all of them: <strong>if there's no amount in a currency, the rule doesn't apply there</strong>. A \"€5 off\" coupon with no dollar amount simply doesn't work on a dollar cart. It never turns into $5 or $0.</p>\n<h2 id=\"the-shopper-s-currency-picked-for-them\">The shopper's currency, picked for them<a href=\"#the-shopper-s-currency-picked-for-them\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>KahunaCart works out a shopper's currency from what it knows: a choice they already made, the country on their address, or a country header from your CDN, such as Cloudflare's <code>CF-IPCountry</code>. A switcher in the header lets them change it, and <code>?currency=EUR</code> on a link opens a page in euros.</p>\n<p>Once a shopper has a cart, the cart's currency wins. Switching moves the whole cart: every line is priced again in the new currency, and anything with no price there is taken out and named. If a cart already had a payment page opened at a provider, it keeps its currency for good and offers <strong>Start a new cart in EUR</strong> instead, so no payment is ever left pointing at the wrong amount.</p>\n<h2 id=\"orders-refunds-and-reports-stay-in-one-currency\">Orders, refunds and reports stay in one currency<a href=\"#orders-refunds-and-reports-stay-in-one-currency\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Every order is in exactly one currency, from the cart to the last refund. Lines, shipping, tax, payments, refunds and order changes are all in that currency, and invoices and emails print it.</p>\n<p>Reports, the dashboard, customer lifetime spend and cart recovery add up one currency at a time, with a picker to choose which. They never add euros to dollars and call the result a total.</p>\n<p>Payment methods that can't take an order's currency aren't offered for it. Every payment provider plugin has an update for this release, and <strong>Stripe</strong> 1.2.6 sends Icelandic krona, Ugandan shilling and Malagasy ariary at the scale Stripe expects.</p>\n<h2 id=\"add-ons-agents-and-caching\">Add-ons, agents and caching<a href=\"#add-ons-agents-and-caching\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p><strong>Subscriptions</strong> 1.1.5, <strong>Bookings</strong> 1.1.5, <strong>Rentals</strong> 1.0.6 and <strong>Shipping</strong> 1.1.7 all price in each currency you sell in, and a subscription renews in the currency it started in.</p>\n<p>AI shopping agents get the same treatment. The storefront API, the storefront MCP endpoint, UCP checkout, the product feeds and the structured data on product pages all work in every currency you sell in.</p>\n<p>If you use a CDN, one rule keeps it simple: a page is only cached publicly when its address decides the currency. The <a href=\"/docs/money/currencies#caching-and-cdns\">caching section of the guide</a> explains how to set that up.</p>\n<p><strong>A store that sells in one currency sees no change.</strong> If you never list a second currency, the storefront and the admin look and work as they did, and the new tables stay empty. Want to try it first? <code>bin/plugin kahunacart seed --currencies=EUR,GBP</code> fills a demo store with prices in euros and pounds.</p>\n<h2 id=\"also-in-this-release\">Also in this release<a href=\"#also-in-this-release\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<ul>\n<li><strong>Every admin table has a search box</strong>, next to the sortable headings from 1.3.8.</li>\n<li><strong>Choosing a payment method at checkout updates the total.</strong> The fee and its tax are added as soon as the method is picked, so the total the shopper sees is the total they pay.</li>\n<li><strong>A catalogue file with a variant option that doesn't match is refused.</strong> If a variant names a value its option doesn't list, usually because the values are in a different order than the options, the import now stops and tells you which product, variant and label. It used to import, and the product page then showed every choice disabled.</li>\n</ul>\n<h2 id=\"fixes\">Fixes<a href=\"#fixes\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<ul>\n<li><strong>The configuration export no longer carries the store's signing secret.</strong> Earlier exports included the secret the store signs order, invoice and sign-in links with, and importing a file replaced the target store's secret. The secret now stays on its store, and so do other keys and webhook addresses. If you have shared an export file, update.</li>\n<li><strong>Subscriptions now handle payment method fees properly.</strong> Stripe left the fee off subscription payments, so the store paid it. <strong>Subscriptions</strong> 1.1.5 adds a choice: a plan's price can have the method's fee <strong>added</strong> (the subscriber pays it, with its tax, on every payment, and on an invoice renewal a cheaper method costs less) or <strong>included</strong> (the plan's price is what they pay, whatever the method). Added is the default, each plan can choose its own, and subscriptions that already exist renew as they always did. Update <strong>Stripe</strong> to 1.2.6 as well.</li>\n<li>A product with a fixed-price add-on can be saved from the editor again.</li>\n<li>Choosing a collection point at checkout no longer leaves the next step disabled.</li>\n<li>The payment method editor no longer throws away what you typed when you add or change a condition.</li>\n<li>Restoring a product revision no longer undoes a sale made at the same moment.</li>\n<li>A database retry after a deadlock no longer repeats a decision made on stale data.</li>\n</ul>",
            "date_published": "2026-10-04T12:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.4.0"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-1-3-6-performance",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-1-3-6-performance",
            "title": "KahunaCart 1.3.6: same server, a lot more shoppers",
            "summary": "On the same benchmark server, a category page on MariaDB went from 69 requests a second to 189. Here's what a profiler found, the search bug that had been running in every benchmark we took, and what you need to do to get all of it.",
            "content_html": "<p>When the first numbers came back from our new dedicated benchmark server, I asked a simple question: is there room left? The category page on MariaDB topped out at 69 requests a second, while a product page on the same box managed 145. A listing shouldn't cost twice as much as a product. So I put a profiler on it, and the answer turned into <strong>KahunaCart 1.3.6</strong>.</p>\n<p>Same server, same store of 5,000 products and 250,000 orders. That category page now handles <strong>189 requests a second</strong>. Search on MariaDB went from 75 to 186. Capacity went up on every route we measure, on all three database engines.</p>\n<h2 id=\"what-the-profiler-found\">What the profiler found<a href=\"#what-the-profiler-found\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The biggest single cost was hiding in the filter panel. Next to each tick box on a category or search page, the store shows how many products match. To decide which values to offer at all, it also counted every attribute value across the whole catalog, on every request. On MariaDB that was 67 to 70 ms of database work per page, most of the time the page spent in the database. Those counts only change when the catalog does, so now they're kept until it does. The category counts in the navigation got the same treatment.</p>\n<p>Smaller things added up:</p>\n<ul>\n<li><strong>Catalog listings</strong> pick the page of product ids first and then load only those products. Before, the database joined and sorted every product for each page. The first catalog page's query on MariaDB went from 39 ms to under 10.</li>\n<li><strong>PostgreSQL search</strong> ranks from a stored search column instead of re-reading the text of every match. A search for a common word went from 471 ms to under 10.</li>\n<li><strong>Cart recalculation</strong> writes only what changed. An unchanged cart used to cost 26 database statements, 9 of them writes. Now it's 14 statements and no writes at all.</li>\n<li><strong>Prices</strong> format several times faster, and the plugin does less at the start of every request. The helper functions for PDFs load only when you actually make a PDF, and the notification registry is built the first time something needs it.</li>\n</ul>\n<h2 id=\"the-search-bug\">The search bug<a href=\"#the-search-bug\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The biggest jump in search wasn't a slow query. It was a bug, and it could hit a live store.</p>\n<p>When the product search index looked out of date (after a deploy or a restore without the data folder, or a bulk import that wrote products directly), the next shopper to search rebuilt the whole index inside their own request. That takes about 13 seconds. Worse, every other search that arrived in those 13 seconds saw the same out-of-date index, started a rebuild of its own and deleted the one in progress. Under steady search traffic they just kept knocking each other over. Over two days of load tests, the benchmark store rebuilt its index 2,367 times.</p>\n<p>And it had been running in every benchmark we'd taken. That's why search always looked like the weak spot. It was running during the first 1.3.6 run as well, so we withdrew that run and took a new one after the fix.</p>\n<p>Now only one rebuild runs at a time, in the background, into a new file. The old index keeps answering searches until the new one is complete and switched in. If there's no usable index yet, searches fall back to a plain database search, so a shopper never waits on a rebuild. While I was in there, I found search was also asking the index for a highlighted snippet of every match (several hundred for a common word) and then reading only the ids. It asks for the ids alone now.</p>\n<p>Search capacity roughly doubled on SQLite (125 to 258 requests a second) and PostgreSQL (73 to 140), and went from 75 to 186 on MariaDB. Search is now strong on every engine.</p>\n<h2 id=\"mysql-and-mariadb-search-that-slowed-with-every-sale\">MySQL and MariaDB search that slowed with every sale<a href=\"#mysql-and-mariadb-search-that-slowed-with-every-sale\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Every product save and every stock movement reindexes that product. On MySQL and MariaDB, the catalog index deleted and re-inserted all of the product's rows each time. But InnoDB keeps deleted rows in a full-text index until somebody optimizes the table, and nobody ever does. One upgrade and reindex left our benchmark store with 30,000 dead entries on 15,000 live rows, which made searches 2 to 10 times slower. A busy store would have got slower with every sale. The index now writes only the rows that changed, and a full rebuild finishes by optimizing the table.</p>\n<h2 id=\"the-admin-got-faster-too\">The admin got faster too<a href=\"#the-admin-got-faster-too\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Order search on MySQL and MariaDB went from about 260 ms to about 5 ms. It tries an exact order number or email address first, and it stops asking the full-text index about words that almost every order shares, like a common email domain or your order number prefix. Reports are about 30% faster on SQLite and PostgreSQL.</p>\n<h2 id=\"grav-2-2-3-and-faster-templates\">Grav 2.2.3 and faster templates<a href=\"#grav-2-2-3-and-faster-templates\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p><strong>Grav 2.2.3</strong> ships alongside, and it brings its own share. Grav 2 runs Twig in page content inside a sandbox, which checks every call so nothing dangerous can be done from a page. Theme and plugin templates were paying for those same checks, even though they're code you chose to install. Grav's <strong>Twig</strong> fork now compiles them without the sandbox checks, and a listing template renders about twice as fast, within about 5% of no sandbox at all. It also moves to Twig 3.30. In a test on PostgreSQL, the Grav update on its own was worth 4 to 9% of capacity, and every Grav site gets it, store or not.</p>\n<h2 id=\"the-numbers\">The numbers<a href=\"#the-numbers\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Capacity here is the most requests a second a page sustained with 95% of them answered in under 500 ms. Same 8 vCPU server and the same store, <strong>KahunaCart 1.3.5</strong> with Grav 2.2.1 against <strong>KahunaCart 1.3.6</strong> with Grav 2.2.3:</p>\n<table>\n<thead>\n<tr>\n<th>Page</th>\n<th>SQLite</th>\n<th>MariaDB</th>\n<th>PostgreSQL</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Home</td>\n<td>224 → 352</td>\n<td>112 → 209</td>\n<td>127 → 189</td>\n</tr>\n<tr>\n<td>Catalog</td>\n<td>171 → 274</td>\n<td>111 → 191</td>\n<td>79 → 143</td>\n</tr>\n<tr>\n<td>Category</td>\n<td>115 → 252</td>\n<td>69 → 189</td>\n<td>74 → 135</td>\n</tr>\n<tr>\n<td>Product</td>\n<td>233 → 361</td>\n<td>145 → 228</td>\n<td>129 → 154</td>\n</tr>\n<tr>\n<td>Search</td>\n<td>125 → 258</td>\n<td>75 → 186</td>\n<td>73 → 140</td>\n</tr>\n</tbody>\n</table>\n<p>Under the normal mixed workload of browsing, carts and checkouts, 95% of catalog pages came back within 64 ms on SQLite (down from 94), 90 ms on MariaDB (from 151) and 104 ms on PostgreSQL (from 175). One request failed out of 73,111 across all three engines. Every route, the full method and every earlier run are on the <a href=\"/benchmarks\">benchmarks page</a>, and the <a href=\"/performance\">performance page</a> explains in plainer terms why the store is fast.</p>\n<h2 id=\"optional-persistent-database-connections\">Optional: persistent database connections<a href=\"#optional-persistent-database-connections\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>This one is <strong>not</strong> in the published numbers. KahunaCart 1.3.6 adds a <strong>Persistent Connections</strong> setting for stores on MySQL, MariaDB or PostgreSQL, and it's off by default. Turn it on and each PHP worker keeps its database connection open between requests instead of opening a new one every time. A new PostgreSQL connection costs about 7 ms, which is a big slice of a page that otherwise takes a few dozen. In a separate test on the same server, it raised PostgreSQL capacity by 50 to 65%: the home page went from 187 to 306 requests a second and the catalog from 132 to 197.</p>\n<p>The catch is that every worker holds a connection even when it's idle, so your database's <code>max_connections</code> has to cover every PHP worker you run, plus the scheduler. That's why it's off. If you run your own database server and know those numbers, I'd turn it on. The setting now lives in Grav's shared database kit, so <strong>Forum Pro</strong> and <strong>Mailroom</strong> get the same setting in their next releases.</p>\n<h2 id=\"updating\">Updating<a href=\"#updating\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Update Grav to <strong>2.2.3</strong> and KahunaCart to <strong>1.3.6</strong> or later (1.3.7 followed the same day with a checkout fix), from the admin or with <code>bin/gpm self-upgrade</code> and <code>bin/gpm update kahunacart</code>. The database migrations run as usual. That's it, nothing else to change.</p>\n<p>If you want to see what your own hosting can do, the <a href=\"/docs/operations/production/benchmarking\">benchmarking guide</a> shows how to seed a store this size and time it. And if you run the numbers, I'd love to see them on the KahunaCart <a href=\"https://discord.gg/vMs3UTEAg4\">Discord</a>.</p>\n<p>Andy</p>",
            "date_published": "2026-09-29T12:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Article"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-1-3-0",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-1-3-0",
            "title": "KahunaCart 1.3.0: AI shopping agents and Squarespace import",
            "summary": "AI shopping agents in Google AI Mode, Gemini and ChatGPT can now find your products and buy them, and a Squarespace store moves in with one command.",
            "content_html": "<p><strong>KahunaCart 1.3.0</strong> is out, with 40 changes: 20 new features, 13 improvements and 7 fixes. It's the first minor release since launch, and the headline is agentic commerce.</p>\n<h2 id=\"ai-shopping-agents-can-buy-from-your-store\">AI shopping agents can buy from your store<a href=\"#ai-shopping-agents-can-buy-from-your-store\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>People are starting to shop inside AI assistants, and I wanted KahunaCart stores ready for it. With 1.3.0, Google AI Mode and Gemini can put one of your products in a basket and pay with Google Pay without the buyer ever leaving the conversation. ChatGPT can find your products through the OpenAI product feed. And any MCP client (Claude, Cursor, a ChatGPT connector) can search your catalog and build a cart.</p>\n<p>It comes in four parts that build on each other:</p>\n<ul>\n<li><strong>Structured data</strong> on product pages: the brand, GTIN, condition, seller, per-country shipping and your return policy</li>\n<li><strong>Product feeds</strong> for Google Merchant Center and ChatGPT, rebuilt in the background after a catalog change</li>\n<li><strong>A storefront MCP endpoint</strong> with thirteen tools for searching the catalog, answering policy questions and building a checkout</li>\n<li><strong>UCP checkout</strong>, the Universal Commerce Protocol that Google's agent checkout speaks, with Google Pay through <strong>Stripe</strong> 1.2.4</li>\n</ul>\n<p>An agent sees exactly what a logged-out visitor sees. It can't sign in, it can't set a price, and it never sees a stock count or a cost. Prices, tax and shipping come from your store, worked out by the same calculator the storefront uses. If an agent can't finish a purchase itself (a subscription, a deposit or a pay-what-you-want product, say), the buyer lands on your own checkout with the cart already filled.</p>\n<p>Agent orders get their own <code>agent</code> source in reports, so you can see what the assistants actually sell for you. A new <strong>Mark delivered</strong> button on each shipment tells the platform when the parcel arrived.</p>\n<p><strong>All of it is off until you turn it on.</strong> A store that never opens the new <strong>Settings → Agentic Commerce</strong> tab sells exactly as it did on 1.2. The <a href=\"/docs/ai-integration/agentic-commerce\">agentic commerce docs</a> walk through the setup for each platform.</p>\n<h2 id=\"move-a-squarespace-store-in-with-one-command\">Move a Squarespace store in with one command<a href=\"#move-a-squarespace-store-in-with-one-command\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Squarespace doesn't give you one export. You get a products CSV, an orders CSV, a customers CSV and a WordPress export of the site, and none of them fit an importer as they come. Put all four in one folder and run:</p>\n<pre><code class=\"language-bash\">bin/plugin kahunacart squarespace &lt;folder&gt;</code></pre>\n<p>Products come in with every variant, price, sale price and stock level. Pictures are downloaded from Squarespace's servers before those links stop working. Customers and orders keep their numbers, dates and totals, the site and blog become Grav pages, and you get redirects from every old Squarespace address to its new one, ready to paste into <code>site.yaml</code>.</p>\n<p><code>--dry-run</code> writes nothing, and running it again updates instead of duplicating. We tested it against a real store's exports, not sample files. The <a href=\"/docs/operations/importing#moving-from-squarespace\">importing docs</a> cover which exports to download and what comes across.</p>\n<h2 id=\"search-that-knows-what-shoppers-mean\">Search that knows what shoppers mean<a href=\"#search-that-knows-what-shoppers-mean\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Product search now looks at each product's categories, tags, brand and attributes, so <code>tops</code> finds a tee filed under Tops. That's on for everyone.</p>\n<p>Switch on <strong>semantic search</strong> as well and the storefront ranks by meaning, not just keywords. A search for <code>hat</code> finds the cap and the beanie. It works with any OpenAI-compatible embeddings API, including OpenAI, OpenRouter or Ollama on your own server, and it measures its own noise level on your catalog so a search for <code>asdf</code> doesn't come back with random products. Leave it off and search works exactly as before, with nothing sent anywhere. See <a href=\"/docs/store/storefront#semantic-search\">Semantic search</a> to set it up.</p>\n<h2 id=\"grav-2-2-and-lazy-sessions\">Grav 2.2 and lazy sessions<a href=\"#grav-2-2-and-lazy-sessions\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p><strong>Grav 2.2</strong> came out today with lazy sessions. Turn them on and shoppers who are only browsing no longer get a session cookie from the store, so a proxy or CDN can cache those pages. Orders still record where a shopper came from, and signing in from a store page still brings them back to it.</p>\n<h2 id=\"fixes\">Fixes<a href=\"#fixes\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<ul>\n<li><strong>A sign-in link no longer skips two-factor.</strong> A customer who had two-factor on was signed in by the emailed link alone. The link and the set-password page now ask for the code. If your store uses two-factor, update.</li>\n<li>KahunaCart no longer breaks when another plugin ships an older copy of the math library its prices depend on. With <strong>grav-ray</strong> installed, price calculations stopped with a fatal error.</li>\n<li>Hourly rate limits no longer reset early on a busy store.</li>\n<li>A checkout switched from delivery to pickup no longer prints the delivery address on the invoice and packing slip.</li>\n</ul>\n<p>Under the covers, the database layer moved to our shared <strong>grav-db-kit</strong> package. Add-ons built for 1.2 keep working unchanged.</p>\n<p>Update from the admin or with <code>bin/gpm update kahunacart</code>. The full list is in the <a href=\"https://github.com/kahunacart/grav-plugin-kahunacart/releases/tag/1.3.0\">changelog</a>, and questions and feedback are welcome on the KahunaCart <a href=\"https://discord.gg/vMs3UTEAg4\">Discord</a>.</p>\n<p>Andy</p>",
            "date_published": "2026-09-24T12:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.3.0"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-bookings-rentals-available",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-bookings-rentals-available",
            "title": "KahunaCart Bookings and Rentals",
            "summary": "Two new add-ons let your shop sell time as well as things. Bookings takes appointments and classes and gives your staff a front desk. Rentals adds nightly stays on top, with seasonal rates, deposits and calendar sync with Airbnb, VRBO and Booking.com.",
            "content_html": "<p>When I started on <strong>KahunaCart</strong>, the plan was a shop for things: downloads, licences, subscriptions, boxes in the post. But a lot of people who run small businesses on Grav don't sell things at all. They sell an hour in a chair, a spot in a Tuesday class, or a week at the beach. Until now they needed a separate booking service for that, with its own checkout, its own customer list and its own monthly bill. So we built two add-ons that do it inside your shop. <strong>KahunaCart Bookings</strong> and <strong>KahunaCart Rentals</strong> are available today.</p>\n<h2 id=\"bookings-appointments-and-classes\">Bookings: appointments and classes<a href=\"#bookings-appointments-and-classes\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Set a product's type to <strong>Booking</strong> and its page grows a calendar. Customers see the free days and times, pick a staff member if you allow it, and pay through the same cart and checkout as everything else in your store. Picking a time holds it for fifteen minutes while they finish. You can also take payment on the day, and free bookings skip the cart entirely.</p>\n<p>The part I'm proudest of is how it handles what a service needs. A dog groom needs a groomer <em>and</em> a tub. A haircut needs a stylist and a chair. You list your people, rooms and equipment, tell each service what it needs, and the shop only offers times when everything is free. Ten identical axe lanes are one record with a capacity of ten, not ten records you have to keep in sync.</p>\n<p>Also in the box:</p>\n<ul>\n<li><strong>Classes and courses</strong> with a register for each date and a waiting list when a class fills up. When a place opens on a paid class, the next person is offered it and it's held while they pay.</li>\n<li><strong>A diary</strong> with a row for each person, room and piece of equipment, working hours and time off behind the bookings, and conflicts in red.</li>\n<li><strong>Troubleshoot</strong>, which tells you why a time isn't on offer, with reasons like <em>Tub: booked</em>.</li>\n<li><strong>Emails and reminders</strong>: confirmations, a reminder a day and an hour before, a thank-you afterwards, and a link where customers can reschedule or cancel up to your cutoff.</li>\n<li><strong>Calendar feeds</strong> so staff can see their appointments on their phones, and imported calendars to block time that's already taken.</li>\n</ul>\n<h2 id=\"a-front-desk-for-your-staff\">A front desk for your staff<a href=\"#a-front-desk-for-your-staff\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The front desk is the piece I'd show a salon owner first. Give your reception tablet a site account with front desk access and open <code>/desk</code>. Staff see the day in columns, tap to check someone in, press and hold to move an appointment, and use <strong>New booking</strong> for walk-ins and phone calls. Each appointment shows what's owed in plain words, like <em>Deposit paid, $25.00 to pay today</em>.</p>\n<p>Desk staff never see the admin, your orders, your payment providers or refunds. You can give reception the whole diary or give a stylist just their own column.</p>\n<h2 id=\"rentals-let-it-by-the-night\">Rentals: let it by the night<a href=\"#rentals-let-it-by-the-night\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p><strong>Rentals</strong> sits on top of Bookings and adds nightly stays for holiday lets, B&amp;B rooms, cabins, pitches or anything else you rent by the day. Guests pick arrival and departure dates on a calendar that shows availability and changeover days, and they get an itemised quote before they pay.</p>\n<p>You set the rules once per property:</p>\n<ul>\n<li>Check-in and check-out times, with same-day changeovers when check-out comes first</li>\n<li>Minimum and maximum stays, and which weekdays guests can arrive or leave</li>\n<li>A standard nightly rate, with dated <strong>seasonal bands</strong> on top (Christmas week can sit inside a winter season)</li>\n<li>Cleaning fees per stay and extra-guest fees per night, each shown on its own line</li>\n<li>A <strong>deposit</strong> at checkout, with the balance due a set number of days before arrival and a payment link sent to the guest</li>\n<li>A cancellation policy that works out the refund and hands you a request to approve</li>\n</ul>\n<p>Rentals also swaps calendar links with <strong>Airbnb, VRBO and Booking.com</strong>, so a stay booked there blocks the nights on your site, and a stay booked on your site blocks them there. Only \"Not available\" and two dates go out. Guest names and prices never leave your server. Calendar sync runs on each site's refresh schedule, so two sites can still sell the same dates in between. The <a href=\"/docs/rentals/channel-sync\">channel sync docs</a> explain how to handle that when it happens.</p>\n<h2 id=\"try-all-three-demo-businesses\">Try all three demo businesses<a href=\"#try-all-three-demo-businesses\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The <a href=\"https://demo.kahunacart.com/bookings\">demo</a> now runs three made-up businesses, each with its own look:</p>\n<ul>\n<li><strong>Hollis &amp; Fern</strong>, a hair salon, on Bookings</li>\n<li><strong>Ironbark</strong>, an axe-throwing range with lanes and party sizes, on Bookings</li>\n<li><strong>Saltmeadow</strong>, a beach house let by the night, on Bookings + Rentals</li>\n</ul>\n<p>Book something, then open the <a href=\"https://demo.kahunacart.com/desk\">front desk</a> or the <a href=\"https://demo.kahunacart.com/admin/plugin/kahunacart#/section/bookings\">admin</a> to see it from the owner's side. The sign-in details are on the demo page, nothing is charged, and every booking is wiped on the hour. The <a href=\"/add-ons/bookings\">Bookings</a> and <a href=\"/add-ons/rentals\">Rentals</a> pages have screenshots of every screen too.</p>\n<h2 id=\"also-new-in-the-core\">Also new in the core<a href=\"#also-new-in-the-core\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>A few things went into <strong>KahunaCart</strong> itself while these were being built, and they're useful well beyond bookings:</p>\n<ul>\n<li><strong>Pay what you want.</strong> Tick <strong>Let shoppers name their price</strong> on a product and the buy button gets a <strong>Name your price</strong> box, with a minimum, an optional suggested price and an optional maximum. See <a href=\"/docs/store/products#pay-what-you-want\">pay what you want</a>.</li>\n<li><strong>Deposits at checkout.</strong> Customers can now pay a deposit at checkout or from a pay link, and the store asks for the balance on its own when it's due. See <a href=\"/docs/money/deposits\">deposits</a>.</li>\n<li><strong>Bundle suggestions.</strong> If a shopper has the parts of a bundle in the cart separately, the cart offers them the bundle instead.</li>\n<li><strong>Customers in more than one group</strong>, and price lists that show their discount on the product page rather than only in the cart.</li>\n</ul>\n<h2 id=\"get-started\">Get started<a href=\"#get-started\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Bookings and Rentals need <strong>KahunaCart 1.2.11 or later</strong>.</p>\n<ul>\n<li><strong>Bookings</strong> is $149.</li>\n<li><strong>Rentals only</strong> is $149, for sites that already own Bookings.</li>\n<li><strong>Bookings + Rentals</strong> is $249 for both.</li>\n</ul>\n<p>Each one is a one-time purchase for your site, and updates are covered by your site's single renewal. If you buy the two separately in one cart, the store will offer you the bundle and save you $49. Add them to your licence in the <a href=\"/shop/product/kahunacart-bookings\">store</a>, install them through Grav's plugin manager, and follow the <a href=\"/docs/bookings/getting-started\">Bookings setup guide</a>. The docs include ready-made examples for salons, groomers, personal trainers, massage therapists, ranges, holiday lets and a four-room B&amp;B, each with a seed command that builds a working setup you can poke at.</p>\n<p>If you run a salon, a studio, a range or a holiday let on Grav, I'd really like to hear how this fits your business. Come and tell us on <a href=\"https://discord.gg/vMs3UTEAg4\">Discord</a>.</p>\n<p>Andy</p>",
            "date_published": "2026-09-22T12:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.0.0"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-shipping-available",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-shipping-available",
            "title": "KahunaCart Shipping is here: from checkout to the doorstep",
            "summary": "Our new Shipping add-on connects Shippo, EasyPost and ShipEngine to KahunaCart, with live carrier rates, box packing, shipping labels and parcel tracking in your store admin.",
            "content_html": "<p>Taking an order is only half the job when you sell physical products. The other half is working out the postage, finding the right box, printing a label and keeping the customer informed until it arrives. <strong>KahunaCart Shipping is now available</strong>, bringing that work into the same admin where you manage your store.</p>\n<h2 id=\"your-carriers-connected\">Your carriers, connected<a href=\"#your-carriers-connected\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Choose <strong>Shippo, EasyPost or ShipEngine</strong>, connect your account and enable the carrier services you want to offer. That gives your store access to services from carriers such as USPS, UPS, FedEx and DHL, depending on your provider account and location. You choose one shipping provider for the store and create a shipping method for each service you want customers to see.</p>\n<p>Live rates work alongside KahunaCart's existing shipping methods. You can keep your flat rates and nominate a fallback method so customers can still check out when a carrier cannot return a quote.</p>\n<h2 id=\"postage-for-the-boxes-you-actually-use\">Postage for the boxes you actually use<a href=\"#postage-for-the-boxes-you-actually-use\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Add your box sizes, weight limits and product measurements, and Shipping works out how an order fits into parcels. It accounts for dimensional weight as well as the weight on the scale, so a large, light item does not get treated like a small one.</p>\n<p>You can inspect the proposed packing before buying anything: which box each parcel uses, what is inside it and which weight the carrier will bill. Orders that need more than one box get more than one parcel.</p>\n<h2 id=\"labels-beside-the-order\">Labels beside the order<a href=\"#labels-beside-the-order\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Open an order, review its parcels, choose a service and buy the labels from the admin. Shipping fetches a fresh quote and puts the price on the purchase button, so you know what you are about to spend. The labels are kept on your server for printing, and the tracking numbers are recorded against the order.</p>\n<p>For international parcels, Shipping builds the customs declaration and identifies missing product details before you buy. If you need to void an unused label, you can request that from the admin too, subject to the carrier's rules.</p>\n<h2 id=\"keep-customers-in-the-loop\">Keep customers in the loop<a href=\"#keep-customers-in-the-loop\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Customers can follow their parcels from their receipt and account page, with tracking links and delivery updates by email. Tracking updates arrive through webhooks, with background polling as a backup.</p>\n<p>The release also makes webhook setup a pair of actions on the <strong>Shipping carriers</strong> screen: generate the secret, then register the webhook with your provider. Keep your store's scheduler running so tracking and customer notifications continue in the background.</p>\n<h2 id=\"try-it-before-buying-postage\">Try it before buying postage<a href=\"#try-it-before-buying-postage\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The built-in demo carrier quotes rates, produces sample labels and simulates a parcel's journey without contacting a shipping provider or charging for postage. It is also running on the <a href=\"https://demo.kahunacart.com/admin\">live demo</a>, where you can browse the read-only admin using the sign-in details on its login page. The <a href=\"/add-ons/shipping\">Shipping page</a> has screenshots of the carrier settings, packing, labels and tracking.</p>\n<h2 id=\"get-started\">Get started<a href=\"#get-started\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Shipping is a separate add-on for <strong>KahunaCart 1.2.10 or later</strong>. Add it to your licence in the <a href=\"/shop/product/kahunacart-shipping\">store</a>, install it through Grav's plugin manager and follow the <a href=\"/docs/shipping/getting-started\">setup guide</a> to connect your provider, add a ship-from address and configure your boxes and services.</p>\n<p>The add-on is a one-time purchase for your site, with updates covered by your site's single renewal. Postage and any shipping-provider fees are paid separately through your provider account.</p>\n<p>If you sell physical products with Grav, I would love to hear how it works for your store. Come join us on <a href=\"https://discord.gg/vMs3UTEAg4\">Discord</a>.</p>\n<p>Andy</p>",
            "date_published": "2026-09-16T12:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.1.0"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-available",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-available",
            "title": "KahunaCart is out. Go build a store.",
            "summary": "The cart is open, 1.2.8 is on GPM, and the first 50 customers still get 50% off and the founders renewal rate. Here is what you get and how to have a shop running in an hour. (Early access ended on 13 October 2026.)",
            "content_html": "<p>I have wanted this for about eight years. Every time someone asked \"can I sell things with Grav?\" the honest answer was a shrug and a link to a hosted cart that hijacks your checkout page, your customer data and a slice of every sale. I wanted the opposite: a shop that lives inside a real Grav site, on your server, in your theme, with your content around it. So we built one. Today <strong>KahunaCart</strong> is for sale, <strong>1.2.8</strong> is on GPM, the docs are up, the demo is up, and there is a Discord server waiting for you.</p>\n<h2 id=\"the-deal\">The deal<a href=\"#the-deal\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p><strong>$249 per site, once.</strong> That buys the whole platform, the first year of updates and support, and no transaction fees, ever. Unlimited products. Unlimited orders. Unlimited customers. Stripe, Polar, PayPal, offline payments and a test provider are in the box, so you can take money the same afternoon.</p>\n<p>Three add-ons are available today, and each one attaches to the same licence key:</p>\n<ul>\n<li><strong>License Keys</strong> ($129) sells software keys and handles activations and update windows. It is the add-on selling you this plugin right now!</li>\n<li><strong>Subscriptions</strong> ($129) runs recurring plans, with the billing calendar kept by the provider or by the store.</li>\n<li><strong>Newsletters</strong> ($149) is the mailing list, the campaigns and the automations, built on the customer records the shop already has.</li>\n</ul>\n<p>After the first year, renewal is $99 and optional. It is per licence and it covers everything on that licence. Just KahunaCart? $99 renews KahunaCart. KahunaCart plus all three add-ons on one key? The same $99 renews all four. And if you skip a year, nothing switches off. I have paid enough per-extension renewals in my life to know how it feels, and I was not going to do that to you.</p>\n<h2 id=\"early-access-50-off-for-the-first-50-customers\">Early access: 50% off for the first 50 customers<a href=\"#early-access-50-off-for-the-first-50-customers\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p><strong>Update: early access ended on 13 October 2026.</strong> The founders who bought with a code keep their $49 renewal for the life of the licence.</p>\n<p>If you are on the launch list, go check your inbox. Your email has a code worth <strong>50% off KahunaCart and every add-on</strong>, good for thirty days, and buying with it puts you in the founders group with a renewal of <strong>$49 a year for the life of the licence</strong>.</p>\n<p>Not on the list? You have not missed it. <a href=\"/#newsletter\">Sign up on the homepage</a> and the same code comes straight to you. The offer is for the first 50 customers. Your code works up to five times, so you can come back for an add-on or a second licence, and it closes when the fiftieth customer has bought. But there is a catch, and it is the good kind: I want to hear what breaks. Early access exists so the rough edges get found by real stores before the wider launch, and I read every reply.</p>\n<h2 id=\"from-zero-to-a-store-in-an-hour\">From zero to a store in an hour<a href=\"#from-zero-to-a-store-in-an-hour\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>KahunaCart needs <strong>Grav 2.1.0</strong>, which shipped last week. Put your key in <code>user/data/licenses.yaml</code> and run:</p>\n<pre><code class=\"language-bash\">bin/gpm install kahunacart</code></pre>\n<p>Prefer clicking? Install it from <strong>Plugins</strong> in the admin and enter the key through <strong>License Manager</strong>. Prefer skipping all of that? Your account page has a ready-to-run package: a complete Grav site with KahunaCart, every add-on your key covers, the free <strong>Kahuna</strong> theme, a sample catalogue and your key already inside. Unzip it, point a web server at it, and you are looking at a store. The <a href=\"/docs/start/getting-started\">Getting started</a> chapter takes it from there.</p>\n<h2 id=\"is-it-ready\">Is it ready?<a href=\"#is-it-ready\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>This is a 1.2 release, not a 1.0. There are 15,000 automated tests behind it, across the core and the add-ons. The published benchmark serves a catalogue page in 204 ms at the 95th percentile with 250,000 orders in SQLite, on a two-core server. The nine payment providers beyond the bundled four are built and tested and marked coming soon on the site. They go out as customers ask, so if you need Mollie, Paddle or Square, tell me and you will move it up the list.</p>\n<p>Want to kick the tyres first? The <a href=\"https://demo.kahunacart.com\">live demo</a> resets every hour. Break whatever you like.</p>\n<h2 id=\"come-say-hello\">Come say hello<a href=\"#come-say-hello\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The <a href=\"https://discord.gg/vMs3UTEAg4\">KahunaCart Discord</a> is where the questions, the bugs and the opinions go, and I am there most days. The <a href=\"/docs\">docs</a> cover every screen, and the <a href=\"/shop\">store</a> has the plain answers on renewals and add-ons.</p>\n<p>Thank you to everyone who signed up early, and to the Grav community for years of patience while I built this. It is a very good day. Go build a store!</p>\n<p>Andy</p>",
            "date_published": "2026-09-14T08:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Article"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-1-2-0",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-1-2-0",
            "title": "KahunaCart 1.2.0: Checkout and store operations",
            "summary": "Cart recovery, configurable checkout fields, consolidated settings, expanded shipping and tax options, and faster reporting.",
            "content_html": "<p><strong>KahunaCart 1.2.0</strong> adds 66 changes to checkout and store operations. It remains a private release, with early access planned for a later version.</p>\n<h2 id=\"cart-recovery-and-customer-communication\">Cart recovery and customer communication<a href=\"#cart-recovery-and-customer-communication\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Cart recovery emails include a signed link that restores the cart using current prices. The workflow sends one reminder, and the Recovery screen reports restored carts and the recovery rate.</p>\n<p>Customers can also receive shared-cart links and email sign-in links. The email log records messages and retries. Order notices preserve changes such as an expired coupon or unavailable item, while a separate queue highlights orders requiring staff attention.</p>\n<h2 id=\"checkout-fields-and-addresses\">Checkout fields and addresses<a href=\"#checkout-fields-and-addresses\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Checkout fields can be configured with conditions and placed in four template regions. Add-ons and merchants can add fields such as delivery dates or gift messages without overriding a template.</p>\n<p>Country records hold editable address layouts, required fields, postcode rules and phone rules. The system covers 249 country records, with approximately sixty address layouts described in this release. Pickup locations add addresses and opening hours, with tax based on the collection location. Quantity limits and deposits provide more control over purchases and payment timing.</p>\n<h2 id=\"settings-and-order-status\">Settings and order status<a href=\"#settings-and-order-status\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Store settings are available within the KahunaCart admin, with search and detection of conflicting options. Configuration can be exported and imported between stores.</p>\n<p>Order, payment and fulfillment statuses have explicit behavior flags, including whether they count as paid, affect stock or represent a final state. Customer-facing labels can differ from internal labels.</p>\n<h2 id=\"shipping-and-tax\">Shipping and tax<a href=\"#shipping-and-tax\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Shipping supports six rate types, cost expressions, class policies and multiple parcels with separate methods. Unavailable methods display a reason. Live carrier integrations remain outside core.</p>\n<p>Tax-inclusive pricing can vary by region. Tax tables support compound rates and CSV import and export, with an extension interface for regional tax services.</p>\n<h2 id=\"performance\">Performance<a href=\"#performance\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Filtered reports are ten times faster in the measured workload, and a migrator memory leak has been fixed. The published mixed-load SQLite run recorded a median of 147 ms and p95 of 377 ms on the 2 vCPU server with 250,000 orders, with zero errors. See the <a href=\"/benchmarks\">benchmarks</a> for the test conditions.</p>\n<h2 id=\"add-on-rollback-and-analytics\">Add-on rollback and analytics<a href=\"#add-on-rollback-and-analytics\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Compensating hooks pair an add-on action with an undo. If a later step fails, completed actions are undone in reverse order. Five lifecycle points support this: order completion, payment capture, fulfillment creation, cancellation and refund. The event list now contains 71 events.</p>\n<p>Analytics adds period comparisons, twelve metrics, sixteen filters and shareable views. Orders record their source, while item costs and margin are recorded at sale time so later price changes do not alter historical figures.</p>\n<p>The <a href=\"/docs\">documentation</a> covers setup and behavior. Questions and feedback are welcome on the Grav <a href=\"https://chat.getgrav.org\">Discord</a>.</p>\n<p>Andy</p>",
            "date_published": "2026-09-02T18:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.2.0"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-1-1-0",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-1-1-0",
            "title": "KahunaCart 1.1.0: Payments and add-on development",
            "summary": "Payment authorization, saved methods, express checkout and new interfaces for custom fields, add-ons and integrations.",
            "content_html": "<p><strong>KahunaCart 1.1.0</strong> adds 58 changes focused on payments and extension development. It follows two fixes for report date ranges and misspelled searches on small stores. This remains a private release; early access will open later.</p>\n<h2 id=\"authorize-payments-before-capture\">Authorize payments before capture<a href=\"#authorize-payments-before-capture\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Supported providers can reserve the payment amount at checkout and capture it later. This lets staff check stock or prepare an order before collecting payment. If the order is cancelled, the authorization can be voided instead.</p>\n<p>Stripe, PayPal, Checkout.com, Mollie and Shopify support this workflow. Payment records, order status and events track the result.</p>\n<h2 id=\"saved-methods-and-express-checkout\">Saved methods and express checkout<a href=\"#saved-methods-and-express-checkout\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Stripe is the first implementation of saved payment methods, using a provider token associated with the customer. It also supports off-session charging.</p>\n<p>The cart and checkout have designated locations for Apple Pay, Google Pay and Link buttons. A shared styling contract lets themes present supported providers consistently.</p>\n<p>Provider credentials are encrypted when saved. Capability documentation is generated from provider declarations and checked by tests. All 13 providers now declare the 1.1.0 contract, with subsequent changes following semantic versioning.</p>\n<h2 id=\"add-on-interfaces\">Add-on interfaces<a href=\"#add-on-interfaces\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Typed custom fields can be added to products, variants, orders, order items, customers and addresses. They use indexed columns and can be declared by an add-on or configured by a merchant.</p>\n<p>Add-ons also gain a settings store, thirteen interface extension zones, named event constants and five plugin generators. The test harness provides an in-memory store for add-on tests.</p>\n<h2 id=\"emails-and-integrations\">Emails and integrations<a href=\"#emails-and-integrations\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Store emails are listed in one place with enable controls and template selection. Staff can preview messages against an order without sending them. Object templates provide editable formats for references, filenames and subjects.</p>\n<p>Outbound webhooks support signed deliveries, retries and a delivery log. The storefront JSON API provides cart and checkout operations for JavaScript frontends and can be disabled in configuration.</p>\n<h2 id=\"installation-checks\">Installation checks<a href=\"#installation-checks\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The check command runs nine installation checks. The release also adds a job-queue screen, test-data reset options and reports for outdated template overrides. Admin tests identified and fixed two screens that failed when opened.</p>\n<p>See the <a href=\"/docs\">documentation</a> for configuration and developer details, or join the Grav <a href=\"https://chat.getgrav.org\">Discord</a> to discuss the release.</p>\n<p>Andy</p>",
            "date_published": "2026-09-02T09:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.1.0"
            ]
        },
        {
            "id": "https://kahunacart.rhuk.net/news/kahunacart-1-0-0",
            "url": "https://kahunacart.rhuk.net/news/kahunacart-1-0-0",
            "title": "KahunaCart 1.0.0: Core store features",
            "summary": "The first internal release brings catalog, checkout, orders, tax, shipping, promotions, downloads and customer accounts to Grav.",
            "content_html": "<p>I've been building Grav since 2014, and store owners have often asked how to add ecommerce to their sites. KahunaCart is my answer: a store that uses Grav's theme and admin while keeping commerce records in a database.</p>\n<p><strong>KahunaCart 1.0.0</strong> is the internal milestone for the core store features. It includes 342 changelog entries. At this stage, only two of us have installed it and it is not yet for sale. Early access will open with a later version.</p>\n<h2 id=\"content-and-commerce\">Content and commerce<a href=\"#content-and-commerce\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Products, orders, customers and price adjustments use SQLite, MySQL or PostgreSQL. Grav content, templates and configuration remain in files. SQLite is the default and does not require a separate database server.</p>\n<p>The storefront includes 27 Twig templates and 42 Twig functions. Themes can override individual templates or add product grids, buy buttons and cart components to existing pages.</p>\n<h2 id=\"store-operations\">Store operations<a href=\"#store-operations\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>Prices use currency-aware calculations in minor units. Order numbers and stock changes use database transactions. Staff can configure order statuses and prepare order changes before confirming them.</p>\n<p>Store credit and gift cards share a transaction ledger. Refunds can return to the payment provider, store credit or both, with credit notes recording the refund.</p>\n<p>Price lists, quantity tiers, customer groups, coupons and sales cover several pricing models. Promotions include buy-one-get-one and cheapest-item-free offers. The documented price-selection rules have corresponding tests.</p>\n<p>The release also includes ranked product search, partial shipments, tracking numbers, packing slips and CSV imports with saved mappings and previews. Reports use daily summaries to reduce repeated work on the order history.</p>\n<h2 id=\"testing\">Testing<a href=\"#testing\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The benchmark dataset contains 5,000 products, 25,000 customers and 250,000 orders. On the published 2 vCPU server, SQLite catalog requests reached a p95 of 204 ms with no errors, down from approximately 7.4 seconds before the performance changes. The <a href=\"/benchmarks\">benchmark page</a> includes the workload and earlier runs.</p>\n<p>The release has 5,235 test methods. Tests also check the administration API documentation and MCP manifest against the implementation.</p>\n<h2 id=\"next-steps\">Next steps<a href=\"#next-steps\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"></a></h2>\n<p>The next releases focus on payment integrations, add-ons and day-to-day operations. The <a href=\"/docs\">documentation</a> covers the current features. You can also find me on the Grav <a href=\"https://chat.getgrav.org\">Discord</a>.</p>\n<p>Andy</p>",
            "date_published": "2026-08-31T12:00:00+00:00",
            "authors": [
                {
                    "name": "Andy Miller"
                }
            ],
            "tags": [
                "Release",
                "1.0.0"
            ]
        }
    ]
}
