Live Integration

A live integrations consists of real-time requests from Dynamicweb to a remote system. The responses are then used to update e.g. product, cart or order information before displaying them in the frontend.

Currently, a standard live integration implementation allows for live integration of prices and stock, live calculation of shopping carts and live creation of orders in the remote system.

The data flow differs slightly depending on the type of live integration implemented:

  • When a page containing products is being rendered, a list of product IDs are sent to the remote system alongside the ID of the current user. The remote system then returns the prices and stock state for that user, and the products in memory are then rendered with the prices appropriate to that user. No data is saved in the Dynamicweb database.
  • Likewise, when a shopping cart or an order is being created, the cart information (products, quantities, user information) is sent to the remote system. The remote system then returns an updated cart or order with customer specific prices and discounts – and the information is saved in the Dynamicweb database before being shown to the user. If an order is created in the remote system, the ID is returned to Dynamicweb and saved in the Dynamicweb database so that the link between the order in the remote system and in the Dynamicweb database is maintained.

In this article we will cover four things:

  • How to set up a live integration on your remote system and in Dynamicweb
  • The data formats of various requests and responses
  • How to customize your live integration
  • How to set up the Integration Customer Center module

The initial setup procedure when creating a live integration is similar to creating a batch integration – you must:

  • Install and configure a plugin and the Dynamicweb Connector on your remote system
  • Test the connections using the Dynamicweb Service Test app
  • Use a batch scheduled task to import data from your remote system

The reason is pretty straight forward; you can’t request real time information about e.g. prices, carts, or orders if you don’t have some information about them in your database in the first place – to retrieve user specific prices for a product, for instance, you need both a product ID and a user ID in Dynamicweb to send with the request. And naturally we can’t render, index and filter products before they exist in Dynamicweb.

You can read more about the initial setup procedure in the Batch Integration article.

After the initial setup procedure is completed, you must:

  • Configure the Live Integration add-in
  • Test the connection between Dynamicweb and the remote system

 

NB! Additionally, if your integration contains discounts, you must also create a new sales discount of type ‘Live integration discount’ – it will only be available for creation when the live integration dll is in your bin folder. Do not confuse "Sales discount" with "Order discount". "Order discount" is the newer, but it has no Add-In functionality, which is why you should use "Sales discount".

If you do not find "Sales discount" on the list (alongside "Order discount"), activate it from Management Center -> Ecommerce -> Advanced configuration -> Order discounts. Here you should uncheck Only use order discounts.

In order to configure the live integration add-in, you must copy the LiveIntegration.dll into the bin folder on your solution.

Once this is done, you will be able to access the live integration configuration page from Settings > Integration > Integration Framework live.

Assuming you are using the basic live integration provided by us, the interface will look like Figure 3.1.

Figure 3.1 The standaed live integration add-in

To configure the add-in, point it to the web service URL exposed by the Dynamicweb Connector service installed in your remote environment. You can have multiple endpoints, e.g. for multiple accountings – each endpoint requiring a unique URL (with a matching connector service – 2 endpoints = 2 connectors).

The URL syntax for the URL is:

[URL];[Field];[Value]

The Field property can get data from either of these:

  • User.Company (AccessUserCompany column of table AccessUser)
  • User.Department (AccessUserDepartment column of table AccessUser)
  • User.[Any custom field] (System name (!) of any custom field column of table AccessUser).
  • Order.[Any custom field] (Any custom field column of table EcomOrder)
  • Session.Shop (ShopID of the shop in current http context)

Additionally you can:

  • Enter an encryption security key matching the secret defined in the DynamicwebConnector configuration file (true?)
  • Set a tag for checking the state of the live integration connection (and e.g. warn users that customer specific prices are not available, or hide the product catalog behind an error message, etc.)
  • Allow live integration for anonymous users. This is not implemented in the remote system plugins by default, but is available for anyone who wishes to implement the functionality
  • Set caching levels to either session or per page load, depending on how critical it is to you that the information being rendered is up to date
  • Enter a text for imported discounts – which, by default, do not include a descriptive text to display in carts and on orders

You can also set up email notifications to notify key people of problems with the connection, and set various logging levels for development and production use.

Finally, you can include various custom fields in the XML when sending to request price and order calculations from the remote system. Custom fields are not handled by the plugin examples we provide, but the option is available for anyone who wishes to implement functionality which requires them.

When you’ve configured the live integration add-in, you can use the Dynamicweb Service Test app to test the connection between Dynamicweb and your remote system.

Conveniently, the Dynamicweb Service Test app can also be accessed directly from Settings > Integration > Integration Framework Live, provided that you have the live integration .dll in your bin folder.

Read about the test app in the Batch Integration article.

If you need to adjust the integration on the remote side, or if you need to create a custom integration, you need to know the format and content of the XML documents that are being sent back and forth between Dynamicweb and the remote system.

And when implementing a new integration, or editing an existing integration, the format of the XML you are returning to your Dynamicweb solution should match the Dynamicweb XML structure.

Below you can find examples of the XML requests and responses that are sent when using the live integration. 

The XML request sent to the remote system when one or more products are being shown in the frontend looks like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <GetEcomData ExternalUserID="60000"> <tables> <Products type="filter"> <Product> <ProductID>1000</ProductID> <ProductVariantID></ProductVariantID> <ProductNumber>1000</ProductNumber> <CurrencyCode>DKK</CurrencyCode> </Product> <Product> <ProductID>1001</ProductID> <ProductVariantID></ProductVariantID> <ProductNumber>1001</ProductNumber> <CurrencyCode>DKK</CurrencyCode> </Product> </Products> </tables> </GetEcomData>

If the Live Integration is set up to allow anonymous access, and no user is logged in, the ExternalUserID will contain the string “Anonymous”

The expected XML response looks like this:

<?xml version="1.0" encoding="utf-8"?> <tables> <table tableName="EcomProducts"> <item table="EcomProducts"> <column columnName="ProductID"><![CDATA[1000]]></column> <column columnName="ProductVariantID"><![CDATA[]]></column> <column columnName="ProductNumber"><![CDATA[1000]]></column> <column columnName="ProductName"><![CDATA[Cykel]]></column> <column columnName="ProductPrice"><![CDATA[4,500]]></column> <column columnName="ProductStock"><![CDATA[32]]></column> <column columnName="ProductCurrencyCode"><![CDATA[]]></column> </item> <item table="EcomProducts"> <column columnName="ProductID"><![CDATA[1001]]></column> <column columnName="ProductVariantID"><![CDATA[]]></column> <column columnName="ProductNumber"><![CDATA[1001]]></column> <column columnName="ProductName"><![CDATA[Turcykel]]></column> <column columnName="ProductPrice"><![CDATA[4,000]]></column> <column columnName="ProductStock"><![CDATA[0]]></column> <column columnName="ProductCurrencyCode"><![CDATA[]]></column> </item> </table> </tables>

Strictly speaking, only the ProductID, ProductVariantID, price and stock info is used when updating the products in memory.

The ProductName and ProductNumber make it easier when debugging, along with info on currency.

The XML request when calculating a cart or creating an order looks like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <CalculateOrder> <Orders> <Order> <column columnName="OrderCustomerAccessUserExternalId">60000</column> <column columnName="CreateOrder">True</column> <column columnName="OrderID">ORDER19</column> <column columnName="OrderCurrencyCode">DKK</column> <column columnName="OrderDate">24-04-2013 17:14:07</column> <column columnName="OrderPaymentMethodName"></column> <column columnName="OrderShippingMethodName"></column> <column columnName="OrderCustomerName">Mark Hifi-Shop</column> <column columnName="OrderCustomerAddress">Centergade 28</column> <column columnName="OrderCustomerAddress2"></column> <column columnName="OrderCustomerCity">Glostrup</column> <column columnName="OrderCustomerCountryCode"></column> <column columnName="OrderCustomerEmail">Mark@cronuscorp.net</column> <column columnName="OrderCustomerFax"></column> <column columnName="OrderCustomerPhone"></column> <column columnName="OrderCustomerZip">2600</column> <column columnName="OrderDeliveryName"></column> <column columnName="OrderDeliveryAddress"></column> <column columnName="OrderDeliveryAddress2"></column> <column columnName="OrderDeliveryCity"></column> <column columnName="OrderDeliveryCountryCode"></column> <column columnName="OrderDeliveryEmail"></column> <column columnName="OrderDeliveryFax"></column> <column columnName="OrderDeliveryPhone"></column> <column columnName="OrderDeliveryZip"></column> </Order> </Orders> <OrderLines> <OrderLine> <column columnName="OrderLineID">OL97</column> <column columnName="OrderLineProductID">1000</column> <column columnName="OrderLineQuantity">1</column> <column columnName="OrderLinePriceWithoutVAT">4500</column> <column columnName="OrderLineUnitPriceWithoutVAT">4500</column> </OrderLine> </OrderLines> </CalculateOrder>

Similarly to the Product list request, the OrderCustomerAccessUserExternalId node will be set to “Anonymous” if the live integration is configured to allow anonymous customers. The CreateOrder node is “False” if this is simply a cart to be calculated, and true otherwise.

If there are any discounts set up in Dynamicweb, these will be included as order lines. Assuming you are handling discounts in the ERP system, it is highly recommended that you do not set up any discounts in Dynamicweb (apart from the sales discount of the type ‘Live integration discount’ mentioned above).

Note that while this format looks like there could be multiple orders in one request, the live integration will always only send exactly one order to be calculated.

The expected XML response looks like this:

<?xml version="1.0" encoding="utf-8"?> <tables> <table tableName="EcomOrderLines"> <item table="EcomOrderLines"> <column columnName="OrderLineProductNumber"><![CDATA[1000]]></column> <column columnName="OrderLineProductVariantID"><![CDATA[]]></column> <column columnName="OrderLineID"><![CDATA[10000]]></column> <column columnName="OrderLineQuantity"><![CDATA[1]]></column> <column columnName="OrderLineType"><![CDATA[0]]></column> <column columnName="OrderLinePriceWithoutVAT"><![CDATA[9,000]]></column> <column columnName="OrderLineUnitPriceWithoutVAT"><![CDATA[9,000]]></column> <column columnName="OrderLinePriceWithVAT"><![CDATA[9,000]]></column> <column columnName="OrderLineUnitPriceWithVAT"><![CDATA[9,000]]></column> <column columnName="OrderLinePriceVAT"><![CDATA[0]]></column> <column columnName="OrderLineUnitPriceVAT"><![CDATA[0]]></column> <column columnName="OrderLinePriceVATPercent"><![CDATA[0]]></column> <column columnName="OrderLineUnitPriceVATPercent"><![CDATA[0]]></column> </item> <item table="EcomOrderLines"> <column columnName="OrderLineProductNumber"><![CDATA[1000]]></column> <column columnName="OrderLineProductVariantID"><![CDATA[]]></column> <column columnName="OrderLineParentLineID"><![CDATA[10000]]></column> <column columnName="OrderLineType"><![CDATA[3]]></column> <column columnName="OrderLineQuantity"><![CDATA[1]]></column> <column columnName="OrderLinePriceWithoutVAT"><![CDATA[4,500]]></column> <column columnName="OrderLinePriceWithVAT"><![CDATA[4,500]]></column> <column columnName="OrderLineDiscountPercentage"><![CDATA[50]]></column> <column columnName="OrderLinePriceVAT"><![CDATA[0]]></column> <column columnName="OrderLinePriceVATPercent"><![CDATA[0]]></column> <column columnName="OrderLineUnitPriceVATPercent"><![CDATA[0]]></column> </item> </table> <table tableName="EcomOrders"> <item table="EcomOrders"> <column columnName="OrderCreated"><![CDATA[TRUE]]></column> <column columnName="OrderID"><![CDATA[1030]]></column> <column columnName="OrderPriceWithVAT"><![CDATA[4,500]]></column> <column columnName="OrderPriceWithoutVAT"><![CDATA[4,500]]></column> <column columnName="OrderPriceVAT"><![CDATA[0]]></column> <column columnName="OrderSalesDiscount"><![CDATA[4,500]]></column> <column columnName="OrderShippingFee"><![CDATA[]]></column> </item> </table> </tables>

Once again, if this is simply a cart that is being calculated, the response should contain “False” in the OrderCreated node on the EcomOrders Item.

The orderLineType can contain several Values:

  • 0 is a regular product orderline
  • 1 is an Order Discount
  • 3 is a Product Discount

Any other orderLineTypes are ignored.

If you return a product discount, make sure that the parentID is set correctly.

If you need to manipulate your data or the dataflow between Dynamicweb and the remote system there are several ways to accomplish that. For live integration those options are:

  • Live Integration Project
    The Live Integration Project, an implementation of the notification subscribers needed to handle a live integration, is meant to be customized, which means that you can customize both the requests sent to the Dynamicweb Connector and the way you handle the data which is returned.
  • Custom Dynamicweb Connector add-ins
    If you are creating your own integration you have full control over which data is returned by the Dynamicweb Connector, since your custom add-in code handles the dataflow to and from the remote system. Custom add-ins are usually only relevant if you are integrating with a remote system for which we do not provide a standard add-in, or if the remote system is heavily customized.

The Live Integration Project is an implementation of notification subscribers, a PriceProvider and a configurable add-in, in order to handle live integration of prices and stock, shopping carts and orders. It can be used as an advanced starting point for developing custom live integrations.

The overall structure is as follows:

  • The LiveIntegrationAddin class is for adding GUI functionality. This class is a configurable add-in, which means that the properties (if decorated correctly) show up in the Live Integration Framework add-in screen. Use this class to add settings which should be configurable by the administrator
  • The PrepareProductInfoProvider class handles retrieving/caching data when showing product lists, and updating the product lists with the retrieved information. Custom data sent to your remote system must be added manually using the BuildProductRequest method, and return data must be updated manually using the ProcessResponse method.
  • The Orderhandler class handles retrieving/caching data when showing carts or completing orders, and updating the cart/order with the retrieved information. It also caches discounts received from the remote system, to be used by the DiscountProvider. Custom data sent with a request must be added manually to the BuildOrderRequest method, and return custom data must be updated manually using the ProcessResponse method.
  • The connector class handles the actual communication using the ErpServiceCaller helper class
  • The DiscountProvider class is used for displaying discounts correctly in cart/order. It retrieved discounts cached by the OrderHandler and adds them to the order or cart.

Logging is used throughout the Live Integration and is implemented in the Logger file. The log can be accessed from the Live Integration Framework node in the Dynamicweb backend.

The ConfigurationFileReader is used for loading/saving settings in the LiveIntegrationAddin class – it should be extended if you add settings to your add-in.

The tag which checks if a live integration connection is available is implemented in the PageOnGlobalTagsObserver class.

The Dynamicweb Connector receives and transmits data to and from the remote system and Dynamicweb.

The DynamicwebConnector add-in interface is fairly simple; it has one method, which takes a string as an argument, and returns a string.

When you create your own version of the add-in, you must

  • Handle the incoming xml sent by Dynamicweb
  • Return xml in the format which Dynamicweb expects

A template for creating custom connector add-ins is included in the Dynamicweb templates for Visual Studio – and the XML data formats are described above.

Have you considered using the Import data with custom request add-in? It may be an easier solution than creating a custom DynamicwebConnector add-in.

The Integration Customer Center is a module which displays data retrieved directly from a remote system in real-time.

Supported document types are:

  • Open orders
  • Credit notes
  • Invoices

Using the ICC you can:

  • Retrieve lists of each supported document type
  • Render an instance of a document type based on a template
  • Retrieve a pdf of an instance of a document type

Before you can use the integration customer center, you must have a live integration setup up and running, as the ICC depends on retrieving and displaying data in real-time – and then:

  • Add the module to a paragraph on your solution
  • Make sure your remote system returns XML in the format expected by Dynamicweb

Add to module to a paragraph on your solution to access the module settings, which allow you to control the module behavior.

Figure 12.1 The Integration Customer Center module settings

With the navigation template, you can render a navigation for moving between your ICC pages – Orders, Invoices, etc. The template must contain the <!--@Ecom:IntegrationCustomerCenter.PageContent--> tag.

The pages list controls which types of documents you want to render. Each page settings view (Figure 12.2) allows you to:

  • Name the page and select a document type to render
  • Provide a menu name for the navigation and a tag name
  • Select a list template and an item template to render the list and list entries with
Figure 12.2 ICC Page settings

Finally, you can set up paging using the paging controls.

A requests from the ICC to retrieve an order list looks like this:

<GetList type="Invoice" customerID="1234" requestAmount="10" firstItem="10"></GetList>

Where type equals the document type selected in the page settings.

An example of the XML we expect to get back:

<?xml version="1.0" encoding="utf-8"?> <Items type="Invoice" totalCount="2"> <item> <column columnName="id"><![CDATA[SF11598]]></column> <column columnName="orderDate"><![CDATA[08/26/16]]></column> <column columnName="shipDate"><![CDATA[08/26/16]]></column> <column columnName="dueDate"><![CDATA[09/05/16]]></column> <column columnName="totalAmount"><![CDATA[7.123,55]]></column> <column columnName="totalAmountIncVat"><![CDATA[8.904,44]]></column> </item> <item> <column columnName="id"><![CDATA[SF11599]]></column> <column columnName="orderDate"><![CDATA[08/26/16]]></column> <column columnName="shipDate"><![CDATA[08/26/16]]></column> <column columnName="dueDate"><![CDATA[09/05/16]]></column> <column columnName="totalAmount"><![CDATA[755,00]]></column> <column columnName="totalAmountIncVat"><![CDATA[800,00]]></column> </item> </Items>

When an order in an order list is opened, another request is sent:

<GetItem type="Invoice" customerID="2960" documentNO="SF11598"></GetItem>

The response we get back from the GetItem request looks like this:

<?xml version="1.0" encoding="utf-8"?> <Items type="Invoice"> <table tableName="EcomOrders"> <item table="EcomOrders"> <column columnName="ID"><![CDATA[SF11598]]></column> <column columnName="CurrencyCode"><![CDATA[]]></column> <column columnName="Date"><![CDATA[08/26/16]]></column> <column columnName="PaymentMethodName"><![CDATA[]]></column> <column columnName="ShippingMethodName"><![CDATA[]]></column> <column columnName="CustomerName"><![CDATA[TEST TEST WEBSHOP TEST TEST ]]></column> <column columnName="CustomerAddress"><![CDATA[TESTVEJ 8]]></column> <column columnName="CustomerAddress2"><![CDATA[]]></column> <column columnName="CustomerCity"><![CDATA[Randers C]]></column> <column columnName="CustomerCountryCode"><![CDATA[DK]]></column> <column columnName="CustomerEmail"><![CDATA[]]></column> <column columnName="CustomerFax"><![CDATA[]]></column> <column columnName="CustomerPhone"><![CDATA[]]></column> <column columnName="CustomerZip"><![CDATA[8900]]></column> <column columnName="DeliveryName"><![CDATA[TEST TEST WEBSHOP TEST TEST ]]></column> <column columnName="DeliveryAddress"><![CDATA[TESTVEJ 8]]></column> <column columnName="DeliveryAddress2"><![CDATA[]]></column> <column columnName="DeliveryCity"><![CDATA[Randers C]]></column> <column columnName="DeliveryCountryCode"><![CDATA[DK]]></column> <column columnName="DeliveryEmail"><![CDATA[]]></column> <column columnName="DeliveryFax"><![CDATA[]]></column> <column columnName="DeliveryPhone"><![CDATA[]]></column> <column columnName="DeliveryZip"><![CDATA[8900]]></column> <column columnName="PriceWithVAT"><![CDATA[0,00]]></column> <column columnName="PriceWithoutVAT"><![CDATA[0,00]]></column> <column columnName="PriceVAT"><![CDATA[0,00]]></column> <column columnName="SalesDiscount"><![CDATA[0,00]]></column> <column columnName="ShippingFee"><![CDATA[]]></column> </item> </table> <table tableName="EcomOrderLines"> <item table="EcomOrderLines"> <column columnName="ProductNumber"><![CDATA[3966]]></column> <column columnName="ProductVariantID"><![CDATA[]]></column> <column columnName="Unit"><![CDATA[Unit_KILO]]></column> <column columnName="ID"><![CDATA[10000]]></column> <column columnName="Quantity"><![CDATA[22,50]]></column> <column columnName="Type"><![CDATA[0]]></column> <column columnName="PriceWithoutVAT"><![CDATA[832,50]]></column> <column columnName="UnitPriceWithoutVAT"><![CDATA[37,00]]></column> <column columnName="PriceWithVAT"><![CDATA[1.040,63]]></column> <column columnName="UnitPriceWithVAT"><![CDATA[46,25]]></column> <column columnName="PriceVAT"><![CDATA[208,13]]></column> <column columnName="UnitPriceVAT"><![CDATA[9,25]]></column> <column columnName="PriceVATPercent"><![CDATA[25,00]]></column> <column columnName="UnitPriceVATPercent"><![CDATA[25,00]]></column> </item> <item table="EcomOrderLines"> <column columnName="ProductNumber"><![CDATA[4548]]></column> <column columnName="ProductVariantID"><![CDATA[]]></column> <column columnName="Unit"><![CDATA[Unit_KILO]]></column> <column columnName="ID"><![CDATA[20000]]></column> <column columnName="Quantity"><![CDATA[50,25]]></column> <column columnName="Type"><![CDATA[0]]></column> <column columnName="PriceWithoutVAT"><![CDATA[6.180,75]]></column> <column columnName="UnitPriceWithoutVAT"><![CDATA[123,00]]></column> <column columnName="PriceWithVAT"><![CDATA[7.725,94]]></column> <column columnName="UnitPriceWithVAT"><![CDATA[153,75]]></column> <column columnName="PriceVAT"><![CDATA[1.545,19]]></column> <column columnName="UnitPriceVAT"><![CDATA[30,75]]></column> <column columnName="PriceVATPercent"><![CDATA[25,00]]></column> <column columnName="UnitPriceVATPercent"><![CDATA[25,00]]></column> </item> <item table="EcomOrderLines"> <column columnName="ProductNumber"><![CDATA[2424]]></column> <column columnName="ProductVariantID"><![CDATA[]]></column> <column columnName="Unit"><![CDATA[Unit_KARTON]]></column> <column columnName="ID"><![CDATA[30000]]></column> <column columnName="Quantity"><![CDATA[1,00]]></column> <column columnName="Type"><![CDATA[0]]></column> <column columnName="PriceWithoutVAT"><![CDATA[129,77]]></column> <column columnName="UnitPriceWithoutVAT"><![CDATA[129,77]]></column> <column columnName="PriceWithVAT"><![CDATA[162,21]]></column> <column columnName="UnitPriceWithVAT"><![CDATA[162,21]]></column> <column columnName="PriceVAT"><![CDATA[32,44]]></column> <column columnName="UnitPriceVAT"><![CDATA[32,44]]></column> <column columnName="PriceVATPercent"><![CDATA[25,00]]></column> <column columnName="UnitPriceVATPercent"><![CDATA[25,00]]></column> </item> <item table="EcomOrderLines"> <column columnName="ProductNumber"><![CDATA[2424]]></column> <column columnName="ProductVariantID"><![CDATA[]]></column> <column columnName="Unit"><![CDATA[Unit_KARTON]]></column> <column columnName="ParentLineID"><![CDATA[30000]]></column> <column columnName="Type"><![CDATA[3]]></column> <column columnName="Quantity"><![CDATA[1]]></column> <column columnName="PriceWithoutVAT"><![CDATA[19,47]]></column> <column columnName="PriceWithVAT"><![CDATA[24,34]]></column> <column columnName="DiscountPercentage"><![CDATA[15,00]]></column> <column columnName="PriceVAT"><![CDATA[4,87]]></column> <column columnName="PriceVATPercent"><![CDATA[25,00]]></column> <column columnName="UnitPriceVATPercent"><![CDATA[25,00]]></column> </item> </table> </Items>

When checking the xml that is being generated by the remote system, there are a few details you should be aware of:

  • The EcomOrders and EcomOrderlines values in the "table" properties are expected by the Live Integration Project.
  • If you add columns to either orders or order lines, they will automatically be available via tags in the templates for rendering the items and lists
  • The communication between Dynamicweb and the remote system, and the conversion between the XML generated by the remote system and Dynamicweb tags, is handled by the Live Integration Project, in the internal class IntegrationCustomerCenterHandler - and can be customized there