| /******************************************************************************* |
| * 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.ofbiz.accounting.thirdparty.paypal; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.PrintWriter; |
| import java.math.BigDecimal; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.text.ParseException; |
| import java.text.SimpleDateFormat; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.ofbiz.base.util.Debug; |
| import org.ofbiz.base.util.UtilDateTime; |
| import org.ofbiz.base.util.UtilFormatOut; |
| import org.ofbiz.base.util.UtilHttp; |
| import org.ofbiz.base.util.UtilMisc; |
| import org.ofbiz.base.util.UtilProperties; |
| import org.ofbiz.base.util.UtilValidate; |
| import org.ofbiz.entity.Delegator; |
| import org.ofbiz.entity.GenericEntityException; |
| import org.ofbiz.entity.GenericValue; |
| import org.ofbiz.entity.transaction.GenericTransactionException; |
| import org.ofbiz.entity.transaction.TransactionUtil; |
| import org.ofbiz.entity.util.EntityQuery; |
| import org.ofbiz.entity.util.EntityUtilProperties; |
| import org.ofbiz.order.order.OrderChangeHelper; |
| import org.ofbiz.product.store.ProductStoreWorker; |
| import org.ofbiz.service.GenericServiceException; |
| import org.ofbiz.service.LocalDispatcher; |
| import org.ofbiz.service.ModelService; |
| |
| |
| public class PayPalEvents { |
| |
| public static final String resource = "AccountingUiLabels"; |
| public static final String resourceErr = "AccountingErrorUiLabels"; |
| public static final String commonResource = "CommonUiLabels"; |
| public static final String module = PayPalEvents.class.getName(); |
| |
| /** Initiate PayPal Request */ |
| public static String callPayPal(HttpServletRequest request, HttpServletResponse response) { |
| Locale locale = UtilHttp.getLocale(request); |
| Delegator delegator = (Delegator) request.getAttribute("delegator"); |
| GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin"); |
| |
| // get the orderId |
| String orderId = (String) request.getAttribute("orderId"); |
| |
| // get the order header |
| GenericValue orderHeader = null; |
| try { |
| orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Cannot get the order header for order: " + orderId, module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.problemsGettingOrderHeader", locale)); |
| return "error"; |
| } |
| |
| // get the order total |
| String orderTotal = orderHeader.getBigDecimal("grandTotal").toPlainString(); |
| String currencyUom = orderHeader.getString("currencyUom"); |
| |
| // get the product store |
| GenericValue productStore = ProductStoreWorker.getProductStore(request); |
| |
| if (productStore == null) { |
| Debug.logError("ProductStore is null", module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.problemsGettingMerchantConfiguration", locale)); |
| return "error"; |
| } |
| |
| // get the payment properties file |
| GenericValue paymentConfig = ProductStoreWorker.getProductStorePaymentSetting(delegator, productStore.getString("productStoreId"), "EXT_PAYPAL", null, true); |
| String configString = null; |
| String paymentGatewayConfigId = null; |
| if (paymentConfig != null) { |
| paymentGatewayConfigId = paymentConfig.getString("paymentGatewayConfigId"); |
| configString = paymentConfig.getString("paymentPropertiesPath"); |
| } |
| |
| if (configString == null) { |
| configString = "payment.properties"; |
| } |
| |
| // get the company name |
| String company = UtilFormatOut.checkEmpty(productStore.getString("companyName"), ""); |
| |
| // create the item name |
| String itemName = UtilProperties.getMessage(resource, "AccountingOrderNr", locale) + orderId + " " + |
| (company != null ? UtilProperties.getMessage(commonResource, "CommonFrom", locale) + " "+ company : ""); |
| String itemNumber = "0"; |
| |
| // get the redirect url |
| String redirectUrl = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "redirectUrl", configString, "payment.paypal.redirect"); |
| |
| // get the notify url |
| String notifyUrl = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "notifyUrl", configString, "payment.paypal.notify"); |
| |
| // get the return urls |
| String returnUrl = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "returnUrl", configString, "payment.paypal.return"); |
| |
| // get the cancel return urls |
| String cancelReturnUrl = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "cancelReturnUrl", configString, "payment.paypal.cancelReturn"); |
| |
| // get the image url |
| String imageUrl = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "imageUrl", configString, "payment.paypal.image"); |
| |
| // get the paypal account |
| String payPalAccount = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "businessEmail", configString, "payment.paypal.business"); |
| |
| if (UtilValidate.isEmpty(redirectUrl) |
| || UtilValidate.isEmpty(notifyUrl) |
| || UtilValidate.isEmpty(returnUrl) |
| || UtilValidate.isEmpty(imageUrl) |
| || UtilValidate.isEmpty(payPalAccount)) { |
| Debug.logError("Payment properties is not configured properly, some notify URL from PayPal is not correctly defined!", module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.problemsGettingMerchantConfiguration", locale)); |
| return "error"; |
| } |
| |
| // create the redirect string |
| Map <String, Object> parameters = new LinkedHashMap <String, Object>(); |
| parameters.put("cmd", "_xclick"); |
| parameters.put("business", payPalAccount); |
| parameters.put("item_name", itemName); |
| parameters.put("item_number", itemNumber); |
| parameters.put("invoice", orderId); |
| parameters.put("custom", userLogin.getString("userLoginId")); |
| parameters.put("amount", orderTotal); |
| parameters.put("currency_code", currencyUom); |
| parameters.put("return", returnUrl); |
| if (UtilValidate.isNotEmpty(cancelReturnUrl)) parameters.put("cancel_return", cancelReturnUrl); |
| parameters.put("notify_url", notifyUrl); |
| parameters.put("image_url", imageUrl); |
| parameters.put("no_note", "1"); // no notes allowed in paypal (not passed back) |
| parameters.put("no_shipping", "1"); // no shipping address required (local shipping used) |
| |
| String encodedParameters = UtilHttp.urlEncodeArgs(parameters, false); |
| String redirectString = redirectUrl + "?" + encodedParameters; |
| |
| // set the order in the session for cancelled orders |
| request.getSession().setAttribute("PAYPAL_ORDER", orderId); |
| |
| // redirect to paypal |
| try { |
| response.sendRedirect(redirectString); |
| } catch (IOException e) { |
| Debug.logError(e, "Problems redirecting to PayPal", module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.problemsConnectingWithPayPal", locale)); |
| return "error"; |
| } |
| |
| return "success"; |
| } |
| |
| /** PayPal Call-Back Event */ |
| public static String payPalIPN(HttpServletRequest request, HttpServletResponse response) { |
| Locale locale = UtilHttp.getLocale(request); |
| Delegator delegator = (Delegator) request.getAttribute("delegator"); |
| LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher"); |
| |
| // get the product store |
| GenericValue productStore = ProductStoreWorker.getProductStore(request); |
| if (productStore == null) { |
| Debug.logError("ProductStore is null", module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.problemsGettingMerchantConfiguration", locale)); |
| return "error"; |
| } |
| |
| // get the payment properties file |
| GenericValue paymentConfig = ProductStoreWorker.getProductStorePaymentSetting(delegator, productStore.getString("productStoreId"), "EXT_PAYPAL", null, true); |
| |
| String configString = null; |
| String paymentGatewayConfigId = null; |
| if (paymentConfig != null) { |
| paymentGatewayConfigId = paymentConfig.getString("paymentGatewayConfigId"); |
| configString = paymentConfig.getString("paymentPropertiesPath"); |
| } |
| |
| if (configString == null) { |
| configString = "payment.properties"; |
| } |
| |
| // get the confirm URL |
| String confirmUrl = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "confirmUrl", configString, "payment.paypal.confirm"); |
| |
| // get the redirect URL |
| String redirectUrl = getPaymentGatewayConfigValue(delegator, paymentGatewayConfigId, "redirectUrl", configString, "payment.paypal.redirect"); |
| |
| if (UtilValidate.isEmpty(confirmUrl) || UtilValidate.isEmpty(redirectUrl)) { |
| Debug.logError("Payment properties is not configured properly, no confirm URL defined!", module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.problemsGettingMerchantConfiguration", locale)); |
| return "error"; |
| } |
| |
| // first verify this is valid from PayPal |
| Map <String, Object> parametersMap = UtilHttp.getParameterMap(request); |
| parametersMap.put("cmd", "_notify-validate"); |
| |
| // send off the confirm request |
| String confirmResp = null; |
| |
| try { |
| String str = UtilHttp.urlEncodeArgs(parametersMap); |
| URL u = new URL(redirectUrl); |
| URLConnection uc = u.openConnection(); |
| uc.setDoOutput(true); |
| uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); |
| PrintWriter pw = new PrintWriter(uc.getOutputStream()); |
| pw.println(str); |
| pw.close(); |
| |
| BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream())); |
| confirmResp = in.readLine(); |
| in.close(); |
| Debug.logError("PayPal Verification Response: " + confirmResp, module); |
| } catch (IOException e) { |
| Debug.logError(e, "Problems sending verification message", module); |
| } |
| |
| Debug.logInfo("Got verification from PayPal, processing..", module); |
| boolean verified = false; |
| for (String name : parametersMap.keySet()) { |
| String value = request.getParameter(name); |
| Debug.logError("### Param: " + name + " => " + value, module); |
| if (UtilValidate.isNotEmpty(name) && "payer_status".equalsIgnoreCase(name) && |
| UtilValidate.isNotEmpty(value) && "verified".equalsIgnoreCase(value)) { |
| verified = true; |
| } |
| } |
| if (!verified) { |
| Debug.logError("###### PayPal did not verify this request, need investigation!", module); |
| } |
| |
| // get the system user |
| GenericValue userLogin = null; |
| try { |
| userLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", "system").queryOne(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Cannot get UserLogin for: system; cannot continue", module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.problemsGettingAuthenticationUser", locale)); |
| return "error"; |
| } |
| |
| // get the orderId |
| String orderId = request.getParameter("invoice"); |
| |
| // get the order header |
| GenericValue orderHeader = null; |
| if (UtilValidate.isNotEmpty(orderId)) { |
| try { |
| orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Cannot get the order header for order: " + orderId, module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.problemsGettingOrderHeader", locale)); |
| return "error"; |
| } |
| } else { |
| Debug.logError("PayPal did not callback with a valid orderId!", module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.noValidOrderIdReturned", locale)); |
| return "error"; |
| } |
| |
| if (orderHeader == null) { |
| Debug.logError("Cannot get the order header for order: " + orderId, module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.problemsGettingOrderHeader", locale)); |
| return "error"; |
| } |
| |
| /* get payment data |
| String paymentCurrency = request.getParameter("mc_currency"); |
| String paymentAmount = request.getParameter("mc_gross"); |
| String paymentFee = request.getParameter("mc_fee"); |
| String transactionId = request.getParameter("txn_id"); |
| */ |
| |
| // get the transaction status |
| String paymentStatus = request.getParameter("payment_status"); |
| |
| // attempt to start a transaction |
| boolean okay = true; |
| boolean beganTransaction = false; |
| try { |
| beganTransaction = TransactionUtil.begin(); |
| |
| if (paymentStatus.equals("Completed")) { |
| okay = OrderChangeHelper.approveOrder(dispatcher, userLogin, orderId); |
| } else if (paymentStatus.equals("Failed") || paymentStatus.equals("Denied")) { |
| okay = OrderChangeHelper.cancelOrder(dispatcher, userLogin, orderId); |
| } |
| |
| if (okay) { |
| // set the payment preference |
| okay = setPaymentPreferences(delegator, dispatcher, userLogin, orderId, request); |
| } |
| } catch (Exception e) { |
| String errMsg = "Error handling PayPal notification"; |
| Debug.logError(e, errMsg, module); |
| try { |
| TransactionUtil.rollback(beganTransaction, errMsg, e); |
| } catch (GenericTransactionException gte2) { |
| Debug.logError(gte2, "Unable to rollback transaction", module); |
| } |
| } finally { |
| if (!okay) { |
| try { |
| TransactionUtil.rollback(beganTransaction, "Failure in processing PayPal callback", null); |
| } catch (GenericTransactionException gte) { |
| Debug.logError(gte, "Unable to rollback transaction", module); |
| } |
| } else { |
| try { |
| TransactionUtil.commit(beganTransaction); |
| } catch (GenericTransactionException gte) { |
| Debug.logError(gte, "Unable to commit transaction", module); |
| } |
| } |
| } |
| |
| |
| if (okay) { |
| // attempt to release the offline hold on the order (workflow) |
| OrderChangeHelper.releaseInitialOrderHold(dispatcher, orderId); |
| |
| // call the email confirm service |
| Map <String, String> emailContext = UtilMisc.toMap("orderId", orderId); |
| try { |
| dispatcher.runSync("sendOrderConfirmation", emailContext); |
| } catch (GenericServiceException e) { |
| Debug.logError(e, "Problems sending email confirmation", module); |
| } |
| } |
| |
| return "success"; |
| } |
| |
| /** Event called when customer cancels a paypal order */ |
| public static String cancelPayPalOrder(HttpServletRequest request, HttpServletResponse response) { |
| Locale locale = UtilHttp.getLocale(request); |
| LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher"); |
| GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin"); |
| |
| // get the stored order id from the session |
| String orderId = (String) request.getSession().getAttribute("PAYPAL_ORDER"); |
| |
| // attempt to start a transaction |
| boolean beganTransaction = false; |
| try { |
| beganTransaction = TransactionUtil.begin(); |
| } catch (GenericTransactionException gte) { |
| Debug.logError(gte, "Unable to begin transaction", module); |
| } |
| |
| // cancel the order |
| boolean okay = OrderChangeHelper.cancelOrder(dispatcher, userLogin, orderId); |
| |
| if (okay) { |
| try { |
| TransactionUtil.commit(beganTransaction); |
| } catch (GenericTransactionException gte) { |
| Debug.logError(gte, "Unable to commit transaction", module); |
| } |
| } else { |
| try { |
| TransactionUtil.rollback(beganTransaction, "Failure in processing PayPal cancel callback", null); |
| } catch (GenericTransactionException gte) { |
| Debug.logError(gte, "Unable to rollback transaction", module); |
| } |
| } |
| |
| // attempt to release the offline hold on the order (workflow) |
| if (okay) |
| OrderChangeHelper.releaseInitialOrderHold(dispatcher, orderId); |
| |
| request.setAttribute("_EVENT_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.previousPayPalOrderHasBeenCancelled", locale)); |
| return "success"; |
| } |
| |
| private static boolean setPaymentPreferences(Delegator delegator, LocalDispatcher dispatcher, GenericValue userLogin, String orderId, HttpServletRequest request) { |
| Debug.logVerbose("Setting payment prefrences..", module); |
| List <GenericValue> paymentPrefs = null; |
| try { |
| paymentPrefs = EntityQuery.use(delegator).from("OrderPaymentPreference").where("orderId", orderId, "statusId", "PAYMENT_NOT_RECEIVED").queryList(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Cannot get payment preferences for order #" + orderId, module); |
| return false; |
| } |
| if (paymentPrefs.size() > 0) { |
| for (GenericValue pref : paymentPrefs) { |
| boolean okay = setPaymentPreference(dispatcher, userLogin, pref, request); |
| if (!okay) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean setPaymentPreference(LocalDispatcher dispatcher, GenericValue userLogin, GenericValue paymentPreference, HttpServletRequest request) { |
| Locale locale = UtilHttp.getLocale(request); |
| String paymentDate = request.getParameter("payment_date"); |
| String paymentType = request.getParameter("payment_type"); |
| String paymentAmount = request.getParameter("mc_gross"); |
| String paymentStatus = request.getParameter("payment_status"); |
| String transactionId = request.getParameter("txn_id"); |
| |
| List <GenericValue> toStore = new LinkedList <GenericValue> (); |
| |
| // PayPal returns the timestamp in the format 'hh:mm:ss Jan 1, 2000 PST' |
| // Parse this into a valid Timestamp Object |
| SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss MMM d, yyyy z"); |
| java.sql.Timestamp authDate = null; |
| try { |
| authDate = new java.sql.Timestamp(sdf.parse(paymentDate).getTime()); |
| } catch (ParseException e) { |
| Debug.logError(e, "Cannot parse date string: " + paymentDate, module); |
| authDate = UtilDateTime.nowTimestamp(); |
| } catch (NullPointerException e) { |
| Debug.logError(e, "Cannot parse date string: " + paymentDate, module); |
| authDate = UtilDateTime.nowTimestamp(); |
| } |
| |
| paymentPreference.set("maxAmount", new BigDecimal(paymentAmount)); |
| if (paymentStatus.equals("Completed")) { |
| paymentPreference.set("statusId", "PAYMENT_RECEIVED"); |
| } else if (paymentStatus.equals("Pending")) { |
| paymentPreference.set("statusId", "PAYMENT_NOT_RECEIVED"); |
| } else { |
| paymentPreference.set("statusId", "PAYMENT_CANCELLED"); |
| } |
| toStore.add(paymentPreference); |
| |
| |
| Delegator delegator = paymentPreference.getDelegator(); |
| |
| // create the PaymentGatewayResponse |
| String responseId = delegator.getNextSeqId("PaymentGatewayResponse"); |
| GenericValue response = delegator.makeValue("PaymentGatewayResponse"); |
| response.set("paymentGatewayResponseId", responseId); |
| response.set("paymentServiceTypeEnumId", "PRDS_PAY_EXTERNAL"); |
| response.set("orderPaymentPreferenceId", paymentPreference.get("orderPaymentPreferenceId")); |
| response.set("paymentMethodTypeId", paymentPreference.get("paymentMethodTypeId")); |
| response.set("paymentMethodId", paymentPreference.get("paymentMethodId")); |
| |
| // set the auth info |
| response.set("amount", new BigDecimal(paymentAmount)); |
| response.set("referenceNum", transactionId); |
| response.set("gatewayCode", paymentStatus); |
| response.set("gatewayFlag", paymentStatus.substring(0,1)); |
| response.set("gatewayMessage", paymentType); |
| response.set("transactionDate", authDate); |
| toStore.add(response); |
| |
| try { |
| delegator.storeAll(toStore); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Cannot set payment preference/payment info", module); |
| return false; |
| } |
| |
| // create a payment record too |
| Map <String, Object> results = null; |
| try { |
| String comment = UtilProperties.getMessage(resource, "AccountingPaymentReceiveViaPayPal", locale); |
| results = dispatcher.runSync("createPaymentFromPreference", UtilMisc.toMap("userLogin", userLogin, |
| "orderPaymentPreferenceId", paymentPreference.get("orderPaymentPreferenceId"), "comments", comment)); |
| } catch (GenericServiceException e) { |
| Debug.logError(e, "Failed to execute service createPaymentFromPreference", module); |
| request.setAttribute("_ERROR_MESSAGE_", UtilProperties.getMessage(resourceErr, "payPalEvents.failedToExecuteServiceCreatePaymentFromPreference", locale)); |
| return false; |
| } |
| |
| if ((results == null) || (results.get(ModelService.RESPONSE_MESSAGE).equals(ModelService.RESPOND_ERROR))) { |
| Debug.logError((String) results.get(ModelService.ERROR_MESSAGE), module); |
| request.setAttribute("_ERROR_MESSAGE_", results.get(ModelService.ERROR_MESSAGE)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| private static String getPaymentGatewayConfigValue(Delegator delegator, String paymentGatewayConfigId, String paymentGatewayConfigParameterName, |
| String resource, String parameterName) { |
| String returnValue = ""; |
| if (UtilValidate.isNotEmpty(paymentGatewayConfigId)) { |
| try { |
| GenericValue payPal = EntityQuery.use(delegator).from("PaymentGatewayPayPal").where("paymentGatewayConfigId", paymentGatewayConfigId).queryOne(); |
| if (payPal != null) { |
| String payPalField = payPal.getString(paymentGatewayConfigParameterName); |
| if (payPalField != null) { |
| returnValue = payPalField.trim(); |
| } |
| } |
| } catch (GenericEntityException e) { |
| Debug.logError(e, module); |
| } |
| } else { |
| String value = EntityUtilProperties.getPropertyValue(resource, parameterName, delegator); |
| if (value != null) { |
| returnValue = value.trim(); |
| } |
| } |
| return returnValue; |
| } |
| } |