Customizing eCommerce

Ektron's eCommerce Module lets you set up an eCommerce Web site, 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 controls to quickly create an eCommerce Web site. Ektron also provides the ability to partially or fully customize your Web site. For example, you can customize your site’s order process workflow.

Customizing workflows

Customizing Workflows

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 utilizes 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:

  • sends an email to the customer when the order is received
  • checks to see whether the item is a tangible product or a virtual product. Then, continues down the workflow path that’s appropriate for the item.
  • handles shipping notifications and updates the account throughout the shipping process
  • handles the order if it is canceled
  • handles the order if it is determined to be fraudulent
  • marks the order as complete once all activities are finished

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 http://msdn.microsoft.com/en-us/netframework/aa663328.aspx.

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:

  • Activities—executed inside the workflow once the activity is reached. For example, an email might be sent to a customer once their order is received by using the AdvancedEmailActivity placed after the OrderReceivedEventActivity. This email is automatically sent with no human interaction needed, and the workflow would continue.
  • Event Activities—pauses the workflow until the event occurs. When the event takes place, the associated activity is executed. Think of events as road blocks in the workflow that are not opened unless a matching action happens. When the event happens, the workflow follows the path associated with that event.

    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.

    —Workflow activities—

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 workflows projects associated with your eCommerce site and change workflow projects at anytime. Note, however, that only one workflow project can run at one time. When an order process starts with a specified workflow, it continues through that Workflow.

Installing Ektron's sample workflow template

Installing Ektron’s Sample Workflow Template

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.

NOTE: If you are using Visual Studio 2010, replace 2008 with 2010 in paths below.

From:

C:\Program Files\Ektron\CMS400SDK\Commerce\Workflow\Templates\VS2008

To:

C:\Documents and Settings\~user name~\My Documents\Visual Studio 2008\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.

In Windows 7, the path looks like this:

C:\Users\~user name~\Documents\Visual Studio 2008\Templates\ProjectTemplates\Visual C#\Workflow

Extract the .zip file in the To location. Then, you can work with a sample workflow. See Also: Working with the Sample Workflow

Working with the sample workflow

Working with the Sample Workflow

Prerequisite: You installed Ektron’s sample workflow template. See Installing Ektron’s Sample Workflow Template.

  1. Open Visual Studio.
  2. Choose File > New > Project.
  3. If you are using C#, expand the Visual C# project type. Otherwise, expand the Visual Basic project type.
  4. Click Workflow.
  5. From the My Templates area, select Ektron Ordering Sequential Flow.
  6. Select a new Name. If necessary, change the Location, Solution and Solution Name.
  7. Click OK.
  8. If a prior version of the software developer's kit (SDK) is installed, the assemblies in the sample may be older than those in your site. To avoid an issue:
    1. From your project's bin folder, remove Ektron.Cms.Common.dll and Ektron.Workflow.dll.
    2. Copy those files from the bin folder of your Ektron site to the bin folder of the newly-created workflow project.
    3. Re-establish the References to these dlls from the project's bin folder.
  9. Open the Workflow.cs file in the Solution Explorer.

  10. Make any necessary changes to the workflow. For information on Ektron’s Workflow Activities, see Customizing eCommerce. For information on Microsoft’s Workflow Activities, see http://msdn.microsoft.com/en-us/magazine/cc163504.aspx.
  11. Debug as needed.
  12. Build the workflow. This creates a DLL of the workflow and places it in the project’s bin/debug folder.
  13. Navigate to the DLL's folder. For example, C:\Documents and Settings\~user name~\My Documents\Visual Studio 2008\Projects\MyWorkFlow\MyWorkFlow\bin\Debug.
  14. Move or copy the new workflow DLL file to your Web site’s bin folder.
Adding workflow activities to your toolbox

Adding Workflow Activities to Your Toolbox

  1. Open or create a Workflow in Visual Studio. See Installing Ektron’s Sample Workflow Template
  2. Display the Toolbox (View > Toolbox).
  3. Right-click the mouse within the Toolbox and choose Add Tab.
  4. Type Ektron Workflow Activities then press <Enter>.
  5. Click the Ektron Workflow Activities tab.
  6. Right-click the mouse in the empty area and choose Choose Items. The Choose Toolbox Items dialog appears.

  7. In VS 2008, select the Activities tab. In VS 2010, select the .NET Framework Components tab.
  8. Browse to 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\CMS400v8x\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.

  9. Click OK.

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.

Removing workflow activities

Removing Workflow Activities

  1. Display the Visual Studio toolbox (View > Toolbox).
  2. Right-click the mouse within the Toolbox.
  3. Click Choose Items.
  4. Click the Activities or .NET Framework Components tab.
  5. Click Namespace or Assembly Name to sort Workflow Activities.
  6. Uncheck all boxes that begin with Ektron.Workflow.Activities.
  7. Click OK.
  8. Right-click the Ektron Workflow Activities tab.
  9. Click Delete Tab.
Updating workflow activities

Updating Workflow Activities

To update workflow activities, remove the existing ones in Visual Studio, then add new ones.

Inserting workflow activities using drag and drop

Inserting Workflow Activities Using Drag and Drop

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:

  1. Display the Visual Studio toolbox (View > Toolbox).
  2. Click the Ektron Workflow Activities tab. Ektron’s Workflow Activities appear.

  3. Drag an activity and drop it into the desired location in the Workflow.
  4. As desired, modify the activity’s properties using Visual Studio's Properties area.

For a description of activity properties, see the individual activities as listed in Customizing eCommerce.

AdvancedEmailActivity

AdvancedEmailActivity

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: eCommerce Messages.

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.)

—Properties—

Activities

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.

Email Message

  • Bcc (String)—A blind carbon copy of the email is sent to the address entered in this property. If you select OrderId for the EmailArgs property, the Bcc property is dynamically populated with the order’s information.
  • Body (String)—Enter the main content of the email. If you select a parameter from the MessageType property, the Body property uses the corresponding content for that message type.
  • CC (String)—A carbon copy of the email is sent to the email address entered in this property. If you select OrderId for the EmailArgs property, the CC property is dynamically populated with the order’s information.
  • From (String)—The address from which the email is sent. If you select OrderId for the EmailArgs property, the CC property is dynamically populated with the order’s information.
  • HtmlBody (Boolean)—Select whether to send the email in HTML or plain text. True (default), use HTML; False, use plain text.
  • ReplyTo (String)—Specify the address to which the recipient of the email replies.
  • Subject (String)—Enter subject information for the email. If you select a parameter from the MessageType property, the Subject property uses the subject information for that message type.
  • To (String)—The email is sent to the address in this property. If you select OrderId for the EmailArgs property, the To property is dynamically populated with the order’s email information.

Email Server

  • Port (Integer)—Set this value to the port your system uses access to retrieve email. In most cases, the port is set to 25. If that is not the case, see your System Administrator. The default is 25. If blank, this property uses the ek_SMTPPort value in the site’s web.config file.
  • SmtpHost (String)—The address of the server hosting the email system. If blank, this property uses the ek_SMTPServer in the site’s web.config file.

Handlers

  • SendingEmail (String)—Event handler to add custom code to the sending email event. This event fires right before the email is sent.
  • SentEmail (String)—Event handler to add custom code to the sent email event. This event fires right after the email is sent.

Misc

  • EmailArgs—Select OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.
  • MessageType—Select the type of message that is sent for this activity. If you select None, the message is sent with the text in the Body property.
    • None (default)—uses the text in the Body property.
    • OrderReceived—Sends the Order Received email defined in the Workarea > Settings > eCommerce > Configuration > Messages screen when an order is received.
    • OrderCancelled—Sends the Order Cancelled email defined in the Workarea > Settings > eCommerce > Configuration > Messages screen when an order is received.
    • OrderShipped—Sends the Order Confirmation email defined in the Workarea > Settings > eCommerce > Configuration > Messages screen when an order is received.

For information on defining the messages, see eCommerce Messages.

BasicEmailActivity

BasicEmailActivity

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.

—Properties—

Activities

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.

Email Message

  • Bcc (String)—A blind carbon copy of the email is sent to the address entered in this property.
  • Body (String)—Enter the main content of the email.
  • CC (String)—A carbon copy of the email is sent to the email address entered in this property.
  • From (String)—The address from which the email is sent.
  • HtmlBody (Boolean)—Select whether to send the email in HTML (True) or plain text (False).
  • ReplyTo (String)—Specify the address to which the recipient of the email replies.
  • Subject (String)—Enter a brief summary for the email.
  • To (String)—The email is sent to the address in this property.

Email Server

  • Port (Integer)—Set this value to the port your system access to retrieve email. In most cases, the port is set to 25. If that is not the case, see your System Administrator. The default is 25. If blank, this property uses the ek_SMTPPort value in the site’s web.config file.
  • SmtpHost (String)—The address of the server hosting the email system. If blank, this property uses the ek_SMTPServer in the site’s web.config file.

Handlers

  • SendingEmail (String)—Event handler to add custom code to the sending email event.
  • SentEmail (String)—Event handler to add custom code to the sent email event.
CaptureOrderActivity

CaptureOrderActivity

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.

—Properties—

Activity

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.

Handlers

  • MethodInvoking—Select a method to call before the order is captured.

Misc

  • orderIdSelect OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.
CheckStockActivity

CheckStockActivity

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.

—Properties—

Activity

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.

Handlers

  • MethodInvoking—Select a method to call before the stock level is checked.

Misc

  • orderId—Select OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.
  • ReturnValue (Boolean)
    • True—the quantity of each product ordered is in stock.
    • False (default)—there is not enough product in stock to fulfill the order.
OrderCancelledEventActivity

OrderCancelledEventActivity

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.

—Properties—

Activity

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.
  • Roles

Handlers

  • Invoked (String)—Specify code that executes when the order is canceled.

Misc

  • OrderIdSelect OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.
OrderCapturedEventActivity

OrderCapturedEventActivity

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.

—Properties—

Activity

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.
  • Roles

Handlers

  • Invoked (String)—Specify code that executes when the order has been captured.

Misc

  • OrderId—Select OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.
OrderFraudEventActivity

OrderFraudEventActivity

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.

—Properties—

Activity

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.
  • Roles

Handlers

  • Invoked (String)—Specify code that executes when the order has been marked as fraud.

Misc

  • OrderId—Select OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.
OrderProcessedEventActivity

OrderProcessedEventActivity

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.

—Properties—

Activity

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.
  • Roles

Handlers

  • Invoked (String)—Specify code that executes when the order has been processed.

Misc

  • OrderId—Select OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.
OrderReceivedEventActivity

OrderReceivedEventActivity

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.

—Properties—

Activity

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.
  • Roles

Handlers

  • Invoked (String)—Specify code that executes when the order has been received.

Misc

  • OrderId—Select OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId from the list.
OrderShippedEventActivity

OrderShippedEventActivity

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.

—Properties—

Activity

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.
  • Roles

Handlers

  • Invoked (String)—Specify code that executes when the order is shipped.

Misc

  • OrderId—Select OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId. from the list.
OrderUpdatedEventActivity

OrderUpdatedEventActivity

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.

—Properties—

Activity

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.
  • Roles

Handlers

  • Invoked (String)—Specify code that executes when the order has been updated.

Misc

  • OrderId—Select OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId. from the list.
UpdateOrderActivity

UpdateOrderActivity

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 Orders.

—Properties—

Activity

  • Name (String)—Enter a name for the Activity.
  • Description (String)—Enter a description for the Activity.
  • Enabled (Boolean)—True (default), enable the activity; False, disable.

Handlers

  • MethodInvoking—Select a method to call before the stock level is checked.

Misc

  • OrderId—Select OrderId to dynamically associate this activity with orders in Ektron. Click Browse () and select OrderId. from the list.
  • orderStatus
    • Received (default)—marks an order as received
    • InProcess—marks the order as in process
    • Shipped—marks the order as shipped
    • Completed—marks the order as completed
    • Canceled—marks the order as canceled
    • OnHold—puts the order on hold
    • Fraud—marks the order as fraud
Customizing the Payment Gateway Provider

Customizing the Payment Gateway Provider

NOTE: To see a webinar on this topic, go to http://www.ektron.com/Resources/Webinars/Creating-Your-Own-Payment-Gateway-Provider/.

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.

  1. A customer purchases a product from your site and submits his payment information.
  2. Ektron passes the information to your payment gateway.
  3. Your payment gateway provider passes the information to your bank’s processor.
  4. The bank’s processor submits the information to a credit card interchange for processing, clearing and settlement.
  5. The interchange notifies the customer’s credit card company of the transactions details.
  6. The credit card company accepts or declines the transaction based on the customers account information.
  7. If the transaction is approved, the funds are transferred to the interchange.
  8. The credit card interchange sends information about whether the transaction is approved to your bank’s processor.
  9. This information is passed to your payment gateway provider.
  10. The provider notifies your site of the information. The results of the transaction are displayed on the page the customer is viewing.
  11. The credit card interchange sends funds to your merchant account.

This section explains how to extend Ektron’s Payment Gateway Provider Architecture to build your own customized Payment Gateway Provider.

See Also: Payment Options

Payment Gateway Provider object model

Payment Gateway Provider Object Model

Below is 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.

Creating a custom Payment Gateway Provider

Creating a Custom Payment Gateway Provider

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

  1. Create a class library project in Visual Studio.
  2. Add references to these DLLs:
    • Ektron.Cms.Commerce
    • Ektron.Cms.Common
    • Ektron.Cms.ObjectFactory
    • Ektron.Cms.Instrumentation
    • System.Configuration
    • Add these using statements to the code-behind:
      using System;
      using System.Collection;
      using System.Collection.Specialized;
      using Ektron.Cms;
      using Ektron.Cms.Common;
      using Ektron.Cms.Commerce;   
  3. Change the namespace to:
    namespace Ektron.Cms.Extensibility.Commerce.Samples
  4. Rename your class and inherit as below:
    public class CustomPaymentProvider: 
      Ektron.Cms.Commerce.PaymentGatewayProvider.PaymentGatewayProvider
  5. Add the following constructor.
    #region constructor, member variables
    public CustomPaymentProvider() { }
    #endregion
  6. Add the methods required by the PaymentGatewayProvider base class. The Initialize method reads configuration information, the others are related to the payments and will be completed later in the example.
    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)
    {
    }
    
  7. Implement the Authorize method that you added in the previous step. This method is called to authorize a given amount of money when an order is submitted.
    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;

  8. Implement the AuthorizeAndCapture, CapturePreauthorization and VoidPreAuthorization methods you added earlier.
  9. AuthorizeAndCapture requires that Authorization.CapturedOn be set, as the capture occurs at the same time as the Authorization.
  10. For CapturePreauthorization and VoidPreAuthorization, set the appropriate dates for these actions, so they are recorded in the system properly.
    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;
    }
  11. Save and build the project.
  12. Copy your project’s DLL file to your Ektron Web site’s bin directory.
  13. Register the provider in your site’s web.config file. The web.config file provides the facility to manage payment gateway providers within Ektron.
  14. To do this, locate the EktronPaymentGateway section and change the defaultProvider parameter to the name of your custom provider. Note that changing this from “Automatic” to the name of your provider overrides the settings in Workarea > Settings > Commerce > Configuration > Payment Gateways. Ektronnow will use the new provider.
  15. If you start your search from the top of the file, it is the second instance.
    <EktronPaymentGateway defaultProvider="CustomPaymentProvider">
  16. Add your custom payment provider between the EktronPaymentGateway’s <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>
    
  17. Save the web.config file.
  18. Next, add the payment gateway to the Workarea.
    1. Go to Workarea > Commerce > Configuration > Payment Options.
    2. Select New > Payment Gateway.
    3. In the Name drop-down, select customerPaymentProvider.
    4. Check both Cards and Checks checkboxes.
    5. Save these changes. Your custom payment provider is now the default provider. Whenever a payment provider needs to be contacted, the information routes through the new custom provider.
Making your custom provider appear as an option in the workarea

Making Your Custom Provider Appear as an Option in the Workarea

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:

  1. In the web.config file, locate the EktronPaymentGateway section and make sure the defaultProvider parameter is set to Automatic.
    <EktronPaymentGateway defaultProvider="Automatic">
  2. Save the web.config file.
  3. Navigate to the Workarea > Settings > Commerce >Configuration > Payment Gateways screen.
  4. Choose New > Payment Gateway.
  5. From the Name drop-down list, select the name of the your new custom gateway.
  6. Check the Default box if you want this to be the default payment gateway. The default payment gateway is the gateway all payment information is routed through.
  7. Enter the User ID for this payment gateway. This ID will identify your account with this gateway provider.
  8. Enter the password for your account with this gateway provider.
  9. If this gateway provider expects additional parameters when contacting it, click the Expand Custom Values link and enter those values into the Custom 1 and Custom 2 fields.
  10. Click Save. The custom gateway provider has been added and can now be managed from the Workarea.
CustomGatewayProvider code example

CustomGatewayProvider Code Example

WARNING! Copying and pasting the code below and using it 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;
      }
    }
  }
Customizing the Shipment Provider

Customizing the Shipment Provider

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.

  • package information—for example, how many, height, width, depth and weight of each package.
  • warehouse address—the address from which the items will be shipped. A warehouse’s information can be set in the Workarea > Settings > Commerce > Shipping > Warehouses screen.
  • destination address—the address a customer entered as his shipping address when making the purchase.
  • desired shipping options—for example, if you have Next Day, 2-Day Ground and 3-Day Ground as shipping options, you can pass the option chosen by the customer.

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.

  1. A customer adds 2 books his to cart and clicks checkout.
  2. The shipping calculator looks for the smallest possible package that will fit the items.
  3. The shipping calculator calls the registered shipping provider for each package in the order.
  4. The shipping calculator combines all the rates received if more than one package is being shipped and returns the shipping method rates to the checkout control.
  5. The customer selects the desired shipping method and it is saved with the cart.

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.

Shipment Provider object model

Shipment Provider Object Model

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.

Creating a custom Shipment Provider

Creating a Custom Shipment Provider

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.

  1. Create a class library project in Visual Studio.
  2. Add references to these DLLs:
    Ektron.Cms.Commerce
    Ektron.Cms.Common
    Ektron.Cms.ObjectFactory
    Ektron.Cms.Instrumentation
    System.Configuration
    
  3. Add these using statements to the code-behind:
    using System.Configuration.      
    using Ektron.Cms.Commerce.Shipment.Provider;      
    using Ektron.Cms.Commerce      
    using Ektron.Cms.Instrumentation
  4. Change the namespace to:
    namespace Ektron.Cms.Extensibility.Commerce.Samples
    {
  5. Rename your class and inherit as below:
    public class CustomShipmentProvider : ShipmentPovider
    {
  6. Add the following constructor private variables and properties. The items added to the 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  
  7. Add the following methods required by the ShipmentProvider base class.
    • Initialize—reads configuration information
    • GetServiceTypes—returns a List <string> of the shipping options available
    • GetTrackingUrl—exposes tracking support
    • GetRates—returns a list of shipping rates for the items being shipped. The contents of this method will be added in the next step.
    #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
        
  8. Implement the GetRates method. Add this code to public override for GetRates which was added during the previous step. (This example uses flat rates for your shipping methods. In a real world scenario, the shipping methods and their corresponding rates can be obtained via FedEx, UPS, a third party application or a Web service.) When complete, the code should look like this.
    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;
      }
    }
    }    
    
  9. You can use the GetRates method to restrict countries to which you ship. For example, if you want to prevent shipping to all countries except Canada, add these lines of code to the GetRates method.
    if (destination.Country.Id != 124)
      throw new Ektron.Cms.Commerce.Exceptions.Shipping.InvalidAddressException
        ("We ship only to Canada.");
    
  10. Save and build the project.
  11. Copy your project’s DLL file to your Ektron Web site’s bin directory.
  12. Register the provider in your site’s shipment.config file. This file provides the facility to manage shipping providers within Ektron. Locate the shipmentProvider section and change the defaultProvider parameter to the name of your custom provider.
    <shipmentProvider defaultProvider="CustomShipmentProvider">    
    
  13. Add your custom shipping provider between the shipmentProvider’s <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>    
    
  14. Save the web.config file.
  15. Your custom shipping provider is now the default provider. Whenever you need to obtain shipping rate information, the information routes through the new custom provider.
  16. Add the shipping methods that are available for your provider in the Workarea > Settings > Commerce -> Shipping > Methods screen.
    1. Select New > Shipping Method.
    2. Enter a name. This is what a customer sees during the checkout process. An example is “Ground” or “Next Day”. For this example, use “Custom1”.
    3. Check the Active checkbox.
    4. Click View Options and select CustomOption_1 from the list.
    5. Click Save).
    6. Repeat this process for the CustomOption2 shipping method type.

The options are now available in the checkout process, and are shown on the shipping method selection screen.

—CustomShipmentProvider C# code example—

CustomShipmentProvider Code Example

WARNING! Copying and pasting the code below and using it 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
  }
}
Customizing the Inventory Provider

Customizing the Inventory Provider

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.

Inventory Provider object model

Inventory Provider Object Model

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.

Creating a custom Inventory Provider

Creating a Custom Inventory Provider

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.

  1. Create a class library project in Visual Studio.
  2. Add references to these DLLs:
    • Ektron.Cms.Commerce
    • Ektron.Cms.Common
    • Ektron.Cms.ObjectFactory
    • System.Configuration
  3. Add these using statements to the code-behind.
    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;
  4. Change the namespace to:
    Ektron.Cms.Extensibility.Commerce.Samples         
             {        
  5. Rename your class and inherit as below:
    CustomInventoryProvider
                InventoryPovider            
  6. Add the following constructor, private variables and properties.
    Public CustomInventoryProvider() { } private CmsInventory _inventory;
    protected CmsInventory Inventory
      {     
          get
          {      
               if (_inventory == null)
                {           
                  _inventory = CmsInventory(RequestInformation);
                }  
                return_inventory;
            }
      }        
  7. Override the following methods. These methods area called when the inventory system is queried.
    • GetInventory—returns an InventoryData data class for a given product id.
    • SaveInventory—persists updates to the stock levels inside the inventory system.

      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);
            }
         }
            
  8. Save and build the project.
  9. Copy your project’s DLL file to your Ektron Web site’s bin directory.
  10. Register the provider in your site’s 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">
  11. Add your custom payment provider between the EktronPaymentGateway’s <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>       
  12. Save the 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.

—CustomInventoryProvider C# code example—

CustomInventoryProvider Code Example

WARNING! Copying and pasting the code below and using it 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);
        }
      }
    }
  }    
Customizing shipping calculations

Customizing Shipping Calculations

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.

Creating the calculator code

Creating the Calculator Code

  1. Create a class library project in Visual Studio named 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
  2. Add the following using statements.
    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;        
  3. Change the namespace to 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
           {
    
  4. Add a few helper variables, properties, and functions. The function 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 widget.
  5. The following logic fits the goods into packages, adding a function to get another warehouse location. (It is hard-coded in this example.)
    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;
    }       
  6. Override the OnBeforeCalculate method, which is called when shipping is calculated for the basket.
    public override void 
      OnBeforeCalculate(BasketCalculatorData basketData, 
      ShippingMethodData shippingMethod, CancellableEventArgs eventArgs)
                {
                }
    
  7. In this method, add code to ship items with a tax class id of 4 from another warehouse, versus the default one specified (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
    //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;
               }
            
  8. When shipping is calculated for the basket, the above function runs first. Setting the IsCancelled property to true on eventArgs instructs Ektron to stop further execution of the shipping calculations. This, in effect, overrides the shipping functionality.
  9. Build the project, and copy the assembly into the Ektron site's bin directory.
Registering the event with the system

Registering the Event with the System

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:

  1. Open the siteroot/objectfactory.config file.
  2. Find the objectStrategies section of the file.
  3. Within that section, locate or add this key: name="ShippingCalculator".
  4. Add a reference to the class created earlier in the <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>