# Upgrade Merx 1 to 2

> What changed between Merx 1 and Merx 2, and what you have to rewrite.

Merx 2 renames a lot and moves a few things to a different place entirely. Work through the sections below in order; each one is independent.

## Order page model

The abstract model is gone. Extend the class instead.

```php title="/site/models/order.php (Merx 1)"
class OrderPage extends OrderPageAbstract
{
```

```php title="/site/models/order.php (Merx 2)"
class OrderPage extends \Wagnerwagner\Merx\OrderPage
{
```

## Options

Every option moved namespace.

`ww.merx.[name]` → [`wagnerwagner.merx.[name]`](https://merx2.wagnerwagner.de/reference/options.md)

Three are gone. `currency` and `currencySymbol` are replaced by [pricing rules](https://merx2.wagnerwagner.de/guide/configuration/currencies.md), which set the currency per rule instead of once for the whole shop. `successPage` is replaced by the [API](https://merx2.wagnerwagner.de/guide/api/concept.md) — see *The success page* below.

Everything else is new, including [`license`](https://merx2.wagnerwagner.de/reference/options/config-options/license.md), which a production shop needs. The [config options reference](https://merx2.wagnerwagner.de/reference/options/config-options.md) lists them all.

## Products

A product carried its tax rate as a percentage. It now names a tax rule, and the rate lives in the config.

```kirby title="/content/products/1_bike/product.txt (Merx 1)"
Price: 2000

----

Tax: 19
```

```kirby title="/content/products/1_bike/product.txt (Merx 2)"
Price: 2000

----

Taxrule: default
```

The rule itself goes into [`taxRules`](https://merx2.wagnerwagner.de/reference/options/config-options/tax-rules.md), with the rate as a decimal: `Tax: 19` becomes `'rate' => 0.19`. See [Taxes](https://merx2.wagnerwagner.de/guide/configuration/taxes.md).

Merx counts the [stock](https://merx2.wagnerwagner.de/guide/getting-started/stock.md) itself. A `Stock` field keeps working as it is, but delete the hooks that lowered it, or every order lowers it twice. The method returns the number now, not the field:

`$page->stock()->toFloat()` → [`$page->stock()`](https://merx2.wagnerwagner.de/reference/classes/product-page/stock.md)

## Hooks

`initializePayment:before` → [`initializeOrder:before`](https://merx2.wagnerwagner.de/reference/options/hooks/initialize-order-before.md)
`initializePayment:after` → [`initializeOrder:after`](https://merx2.wagnerwagner.de/reference/options/hooks/initialize-order-after.md)
`completePayment:before` → [`createOrder:before`](https://merx2.wagnerwagner.de/reference/options/hooks/create-order-before.md)
`completePayment:after` → [`createOrder:after`](https://merx2.wagnerwagner.de/reference/options/hooks/create-order-after.md)

The single `ww.merx.cart` hook is gone. It fired whenever a cart was built; there is now one pair of hooks per operation.

`cart` → [`cart.add:after`](https://merx2.wagnerwagner.de/reference/options/hooks/cart-add-after.md), [`cart.remove:after`](https://merx2.wagnerwagner.de/reference/options/hooks/cart-remove-after.md) and [`cart.updateItem:after`](https://merx2.wagnerwagner.de/reference/options/hooks/cart-update-item-after.md)

Apart from that, there are many more [new hooks](https://merx2.wagnerwagner.de/reference/options/hooks.md).

## Merx methods

`Merx::initializePayment` → [`Merx::initializeOrder`](https://merx2.wagnerwagner.de/reference/classes/merx/initialize-order.md)
`Merx::completePayment` → [`Merx::createOrder`](https://merx2.wagnerwagner.de/reference/classes/merx/create-order.md)
`Merx::formatPrice` → [`Merx::formatCurrency`](https://merx2.wagnerwagner.de/reference/classes/merx/format-currency.md)

`Merx::calculateNet`, `Merx::getFieldError`, `Merx::setMessage` and `Merx::getMessage` are gone without a replacement. Net prices are part of [`Price`](https://merx2.wagnerwagner.de/reference/classes/price.md) now, and field errors come back from the [checkout endpoint](https://merx2.wagnerwagner.de/reference/api/routes/shop-checkout-post.md) as JSON.

`formatCurrency` takes a currency, where `formatPrice` used the global option. You can wrap it.

```php title="/site/config/config.php"
<?php
if (!function_exists('formatPrice')) {
  function formatPrice(?float $price) {
    return Merx::formatCurrency($price, 'EUR');
  }
}
```

## Cart and items

`$cart->append()` → [`$cart->add()`](https://merx2.wagnerwagner.de/reference/classes/cart/add.md)
`$cart->update([…])` → [`$cart->updateItem($key, […])`](https://merx2.wagnerwagner.de/reference/classes/cart/update-item.md), one item at a time
`$cart->getSum()` → [`$cart->total()`](https://merx2.wagnerwagner.de/reference/classes/cart/total.md)
`$cart->getTax()` → `$cart->total()->tax`
`$cart->getTaxRates()` → [`$cart->taxRates()`](https://merx2.wagnerwagner.de/reference/classes/cart/tax-rates.md)

`getFormattedItems()` is gone. Items format themselves — see *Reading cart items* below.

### Adding to the cart

`add()` took an `id` plus arbitrary keys at the top level. It now accepts the parameters of [`new ListItem()`](https://merx2.wagnerwagner.de/reference/classes/list-item/__construct.md) and nothing else, so any other key fails with `Unknown named parameter`.

The identifier is `key` instead of `id`, the product page is `page`, and your own attributes belong in `data`. Leaving out `key` makes Merx use the page’s UUID.

```php title="Merx 1"
$cart->add([
  'id' => 'products/nice-shoes',
  'color' => 'red',
]);
```

```php title="Merx 2"
$cart->add([
  'key' => 'nice-shoes-red',
  'page' => 'products/nice-shoes',
  'data' => ['color' => 'red'],
]);
```

### Reading cart items

Cart items were arrays. They are [`ListItem`](https://merx2.wagnerwagner.de/reference/classes/list-item.md) objects now, in the cart as well as in [`$orderPage->cart()`](https://merx2.wagnerwagner.de/reference/classes/order-page/cart.md).

`$cartItem['id']` → `$item->page`
`$cartItem['quantity']` → `$item->quantity`
`$cartItem['price']` → `$item->price`

## Order page

`$orderPage->invoiceNumber()` → [`$orderPage->orderNumber()`](https://merx2.wagnerwagner.de/reference/options/config-options/order-number.md)
`$orderPage->invoiceDate()` → `$orderPage->dateCreated()`
`$orderPage->payedDate()` and `paidDate()` → `$orderPage->datePaid()`
`$orderPage->paymentComplete()` → [`$orderPage->isPaid()`](https://merx2.wagnerwagner.de/reference/classes/order-page/is-paid.md)
`$orderPage->formattedSum()` → [`$orderPage->total()`](https://merx2.wagnerwagner.de/reference/classes/order-page/total.md)

The order number is stored on the page now. Merx 1 derived it from the page’s sorting number, so it changed when the order was re-sorted. You can replace how it is built with the [`orderNumber`](https://merx2.wagnerwagner.de/reference/options/config-options/order-number.md) option.

### Existing orders

Merx does not migrate the orders you already have. Rename their fields yourself, or leave them as they are if you only keep them for the record.

`Invoicedate` → `Datecreated`
`Paiddate` → `Datepaid`
`Paymentmethod` → `Paymentgateway`

`Paymentcomplete` is gone. An order is paid once it has a `Datepaid`.

Every entry in `Items` needs a `type` — `product` for anything you sell. Merx 1 wrote `template` instead.

And because the order number is a field now, an order without `Ordernumber` stays untitled.

## Blueprints

Kirby 6 replaced sections with fields, and so did Merx.

`sections/order` → [`fields/order`](https://merx2.wagnerwagner.de/reference/blueprints/fields/order.md), a field group
`sections/orders` → [`fields/orders`](https://merx2.wagnerwagner.de/reference/blueprints/fields/orders.md), a `pagelist` field
`sections/payment` → [`fields/payment`](https://merx2.wagnerwagner.de/reference/blueprints/fields/payment.md), a field group

Where you extend one of them, change the name and move it from `sections:` to `fields:`. Under `sections:`, Kirby shows an error in its place.

> [!IMPORTANT]
> If you replaced one of them with a file of your own, such as `/site/blueprints/sections/orders.yml`, move it to `/site/blueprints/fields/` and turn it into a field: `type: fields` becomes `type: group`, `type: pages` becomes `type: pagelist` and `headline` becomes `label`. A file left in `sections/` is ignored without an error.

[`layouts/order`](https://merx2.wagnerwagner.de/reference/blueprints/layouts/order.md) also has a Personal data tab now. It shows [`fields/personal-data`](https://merx2.wagnerwagner.de/reference/blueprints/fields/personal-data.md), and that is where your customer fields go — see [Validate checkout](https://merx2.wagnerwagner.de/guide/getting-started/validate-checkout.md). Kirby’s [Sections become fields](https://getkirby.com/releases/6/blueprints#sections-become-fields) covers your other blueprints.

## The success page

Merx 1 created a virtual success page and sent the customer there. There is none in Merx 2.

The provider returns the customer to [`/api/shop/success`](https://merx2.wagnerwagner.de/reference/api/routes/shop-success-get.md), which completes the payment, writes the order page and redirects to it. Delete the `successPage` option and the template that went with it.

## Error messages

Three translation keys were renamed along with the methods.

`error.merx.initializePayment` → [`error.merx.initializeOrder`](https://merx2.wagnerwagner.de/reference/options/translations/error-merx-initializeorder.md)
`error.merx.completePayment` → [`error.merx.createOrder`](https://merx2.wagnerwagner.de/reference/options/translations/error-merx-createorder.md)
`error.merx.noPaymentMethod` → [`error.merx.noPaymentGateway`](https://merx2.wagnerwagner.de/reference/options/translations/error-merx-nopaymentgateway.md)

If you overrode any of them, the [translations reference](https://merx2.wagnerwagner.de/reference/options/translations.md) lists every string Merx ships today.

## Payment methods and gateways

Payment methods `credit-card-sca`, `credit-card`, `sepa-debit`, `klarna` and `ideal` are removed in favor of `stripe-elements`. You can still use all payment methods mentioned and many more with [Stripe Elements](https://merx2.wagnerwagner.de/guide/payment-gateways/stripe-elements.md).

The field that selects one is `paymentGateway`, not `paymentMethod`.
