← Back to blog

Hyvä Checkout Explained: Magewire, Customization, Payments

Rodrigo Biazzi

Checkout is the part of a Hyvä project that needs a separate decision. The Hyvä theme replaces the storefront JavaScript stack, but the default Magento checkout is a large Knockout and UI component application that does not run on Hyvä. So every Hyvä store ends up with one of a few options for checkout. This article explains Hyvä Checkout from a developer's point of view: how it is built, how you customize it, what it means for payment integrations, and how it compares to keeping the Luma checkout.

Checkout options on a Hyvä store

There are three realistic paths:

  1. Luma checkout fallback. The storefront runs on Hyvä and the checkout routes are rendered with a Luma based theme. This works with every payment module that supports the standard checkout. The cost is that checkout loads the full RequireJS and Knockout stack, and you maintain styling in two themes.
  2. Hyvä Checkout. A separate product from the Hyvä team, built for Hyvä themes. Check hyva.io for its current licensing and pricing.
  3. A third party or custom checkout. Other checkout extensions exist with Hyvä support, and a fully custom checkout is possible on top of GraphQL or the REST API. This is the most expensive path to build and maintain.

The fallback is how most migrations start, because it decouples the theme launch from the checkout work. The broader trade-offs are in Hyvä vs Luma for developers.

How Hyvä Checkout is built

Hyvä Checkout is server rendered. It is built on Magewire, a port of the Laravel Livewire idea to Magento. Instead of a JavaScript application that calls REST endpoints, each part of the checkout is a PHP component with public properties and methods. The template binds form fields to those properties. When the user changes a field, Magewire sends a small request to the server, the PHP component runs, and the server returns the updated HTML for that component.

For a Magento developer this has a big practical effect. Most checkout customization is written in PHP and phtml, with layout XML deciding where things go. You do not need to learn the UI component and Knockout checkout, and you do not need to write mixins for Magento_Checkout/js/model/* files.

The trade-off is that interactions go to the server. Each field change that is bound live produces a request. Component design matters: bind fields so they update on change or on submit rather than on every keystroke, unless you really need live updates.

Checkout configuration and steps

Hyvä Checkout lets you define checkouts and their steps in configuration files named hyva_checkout.xml. A step maps to layout handles, which means the content of each step is controlled with normal layout XML. One step and multi step layouts are both possible, and the store admin can choose which checkout to use in the Hyvä Checkout configuration section. Read the hyva_checkout.xml files in the Hyvä Checkout package itself to see the step names and layout handles for your version before you start adding blocks.

Adding a custom field with Magewire

A typical request: add a delivery note field to the shipping step and save it on the quote. In the Luma checkout this means a UI component, a JavaScript mixin on the shipping save processor, an extension attribute and a plugin. That flow is described in adding a custom field to the Magento 2 checkout. In Hyvä Checkout it is a Magewire component and a template.

First, the component. It loads the current value when it mounts and saves the quote when the property is updated.

<?php
declare(strict_types=1);

namespace Acme\Checkout\Magewire;

use Magento\Checkout\Model\Session as CheckoutSession;
use Magento\Quote\Api\CartRepositoryInterface;
use Magewirephp\Magewire\Component;

class DeliveryNote extends Component
{
    public string $note = '';

    public function __construct(
        private readonly CheckoutSession $checkoutSession,
        private readonly CartRepositoryInterface $cartRepository
    ) {
    }

    public function mount(): void
    {
        $quote = $this->checkoutSession->getQuote();
        $this->note = (string) $quote->getData('acme_delivery_note');
    }

    public function updatedNote(string $value): string
    {
        $value = mb_substr(trim($value), 0, 255);
        $quote = $this->checkoutSession->getQuote();
        $quote->setData('acme_delivery_note', $value);
        $this->cartRepository->save($quote);

        return $value;
    }
}

The updatedNote method follows the Livewire naming convention: updated plus the property name. It runs after the property changes and its return value becomes the new property value. The column acme_delivery_note is added to the quote table and the sales_order table with declarative schema, and copied from quote to order with a fieldset or an observer, the same as on any checkout.

The template binds an input to the property:

<?php
/** @var \Magento\Framework\Escaper $escaper */
/** @var \Acme\Checkout\Magewire\DeliveryNote $magewire */
?>
<div class="my-4">
    <label for="acme-delivery-note" class="block font-semibold">
        <?= $escaper->escapeHtml(__('Delivery note')) ?>
    </label>
    <textarea id="acme-delivery-note"
              class="form-textarea w-full"
              maxlength="255"
              wire:model.lazy="note"></textarea>
</div>

wire:model.lazy syncs the value when the field loses focus, not on every keystroke. Finally, the layout XML adds the block with the component attached through the magewire argument. Place it inside the container of the step where you want the field, using the layout handle for that step.

<block name="checkout.acme.delivery-note"
       template="Acme_Checkout::checkout/delivery-note.phtml">
    <arguments>
        <argument name="magewire" xsi:type="object">Acme\Checkout\Magewire\DeliveryNote</argument>
    </arguments>
</block>

That is the whole feature on the frontend. No JavaScript was written.

Validation before moving on

Components often need to stop the customer from continuing until something is valid, for example a required delivery date. Hyvä Checkout has an evaluation mechanism for this: a component reports whether it is complete, and the checkout blocks navigation or order placement and shows the message if it is not. Use it instead of hiding the place order button with JavaScript, because it keeps the rule on the server.

Payment methods

This is the part to check first on any project. A payment module built for the Luma checkout does not automatically work in Hyvä Checkout. It needs a Hyvä Checkout integration, which is usually a Magewire component for the payment form plus whatever JavaScript the provider's SDK needs.

  • Many payment service providers now publish Hyvä Checkout integrations. Ask the provider, check the Hyvä compatibility information, and confirm the integration supports the features you use (3D Secure, wallets, stored cards, refunds through the admin).
  • Offline methods such as check or money order and bank transfer work with little or no extra code.
  • If your provider has no integration, building one is real work. Budget it as its own task and test it with the provider's sandbox, including failure cases.

The same applies to shipping methods that render custom UI in checkout (pickup point selectors, delivery date pickers). Carriers that only return rates work as is because rate collection is backend code. Anything with a custom frontend widget needs a Hyvä Checkout version.

Hyvä Checkout vs Luma fallback

TopicHyvä CheckoutLuma fallback
Frontend stackMagewire, Alpine.js, TailwindRequireJS, Knockout, UI components, LESS
CustomizationPHP components and layout XMLJS components, mixins, checkout layout XML
Payment modulesNeed a Hyvä Checkout integrationWork as they do on Luma
StylingSame Tailwind theme as the storeSeparate Luma theme to maintain
LicensingSeparate product, check hyva.ioNo extra product

Planning a checkout move

  1. List every payment method, shipping method with a custom widget, and checkout extension in use.
  2. Check each one for Hyvä Checkout support. Anything missing becomes a build task or a reason to delay.
  3. List every checkout customization already in the Luma checkout (custom fields, steps, validations). Each one needs to be rebuilt as a Magewire component.
  4. Decide whether to launch the Hyvä theme with the Luma checkout fallback and move checkout later. This is usually the lower risk order.
  5. Test order placement end to end in staging with every payment method, as guest and logged in, with coupons and with a mixed cart.

If the store has heavy custom logic in checkout today, the move to Hyvä Checkout is often a good moment to simplify. A lot of Luma checkout code exists to work around the JavaScript model and disappears when the logic moves to PHP.

FAQ

Is Hyvä Checkout included with the Hyvä theme?

It is a separate product. Licensing has changed over time, so check hyva.io for the current terms.

Can I launch a Hyvä store with the default Magento checkout?

Yes. A Luma checkout fallback renders the checkout routes with a Luma based theme while the rest of the store uses Hyvä.

What is Magewire?

A Magento port of the Laravel Livewire approach. Components are PHP classes with public properties bound to templates, and updates are rendered on the server and swapped into the page.

Will my payment module work in Hyvä Checkout?

Only if it has a Hyvä Checkout integration. Backend parts like capture and refunds are unchanged, but the payment form in checkout needs a Magewire based version.

I am available for freelance Magento and Hyvä Checkout work. If you need help with this, get in touch.

Related articles