Ektron 9.00
Ektron's eCommerce Module lets you set up an eCommerce website, which delivers a full set of functionality for building an online marketplace.
As a developer, you can use Ektron’s standard functionality and out-of-the-box server controla server control uses API language to interact with the CMS and Framework UI to display the output. A server control can be dragged and dropped onto a Web form and then modified.s to quickly create an eCommerce website. See Using the eCommerce Server Controls for more information about the eCommerce server controls.
Ektron also lets you customize your website. For example, you can customize your site’s order process workflow.
IMPORTANT: If you customize the workflow then upgrade to a new version of Ektron, you need to point to the new references then compile the workflow.
Ektron uses Microsoft’s Windows Workflow Foundation to create and implement order process workflows in Ektron. Within Ektron eCommerce functionality, you use a workflow to handle the ordering process after your site has received an order. The workflow can be as simple or complex as your business requires.
For example, you could have a simple workflow that sends an email to the customer when their order is received, and sends another to the person shipping product for you. Or, you could have a more complex workflow that:
Ektron’s default sample workflow is shown below.
Windows Workflow Foundation (Released with Microsoft’s .NET 3.0) is a development tool that lets you create activities-based workflow that can persist over a given length of time or be paused and restarted depending on events. For more information on WF, visit Windows Workflow Foundation.
Workflows are comprised of activities; each activity represents a portion of your Business Process. When activities have finished within a workflow, it terminates. There are 2 types of activities:
For example, an OrderFraudEventActivity in a workflow would keep the workflow from going through the Fraud Event portion of the workflow unless the order is marked as fraud.
Ektron supports Sequential Workflows and State Machine Workflows. Sequential Workflows are structured; a step-based process where one activity leads to the next. State Machine Workflows typically move from one activity to another when their state changes.
You can have multiple workflow projects associated with your eCommerce site and change workflow projects at any time. However, only 1 workflow project can run at a time. When an order process starts with a specified workflow, it continues through that Workflow.
Prerequisite
Ektron SDK is installed See Also: Using Ektron’s Developer SDK
Ektron provides a C# workflow template sample for a sequential workflow. You can modify this sample or use it to create new workflows. It is based on the Ektron default workflow.
To install Ektron’s sample workflow template, copy and paste the Ektron Ordering Sequential Flow.zip
file using the From & To information below.
From:
C:\Program Files\Ektron\CMS400SDK\Commerce\Workflow\Templates\VS2010
To:
C:\Documents and Settings\~user name~\My Documents\ Visual Studio 2010\Templates\ProjectTemplates\Visual C#\Workflow
NOTE: Make sure you replace ~user name~ in the path with the user name under which Visual Studio project templates are saved.
Extract the .zip file in the To location. Then, you can work with a sample workflow. See Also: Working with the Sample Workflow
Prerequisite
You installed Ektron’s sample workflow template. See Installing Ektron’s Sample Workflow Template.
bin
folder, remove Ektron.Cms.Common.dll
and Ektron.Workflow.dll
. bin
folder of your Ektron site to the bin
folder of the newly-created workflow project. bin
folder.C:\Documents and Settings\~user name~\My Documents\Visual Studio 2010\Projects\MyWorkFlow\MyWorkFlow\bin\Debug
.
webroot\siteroot\bin\Ektron.Workflow.dll
file. This file provides access to Ektron’s workflow activities.NOTE: Alternatively, you could use the following location: C:\Program Files\Ektron\CMS400vxx\bin
. The file is identical in both places. Using the bin folder in your site provides better speed. However, if you use the bin folder in Program Files, you do not have to worry about deleting the .dll file if you change or delete your site.
For easier viewing, right-click the workflow activities and select Sort Items Alphabetically.
NOTE: Ektron’s workflow activities appear only when the workflow is opened in design mode.
Ektron.Workflow.Activities
.To update workflow activities, remove the existing ones in Visual Studio, then add new ones.
Because Visual Studio is a visual environment, lines that connect events and activities change as you add or remove them. To add an activity to a workflow:
Send an email notification, based on an order ID in Ektron, after an event takes place in the workflow. For example, after an order is shipped, you might choose to send an email that notifies customers when their orders are shipped. Or, you might send an email to a customer when their order is received.
With this activity you can choose to send a predefined email message. These messages are defined in the Workarea under Settings > Commerce > Configuration > Messages. See Also: Configuring eCommerce.
Two event handler properties in this activity let you add custom code to SendingEmail
and SentEmail
events. The SendingEmail event fires immediately before the email is sent. The SentEmail event fires immediately after the email sent.
Key properties of this activity are:
EmailArgs
—Set this property to OrderId
to associate the activity with orders in Ektron.MessageTypes
—Set this property to the type of email message this activity sends.Associate email activity with orders in Ektron. You can either supply information such as, To:, From:, Body:, and so on. or use information associated with the order. It also lets you specify the type of message type that’s sent. (The BasicEmailActivity
, on the other hand, is a generic email activity where you specify To:, From:, Body:, and other email information.)
Activities
Email Message
OrderId
for the EmailArgs
property, the Bcc
property is dynamically populated with the order’s information.MessageType
property, the Body
property uses the corresponding content for that message type.OrderId
for the EmailArgs
property, the CC
property is dynamically populated with the order’s information.OrderId
for the EmailArgs
property, the CC
property is dynamically populated with the order’s information.MessageType
property, the Subject
property uses the subject information for that message type.OrderId
for the EmailArgs
property, the To
property is dynamically populated with the order’s email information.Email Server
ek_SMTPPort
value in the site’s web.config
file.ek_SMTPServer
in the site’s web.config
file.Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId
from the list.Body
property.Body
property.For information on defining the messages, see Configuring eCommerce.
Send an email after an event takes place in the workflow. These emails are usually generic and contain the same information for To:, From:, Subject, Body, and so on.
There are 2 event handler properties in this activity that let you add custom code to the SendingEmail
and SentEmail
events. The SendingEmail event fires right before the email is sent. The SentEmail event fires right after the email sent. For example, you want to notify a supervisor when orders are received, but you don’t want the notification to contain any order specific information.
Key properties of this activity are:
To
—enter the address to receive the email.From
—enter the address from which the email is being sent.Subject
—enter a brief summary for the email.Body
—enter the main subject text of the email.BasicEmailActivity sends a generic email. In the activity, you specify To:, From:, Body:, and other email information. (The AdvancedEmailActivity
, on the other hand, lets you send an email associated with orders in Ektron.) You can either supply information such as, To:, From:, Body:, and so on, or use information associated with the order. It also lets you specify the type of message type that’s sent.
Activities
Email Message
Email Server
ek_SMTPPort
value in the site’s web.config
file.ek_SMTPServer
in the site’s web.config
file.Handlers
Initiate the capture of an order in Ektron. When the Workflow reaches this activity, the process of submitting encrypted order information (including the transaction ID) to a payment gateway account happens. At this time, the account is changed for the order amount.
Use this activity when you want the capture to take place automatically in the Workflow. This activity is the same as an Ektron user going to the Workarea and marking an order as Captured. For information on how a Capture works in the Workarea, see Capturing Orders.
Activity
Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.Verify the quantity of items ordered is available for shipping. When the workflow reaches this activity, the quantity of each product in the order is checked. If the required quantity exists for each item, the activity returns True. Otherwise, the activity returns False.
Activity
Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.Block the workflow from going down a cancel path until the order actually has been canceled.
This activity does not cancel the order. It waits for a user, API code or UpdateOrderActivity
to mark the order canceled in Ektron before it continues down the order cancel path. Think of it as gate that’s waiting for a certain event to happen before it can continue. When the event happens, the path is opened in the workflow. For information on how to cancel an order in the Workarea, see Canceling an Order.
Activity
Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.Block the workflow from continuing down a path until the order actually has been captured.
This activity does not capture the order. It waits for a user or API code to mark the order as captured in Ektron before it continues down the path. Think of it as gate that’s waiting for a certain event to happen before it can continue. When the event happens, the path is opened in the workflow. For information on how an order is captured in the Workarea, see Capturing Orders.
Activity
Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.Block the workflow from going down a path that handles fraudulent orders until the order actually has been marked as fraud.
This activity does not mark the order as fraudulent. It waits for a user, API code or the UpdateOrderActivity
to mark the order as fraud in Ektron before it continues down the path. Think of it as gate that’s waiting for a certain event to happen before it can continue. When the event happens, the path is opened in the workflow. For information on how to mark an order as fraud in the Workarea, see Marking the Order as Fraudulent.
Activity
Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.Block the workflow from going down an order process path until the order actually has been processed. This is a generic event activity that lets you specify custom code to somehow process the order.
This activity does not start the process. It waits for a user, API code or the UpdateOrderActivity
to kick off the Process Order action in Ektron before it continues down the path. Think of it as gate that’s waiting for a certain event to happen before it can continue. When the event happens, the path is opened in the workflow. For information on how to cancel an order in the Workarea, see Canceling an Order.
Activity
Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.Usually the first item in an order workflow, this activity does not mark the order as received, but instead waits for Ektron to register a new order. When the order is received, this activity allows the workflow to start. Think of it as gate that’s waiting for a certain event to happen before it can continue. When the event happens, the path is opened in the workflow.
Activity
Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.Block the workflow from going down a shipped order path until the order actually has been shipped.
This activity does not mark the order as shipped. It waits for a user, API code or UpdateOrderActivity
to mark the order as shipped in Ektron before it continues down the path. Think of it as gate that’s waiting for a certain event to happen before it can continue. When the event happens, the path is opened in the workflow. For information on how to mark an order as shipped in the Workarea, see Marking the Order as Shipped.
Activity
Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId. from the list.Block the workflow from going down an updating order path until the order actually has been updated.
This activity does not update the order as shipped. It waits for a user, API code or UpdateOrderActivity
to update an order inEktron before it continues down the path. Think of it as gate that’s waiting for a certain event to happen before it can continue. When the event happens, the path is opened in the workflow.
Activity
Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId. from the list.Automate updating of an order’s status. This activity is the same as an Ektron user going to the Workarea and performing any of the above actions on an order. For information on how to manually complete these tasks in the Workarea, see Processing Orders.
Activity
Handlers
Misc
OrderId
to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId. from the list.A Payment Gateway Provider is a pluggable component that is integrated into Ektron's eCommerce module. A Payment provider handles eCommerce customer payments by utilizing third party payment gateways. Ektron’s eCommerce module accepts payments such as credit cards. Then, it passes that information to a third party service. The third party service processes the payment, and returns a transaction ID that’s stored with the customer's order.
NOTE: Your company needs to set up an account with a third party payment service before utilizing the payment provider. This includes payment providers such as Authorize.NET and PayFlow, which are included with Ektron’s eCommerce Module.
Ektron comes with several payment providers, including Authorize.NET and PayFlow. You can customize these providers or create your own using the extendable Payment Gateway Provider architecture.
Each type of payment gateway provider accepts configuration parameters. For example, Authorize.NET requires a username and password while PayFlow requires a username, password, vendor, and partner.
In addition, some payment gateways may support recurring payments, while others may not. Recurring payments provide the ability to create a payment that recurs at a given interval for a specified period of time. For example, you could create a payment for $9.99 that occurs on the first of every month for the next 12 months. This is something to consider if your site relies on a subscription service. Contact your provider to find out if they support recurring payments.
The following scenario shows the flow of payment information for a customer purchasing a product from your site through you receiving the money in your account.
This section explains how to extend Ektron’s Payment Gateway Provider Architecture to build your own customized Payment Gateway Provider.
See Also: Configuring Payment Options
The following figure shows the Object Model for Ektron’s Payment Gateway Provider.
The PaymentGatewayProvider is the abstract base class you must extend to implement your own payment gateway. Details and descriptions of the PaymentGatewayProvider API can be found in the Ektron API Reference Manual’s Providers API > PaymentGatewayProvider section.
In addition to the out-of-the-box payment gateways providers shipped with Ektron, you can create your own custom payment providers that connect with any payment gateway you choose. Below are the basic code steps you need to complete when creating a custom gateway provider. Additional code examples used by Ektron to create the PayFlow and Authorize.NET providers are located in: C:\Program Files\Ektron\CMS400SDK\Commerce\Providers\Commerce.Providers\PaymentGateways
The complete C# code sample used in this example is available at the end of this section. See CustomGatewayProvider Code Example
Ektron.Cms.Commerce
Ektron.Cms.Common
Ektron.Cms.ObjectFactory
Ektron.Cms.Instrumentation
System.Configuration
using System; using System.Collection; using System.Collection.Specialized; using Ektron.Cms; using Ektron.Cms.Common; using Ektron.Cms.Commerce;
namespace Ektron.Cms.Extensibility.Commerce.Samples
public class CustomPaymentProvider:
Ektron.Cms.Commerce.PaymentGatewayProvider.PaymentGatewayProvider
#region constructor, member variables public CustomPaymentProvider() { } #endregion
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { if (config == null) { throw new ArgumentNullException("config"); // Assign the provider a default name if it doesn't have one if (String.IsNullOrEmpty(name)) name = "CustomPaymentProvider"; if (string.IsNullOrEmpty(config["description"])) { config.Remove("description"); config.Add("description", "CustomPaymentProvider"); } // Call the base class's Initialize method base.Initialize(name, config); // Throw an exception if unrecognized attributes remain if (config.Count > 0) { foreach (string key in config.AllKeys) { EkException.WriteToEventLog("Unrecognized Payment Gateway Provider attribute: " + key, System.Diagnostics.EventLogEntryType.Warning); } } } } public override string Authorize() { } public override string AuthorizeAndCapture() { } public override string CapturePreauthorization(string transactionId) { } public override string VoidPreAuthorization(string transactionId) { }
public override string Authorize() { if (PaymentMethod.GetType() != typeof(CreditCardPayment)) throw new Ektron.Cms.Commerce.Exceptions .AuthorizationException("Invalid Payment Type"); CreditCardPayment creditCard = (CreditCardPayment)this.PaymentMethod; if (creditCard.ExpirationDate.IsExpired()) throw new Ektron.Cms.Commerce.Exceptions.Payment.CreditCard .CardExpiredException("Card Is Expired"); IsSubmissionSuccess = true; Authorization.AuthorizedOn = DateTime.Now; Authorization.TransactionId = new Guid().ToString(); return Authorization.TransactionId; }
In this example, the card number and card holder name are not checked. In a real world scenario, there would be additional validation (for example, via checksum) and the authorization may be obtained via a Web service or HTTP post.
NOTE: If the authorization fails, you can choose to either throw an exception or manually set the failure. For example:SubmissionError =
"Not enough Funds"
;IsSubmissionSuccess =
false
;
public override string AuthorizeAndCapture() { IsSubmissionSuccess = true; Authorization.AuthorizedOn = DateTime.Now; Authorization.CapturedOn = DateTime.Now; Authorization.TransactionId = new Guid().ToString(); return Authorization.TransactionId; } public override string CapturePreauthorization(string transactionId) { IsSubmissionSuccess = true; Authorization.CapturedOn = DateTime.Now; return Authorization.TransactionId; } public override string VoidPreAuthorization(string transactionId) { IsSubmissionSuccess = true; Authorization.VoidedOn = DateTime.Now; return Authorization.TransactionId; }
web.config
file. The web.config
file provides the facility to manage payment gateway providers within Ektron. <EktronPaymentGateway defaultProvider="CustomPaymentProvider">
<providers>
tags. Note that the name defined here must match the defaultProvider
from the previous step.
<providers>
<add name="CustomPaymentProvider"
type="Ektron.Cms.Extensibility.Commerce.Samples.CustomPaymentProvider,
CustomPaymentProvider" />
</providers>
web.config
file.In the example above, you changed the web.config
file’s EktronPaymentGateway defaultProvider parameter to the name of your custom payment provider. This overrides the payment provider settings in the Workarea. To manage all payment providers from the Workarea > Settings > Commerce > Configuration > Payment Gateways screen:
web.config
file, locate the EktronPaymentGateway section and make sure the defaultProvider parameter is set to Automatic
. <EktronPaymentGateway defaultProvider="Automatic">
web.config
file.WARNING! Copying, pasting, and using the following code without modification to create a DLL will not result in a working custom payment provider. This code is provided as an outline of what is needed to create an actual custom payment provider.
using System; using System.Collections; using System.Collections.Specialized; using System.Configuration.Provider; using Ektron.Cms; using Ektron.Cms.Common; using Ektron.Cms.Commerce; namespace Ektron.Cms.Extensibility.Commerce.Samples { public class CustomPaymentProvider : Ektron.Cms.Commerce.PaymentGatewayProvider { public CustomPaymentProvider() { } public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { if (config == null throw new ArgumentNullException(; // Assign the provider a default name if it doesn't have one if (String.IsNullOrEmpty(name)) name = CustomPaymentProvider" if string.IsNullOrEmpty(config(["description"])) { config.Remove("description"); config.Add("description", "CustomPaymentProvider"; } // Call the base class's Initialize method base.Initialize(name, config); // Throw an exception if unrecognized attributes remain if (config.Count > 0) { foreach (string key in config.AllKeys) { EkException.WriteToEventLog("Unrecognized Payment Gateway Provider attribute: " + key, System.Diagnostics.EventLogEntryType.Warning); } } } public override string Authorize() { if (PaymentMethod.GetType() != typeof(CreditCardPayment)) throw new Ektron.Cms.Commerce.Exceptions. AuthorizationException("Invalid Payment Type"); CreditCardPayment creditCard = (CreditCardPayment) this.PaymentMethod; if (creditCard.ExpirationDate.IsExpired()) throw new Ektron.Cms.Commerce.Exceptions.Payment.CreditCard. CardExpiredException( "Card Is Expired"; IsSubmissionSuccess = true; Authorization.AuthorizedOn = DateTime.Now; Authorization.TransactionId = new Guid().ToString(); return Authorization.TransactionId; } public override string AuthorizeAndCapture() { IsSubmissionSuccess = true; Authorization.AuthorizedOn = DateTime.Now; Authorization.CapturedOn = DateTime.Now; Authorization.TransactionId = new Guid().ToString(); return Authorization.TransactionId; } public override string CapturePreauthorization(string transactionId) { IsSubmissionSuccess = true; Authorization.CapturedOn = DateTime.Now; return Authorization.TransactionId; } public override string VoidPreAuthorization(string transactionId) { IsSubmissionSuccess = true; Authorization.VoidedOn = DateTime.Now; return Authorization.TransactionId; } } }
A Shipment Provider is a pluggable component integrated into Ektron's eCommerce module. The Shipping provider handles eCommerce real-time shipping rate retrieval by utilizing third party shipping services such as FedEx or UPS, or you can create your own fixed rate logic. Ektron's eCommerce shipping module calculates the package(s) needed for an order and then passes the following information to the company providing the shipping services via a Shipping Provider.
Ektron comes with several shipping providers, including FedEx and UPS. You can customize these providers or create your own using the extendable Shipping Provider architecture.
Your company will need to set up or have an existing account with a third party shipping service before utilizing the shipping provider. This includes shipping providers such as, FedEx or UPS, which are included with Ektron's eCommerce Module.
Each type of shipping provider accepts configuration parameters. For example, FedEx requires a username, password, account number, and meter number while UPS requires a Username, password, and account number. These configuration parameters along with the provider definitions are stored in the SiteRoot/shipment.config
file. Below is a provider definition example for FedEx.
<add name="FedExShipmentProvider" type="Ektron.Cms.Commerce.Providers.Shipment.FedExShipmentProvider, Ektron.Cms.Commerce.Providers" serviceUrl="https://gatewaybeta.fedex.com/web-services" key="" password="" accountNumber="" meterNumber="" transactionId="Ektron FedEx v3"
The following steps show the flow of shipping calculations for a customer purchasing a product from your site.
NOTE: Many Ektron settings affect your shipping calculations. A default warehouse must be defined with a valid shipping address. Shipping methods you would like to offer customers must be defined. For example, the FedEx provider supports over 10 possible shipping methods, but you may only be concerned with Priority, 2 Day, and Ground. This must be defined in Ektron. You can also define the shipping packages your business uses. The Shipping calculator will try and fit the order items into any packages defined in Ektron. If no packages are defined, the item's dimensions are passed to the provider instead.
The following figure shows the Object Model for Ektron’s Shipment Provider.
The ShipmentProvider is the abstract base class you must extend to implement your own Shipping Provider. Details and descriptions of the ShipmentProvider API can be found in the Ektron API Reference Manual’s Providers API > Shipment > Provider > ShipmentProvider.
In addition to the out-of-the-box shipment providers that come with Ektron, you can create a custom provider that connects with any shipping company you choose. Below are the basic code steps you need to complete when creating a custom shipment provider. Additional code examples used by Ektron to create FedEx, Flat Rate and UPS shipment providers are located in:
C:\Program Files\Ektron\CMS400SDK\Commerce\Providers\Commerce.Providers\Shipping
The complete C# code sample used in this example is available at the end of this section. See CustomShipmentProvider Code Example.
Ektron.Cms.Commerce Ektron.Cms.Common Ektron.Cms.ObjectFactory Ektron.Cms.Instrumentation System.Configuration
using System.Configuration. using Ektron.Cms.Commerce.Shipment.Provider; using Ektron.Cms.Commerce using Ektron.Cms.Instrumentation
namespace Ektron.Cms.Extensibility.Commerce.Samples
{
public class CustomShipmentProvider : ShipmentPovider {
shippingOptionList
are the shipping methods exposed as a service type in the management screen inside the Workarea.
#region constructor, member variables public CustomShipmentProvider() { IsTrackingSupported = false; _shippingOptionList = new List<string>(); _shippingOptionList.Add("CustomOption_1"); _shippingOptionList.Add("CustomOption_2"); } private List<string> _shippingOptionList; #endregion
#region ShipmentProvider Implementation public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { if (config == null) { throw new ArgumentNullException("config"); // Assign the provider a default name if it doesn't have one if (string.IsNullOrEmpty(name)) name = "CustomShipmentProvider"; if (string.IsNullOrEmpty(config["description"])) { config.Remove("description"); config.Add("description", "CustomShipmentProvider Provider"); } // Call the base class's Initialize method base.Initialize(name, config); // Throw an exception if unrecognized attributes remain if (config.Count == 0) throw new ProviderException("Shipment provider attribute missing."); else { //read all config attributes. ServiceUrl = config["serviceUrl"]; Key = config["key"]; Password = config["password"]; AccountNumber = config["accountNumber"]; MeterNumber = config["meterNumber"]; TransactionId = config["transactionId"]; } } public override List<string> GetServiceTypes() { return _shippingOptionList; } public override string GetTrackingUrl(string trackingId) { return ""; } public override List <ShippingOptionData>GetRates(IEnumerable <ShippingMethodData> expectedOptions, AddressData origin, AddressData destination, Weight weight, Dimensions dimensions) { } #endregion
public override List <ShippingOptionData> GetRates(IEnumerable<ShippingMethodData> expectedOptions, AddressData origin, AddressData destination, Weight weight, Dimensions dimensions) { List <ShippingOptionData> availableOptions = new List <ShippingOptionData>(); try { foreach (ShippingMethodData expectedOption in expectedOptions) { Log.WriteInfo("Custom Shipping Provider.ExpectedOption:" + expectedOption.Name); switch (expectedOption.ProviderService.ToLower()) { case "customoption_1": ShippingOptionData customOption1 = new ShippingOptionData(); customOption1.Id = expectedOption.Id; customOption1.Name = expectedOption.Name; customOption1.ShippingFee = 25.00M; customOption1.ProviderService = "CustomOption_1"; availableOptions.Add(customOption1); break; case "customoption_2": ShippingOptionData customOption2 = new ShippingOptionData(); customOption2.Id = expectedOption.Id; customOption2.Name = expectedOption.Name; customOption2.ShippingFee = 50.00M; customOption2.ProviderService = "CustomOption_2"; availableOptions.Add(customOption2); break; } } } catch (Exception e) { Log.WriteError("Custom Shipping Provider: Error retrieving shipping rates." + e.Message); throw; } return availableOptions; }
if (destination.Country.Id != 124)
throw new Ektron.Cms.Commerce.Exceptions.Shipping.InvalidAddressException
("We ship only to Canada.");
<shipmentProvider defaultProvider="CustomShipmentProvider">
<providers>
tags. Note that the name defined here needs to match the name defined as the defaultProvider in the previous step. <providers>
<add name="CustomShipmentProvider"
type="Ektron.Cms.Extensibility.Commerce.Samples.CustomShipmentProvider,
CustomShipmentProvider" serviceUrl="" key="" password=""
accountNumber="" meterNumber=""
transactionId="CustomShipmentProvider based transaction" />
</providers>
web.config
file.The options are now available in the checkout process, and are shown on the shipping method selection screen.
WARNING! Copying, pasting, and using the following code without modification to create a DLL does not result in a working “real time” shipping provider. This example uses fixed rates and should be modified to meet your needs.
The following code example is used in Creating a Custom Shipment Provider.
using System.Configuration.Provider; using Ektron.Cms.Commerce.Shipment.Provider; using Ektron.Cms.Commerce; using Ektron.Cms.Instrumentation; namespace Ektron.Cms.Extensibility.Commerce.Samples { public class CustomShipmentProvider : ShipmentProvider { #region constructor, member variables public CustomShipmentProvider() { IsTrackingSupported = false; _shippingOptionList = new List<string>(); _shippingOptionList.Add("CustomOption_1"); _shippingOptionList.Add("CustomOption_2"); } private List<string> _shippingOptionList; #endregion #region ShipmentProvider Implementation public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { if (config == null) throw new ArgumentNullException("config"); // Assign the provider a default name if it doesn't have one if (string.IsNullOrEmpty(name)) name = "CustomShipmentProvider"; if (string.IsNullOrEmpty(config["description"])) { config.Remove("description"); config.Add("description", "CustomShipmentProvider Provider"); } // Call the base class's Initialize method base.Initialize(name, config); // Throw an exception if unrecognized attributes remain if (config.Count == 0) throw new ProviderException("Shipment provider attribute missing."); else { //read all config attributes. ServiceUrl = config["serviceUrl"]; Key = config["key"]; Password = config["password"]; AccountNumber = config["accountNumber"]; MeterNumber = config["meterNumber"]; TransactionId = config["transactionId"]; } } public override List<string> GetServiceTypes() { return _shippingOptionList; } public override string GetTrackingUrl(string trackingId) { return ""; } public override List<ShippingOptionData> GetRates(IEnumerable<ShippingMethodData> expectedOptions, AddressData origin, AddressData destination, Weight weight, Dimensions dimensions) { List<ShippingOptionData> availableOptions = new List<ShippingOptionData>(); try { foreach (ShippingMethodData expectedOption in expectedOptions) { Log.WriteInfo("Custom Shipping Provider.ExpectedOption:" + expectedOption.Name); switch (expectedOption.ProviderService.ToLower()) { case "customoption_1": ShippingOptionData customOption1 = new ShippingOptionData(); customOption1.Id = expectedOption.Id; customOption1.Name = expectedOption.Name; customOption1.ShippingFee = 25.00M; customOption1.ProviderService = "CustomOption_1"; availableOptions.Add(customOption1); break; case "customoption_2": ShippingOptionData customOption2 = new ShippingOptionData(); customOption2.Id = expectedOption.Id; customOption2.Name = expectedOption.Name; customOption2.ShippingFee = 50.00M; customOption2.ProviderService = "CustomOption_2"; availableOptions.Add(customOption2); break; } } } catch (Exception e) { Log.WriteError("Custom Shipping Provider: Error retrieving shipping rates." + e.Message); throw; } return availableOptions; } #endregion } }
An Inventory Provider is a pluggable component that’s integrated into Ektron's eCommerce module. An Inventory Provider handles the retrieving and updating of inventory information for products within Ektron. Out of the Box, Ektron comes with a default Ektron Inventory Provider that tracks inventory within its database.
If your business has an Accounting or Enterprise Resource Management solution that manages inventory, you can create a custom Inventory Provider that retrieves and updates inventory information directly from that system. Inventory data can be stored in any number of locations, including databases, ERP or CRM systems, or even XML files.
The following figure shows the Object Model for Ektron’s Inventory Provider.
The InventoryProvider is the abstract base class you must extend to implement your own Inventory Provider. Details and descriptions of the InventoryProvider API can be found in the Ektron API Reference Manual’s Providers API > Inventory > Provider > InventoryProvider.
In addition to the out-of-the-box inventory provider that comes with Ektron, you can create your own custom provider that connects with an existing inventory system. Below are the basic code steps you need to complete when creating a custom inventory provider. A code example for the inventory provider used by Ektron is located in:
C:\Program Files\Ektron\CMS400SDK\Commerce\Providers\Commerce.Providers\Inventory
The complete C# code sample used in this example is available at the end of this section. See CustomInventoryProvider Code Example.
using Ektron.Cms.Commerce.Inventory.Provider; using Ektron.Cms.Commerce.Data;
using Ektron.Cms.Commerce; using Ektron.Cms.Common; using Ektron.Cms.Extensibility; using Ektron.Cms.Extensibility.Commerce;
Ektron.Cms.Extensibility.Commerce.Samples {
CustomInventoryProvider InventoryPovider
Public CustomInventoryProvider() { } private CmsInventory _inventory; protected CmsInventory Inventory { get { if (_inventory == null) { _inventory = CmsInventory(RequestInformation); } return_inventory; } }
In this example, we trigger Ektron extensibility events, such as OnBeforeInventorySave()
and OnAfterInventorySave()
. In a real world scenario, your existing inventory system may have its own inventory events that are used.
public override InventoryData GetInventory (long entryId) { return Inventory.GetInventory(entryId); } public override void SaveInventory(InventoryData inventory) { OnBeforeInventorySave(inventory); Inventory.SaveInventory(inventory); OnAfterInventorySave(inventory); if (inventory.UnitsInStock < inventory.ReorderLevel) { OnInventoryReorderLevelReached(inventory); } }
web.config
file. The web.config
file provides the facility to manage inventory providers within Ektron. Locate the InventoryProvider section and change the defaultProvider parameter to the name of your custom provider. If you start your search from the top of the file, it will be the second instance. <inventoryProvider defaultProvider="CustomInventoryProvider">
<providers>
tags. Note that the name defined here needs to match the name defined as the defaultProvider in the previous step. <providers>
<add name="CustomInventoryProvider"
type="Ektron.Cms.Extensibility.Commerce.Samples.CustomInventoryProvider,
CustomInventoryProvider"/>
</providers>
web.config
file.Your custom inventory provider is now the default provider. Whenever the inventory is queried, the call routes through the new custom provider.
WARNING! Copying, pasting, and using the following code without modification to create a DLL does not result in a working “real time” shipping provider. This example uses fixed rates and should be modified to meet your needs.
The following code example is used in Creating a Custom Inventory Provider.
using Ektron.Cms.Commerce.Inventory.Provider; using Ektron.Cms.Commerce.Data; using Ektron.Cms.Commerce; using Ektron.Cms.Common; using Ektron.Cms.Extensibility; using Ektron.Cms.Extensibility.Commerce; namespace Ektron.Cms.Extensibility.Commerce.Samples { public class CustomInventoryProvider : InventoryProvider { public CustomInventoryProvider() { }private CmsInventory _inventory; protected CmsInventory Inventory { get { if (_inventory == null) { _inventory = new CmsInventory(RequestInformation); } return _inventory; } } public override InventoryData GetInventory(long entryId) { return Inventory.GetInventory(entryId); } public override void SaveInventory(InventoryData inventory) { OnBeforeInventorySave(inventory); Inventory.SaveInventory(inventory); OnAfterInventorySave(inventory); if (inventory.UnitsInStock < inventory.ReorderLevel) { OnInventoryReorderLevelReached(inventory); } } } }
The shipping system provides an easy and extensible way to override the shipping calculations inside Ektron. This example sets up a custom shipping calculator to get shipping rates with a different source location for a class of products. It has 2 major parts.
Commerce.CustomShippingCalculator
. You need to import the necessary Ektron namespaces. To do this, add references to: Ektron.Cms.Api
Ektron.Cms.Commerce
Ektron.Cms.Common
Ektron.Cms.Instrumentation
Ektron.Cms.ObjectFactory
Microsoft.Practices.EnterpriseLibrary.Validation
System.Configuration
using System.Collections.Generic; using Microsoft.Practices.EnterpriseLibrary.Validation; using Ektron.Cms; using Ektron.Cms.Common; using Ektron.Cms.Commerce; using Ektron.Cms.Commerce.Shipment.Provider; using Ektron.Cms.Extensibility; using Ektron.Cms.Extensibility.Commerce; using Ektron.Cms.Instrumentation; using Ektron.Cms.Commerce.Exceptions; using Ektron.Cms.Commerce.Exceptions.Shipping;
Ektron.Cms.Extensibility.Commerce.Samples
. Then, rename your class to CustomShippingCalculator
and inherit from the ShippingCalculatorStrategy
class. See sample code below.
namespace Ektron.Cms.Extensibility.Commerce.Samples
{ public class CustomShippingCalculator : ShippingCalculatorStrategy
{
GetFlatPackageList
"flattens" the items in the basket, returning 1 for each item quantity. So, if we have 4 widgets, we'll have 4 packages, each with one widgetWidgets are mini-applications that you place on a Web page using PageBuilder; a widget provides either specific functionality (calculators, search, social bars, etc.) or areas into which you can add content (content blocks, list summaries, collections, and so on).. namespace Ektron.Cms.Extensibility.Commerce.Samples { public class CustomShippingCalculator : ShippingCalculatorStrategy { private EkRequestInformation _requestInfo; private IShippingMethod _shippingService; protected EkRequestInformation RequestInformation { get { if (_requestInfo == null) { _requestInfo = ObjectFactory.GetRequestInfoProvider().GetRequestInformation(); } return _requestInfo; } } } protected IShippingMethod ShippingService { get { if (_shippingService == null) { _shippingService = ObjectFactory.GetShippingMethod(); } return _shippingService; } } protected List<ShippingMethodData> GetActiveShippingMethodList() { Criteria<ShippingMethodProperty> criteria = new Criteria<ShippingMethodProperty> (ShippingMethodProperty.DisplayOrder, EkEnumeration.OrderByDirection.Ascending); criteria.AddFilter(ShippingMethodProperty.IsActive, CriteriaFilterOperator.EqualTo, true); return ShippingService.GetList(criteria); } protected List<ShippingPackageData> GetFlatPackageList(List<AdjustedBasketItem> basketItems) { List<ShippingPackageData> flatList = new List<ShippingPackageData>(); foreach (AdjustedBasketItem item in basketItems) { if (item.IsTangible) { for (int x = 1; x <= item.Quantity; x++) { ShippingPackageData package = new ShippingPackageData(item.Dimensions, item.Weight); package.Items.Add(item); flatList.Add(package); } } } return flatList; } private AddressData GetWarehouseAddressData() { AddressData address = new AddressData(); address.Name = "Ektron"; address.AddressLine1 = "542 Amherst St."; address.City = "Nashua"; address.Country = new CountryData(); address.Country.Id = 840; // USA address.Region = new RegionData(); address.Region.Id = 31; // NH address.PostalCode = "03063"; return address; } }
OnBeforeCalculate
method, which is called when shipping is calculated for the basket.
public override void OnBeforeCalculate(BasketCalculatorData basketData, ShippingMethodData shippingMethod, CancellableEventArgs eventArgs) { }
package.Items[0].TaxClassId == 4
).
public override void OnBeforeCalculate(BasketCalculatorData basketData, ShippingMethodData shippingMethod, CancellableEventArgs eventArgs)) { bool hasTangibles = basketData.Basket.HasTangibleItems; List<ShippingMethodData> availableShippingMethods = GetActiveShippingMethodList(); List<ShippingPackageData> shippingPackages = GetOrderPackages(basketData.AdjustedItems); //convert list of items into flat list - all 1 quantity. List<ShippingPackageData> shippingPackages = FlattenItemsIntoPackages(basketData.AdjustedItems); if (shippingPackages.Count == 0) { Log.WriteInfo("No packages to calculate shipping for."); return; } Dictionary<string, int> rateCount = new Dictionary<string, int>(); //Get Rate for each package foreach (ShippingPackageData package in shippingPackages) { List<ShippingOptionData> shippingRateList; Log.WriteVerbose(string.Format("Shipping package: {0} x {1} x {2} {3}, {4} {5}", package.Dimensions.Height, package.Dimensions.Length, package.Dimensions.Width, package.Dimensions.Units.ToString(), package.TotalItemWeight.Amount, package.TotalItemWeight.Units.ToString())); //Get rate for each available option and populate the // BasketCalculatorData.ShippingRates list. if (package.Items[0].TaxClassId == 4) shippingRateList = ShipmentProviderManager.Provider.GetRates(availableShippingMethods, GetWarehouseAddressData(), basketData.ShipTo, package.TotalItemWeight, package.Dimensions); else shippingRateList = ShipmentProviderManager.Provider.GetRates (availableShippingMethods, basketData.ShipFrom, basketData.ShipTo, package.TotalItemWeight, package.Dimensions); if (shippingRateList.Count == 0) { EkException.ThrowException(new NoRatesAvailableException ("No shipping rates are available for the supplied item.")); } //add rates to basket.ShippingRates foreach (ShippingOptionData rate in shippingRateList) { //if there are multiple packages, we need to total up rates for each //shipping method. //NOTE: we can only return rates that are applicable to ALL packages. //For example, package 1 has rates for ground, priority, freight // package 2 has rates for ground, priority //only return rates for ground and priority since freight will only be //a partial quote. //check if this shipping method has already been added. ShippingOptionData existingMethodRate = basketData.ShippingRates.Find(delegate(ShippingOptionData match) { return match.ProviderService == rate.ProviderService; }); if (existingMethodRate == null) { basketData.ShippingRates.Add(rate); } else { existingMethodRate.ShippingFee += rate.ShippingFee; } //need to keep count of how many times this shipping method rate is added //to make sure we have a method rate for each package. if(rateCount.ContainsKey(rate.ProviderService)) { rateCount[rate.ProviderService]++; } else { rateCount.Add(rate.ProviderService, 1); } } } //default exchange rate ExchangeRateData exchangeRate = new ExchangeRateData(0, 0, 1m, null); if (basketData.ShippingRates.Count > 0 && basketData.ShippingRates[0].CurrencyId != RequestInformation.CommerceSettings.CurrencyId) { IExchangeRate rateService = ObjectFactory.GetExchangeRate(); exchangeRate = rateService.GetCurrentExchangeRate(); if (exchangeRate == null) { Log.WriteError(string.Format("No exchange rate found for current currency {0}. Shipping rates could not be converted to current currency.", RequestInformation.CommerceSettings.CurrencyId)); EkException.ThrowException(new CmsException(string.Format ("No exchange rate found for current currency {0}. Shipping rates could not be converted to current currency.", RequestInformation.CommerceSettings.CurrencyId))); } } //go through all shipping methods and remove any that don't have rates //for packages. for(int x = 0; x < basketData.ShippingRates.Count; x++) { bool removed = false; if(rateCount[basketData.ShippingRates[x].ProviderService] < shippingPackages.Count) { basketData.ShippingRates.RemoveAt(x); x--; //need to decrement loop counter so we don't skip any as we loop through removed = true; } if (!removed) { if (basketData.ShippingRates[x].CurrencyId != RequestInformation.CommerceSettings.CurrencyId) { //if shipping rate currency is not current currency, convert it basketData.ShippingRates[x].ShippingFee = exchangeRate.ConvertPrice(basketData.ShippingRates[x].ShippingFee); } //is this the selected shippingmethod for basket? If so, set shipping cost //on basket if (shippingMethod != null && shippingMethod.ProviderService == basketData.ShippingRates[x].ProviderService) { basketData.TotalShippingCost += basketData.ShippingRates[x].ShippingFee; } } } if (basketData.ShippingRates.Count == 0 && hasTangibles) { basketData.ValidationResults.AddResult(new ValidationResult ("No shipping rates are available for the supplied item.", this, "", "", null)); } eventArgs.IsCancelled = true; }
IsCancelled
property to true on eventArgs
instructs Ektron to stop further execution of the shipping calculations. This, in effect, overrides the shipping functionality.After you create the event assembly and place it inside the bin directory, you need to register the event. Use the objectfactory.config
file to register custom events within Ektron. To do that:
siteroot/objectfactory.config
file.objectStrategies
section of the file.name="ShippingCalculator"
. <strategies>
key, as shown below.
<objectStrategies>
<add name=ShippingCalculator">
<strategies>
<add name="CustomShippingCalculator"
type="Ektron.Cms.Extensibility.Commerce.Samples.CustomShippingCalculator,
Commerce.CustomShippingCalculator"/>
</strategies>
</add>
</objectStrategies>