/******************************************************************************* * 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.sagepay; import java.math.BigDecimal; import java.sql.Timestamp; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.ofbiz.accounting.payment.PaymentGatewayServices; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.UtilDateTime; import org.apache.ofbiz.base.util.UtilFormatOut; import org.apache.ofbiz.base.util.UtilMisc; import org.apache.ofbiz.base.util.UtilProperties; import org.apache.ofbiz.entity.GenericEntityException; import org.apache.ofbiz.entity.GenericValue; import org.apache.ofbiz.entity.condition.EntityCondition; import org.apache.ofbiz.entity.util.EntityUtil; 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 SagePayPaymentServices { public static final String module = SagePayPaymentServices.class.getName(); public final static String resource = "AccountingUiLabels"; private static Map<String, String> buildCustomerBillingInfo(Map<String, Object> context) { Debug.logInfo("SagePay - Entered buildCustomerBillingInfo", module); Debug.logInfo("SagePay buildCustomerBillingInfo context : " + context, module); Map<String, String> billingInfo = new HashMap<String, String>(); String orderId = null; BigDecimal processAmount = null; String currency = null; String cardNumber = null; String cardType = null; String nameOnCard = null; String expireDate = null; String securityCode = null; String postalCode = null; String address = null; try { GenericValue opp = (GenericValue) context.get("orderPaymentPreference"); if (opp != null) { if ("CREDIT_CARD".equals(opp.getString("paymentMethodTypeId"))) { GenericValue creditCard = (GenericValue) context.get("creditCard"); if (creditCard == null || !(opp.get("paymentMethodId").equals(creditCard.get("paymentMethodId")))) { creditCard = opp.getRelatedOne("CreditCard", false); } securityCode = opp.getString("securityCode"); //getting billing address GenericValue billingAddress = (GenericValue) context.get("billingAddress"); postalCode = billingAddress.getString("postalCode"); String address2 = billingAddress.getString("address2"); if (address2 == null){ address2 = ""; } address = billingAddress.getString("address1") + " " + address2; //getting card details cardNumber = creditCard.getString("cardNumber"); String firstName = creditCard.getString("firstNameOnCard"); String middleName = creditCard.getString("middleNameOnCard"); String lastName = creditCard.getString("lastNameOnCard"); if (middleName == null){ middleName = ""; } nameOnCard = firstName + " " + middleName + " " + lastName; cardType = creditCard.getString("cardType"); if (cardType != null) { if ("CCT_MASTERCARD".equals(cardType)) { cardType = "MC"; } if ("CCT_VISAELECTRON".equals(cardType)) { cardType = "UKE"; } if ("CCT_DINERSCLUB".equals(cardType)) { cardType = "DC"; } if ("CCT_SWITCH".equals(cardType)) { cardType = "MAESTRO"; } } expireDate = creditCard.getString("expireDate"); String month = expireDate.substring(0,2); String year = expireDate.substring(5); expireDate = month + year; //getting order details orderId = UtilFormatOut.checkNull((String) context.get("orderId")); processAmount = (BigDecimal) context.get("processAmount"); currency = (String) context.get("currency"); } else { Debug.logWarning("Payment preference " + opp + " is not a credit card", module); } } } catch (GenericEntityException ex) { Debug.logError("Cannot build customer information for " + context + " due to error: " + ex.getMessage(), module); return null; } billingInfo.put("orderId", orderId); billingInfo.put("amount", processAmount.toString()); billingInfo.put("currency", currency); billingInfo.put("description", orderId); billingInfo.put("cardNumber", cardNumber); billingInfo.put("cardHolder", nameOnCard); billingInfo.put("expiryDate", expireDate); billingInfo.put("cardType", cardType); billingInfo.put("cv2", securityCode); billingInfo.put("billingPostCode", postalCode); billingInfo.put("billingAddress", address); Debug.logInfo("SagePay billingInfo : " + billingInfo, module); Debug.logInfo("SagePay - Exiting buildCustomerBillingInfo", module); return billingInfo; } public static Map<String, Object> ccAuth(DispatchContext dctx, Map<String, Object> context) { Debug.logInfo("SagePay - Entered ccAuth", module); Debug.logInfo("SagePay ccAuth context : " + context, module); Map<String, Object> response = null; String orderId = (String) context.get("orderId"); Locale locale = (Locale) context.get("locale"); GenericValue orderPaymentPreference = (GenericValue) context.get("orderPaymentPreference"); if (orderPaymentPreference == null) { response = ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingSagePayOrderPaymenPreferenceIsNull", UtilMisc.toMap("orderId", orderId, "orderPaymentPreference", orderPaymentPreference), locale)); } else { response = processCardAuthorisationPayment(dctx, context); } Debug.logInfo("SagePay ccAuth response : " + response, module); Debug.logInfo("SagePay - Exiting ccAuth", module); return response; } private static Map<String, Object> processCardAuthorisationPayment(DispatchContext ctx, Map<String, Object> context) { Map<String, Object> result = ServiceUtil.returnSuccess(); LocalDispatcher dispatcher = ctx.getDispatcher(); Locale locale = (Locale) context.get("locale"); Map<String, String> billingInfo = buildCustomerBillingInfo(context); String paymentGatewayConfigId = (String) context.get("paymentGatewayConfigId"); try { Map<String, Object> paymentResult = dispatcher.runSync("SagePayPaymentAuthentication", UtilMisc.toMap( "paymentGatewayConfigId", paymentGatewayConfigId, "vendorTxCode", billingInfo.get("orderId"), "cardHolder", billingInfo.get("cardHolder"), "cardNumber", billingInfo.get("cardNumber"), "expiryDate", billingInfo.get("expiryDate"), "cardType", billingInfo.get("cardType"), "cv2", billingInfo.get("cv2"), "description", billingInfo.get("description"), "amount", billingInfo.get("amount"), "currency", billingInfo.get("currency"), "billingAddress", billingInfo.get("billingAddress"), "billingPostCode", billingInfo.get("billingPostCode") ) ); Debug.logInfo("SagePay - SagePayPaymentAuthentication result : " + paymentResult, module); String transactionType = (String) paymentResult.get("transactionType"); String status = (String) paymentResult.get("status"); String statusDetail = (String) paymentResult.get("statusDetail"); String vpsTxId = (String) paymentResult.get("vpsTxId"); String securityKey = (String) paymentResult.get("securityKey"); String txAuthNo = (String) paymentResult.get("txAuthNo"); String vendorTxCode = (String) paymentResult.get("vendorTxCode"); String amount = (String) paymentResult.get("amount"); if (status != null && "OK".equals(status)) { Debug.logInfo("SagePay - Payment authorized for order : " + vendorTxCode, module); result = SagePayUtil.buildCardAuthorisationPaymentResponse(Boolean.TRUE, txAuthNo, securityKey, new BigDecimal(amount), vpsTxId, vendorTxCode, statusDetail); if ("PAYMENT".equals(transactionType)) { Map<String,Object> captureResult = SagePayUtil.buildCardCapturePaymentResponse(Boolean.TRUE, txAuthNo, securityKey, new BigDecimal(amount), vpsTxId, vendorTxCode, statusDetail); result.putAll(captureResult); } } else if (status != null && "INVALID".equals(status)) { Debug.logInfo("SagePay - Invalid authorisation request for order : " + vendorTxCode, module); result = SagePayUtil.buildCardAuthorisationPaymentResponse(Boolean.FALSE, null, null, BigDecimal.ZERO, "INVALID", vendorTxCode, statusDetail); } else if (status != null && "MALFORMED".equals(status)) { Debug.logInfo("SagePay - Malformed authorisation request for order : " + vendorTxCode, module); result = SagePayUtil.buildCardAuthorisationPaymentResponse(Boolean.FALSE, null, null, BigDecimal.ZERO, "MALFORMED", vendorTxCode, statusDetail); } else if (status != null && "NOTAUTHED".equals(status)) { Debug.logInfo("SagePay - NotAuthed authorisation request for order : " + vendorTxCode, module); result = SagePayUtil.buildCardAuthorisationPaymentResponse(Boolean.FALSE, null, securityKey, BigDecimal.ZERO, vpsTxId, vendorTxCode, statusDetail); } else if (status != null && "REJECTED".equals(status)) { Debug.logInfo("SagePay - Rejected authorisation request for order : " + vendorTxCode, module); result = SagePayUtil.buildCardAuthorisationPaymentResponse(Boolean.FALSE, null, securityKey, new BigDecimal(amount), vpsTxId, vendorTxCode, statusDetail); } else { Debug.logInfo("SagePay - Invalid status " + status + " received for order : " + vendorTxCode, module); result = SagePayUtil.buildCardAuthorisationPaymentResponse(Boolean.FALSE, null, null, BigDecimal.ZERO, "ERROR", vendorTxCode, statusDetail); } } catch(GenericServiceException e) { Debug.logError(e, "Error in calling SagePayPaymentAuthentication", module); result = ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingSagePayPaymentAuthorisationException", UtilMisc.toMap("errorString", e.getMessage()), locale)); } return result; } public static Map<String, Object> ccCapture(DispatchContext ctx, Map<String, Object> context) { Debug.logInfo("SagePay - Entered ccCapture", module); Debug.logInfo("SagePay ccCapture context : " + context, module); GenericValue orderPaymentPreference = (GenericValue) context.get("orderPaymentPreference"); GenericValue authTransaction = PaymentGatewayServices.getAuthTransaction(orderPaymentPreference); context.put("authTransaction", authTransaction); Map<String, Object> response = processCardCapturePayment(ctx, context); Debug.logInfo("SagePay ccCapture response : " + response, module); Debug.logInfo("SagePay - Exiting ccCapture", module); return response; } private static Map<String, Object> processCardCapturePayment(DispatchContext ctx, Map<String, Object> context) { Map<String, Object> result = ServiceUtil.returnSuccess(); LocalDispatcher dispatcher = ctx.getDispatcher(); Locale locale = (Locale) context.get("locale"); String paymentGatewayConfigId = (String) context.get("paymentGatewayConfigId"); GenericValue authTransaction = (GenericValue) context.get("authTransaction"); BigDecimal amount = (BigDecimal) context.get("captureAmount"); String vendorTxCode = (String) authTransaction.get("altReference"); String vpsTxId = (String) authTransaction.get("referenceNum"); String securityKey = (String) authTransaction.get("gatewayFlag"); String txAuthCode = (String) authTransaction.get("gatewayCode"); try { Map<String, Object> paymentResult = dispatcher.runSync("SagePayPaymentAuthorisation", UtilMisc.toMap( "paymentGatewayConfigId", paymentGatewayConfigId, "vendorTxCode", vendorTxCode, "vpsTxId", vpsTxId, "securityKey", securityKey, "txAuthNo", txAuthCode, "amount", amount.toString() ) ); Debug.logInfo("SagePay - SagePayPaymentAuthorisation result : " + paymentResult, module); String status = (String) paymentResult.get("status"); String statusDetail = (String) paymentResult.get("statusDetail"); if (status != null && "OK".equals(status)) { Debug.logInfo("SagePay Payment Released for Order : " + vendorTxCode, module); result = SagePayUtil.buildCardCapturePaymentResponse(Boolean.TRUE, txAuthCode, securityKey, amount, vpsTxId, vendorTxCode, statusDetail); } else { Debug.logInfo("SagePay - Invalid status " + status + " received for order : " + vendorTxCode, module); result = SagePayUtil.buildCardCapturePaymentResponse(Boolean.FALSE, txAuthCode, securityKey, amount, vpsTxId, vendorTxCode, statusDetail); } } catch(GenericServiceException e) { Debug.logError(e, "Error in calling SagePayPaymentAuthorisation", module); result = ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingSagePayPaymentAuthorisationException", UtilMisc.toMap("errorString", e.getMessage()), locale)); } return result; } public static Map<String, Object> ccRefund(DispatchContext ctx, Map<String, Object> context) { Debug.logInfo("SagePay - Entered ccRefund", module); Debug.logInfo("SagePay ccRefund context : " + context, module); Locale locale = (Locale) context.get("locale"); GenericValue orderPaymentPreference = (GenericValue) context.get("orderPaymentPreference"); GenericValue captureTransaction = PaymentGatewayServices.getCaptureTransaction(orderPaymentPreference); if (captureTransaction == null) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingPaymentTransactionAuthorizationNotFoundCannotRefund", locale)); } Debug.logInfo("SagePay ccRefund captureTransaction : " + captureTransaction, module); GenericValue creditCard = null; try { creditCard = orderPaymentPreference.getRelatedOne("CreditCard", false); } catch (GenericEntityException e) { Debug.logError(e, "Error getting CreditCard for OrderPaymentPreference : " + orderPaymentPreference, module); return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingPaymentUnableToGetCCInfo", locale) + " " + orderPaymentPreference); } context.put("creditCard",creditCard); context.put("captureTransaction", captureTransaction); List<GenericValue> authTransactions = PaymentGatewayServices.getAuthTransactions(orderPaymentPreference); EntityCondition authCondition = EntityCondition.makeCondition("paymentServiceTypeEnumId", "PRDS_PAY_AUTH"); List<GenericValue> authTransactions1 = EntityUtil.filterByCondition(authTransactions, authCondition); GenericValue authTransaction = EntityUtil.getFirst(authTransactions1); Timestamp authTime = authTransaction.getTimestamp("transactionDate"); Calendar authCal = Calendar.getInstance(); authCal.setTimeInMillis(authTime.getTime()); Timestamp nowTime = UtilDateTime.nowTimestamp(); Calendar nowCal = Calendar.getInstance(); nowCal.setTimeInMillis(nowTime.getTime()); Calendar yesterday = Calendar.getInstance(); yesterday.set(nowCal.get(Calendar.YEAR), nowCal.get(Calendar.MONTH), nowCal.get(Calendar.DATE), 23, 59, 59); yesterday.add(Calendar.DAY_OF_YEAR, -1); Map<String, Object> response = null; if (authCal.before(yesterday)) { Debug.logInfo("SagePay - Calling Refund for Refund", module); response = processCardRefundPayment(ctx, context); } else { Calendar cal = Calendar.getInstance(); cal.set(nowCal.get(Calendar.YEAR), nowCal.get(Calendar.MONTH), nowCal.get(Calendar.DATE), 23, 49, 59); if (authCal.before(cal)) { Debug.logInfo("SagePay - Calling Void for Refund", module); response = processCardVoidPayment(ctx, context); } else { Debug.logInfo("SagePay - Calling Refund for Refund", module); response = processCardRefundPayment(ctx, context); } } Debug.logInfo("SagePay ccRefund response : " + response, module); return response; } private static Map<String, Object> processCardRefundPayment(DispatchContext ctx, Map<String, Object> context) { Map<String, Object> result = ServiceUtil.returnSuccess(); LocalDispatcher dispatcher = ctx.getDispatcher(); Locale locale = (Locale) context.get("locale"); String paymentGatewayConfigId = (String) context.get("paymentGatewayConfigId"); GenericValue captureTransaction = (GenericValue) context.get("captureTransaction"); BigDecimal amount = (BigDecimal) context.get("refundAmount"); String orderId = (String) captureTransaction.get("altReference"); orderId = "R" + orderId; try { Map<String, Object> paymentResult = dispatcher.runSync("SagePayPaymentRefund", UtilMisc.toMap( "paymentGatewayConfigId", paymentGatewayConfigId, "vendorTxCode", orderId, "amount", amount.toString(), "currency", "GBP", "description", orderId, "relatedVPSTxId", captureTransaction.get("referenceNum"), "relatedVendorTxCode", captureTransaction.get("altReference"), "relatedSecurityKey", captureTransaction.get("gatewayFlag"), "relatedTxAuthNo", captureTransaction.get("gatewayCode") ) ); Debug.logInfo("SagePay - SagePayPaymentRefund result : " + paymentResult, module); String status = (String) paymentResult.get("status"); String statusDetail = (String) paymentResult.get("statusDetail"); String vpsTxId = (String) paymentResult.get("vpsTxId"); String txAuthNo = (String) paymentResult.get("txAuthNo"); if (status != null && "OK".equals(status)) { Debug.logInfo("SagePay Payment Refunded for Order : " + orderId, module); result = SagePayUtil.buildCardRefundPaymentResponse(Boolean.TRUE, txAuthNo, amount, vpsTxId, orderId, statusDetail); } else { Debug.logInfo("SagePay - Invalid status " + status + " received for order : " + orderId, module); result = SagePayUtil.buildCardRefundPaymentResponse(Boolean.FALSE, null, BigDecimal.ZERO, status, orderId, statusDetail); } } catch(GenericServiceException e) { Debug.logError(e, "Error in calling SagePayPaymentRefund", module); result = ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingSagePayPaymentRefundException", UtilMisc.toMap("errorString", e.getMessage()), locale)); } return result; } private static Map<String, Object> processCardVoidPayment(DispatchContext ctx, Map<String, Object> context) { Map<String, Object> result = ServiceUtil.returnSuccess(); LocalDispatcher dispatcher = ctx.getDispatcher(); Locale locale = (Locale) context.get("locale"); String paymentGatewayConfigId = (String) context.get("paymentGatewayConfigId"); GenericValue captureTransaction = (GenericValue) context.get("captureTransaction"); BigDecimal amount = (BigDecimal) context.get("refundAmount"); String orderId = (String) captureTransaction.get("altReference"); try { Map<String, Object> paymentResult = dispatcher.runSync("SagePayPaymentVoid", UtilMisc.toMap( "paymentGatewayConfigId", paymentGatewayConfigId, "vendorTxCode", captureTransaction.get("altReference"), "vpsTxId", captureTransaction.get("referenceNum"), "securityKey", captureTransaction.get("gatewayFlag"), "txAuthNo", captureTransaction.get("gatewayCode") ) ); Debug.logInfo("SagePay - SagePayPaymentVoid result : " + paymentResult, module); String status = (String) paymentResult.get("status"); String statusDetail = (String) paymentResult.get("statusDetail"); if (status != null && "OK".equals(status)) { Debug.logInfo("SagePay Payment Voided for Order : " + orderId, module); result = SagePayUtil.buildCardVoidPaymentResponse(Boolean.TRUE, amount, "SUCCESS", orderId, statusDetail); } else if (status != null && "MALFORMED".equals(status)) { Debug.logInfo("SagePay - Malformed void request for order : " + orderId, module); result = SagePayUtil.buildCardVoidPaymentResponse(Boolean.FALSE, BigDecimal.ZERO, "MALFORMED", orderId, statusDetail); } else if (status != null && "INVALID".equals(status)){ Debug.logInfo("SagePay - Invalid void request for order : " + orderId, module); result = SagePayUtil.buildCardVoidPaymentResponse(Boolean.FALSE, BigDecimal.ZERO, "INVALID", orderId, statusDetail); } else if (status != null && "ERROR".equals(status)){ Debug.logInfo("SagePay - Error in void request for order : " + orderId, module); result = SagePayUtil.buildCardVoidPaymentResponse(Boolean.FALSE, BigDecimal.ZERO, "ERROR", orderId, statusDetail); } } catch(GenericServiceException e) { Debug.logError(e, "Error in calling SagePayPaymentVoid", module); result = ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingSagePayPaymentVoidException", UtilMisc.toMap("errorString", e.getMessage()), locale)); } return result; } public static Map<String, Object> ccRelease(DispatchContext ctx, Map<String, Object> context) { Debug.logInfo("SagePay - Entered ccRelease", module); Debug.logInfo("SagePay ccRelease context : " + context, module); Locale locale = (Locale) context.get("locale"); GenericValue orderPaymentPreference = (GenericValue) context.get("orderPaymentPreference"); GenericValue authTransaction = PaymentGatewayServices.getAuthTransaction(orderPaymentPreference); if (authTransaction == null) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingPaymentTransactionAuthorizationNotFoundCannotRelease", locale)); } context.put("authTransaction", authTransaction); Map<String, Object> response = processCardReleasePayment(ctx, context); Debug.logInfo("SagePay ccRelease response : " + response, module); return response; } private static Map<String, Object> processCardReleasePayment(DispatchContext ctx, Map<String, Object> context) { Map<String, Object> result = ServiceUtil.returnSuccess(); Locale locale = (Locale) context.get("locale"); LocalDispatcher dispatcher = ctx.getDispatcher(); String paymentGatewayConfigId = (String) context.get("paymentGatewayConfigId"); BigDecimal amount = (BigDecimal) context.get("releaseAmount"); GenericValue authTransaction = (GenericValue) context.get("authTransaction"); String orderId = (String) authTransaction.get("altReference"); String refNum = (String) authTransaction.get("referenceNum"); try { Map<String, Object> paymentResult = dispatcher.runSync("SagePayPaymentRelease", UtilMisc.toMap( "paymentGatewayConfigId", paymentGatewayConfigId, "vendorTxCode", orderId, "releaseAmount", amount.toString(), "vpsTxId", refNum, "securityKey", authTransaction.get("gatewayFlag"), "txAuthNo", authTransaction.get("gatewayCode") ) ); Debug.logInfo("SagePay - SagePayPaymentRelease result : " + paymentResult, module); String status = (String) paymentResult.get("status"); String statusDetail = (String) paymentResult.get("statusDetail"); if (status != null && "OK".equals(status)) { Debug.logInfo("SagePay Payment Released for Order : " + orderId, module); result = SagePayUtil.buildCardReleasePaymentResponse(Boolean.TRUE, null, amount, refNum, orderId, statusDetail); } else { Debug.logInfo("SagePay - Invalid status " + status + " received for order : " + orderId, module); result = SagePayUtil.buildCardReleasePaymentResponse(Boolean.FALSE, null, amount, refNum, orderId, statusDetail); } } catch(GenericServiceException e) { Debug.logError(e, "Error in calling SagePayPaymentRelease", module); result = ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingSagePayPaymentReleaseException", UtilMisc.toMap("errorString", e.getMessage()), locale)); } return result; } }