/******************************************************************************* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. *******************************************************************************/ package org.apache.ofbiz.accounting.thirdparty.gosoftware; import java.io.IOException; import java.math.BigDecimal; import java.sql.Timestamp; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import org.apache.ofbiz.accounting.payment.PaymentGatewayServices; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.GeneralException; import org.apache.ofbiz.base.util.StringUtil; import org.apache.ofbiz.base.util.UtilMisc; import org.apache.ofbiz.base.util.UtilNumber; import org.apache.ofbiz.base.util.UtilProperties; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.entity.Delegator; import org.apache.ofbiz.entity.GenericEntityException; import org.apache.ofbiz.entity.GenericValue; import org.apache.ofbiz.entity.util.EntityQuery; import org.apache.ofbiz.entity.util.EntityUtilProperties; import org.apache.ofbiz.service.DispatchContext; import org.apache.ofbiz.service.GenericServiceException; import org.apache.ofbiz.service.LocalDispatcher; import org.apache.ofbiz.service.ServiceUtil; public class RitaServices { public static final String module = RitaServices.class.getName(); private static int decimals = UtilNumber.getBigDecimalScale("invoice.decimals"); private static int rounding = UtilNumber.getBigDecimalRoundingMode("invoice.rounding"); public final static String resource = "AccountingUiLabels"; public static final String resourceOrder = "OrderUiLabels"; public static Map<String, Object> ccAuth(DispatchContext dctx, Map<String, ? extends Object> context) { Locale locale = (Locale) context.get("locale"); Delegator delegator = dctx.getDelegator(); Properties props = buildPccProperties(context, delegator); RitaApi api = getApi(props, "CREDIT"); if (api == null) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingRitaErrorGettingPaymentGatewayConfig", locale)); } try { RitaServices.setCreditCardInfo(api, dctx.getDelegator(), context); } catch (GeneralException e) { return ServiceUtil.returnError(e.getMessage()); } // basic tx info api.set(RitaApi.TRANS_AMOUNT, getAmountString(context, "processAmount")); api.set(RitaApi.INVOICE, context.get("orderId")); // command setting if ("1".equals(props.getProperty("autoBill"))) { // sale api.set(RitaApi.COMMAND, "SALE"); } else { // pre-auth api.set(RitaApi.COMMAND, "PRE_AUTH"); } // send the transaction RitaApi out = null; try { Debug.logInfo("Sending request to RiTA", module); out = api.send(); } catch (IOException e) { Debug.logError(e, module); return ServiceUtil.returnError(e.getMessage()); } catch (GeneralException e) { Debug.logError(e, module); return ServiceUtil.returnError(e.getMessage()); } if (out != null) { Map<String, Object> result = ServiceUtil.returnSuccess(); String resultCode = out.get(RitaApi.RESULT); boolean passed = false; if ("CAPTURED".equals(resultCode)) { result.put("authResult", Boolean.TRUE); result.put("captureResult", Boolean.TRUE); passed = true; } else if ("APPROVED".equals(resultCode)) { result.put("authCode", out.get(RitaApi.AUTH_CODE)); result.put("authResult", Boolean.TRUE); passed = true; } else if ("PROCESSED".equals(resultCode)) { result.put("authResult", Boolean.TRUE); } else { result.put("authResult", Boolean.FALSE); } result.put("authRefNum", out.get(RitaApi.INTRN_SEQ_NUM) != null ? out.get(RitaApi.INTRN_SEQ_NUM) : ""); result.put("processAmount", context.get("processAmount")); result.put("authCode", out.get(RitaApi.AUTH_CODE)); result.put("authFlag", out.get(RitaApi.REFERENCE)); result.put("authMessage", out.get(RitaApi.RESULT)); result.put("cvCode", out.get(RitaApi.CVV2_CODE)); result.put("avsCode", out.get(RitaApi.AVS_CODE)); if (!passed) { String respMsg = out.get(RitaApi.RESULT) + " / " + out.get(RitaApi.INTRN_SEQ_NUM); result.put("customerRespMsgs", UtilMisc.toList(respMsg)); } if (result.get("captureResult") != null) { result.put("captureCode", out.get(RitaApi.AUTH_CODE)); result.put("captureFlag", out.get(RitaApi.REFERENCE)); result.put("captureRefNum", out.get(RitaApi.INTRN_SEQ_NUM)); result.put("captureMessage", out.get(RitaApi.RESULT)); } return result; } else { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingRitaResultIsNull", locale)); } } public static Map<String, Object> ccCapture(DispatchContext dctx, Map<String, ? extends Object> context) { GenericValue orderPaymentPreference = (GenericValue) context.get("orderPaymentPreference"); Locale locale = (Locale) context.get("locale"); Delegator delegator = dctx.getDelegator(); //lets see if there is a auth transaction already in context GenericValue authTransaction = (GenericValue) context.get("authTrans"); if (authTransaction == null) { authTransaction = PaymentGatewayServices.getAuthTransaction(orderPaymentPreference); } if (authTransaction == null) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingPaymentTransactionAuthorizationNotFoundCannotCapture", locale)); } // setup the RiTA Interface Properties props = buildPccProperties(context, delegator); RitaApi api = getApi(props, "CREDIT"); if (api == null) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingRitaErrorGettingPaymentGatewayConfig", locale)); } api.set(RitaApi.ORIG_SEQ_NUM, authTransaction.getString("referenceNum")); api.set(RitaApi.COMMAND, "COMPLETION"); // send the transaction RitaApi out = null; try { out = api.send(); } catch (IOException e) { Debug.logError(e, module); return ServiceUtil.returnError(e.getMessage()); } catch (GeneralException e) { Debug.logError(e, module); return ServiceUtil.returnError(e.getMessage()); } if (out != null) { Map<String, Object> result = ServiceUtil.returnSuccess(); String resultCode = out.get(RitaApi.RESULT); if ("CAPTURED".equals(resultCode)) { result.put("captureResult", Boolean.TRUE); } else { result.put("captureResult", Boolean.FALSE); } result.put("captureAmount", context.get("captureAmount")); result.put("captureRefNum", out.get(RitaApi.INTRN_SEQ_NUM) != null ? out.get(RitaApi.INTRN_SEQ_NUM) : ""); result.put("captureCode", out.get(RitaApi.AUTH_CODE)); result.put("captureFlag", out.get(RitaApi.REFERENCE)); result.put("captureMessage", out.get(RitaApi.RESULT)); return result; } else { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingRitaResultIsNull", locale)); } } public static Map<String, Object> ccVoidRelease(DispatchContext dctx, Map<String, ? extends Object> context) { return ccVoid(dctx, context, false); } public static Map<String, Object> ccVoidRefund(DispatchContext dctx, Map<String, ? extends Object> context) { return ccVoid(dctx, context, true); } private static Map<String, Object> ccVoid(DispatchContext dctx, Map<String, ? extends Object> context, boolean isRefund) { GenericValue orderPaymentPreference = (GenericValue) context.get("orderPaymentPreference"); Locale locale = (Locale) context.get("locale"); Delegator delegator = dctx.getDelegator(); //lets see if there is a auth transaction already in context GenericValue authTransaction = (GenericValue) context.get("authTrans"); if (authTransaction == null) { authTransaction = PaymentGatewayServices.getAuthTransaction(orderPaymentPreference); } if (authTransaction == null) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingPaymentTransactionAuthorizationNotFoundCannotRelease", locale)); } // setup the RiTA Interface Properties props = buildPccProperties(context, delegator); RitaApi api = getApi(props, "CREDIT"); if (api == null) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingRitaErrorGettingPaymentGatewayConfig", locale)); } api.set(RitaApi.TRANS_AMOUNT, getAmountString(context, isRefund ? "refundAmount" : "releaseAmount")); api.set(RitaApi.ORIG_SEQ_NUM, authTransaction.getString("referenceNum")); api.set(RitaApi.COMMAND, "VOID"); // check to make sure we are configured for SALE mode if (!"1".equals(props.getProperty("autoBill"))) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingRitaCannotSupportReleasingPreAuth", locale)); } // send the transaction RitaApi out = null; try { out = api.send(); } catch (IOException e) { Debug.logError(e, module); return ServiceUtil.returnError(e.getMessage()); } catch (GeneralException e) { Debug.logError(e, module); return ServiceUtil.returnError(e.getMessage()); } if (out != null) { Map<String, Object> result = ServiceUtil.returnSuccess(); String resultCode = out.get(RitaApi.RESULT); if ("VOIDED".equals(resultCode)) { result.put(isRefund ? "refundResult" : "releaseResult", Boolean.TRUE); } else { result.put(isRefund ? "refundResult" : "releaseResult", Boolean.FALSE); } result.put(isRefund ? "refundAmount" : "releaseAmount", context.get(isRefund ? "refundAmount" : "releaseAmount")); result.put(isRefund ? "refundRefNum" : "releaseRefNum", out.get(RitaApi.INTRN_SEQ_NUM) != null ? out.get(RitaApi.INTRN_SEQ_NUM) : ""); result.put(isRefund ? "refundCode" : "releaseCode", out.get(RitaApi.AUTH_CODE)); result.put(isRefund ? "refundFlag" : "releaseFlag", out.get(RitaApi.REFERENCE)); result.put(isRefund ? "refundMessage" : "releaseMessage", out.get(RitaApi.RESULT)); return result; } else { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingRitaResultIsNull", locale)); } } public static Map<String, Object> ccCreditRefund(DispatchContext dctx, Map<String, ? extends Object> context) { GenericValue orderPaymentPreference = (GenericValue) context.get("orderPaymentPreference"); Locale locale = (Locale) context.get("locale"); Delegator delegator = dctx.getDelegator(); //lets see if there is a auth transaction already in context GenericValue authTransaction = (GenericValue) context.get("authTrans"); if (authTransaction == null) { authTransaction = PaymentGatewayServices.getAuthTransaction(orderPaymentPreference); } if (authTransaction == null) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingPaymentTransactionAuthorizationNotFoundCannotRefund", locale)); } // setup the RiTA Interface Properties props = buildPccProperties(context, delegator); RitaApi api = getApi(props, "CREDIT"); if (api == null) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingRitaErrorGettingPaymentGatewayConfig", locale)); } // set the required cc info try { RitaServices.setCreditCardInfo(api, dctx.getDelegator(), context); } catch (GeneralException e) { return ServiceUtil.returnError(e.getMessage()); } api.set(RitaApi.TRANS_AMOUNT, getAmountString(context, "refundAmount")); api.set(RitaApi.ORIG_SEQ_NUM, authTransaction.getString("referenceNum")); api.set(RitaApi.COMMAND, "CREDIT"); // send the transaction RitaApi out = null; try { out = api.send(); } catch (IOException e) { Debug.logError(e, module); return ServiceUtil.returnError(e.getMessage()); } catch (GeneralException e) { Debug.logError(e, module); return ServiceUtil.returnError(e.getMessage()); } if (out != null) { Map<String, Object> result = ServiceUtil.returnSuccess(); String resultCode = out.get(RitaApi.RESULT); if ("CAPTURED".equals(resultCode)) { result.put("refundResult", Boolean.TRUE); } else { result.put("refundResult", Boolean.FALSE); } result.put("refundAmount", context.get("refundAmount")); result.put("refundRefNum", out.get(RitaApi.INTRN_SEQ_NUM) != null ? out.get(RitaApi.INTRN_SEQ_NUM) : ""); result.put("refundCode", out.get(RitaApi.AUTH_CODE)); result.put("refundFlag", out.get(RitaApi.REFERENCE)); result.put("refundMessage", out.get(RitaApi.RESULT)); return result; } else { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingRitaResultIsNull", locale)); } } public static Map<String, Object> ccRefund(DispatchContext dctx, Map<String, ? extends Object> context) { LocalDispatcher dispatcher = dctx.getDispatcher(); Delegator delegator = dctx.getDelegator(); GenericValue orderPaymentPreference = (GenericValue) context.get("orderPaymentPreference"); Locale locale = (Locale) context.get("locale"); GenericValue orderHeader = null; try { orderHeader = orderPaymentPreference.getRelatedOne("OrderHeader", false); } catch (GenericEntityException e) { Debug.logError(e, module); return ServiceUtil.returnError(UtilProperties.getMessage(resourceOrder, "OrderOrderNotFound", UtilMisc.toMap("orderId", orderPaymentPreference.getString("orderId")), locale)); } if (orderHeader != null) { String terminalId = orderHeader.getString("terminalId"); boolean isVoid = false; if (terminalId != null) { Timestamp orderDate = orderHeader.getTimestamp("orderDate"); GenericValue terminalState = null; try { terminalState = EntityQuery.use(delegator).from("PosTerminalState") .where("posTerminalId", terminalId).filterByDate("openedDate", "closedDate").queryFirst(); } catch (GenericEntityException e) { Debug.logError(e, module); } // this is the current opened terminal if (terminalState != null) { Timestamp openDate = terminalState.getTimestamp("openedDate"); // if the order date is after the open date of the current state // the order happend within the current open/close of the terminal if (orderDate.after(openDate)) { isVoid = true; } } } Map<String, Object> refundResp = null; try { if (isVoid) { refundResp = dispatcher.runSync("ritaCCVoidRefund", context); } else { refundResp = dispatcher.runSync("ritaCCCreditRefund", context); } } catch (GenericServiceException e) { Debug.logError(e, module); return ServiceUtil.returnError(UtilProperties.getMessage(resourceOrder, "AccountingRitaErrorServiceException", locale)); } return refundResp; } else { return ServiceUtil.returnError(UtilProperties.getMessage(resourceOrder, "OrderOrderNotFound", UtilMisc.toMap("orderId", orderPaymentPreference.getString("orderId")), locale)); } } private static void setCreditCardInfo(RitaApi api, Delegator delegator, Map<String, ? extends Object> context) throws GeneralException { GenericValue orderPaymentPreference = (GenericValue) context.get("orderPaymentPreference"); GenericValue creditCard = (GenericValue) context.get("creditCard"); if (creditCard == null) { creditCard = EntityQuery.use(delegator).from("CreditCard").where("paymentMethodId", orderPaymentPreference.getString("paymentMethodId")).queryOne(); } if (creditCard != null) { List<String> expDateList = StringUtil.split(creditCard.getString("expireDate"), "/"); String month = expDateList.get(0); String year = expDateList.get(1); String y2d = year.substring(2); String title = creditCard.getString("titleOnCard"); String fname = creditCard.getString("firstNameOnCard"); String mname = creditCard.getString("middleNameOnCard"); String lname = creditCard.getString("lastNameOnCard"); String sufix = creditCard.getString("suffixOnCard"); StringBuilder name = new StringBuilder(); if (UtilValidate.isNotEmpty(title)) { name.append(title).append(" "); } if (UtilValidate.isNotEmpty(fname)) { name.append(fname).append(" "); } if (UtilValidate.isNotEmpty(mname)) { name.append(mname).append(" "); } if (UtilValidate.isNotEmpty(lname)) { name.append(lname).append(" "); } if (UtilValidate.isNotEmpty(sufix)) { name.append(sufix); } String nameOnCard = name.toString().trim(); String acctNumber = creditCard.getString("cardNumber"); String cvNum = (String) context.get("cardSecurityCode"); api.set(RitaApi.ACCT_NUM, acctNumber); api.set(RitaApi.EXP_MONTH, month); api.set(RitaApi.EXP_YEAR, y2d); api.set(RitaApi.CARDHOLDER, nameOnCard); if (UtilValidate.isNotEmpty(cvNum)) { api.set(RitaApi.CVV2, cvNum); } // billing address information GenericValue billingAddress = (GenericValue) context.get("billingAddress"); if (billingAddress != null) { api.set(RitaApi.CUSTOMER_STREET, billingAddress.getString("address1")); api.set(RitaApi.CUSTOMER_ZIP, billingAddress.getString("postalCode")); } else { String zipCode = orderPaymentPreference.getString("billingPostalCode"); if (UtilValidate.isNotEmpty(zipCode)) { api.set(RitaApi.CUSTOMER_ZIP, zipCode); } } // set the present flag String presentFlag = orderPaymentPreference.getString("presentFlag"); if (presentFlag == null) { presentFlag = "N"; } api.set(RitaApi.PRESENT_FLAG, presentFlag.equals("Y") ? "3" : "1"); // 1, no present, 2 present, 3 swiped } else { throw new GeneralException("No CreditCard object found"); } } private static RitaApi getApi(Properties props) { if (props == null) { Debug.logError("Cannot load API w/ null properties", module); return null; } String host = props.getProperty("host"); int port = 0; try { port = Integer.parseInt(props.getProperty("port")); } catch (Exception e) { Debug.logError(e, module); } boolean ssl = props.getProperty("ssl", "N").equals("Y") ? true : false; RitaApi api = null; if (port > 0 && host != null) { api = new RitaApi(host, port, ssl); } else { api = new RitaApi(); } api.set(RitaApi.CLIENT_ID, props.getProperty("clientID")); api.set(RitaApi.USER_ID, props.getProperty("userID")); api.set(RitaApi.USER_PW, props.getProperty("userPW")); api.set(RitaApi.FORCE_FLAG, props.getProperty("forceTx")); return api; } private static RitaApi getApi(Properties props, String paymentType) { RitaApi api = getApi(props); api.set(RitaApi.FUNCTION_TYPE, "PAYMENT"); api.set(RitaApi.PAYMENT_TYPE, paymentType); return api; } private static Properties buildPccProperties(Map<String, ? extends Object> context, Delegator delegator) { String configString = (String) context.get("paymentConfig"); if (configString == null) { configString = "payment.properties"; } String clientId = EntityUtilProperties.getPropertyValue(configString, "payment.rita.clientID", delegator); String userId = EntityUtilProperties.getPropertyValue(configString, "payment.rita.userID", delegator); String userPw = EntityUtilProperties.getPropertyValue(configString, "payment.rita.userPW", delegator); String host = EntityUtilProperties.getPropertyValue(configString, "payment.rita.host", delegator); String port = EntityUtilProperties.getPropertyValue(configString, "payment.rita.port", delegator); String ssl = EntityUtilProperties.getPropertyValue(configString, "payment.rita.ssl", "N", delegator); String autoBill = EntityUtilProperties.getPropertyValue(configString, "payment.rita.autoBill", "0", delegator); String forceTx = EntityUtilProperties.getPropertyValue(configString, "payment.rita.forceTx", "0", delegator); // some property checking if (UtilValidate.isEmpty(clientId)) { Debug.logWarning("The clientID property in [" + configString + "] is not configured", module); return null; } if (UtilValidate.isEmpty(userId)) { Debug.logWarning("The userID property in [" + configString + "] is not configured", module); return null; } if (UtilValidate.isEmpty(userPw)) { Debug.logWarning("The userPW property in [" + configString + "] is not configured", module); return null; } // create some properties for CS Client Properties props = new Properties(); props.put("clientID", clientId); props.put("userID", userId); props.put("userPW", userPw); props.put("host", host); props.put("port", port); props.put("ssl", ssl); props.put("autoBill", autoBill); props.put("forceTx", forceTx); Debug.logInfo("Returning properties - " + props, module); return props; } private static String getAmountString(Map<String, ? extends Object> context, String amountField) { BigDecimal processAmount = (BigDecimal) context.get(amountField); return processAmount.setScale(decimals, rounding).toPlainString(); } }