Merx – Plugin to create online shops with Kirby 3

, ,

Hi @stffr,

everything you want to do is possible with Merx.

  1. You can use hooks to throw errors when the cart does not fulfill your requirements.
  2. See Shipping Costs cookbook
  3. Merx does not rely on any payment method. You can chose whatever you like.

Generally speaking Merx Starterkit might be a good starting point. It comes with an “Prepayment” (“Invoice”) payment method, you can remove the other payment methods.

Hello there, hello @tobiasfabian ,

I am very happy that I completed and launched the first webshop I made with Merx. I love Kirby’s flexibility and so Merx seemed like the right choice for me and my client. And since we had some custom functionalities we wanted to implement, Merx’s flexibility was perfect and very useful.

The webshop is for a fashion label based in Munich, Germany.

Some of the custom functionalities that I implemented are:

  • Custom discount system with expiration dates, use limits, exclusion of certain categories, automatic discount creation for new newsletter subscribers
  • Connection between the products and the label’s ERP
  • Multilanguage
  • Frontend switch between shipping countries recalculates the shipping cost, updates tax rates and updates prices
  • Refunds can be made via the Stripe and Paypal API and automatic emails are being sent

There are some more but I think these are some of the more interesting.
There is still some work that has to be done on the online shop, also some SEO and stuff like that, but I am super happy with the result so far.

Again, this project showed me what can be done with Kirby and I have to thank @tobiasfabian for all the great work on this plugin. Working with it is a lot of fun and I can implemented just everything I could possbibly think of.

Here is the URL: https://hannibal-collection.com

Sigi

Hi @tobiasfabian,

My collaborator and I are working on a design education e-commerce platform, and we are testing out Merx; this is really a great plugin. We love its flexibility. Thanks for all your work!

A quick question: Does Merx support having a user signup page + a user account page that archives all the previous purchases? We are hoping to let each user be able to browse the courses they enrolled in on that page. If it is not a built-in functionality in Merx, we were wondering if you have any guidance how to implement by custom code.

Thanks in advance for your time!

Hi @mkimk,

welcome to the forum and thank you for having a look at Merx.

hannibal-collection.com has a login page. So yes it’s possible. But: You have to develop it by your own. I can’t give you a specific guidance how to implement this. In general you can use the Starterkit and try to integrate a user management.

merx plugin is broken when running kirby 3.7.1 (or newer). can you take a look? thanks.

Hello there,

does anyone here have some experience with re-calculating tax and shipping costs based on the selected country on the checkout page? I’m not 100% sure how to address this problem.

Hi @mafleig,

In the model for my shipping product, I have a price function that recalculates the price dynamically based on the destination and tax. For the tax rates, I have a structure field in the blueprint that I have to update regularly. The code looks something like this:

public function price(): Field
    {

        if (Cookie::get('shipping-destination')) {
            $currenttax = kirby()->site()->taxsettings()->toStructure()->findBy('country', Cookie::get('shipping-destination')) ? kirby()->site()->taxsettings()->toStructure()->findBy('country', Cookie::get('shipping-destination'))->tax()->toFloat() : 0.0;
            $destination = Cookie::get('shipping-destination');
        } else {
            $currenttax = 19;
            $destination = 'DE';
        }

        if ($destination == 'DE') {
            $value = 7.00;
            return new Field($this, 'price', $value);
        } else {
            $value = 10.08 * (1 + ($currenttax / 100));
            return new Field($this, 'price', $value);
        }

    }

Hope this helps you to find a solution. Also I got a lot out of the Starterkit’s code.

Sigi

Thanks @sigi , this looks very nice and simple. I also got use of the Starterkit a lot, so if you change the country on the order page, the tax an shipping should be updated, right?

Hi @mafleig,

Yes, exactly. I implemented that with JS that reads the input of the country field and a route in the config.php that gets called and sets the cookie or session storage for the shipping destination.

Sigi

Reading the country field with JS is working fine, but i’m struggling with passing/reading the cookie or session storage. Are there any resources or is there a use case inside the startekit related to this? Couldn’t find anything so far. I’d really like to dive deeper there but don’t know where to start. My goal is to work this out by myself rather than just copy any stuff together and see if it works.

Hi @mafleig,

Cookie and session storage docs are here:

I think, in the checkout.js of the starterkit, there is an example of how to fetch a POST request with JS.

Sigi

Hi,
I am considering Merx for a new ecommerce build (have used Snipcart previously). Can I check that it is possible to use ApplePay / Google Wallet as payment methods? Ideally I would like these alongside something like Stripe for card payments and PayPal. If anyone has any info that would be lovely

Hi,
for Merx there is a plug-in to use Molli as payment gateway. It support all you needed payment options.

Hey @sigi , Some months passed, and I’m still desperate with this one. I just can’t figure out in what order the actions should occur and which files i have to modify at all. A vague guess: the model and routing are the main problem. Really sorry for the mess. :sweat:

Hi @mafleig , maybe you want to share some of your code so I can see the problem?

Well, first thing i did was creating a country/tax field inside the shipping.yml:

  taxsettings:
      type: structure
      fields:
        country:
          type: text
        vat:
          type: number

Works fine, next step: Utilizing it inside the checkout.php:

<select data-action="country" id="country" name="country">
<option value=""selected disabled hidden>Auswählen...</option>
<?php 
$items = $site->find('shipping')->taxsettings()->toStructure();
foreach ($items as $item): 
?>
 <option value="<?= $item->country() ?>"><?= $item->country() ?></option>
<?php endforeach ?>
</select>

Also works fine, next one: creating a cookie via JS inside checkout.js:

const selectElement = document.querySelector('select[data-action="country"]');
function updateCountry() {
var countryValue = selectElement.options[selectElement.selectedIndex].value;
document.cookie = "shipping-destination=" + countryValue + "; SameSite=Lax";
}  
selectElement.addEventListener('change', updateCountry);

Also works fine, cookie updates as intended.
And this is where it gets messy.

Problem 1: I am quite new to routes in general, so i couldn’t find a good example to start with for my use case. As you already said, it has to go inside my config, right?

Problem 2: POST request with JS. Same here basically. Not enough experience yet. Especially where to place it.

Problem 3: The Model. The example you gave earlier basically covers everything, right? mine looks like this right now:

<?php

class ShippingPage extends Page
{

    public function price(): Field
    {

        if (Cookie::get('shipping-destination')) {
            $currenttax = kirby()->site()->taxsettings()->toStructure()->findBy('country', Cookie::get('shipping-destination')) ? kirby()->site()->taxsettings()->toStructure()->findBy('country', Cookie::get('shipping-destination'))->tax()->toFloat() : 0.0;
            $destination = Cookie::get('shipping-destination');
        } else {
            $currenttax = 19;
            $destination = 'DE';
        }

        if ($destination == 'DE') {
            $value = 7.00;
            return new Field($this, 'price', $value);
        } else {
            $value = 10.08 * (1 + ($currenttax / 100));
            return new Field($this, 'price', $value);
        }

    }

     public function tax(): float
    {
        if ($this->content()->tax()->isEmpty()) {
            return 0;
        }
        return $this->kirby()->option('taxRates')[$this->content()->tax()->toString()];
    }
}

Really sorry, I know it’s slightly over my head. I started the project without having this in mind, but I’ve come so far and this is the only thing that’s missing to make the project run perfectly.

Hi @mafleig , does the model read your cookie correctly? I had some issues with setting cookies in JS in the past, that’s why I am setting the cookie also via a route in the config.php.

For more information about routes, see here: Routing | Kirby CMS

Exemplary route:

[
      'method' => 'post',
      'pattern' => 'update-destination',
      'action' => function() {
        try {
          $data = kirby()->request()->data();

          Cookie::set('shipping-destination', $data['destination']);
          kirby()->session()->set('shipping-destination', $data['destination']);

          return [
            'status' => 200,
            'data' => $data,
          ];
        } catch (Kirby\Exception\Exception $ex) {
          return $ex->toArray();
        }
      },
    ],

An example to call a route from JS:

element.addEventListener('change', () => {
    const body = new FormData();
    body.append('destination', element.value);
    fetch('/update-destination', {
      method: 'POST',
      credentials: 'same-origin',
      body,
    }).then(response => response.json()).then((data) => {
      if (data.status === 200) {
        // success event
      } else if (data.status === 400 && data.message) {
        console.log(data.message);
      }
    });
  });

Just a rough example of course.

Your model looks fine to me.

Happy new Year everybody. I am looking in mere right now and I love it so far. It is a great plugin and will be used in the future. Thanks @tobiasfabian, great work.

I am not sure if it has been asked before but I couldn’t find it in the docs or here in the forum. Is there an easy way to change the currency and to recalculate the prices based on a factor?
I know that other CMS e-commerce systems have either a auto fetch version of the factor to be added/subtracted online, or it can be changed with a fixed value, to prevent quick price changes.

Any help on addressing the problem and/or a future feature would be great to achieve this easily.

Cheers,
Simon

Hi everyone, after leaving the checkout I don’t get redirected and just see the following JSON file. Any ideas how to address this problem? (I use the mollie payment plugin, but it also appears in the non-modified starterkit.)

{"status":201,"redirect":"https:\/\/www.mollie.com\/checkout\/select-method\/XXXXXXXX"}

Hi @mafleig,

do you use the Starterkit? If so, you see the response of /api/shop/checkout which is defined in site/plugins/site/api/routes/checkout-post.php. This API response is meant to be handled by JavaScript (assets/js/templates/checkout.js). Redirection is handled by JavaScript.

Can you check if your JavaScript is working properly?

If you don’t want to use JavaScript you can update site/plugins/site/api/routes/checkout-post.php. Instead of returning an JSON array you can use go($redirect) for redirection.