Merx – Plugin to create online shops with Kirby 3

Hi @chriszzz,
Merx is more like a toolset for creating an online shop. You have to develop and design the cart yourself. You can create a very simple cart with 29 lines of code:

Yes, the cart information are stored in a cookie that looks like this.

a:7:{s:9:"startTime";i:1576146716;s:10:"expiryTime";i:1576153916;s:8:"duration";i:7200;s:7:"timeout";i:1800;s:12:"lastActivity";i:1576146716;s:9:"renewable";b:1;s:4:"data";a:1:{s:17:"ww.merx.cartItems";a:2:{s:22:"products/knitted-socks";a:7:{s:2:"id";s:22:"products/knitted-socks";s:8:"quantity";s:1:"2";s:5:"title";s:13:"Knitted socks";s:5:"price";d:14.99;s:3:"tax";d:2.3933613445378152;s:3:"sum";s:5:"29.98";s:6:"sumTax";s:15:"4.7867226890756";}s:24:"products/colorful-gloves";a:7:{s:2:"id";s:24:"products/colorful-gloves";s:8:"quantity";s:1:"4";s:5:"title";s:15:"Colorful Gloves";s:5:"price";d:19.95;s:3:"tax";d:3.1852941176470586;s:3:"sum";s:4:"79.8";s:6:"sumTax";s:15:"12.741176470588";}}}}

Oh great, I’ll give it a try!
Sorry, completely missed your answer :slight_smile:

Dear Tobias,
thanks a lot for the plainkit!! I couldn’t find time yet to try and understand how it works, but i just gave it a try and fail already to generate the necessary keys. At the moment, i’d just like to try the paypal option and i could generate the ww.merx.paypal.sandbox.clientID and the ww.merx.paypal.sandbox.secret
But i have absolutely no idea how to obtain a ww.merx.paypal.sandbox.experienceProfileID . If i check the link you provide to the delevoper page from paypal, i can only find it referenced here:
but whatever is described there is way over my abilities…
Can you give me a hint on how this actually works? I think it would be extremely useful to have some kind of short guide in your documentation how this actually works… No worries, if this is too much work, i totally understand. It just sadly means i have to stop already at the doorstep…

Getting a PayPal experienceProfileID is indeed quite complicated and only relevant for some use cases. That’s why I removed it from the plugin a while ago. The two lines in the documentation where leftovers from a previous version of the plugin. I removed these two lines.

Hi, Tobias.

I just bought a Merx license, after experimenting with the plugin for a few days. The level of integration in Kirby is really nice! It leaves a lot of room for customizing the purchase process.

My main problem is with delivery issues (my shop sells mainly small items, editions and posters).
I associated a “weight” attribute to each product, and used the hook described here to calculate the total weight and deduct from it the general shipping costs.

Using the addition of a “shipping” item to the cart, a problem is that I have to calculate these costs in two places: both in the “ww.merx.cart” hook and in the “update-cart-item” route. In addition, I would like to propose a “pick up at the shop” option, and vary the shipping cost depending on the buyer’s address.

I think that the best way to calculate the shipping cost would be to wait until the shipping address (or even the shipping method) is determined, and to dissociate the shipping from the shopping cart. Would a method to be integrated to the “order” model be the right hypothesis?

Thanks in advance.


This looks awesome. I am curious if there is anyone who build a live webshop with Merx? Can you share the site with us? :smiley:

Welcome on board Julien and thanks for using Merx and buying a license. Your shop, as you describe it, sounds very promising. I’m looking forward to seeing it live in action. I am pleased to hear that Merx’s intended flexibility seems to work for you (calculate shipping based on weight of goods).

Regarding your question: Maybe you don’t need the ww.merx.cart hook.

For I update the cart via a merx-api/update-cart route when the country changes (Germans have to pay 19 % Vat). This country dropdown could be your pick up at the shop checkbox. So your cart-hook could be like this (untested):

'method' => 'post',
'pattern' => 'custom-api/update-cart',
'action' => function() {
  $cart = cart();
  if ((string)$_POST['pickup'] === 'on') {
  } else {
    // … calculate shipping costs based on weight and/or $_POST['country']
    $shippingCosts = 12.99;
      'id' => 'shipping',
      'price' => $shippingCosts,

If you need the same functionality in the ww.merx.cart hook and in your update-cart route adding a method to your OrderPage class could be an option but I think it’s not the ideal solution. You can create a global function at config.php which should be available in the hook and the route.

I hope this helps a little. If you want, I can take a closer look at the code and your implementation.

1 Like

Thanks a lot for your answer, Tobias.
I’m having some busy days, but I’ll be back on Monday. I’ll take a look at your proposal and let you know.
Since I’m mostly a frontend guy, I’d be happy if you could look at my way of doing things!

From my point of view (I’m not used to developing e-commerce sites), Merx seems very promising for lightweight shops (I had tried shopkit, from @samnabi, but the limit of compatibility - Kirby 2- as well as the very impactful approach on the structure of the site is a problem for me). I will publish the link here very soon.

1 Like

Thanks! I am looking forward :upside_down_face:

I spent the last weeks building an online shop with Merx and I have to agree to what has been said prior: Merx has a lot of potential to elevate Kirby and makes it very applicable for small and simple shops. I love it!

As I am coming more from a web design background, I yet have to fully get comfortable with the object-oriented way Kirby and Merx is put together.

That’s why I have a question with the order pages, more precisely with the completePayment gateway. I would like to add an invoice number directly, when the order page is created once the payment is complete. Regardless which payment option has been chosen. So the same as it already happens with payedDate.

I have seen that you have created a invoiceNumber function for the example shop, which uses the Kirby page sort number. The problem I have with that is, that if an order gets deleted, all invoice numbers get newly assigned. So one order suddenly gets a new invoice number, just because of the changed sorting of the pages. Having the invoice numbers unique und collision proof would be nice.

I guess it is possible to extend the completePayment function via the config.php, but I lack the skills to figure out how exactly this has to happen. Could you point me in the right direction?


I’m trying to add Merx to an existing website. I’ve resorted to using files and code from the plainkit as the documentation is somewhat lacking in detail for me. I’m using this code to add a product to the cart:

  <form action="add" method="post">
    <h3><?= $page->title() ?></h3>
    Price: <?= formatPrice($page->price()->toFloat()) ?><br>
    Tax: <?= formatPrice(calculateTax($page->price()->toFloat(), $page->tax()->toFloat())) ?><br>
    <input type="hidden" name="id" value="<?= $page->id() ?>">
    <input type="number" name="quantity" value="1" min="1">
    <button>add to cart</button>

And this in the config file:

use Kirby\Exception\Exception;

return [
  'debug' => true,
  'ww.merx.gateways' => [
    'empty-gateway' => [],
  'routes' => [
      'pattern' => 'add',
      'method' => 'post',
      'action'  => function () {
        $id = get('id');
        $quantity = get('quantity');
        try {
            'id' => $id,
            'quantity' => $quantity,
        } catch (Exception $ex) {
          return $ex->getMessage();

However it’s just sending me to the ‘Error’ page, and I cannot work out what’s missing compared to the plainkit (which I have working locally).

Considering the license for Merx is almost that of Kirby itself, I’d expect a little bit more from the documentation. Getting a basic setup working seems to be a common issue in this thread, and the Cookbook is way to vague in comparison to a Kirby Cookbook. I’d really love to use Merx on this project.

Hey @leuys,

thanks for the kind words and your question. I thought this might be a good one for a cookbook:

1 Like

Hey @jamiehunter,

You can try $ex->toArray() instead of $ex->getMessage() to get more details about what’s going wrong.

Where did you struggle most? What do you miss in detail in the documentation?

Yes, that’s it. Thank you @tobiasfabian!
Also, I now moved my functions that send the confirmation emails, form the page.changeStatus:after hook (as it was set in the demo shop) to the ww.merx.completePayment:after hook.
Because otherwise the confirmation emails couldn’t read the invoice number.
My understanding is, that the page.changeStatus:after hook gets called before the ww.merx.completePayment:after hook. Is that correct?

Yes, ww.merx.completePayment:after is definitely the better place for confirmation emails.

An yes page.changeStatus:after is called (two lines) before ww.merx.completePayment:after.

Maybe you have to use the $orderPage returned by $orderPage->update():

$orderPage = $orderPage->update([
  'invoiceNumber' => $invoiceNumber,

Hello and thank you for developing MERX @tobiasfabian. I am going to buy a license and use it to develop a store for a client.

Since I started with the docs I wanted to give my first impression, considering this would be the first e-store I build, and only having experience as a client.

What in first impression I miss is, perhaps, a one page overview of the basic needed steps and setup assumptions, in order, without going into much detail for each of them. Without being able to conceptualize the system as a whole, the ammount of tools and particular cases in each of them is causing me to… stray from the… path of understanding. :man_student:t4:

Alternatively a video or step by step tutorial on building the plainkit would probably be more than enough context.

I may be dense, though.

Thank you very much


I agree with @plagasul, a step-by-step guide of some kind – like how to add Merx to an existing Kirby project – would be really good. It seems like the example shop and Plainkit are set up definitely, and the documentation explains neither too well.

1 Like


Is the cart’s cookie a session cookie ? do we need to care about GDPR for this one?

Thank you

1 Like

Another question @tobiasfabian, bitte.

I assumed that if I want to store orders I have to do that manually, for example via a hook, or does merx actually creates the order .txt files at some point by default ?

Thank you