How to hook and execute custom code against store events

From AbleCommerce Wiki
Jump to: navigation, search

In past we saw this question from time to time where developers asking for the ability to intercept store events like Order Placed, Order Paid etc. The good news is, now it will be possiable to intercept all avilable event triggers in Gold R5 and upwards. We have incorporated the support to implement store event handling more like a service. Out of the box there is default event handling implemented and configured. In order to provide your custom handling you can create your cutom event handler by following certain rules and then can plug it in place of default one.

Out of the box CommerceBuilder.Eventing.DefaultEventsHandler class provides the default event handling. If you want to override the default event handling completly then all you need is to implement CommerceBuilder.Eventing.IStoreEventsHandler interface. Alternativly you can also extend your class from CommerceBuilder.Eventing.DefaultEventsHandler and override specific event handler methods. Once you are done with your custom event handler you can register it into application by creating an entry in Website/App_Data/windsor.config file. This will hook your custom event handler and route the execution control to your custom class against events.

Following class provides the custom event handling sample where we need are performing some actions against OrderPlaced and DigitalGoodActivated events. For OrderPlaced event we are simply creating and storing some order XML for some third party tool under App_Data while for DigitalGoodActivated we are passing generated URL for download from external website. For all other events we are simply calling the default implementations.

MyEventsHandler.cs

namespace AbleExtensions
{
    using System;
    using System.IO;
    using System.Xml.Serialization;
    using CommerceBuilder.DigitalDelivery;
    using CommerceBuilder.DomainModel;
    using CommerceBuilder.Eventing;
    using CommerceBuilder.Messaging;
    using CommerceBuilder.Orders;
    using CommerceBuilder.Utility;

    /// <summary>
    /// This class extends the functionality implemented by DefaultEventHandler (Requires Gold R5 or Higher)
    /// </summary>
    public class MyEventsHandler : DefaultEventsHandler
    {
        /// <summary>
        /// This method provides the ability to intercept the ability to intercept OrderPlaced event and do some custom order processing before sending the Email.
        /// </summary>
        /// <param name="o">sender object</param>
        /// <param name="e">event arguments</param>
        public override void OrderPlaced(object o, OrderPlacedEventArgs e)
        {
            /* 
             * WE WANT TO SAVE SOME ORDER INFORMATION IN XML FORMAT TO CONSUME IT IN SOME THIRDPARTY SOFTWARE.
             * THESE XML FILES WILL BE STORED UNDER App_Data
             */

            try
            {
                Order order = e.Order;
                MyOrder myOrder = new MyOrder() { Id = order.Id, OrderNumber = order.OrderNumber, TotalCharges = order.TotalCharges };

                // BUILD PATH TO APP_DATA FOLDER TO STORE THE XML FILES AND MAKE SURE FILE NAMES ARE UNIQUE
                string fileName = FileHelper.SafeMapPath(string.Format("~/App_Data/order-{0}.xml", order.Id));
                using (StreamWriter sw = new StreamWriter(fileName))
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyOrder));
                    xmlSerializer.Serialize(sw, myOrder);
                    sw.Flush();
                    sw.Close();
                }

            }
            catch (Exception exp)
            {
                Logger.Error(exp.Message);
            }


            // AFTER DOING OUR CUSTOM PROCESSING WE NEED TO TRIGGER BASE EVENT HANDLER TO DO THE DEFAULT HANDLING
            base.OrderPlaced(o, e);
        }

        /// <summary>
        /// This method helps us intercept the event when digital good is activated to generate custom download URLs.
        /// It will provide custom Email handling for this event.
        /// </summary>
        /// <param name="o"></param>
        /// <param name="e"></param>
        public override void DigitalGoodActivated(object o, DigitalGoodActivatedEventArgs e)
        {
            string downloadUrl = GetDownloadUrl(e.OrderItemDigitalGood);

            OrderItemDigitalGood oidg = e.OrderItemDigitalGood;
            DigitalGood dg = oidg.DigitalGood;

            // LOAD ASSOCIATED EMAIL TEMPLATE
            EmailTemplate template = EntityLoader.Load<EmailTemplate>(dg.ActivationEmailId);
            if (template != null)
            {
                template.Parameters["downloadUrl"] = downloadUrl;
                template.Parameters["store"] = oidg.OrderItem.Order.Store;
                template.Parameters["digitalGood"] = oidg.DigitalGood;
                template.Parameters["customer"] = oidg.OrderItem.Order.User;
                template.Parameters["orderItem"] = oidg.OrderItem;
                template.Parameters["orderItemDigitalGood"] = oidg;
                template.Parameters["order"] = oidg.OrderItem.Order;

                // ALL PARAMTERS ARE SET, SEND THE EMAIL TEMPLATE IN ASYNC MANNER
                // IN EMAIL TEMPLATE DOWNLOAD URL CAN BE ACCESSED USING $downloadUrl variable.
                template.Send(true);
            }
        }

        public override void CustomerPasswordRequest(object o, CustomerPasswordRequestEventArgs e)
        {
            Logger.Info("CustomerPasswordRequest event");
            base.CustomerPasswordRequest(o, e);
        }

        public override void DigitalGoodDeactivated(object o, DigitalGoodDeactivatedEventArgs e)
        {
            Logger.Info("DigitalGoodDeactivated event");
            base.DigitalGoodDeactivated(o, e);
        }

        public override void GiftCertificateValidated(object o, GiftCertificateValidatedEventArgs e)
        {
            Logger.Info("GiftCertificateValidated event");
            base.GiftCertificateValidated(o, e);
        }

        public override void InventoryDestocked(object o, InventoryDestockedEventArgs e)
        {
            Logger.Info("InventoryDestocked event");
            base.InventoryDestocked(o, e);
        }

        public override void InventoryRestocked(object o, InventoryRestockedEventArgs e)
        {
            Logger.Info("InventoryRestocked event");
            base.InventoryRestocked(o, e);
        }

        public override void LicenseKeyFulfilled(object o, LicenseKeyFulfilledEventArgs e)
        {
            Logger.Info("LicenseKeyFulfilled event");
            base.LicenseKeyFulfilled(o, e);
        }

        public override void LicenseKeyReturned(object o, LicenseKeyReturnedEventArgs e)
        {
            Logger.Info("LicenseKeyReturned event");
            base.LicenseKeyReturned(o, e);
        }

        public override void LowInventoryItemPurchased(object o, LowInventoryItemPurchasedEventArgs e)
        {
            Logger.Info("LowInventoryItemPurchased event");
            base.LowInventoryItemPurchased(o, e);
        }

        public override void OrderCancelled(object o, OrderCancelledEventArgs e)
        {
            Logger.Info("OrderCancelled event");
            base.OrderCancelled(o, e);
        }

        public override void OrderNoteAddedByCustomer(object o, OrderNoteAddedByCustomerEventArgs e)
        {
            Logger.Info("OrderNoteAddedByCustomer event");
            base.OrderNoteAddedByCustomer(o, e);
        }

        public override void OrderNoteAddedByMerchant(object o, OrderNoteAddedByMerchantEventArgs e)
        {
            Logger.Info("OrderNoteAddedByMerchant event");
            base.OrderNoteAddedByMerchant(o, e);
        }

        public override void OrderPaid(object o, OrderPaidEventArgs e)
        {
            Logger.Info("OrderPaid event");
            base.OrderPaid(o, e);
        }

        public override void OrderPaidCreditBalance(object o, OrderPaidCreditBalanceEventArgs e)
        {
            Logger.Info("OrderPaidCreditBalance event");
            base.OrderPaidCreditBalance(o, e);
        }

        public override void OrderPaidNoShipments(object o, OrderPaidNoShipmentsEventArgs e)
        {
            Logger.Info("OrderPaidNoShipments event");
            base.OrderPaidNoShipments(o, e);
        }

        public override void OrderPaidPartial(object o, OrderPaidPartialEventArgs e)
        {
            Logger.Info("OrderPaidPartial event");
            base.OrderPaidPartial(o, e);
        }

        public override void OrderShipped(object o, OrderShippedEventArgs e)
        {
            Logger.Info("OrderShipped event");
            base.OrderShipped(o, e);
        }

        public override void OrderShippedPartial(object o, OrderShippedPartialEventArgs e)
        {
            Logger.Info("OrderShippedPartial event");
            base.OrderShippedPartial(o, e);
        }

        public override void OrderStatusUpdated(object o, OrderStatusUpdatedEventArgs e)
        {
            Logger.Info("OrderStatusUpdated event");
            base.OrderStatusUpdated(o, e);
        }

        public override void PaymentAuthorizationFailed(object o, PaymentAuthorizationFailedEventArgs e)
        {
            Logger.Info("PaymentAuthorizationFailed event");
            base.PaymentAuthorizationFailed(o, e);
        }

        public override void PaymentAuthorized(object o, PaymentAuthorizedEventArgs e)
        {
            Logger.Info("PaymentAuthorized event");
            base.PaymentAuthorized(o, e);
        }

        public override void PaymentCaptureFailed(object o, PaymentCaptureFailedEventArgs e)
        {
            Logger.Info("PaymentCaptureFailed event");
            base.PaymentCaptureFailed(o, e);
        }

        public override void PaymentCaptured(object o, PaymentCapturedEventArgs e)
        {
            Logger.Info("PaymentCaptured event");
            base.PaymentCaptured(o, e);
        }

        public override void PaymentCapturedPartial(object o, PaymentCapturedPartialEventArgs e)
        {
            Logger.Info("PaymentCapturedPartial event");
            base.PaymentCapturedPartial(o, e);
        }

        public override void PaymentRefunded(object o, PaymentRefundedEventArgs e)
        {
            Logger.Info("PaymentRefunded event");
            base.PaymentRefunded(o, e);
        }

        public override void PaymentRefundedPartial(object o, PaymentRefundedPartialEventArgs e)
        {
            Logger.Info("PaymentRefundedPartial event");
            base.PaymentRefundedPartial(o, e);
        }

        public override void ShipmentReturned(object o, ShipmentReturnedEventArgs e)
        {
            Logger.Info("ShipmentReturned event");
            base.ShipmentReturned(o, e);
        }

        public override void ShipmentReturnedPartial(object o, ShipmentReturnedPartialEventArgs e)
        {
            Logger.Info("ShipmentReturnedPartial event");
            base.ShipmentReturnedPartial(o, e);
        }

        public override void ShipmentShipped(object o, ShipmentShippedEventArgs e)
        {
            Logger.Info("ShipmentShipped event");
            base.ShipmentShipped(o, e);
        }

        #region UTILITY

        /// <summary>
        /// This is just sample function and you will have to implement it according to your download provider. 
        /// </summary>
        /// <param name="item">OrderItemDigitalGood Item</param>
        /// <returns>Download URL to download server</returns>
        private string GetDownloadUrl(OrderItemDigitalGood item)
        {
            return string.Format("somedownloadserver.xyz.tld/download?id={0}&code={1}", item.Id, "URL validity code here");
        }

        public class MyOrder
        {
            public int Id { get; set; }
            public int OrderNumber { get; set; }
            public decimal TotalCharges { get; set; }
        }

        #endregion
    }
}

Following is the windsor configuration to register our custom handler in application. All you need is to create one component entry in windsor.config file.

windsor.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <components>
    <component id="MyEventHandler" service=" CommerceBuilder.Eventing.IStoreEventsHandler, CommerceBuilder" type="AbleExtensions.MyEventsHandler, AbleExtensions"/>
</components>
</configuration>