001: /*******************************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: *******************************************************************************/package org.ofbiz.accounting.thirdparty.paypal;
019:
020: import java.io.BufferedReader;
021: import java.io.IOException;
022: import java.io.InputStreamReader;
023: import java.io.PrintWriter;
024: import java.net.URL;
025: import java.net.URLConnection;
026: import java.text.ParseException;
027: import java.text.SimpleDateFormat;
028: import java.util.Iterator;
029: import java.util.LinkedList;
030: import java.util.List;
031: import java.util.Locale;
032: import java.util.Map;
033: import java.util.Set;
034: import javax.servlet.ServletRequest;
035: import javax.servlet.http.HttpServletRequest;
036: import javax.servlet.http.HttpServletResponse;
037:
038: import org.ofbiz.base.util.Debug;
039: import org.ofbiz.base.util.UtilDateTime;
040: import org.ofbiz.base.util.UtilFormatOut;
041: import org.ofbiz.base.util.UtilHttp;
042: import org.ofbiz.base.util.UtilMisc;
043: import org.ofbiz.base.util.UtilProperties;
044: import org.ofbiz.base.util.UtilValidate;
045: import org.ofbiz.entity.GenericDelegator;
046: import org.ofbiz.entity.GenericEntityException;
047: import org.ofbiz.entity.GenericValue;
048: import org.ofbiz.entity.transaction.GenericTransactionException;
049: import org.ofbiz.entity.transaction.TransactionUtil;
050: import org.ofbiz.order.order.OrderChangeHelper;
051: import org.ofbiz.product.catalog.CatalogWorker;
052: import org.ofbiz.product.store.ProductStoreWorker;
053: import org.ofbiz.service.GenericServiceException;
054: import org.ofbiz.service.ModelService;
055: import org.ofbiz.service.LocalDispatcher;
056:
057: import org.apache.commons.collections.map.LinkedMap;
058:
059: public class PayPalEvents {
060:
061: public static final String resource = "AccountingUiLabels";
062: public static final String module = PayPalEvents.class.getName();
063:
064: /** Initiate PayPal Request */
065: public static String callPayPal(HttpServletRequest request,
066: HttpServletResponse response) {
067: Locale locale = UtilHttp.getLocale(request);
068: GenericDelegator delegator = (GenericDelegator) request
069: .getAttribute("delegator");
070: GenericValue userLogin = (GenericValue) request.getSession()
071: .getAttribute("userLogin");
072:
073: // get the orderId
074: String orderId = (String) request.getAttribute("orderId");
075:
076: // get the order header
077: GenericValue orderHeader = null;
078: try {
079: orderHeader = delegator.findByPrimaryKey("OrderHeader",
080: UtilMisc.toMap("orderId", orderId));
081: } catch (GenericEntityException e) {
082: Debug.logError(e, "Cannot get the order header for order: "
083: + orderId, module);
084: request.setAttribute("_ERROR_MESSAGE_",
085: "Problems getting order header.");
086: return "error";
087: }
088:
089: // get the order total
090: String orderTotal = UtilFormatOut.formatPrice(orderHeader
091: .getDouble("grandTotal"));
092:
093: // get the webSiteId
094: String webSiteId = CatalogWorker.getWebSiteId(request);
095:
096: // get the product store
097: GenericValue productStore = ProductStoreWorker
098: .getProductStore(request);
099:
100: if (productStore == null) {
101: Debug.logError("ProductStore is null", module);
102: request
103: .setAttribute("_ERROR_MESSAGE_",
104: "Problems getting merchant configuration, please contact customer service.");
105: return "error";
106: }
107:
108: // get the payment properties file
109: GenericValue paymentConfig = ProductStoreWorker
110: .getProductStorePaymentSetting(delegator, productStore
111: .getString("productStoreId"), "EXT_PAYPAL",
112: null, true);
113: String configString = null;
114: if (paymentConfig != null) {
115: configString = paymentConfig
116: .getString("paymentPropertiesPath");
117: }
118:
119: if (configString == null) {
120: configString = "payment.properties";
121: }
122:
123: // get the company name
124: String company = UtilFormatOut.checkEmpty(productStore
125: .getString("companyName"), "");
126:
127: // create the item name
128: String itemName = "Order #" + orderId
129: + (company != null ? " from " + company : "");
130: String itemNumber = "0";
131:
132: // get the redirect url
133: String redirectUrl = UtilProperties.getPropertyValue(
134: configString, "payment.paypal.redirect");
135:
136: // get the notify url
137: String notifyUrl = UtilProperties.getPropertyValue(
138: configString, "payment.paypal.notify");
139:
140: // get the return urls
141: String returnUrl = UtilProperties.getPropertyValue(
142: configString, "payment.paypal.return");
143: String cancelReturnUrl = UtilProperties.getPropertyValue(
144: configString, "payment.paypal.cancelReturn");
145:
146: // get the image url
147: String imageUrl = UtilProperties.getPropertyValue(configString,
148: "payment.paypal.image");
149:
150: // get the paypal account
151: String payPalAccount = UtilProperties.getPropertyValue(
152: configString, "payment.paypal.business");
153:
154: if (UtilValidate.isEmpty(redirectUrl)
155: || UtilValidate.isEmpty(notifyUrl)
156: || UtilValidate.isEmpty(returnUrl)
157: || UtilValidate.isEmpty(cancelReturnUrl)
158: || UtilValidate.isEmpty(imageUrl)
159: || UtilValidate.isEmpty(payPalAccount)) {
160: Debug
161: .logError(
162: "Payment properties is not configured properly, some notify URL from PayPal is not correctly defined!",
163: module);
164: request
165: .setAttribute(
166: "_ERROR_MESSAGE_",
167: UtilProperties
168: .getMessage(
169: resource,
170: "payPalEvents.problemsGettingMerchantConfiguration",
171: locale));
172: return "error";
173: }
174:
175: // create the redirect string
176: Map parameters = new LinkedMap();
177: parameters.put("cmd", "_xclick");
178: parameters.put("business", payPalAccount);
179: parameters.put("item_name", itemName);
180: parameters.put("item_number", itemNumber);
181: parameters.put("invoice", orderId);
182: parameters.put("custom", userLogin.getString("userLoginId"));
183: parameters.put("amount", orderTotal);
184: parameters.put("return", returnUrl);
185: parameters.put("cancel_return", cancelReturnUrl);
186: parameters.put("notify_url", notifyUrl);
187: parameters.put("image_url", imageUrl);
188: parameters.put("no_note", "1"); // no notes allowed in paypal (not passed back)
189: parameters.put("no_shipping", "1"); // no shipping address required (local shipping used)
190:
191: String encodedParameters = UtilHttp.urlEncodeArgs(parameters,
192: false);
193: String redirectString = redirectUrl + "?" + encodedParameters;
194:
195: // set the order in the session for cancelled orders
196: request.getSession().setAttribute("PAYPAL_ORDER", orderId);
197:
198: // redirect to paypal
199: try {
200: response.sendRedirect(redirectString);
201: } catch (IOException e) {
202: Debug.logError(e, "Problems redirecting to PayPal", module);
203: request
204: .setAttribute("_ERROR_MESSAGE_",
205: "Problems connecting with PayPal, please contact customer service.");
206: return "error";
207: }
208:
209: return "success";
210: }
211:
212: /** PayPal Call-Back Event */
213: public static String payPalIPN(HttpServletRequest request,
214: HttpServletResponse response) {
215: GenericDelegator delegator = (GenericDelegator) request
216: .getAttribute("delegator");
217: LocalDispatcher dispatcher = (LocalDispatcher) request
218: .getAttribute("dispatcher");
219:
220: // get the webSiteId
221: String webSiteId = CatalogWorker.getWebSiteId(request);
222:
223: // get the product store
224: GenericValue productStore = ProductStoreWorker
225: .getProductStore(request);
226:
227: // get the payment properties file
228: GenericValue paymentConfig = ProductStoreWorker
229: .getProductStorePaymentSetting(delegator, productStore
230: .getString("productStoreId"), "EXT_PAYPAL",
231: null, true);
232: String configString = null;
233: if (paymentConfig != null) {
234: configString = paymentConfig
235: .getString("paymentPropertiesPath");
236: }
237:
238: if (configString == null) {
239: configString = "payment.properties";
240: }
241:
242: // get the confirm URL
243: String confirmUrl = UtilProperties.getPropertyValue(
244: configString, "payment.paypal.confirm");
245:
246: // get the redirect URL
247: String redirectUrl = UtilProperties.getPropertyValue(
248: configString, "payment.paypal.redirect");
249:
250: if (confirmUrl == null || redirectUrl == null) {
251: Debug
252: .logError(
253: "Payment properties is not configured properly, no confirm URL defined!",
254: module);
255: request
256: .setAttribute("_ERROR_MESSAGE_",
257: "PayPal has not been configured, please contact customer service.");
258: return "error";
259: }
260:
261: // first verify this is valid from PayPal
262: Map parametersMap = UtilHttp.getParameterMap(request);
263: parametersMap.put("cmd", "_notify-validate");
264:
265: // send off the confirm request
266: String confirmResp = null;
267:
268: try {
269: String str = UtilHttp.urlEncodeArgs(parametersMap);
270: URL u = new URL(redirectUrl);
271: URLConnection uc = u.openConnection();
272: uc.setDoOutput(true);
273: uc.setRequestProperty("Content-Type",
274: "application/x-www-form-urlencoded");
275: PrintWriter pw = new PrintWriter(uc.getOutputStream());
276: pw.println(str);
277: pw.close();
278:
279: BufferedReader in = new BufferedReader(
280: new InputStreamReader(uc.getInputStream()));
281: confirmResp = in.readLine();
282: in.close();
283: Debug.logError("PayPal Verification Response: "
284: + confirmResp, module);
285: } catch (IOException e) {
286: Debug.logError(e, "Problems sending verification message",
287: module);
288: }
289:
290: if (confirmResp.trim().equals("VERIFIED")) {
291: // we passed verification
292: Debug.logInfo("Got verification from PayPal, processing..",
293: module);
294: } else {
295: Debug
296: .logError(
297: "###### PayPal did not verify this request, need investigation!",
298: module);
299: Set keySet = parametersMap.keySet();
300: Iterator i = keySet.iterator();
301: while (i.hasNext()) {
302: String name = (String) i.next();
303: String value = request.getParameter(name);
304: Debug.logError("### Param: " + name + " => " + value,
305: module);
306: }
307: }
308:
309: // get the user
310: GenericValue userLogin = null;
311: String userLoginId = request.getParameter("custom");
312: if (userLoginId == null)
313: userLoginId = "admin";
314: try {
315: userLogin = delegator.findByPrimaryKey("UserLogin",
316: UtilMisc.toMap("userLoginId", userLoginId));
317: } catch (GenericEntityException e) {
318: Debug.logError(e, "Cannot get UserLogin for: "
319: + userLoginId + "; cannot continue", module);
320: request.setAttribute("_ERROR_MESSAGE_",
321: "Problems getting authentication user.");
322: return "error";
323: }
324:
325: // get the orderId
326: String orderId = request.getParameter("invoice");
327:
328: // get the order header
329: GenericValue orderHeader = null;
330: if (UtilValidate.isNotEmpty(orderId)) {
331: try {
332: orderHeader = delegator.findByPrimaryKey("OrderHeader",
333: UtilMisc.toMap("orderId", orderId));
334: } catch (GenericEntityException e) {
335: Debug.logError(e,
336: "Cannot get the order header for order: "
337: + orderId, module);
338: request.setAttribute("_ERROR_MESSAGE_",
339: "Problems getting order header.");
340: return "error";
341: }
342: } else {
343: Debug.logError(
344: "PayPal did not callback with a valid orderId!",
345: module);
346: request.setAttribute("_ERROR_MESSAGE_",
347: "No valid orderId returned with PayPal Callback.");
348: return "error";
349: }
350:
351: if (orderHeader == null) {
352: Debug.logError("Cannot get the order header for order: "
353: + orderId, module);
354: request
355: .setAttribute("_ERROR_MESSAGE_",
356: "Problems getting order header; not a valid orderId.");
357: return "error";
358: }
359:
360: // get payment data
361: String paymentCurrency = request.getParameter("mc_currency");
362: String paymentAmount = request.getParameter("mc_gross");
363: String paymentFee = request.getParameter("mc_fee");
364: String transactionId = request.getParameter("txn_id");
365:
366: // get the transaction status
367: String paymentStatus = request.getParameter("payment_status");
368:
369: // attempt to start a transaction
370: boolean okay = true;
371: boolean beganTransaction = false;
372: try {
373: beganTransaction = TransactionUtil.begin();
374:
375: if (paymentStatus.equals("Completed")) {
376: okay = OrderChangeHelper.approveOrder(dispatcher,
377: userLogin, orderId);
378: } else if (paymentStatus.equals("Failed")
379: || paymentStatus.equals("Denied")) {
380: okay = OrderChangeHelper.cancelOrder(dispatcher,
381: userLogin, orderId);
382: }
383:
384: if (okay) {
385: // set the payment preference
386: okay = setPaymentPreferences(delegator, dispatcher,
387: userLogin, orderId, request);
388: }
389: } catch (Exception e) {
390: String errMsg = "Error handling PayPal notification";
391: Debug.logError(e, errMsg, module);
392: try {
393: TransactionUtil.rollback(beganTransaction, errMsg, e);
394: } catch (GenericTransactionException gte2) {
395: Debug.logError(gte2, "Unable to rollback transaction",
396: module);
397: }
398: } finally {
399: if (!okay) {
400: try {
401: TransactionUtil.rollback(beganTransaction,
402: "Failure in processing PayPal callback",
403: null);
404: } catch (GenericTransactionException gte) {
405: Debug.logError(gte,
406: "Unable to rollback transaction", module);
407: }
408: } else {
409: try {
410: TransactionUtil.commit(beganTransaction);
411: } catch (GenericTransactionException gte) {
412: Debug.logError(gte, "Unable to commit transaction",
413: module);
414: }
415: }
416: }
417:
418: if (okay) {
419: // attempt to release the offline hold on the order (workflow)
420: OrderChangeHelper.releaseInitialOrderHold(dispatcher,
421: orderId);
422:
423: // call the email confirm service
424: Map emailContext = UtilMisc.toMap("orderId", orderId);
425: try {
426: Map emailResult = dispatcher.runSync(
427: "sendOrderConfirmation", emailContext);
428: } catch (GenericServiceException e) {
429: Debug.logError(e,
430: "Problems sending email confirmation", module);
431: }
432: }
433:
434: return "success";
435: }
436:
437: /** Event called when customer cancels a paypal order */
438: public static String cancelPayPalOrder(HttpServletRequest request,
439: HttpServletResponse response) {
440: LocalDispatcher dispatcher = (LocalDispatcher) request
441: .getAttribute("dispatcher");
442: GenericValue userLogin = (GenericValue) request.getSession()
443: .getAttribute("userLogin");
444:
445: // get the stored order id from the session
446: String orderId = (String) request.getSession().getAttribute(
447: "PAYPAL_ORDER");
448:
449: // attempt to start a transaction
450: boolean beganTransaction = false;
451: try {
452: beganTransaction = TransactionUtil.begin();
453: } catch (GenericTransactionException gte) {
454: Debug.logError(gte, "Unable to begin transaction", module);
455: }
456:
457: // cancel the order
458: boolean okay = OrderChangeHelper.cancelOrder(dispatcher,
459: userLogin, orderId);
460:
461: if (okay) {
462: try {
463: TransactionUtil.commit(beganTransaction);
464: } catch (GenericTransactionException gte) {
465: Debug.logError(gte, "Unable to commit transaction",
466: module);
467: }
468: } else {
469: try {
470: TransactionUtil.rollback(beganTransaction,
471: "Failure in processing PayPal cancel callback",
472: null);
473: } catch (GenericTransactionException gte) {
474: Debug.logError(gte, "Unable to rollback transaction",
475: module);
476: }
477: }
478:
479: // attempt to release the offline hold on the order (workflow)
480: if (okay)
481: OrderChangeHelper.releaseInitialOrderHold(dispatcher,
482: orderId);
483:
484: request.setAttribute("_EVENT_MESSAGE_",
485: "Previous PayPal order has been cancelled.");
486: return "success";
487: }
488:
489: private static boolean setPaymentPreferences(
490: GenericDelegator delegator, LocalDispatcher dispatcher,
491: GenericValue userLogin, String orderId,
492: ServletRequest request) {
493: Debug.logVerbose("Setting payment prefrences..", module);
494: List paymentPrefs = null;
495: try {
496: Map paymentFields = UtilMisc.toMap("orderId", orderId,
497: "statusId", "PAYMENT_NOT_RECEIVED");
498: paymentPrefs = delegator.findByAnd(
499: "OrderPaymentPreference", paymentFields);
500: } catch (GenericEntityException e) {
501: Debug.logError(e,
502: "Cannot get payment preferences for order #"
503: + orderId, module);
504: return false;
505: }
506: if (paymentPrefs != null && paymentPrefs.size() > 0) {
507: Iterator i = paymentPrefs.iterator();
508: while (i.hasNext()) {
509: GenericValue pref = (GenericValue) i.next();
510: boolean okay = setPaymentPreference(dispatcher,
511: userLogin, pref, request);
512: if (!okay)
513: return false;
514: }
515: }
516: return true;
517: }
518:
519: private static boolean setPaymentPreference(
520: LocalDispatcher dispatcher, GenericValue userLogin,
521: GenericValue paymentPreference, ServletRequest request) {
522: String paymentDate = request.getParameter("payment_date");
523: String paymentType = request.getParameter("payment_type");
524: String paymentAmount = request.getParameter("mc_gross");
525: String paymentStatus = request.getParameter("payment_status");
526: String transactionId = request.getParameter("txn_id");
527:
528: List toStore = new LinkedList();
529:
530: // PayPal returns the timestamp in the format 'hh:mm:ss Jan 1, 2000 PST'
531: // Parse this into a valid Timestamp Object
532: SimpleDateFormat sdf = new SimpleDateFormat(
533: "hh:mm:ss MMM d, yyyy z");
534: java.sql.Timestamp authDate = null;
535: try {
536: authDate = new java.sql.Timestamp(sdf.parse(paymentDate)
537: .getTime());
538: } catch (ParseException e) {
539: Debug.logError(e, "Cannot parse date string: "
540: + paymentDate, module);
541: authDate = UtilDateTime.nowTimestamp();
542: } catch (NullPointerException e) {
543: Debug.logError(e, "Cannot parse date string: "
544: + paymentDate, module);
545: authDate = UtilDateTime.nowTimestamp();
546: }
547:
548: paymentPreference.set("maxAmount", new Double(paymentAmount));
549: if (paymentStatus.equals("Completed")) {
550: paymentPreference.set("statusId", "PAYMENT_RECEIVED");
551: } else if (paymentStatus.equals("Pending")) {
552: paymentPreference.set("statusId", "PAYMENT_NOT_RECEIVED");
553: } else {
554: paymentPreference.set("statusId", "PAYMENT_CANCELLED");
555: }
556: toStore.add(paymentPreference);
557:
558: GenericDelegator delegator = paymentPreference.getDelegator();
559:
560: // create the PaymentGatewayResponse
561: String responseId = delegator
562: .getNextSeqId("PaymentGatewayResponse");
563: GenericValue response = delegator.makeValue(
564: "PaymentGatewayResponse", null);
565: response.set("paymentGatewayResponseId", responseId);
566: response.set("paymentServiceTypeEnumId", "PRDS_PAY_EXTERNAL");
567: response.set("orderPaymentPreferenceId", paymentPreference
568: .get("orderPaymentPreferenceId"));
569: response.set("paymentMethodTypeId", paymentPreference
570: .get("paymentMethodTypeId"));
571: response.set("paymentMethodId", paymentPreference
572: .get("paymentMethodId"));
573:
574: // set the auth info
575: response.set("amount", new Double(paymentAmount));
576: response.set("referenceNum", transactionId);
577: response.set("gatewayCode", paymentStatus);
578: response.set("gatewayFlag", paymentStatus.substring(0, 1));
579: response.set("gatewayMessage", paymentType);
580: response.set("transactionDate", authDate);
581: toStore.add(response);
582:
583: try {
584: delegator.storeAll(toStore);
585: } catch (GenericEntityException e) {
586: Debug.logError(e,
587: "Cannot set payment preference/payment info",
588: module);
589: return false;
590: }
591:
592: // create a payment record too
593: Map results = null;
594: try {
595: results = dispatcher.runSync("createPaymentFromPreference",
596: UtilMisc.toMap("userLogin", userLogin,
597: "orderPaymentPreferenceId",
598: paymentPreference
599: .get("orderPaymentPreferenceId"),
600: "comments", "Payment receive via PayPal"));
601: } catch (GenericServiceException e) {
602: Debug
603: .logError(
604: e,
605: "Failed to execute service createPaymentFromPreference",
606: module);
607: request.setAttribute("_ERROR_MESSAGE_", e.getMessage());
608: return false;
609: }
610:
611: if ((results == null)
612: || (results.get(ModelService.RESPONSE_MESSAGE)
613: .equals(ModelService.RESPOND_ERROR))) {
614: Debug.logError((String) results
615: .get(ModelService.ERROR_MESSAGE), module);
616: request.setAttribute("_ERROR_MESSAGE_", (String) results
617: .get(ModelService.ERROR_MESSAGE));
618: return false;
619: }
620:
621: return true;
622: }
623:
624: }
|