package org.onehippo.forge.konakart.hst.components; import com.konakart.al.KKAppEng; import com.konakart.al.KKAppException; import com.konakart.app.EmailOptions; import com.konakart.app.IpnHistory; import com.konakart.app.KKException; import com.konakart.app.NameValue; import com.konakart.appif.*; import com.konakart.bl.ConfigConstants; import com.konakart.util.Utils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; /** * Base call back class for each gateway used for the payment connection. */ public abstract class KKGatewayCallBackComponent extends KKHstActionComponent { /** * Validate the order and ensure that we are using the correct payment gateway * * @param order the order to validate * @param code the module name must be the same as the class name although it can be all in lowercase in order * @throws com.konakart.al.KKAppException */ protected void validateOrder(OrderIf order, String code) throws KKAppException { if (order == null) { throw new KKAppException("There is no order."); } if (order.getPaymentDetails() == null) { throw new KKAppException("There is no PaymentDetails object attached to the order."); } if (order.getPaymentDetails().getCode() == null) { throw new KKAppException( "The PaymentDetails object contains a null code so we cannot determine which payment gateway to use."); } if (!order.getPaymentDetails().getCode().equals(code)) { throw new KKAppException("The PaymentDetails object contains the gateway code: " + order.getPaymentDetails().getCode() + " which does not equal the code of the gateway being used: " + code); } } /** * Sends data to the payment gateway via a POST. Parameters are received from the PaymentDetails * object and the credit card parameters that have just been input by the customer are sent in a * separate list; the ccParmList. Any miscellaneous parameters can also be added to the * ccParmList if required. * * @param pd the PaymentDetails object * @param ccParmList the credit card parameters * @return The response to the post * @throws java.io.IOException */ public String postData(PaymentDetailsIf pd, List<NameValueIf> ccParmList) throws IOException { URL url = new URL(pd.getRequestUrl()); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setDoInput(true); if (pd.getReferrer() != null && pd.getReferrer().length() > 1) { connection.setRequestProperty("Referer", pd.getReferrer()); } // This is the one that should be used from v5.5.0.0 customizeConnection(connection, pd, ccParmList); PrintWriter out = new PrintWriter(connection.getOutputStream()); StringBuffer sb = getGatewayRequest(pd, ccParmList); if (log.isDebugEnabled()) { log.debug("Post URL = " + pd.getRequestUrl()); log.debug("Post string =\n" + sb.toString()); String logStr = ""; int padding = 25; if (pd.getParameters() != null) { for (int i = 0; i < pd.getParameters().length; i++) { NameValueIf nv = pd.getParameters()[i]; logStr += "\n " + Utils.padRight(nv.getName(), padding) + " = " + nv.getValue(); } } if (ccParmList != null) { for (NameValueIf nv : ccParmList) { logStr += "\n " + Utils.padRight(nv.getName(), padding) + " = " + nv.getValue(); } } log.debug("Post source data:" + logStr); } // Send the message out.print(sb.toString()); out.close(); // Get back the response StringBuilder respSb = new StringBuilder(); BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line = in.readLine(); while (line != null) { respSb.append(line); line = in.readLine(); } in.close(); return respSb.toString(); } /** * Sends data to the payment gateway via a GET. Parameters are received from the PaymentDetails * object and the credit card parameters that have just been input by the customer are send in a * separate list; the ccParmList * * @param pd the PaymentDetails object * @param ccParmList the credit card parameters * @return The response to the post * @throws java.io.IOException */ public String getData(PaymentDetailsIf pd, List<NameValueIf> ccParmList) throws IOException { // Construct data for GET String urlStr = pd.getRequestUrl(); StringBuffer sbRequest = getGatewayRequest(pd, ccParmList); if (log.isDebugEnabled()) { log.debug("GET URL = " + urlStr + sbRequest.toString()); } URL url = new URL(urlStr + sbRequest.toString()); // Send data URLConnection conn = url.openConnection(); // Get the response BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); StringBuilder sbReply = new StringBuilder(); String line; while ((line = rd.readLine()) != null) { sbReply.append(line); } rd.close(); return sbReply.toString(); } /** * This method can be specialized in the super class to customize the format of the request * * @param pd the PaymentDetails * @param ccParmList the credit card parameters * @return a StringBuffer containing the gateway request */ protected StringBuffer getGatewayRequest(PaymentDetailsIf pd, List<NameValueIf> ccParmList) { // Create the message from the parameters in the PaymentDetails object StringBuffer sb = new StringBuffer(); for (int i = 0; i < pd.getParameters().length; i++) { NameValueIf nv = pd.getParameters()[i]; if (i > 0) { sb.append("&"); } sb.append(nv.getName()); sb.append("="); sb.append(nv.getValue()); } // Add the credit card parameters for (NameValueIf nv : ccParmList) { sb.append("&"); sb.append(nv.getName()); sb.append("="); sb.append(nv.getValue()); } if (log.isDebugEnabled()) { log.debug("GatewayRequest = \n" + sb); } return sb; } /** * @param kkAppEng the konakart engine * @return Returns the logFileDirectory. We look it up every time. */ public String getLogFileDirectory(KKAppEng kkAppEng) { String conf = kkAppEng.getConfig(ConfigConstants.KONAKART_LOG_FILE_DIRECTORY); if (conf != null) { return conf + System.getProperty("file.separator"); } return ""; } /** * This method is optionally called from the sub class to load up the parameters into a * HashTable for efficient subsequent processing * * @param pd PaymentDetails object * @param ccParmList name value pair list of CC parameters * @return a hash map containing the parameters for rapid lookup */ protected HashMap<String, String> hashParameters(PaymentDetailsIf pd, List<NameValueIf> ccParmList) { HashMap<String, String> paramHash = new HashMap<String, String>(); if (pd != null) { for (int c = 0; c < pd.getParameters().length; c++) { paramHash.put(pd.getParameters()[c].getName(), pd.getParameters()[c].getValue()); } } if (ccParmList != null) { for (NameValueIf aCcParmList : ccParmList) { paramHash.put(aCcParmList.getName(), aCcParmList.getValue()); } } return paramHash; } /** * Get the value of the parameter with the specified name from the PaymentDetails object * * @param paramName parameter name to look up * @param pd PaymentDetails object * @return value of the parameter or null if the parameter is not found */ protected String getParameterFromPaymentDetails(String paramName, PaymentDetailsIf pd) { String value = null; if (pd != null && pd.getParameters() != null) { for (int c = 0; c < pd.getParameters().length; c++) { if (paramName.equals(pd.getParameters()[c].getName())) { return pd.getParameters()[c].getValue(); } } } return value; } /** * This method is normally specialized in the sub class to customize the connection * * @param connection the HTTP connection object * @param pd the Payment Details * @param paramList Additional parameters (typically credit card details but can also be any * miscellaneous extra parameters that may be required) */ protected abstract void customizeConnection(HttpURLConnection connection, PaymentDetailsIf pd, List<NameValueIf> paramList); /** * Send an order confirmation eMail. The template used is different if the order is successful * or not. * * @param kkAppEng the konakart app engine * @param orderId the order id * @param success true if the order is successful, false otherwise * @throws com.konakart.app.KKException . */ protected void sendOrderConfirmationMail(KKAppEng kkAppEng, int orderId, boolean success) throws KKException { String countryCode = kkAppEng.getLocale().substring(0, 2); sendOrderConfirmationMail(kkAppEng.getEng(), kkAppEng.getSessionId(), countryCode, orderId, success); } /** * Send an order confirmation eMail. The template used is different if the order is successful * or not. * * @param eng the engine * @param sessionId the Konakart session id * @param countryCode the country code * @param orderId the order id * @param success true if the order is successful, false otherwise * @throws com.konakart.app.KKException . */ protected void sendOrderConfirmationMail(KKEngIf eng, String sessionId, String countryCode, int orderId, boolean success) throws KKException { EmailOptionsIf options = new EmailOptions(); // Default behaviour is not to create or attach the PDF invoice options.setCreateInvoice(false); options.setAttachInvoice(false); options.setCountryCode(countryCode); if (success) { options.setTemplateName("OrderConfPaymentSuccess"); // Attach the invoice to the confirmation email (Enterprise Only). Defaults to false. // options.setAttachInvoice(true); // Create the invoice (if not already present) for attaching to the confirmation email // (Enterprise Only). Defaults to false. // options.setCreateInvoice(true); } else { options.setTemplateName("OrderConfPaymentFailure"); } eng.sendOrderConfirmationEmail1(sessionId, orderId, /* langIdForOrder */ -1, options); } /** * Saves an IPN History record. The KKAppEng should have a logged-in user with a valid * sessionId. * * @param kkAppEng the KKAppEng application engine which should contain a valid sessionId * @param orderId the orderId involved in the transaction * @param moduleCode the code of the module * @param gatewayFullResponse full response from the gateway * @param gatewayResult summary gateway response * @param gatewayTransactionId an Id from the gateway that identifies the transaction * @param konakartResultDescription KonaKart result description * @param konakartResultId KonaKart result code * @throws com.konakart.app.KKException */ protected void saveIPNrecord(KKAppEng kkAppEng, int orderId, String moduleCode, String gatewayFullResponse, String gatewayResult, String gatewayTransactionId, String konakartResultDescription, int konakartResultId) throws KKException { try { IpnHistoryIf ipnHistory = new IpnHistory(); ipnHistory.setOrderId(orderId); ipnHistory.setModuleCode(moduleCode); ipnHistory.setDateAdded(new GregorianCalendar()); ipnHistory.setGatewayFullResponse(gatewayFullResponse); ipnHistory.setGatewayResult(Utils.trim(gatewayResult, 128)); ipnHistory.setGatewayTransactionId(gatewayTransactionId); ipnHistory.setKonakartResultDescription(Utils.trim(konakartResultDescription, 255)); ipnHistory.setKonakartResultId(konakartResultId); ipnHistory.setCustomerId(kkAppEng.getCustomerMgr().getCurrentCustomer().getId()); kkAppEng.getEng().saveIpnHistory(kkAppEng.getSessionId(), ipnHistory); } catch (KKException e) { log.warn("Exception occured trying to save IPN History record: " + e.getMessage()); throw e; } } /** * Add more parameters to the PaymentDetails object. Normally this method would be on the * PaymentDetails class but it's placed here instead because of the automatic code generation * that occurs on the PaymentDetails class for web services, JSON and RMI etc. * * @param pd the PaymentDetails object * @param newParameters The parameters to set. */ protected void addParameters(PaymentDetailsIf pd, List<NameValueIf> newParameters) { List<NameValueIf> parmList = new ArrayList<NameValueIf>(); // Add the new parameters to our temporary list if (newParameters != null) { for (NameValueIf newParameter : newParameters) { parmList.add(newParameter); } } // Add the existing parameters to our temporary list if (pd.getParameters() != null) { for (int p = 0; p < pd.getParameters().length; p++) { parmList.add(pd.getParameters()[p]); } } // Now replace the parameters with a new set NameValueIf[] nvArray = new NameValue[parmList.size()]; parmList.toArray(nvArray); pd.setParameters(nvArray); } }