How to Create a Payment Module

This section explains how to create a new payment module for KonaKart using its module plug-in framework.

Introduction

KonaKart supports additional payment/shipping/order total/discount modules using a simple plug-in model.

Payment modules are typically designed to interface between KonaKart and a 3rd Party payment gateway supplier - like PayPal, Authorize.Net or WorldPay etc.

You can install as many payment gateways as you like with KonaKart but typically you would just have one configured as there is usually a cost associated with the merchant account that you have to set-up with each payment organization in order to receive your payments.

Suppose you want to create a brand new payment gateway module to interface between KonaKart and your chosen payment gateway supplier. For the purposes of this guide, let's call the payment gateway supplier "KonaPay" (a completely fictitious gateway).

Study the "KonaPay" APIs

First of all you should find out as much as you can about which interfaces "KonaPay" supports. They might supply an XML interface, a HTTP post interface, a SOAP interface or any kind of weird custom API that allows you to communicate with them. Some payment gateways do not provide any APIs for providing credit card information at all and require that your user is directed to their site to enter such sensitive details.

Choose which Interface Type you want for your users

So, you have to consider which type of gateway you want to implement for your users? This may well affect which gateway supplier you choose. Do you want to collect the credit card details yourself within KonaKart or do you want to send the users off to a payment gateway site to collect this information? Some prefer the former, some the latter.

You will see in the code that you need to set the paymentType on the PaymentDetails object, which defines which of these two modes you want:


// For Authorize.Net,YourPay etc.
setPaymentType(PaymentDetails.SERVER_PAYMENT_GATEWAY);  

or

// For PayPal, WorldPay etc.
setPaymentType(PaymentDetails.BROWSER_PAYMENT_GATEWAY); 

For the sake of our discussion, KonaPay provides a well-documented HTTP API that allows us to collect credit card information on the KonaKart site and send these off synchronously for payment authorization - which we think provides the best user experience at payment time. It's attractive to us because we can retain the same look and feel of our KonaKart store across the full shopping experience - rather than having to jump off to a payment gateway page provided by the gateway supplier which doesn't look anything like the rest of our site.

OK, we've chosen "KonaPay" because it fits our preferred implementation model and it has a well-documented API, what next?

Sign up for a Test Account with "KonaPay"

Typically, the payment gateway supplier will provide a free dummy test account so the next step is to apply for one of these at "KonaPay". Fortunately, "KonaPay" do provide a test account so we sign up and study the API documentation in a little more detail.

Determine which of the existing payment modules is the closest match

The next important step is to study the existing payment modules to see which one looks to be the closest fit to "KonaPay". This step is very important and will save you a great deal of time if you pick a close match.

The good news is that many of the gateways work in very similar ways within the two distinct payment module groups:

  • Payment details are collected inside KonaKart ("Server Mode" - Authorize.Net, YourPay, PayJunction, USAePay)

  • Payment details are collected at Gateway Supplier's Site ("Browser Mode" - WorldPay, PayPal, ChronoPay, ePayBg)

Copy the files of the closest match as the starting point

We decide that PayJunction is the closest match to "KonaPay" so we make copies of all the PayJunction files and directories - ensuring that we are completely consistent with the naming conventions used by PayJuntion (ie, the same lower/mixed case conventions as PayJunction). Make copies of the various properties files as well and rename these as appropriate to "KonaPay", "Konapay" or "konapay" in a consistent manner.

There will be payment directories to copy under com.konakart.bl.modules.payment and com.konakartadmin.modules.payment.

We also need to copy modules/src/com/konakart/actions/gateways/PayjunctionAction.java to modules/src/com/konakart/actions/gateways/KonapayAction.java

Define the configuration parameters

Next, we need to decide which configuration options we will allow an administrator to change once the "KonaPay" module is installed.

PayJunction allows these to be configured: (the following is the code that initializes these in modules/src/com/konakartadmin/modules/payment/payjunction/Payjunction.java:)


configs[i++] = new KKConfiguration(
    /* title */"Enable PayJunction Module",
    /* key */"MODULE_PAYMENT_PAYJUNCTION_STATUS",
    /* value */"true",
    /* description */ "Do you want to accept PayJunction payments? ('true' or 'false')",
    /* groupId */groupId, /* sort Order */i, /* useFun */"",
    /* setFun */"choice('true', 'false')",
    /* dateAdd */now);

configs[i++] = new KKConfiguration(
    /* title */"Sort order of display.",
    /* key */"MODULE_PAYMENT_PAYJUNCTION_SORT_ORDER",
    /* value */"0",
    /* description */ "Sort order of display. Lowest is displayed first.",
    /* groupId */groupId, /* sort Order */i, /* useFun */"", /* setFun */"", /* dateAdd */now);

configs[i++] = new KKConfiguration(
    /* title */"Payment Zone",
    /* key */"MODULE_PAYMENT_PAYJUNCTION_ZONE",
    /* value */"0",
    /* description */ "If a zone is selected, only enable this payment method for that zone.",
    /* groupId */groupId, /* sort Order */i,
    /* useFun */"tep_get_zone_class_title",
    /* setFun */"tep_cfg_pull_down_zone_classes(",
    /* dateAdd */now);

configs[i++] = new KKConfiguration(
    /* title */"PayJunction Username",
    /* key */"MODULE_PAYMENT_PAYJUNCTION_USERNAME",
    /* value */"pj-ql-01",
    /* description */ "The username used to access the PayJunction service",
    /* groupId */groupId, /* sort Order */i, /* useFun */"", /* setFun */"", /* dateAdd */now);

configs[i++] = new KKConfiguration(
    /* title */"PayJunction Password",
    /* key */"MODULE_PAYMENT_PAYJUNCTION_PASSWORD",
    /* value */"pj-ql-01p",
    /* description */ "The password used to access the PayJunction service",
    /* groupId */groupId, /* sort Order */i, /* useFun */"", /* setFun */"", /* dateAdd */now);

configs[i++] = new KKConfiguration(
    /* title */"Payment Server URL",
    /* key */"MODULE_PAYMENT_PAYJUNCTION_URL",
    /* value */"https://payjunction.com/quick_link",
    /* description */ "URL used by KonaKart to send the transaction details",
    /* groupId */groupId, /* sort Order */i, /* useFun */"", /* setFun */"", /* dateAdd */now);

configs[i++] = new KKConfiguration(
    /* title */"Security Options",
    /* key */"MODULE_PAYMENT_PAYJUNCTION_SECURITY",
    /* value */"AWZ|M|false|true|false",
    /* description */
       "Security Options for Pay Junction - refer to PayJunction documentation for details",
    /* groupId */groupId, /* sort Order */i, /* useFun */"", /* setFun */"", /* dateAdd */now);

configs[i++] = new KKConfiguration(
    /* title */"Debug Mode",
    /* key */"MODULE_PAYMENT_PAYJUNCTION_DEBUG_MODE",
    /* value */"true",
    /* description */ "If set to true, the PayJunction module will be set to debug code",
    /* groupId */groupId, /* sort Order */i, /* useFun */"",
    /* setFun */"choice('true', 'false')",
    /* dateAdd */now);

The above are quite a typical combination of fields. Let's look at each one in turn:

  • MODULE_PAYMENT_PAYJUNCTION_STATUS - for enabling/disabling the module. Typically provided for all modules.

  • MODULE_PAYMENT_PAYJUNCTION_SORT_ORDER - for order the module is displayed if there are more than one available. Typically provided for all modules

  • MODULE_PAYMENT_PAYJUNCTION_ZONE - for defining a zone where this payment module can be used. Typically provided for all modules.

  • MODULE_PAYMENT_PAYJUNCTION_USERNAME - username to access the payment gateway service. Often required for payment modules

  • MODULE_PAYMENT_PAYJUNCTION_PASSWORD - password to access the payment gateway service. Often required for payment modules

  • MODULE_PAYMENT_PAYJUNCTION_URL - the payment service URL. Often required for payment modules (useful for switching between the payment service's test and live services.

  • MODULE_PAYMENT_PAYJUNCTION_SECURITY - a special configuration option particular to PayJunction. Typically payment modules would have one or two of these, but they would be different between gateway services.

  • MODULE_PAYMENT_PAYJUNCTION_DEBUG_MODE - handy for diagnosing problems, especially in development. Typically provided for all modules

For "KonaPay" we will change the configuration parameter names to include "KONAPAY" rather than "PAYJUNCTION", so we would have "MODULE_PAYMENT_KONAPAY_STATUS" etc... We need to replace _PAYJUNCTION_ with _KONAPAY_ throughout all the source and properties files. For "KonaPay" we will assume, for simplicity that we will define the same set as is defined for PayJunction except the MODULE_PAYMENT_PAYJUNCTION_SECURITY variable which we'll exclude.

Understanding the Configuration Options

Looking more closely at the KKConfiguration structure that is initialized for each configuration object:


configs[i++] = new KKConfiguration(
    /* title */"Enable PayJunction Module",
    /* key */"MODULE_PAYMENT_PAYJUNCTION_STATUS",
    /* value */"true",
    /* description */ "Do you want to accept PayJunction payments? ('true' or 'false')",
    /* groupId */groupId,
    /* sort Order */i,
    /* useFun */"",
    /* setFun */ "choice('true', 'false')",
    /* dateAdd */now);

The comments should help a lot in understanding what these attributes are for. In more detail:

  • title - affects what's shown on the screen of the Admin App

  • key - the unique key by which this key is known

  • value - the default value

  • description - this is provided as floatover help in the Admin App

  • groupId - the groupId - this is 6 for payment modules

  • sortOrder - the order in which these keys are displayed in the Admin App

  • useFun - a function that defines how this key should be "used"

  • setFun - a function that defines how this key is "set"

  • dateAdd - the date the key is added to the database.

The only slightly complicated ones are the use and set functions. The example above shows the setFun to use to display a true/false toggle in the Admin App. The setFun and useFun can be null for most simple text fields. These setFun and useFun formats are used throughout the Admin App and perhaps the easiest way to see how they work is to look at these columns in the database for each configuration key that uses them.. eg: Issue "SELECT configuration_title, configuration_key, use_function, set_function FROM configuration;" against your database to see lots of examples.

** Ask questions to support@konakart.com (if you have a support contract) or in the forum if you're uncertain about these.

Add the "KonaPay" gateway to the Admin App

In order to see the "KonaPay" payment gateway module in the Admin App we have to add it to the list of payment modules in webapps/konakartadmin/WEB-INF/classes/konakartadmin.properties. Remember to match the classname - so be careful with your lower case/upper case characters! Once set, the Admin App will inclde the "KonaPay" module in the set of modules that can be installed or removed from the system: (Konapay has been added in bold below):


# ------------------------------------------------------------------
# Set the class names of the various modules you would like to make 
# available. The administrator can still choose to enable or disable
# these.
#
# Note that if you remove a module from the definitions below that 
# has already been set up in the database the users may still have 
# access to the modules in the konakart application.   Hence, it is
# advisable to remove the modules before they are removed from these
# definitions.

# Make these space or semi-colon-separated class names - they have
# implied prefixes of:
#
#     com.konakartadmin.modules.payment.{lower case module name}.
#     com.konakartadmin.modules.shipping.{lower case module name}.
#     com.konakartadmin.modules.orderTotal.{lower case module name}.

konakart.modules.payment=Authorizenet Yourpay Payjunction Konapay
konakart.modules.shipping=Flat Item Table Zones Free DigitalDownload
konakart.modules.ordertotal=Tax Total ProductDiscount TotalDiscount 

Implement the PaymentInterface

Next we need to implement the PaymentInterface in our Konapay class in the com.konakart.bl.modules.payment.konapay package. If the PayJunction directories were copied correctly earlier the java file should be at custom/modules/src/com/konakart/bl/modules/payment/konapay

This Konapay.java source extends BasePaymentModule (whose source is provided at custom/appn/src/com/konakart/bl/modules/payment/BasePaymentModule.java) and implements PaymentInterface.

It's advisable to follow the pattern you see in the file you copied. Therefore, rename all the constants and variables to names appropriate for "KonaPay" and set up the PaymentDetails object in a similar way in getPaymentDetails().

For much of the PaymentDetails, the values set will be very similar between payment modules (eg. the payment module code, the sort order, title and description etc) but there will always be differences in the "NameValue[]" parameters that are added further down in the getPaymentDetails() method.

NameValue[] Parameters

Each payment gateway will need different parameter names so to work out which NameValue pairs you need to add you need to study the API specification. With PayJunction, you can see that the following are required: dc_logon, dc_password, dc_transaction_type etc. With "KonaPay" there will be different names and perhaps different encoding rules; the API documentation will define the requirements. Check the examples in the gateway documentation, these often make it clear what the names and values should be. Once the parameters are understood, add these to the PaymentDetails - they will be used later to build the request to the gateway supplier.

Implement the Action code

Next we need to implement the Action code in our KonapayAction class in the com.konakart.actions.gateways package. The "KonaPay" java file should be called custom/modules/src/com/konakart/actions/gateways/KonapayAction.java. (Notice that the (usually synchronous) modules which collect the payment information from the user on the KonaKart site have to have "com.konakart.actions.gateways" implementations, whereas the (asynchronous) modules that divert the user off to a page on the gateway's site have to have "com.konakart.actions.ipn" implementations).

This Konapay.java source extends BasePaymentModule (whose source is provided at custom/appn/src/com/konakart/bl/modules/payment/BasePaymentModule.java) and implements PaymentInterface.

It's advisable to follow the pattern in the file you copied from PayJunction. Therefore, rename all the constants and variables to names appropriate for "KonaPay" and set up the PaymentDetails object in a similar way in getPaymentDetails().

The Action files are quite well documented so it should be easy to see where to make modifications for a new module. Basically you will have to create your message to post to the gateway then process the returned information to determine success or failure. If there's a failure you can return the user to the credit card screen to try again (and show the error message that you received). Alternatively, for more serious errors, you can show the exception in a standard form for the KonaKart site.

Save IPN details

In order for your KonaKart administrator to diagnose payment gateway problems in the future, it's a good idea to save details of each transaction with a call to "saveIpnHistory" (see kkAppEng.getEng().saveIpnHistory()). Set the various attributes on IpnHistoryIf to values that you derive from the response as you see in the example source. These records are available in the KonaKart Admin App for inspection and can be useful when diagnosing problems with payment gateways. The records can also be useful in recording unique transaction codes that are provided by the gateway suppliers for proving that payments have been made, should that need ever arise.

Save the gateway response to a file

Another technique that you can use to diagnose problems is to save gateway responses to a file on disk. An example of such code is in the YourpayAction source as follows:


if (yourPayDebugMode)
{
    // Write the response to an HTML file which is handy for
    // diagnosing problems

    try
    {
        String outputFilename = getLogFileDirectory() 
                + "yourpay_resp_"
                + order.getId()
                + ".html";
        File myOutFile = new File(outputFilename);
        if (log.isDebugEnabled())
        {
            log.debug("Write gateway response to " + myOutFile.getAbsolutePath());
        }
        BufferedWriter bw = new BufferedWriter(new FileWriter(myOutFile));
        bw.write(gatewayResp);
        bw.close();
    } catch (Exception e)
    {
        // dump the exception and continue
        e.printStackTrace();
    }
}

Note that the yourPayDebugMode boolean could be set by one of your module configuration parameters and the log file location that is returned by the getLogFileDirectory() call is defined in the Admin Application under the Configuration >> Logging section.

Send payment confirmation email

You have a choice whether or not to send payment confirmation mails in the Action class. (You are also free to change the format of these emails if you wish by modifying the velocity templates). The call for sending emails is simply kkAppEng.getEng().sendOrderConfirmationEmail().

Struts mapping

This tutorial was written for a Struts-1 MVC implementation but the concepts are the same for Struts-2. The easiest way to understand what you have to do is to copy the Actions and structs.xml section for the payment gateway you are using as a template for KonaPay.

More often that not the Struts mapping code in the Action class will be the same for all the synchronous (or "server-side") payment modules. Each "mapping string" in the mapping.forward("mapping string") calls must match the struts-config.xml file as follows: The entry for "KonaPay" has been added below:


<!-- ================================= Server Side Gateways -->
 <action path="/USAePay" 
    type="com.konakart.actions.gateways.UsaepayAction">
     <forward name="Approved" path="/CheckoutFinished.do"/>
     <forward name="TryAgain" path="/CheckoutServerPayment.do"/>
     <forward name="NotLoggedIn" path="/CheckoutDelivery.do"/>
 </action>

 <action path="/AuthorizeNet" 
    type="com.konakart.actions.gateways.AuthorizenetAction">
     <forward name="Approved" path="/CheckoutFinished.do"/>
     <forward name="TryAgain" path="/CheckoutServerPayment.do"/>
     <forward name="NotLoggedIn" path="/CheckoutDelivery.do"/>
 </action>

 <action path="/PayJunction" 
    type="com.konakart.actions.gateways.PayjunctionAction">
     <forward name="Approved" path="/CheckoutFinished.do"/>
     <forward name="TryAgain" path="/CheckoutServerPayment.do"/>
     <forward name="NotLoggedIn" path="/CheckoutDelivery.do"/>
 </action>

 <action path="/KonaPay" 
    type="com.konakart.actions.gateways.KonapayAction">
     <forward name="Approved" path="/CheckoutFinished.do"/>
     <forward name="TryAgain" path="/CheckoutServerPayment.do"/>
     <forward name="NotLoggedIn" path="/CheckoutDelivery.do"/>
 </action>

 <action path="/YourPay" 
    type="com.konakart.actions.gateways.YourpayAction">
     <forward name="Approved" path="/CheckoutFinished.do"/>
     <forward name="TryAgain" path="/CheckoutServerPayment.do"/>
     <forward name="NotLoggedIn" path="/CheckoutDelivery.do"/>
 </action>

One other change is required in struts-config.xml (Struts-1) which defines the forwarding for the server-side payments: It's critical to maintain naming consistency here (use lower or mixed case as in the other examples - or whatever it takes to match your definitions elsewhere).


<action path="/CheckoutServerPaymentSubmit"
      type="com.konakart.actions.CheckoutServerPaymentSubmitAction"
      name="CreditCardForm" scope="request" validate="true"
      input="/CheckoutServerPayment.do">
    <forward name="usaepay" path="/USAePay.do"/>
    <forward name="authorizenet" path="/AuthorizeNet.do"/>
    <forward name="payjunction" path="/PayJunction.do"/>
    <forward name="konapay" path="/KonaPay.do"/>
    <forward name="yourpay" path="/YourPay.do"/>
</action>

Build, Deploy and Test

Once the java code is built, the properties files and the struts-config.xml have been updated, all that is required to be done is to execute the ant build, copy the new jars into position, restart tomcat and test!

The gateway suppliers typically provide test credit card numbers for you to use for these tests.

If you have questions on customizing KonaKart please post these on our forum , at http://www.konakart.com/forum