Skip to content
  1. Extras
  2. MiniShop3
  3. Frontend interface
  4. Checkout

Checkout

The checkout page is the final step of a purchase. MiniShop3 provides a ready-made template and order form chunk with contact details, delivery, and payment selection.

Page structure

ComponentFileChunk name in DBPurpose
Page templateelements/templates/order.tplPage layout, msOrder call
Form chunkelements/chunks/ms3_order.tpltpl.msOrderCheckout form

Snippet call

fenom
{'!msOrder' | snippet : [
    'tpl' => 'tpl.msOrder'
]}

Caching

The msOrder snippet must be called uncached (!msOrder) because it works with the user session.

Order form

The form contains the following sections:

SectionDescription
Empty cartMessage and link to the catalog (if the cart is empty)
Contact detailsFirst name, last name, email, phone, comment
Payment methodsRadio buttons with logo and description
Delivery methodsRadio buttons with logo and description
Delivery addressPostal code, region, city, street, building, entrance, floor, apartment
Saved addressesDropdown of previously saved addresses (for logged-in customers)
Summary panelProduct cost, delivery cost, total, cancel and submit buttons

Placeholders

The form chunk exposes the following data:

PlaceholderTypeDescription
$isCartEmptyboolCart is empty
$formarrayForm field values ($form.first_name, $form.email, etc.)
$orderarrayOrder data ($order.cost, $order.delivery_cost, $order.cart_cost)
$deliveriesarrayDelivery methods
$paymentsarrayPayment methods
$addressesarrayCustomer saved addresses
$isCustomerAuthboolCustomer is logged in

Delivery and payment linkage

Each delivery method contains a payments array with IDs of available payment methods. JavaScript automatically filters payment options when delivery changes.

Validation

How field validation works

Required fields and validation rules are configured per delivery method in the Manager. For example, you can require a full address for courier delivery but only email for pickup.

Detailed documentation on setting up validation rules — in Delivery settings → Order field validation.

Validation process

  1. When a field value is added (ms3.order.setField) — the server validates it against delivery rules
  2. On order submission — the server checks that all required fields are filled
  3. On error — the frontend highlights invalid fields

Error display

Each form field contains an .invalid-feedback container. On validation error, JavaScript adds the is-invalid class to the field and puts the error text in the container:

fenom
<input type="text" name="email" class="form-control"
       value="{$form.email}">
<div class="invalid-feedback"></div>

Checkbox validation

For a required checkbox (for example, agreement to terms), use the accepted rule in delivery settings. It checks that the value equals "yes", "on", "1", or true.

JavaScript API

ms3.order object

javascript
// Submit order
ms3.order.submit();

// Update delivery method
ms3.order.setDelivery(deliveryId);

// Update payment method
ms3.order.setPayment(paymentId);

// Update form field
ms3.order.setField('city', 'Moscow');

Events

javascript
// Before order submission
document.addEventListener('ms3:order:before-submit', (e) => {
    console.log('Order data:', e.detail);
    // Cancel submission: e.preventDefault()
});

// After successful checkout
document.addEventListener('ms3:order:success', (e) => {
    console.log('Order created:', e.detail.order_id);
    window.location.href = e.detail.redirect;
});

// On checkout error
document.addEventListener('ms3:order:error', (e) => {
    console.error('Errors:', e.detail.errors);
});

// On delivery method change
document.addEventListener('ms3:order:delivery-changed', (e) => {
    console.log('Delivery selected:', e.detail.delivery_id);
});

// On payment method change
document.addEventListener('ms3:order:payment-changed', (e) => {
    console.log('Payment selected:', e.detail.payment_id);
});

Server events

Order field events

EventWhenParameters
msOnBeforeAddToOrderBefore adding a fieldkey, value, draft
msOnAddToOrderAfter adding a fieldkey, value, draft
msOnBeforeRemoveFromOrderBefore removing a fieldkey, draft
msOnRemoveFromOrderAfter removing a fieldkey, draft

Validation events

EventWhenParameters
msOnBeforeValidateOrderValueBefore value validationkey, value, orderData
msOnValidateOrderValueValidation passedkey, value
msOnErrorValidateOrderValueValidation errorkey, value, error

Checkout events

EventWhenParameters
msOnSubmitOrderBefore checkout startshandler, draft, orderData, data
msOnBeforeCreateOrderBefore order creationhandler, msOrder
msOnCreateOrderAfter order creationhandler, msOrder

Customization

Changing the order form

  1. Create your own chunk, e.g. tpl.myOrder
  2. Specify it in the call: 'tpl' => 'tpl.myOrder'
  3. Use the available placeholders from the msOrder documentation

Adding custom fields

Custom fields (not part of the standard order model) are handled in two steps:

1. Validation — via delivery validation rules.

Add the field in JSON mode in delivery settings:

json
{
  "first_name": "required",
  "email": "required|email",
  "agree": "accepted"
}

2. Saving — via a plugin, if the custom field value must be stored on the order.

Standard fields (first_name, email, city, etc.) are saved automatically. Custom fields (not from the msOrder / msOrderAddress model) must be saved to order properties via a plugin:

php
switch ($modx->event->name) {
    case 'msOnBeforeCreateOrder':
        // $msOrder is available from event parameters
        $address = $msOrder->Address;
        if ($address) {
            $properties = $msOrder->get('properties') ?: [];
            $properties['agree'] = $address->get('properties')['agree'] ?? '';
            $msOrder->set('properties', $properties);
        }
        break;
}

Подсказка

If a custom field is used only for validation (for example, an "I agree to the terms" checkbox), you do not have to save it — the accepted rule in delivery settings is enough.

Responsive layout

The form uses Bootstrap 5 Grid:

ScreenColumns
< 992pxOne section per row (100%)
≥ 992pxTwo sections per row (50% + 50%)