So I’m subjected to using an off the shelf Content Management System called Kentico, which happens to be the only half way decent CMS written in .NET. I’ll vent that argument some other day.

One of the thing’s I’ve had to write for the CMS was a Custom Payment Gateway, and while their documentation is vast and covers a lot, it also misses out on a few really non-obvious things.

Kentico CMS E-commerce Guide

The Kentico Guide details the process of developing a custom payment gateway, but I’m going to write this as more of a tutorial.

I would say there's three parts to the process.

  1. Creating the User Control which will capture the payment details.
  2. Creating the Custom Gateway class.
  3. Tying it all together.

 
I’ll start with the User control since that’s the most basic part.

Note: This is based on the idea that your taking the payment yourself, and not using a hosted payment solution where the payment is done by your payment gateway provider.

Gateway Form

Ok so first we need to create a User Control with the form:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="custom-gateway-form.ascx.cs" Inherits="custom_gateway_form" %>

<fieldset>
    <ol>
        <li>
            <label>Card Holder Name:</label>
            <asp:TextBox ID="txtCardHolderName" runat="server" />
        </li>
        <li>
            <label>Card Number:</label>
            <asp:TextBox ID="txtCardNumber" runat="server" />
        </li>
        <li>
            <label>Expiry:</label>
            <asp:DropDownList ID="ddlMonth" runat="server">
                <asp:ListItem Value="01">01</asp:ListItem>
                <asp:ListItem Value="02">02</asp:ListItem>
                . . .
                <asp:ListItem Value="11">11</asp:ListItem>
                <asp:ListItem Value="12">12</asp:ListItem>
            </asp:DropDownList>
            /
            <asp:DropDownList ID="ddlYear" runat="server">
                <asp:ListItem Value="09">09</asp:ListItem>
                <asp:ListItem Value="10">10</asp:ListItem>
                <asp:ListItem Value="11">11</asp:ListItem>
                <asp:ListItem Value="12">12</asp:ListItem>
            </asp:DropDownList>
        </li>
        <li>
            <label>CVV:</label>
            <asp:TextBox ID="txtCVV" runat="server" />
        </li>
    </ol>
</fieldset>


Next we need to setup the code behind.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using CMS.EcommerceProvider;

public partial class custom_gateway_form : CMSPaymentGatewayForm
{
    public override void LoadData()
    {
        base.LoadData();
    }

    public override string ValidateData()
    {
        return base.ValidateData();
    }

    public override string ProcessData()
    {
        return base.ProcessData();
    }
}


So in the code behind we need to inherit CMSPaymentGatewayForm. This class gives us access to 3 virtual methods we can override.

Unless you want or need to load up personal data you shouldn’t need to worry about LoadData().

ValidateData() is where you would validate all your inputs to ensure the end user has entered in a credit card number and such. The method called on post back, and if an empty string is returned, it’s assumed that everything checks out ok and it begins ProcessDate().

In ProcessData() we need to capture the user’s information that is required to be sent to the payment gateway. This is stored in

ShoppingCartInfoObj.PaymentGatewayCustomData


So we populate all the information

public override string ProcessData()
{
    ShoppingCartInfoObj.PaymentGatewayCustomData["CardHolderName"]  = txtCardHolderName.Text.Trim();
    ShoppingCartInfoObj.PaymentGatewayCustomData["CardNumber"]      = txtCardNumber.Text.Trim();
    ShoppingCartInfoObj.PaymentGatewayCustomData["ExpiryMonth"]     = ddlMonth.SelectedValue;
    ShoppingCartInfoObj.PaymentGatewayCustomData["ExpiryYear"]      = ddlYear.SelectedValue;
    ShoppingCartInfoObj.PaymentGatewayCustomData["CVV"]             = txtCVV.Text.Trim();

    return "";
}


Again, if everything checks out ok, return an empty string, otherwise return an error message.

This is all that is required for the for the User Control.

Payment Gateway Class

Create a new Class Library for the gateway, and references to all of Kentico’s CMS. assemblies, and System.Web. (I could probably sit and ween out which ones are actually used, but I personally don’t like Kentico enough to really care.)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

using CMS.EcommerceProvider;
using CMS.GlobalHelper;
using CMS.Ecommerce;

namespace CustomPaymentGateway
{
    public class CustomGateway : CMSPaymentGatewayProvider
    {
        public override CMSPaymentGatewayForm GetPaymentDataForm()
        {
            return base.GetPaymentDataForm();
        }

        public override void ProcessPayment()
        {
            base.ProcessPayment();
        }
    }
}


So we need to inherit Kentico’s CMSPaymentGatewayProvider class, and override the two methods, GetPaymentDataForm(), and ProcessPayment().

The GetPaymentDataForm method is what is going to load the User Control we just made.

public override CMSPaymentGatewayForm GetPaymentDataForm()
{
    try
    {
        return (CMSPaymentGatewayForm)ShoppingCartControl.LoadControl("~/CMSTemplates/custom-gateway-form.ascx");
    }
    catch
    {
        return null;
    }
}


The ProcessPayment method is where the the request is made to the payment gateway and the payment result is captured.

public override void ProcessPayment()
{
    string cardHolderName   = ShoppingCartInfoObj.PaymentGatewayCustomData["CardHolderName"].ToString(),
           cardNumber       = ShoppingCartInfoObj.PaymentGatewayCustomData["CardHolderName"].ToString(),
           cardCVC          = ShoppingCartInfoObj.PaymentGatewayCustomData["CVV"].ToString(),
           cardExpiryMonth  = ShoppingCartInfoObj.PaymentGatewayCustomData["ExpiryMonth"].ToString(),
           cardExpiryYear   = ShoppingCartInfoObj.PaymentGatewayCustomData["ExpiryYear"].ToString();

    //TODO: Take actual payment...

    // Capture the Payment Result
    PaymentResult.PaymentIsCompleted    = true; //false if payment failed...
    PaymentResult.PaymentStatusName     = "";

    PaymentResult.PaymentDate           = DateTime.Now;
    PaymentResult.PaymentTransactionID  = "";

    PaymentResult.PaymentDescription    = "";
    PaymentResult.PaymentMethodID       = ShoppingCartInfoObj.ShoppingCartPaymentOptionID;

    // Set any error message if need be...
    ErrorMessage                        = "";

    // Save the Payment Result
    UpdateOrderPaymentResult();
}


The ProcessPayment is pretty straight forward, but the real gotcha is you actually have to set the Payment Method ID. It doesn’t really make sense because you choose the Payment Method, proceed to the next step which is the payment form, it knows which form to load, yet when the payment occurs, it doesn’t actually know which Payment Method you used, so you have to manually assign it to the result.

But basically that’s all there is to it, it’s not difficult, but there's 3 main things I don’t get at first:

  • What the GetPaymentDataForm actually does.
  • That I have to reference System.Web otherwise I don’t get “LoadControl” for the GetPaymentDataForm method.
  • That I actually have to manually assign the PaymentMethodID…

The End

All that's left now is to wire it all up, which is pretty simple.

    Sign into the CMSDesk, browse to Tools > E-commerce > Configuration > Payment methods.
    Add the new payment method, and away you go.