com.konakart.actions.ipn.BarclaycardSmartPayApiNotificationAction.java Source code

Java tutorial

Introduction

Here is the source code for com.konakart.actions.ipn.BarclaycardSmartPayApiNotificationAction.java

Source

//
// (c) 2006 DS Data Systems UK Ltd, All rights reserved.
//
// DS Data Systems and KonaKart and their respective logos, are 
// trademarks of DS Data Systems UK Ltd. All rights reserved.
//
// The information in this document is free software; you can redistribute 
// it and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// Original version contributed by Chris Derham (Atomus Ltd)
//

package com.konakart.actions.ipn;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.ServletActionContext;

import com.konakart.actions.gateways.BaseGatewayAction;
import com.konakart.al.KKAppEng;
import com.konakart.app.IpnHistory;
import com.konakart.app.KKException;
import com.konakart.appif.IpnHistoryIf;
import com.konakart.appif.SSOTokenIf;
import com.konakart.bl.modules.payment.barclaycardsmartpayapi.BarclaycardSmartPayApi;
import com.konakart.bl.modules.payment.barclaycardsmartpayhosted.BarclaycardSmartPayHosted;

/**
 * This class is an Action class for what to do when a payment notification callback is received
 * from Barclaycard SmartPay Api.
 */
public class BarclaycardSmartPayApiNotificationAction extends BaseGatewayAction {
    /**
     * The <code>Log</code> instance for this application.
     */
    protected Log log = LogFactory.getLog(BarclaycardSmartPayApiNotificationAction.class);

    // Return codes and descriptions
    private static final int RET0 = 0;

    private static final String RET0_DESC = "Transaction OK";

    private static final int RET2 = -2;

    private static final String RET2_DESC = "HTTP Authentication Failed";

    private static final int RET3 = -3;

    private static final String RET3_DESC = "Unable to obtain order number";

    private static final int RET4 = -4;

    private static final String RET4_DESC = "There has been an unexpected exception. Please look at the log.";

    private static final long serialVersionUID = 1L;

    public String execute() {
        HttpServletRequest request = ServletActionContext.getRequest();
        HttpServletResponse response = ServletActionContext.getResponse();

        String httpAuthStr = null;
        String httpUsername;
        String httpPassword;
        String pspReference = null;
        String merchantReference = null;
        String merchantAccountCode = null;
        String eventDate = null;
        String successString = null;
        boolean success = false;
        String paymentMethod = null;
        String value = null;
        String currency = null;
        String reason = null;

        String eventCode = null;
        String status = null;

        String sessionId = null;
        KKAppEng kkAppEng = null;

        if (log.isDebugEnabled()) {
            log.debug(BarclaycardSmartPayApi.BC_SPAY_API_GATEWAY_CODE + " Notification Action");
        }

        // Create these outside of try / catch since they are needed in the case of a general
        // exception
        IpnHistoryIf ipnHistory = new IpnHistory();
        ipnHistory.setOrderId(-1);
        ipnHistory.setModuleCode(BarclaycardSmartPayApi.BC_SPAY_API_GATEWAY_CODE);

        try {
            // Process the parameters sent in the callback
            StringBuffer sb = new StringBuffer();
            if (request != null) {
                Enumeration<String> en = request.getParameterNames();
                while (en.hasMoreElements()) {
                    String paramName = en.nextElement();
                    String paramValue = request.getParameter(paramName);
                    if (sb.length() > 0) {
                        sb.append("\n");
                    }
                    sb.append(paramName);
                    sb.append(" = ");
                    sb.append(paramValue);

                    // Capture important variables so that we can determine whether the transaction
                    // was successful
                    if (paramName != null) {
                        if (paramName.equalsIgnoreCase("eventCode")) {
                            eventCode = paramValue;
                        } else if (paramName.equalsIgnoreCase("pspReference")) {
                            pspReference = paramValue;
                        } else if (paramName.equalsIgnoreCase("merchantReference")) {
                            merchantReference = paramValue;
                        } else if (paramName.equalsIgnoreCase("merchantAccountCode")) {
                            merchantAccountCode = paramValue;
                        } else if (paramName.equalsIgnoreCase("eventDate")) {
                            eventDate = paramValue;
                        } else if (paramName.equalsIgnoreCase("success")) {
                            successString = paramValue;
                            success = Boolean.valueOf(successString);
                        } else if (paramName.equalsIgnoreCase("paymentMethod")) {
                            paymentMethod = paramValue;
                        } else if (paramName.equalsIgnoreCase("value")) {
                            value = paramValue;
                        } else if (paramName.equalsIgnoreCase("currency")) {
                            currency = paramValue;
                        } else if (paramName.equalsIgnoreCase("reason")) {
                            reason = paramValue;
                        }
                    }
                }
            }

            if (log.isDebugEnabled()) {
                log.debug(BarclaycardSmartPayApi.BC_SPAY_API_GATEWAY_CODE + " Raw Notification Data:\n"
                        + sb.toString());
                log.debug("\n    merchantAccountCode       = " + merchantAccountCode + "\n"
                        + "    eventCode                 = " + eventCode + "\n" + "    eventDate                 = "
                        + eventDate + "\n" + "    merchantReference         = " + merchantReference + "\n"
                        + "    pspReference              = " + pspReference + "\n"
                        + "    paymentMethod             = " + paymentMethod + "\n"
                        + "    amount                    = " + value + "\n" + "    currency                  = "
                        + currency + "\n" + "    success                   = " + successString + "\n"
                        + "    reason                    = " + reason);
            }

            // If we didn't receive an eventCode, we log a warning and return
            if (eventCode == null) {
                log.warn("No eventCode returned by " + BarclaycardSmartPayApi.BC_SPAY_API_GATEWAY_CODE);
                return null;
            }

            status = eventCode;
            if (eventCode.equals("AUTHORISATION")) {
                if (success) {
                    status += " successful";
                } else {
                    status += " unsuccessful";
                }
            }

            // Fill more details of the IPN history class
            ipnHistory.setGatewayResult(status);
            ipnHistory.setGatewayFullResponse(sb.toString());
            ipnHistory.setGatewayTransactionId(pspReference);

            /*
             * Get the uuid from the request so that we can look up the SSO Token
             */
            if (merchantReference == null) {
                throw new Exception(
                        "The callback from BarclaycardSmartPayApi did not contain the 'merchantReference' parameter.");
            }

            // Get an instance of the KonaKart engine and look up the token
            kkAppEng = this.getKKAppEng(request, response);
            SSOTokenIf token = kkAppEng.getEng().getSSOToken(merchantReference, /* deleteToken */
                    true);
            if (token == null) {
                throw new Exception("The SSOToken from the BarclaycardSmartPayApi callback is null");
            }

            /*
             * Use the session of the logged in user to initialise kkAppEng
             */
            try {
                kkAppEng.getEng().checkSession(token.getSessionId());
            } catch (KKException e) {
                throw new Exception(
                        "The SessionId from the SSOToken in the BarclaycardSmartPayApi Callback is not valid: "
                                + token.getSessionId());
            }

            // Log in the user
            kkAppEng.getCustomerMgr().loginBySession(token.getSessionId());
            sessionId = token.getSessionId();

            /*
             * Get the parameters from the token
             */
            String custom1 = token.getCustom1();
            String[] custom1Array = custom1.split("~");
            if (custom1Array == null || custom1Array.length != 3) {
                throw new Exception("Custom1 field of token doesn't contain expected data: " + token.getCustom1());
            }
            httpAuthStr = custom1Array[0];
            int orderId = Integer.parseInt(custom1Array[1]);
            String countryCode = custom1Array[2];
            httpUsername = token.getCustom2();
            httpPassword = token.getCustom3();

            if (countryCode == null) {
                log.warn("CountryCode not returned in the " + BarclaycardSmartPayHosted.BC_SPAY_HOSTED_GATEWAY_CODE
                        + " response");
            }

            ipnHistory.setOrderId(orderId);

            // Do HTTP Authentication if required
            if (httpAuthStr != null && Boolean.valueOf(httpAuthStr)) {
                // Get Authorization header
                String auth = null;

                if (request != null) {
                    auth = request.getHeader("Authorization");
                }

                // Do we allow that user?
                if (!allowUser(auth, httpUsername, httpPassword)) {
                    // Not allowed, so return "unauthorized"
                    response.setContentType("text/plain");
                    response.setHeader("WWW-Authenticate", "BASIC realm=\"Protected Page\"");
                    response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                    log.warn("Notification from " + BarclaycardSmartPayApi.BC_SPAY_API_GATEWAY_CODE
                            + " could not be Authenticated");

                    ipnHistory.setKonakartResultDescription(RET2_DESC);
                    ipnHistory.setKonakartResultId(RET2);
                    kkAppEng.getEng().saveIpnHistory(sessionId, ipnHistory);
                    return null;
                }
            }

            if (log.isDebugEnabled()) {
                log.debug("Accept Notification for " + BarclaycardSmartPayApi.BC_SPAY_API_GATEWAY_CODE);
            }

            // We always accept the Notification if we get this far
            response.setContentType("text/plain");
            response.getWriter().print("[accepted]\n");

            if (orderId < 0) {
                ipnHistory.setKonakartResultDescription(RET3_DESC);
                ipnHistory.setKonakartResultId(RET3);
                kkAppEng.getEng().saveIpnHistory(sessionId, ipnHistory);
                return null;
            }

            // If it's not an AUTHORISATION event, we just throw it away
            if (!eventCode.equals("AUTHORISATION")) {
                if (log.isInfoEnabled()) {
                    log.info("'" + eventCode + "' notification sent from "
                            + BarclaycardSmartPayApi.BC_SPAY_API_GATEWAY_CODE + " discarded");
                }
                return null;
            }

            // If we're about to set the order status to the current value we'll assume this is a
            // duplicate Notification from Barclaycard and not do any updates

            int currentOrderStatus = kkAppEng.getEng().getOrderStatus(sessionId, orderId);

            if (log.isDebugEnabled()) {
                log.debug("currentOrderStatus for orderId " + orderId + " = " + currentOrderStatus);
            }

            if ((success && currentOrderStatus == com.konakart.bl.OrderMgr.PAYMENT_RECEIVED_STATUS)
                    || (!success && currentOrderStatus == com.konakart.bl.OrderMgr.PAYMENT_DECLINED_STATUS)) {
                if (log.isDebugEnabled()) {
                    log.debug("Possible Duplicate '" + eventCode + "' notification sent from "
                            + BarclaycardSmartPayApi.BC_SPAY_API_GATEWAY_CODE + " discarded");
                }
                return null;
            }

            ipnHistory.setKonakartResultDescription(RET0_DESC);
            ipnHistory.setKonakartResultId(RET0);
            kkAppEng.getEng().saveIpnHistory(sessionId, ipnHistory);

            return null;

        } catch (Exception e) {
            try {
                if (sessionId != null) {
                    ipnHistory.setKonakartResultDescription(RET4_DESC);
                    ipnHistory.setKonakartResultId(RET4);
                    if (kkAppEng != null) {
                        kkAppEng.getEng().saveIpnHistory(sessionId, ipnHistory);
                    }
                }
            } catch (KKException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
            return null;
        } finally {
            if (sessionId != null && kkAppEng != null) {
                try {
                    kkAppEng.getEng().logout(sessionId);
                } catch (KKException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Checks the user information sent in the Authorization header to see if the user is allowed
     * 
     * @param auth
     * @param username
     * @param password
     * @return true if the user is authenticated, otherwise false
     */
    protected boolean allowUser(String auth, String username, String password) {
        if (auth == null) {
            return false; // credentials are missing
        }

        if (!auth.toUpperCase().startsWith("BASIC ")) {
            return false; // we only do BASIC
        }

        // Get encoded user and password, comes after "BASIC "
        String userpassEncoded = auth.substring(6);

        // Decode it
        String userpassDecoded = new String(Base64.decodeBase64(userpassEncoded.getBytes()));

        if (log.isDebugEnabled()) {
            log.debug("auth credentials decoded = "
                    + userpassDecoded.substring(0, Math.min(userpassDecoded.length(), 4)) + "*****");
            log.debug("stored credentials       = " + username.substring(0, Math.min(username.length(), 4))
                    + "*****");
        }

        // Check our stored username and password to see if this user and password are "allowed"
        if ((username + ":" + password).equals(userpassDecoded)) {
            return true;
        }

        return false;
    }
}