1 (905) 264-1100 Monday-Friday, 9am - 6pm
info@apiqu.com Drop us a line anytime!
100 Arbors Lane, Unit D Vaughan, Canada
Toggle Menu
1 (905) 264-1100 Monday-Friday, 9am - 6pm
info@apiqu.com Drop us a line anytime!
100 Arbors Lane, Unit D Vaughan, Canada
Toggle Menu
Active Commerce

Active Commerce Beanstream Payment Provider

Active Commerce’s recent disappointing news to discontinue their e-commerce platform brought sorrow to our team; We did enjoy working with the product, and still believe that it's one of the best e-commerce solutions for Sitecore, if not the best at the moment.

As for developer, this commerce solution is comfortable to use as it is native to Sitecore and uses familiar terms and technologies. The Active Commerce team provides insightful documentation, helpful support, and thorough training. The basic installation package comes with a number of payment options, shipping providers, and an excellent API to extend and develop new customizations.

Our team has done quite a few implementations using Active Commerce, and developers really loved it.

Payment Provider example

To show how simple and powerful this platform is, I would like to give an example in which we will build new integration. It will provide our web store with ability to accept credit cards by using the Canadian Beanstream payment gateway.

So the first thing we need, of course, is an account on Beanstream; As many others, Beanstream gives you a "sandbox" account to run your tests. Once you registered you will obtain a Merchant ID and an API Key. The documentation, SDK and coding samples can be found on the site as well. That's everything we need to start coding.

Coding

In this example, I would like to have the new functionality completely separate from the website code, by doing this we can reuse the code in our future projects.

I will start by creating a new Class Library in Visual Studio and adding references to the project.

First of all, we will need to install the Beanstream Nuget package and add references to Sitecore.Kernel.dll, ActiveCommerce.Kernel.dll, Sitecore.Ecommerce.Kernel.dll, and Sitecore.Ecommerce.DomainModel.dll. You can find the libraries in the BIN folder of your e-commerce site, assuming you already have an installed site with AC module.

Now we’re all set. Lets create a new class named "PaymentProvider" derived from the abstract class ActiveCommerce.Payment.IntegratedPaymentProviderBase. The base abstract class provides us basic method to invoke payments, and supporting additional features like collecting payments at a later time or refunds we will implement two interfaces Sitecore.Ecommerce.DomainModel.Payments.IReservable and Sitecore.Ecommerce.DomainModel.Payments.ICreditable



using System;
using ActiveCommerce.Payments;
using Sitecore.Ecommerce.DomainModel.Payments;

namespace Apiqu.AC.Beanstream
{
    public class PaymentProvider : IntegratedPaymentProviderBase, IReservable, ICreditable
    {
        public override void Invoke(Sitecore.Ecommerce.DomainModel.Payments.PaymentSystem paymentSystem, Sitecore.Ecommerce.DomainModel.Payments.PaymentArgs paymentArgs)
        {
            throw new NotImplementedException();
        }
        public void CancelReservation(Sitecore.Ecommerce.DomainModel.Payments.PaymentSystem paymentSystem, Sitecore.Ecommerce.DomainModel.Payments.PaymentArgs paymentArgs, ReservationTicket reservationTicket)
        {
            throw new NotImplementedException();
        }

        public void Capture(Sitecore.Ecommerce.DomainModel.Payments.PaymentSystem paymentSystem, Sitecore.Ecommerce.DomainModel.Payments.PaymentArgs paymentArgs, ReservationTicket reservationTicket, decimal amount)
        {
            throw new NotImplementedException();
        }

        public void Credit(Sitecore.Ecommerce.DomainModel.Payments.PaymentSystem paymentSystem, Sitecore.Ecommerce.DomainModel.Payments.PaymentArgs paymentArgs, ReservationTicket reservationTicket)
        {
            throw new NotImplementedException();
        }

    }
}


With this example, we just want to process a simple credit card transaction - nothing complex. There are three steps to accomplish that:

  • Create a Beanstream object to get access to the service
  • Create a payment request
  • Send the request to the payment gateway

Here is how it looks in the code:



using System;
using ActiveCommerce.Payments;
using Sitecore.Ecommerce.DomainModel.Payments;
using Beanstream.Api.SDK;
using Beanstream.Api.SDK.Requests;
using Beanstream.Api.SDK.Domain;
using Sitecore.Diagnostics;

namespace Apiqu.AC.Beanstream
{
    public class PaymentProvider : IntegratedPaymentProviderBase, IReservable, ICreditable
    {
        protected const int MERCHANT_ID = 1234567;
        protected const string PAYMENTS_API_KEY = "YOUR_API_KEYD";
        protected const string PAYMENTS_API_VER = "1";

        public override void Invoke(Sitecore.Ecommerce.DomainModel.Payments.PaymentSystem paymentSystem, Sitecore.Ecommerce.DomainModel.Payments.PaymentArgs paymentArgs)
        {
            // Get shopping cart, payment arguments and details
            var paymentArguments = paymentArgs as ActiveCommerce.Payments.PaymentArgs;
            var cart = paymentArguments.ShoppingCart as ActiveCommerce.Carts.ShoppingCart;
            var paymentDetails = paymentArguments.PaymentDetails as ActiveCommerce.Payments.CreditCardInfo;

            // Create a Beanstream API object to provide access
            Gateway beanstream = new Gateway()
            {
                MerchantId = MERCHANT_ID,
                PaymentsApiKey = PAYMENTS_API_KEY,
                ApiVersion = PAYMENTS_API_VER
            };

            // Create a payment request
            var request = new CardPaymentRequest
            {
                Amount = cart.PrimaryPayment.Amount,
                OrderNumber = cart.OrderNumber,
                Comments = cart.ShippingProvider.Title,

                Card = new Card
                {
                    Name = paymentDetails.CardsHolderName,
                    Number = paymentDetails.CardNumber,
                    ExpiryMonth = paymentDetails.ExpirationDate.ToString("MM"),
                    ExpiryYear = paymentDetails.ExpirationDate.ToString("yy"),
                    Cvd = paymentDetails.SecurityCode
                },

                Billing = new Address
                {
                    Name = cart.CustomerInfo.BillingAddress.Name,
                    AddressLine1 = cart.CustomerInfo.BillingAddress.Address,
                    AddressLine2 = cart.CustomerInfo.BillingAddress.Address2,
                    City = cart.CustomerInfo.BillingAddress.City,
                    Province = cart.CustomerInfo.BillingAddress.State,
                    PostalCode = cart.CustomerInfo.BillingAddress.Zip,
                    Country = cart.CustomerInfo.BillingAddress.Country.Code,
                    EmailAddress = cart.CustomerInfo.Email,
                    PhoneNumber = cart.CustomerInfo.Phone
                },

                Shipping = new Address
                {
                    Name = cart.CustomerInfo.ShippingAddress.Name,
                    AddressLine1 = cart.CustomerInfo.ShippingAddress.Address,
                    AddressLine2 = cart.CustomerInfo.ShippingAddress.Address2,
                    City = cart.CustomerInfo.ShippingAddress.City,
                    Province = cart.CustomerInfo.ShippingAddress.State,
                    PostalCode = cart.CustomerInfo.ShippingAddress.Zip,
                    Country = cart.CustomerInfo.ShippingAddress.Country.Code
                }
            };

            try
            {
                // Process payment
                var response = beanstream.Payments.MakePayment(request);

                // Save transaction information
                this.TransactionDetails.AuthorizationCode = response.AuthCode;
                this.TransactionDetails.TransactionNumber = response.TransactionId;
                this.TransactionDetails.ProviderStatus = response.MessageId;
                this.TransactionDetails.ProviderMessage = response.Message;

                // Set payment status
                this.PaymentStatus = PaymentStatus.Succeeded;

            }
            catch (Exception ex)
            {
                this.PaymentStatus = PaymentStatus.Failure;
                Log.Error($"Beanstream reports a transaction processing error. Message: {ex.Message}", this);
            }

        }
        public void CancelReservation(Sitecore.Ecommerce.DomainModel.Payments.PaymentSystem paymentSystem, Sitecore.Ecommerce.DomainModel.Payments.PaymentArgs paymentArgs, ReservationTicket reservationTicket)
        {
            throw new NotImplementedException();
        }

        public void Capture(Sitecore.Ecommerce.DomainModel.Payments.PaymentSystem paymentSystem, Sitecore.Ecommerce.DomainModel.Payments.PaymentArgs paymentArgs, ReservationTicket reservationTicket, decimal amount)
        {
            throw new NotImplementedException();
        }

        public void Credit(Sitecore.Ecommerce.DomainModel.Payments.PaymentSystem paymentSystem, Sitecore.Ecommerce.DomainModel.Payments.PaymentArgs paymentArgs, ReservationTicket reservationTicket)
        {
            throw new NotImplementedException();
        }

    }
}


This is very basic implementation. Of course, you can add more functionality to the payment provider but it's good enough for starting your own payment integration.

Once we have our payment provider we need to do three more things to make it work with Active Commerce.

Registering the new payment provider with Unity:



using ActiveCommerce.IoC;
using Microsoft.Practices.Unity;

namespace Apiqu.AC.Beanstream
{
    public class RegisterTypes : ITypeRegistration
    {
        public void Process(IUnityContainer container)
        {
            // Register with Unity
            container.RegisterType<Sitecore.Ecommerce.DomainModel.Payments.PaymentProvider, Apiqu.AC.Beanstream.PaymentProvider>("BEANSTREAM");
        }
        public int SortOrder
        {
            get
            {
                return 0;
            }
        }
    }
}


Adding new initializing processor in Sitecore:



<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <initialize>
        <processor type="Apiqu.AC.Beanstream.RegisterTypes, Apiqu.AC.Beanstream" patch:after="processor[@type='ActiveCommerce.Pipelines.Loader.ConfigureEntities, ActiveCommerce.Kernel']"<>/processor>
      </initialize>
    </pipelines>
  </sitecore>
</configuration>


All is left is to create new payment item in Sitecore under settings/payment option. Use "BEANSTREAM" as a code for the payment option.

Done!

One issue that I hit in the past involved the Newtonsoft version. Sitecore uses pretty old version of the library while Beanstream requires the latest one.

The solution is simple – redirect the assembly versions in web.config so you can keep both versions in the project.



<dependentassembly>
  <assemblyidentity name="Newtonsoft.Json" publickeytoken="30ad4fe6b2a6aeed"></assemblyidentity>
  <bindingredirect oldversion="0.0.0.0-6.0.0.0" newversion="6.0.0.0"></bindingredirect>
  <bindingredirect oldversion="8.0.0.0-9.0.0.0" newversion="9.0.0.0"></bindingredirect>
  <codebase version="9.0.0.0" href="bin\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll" xmlns="urn:schemas-microsoft-com:asm.v1">
</dependentassembly>


Closing

That's it, simple and elegant. I am not saying that Active Commerce is a walk in the park and everything can be done in a couple of lines of code, but it is definitely very flexible and extendable e-commerce solution for Sitecore.

We like it that way and hope new ecommerce solution from Sitecore will be as robust as Active Commerce and may be even better…