During the checkout flow, the customer is often asked to provide information and select e.g. a payment and shipping method before being allowed to submit the cart and convert it to an order.
In this section we will go though all the different things you can do when submitting to the cart, e.g.
- Billing & Delivery details
- User creation during checkout
- Payment & shipping method selection
- Applying vouchers
- Paying with loyalty points
- Using gift cards
- Creating subscription orders
- Paying with a saved card - and saving a card
- Opt-ins
- Using custom fields
To submit something to the cart you post a form with the id ordersubmit containing input fields that represent the features you want to implement:
<form name="ordersubmit" id="ordersubmit" method="post" role="form">
<!--Insert form content here-->
<button type="submit" name='@GetValue("CartV2.CurrentStepButtonName")' id='@GetValue("CartV2.CurrentStepButtonName")'>@Translate("Update_order", "Update order")</button>
<button type="submit" name='@GetValue("CartV2.NextStepButtonName")' id='@GetValue("CartV2.NextStepButtonName")'>@Translate("Create_order", "Create order")</button>
</form>
In order for the cart to receive this information, the input fields being submitted must have specific names & values (the id only matters when you need to target an element from JavaScript or a <label>). You can read more about each feature below.
How the form drives the flow
Every post to the cart does two things: it saves the submitted fields onto the cart, and it requests a step to render. Which step is requested comes from the name of the button that submitted the form:
CartV2.CurrentStepButtonNameresolves toCartV2.GotoStep{current}– it stays on the current step. Use it for an Update order button that recalculates the cart (e.g. after the customer changes country or quantity) without advancing.CartV2.NextStepButtonNameresolves toCartV2.GotoStep{current+1}– it advances to the next step. Use it for the Continue / Create order button.- You can target any step directly by submitting a button named
CartV2.GotoStep{N}(for example a "back to cart" link that postsCartV2.GotoStep0). The cart still validates that the jump is allowed – see How it works.
The renderer also exposes ready-made button names so you rarely have to build the GotoStep{N} string yourself: CartV2.PreviousStepButtonName, CartV2.CheckoutButtonName, and a per-step Step.ButtonName inside the steps loop (handy for a clickable progress indicator).
A field is only saved when it is present in the post. This is why selecting a country or shipping method is usually wired to submit the form (an onchange="updateCart();" helper that clicks the update button), so the new value reaches the cart and prices/methods refresh immediately.
Tip
Because the cart recalculates on every post, fields that influence price or method availability – country, shipping method, voucher/gift card, loyalty points – should trigger a submit when they change, rather than waiting for the customer to press Continue.
Billing details
To add billing address details to an order, use input fields with the IDs listed below. Most map directly to standard user fields, but a few are for billing-specific information like an EAN-number or a VAT-number.
| Field name & id | Notes |
|---|---|
| EcomOrderCustomerCompany | |
| EcomOrderCustomerName | |
| EcomOrderCustomerTitle | |
| EcomOrderCustomerFirstName | |
| EcomOrderCustomerSurname | |
| EcomOrderCustomerMiddleName | |
| EcomOrderCustomerHouseNumber | |
| EcomOrderCustomerInitials | |
| EcomOrderCustomerPrefix | |
| EcomOrderCustomerAddress | |
| EcomOrderCustomerAddress2 | |
| EcomOrderCustomerZip | |
| EcomOrderCustomerCity | |
| EcomOrderCustomerCountry | This field influences which payment methods & shipping methods can be selected. |
| EcomOrderCustomerRegion | |
| EcomOrderCustomerPhone | |
| EcomOrderCustomerFax | |
| EcomOrderCustomerCell | |
| EcomOrderCustomerEmail | |
| EcomOrderCustomerVatRegNumber | |
| EcomOrderCustomerEAN | |
| EcomOrderCustomerRefId | |
| EcomOrderCustomerComment | Maps to the customer comment order field. |
Since the billing country is important for determining which payment & shipping methods should be shown, you should make sure to preselect any default option and also find a way to update the cart when the user selects one of the commerce countries on the solution:
<!--Billing Address Fields-->
<div>
<!--Personal Fields-->
<div>
<input type="text" name="EcomOrderCustomerTitle" id="Title" value='' />
<input type="text" name="EcomOrderCustomerFirstName" id="EcomOrderCustomerFirstName" value='' />
<input type="text" name="EcomOrderCustomerSurname" id="EcomOrderCustomerSurname" value='' />
<input type="text" name="EcomOrderCustomerEmail" id="EcomOrderCustomerEmail" value='' />
<input type="text" name="EcomOrderCustomerPhone" id="EcomOrderCustomerPhone" value='' />
</div>
<!--Address Fields-->
<div>
<input type="text" name="EcomOrderCustomerAddress" id="EcomOrderCustomerAddress" value='' />
<input type="text" name="EcomOrderCustomerAddress2" id="EcomOrderCustomerAddress2" value='' />
<input type="text" name="EcomOrderCustomerHouseNumber" id="EcomOrderCustomerHouseNumber" value='' />
<input type="text" name="EcomOrderCustomerZip" id="EcomOrderCustomerZip" value='' />
<input type="text" name="EcomOrderCustomerCity" id="EcomOrderCustomerCity" value='' />
<input type="text" name="EcomOrderCustomerRegion" id="EcomOrderCustomerRegion" value='' />
<select name="EcomOrderCustomerCountry" id="EcomOrderCustomerCountry" onchange="updateCart();">
@foreach (var country in GetLoop("Countries"))
{
if (country.GetString("Ecom:Country.IsCustomerCountryOrDefault") == "true")
{
<option value='@country.GetValue("Ecom:Country.Code2")' selected>@country.GetValue("Ecom:Country.Name")</option>
}
else
{
<option value='@country.GetValue("Ecom:Country.Code2")'>@country.GetValue("Ecom:Country.Name")</option>
}
}
</select>
</div>
</div>
Delivery details
Delivery address details are submitted to cart in the same way as billing details – and are in fact almost identical to billing details.
The 'Copy customer info to delivery info fields if empty'-checkbox allows you to copy billing address field values to the delivery address fields if no part of the delivery address has been filled out.
| Field name & id | Notes |
|---|---|
| EcomOrderDeliveryCompany | |
| EcomOrderDeliveryName | |
| EcomOrderDeliveryFirstName | |
| EcomOrderDeliveryTitle | |
| EcomOrderDeliverySurname | |
| EcomOrderDeliveryHouseNumber | |
| EcomOrderDeliveryMiddleName | |
| EcomOrderDeliveryInitials | |
| EcomOrderDeliveryPrefix | |
| EcomOrderDeliveryAddress | |
| EcomOrderDeliveryAddress2 | |
| EcomOrderDeliveryZip | |
| EcomOrderDeliveryCity | |
| EcomOrderDeliveryCountry | |
| EcomOrderDeliveryRegion | |
| EcomOrderDeliveryPhone | |
| EcomOrderDeliveryFax | |
| EcomOrderDeliveryCell | |
| EcomOrderDeliveryEmail |
Creating a user during checkout
To create a user or update an existing user during checkout, submit a field with the name and ID EcomUserCreateNewOrUpdate alongside fields specifying the username and password.
| Field name & Id | Notes |
|---|---|
| EcomUserCreateNewOrUpdate | Older implementations used EcomUserCreateNew |
| EcomUserCreateUserName | |
| EcomUserCreatePassword | |
| EcomUserCreateConfirmPassword |
Payment methods
Payment methods are submitted to cart using input elements with the name EcomCartPaymethodID and an id of the type EcomCartPaymethodID_{PaymentMethodID}, with PaymentMethodID replaced by a valid id.
Some payment methods support rendering a payment form inline directly in the cart template, others require you to progress in the checkout flow. This part is payment-method specific, and the content of the payment form typically comes from a template selected on the payment method.
<!--Payment Methods-->
@foreach (var paymentmethod in GetLoop("Paymethods")) //Loop through payment methods
{
<div class="radio">
<label >
@if (@paymentmethod.GetString("Ecom:Cart.Paymethod.IsSelected") == "true") // check if payment method is selected
{
<input type="radio" name="EcomCartPaymethodID" id='EcomCartPaymethodID_@paymentmethod.GetValue("Ecom:Cart.Paymethod.ID")' value='@paymentmethod.GetValue("Ecom:Cart.Paymethod.ID")' checked='checked' />
}
else
{
<input type="radio" name="EcomCartPaymethodID" id='EcomCartPaymethodID_@paymentmethod.GetValue("Ecom:Cart.Paymethod.ID")' value='@paymentmethod.GetValue("Ecom:Cart.Paymethod.ID")' />
}
@paymentmethod.GetValue("Ecom:Cart.Paymethod.Name")
</label>
</div>
}
<!--Inline Payment Forms-->
@if (!string.IsNullOrWhiteSpace(GetString("Ecom:Cart.PaymentInlineForm")))
{
@GetString("Ecom:Cart.PaymentInlineForm")
}
Shipping methods
Shipping methods are submitted to cart using input elements with the name EcomCartShippingMethodId and an id of the type EcomCartShippingMethodId_{ShippingMethodID} with ShippingMethodID being replaced by a valid id.
Some shipping methods will ask the customer to select e.g. a parcel shop and a parcel type and so on - that's shipping method specific, and is handled in a template selectdd on the shipping method.
<!--Shipping methods-->
@foreach (var shippingmethod in GetLoop("Shippingmethods"))
{
<div class="radio">
<label>
@if (shippingmethod.GetString("Ecom:Cart.Shippingmethod.IsSelected") == "true")
{
<input type="radio" name="EcomCartShippingmethodID" onclick="updateCart();" id='EcomCartShippingmethodID_@shippingmethod.GetValue("Ecom:Cart.Shippingmethod.ID")' value='@shippingmethod.GetValue("Ecom:Cart.Shippingmethod.ID")' checked="checked" />
}
else
{
<input type="radio" name="EcomCartShippingmethodID" onclick="updateCart();" id='EcomCartShippingmethodID_@shippingmethod.GetValue("Ecom:Cart.Shippingmethod.ID")' value='@shippingmethod.GetValue("Ecom:Cart.Shippingmethod.ID")' />
}
@shippingmethod.GetValue("Ecom:Cart.Shippingmethod.Name")
</label>
</div>
<!--Shippingmethod Content - e.g. Parcel shops or Parcel types-->
if (!string.IsNullOrWhiteSpace(shippingmethod.GetString("Ecom:ShippingProvider.Content")))
{
@shippingmethod.GetString("Ecom:ShippingProvider.Content")
}
}
Vouchers
Vouchers are applied to an order by submitting one or more voucher codes in a field named EcomOrderVoucherCode.
<!--Vouchers-->
<input type="text" name="EcomOrderVoucherCode" id="EcomOrderVoucherCode" value='@GetString("Ecom:Order.Customer.VoucherCode")' />
Submitting more than one code
The value of EcomOrderVoucherCode may contain several codes separated by a comma, semicolon or space (e.g. SUMMER10, WELCOME5). On submit, the cart:
- Splits the value into individual codes.
- Validates each code on its own and silently discards the invalid ones.
- Stores the surviving codes back on the order as a single comma-separated string.
Because the field is replaced on every submit (it is not additive), the value you post is the complete set of codes the order should have. This has two consequences:
- Submitting the same code twice has no extra effect – duplicates collapse to a single applied code.
- To add a code while keeping the existing ones, post the existing codes plus the new one. To remove one code but keep the others, post the remaining codes and leave the unwanted one out. Posting an empty value removes them all.
Rendering the applied codes
Two things come back to the template:
Ecom:Order.Customer.VoucherCode– the raw, comma-separated value, suitable for re-populating the input field above (it falls back to the value just posted so the field survives a failed validation).- A
VoucherCodesloop that yields one row per applied code viaEcom:Order.VoucherCode– use this to render a removable "chip" per code.
<!--Show the applied codes as removable chips-->
<ul class="voucher-list">
@foreach (var voucher in GetLoop("VoucherCodes"))
{
<li>@voucher.GetString("Ecom:Order.VoucherCode")</li>
}
</ul>
To make a single chip removable, render a button that re-posts EcomOrderVoucherCode with that one code stripped out – typically by building the new value in JavaScript from the remaining chips, then submitting the cart.
Note
A voucher only does something if it is tied to a discount (or, on the adjustments engine, a voucher condition). A code can be a perfectly valid, unused voucher and still not change the price if no active discount targets it. The discount it triggers shows up as a discount order line, where Ecom:Order:OrderLine.DiscountVoucher holds the code that produced it.
Loyalty points
To apply loyalty points to an order, you submit a field with the id EcomOrderPointsToUse and a value in loyalty points. In this example we first check if the current user is logged in - if she is, we retrieve her points balance and check if it's possible to use points on an order.
@{
var isLoggedIn = Dynamicweb.Security.UserManagement.User.IsExtranetUserLoggedIn();
var currentUser = Dynamicweb.Security.UserManagement.User.GetCurrentExtranetUser();
double pointsbalance = 0;
}
<!--Pay with points-->
@if (isLoggedIn)
{
pointsbalance = Dynamicweb.Ecommerce.Services.Loyalty.GetPointsBalance(currentUser.ID);
<div>Your points: @pointsbalance</div>
@if (pointsbalance > 0)
{
<input type="text" name="EcomOrderPointsToUse" id="EcomOrderPointsToUse" value='@GetValue("Ecom:Order.PointsToUse")' />
}
else
{
<input type="text" name="EcomOrderPointsToUse" id="EcomOrderPointsToUse" value='' disabled title='No loyalty points available' />
}
}
Gift Cards
Gift cards are applied to an order by submitting one or more gift card codes in a field named EcomOrderGiftCardCode.
<input type="text" name="EcomOrderGiftCardCode" id="EcomOrderGiftCardCode" value="" />
Submitting more than one code
Like vouchers, the field can hold several codes separated by a comma, semicolon or space, and the field is treated as the complete set of cards on the order rather than an addition to it. On each submit the cart:
- Compares the posted codes with the gift cards already applied to the cart.
- Keeps the cards whose code is still present, removes the cards whose code is no longer posted, and applies any new codes.
So the removal pattern is the same as for vouchers: to remove one gift card but keep the others, re-post the field with that code left out; posting an empty value removes them all. Applying a card adds a discount order line carrying the gift card code, which is how the redeemed amount is reflected in the cart total.
Rendering applied gift cards
How a redeemed gift card surfaces depends on the stage of the flow:
- In the active cart, each applied card is a discount order line – iterate the order lines and use
Ecom:Order:OrderLine.GiftCardCodeto identify them. - On the receipt (a completed order), two ready-made loops are available:
Ecom:Order.HasUsedGiftCardsistruewhen at least one card was redeemed, and theUsedGiftCardsloop exposes per-card details:Ecom:Order.UsedGiftCard.Name,.Code,.ExpiryDate,.InitialAmount,.UsedAmountForTheOrderand.RemainingBalance(each amount also has the usual price-formatting sub-tags).Ecom:Order.HasGiftCardsistruewhen the order bought gift card products, and theGiftCardsloop exposes the issued cards viaEcom:Order.GiftCard.Name,.Code,.ExpiryDateand.Amount.
@if (GetString("Ecom:Order.HasUsedGiftCards") == "true")
{
<ul>
@foreach (var card in GetLoop("UsedGiftCards"))
{
<li>
@card.GetString("Ecom:Order.UsedGiftCard.Code")
– used @card.GetString("Ecom:Order.UsedGiftCard.UsedAmountForTheOrder")
(balance @card.GetString("Ecom:Order.UsedGiftCard.RemainingBalance"))
</li>
}
</ul>
}
Subscription orders
To create a subscription order you must first check if the payment method supports subscription orders. If it does, submit a boolean field with the id & name EcomRecurringOrderCreate alongside an interval. an interval unit, a start date and an end date.
| Field Name & ID | Value | Notes |
|---|---|---|
| EcomRecurringOrderCreate | ||
| EcomOrderRecurringInterval | - | |
| EcomOrderRecurringIntervalUnit | 0 = days, 1 = weeks, 2 = months | |
| EcomOrderRecurringEndDate | ||
| EcomOrderRecurringEndDate_year | ||
| EcomOrderRecurringEndDate_month | ||
| EcomOrderRecurringEndDate_day | ||
| EcomOrderRecurringStartDate |
@if (@GetBoolean("Ecom:Order.PaymentMethod.RecurringSupport") == true)
{
<!--Checkbox to create recurring orders-->
<input type="checkbox" name="EcomRecurringOrderCreate" id="EcomRecurringOrderCreate" checked='@GetBoolean("Ecom:User.CreateNew")' />
<!--Interval-->
<input type="text" name="EcomOrderRecurringInterval" id="EcomOrderRecurringInterval" value='1' />
<select name="EcomOrderRecurringIntervalUnit" id="EcomOrderRecurringIntervalUnit" value="0">
<option value="0">Days</option>
<option value="1" selected>Weeks</option>
<option value="2">Months</option>
</select>
<!--Start Date-->
<input type="date" name="EcomOrderRecurringStartDate" id="EcomOrderRecurringStartDate" min='@DateTime.Today.ToString("yyyy-MM-dd")' value='@DateTime.Today.ToString("yyyy-MM-dd")' />
<!--End Date-->
<input type="date" name="EcomOrderRecurringEndDate" id="EcomOrderRecurringEndDate" min='@DateTime.Today.AddDays(7).ToString("yyyy-MM-dd")' value='@DateTime.Today.AddDays(7).ToString("yyyy-MM-dd")' />
}
Saved cards
Saved cards - or more properly saved payment methods - are saved by submitting a field named EcomOrderSavedCardCreate with the value true, alongside a field named EcomOrderSavedCardName specifying the name to store the card under. You should ensure that the payment method supports saved cards - at the time of writing this is supported by Stripe, ChargeLogic and QuickPay Payment Window.
Note
The cart reads the name EcomOrderSavedCardCreate (the value must be the string true or false). The _{PaymethodID} suffix you will see on the id attribute in the example below is only there to keep the element ids unique while looping the payment methods – it is not part of the submitted name. Submitting the value false clears a previously requested save.
To use a saved card submit a field named EcomCartSavedCardID whose value is the id of the saved card (the id attribute is suffixed _{SavedCardId} to stay unique in the loop). Selecting a saved card also sets the payment method from the stored card, so you do not need to submit a separate EcomCartPaymethodID.
<!--Saving a card -->
@foreach (var paymentmethod in GetLoop("Paymethods")) //Loop through payment methods
{
if (paymentmethod.GetString("Ecom:Cart.Paymethod.SupportSavedCard") == "true" && @paymentmethod.GetString("Ecom:Cart.Paymethod.IsSelected") == "true") // Check if this method supports saved cards & is selected
{
<input type="checkbox" name="EcomOrderSavedCardCreate" id="EcomOrderSavedCardCreate_@paymentmethod.GetString("Ecom:Cart.Paymethod.ID")" value="true" />
<input type="text" name="EcomOrderSavedCardName" id="MySavedCardName" value='My @paymentmethod.GetValue("Ecom:Cart.Paymethod.Name") Card' />
}
}
<!-- Using a card -->
@{
var savedcards = GetLoop("SavedCards");
}
@if (savedcards.Any()) //Checks if this user has any saved cards
{
foreach (var savedcard in savedcards)
{
<div class="radio">
<label>
@if (@savedcard.GetString("Ecom:SavedCard.IsSelected") == "true") //Checks if the saved card is selected
{
<input type="radio" name="EcomCartSavedCardID" id="EcomCartSavedCardID_@savedcard.GetString("Ecom:SavedCard.ID")" value="@savedcard.GetString("Ecom:SavedCard.ID")" checked="checked" />
}
else
{
<input type="radio" name="EcomCartSavedCardID" id="EcomCartSavedCardID_@savedcard.GetString("Ecom:SavedCard.ID")" value="@savedcard.GetString("Ecom:SavedCard.ID")" />
}
@savedcard.GetString("Ecom:SavedCard.Name")
</label>
</div>
}
}
To allow the customer to see/edit/remove a saved card use a customer center app.
Opt-ins
From the cart you can change the value of the Email permission field on the logged in user, and the user can be asked to accept the Terms & Conditions.
A setting on the cart - Use email subscription - can be used in templates.
<!--Email permission-->
@if (GetString("Ecom:Cart.UseNewsletterSubscription") == "True")
{
<input type="checkbox" name="EcomOrderSubscribeToNewsletter" id="EcomOrderSubscribeToNewsletter" checked='@GetBoolean("Ecom:Order.Customer.NewsletterSubscribe")' />
<label for="EcomOrderSubscribeToNewsletter">Subscribe to newsletter</label>
}
<!--Terms & Conditions-->
<input type="checkbox" name="EcomOrderCustomerAccepted" id="EcomOrderCustomerAccepted" />
<label for="EcomOrderCustomerAccepted">Accept terms & conditions</label>
When the cart's customer acceptance validation is enabled, leaving EcomOrderCustomerAccepted unchecked fails the submit and the configured error message is surfaced through the validation tags.
Rendering validation and errors
When a submit fails validation – a required field is empty, a validation group rule fails, terms aren't accepted, stock is insufficient, and so on – the cart re-renders the step the customer came from and exposes the errors to the template so you can show them. Nothing is lost: the posted values are kept on the cart, so the fields you re-render with value='@GetString(...)' come back populated.
Errors are available in two shapes:
- A
ValidationErrorsloop, with one row per error:Ecom:Cart.ValidationError.FieldSystemName,Ecom:Cart.ValidationError.FieldName(the friendly name) andEcom:Cart.ValidationError.ErrorMessage. Use this for a summary list at the top of the form. - A per-field tag
Ecom:Cart.ValidationError.{FieldSystemName}.ErrorMessage, which is set to the message when that field failed and to an empty string otherwise. Use this to print the message next to the relevant input.
<!--Error summary-->
@if (GetLoop("ValidationErrors").Any())
{
<div class="alert alert-danger">
<ul>
@foreach (var error in GetLoop("ValidationErrors"))
{
<li>@error.GetString("Ecom:Cart.ValidationError.FieldName"): @error.GetString("Ecom:Cart.ValidationError.ErrorMessage")</li>
}
</ul>
</div>
}
<!--Inline error next to a field-->
<input type="text" name="EcomOrderCustomerEmail" value='@GetString("Ecom:Order.Customer.Email")' />
<span class="field-error">@GetString("Ecom:Cart.ValidationError.EcomOrderCustomerEmail.ErrorMessage")</span>
Two related signals are worth handling:
- Removed products – if a product was removed because it no longer exists, its name is passed to the step template so you can tell the customer what happened.
- Method errors – a shipping provider can reject the current selection;
Ecom:Cart.Shippingmethod.Errorcarries any provider error message for the method.
Custom field
Custom fields – such as order fields or a coupon field – are posted to the cart using input elements which take the system name as the field name. No ID is necessary.
<div>
<label for="Coupons">Coupon</label>
<input type="text" name="Coupons" value='' />
</div>
<div>
<label for="TrackingURLFromERP">TrackingURLFromERP</label>
<input type="text" name="TrackingURLFromERP" value='' />
</div>
Other fields
Finally, a small set of standard order fields can be submitted to the cart. They map directly to properties on the order and are used primarily on B2B and integrated solutions. Although you can submit values to them from frontend this is not the typical case.
| Field Name & ID | Maps to order property | Notes |
|---|---|---|
| EcomCartRequisition | Requisition |
A free-text purchase requisition / PO number the buyer wants on the order. Common on B2B checkouts where the customer must reference their own internal purchase order. |
| EcomOrderReference | Reference |
A free-text order reference – e.g. "your reference", the name of the person placing the order, or any reference the customer or sales rep wants to carry on the order. |
| EcomOrderShippingDate | ShippingDate |
A requested shipping/delivery date. The value is parsed as a date; an empty or unparseable value clears it. Use it when the customer can ask for a specific delivery day. |
| EcomOrderIntegrationId | IntegrationOrderId |
The order id in an external system (e.g. an ERP). Normally written by an integration rather than entered by the customer, which is why it is rarely on a checkout form. |
Note
These are standard fields with dedicated order properties – they are not custom fields, so you submit them under the fixed field names above, not under an order field system name.