
Checkout
Last purchase step: contacts, delivery, payment, address. The package ships a page template and form chunk.
Page structure
| Component | File | Chunk name in DB | Purpose |
|---|---|---|---|
| Page template | elements/templates/order.tpl | — | Page layout, msOrder call |
| Form chunk | elements/chunks/ms3_order.tpl | tpl.msOrder | Checkout form |
Snippet call
{'!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:
| Section | Description |
|---|---|
| Empty cart | Message and link to the catalog (if the cart is empty) |
| Contact details | First name, last name, email, phone, comment |
| Payment methods | Radio buttons with logo and description |
| Delivery methods | Radio buttons with logo and description |
| Delivery address | Postal code, region, city, street, building, entrance, floor, apartment |
| Saved addresses | Dropdown of previously saved addresses (for logged-in customers) |
| Summary panel | Product cost, delivery cost, total, cancel and submit buttons |
Placeholders
The form chunk exposes the following data:
| Placeholder | Type | Description |
|---|---|---|
$isCartEmpty | bool | Cart is empty |
$form | array | Form field values ($form.first_name, $form.email, etc.) |
$order | array | Order data ($order.cost, $order.delivery_cost, $order.cart_cost) |
$deliveries | array | Delivery methods |
$payments | array | Payment methods |
$addresses | array | Customer saved addresses |
$isCustomerAuth | bool | Customer is logged in |
Delivery and payment linkage
Each delivery includes a payments array with IDs of allowed payment methods. On delivery change, JS hides incompatible payments. Links are set in the Manager on the delivery card (msDeliveryMember).
If the pair is invalid, submit or Manager finalize returns an error.
Guest and authenticated customer
| Mode | What happens |
|---|---|
| Guest | Fills contacts manually. No saved addresses |
| Authenticated | Form shows an address list. Contacts can come from the profile |
Auto-registration on checkout
Keys:
ms3_customer_auto_register_on_order(on by default)ms3_customer_auto_login_on_order(on by default)
On submit, a guest with a valid email can get an msCustomer row and session without a separate registration. Turn the keys off if accounts are created only via the account form.
Separately: ms3_order_register_user_on_submit creates a modUser on checkout (off by default). That is not the same as msCustomer.
Manual login and registration: Login and registration.
Validation
How field validation works
Required fields and rules are set per delivery method in the Manager. Courier needs an address; pickup often needs only phone and email.
Rule setup: Deliveries → Validation.
Validation process
- On delivery change
OrderUIcallsGET /api/v1/order/delivery/validation-rulesandGET /api/v1/order/delivery/required-fields, hides extra fields, and updatesrequired. - On
ms3.order.setFieldthe server checks the field against the current delivery rules. - On submit the server checks all required fields.
- On error JS adds
is-invalidand text in.invalid-feedback.
Saved addresses
The msOrder snippet loads order-addresses.js (not part of ms3_frontend_assets). In the chunk — <select id="saved_address_id"> with <option data-address='{"city":"..."}'>: selecting an option fills the form fields automatically.
Two API paths:
| Scenario | Endpoint |
|---|---|
| Checkout: apply address to draft | POST /api/v1/order/address/set |
| Pick address from list (AuthUI / msCustomer) | POST /api/v1/customer/changeAddress |
Clear address fields: POST /api/v1/order/address/clean.
Custom fields (_validated)
Fields outside the order model (for example a consent checkbox agreement) go into the draft and are stored in msOrder.properties['_validated']. On order-create events they are available as customFields.
On the storefront the checkbox must send input.checked (1 / 0), not a static value. For consent use the accepted rule on the delivery.
JavaScript API
ms3.order object
// 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
// 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
| Event | When | Parameters |
|---|---|---|
msOnBeforeAddToOrder | Before adding a field | key, value, draft |
msOnAddToOrder | After adding a field | key, value, draft |
msOnBeforeRemoveFromOrder | Before removing a field | key, draft |
msOnRemoveFromOrder | After removing a field | key, draft |
Validation events
| Event | When | Parameters |
|---|---|---|
msOnBeforeValidateOrderValue | Before value validation | key, value, orderData |
msOnValidateOrderValue | Validation passed | key, value |
msOnErrorValidateOrderValue | Validation error | key, value, error |
Checkout events
| Event | When | Parameters |
|---|---|---|
msOnSubmitOrder | Before checkout starts | handler, draft, orderData, data |
msOnBeforeCreateOrder | Before order creation | handler, msOrder |
msOnCreateOrder | After order creation | handler, msOrder |
Customization
Changing the order form
- Create your own chunk, e.g.
tpl.myOrder - Specify it in the call:
'tpl' => 'tpl.myOrder' - Use the available placeholders from the msOrder documentation
Adding custom fields
Fields outside the order model take two steps.
1. Validation. Add rules in delivery settings:
{
"first_name": "required",
"email": "required|email",
"agree": "accepted"
}2. Saving. Standard fields (first_name, email, city, etc.) write themselves. Put foreign keys (not from msOrder / msOrderAddress) into order properties with a plugin if you need them after checkout:
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;
}Подсказка
An “I agree to the terms” checkbox often needs validation only. Then the accepted rule on the delivery is enough. You do not have to write it into the order.
Responsive layout
The form uses Bootstrap 5 Grid:
| Screen | Columns |
|---|---|
| < 992px | One section per row (100%) |
| ≥ 992px | Two sections per row (50% + 50%) |
