Shopping Cart

The Shopping Cart app is a paragraph app which handles everything related to adding products to cart and completing an order.

More specifically the Shopping Cart app does the following:

  • Keeps a record of the items added to cart
  • Shows the items in the cart to the customer
  • Handles the checkout process
  • Shows an order receipt
  • Sends out order confirmation emails

Once the app is added to a paragraph you can configure it using the app settings. The app settings consist of sections controlling different aspects of the app behavior, as outlined below.

The first settings are used to set a shop and (optionally) an order context for the cart (Figure 1.1). You can also check checkout to quote if this is supposed to be a quotes cart.

Figure 1.1 Associating the cart app with a shop or context

If no shop is selected, orders are created under the default shop, as defined in the website Ecommerce settings.

Next, you can define the checkout flow – the steps the customer is required to go through when they want to complete an order (Figure 2.1).

Figure 2.1 The standard checkout flow

The standard checkout flow consists of four steps;

  • Show cart
  • Information
  • Checkout
  • Receipt

Only the checkout step is strictly required, but you usually want to show your customers the contents of the cart and a receipt once they’ve completed checkout. Think of each of the non-essential steps as a virtual breakdown of the information a customer must supply and receive before an order can be created.

To edit a step click the Edit icon and to remove it click the red cross – when you edit a step you should have access to a set of templates demonstrating various Ecommerce features (Figure 2.2).

For another example, check out the the checkout flow (hehe) on Dynamicweb Rapido.

Submitting to cart

During the checkout process, the user will be asked to fill in a number of fields which will then be submitted to cart. In order for these field values to be picked up by the cart the input fields must have specific names, ids and values.

Below you can find an example template which at the time of writing - June 2020 - contains examples of almost all Dynamicweb features which can be submitted to cart. Please note that this is a test template for internal use, so you should not expect to use it directly - but it may still be of use to you.

You can also download the template.

RAZOR
@inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> <script> function updateCart() { var form = document.getElementById('ordersubmit'); var field = document.createElement('input'); field.type = 'hidden'; field.name = 'CartV2.GotoStep0'; field.id = 'CartV2.GotoStep0'; field.value = 'Update'; form.appendChild(field); form.submit(); } function clearRadioGroup(name) { var group = document.getElementsByName(name); for (var i = 0; i < group.length; i++) { group[i].checked = false; } } </script> <!--Cart info--> <div> <div>Current Cart: @GetString("Ecom:Order.ID")</div> </div> <!--Cart contents--> @if (GetBoolean("Ecom:Order.IsEmpty")) { <div class="alert alert-info">Your cart is empty</div> } else { <table class="table orderlines"> <thead> <tr> <th class="name">@Translate("Product_name", "Product name")</th> <th class="quantity">@Translate("Quantity", "Quantity")</th> <th class="amount">@Translate("Price", "Price")</th> <th>CrtCmd</th> </tr> </thead> <tfoot> <tr class="total"> <th>@Translate("Total")</th> <th></th> <th class="amount">@GetValue("Ecom:Order.OrderLines.Total.PriceWithVAT")</th> <th></th> </tr> </tfoot> <tbody> @foreach (var line in GetLoop("OrderLines")) { <tr data-product-auto-id='@line.GetValue("Ecom:Product.AutoID")'> <td class="name"> <a href='@line.GetValue("Ecom:Order:OrderLine.ProductLink")'> @line.GetValue("Ecom:Order:OrderLine.ProductName.Short") @if (!string.IsNullOrWhiteSpace(line.GetString("Ecom:Order:OrderLine.ProductVariantText"))) { @: (@line.GetString("Ecom:Order:OrderLine.ProductVariantText")) } </a> <!--Order line fields--> @foreach (var field in line.GetLoop("Order.OrderLineFields")) { if (!string.IsNullOrWhiteSpace(@field.GetString("Ecom:Order:OrderLine.OrderLineField.Value"))) { <div style="font-size:12px;"><i>@field.GetString("Ecom:Order:OrderLine.OrderLineField.Name"): @field.GetString("Ecom:Order:OrderLine.OrderLineField.Value")</i></div> } } </td> <td class="quantity">@line.GetValue("Ecom:Order:OrderLine.Quantity")</td> <td class="amount">@line.GetValue("Ecom:Order:OrderLine.TotalPrice")</td> <td class="actions"> @if (line.GetBoolean("Ecom:Order:OrderLine.IsProduct")) { <!--Incorderline, decorderline, delorderline--> <form method="post"> <input type="hidden" name="key" value='@line.GetValue("Ecom:Order:OrderLine.Id")' /> <button class="btn btn-default" type="submit" name="CartCmd" value="IncOrderLine"><span class="glyphicon glyphicon-plus"></span></button> <button class="btn btn-default" type="submit" name="CartCmd" value='DecOrderline'><span class="glyphicon glyphicon-minus"></span></button> <button class="btn btn-default" type="submit" name="CartCmd" value="DelOrderLine"><span class="glyphicon glyphicon-trash"></span></button> </form> } </td> <td> </td> </tr> foreach (var bomItem in GetLoop("BOMItems")) { <tr class="bom-item"> <td>@bomItem.GetValue("Ecom:Order:OrderLine.ProductName.Short")</td> <td>@bomItem.GetValue("Ecom:Order:OrderLine.Quantity")</td> <td>@bomItem.GetValue("Ecom:Order:OrderLine.TotalPrice")</td> <td></td> </tr> } } </tbody> </table> <!--Orderline Cart commands--> <div class="panel panel-default col-md-12"> <h3>Cart Commands</h3> <!--Orderlines--> <div class="col-md-12 panel panel-default"> <h4>Orderlines</h4> <table> <thead> <tr> <th class="name">Product</th> <th class="quantity">Quantity</th> <th class="amount"></th> </tr> </thead> <tbody> @foreach (var line in GetLoop("OrderLines")) { if (line.GetBoolean("Ecom:Order:OrderLine.IsProduct")) { <tr> <td> <a href='@line.GetValue("Ecom:Order:OrderLine.ProductLink")'> @line.GetValue("Ecom:Order:OrderLine.ProductName.Short") @if (!string.IsNullOrWhiteSpace(line.GetString("Ecom:Order:OrderLine.ProductVariantText"))) { @: (@line.GetString("Ecom:Order:OrderLine.ProductVariantText")) } </a> </td> <td> <form method="post"> <input type="hidden" name="key" value='@line.GetValue("Ecom:Order:OrderLine.Id")' /> <input type="number" name="quantity" value="1" /> <button class="btn btn-default" type="submit" name="CartCmd" value="Orderline">Add quantity</button> </form> </td> </tr> } } </tbody> </table> </div> <!--Update orderlines--> <div class="col-md-12 panel panel-default"> <h4>UpdateOrderlines</h4> <form method="post"> <table> <thead> <tr> <th class="name">Product</th> <th class="quantity">Quantity</th> <th class="amount"></th> </tr> </thead> <tbody> @foreach (var line in GetLoop("OrderLines")) { if (line.GetBoolean("Ecom:Order:OrderLine.IsProduct")) { <tr> <!--Name--> <td> <a href='@line.GetValue("Ecom:Order:OrderLine.ProductLink")'> @line.GetValue("Ecom:Order:OrderLine.ProductName.Short") @if (!string.IsNullOrWhiteSpace(line.GetString("Ecom:Order:OrderLine.ProductVariantText"))) { @: (@line.GetString("Ecom:Order:OrderLine.ProductVariantText")) } </a> </td> <!--Input--> <td> <input type="number" name="QuantityOrderLine@(line.GetValue("Ecom:Order:OrderLine.Id"))" value="@line.GetValue("Ecom:Order:OrderLine.Quantity")" /> </td> </tr> } } </tbody> </table> <button class="btn btn-default" type="submit" name="CartCmd" value="Updateorderlines">Updateorderlines</button> </form> </div> <div class="col-md-12 panel panel-default"> <h4>Emptycart & Deleteallorderlines</h4> <!--Emptycart--> <form method="post"> <button class="btn btn-default" type="submit" name="CartCmd" value="emptycart"><span class="glyphicon glyphicon-trash"></span> Emptycart</button> </form> <!--Delete all orderlines--> <form method="post"> <button class="btn btn-default" type="submit" name="CartCmd" value="deleteallorderlines"><span class="glyphicon glyphicon-trash"></span>Delete all order lines</button> </form> </div> </div> } <form name="ordersubmit" id="ordersubmit" method="post" role="form"> <!--Billing Address Fields--> <div class="panel panel-default col-md-12"> <h3>Billing address</h3> <p>Billing address details are submitted using input fields with specific names and ids, as in the example below. Many - but not all - of the order fields available can be matched with a specific user field when the user is logged in. You can also retrieve the user object directly via the API and access the properties directly.</p> <!--Personal Fields--> <div class="col-md-12 panel panel-default"> <h4>Personal</h4> <p>All the personal fields map directly to a specific user field, here accessible via the Ecom:Order.Customer.{Field} tags.</p> <label for="EcomOrderCustomerName" class="col-md-4 control-label">Name</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerName" id="EcomOrderCustomerName" value='@GetValue("Ecom:Order.Customer.Name")' /> </div> <label for="EcomOrderCustomerPrefix" class="col-md-4 control-label">Att.</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerPrefix" id="EcomOrderCustomerPrefix" value='@GetValue("Ecom:Order.Customer.Prefix")' /> </div> <label for="EcomOrderCustomerTitle" class="col-md-4 control-label">Title</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerTitle" id="Title" value='@GetValue("Ecom:Order.Customer.Title")' /> </div> <label for="EcomOrderCustomerFirstName" class="col-md-4 control-label">FirstName</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerFirstName" id="EcomOrderCustomerFirstName" value='@GetValue("Ecom:Order.Customer.FirstName")' /> </div> <label for="EcomOrderCustomerMiddleName" class="col-md-4 control-label">MiddleName</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerMiddleName" id="EcomOrderCustomerMiddleName" value='@GetValue("Ecom:Order.Customer.MiddleName")' /> </div> <label for="EcomOrderCustomerSurname" class="col-md-4 control-label">Surname</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerSurname" id="EcomOrderCustomerSurname" value='@GetValue("Ecom:Order.Customer.Surname")' /> </div> <label for="EcomOrderCustomerEmail" class="col-md-4 control-label">Email</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerEmail" id="EcomOrderCustomerEmail" value='@GetValue("Ecom:Order.Customer.Email")' /> </div> </div> <!--Address Fields--> <div class="col-md-12 panel panel-default"> <h4>Address</h4> <p>Like personal fields, the address fields are matched to standard user fields. The exception is the Country field - which determines which payment and shipping methods are selectable. For this field you can use the Countries loop to list all the Ecommerce countries available on the solution.</p> <label for="EcomOrderCustomerAddress" class="col-md-4 control-label">Address</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerAddress" id="EcomOrderCustomerAddress" value='@GetValue("Ecom:Order.Customer.Address")' /> </div> <label for="EcomOrderCustomerAddress2" class="col-md-4 control-label">Address2</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerAddress2" id="EcomOrderCustomerAddress2" value='@GetValue("Ecom:Order.Customer.Address2")' /> </div> <label for="EcomOrderCustomerHouseNumber" class="col-md-4 control-label">HouseNumber</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerHouseNumber" id="EcomOrderCustomerHouseNumber" value='@GetValue("Ecom:Order.Customer.HouseNumber")' /> </div> <label for="EcomOrderCustomerZip" class="col-md-4 control-label">Zip</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerZip" id="EcomOrderCustomerZip" value='@GetValue("Ecom:Order.Customer.Zip")' /> </div> <label for="EcomOrderCustomerCity" class="col-md-4 control-label">City</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerCity" id="EcomOrderCustomerCity" value='@GetValue("Ecom:Order.Customer.City")' /> </div> <label for="EcomOrderCustomerRegion" class="col-md-4 control-label">Region</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerRegion" id="EcomOrderCustomerRegion" value='@GetValue("Ecom:Order.Customer.Region")' /> </div> <label for="EcomOrderCustomerCountry" class="col-md-4 control-label">Country</label> <div class="col-md-8"> <select class="form-control" 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> <!-- Contact Fields--> <div class="col-md-12 panel panel-default"> <h4>Contact</h4> <p>The customer can also submit contact information to the cart - Phone, Fax(!) and Cell numbers - as well as an order comment.</p> <label for="EcomOrderCustomerPhone" class="col-md-4 control-label">Phone</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerPhone" id="EcomOrderCustomerPhone" value='@GetValue("Ecom:Order.Customer.Phone")' /> </div> <label for="EcomOrderCustomerFax" class="col-md-4 control-label">Fax</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerFax" id="EcomOrderCustomerFax" value='@GetValue("Ecom:Order.Customer.Fax")' /> </div> <label for="EcomOrderCustomerCell" class="col-md-4 control-label">Cell</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerCell" id="EcomOrderCustomerCell" value='@GetValue("Ecom:Order.Customer.Cell")' /> </div> <label for="EcomOrderCustomerComment" class="col-md-4 control-label">Comment</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerComment" id="EcomOrderCustomerComment" value='@GetValue("Ecom:Order.Customer.Comment")' /> </div> </div> <!--Other fields--> <div class="col-md-12 panel panel-default"> <h4>Other</h4> <p>Finally, a selection of miscellaneous fields can be submitted to cart to add additional information to the order - e.g. an EAN-number, a VAT registration number, or the customer Company. </p> <label for="EcomOrderCustomerCompany" class="col-md-4 control-label">Company</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerCompany" id="EcomOrderCustomerCompany" value='@GetValue("Ecom:Order.Customer.Company")' /> </div> <label for="EcomOrderCustomerEAN" class="col-md-4 control-label">EAN</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerEAN" id="EcomOrderCustomerEAN" value='@GetValue("Ecom:Order.Customer.EAN")' /> </div> <label for="EcomOrderCustomerVatRegNumber" class="col-md-4 control-label">VatRegNumber</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerVatRegNumber" id="EcomOrderCustomerVatRegNumber" value='@GetValue("Ecom:Order.Customer.VatRegNumber")' /> </div> <label for="EcomOrderCustomerRefId" class="col-md-4 control-label">RefId</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerRefId" id="EcomOrderCustomerRefId" value='@GetValue("Ecom:Order.Customer.RefID")' /> </div> <label for="EcomOrderCustomerInitials" class="col-md-4 control-label">Initials</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderCustomerInitials" id="EcomOrderCustomerInitials" value='@GetValue("Ecom:Order.Customer.Initials")' /> </div> </div> </div> <!--Delivery Address Fields--> <div class="panel panel-default col-md-12"> <h3>Delivery Address fields</h3> <p>In almost all respects, the delivery address fields are identical to the billing address fields. The only difference is that there is no Comment field, and that billing specific information such as the Vat Registration Number is not present.</p> <p>The <i>'Copy customer info to delivery info fields if empty'</i>-checkbox under Settings > Ecommerce > Advanced Configuration > Shopping cart allows you to copy billing address field values to the delivery address fields if no part of the delivery address has been filled out. </p> <!--Name Fields--> <div class="col-md-12 panel panel-default"> <h4>Personal</h4> <label for="EcomOrderDeliveryName" class="col-md-4 control-label">Name</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryName" id="Name" value='' /> </div> <label for="EcomOrderDeliveryPrefix" class="col-md-4 control-label">Att.</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryPrefix" id="Prefix" value='' /> </div> <label for="EcomOrderDeliveryTitle" class="col-md-4 control-label">Title</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryTitle" id="Title" value='' /> </div> <label for="EcomOrderDeliveryFirstName" class="col-md-4 control-label">FirstName</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryFirstName" id="EcomOrderDeliveryFirstName" value='' /> </div> <label for="EcomOrderDeliveryMiddleName" class="col-md-4 control-label">MiddleName</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryMiddleName" id="EcomOrderDeliveryMiddleName" value='' /> </div> <label for="EcomOrderDeliverySurname" class="col-md-4 control-label">Surname</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliverySurname" id="EcomOrderDeliverySurname" value='' /> </div> <label for="EcomOrderDeliveryEmail" class="col-md-4 control-label">Email</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryEmail" id="EcomOrderDeliveryEmail" value='' /> </div> </div> <!--Address Fields--> <div class="col-md-12 panel panel-default"> <h4>Address</h4> <label for="EcomOrderDeliveryAddress" class="col-md-4 control-label">Address</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryAddress" id="EcomOrderDeliveryAddress" value='' /> </div> <label for="EcomOrderDeliveryAddress2" class="col-md-4 control-label">Address2</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryAddress2" id="EcomOrderDeliveryAddress2" value='' /> </div> <label for="EcomOrderDeliveryHouseNumber" class="col-md-4 control-label">HouseNumber</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryHouseNumber" id="EcomOrderDeliveryHouseNumber" value='' /> </div> <label for="EcomOrderDeliveryZip" class="col-md-4 control-label">Zip</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryZip" id="EcomOrderDeliveryZip" value='' /> </div> <label for="EcomOrderDeliveryCity" class="col-md-4 control-label">City</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryCity" id="EcomOrderDeliveryCity" value='' /> </div> <label for="EcomOrderDeliveryRegion" class="col-md-4 control-label">Region</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryRegion" id="EcomOrderDeliveryRegion" value='' /> </div> <label for="EcomOrderDeliveryCountry" class="col-md-4 control-label">Country</label> <div class="col-md-8"> <select class="form-control" name="EcomOrderDeliveryCountry" id="EcomOrderDeliveryCountry" onchange="updateCart();"> <option>Select delivery country</option> @foreach (var country in GetLoop("Countries")) { if (country.GetString("Ecom:Country.IsDeliveryCountry") == "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> <!-- Phone Fields--> <div class="col-md-12 panel panel-default"> <h4>Phone</h4> <label for="EcomOrderDeliveryPhone" class="col-md-4 control-label">Phone</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryPhone" id="EcomOrderDeliveryPhone" value='' /> </div> <label for="EcomOrderDeliveryFax" class="col-md-4 control-label">Fax</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryFax" id="EcomOrderDeliveryFax" value='' /> </div> <label for="EcomOrderDeliveryCell" class="col-md-4 control-label">Cell</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryCell" id="EcomOrderDeliveryCell" value='' /> </div> </div> <!--Other fields--> <div class="col-md-12 panel panel-default"> <h4>Other</h4> <label for="EcomOrderDeliveryCompany" class="col-md-4 control-label">Company</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryCompany" id="EcomOrderDeliveryCompany" value='' /> </div> <label for="EcomOrderDeliveryInitials" class="col-md-4 control-label">Initials</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderDeliveryInitials" id="EcomOrderDeliveryInitials" value='' /> </div> </div> </div> <!--Payment Methods--> <div class="col-md-12 panel panel-default"> <legend><h4>Payment Methods</h4></legend> <p>Payment methods are rendered via the <i>Paymethods</i> loop. Some extra logic is needed if you want to support saved cards - both to save the card and to ensure that you don't submit both a saved payment method and a regular payment method to cart. In this case we use a javascript function to clear the Payment Methods radio button if a saved card is selected.</p> @foreach (var paymentmethod in GetLoop("Paymethods")) //Loop through payment methods { if (paymentmethod.GetString("Ecom:Cart.Paymethod.SupportSavedCard") == "true") // Check if this method supports saved cards { <div class="radio"> <label class="col-sm-10"> @if (@paymentmethod.GetString("Ecom:Cart.Paymethod.IsSelected") == "true") // Check if payment method is selected { <input type="radio" onclick="clearRadioGroup('EcomCartSavedCardID');updateCart();" name="EcomCartPaymethodID" id='EcomCartPaymethodID_@paymentmethod.GetValue("Ecom:Cart.Paymethod.ID")' value='@paymentmethod.GetValue("Ecom:Cart.Paymethod.ID")' checked='checked' /> } else { <input type="radio" onclick="clearRadioGroup('EcomCartSavedCardID');updateCart();" 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> if (@paymentmethod.GetString("Ecom:Cart.Paymethod.IsSelected") == "true") // If payment method is selected and it supports saved cards make it possible to save a card { <div class="radio" style="padding:0 0 0 35px;"> <label> <input type="checkbox" name="EcomOrderSavedCardCreate" id="EcomOrderSavedCardCreate_@paymentmethod.GetString("Ecom:Cart.Paymethod.ID")" value="true" /> <text> Save Card - Name: </text><input type="text" name="EcomOrderSavedCardName" id="MySavedCardName" value='My @paymentmethod.GetValue("Ecom:Cart.Paymethod.Name") Card' /> </label> </div> } } else //If saved cards are not supported { <div class="radio"> <label class="col-sm-10"> @if (@paymentmethod.GetString("Ecom:Cart.Paymethod.IsSelected") == "true") // check if payment method is selected { <input type="radio" onclick="clearRadioGroup('EcomCartSavedCardID');updateCart();" name="EcomCartPaymethodID" id='EcomCartPaymethodID_@paymentmethod.GetValue("Ecom:Cart.Paymethod.ID")' value='@paymentmethod.GetValue("Ecom:Cart.Paymethod.ID")' checked='checked' /> } else { <input type="radio" onclick="clearRadioGroup('EcomCartSavedCardID');updateCart();" 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> } } </div> <!--Saved Cards/Payment Methods--> <div class="col-md-12 panel panel-default"> <legend><h4>Saved Cards</h4></legend> <p>Saved Cards - or more properly saved <i>payment methods</i> - are most easily rendered in a separate list. Like for payment methods, we use a js function to clear any payment method selected when a saved card is selected. </p> @{ var savedcards = GetLoop("SavedCards"); } @if (savedcards.Any()) //Checks if this user has any saved cards { foreach (var savedcard in savedcards) { <div class="radio"> <label class="col-sm-10"> @if (@savedcard.GetString("Ecom:SavedCard.IsSelected") == "true") //Checks if the saved card is selected { <input type="radio" onclick="clearRadioGroup('EcomCartPaymethodID');updateCart();" name="EcomCartSavedCardID" id="EcomCartSavedCardID_@savedcard.GetString("Ecom:SavedCard.ID")" value="@savedcard.GetString("Ecom:SavedCard.ID")" checked="checked" /> } else { <input type="radio" onclick="clearRadioGroup('EcomCartPaymethodID');updateCart();" name="EcomCartSavedCardID" id="EcomCartSavedCardID_@savedcard.GetString("Ecom:SavedCard.ID")" value="@savedcard.GetString("Ecom:SavedCard.ID")" /> } @savedcard.GetString("Ecom:SavedCard.Name") </label> </div> } } else { <div><i>No saved cards for this user</i></div> } </div> <!--Inline payment forms--> <div class="col-md-12 panel panel-default"> <legend><h4>Inline payment form</h4></legend> <p>If a checkout handler supports inline forms, the tag <i>Ecom:Cart.PaymentInlineForm</i> is used to render the checkout handler post form inline.</p> @if (!string.IsNullOrWhiteSpace(GetString("Ecom:Cart.PaymentInlineForm"))) { @GetString("Ecom:Cart.PaymentInlineForm") } else { <div><i>The selected payment method does not support inline payments</i></div> } </div> <!--Shipping--> <div class="col-md-12 panel panel-default"> <legend><h4>Shipping Methods</h4></legend> <p>Shipping method selections are submitted to cart using input elements with the name <i>EcomCartShippingmethodID</i> and an ID of the type EcomCartShippingmethodID_{ShippingMethodID}.</p> @foreach (var shippingmethod in GetLoop("Shippingmethods")) { <div class="radio"> <label class="col-sm-10"> @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> } </div> <!--Vouchers & Coupons--> <div class="panel panel-default col-md-12"> <legend><h4>Vouchers & Coupons</h4></legend> <p>Vouchers are submitted to cart using a field with <i>EcomOrderCustomerVoucher</i> as the name and id. Coupons are discounts triggered by a code - the coupon field is simply a custom order field selected on the discount. Like all custom fields you post values to it using input elements with a name matching the system name of the custom field - no id property necessary. </p> <label for="EcomOrderCustomerVoucher" class="col-sm-4 control-label">@Translate("Voucher", "Voucher")</label> <div class="col-sm-8"> <input class="form-control" type="text" name="EcomOrderCustomerVoucher" id="EcomOrderCustomerVoucher" value='@GetString("Ecom:Order.Customer.VoucherCode")' /> </div> <label for="Coupons" class="col-sm-4 control-label">Coupon</label> <div class="col-sm-8"> <input class="form-control" type="text" name="Coupons" value='' /> </div> </div> <!--Gift Card--> <div class="col-md-12 panel panel-default"> <legend><h4>Gift Card</h4></legend> <label for="EcomOrderGiftCardCode" class="col-sm-4 control-label">Gift Card Code</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderGiftCardCode" id="EcomOrderGiftCardCode" value="" /> </div> </div> <!--Create user during checkout--> <div class="panel panel-default col-md-12"> <legend><h4>Create user during checkout</h4></legend> <p>To create a user during checkout submit a field with the name and ID <i>EcomUserCreateNew</i> to cart alongside fields creating a user name and a password</p> @if (string.IsNullOrWhiteSpace(@GetGlobalValue("Global:Extranet.Name"))) { <div class="form-group"> <label for="EcomUserCreateUserName" class="col-sm-2 control-label">Username</label> <div class="col-md-10"> <input class="form-control" type="text" name="EcomUserCreateUserName" id="EcomUserCreateUserName" /> </div> </div> <div class="form-group"> <label for="EcomUserCreatePassword" class="col-md-2 control-label">New password</label> <div class="col-md-10"> <input class="form-control" type="password" name="EcomUserCreatePassword" id="EcomUserCreatePassword" /> </div> </div> <div class="form-group"> <label for="EcomUserCreateConfirmPassword" class="col-md-2 control-label">Confirm password</label> <div class="col-md-10"> <input class="form-control" type="password" name="EcomUserCreateConfirmPassword" id="EcomUserCreateConfirmPassword" /> </div> </div> <div class="checkbox"> <label class="col-md-12"> <input type="checkbox" name="EcomUserCreateNew" id="EcomUserCreateNew" checked='@GetBoolean("Ecom:User.CreateNew")' /> Save me as customer </label> </div> } else { <div style="font-style:italic">You can't create a user during checkout if you're already logged in</div> } </div> <!--Recurring Orders--> <div class="col-md-12 panel panel-default"> <legend><h4>Recurring Orders</h4></legend> <p>To create a recurring order you must first check if the payment method supports it - if it does you must submit a field with the ID and name <i>EcomRecurringOrderCreate</i> alongside an interval, an interval unit, and a start and end date to the cart.</p> @if (@GetBoolean("Ecom:Order.PaymentMethod.RecurringSupport") == true) { <!--Checkbox to create recurring orders--> <div class="checkbox"> <label class="col-md-12"> <input type="checkbox" name="EcomRecurringOrderCreate" id="EcomRecurringOrderCreate" checked='@GetBoolean("Ecom:User.CreateNew")' /> Create recurring order </label> </div> <!--Interval--> <label for="EcomOrderRecurringInterval" class="col-md-4 control-label">Interval</label> <div class="col-md-8"> <input class="form-control" type="text" name="EcomOrderRecurringInterval" id="EcomOrderRecurringInterval" value='1' /> </div> <!--Interval units--> <label for="EcomOrderRecurringIntervalUnit" class="col-md-4 control-label">EcomOrderRecurringIntervalUnit</label> <div class="col-md-8"> <select name="EcomOrderRecurringIntervalUnit" id="EcomOrderRecurringIntervalUnit" value="0"> <option value="0">Days</option> <option value="1" selected>Weeks</option> <option value="2">Months</option> </select> </div> <!--Start Date--> <div class="col-md-12"> <label for="EcomOrderRecurringStartDate" class="col-md-4 control-label">Start date</label> <input class="col-md-8" type="date" name="EcomOrderRecurringStartDate" id="EcomOrderRecurringStartDate" min="@DateTime.Today.ToString("yyyy-MM-dd")" value='@DateTime.Today.ToString("yyyy-MM-dd")' /> </div> <!--End Date--> <div class="col-md-12"> <label for="EcomOrderRecurringEndDate" class="col-md-4 control-label">End date</label> <input class="col-md-8" type="date" name="EcomOrderRecurringEndDate" id="EcomOrderRecurringEndDate" min="@DateTime.Today.AddDays(7).ToString("yyyy-MM-dd")" value='@DateTime.Today.AddDays(7).ToString("yyyy-MM-dd")' /> </div> } else { <div><i>This payment method does no support recurring orders</i></div> } </div> <!--Custom Fields--> <div class="col-md-12 panel panel-default"> <legend><h4>Custom Fields</h4></legend> <p>Custom fields - such as order fields - are posted to the cart using input elements which take the system name as the field name. No ID is necessary.</p> <label for="TrackingURLFromERP" class="col-sm-4 control-label">TrackingURLFromERP</label> <div class="col-sm-8"> <input class="form-control" type="text" name="TrackingURLFromERP" value='' /> </div> <label for="Coupons" class="col-sm-4 control-label">CustomerOrderNumber</label> <div class="col-sm-8"> <input class="form-control" type="text" name="CustomerOrderNumber" value='' /> </div> </div> <!--Opt-ins--> <div class="panel panel-default col-md-12"> <legend><h4>Opt-ins</h4></legend> <p>From the cart you can change the value of the <i>Email permission</i> field on the logged in user, and the user can be asked to accept the <i>Terms & Conditions</i>.</p> @if (GetString("Ecom:Cart.UseNewsletterSubscription") == "True") { <div class="checkbox"> <label for="EcomOrderSubscribeToNewsletter"> <input type="checkbox" name="EcomOrderSubscribeToNewsletter" id="EcomOrderSubscribeToNewsletter" checked='@GetBoolean("Ecom:Order.Customer.NewsletterSubscribe")' /> Subscribe to newsletter </label> </div> } else { <div><i>This cart does not have the 'Use email subscription' field checked in the app settings</i> </div> } <div class="checkbox"> <label> <input type="checkbox" id="EcomOrderCustomerAccepted" name="EcomOrderCustomerAccepted" value="1" /> Accept terms and conditions </label> </div> </div> <!--Submit or Update cart--> <div class="col-md-12"> <button type="submit" name='@GetValue("CartV2.CurrentStepButtonName")' id='@GetValue("CartV2.CurrentStepButtonName")' class="submitter btn btn-primary">@Translate("Update_order", "Update order")</button> <button type="submit" name='@GetValue("CartV2.NextStepButtonName")' id='@GetValue("CartV2.NextStepButtonName")' class="submitter btn btn-primary">@Translate("Create_order", "Create order")</button> </div> </form>

The notification emails section is used to create email notifications for both customers and staff – most commonly an order confirmation email.

To create a notification click Add notification to open the Edit email window (Figure 3.1) and configure the notification.

Figure 3.1 Creating a notification email

To configure the notification you must first specify a recipient – this can be done in several ways:

  • Check For Customer Email Address to use the billing address email
  • Check For Delivery Email Address to use the delivery address email
  • Check Use field for email recipient to select a custom order or user field
  • Manually specify a recipient email – also accepts a list of email addresses separated by commas

For each notification you must also:

  • Provide a subject
  • Select/create an email template
  • Provide a sender name and sender email
  • Select an encoding (defaults to UTF-8)
  • (Optional) Select an attachment to include with the email

Tip: You can use {OrderID} in the subject to insert the order number in the confirmation email.

The field validation settings (Figure 4.1) allow you to apply validation rules during checkout.

Figure 4.1 The field validation settings

You can:

  • Toggle a check for terms and conditions
  • Toggle a check for stock status – this field is required if product reservation is enabled
  • Select a validation group to apply during checkout

Shipping Address Validation

Prior to Dynamicweb 9.8, three shipping fields were auto-validated - Name, Address, and Country - but as of 9.8 and going forward only Country is automatically validated. You must therefore use field validation groups - as linked to above - to set up further validation of shipping fields.

A number of other settings ara available on the shopping cart app - these are described below.

The Newsletter section(Figure 6.1) is used to make it possible to update the email permission field for the user during checkout. The current value is available via the Ecom:Cart.UseNewsletterSubscription tag.

Figure 6.1 The newsletter settings

The Empty cart settings (Figure 7.1) control what a user will see when they view an empty cart.

Figure 7.1 The empty cart settings

You have three options:

  • Redirect to an internal page lets you select a specific page on your website to redirect users to
  • Show template allows you to select a custom template to show instead of the cart. You can then add cool stuff to the template, of course, like suggested or personalized products and news.
  • Take no special action does exactly what it says, and simply displays the first step of the order process as with a full cart. However, you can use the Ecom:Order.IsEmpty tag to test for and do stuff if a cart is empty.

The User management section (Figure 8.1) allows you to control how the cart interacts with the Users area of Dynamicweb.

Figure 8.1 The user management settings

Basically, you can do two things:

  • Apply user details to order – this setting applied user field like Name, Address, Phone, and so on to an order during checkout. You usually always want this enabled unless you’re creating orders on behalf of anonymous users.
  • Create user during checkout – this option creates users during checkout. Additional settings are available when this setting is enabled:

Setting

Use

Comments

Groups for new users

Add the new users to these groups

 

Update existing users based on email match

If a user with this email exists, update the user with the checkout information details

 

Error messages

Specify validation error messages

 

If a user is logged in on two separate computers, the shopping cart content will be automatically synchronized.

Saving an address to user management

Dynamicweb supports saving an address on a logged in user on checkout. Look at the InformationSaveCart.hmtl template for example code.

The address will be saved to the addresses section of the user, if it doesn’t match an existing address.

On multi-shop solutions the payment & delivery section is used to control exactly which payment methods and shipping methods should be available on this cart (Figure 9.1).

Figure 9.1 The payment and delivery settings

To make a payment method or shipping method available for a cart, move it from the list of available methods (left column) to the list of selected methods (right column).

Leave as All if all methods should be made available, depending on the language context.

The behavior settings (Figure 10.1) control cart behavior in a variety of situations, as outlined below.

Figure 10.1 The behavior settings

You can control:

  • If the cart should show the first order step or show the last visited step for users returning to the cart after leaving the order flow. If you use validation groups on other steps than the first step, set to last visited to make sure you have access to validation loops in your templates.
  • How the cart selects which shipping and payment methods should be available – the options being customer country, delivery country, or delivery country if a delivery address has been selected.
    • Prices in the shopping cart are recalculated when the billing or delivery country is changed. VAT is recalculated when a new bulling country is set, and Taxes are recalculated when a new delivery country is set.
  • If products which have become unavailable should be removed or ignored
  • If you want to use an image pattern from a product catalog in the cart

If you need more than one type of shopping cart on a solution - e.g. a Daily, a Weekly and a Monthly cart - you can create and use different order contexts.

To create an order context:

  • Navigating to Settings > Ecommerce > Orders > Order contexts
  • Click New order context in the toolbar
  • Enter a name
  • Specify which shop(s) to make it available for 
  • Save
Figure 11.1 Creating a new order context

An order context can be selected on each shopping cart app to create non-default carts, e.g. a quotes cart or a weekly cart as an alternative to the default cart.

To add products to a context cart use the OrderContexts loop and related tags.

There’s a saying in software: Never trust user input.

For an open system like an Ecommerce website, this is even more important – and that’s why we have data validation on the shopping cart. This is not only useful for stopping malicious users from submitting bad data, it’s also a helping hand to users who make mistakes. All it takes is a comma instead of a dot in an email address, and suddenly you have missing order confirmations, tracking codes, and so forth.

Here’s how to validate user data on the Shopping Cart.

Order step validation exists to make sure users cannot modify the underlying HTML of your checkout process to skip order steps.

To prevent this from happening:

  • Go to Settings > Ecommerce > Advanced Configuration > Shopping Cart
  • Check the Enable step validation checkbox (Figure 13.1)
Figure 13.1 Order step validation

This ensures that no orders will be marked as Completed before it has passed the payment step.

It’s advisable to combine this with making the Payment method ID field a required in your validation group.

You can read more about the advanced settings in this article.

Address validation lets you set up and manage address validation methods based on address validation providers.

When a customer progresses through the checkout process, their billing and delivery addresses are then validated against the information provided by the address validation provider.

Dynamicweb currently supports using the Avalara address validation provider, which can be used with the Avalara Tax provider, and the FedEx address validation provider, for use with the FedEx shipping provider.

To set up address validation:

  • Go to Settings > Ecommerce > Orders > Address validation
  • Click new in the toolbar

This opens the Address validator settings window (Figure 14.1).

Figure 14.1 Creating an address validator

From here, you must:

  • Name the address validator
  • Activate the address validator by checking the Active checkbox
  • Select one or more countries for which the validator should be active
  • (Optional) Select one or more user groups for which address validation should not be used
  • Select your address validation provider
  • Fill out any parameters required by the provider – these will vary depending on the provider you use.

To implement the address validator in your order flow you have access to the AddressValidators loop and associated tags. For an example, refer to the InformationAddressValidation.html template (located in Files/Templates/eCom7/CartV2/Step).

From Settings > Ecommerce > Advanced configuration > Shopping Cart you have access to a number of advanced configuration options for the Shopping Cart app.

Read about the settings in this article.

database

These are the database tables relevant for the Shopping Cart app and related functionality. 

EcomAddressValidatorSettings

Contains settings for providers in Settings -> Ecom -> Orders -> Address Validation.

Field name Data type Length
AddressValidatorSettingId int 4
AddressValidatorName nvarchar 255
AddressValidatorActive bit 1
AddressValidatorProviderSettings nvarchar Max
AddressValidatorUserGroupIds nvarchar Max

EcomValidationGroups

Contains validation groups as defined in Settings -> Ecommerce -> Orders -> Validation groups.

Field name Data type Length
ValidationGroupId nvarchar 50
ValidationGroupName nvarchar 255
ValidationGroupAutoId int 4
ValidationGroupDoNotValidateIfAllFieldsAreEmpty bit 1

EcomValidationGroupsTranslation

Contains translations (EcomValidationGroupsTranslationValidationGroupLanguageID) of validation groups (EcomValidationGroupsTranslationValidationGroupID).

Field name Data type Length
EcomValidationGroupsTranslationValidationGroupID nvarchar 50
EcomValidationGroupsTranslationValidationGroupLanguageID nvarchar 50
EcomValidationGroupsTranslationValidationGroupName nvarchar 255

EcomValidations

Contains field definitions (ValidationFieldName) for validation rules and keeps track of which validation groups it belongs to (ValidationGroupId).

Field name Data type Length
ValidationId nvarchar 50
ValidationGroupId nvarchar 50
ValidationFieldName nvarchar 255
ValidationUseAndOperator bit 1
ValidationFieldType nvarchar 255
ValidationAutoId int 4

EcomValidationRules

Contains validation rule definitions (ValidationRuleId) and keeps track of which fields (ValidationRuleValidationId) belong to a rule.

Field name Data type Length
ValidationRuleId nvarchar 50
ValidationRuleValidationId nvarchar 50
ValidationRuleType nvarchar 255
ValidationRuleParameters nvarchar Max
ValidationRuleAutoId int 4

EcomSavedForLater

Registers products (SavedForLaterProductId, SavedForLaterVariantId, SavedForlaterLangugageId) marked as Saved for Later during a session (SavedForLaterSessionId).

Field name Data type Length
SavedForLaterId int 4
SavedForLaterProductId nvarchar 30
SavedForLaterVariantId nvarchar 255
SavedForLaterLanguageId nvarchar 50
SavedForLaterSessionId int 4
SavedForLaterDateAdded datetime 8

EcomOrderContexts

Field name Data type Length
OrderContextId nvarchar 50
OrderContextName nvarchar 255
OrderContextAutoId int 4

EcomOrderContextAccessUserRelation

Field name Data type Length
OrderContextAccessUserAccessUserId int 4
OrderContextAccessUserOrderContextId nvarchar 50
OrderContextAccessUserOrderId nvarchar 50
OrderContextAccessUserRelationAutoId int 4

EcomOrderContextShopRelation

Field name Data type Length
OrderContextShopRelationContextId nvarchar 50
OrderContextShopRelationShopId nvarchar 50
OrderContextShopRelationAutoId int 4