
msOrder
Snippet for the checkout form. Shows customer fields, delivery methods, and payment methods.
Caching
The snippet uses the user session and must be called uncached.
Thank-you page
If the URL contains GET parameter msorder (redirect after checkout), the snippet returns an empty string. On the same page use msGetOrder. Do not show checkout and order details together without a URL condition.
Parameters
| Parameter | Default | Description |
|---|---|---|
| tpl | tpl.msOrder | Order form chunk |
| userFields | Mapping of MODX profile fields (modUserProfile) to order fields (JSON). Used when ms3_customer_sync_enabled = true | |
| customerFields | Mapping of customer fields (msCustomer) to order fields (JSON). Used when ms3_customer_sync_enabled = false | |
| includeDeliveryFields | id | Comma-separated delivery fields (* = all). id is always included |
| includePaymentFields | * | Comma-separated payment fields (* = all) |
| includeCustomerAddresses | true | Load saved customer addresses |
| showLog | false | Show execution log |
| return | tpl | Output format: tpl, data |
Examples
Basic output
{'!msOrder' | snippet}Customer field mapping (msCustomer)
When sync is disabled (ms3_customer_sync_enabled = false), data is taken from msCustomer:
{'!msOrder' | snippet : [
'customerFields' => '{"company": "company_name", "inn": "tax_id"}'
]}MODX profile field mapping (modUserProfile)
When sync is enabled (ms3_customer_sync_enabled = true), data is taken from modUserProfile:
{'!msOrder' | snippet : [
'userFields' => '{"company": "extended.company_name"}'
]}Choosing data source
ms3_customer_sync_enabled = false(default): usecustomerFieldsand msCustomer datams3_customer_sync_enabled = true: useuserFieldsand modUserProfile data
Data sources are mutually exclusive — only one is active depending on the setting.
Get data
{'!msOrder' | snippet : [
'return' => 'data'
]}Data structure
With return=data the snippet returns an array:
[
'order' => [
'delivery_id' => 1,
'payment_id' => 2,
'order_comment' => '...',
'cost' => 5300, // Total (number)
'cost_formatted' => '5 300 ₽', // Total with currency
'cart_cost' => 5000, // Products cost
'cart_cost_formatted' => '5 000 ₽',
'delivery_cost' => 300, // Delivery cost
'delivery_cost_formatted' => '300 ₽',
'discount_cost' => 0, // Discount
'discount_cost_formatted' => '0 ₽',
],
'form' => [
'first_name' => 'John',
'last_name' => 'Doe',
'email' => 'user@example.com',
'phone' => '+1 234 567-89-00',
'city' => 'New York',
'street' => 'Example St',
'building' => '1',
'room' => '42',
// ... other address fields
],
'deliveries' => [
1 => [
'id' => 1,
'name' => 'Pickup',
'description' => '...',
'price' => 0,
'logo' => '...',
'payments' => [1, 2], // IDs of available payment methods
],
// ...
],
'payments' => [
1 => [
'id' => 1,
'name' => 'Cash',
'description' => '...',
'logo' => '...',
],
// ...
],
'addresses' => [ // Saved addresses (when includeCustomerAddresses)
[
'id' => 1,
'city' => 'New York',
'street' => 'Main St',
// ...
],
],
'errors' => [], // Fields with validation errors
'isCustomerAuth' => true, // Whether customer is logged in
'isCartEmpty' => false, // Whether cart is empty
]Placeholders in chunk
Form data (contacts and address)
{$form.first_name}— First name{$form.last_name}— Last name{$form.email}— Email{$form.phone}— Phone{$form.city}— City{$form.street}— Street{$form.building}— Building{$form.room}— Apartment/office
State flags
{$isCustomerAuth}— Whether customer is logged in (bool){$isCartEmpty}— Whether cart is empty (bool)
Delivery methods
{foreach $deliveries as $delivery}
<label>
<input type="radio"
name="delivery_id"
value="{$delivery.id}"
{if $order.delivery_id == $delivery.id}checked{/if}>
{$delivery.name}
{if $delivery.price > 0}
— {$delivery.price}
{/if}
</label>
{/foreach}Payment methods
{foreach $payments as $payment}
<label>
<input type="radio"
name="payment_id"
value="{$payment.id}"
{if $order.payment_id == $payment.id}checked{/if}>
{$payment.name}
</label>
{/foreach}Totals
{$order.cart_cost}— Products cost (number){$order.delivery_cost}— Delivery cost (number){$order.discount_cost}— Discount (number){$order.cost}— Total to pay (number){$order.cart_cost_formatted},{$order.delivery_cost_formatted},{$order.discount_cost_formatted},{$order.cost_formatted}— same amounts with currency{$order.currency_symbol}— Currency symbol from MS3 settings
Example chunk
{* tpl.msOrder *}
{if $isCartEmpty}
<div class="alert alert-warning">Cart is empty</div>
{else}
<form class="ms-order ms3_form" method="post">
<input type="hidden" name="ms3_action" value="order/submit">
<h2>Checkout</h2>
{* Contact details *}
<fieldset>
<legend>Contact details</legend>
<div class="form-group">
<label>First name *</label>
<input type="text"
name="first_name"
value="{$form.first_name}"
required>
</div>
<div class="form-group">
<label>Last name</label>
<input type="text"
name="last_name"
value="{$form.last_name}">
</div>
<div class="form-group">
<label>Email *</label>
<input type="email"
name="email"
value="{$form.email}"
required>
</div>
<div class="form-group">
<label>Phone *</label>
<input type="tel"
name="phone"
value="{$form.phone}"
required>
</div>
</fieldset>
{* Address *}
<fieldset>
<legend>Delivery address</legend>
<div class="form-group">
<label>City</label>
<input type="text" name="city" value="{$form.city}">
</div>
<div class="form-group">
<label>Street</label>
<input type="text" name="street" value="{$form.street}">
</div>
<div class="row">
<div class="col">
<label>Building</label>
<input type="text" name="building" value="{$form.building}">
</div>
<div class="col">
<label>Room</label>
<input type="text" name="room" value="{$form.room}">
</div>
</div>
</fieldset>
{* Delivery *}
<fieldset>
<legend>Delivery method</legend>
{foreach $deliveries as $delivery}
<label class="delivery-option">
<input type="radio"
name="delivery_id"
value="{$delivery.id}"
{if $order.delivery_id == $delivery.id}checked{/if}>
<span>{$delivery.name}</span>
{if $delivery.price > 0}
<span class="price">+{$delivery.price}</span>
{/if}
</label>
{/foreach}
</fieldset>
{* Payment *}
<fieldset>
<legend>Payment method</legend>
{foreach $payments as $payment}
<label class="payment-option">
<input type="radio"
name="payment_id"
value="{$payment.id}"
{if $order.payment_id == $payment.id}checked{/if}>
<span>{$payment.name}</span>
</label>
{/foreach}
</fieldset>
{* Comment *}
<fieldset>
<legend>Order comment</legend>
<textarea name="order_comment" rows="3">{$order.order_comment}</textarea>
</fieldset>
{* Total *}
<div class="order-total">
<div>Products: <span>{$order.cart_cost}</span></div>
<div>Delivery: <span>{$order.delivery_cost}</span></div>
{if $order.discount_cost}
<div>Discount: <span>{$order.discount_cost}</span></div>
{/if}
<div class="total">
<strong>Total: <span>{$order.cost}</span></strong>
</div>
</div>
<button type="submit" class="btn btn-primary btn-lg">
Place order
</button>
</form>
{/if}JavaScript interaction
The form uses OrderUI + ms3.orderAPI. There is no public ms3.order object.
// Draft fields
await ms3.orderAPI.add('delivery_id', deliveryId)
await ms3.orderAPI.add('payment_id', paymentId)
await ms3.orderAPI.add('city', 'Moscow')
await ms3.orderAPI.add('order_comment', 'Call before delivery')
// Submit
const response = await ms3.orderAPI.submit()
if (response.success) {
window.location.href = response.data.redirect
}Hooks (needs hooks.js in ms3_frontend_assets):
ms3Hooks.addHook('beforeSubmitOrder', async (data) => {
// data.formData — form FormData
})
ms3Hooks.addHook('afterSubmitOrder', async ({ response }) => {
if (response.success) {
console.log('Order:', response.data.order_id)
}
})Details: JavaScript API, Frontend JS.
