0001: /*******************************************************************************
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: *******************************************************************************/package org.ofbiz.accounting.payment;
0019:
0020: import java.text.DecimalFormat;
0021: import java.text.ParseException;
0022: import java.util.*;
0023: import java.math.BigDecimal;
0024: import java.sql.Timestamp;
0025:
0026: import org.ofbiz.accounting.invoice.InvoiceWorker;
0027: import org.ofbiz.base.util.Debug;
0028: import org.ofbiz.base.util.GeneralException;
0029: import org.ofbiz.base.util.StringUtil;
0030: import org.ofbiz.base.util.UtilDateTime;
0031: import org.ofbiz.base.util.UtilMisc;
0032: import org.ofbiz.base.util.UtilNumber;
0033: import org.ofbiz.base.util.UtilProperties;
0034: import org.ofbiz.base.util.UtilValidate;
0035: import org.ofbiz.entity.GenericDelegator;
0036: import org.ofbiz.entity.GenericEntity;
0037: import org.ofbiz.entity.GenericEntityException;
0038: import org.ofbiz.entity.GenericValue;
0039: import org.ofbiz.entity.condition.EntityCondition;
0040: import org.ofbiz.entity.condition.EntityConditionList;
0041: import org.ofbiz.entity.condition.EntityExpr;
0042: import org.ofbiz.entity.condition.EntityJoinOperator;
0043: import org.ofbiz.entity.condition.EntityOperator;
0044: import org.ofbiz.entity.model.ModelEntity;
0045: import org.ofbiz.entity.util.EntityListIterator;
0046: import org.ofbiz.entity.util.EntityUtil;
0047: import org.ofbiz.order.order.OrderChangeHelper;
0048: import org.ofbiz.order.order.OrderReadHelper;
0049: import org.ofbiz.party.contact.ContactHelper;
0050: import org.ofbiz.product.store.ProductStoreWorker;
0051: import org.ofbiz.security.Security;
0052: import org.ofbiz.service.*;
0053:
0054: /**
0055: * PaymentGatewayServices
0056: */
0057: public class PaymentGatewayServices {
0058:
0059: public static final String module = PaymentGatewayServices.class
0060: .getName();
0061: public static final String AUTH_SERVICE_TYPE = "PRDS_PAY_AUTH";
0062: public static final String REAUTH_SERVICE_TYPE = "PRDS_PAY_REAUTH";
0063: public static final String RELEASE_SERVICE_TYPE = "PRDS_PAY_RELEASE";
0064: public static final String CAPTURE_SERVICE_TYPE = "PRDS_PAY_CAPTURE";
0065: public static final String REFUND_SERVICE_TYPE = "PRDS_PAY_REFUND";
0066: public static final String CREDIT_SERVICE_TYPE = "PRDS_PAY_CREDIT";
0067: private static final int TX_TIME = 300;
0068: private static BigDecimal ZERO = new BigDecimal("0.00");
0069: private static int decimals = -1;
0070: private static int rounding = -1;
0071: static {
0072: decimals = UtilNumber.getBigDecimalScale("order.decimals");
0073: rounding = UtilNumber
0074: .getBigDecimalRoundingMode("order.rounding");
0075:
0076: // set zero to the proper scale
0077: if (decimals != -1)
0078: ZERO = ZERO.setScale(decimals);
0079: }
0080:
0081: /**
0082: * Authorizes a single order preference with an option to specify an amount. The result map has the Booleans
0083: * "errors" and "finished" which notify the user if there were any errors and if the authorizatoin was finished.
0084: * There is also a List "messages" for the authorization response messages and a Double, "processAmount" as the
0085: * amount processed.
0086: *
0087: * TODO: it might be nice to return the paymentGatewayResponseId
0088: */
0089: public static Map authOrderPaymentPreference(DispatchContext dctx,
0090: Map context) {
0091: GenericDelegator delegator = dctx.getDelegator();
0092: LocalDispatcher dispatcher = dctx.getDispatcher();
0093: GenericValue userLogin = (GenericValue) context
0094: .get("userLogin");
0095:
0096: String orderPaymentPreferenceId = (String) context
0097: .get("orderPaymentPreferenceId");
0098: Double overrideAmount = (Double) context.get("overrideAmount");
0099:
0100: // validate overrideAmount if its available
0101: if (overrideAmount != null) {
0102: if (overrideAmount.doubleValue() < 0)
0103: return ServiceUtil.returnError("Amount entered ("
0104: + overrideAmount + ") is negative.");
0105: if (overrideAmount.doubleValue() == 0)
0106: return ServiceUtil.returnError("Amount entered ("
0107: + overrideAmount + ") is zero.");
0108: }
0109:
0110: GenericValue orderHeader = null;
0111: GenericValue orderPaymentPreference = null;
0112: try {
0113: orderPaymentPreference = delegator.findByPrimaryKey(
0114: "OrderPaymentPreference", UtilMisc.toMap(
0115: "orderPaymentPreferenceId",
0116: orderPaymentPreferenceId));
0117: orderHeader = orderPaymentPreference
0118: .getRelatedOne("OrderHeader");
0119: } catch (GenericEntityException e) {
0120: Debug.logError(e, module);
0121: return ServiceUtil
0122: .returnError("Problems getting required information: orderPaymentPreference ["
0123: + orderPaymentPreferenceId + "]");
0124: }
0125: OrderReadHelper orh = new OrderReadHelper(orderHeader);
0126:
0127: // get the total remaining
0128: BigDecimal orderGrandTotal = orh.getOrderGrandTotalBd();
0129: orderGrandTotal = orderGrandTotal.setScale(decimals, rounding);
0130: double totalRemaining = orderGrandTotal.doubleValue();
0131:
0132: // get the process attempts so far
0133: Long procAttempt = orderPaymentPreference
0134: .getLong("processAttempt");
0135: if (procAttempt == null) {
0136: procAttempt = new Long(0);
0137: }
0138:
0139: // update the process attempt count
0140: orderPaymentPreference.set("processAttempt", new Long(
0141: procAttempt.longValue() + 1));
0142: try {
0143: orderPaymentPreference.store();
0144: orderPaymentPreference.refresh();
0145: } catch (GenericEntityException e) {
0146: Debug.logError(e, module);
0147: return ServiceUtil
0148: .returnError("Unable to update OrderPaymentPreference record!");
0149: }
0150:
0151: // if we are already authorized, then this is a re-auth request
0152: boolean reAuth = false;
0153: if (orderPaymentPreference.get("statusId") != null
0154: && "PAYMENT_AUTHORIZED".equals(orderPaymentPreference
0155: .getString("statusId"))) {
0156: reAuth = true;
0157: }
0158:
0159: // use overrideAmount or maxAmount
0160: Double transAmount = null;
0161: if (overrideAmount != null) {
0162: transAmount = overrideAmount;
0163: } else {
0164: transAmount = orderPaymentPreference.getDouble("maxAmount");
0165: }
0166:
0167: // if our transaction amount exists and is zero, there's nothing to process, so return
0168: if ((transAmount != null) && (transAmount.doubleValue() <= 0)) {
0169: Map results = ServiceUtil.returnSuccess();
0170: results.put("finished", Boolean.TRUE); // finished is true since there is nothing to do
0171: results.put("errors", Boolean.FALSE); // errors is false since no error occured
0172: return results;
0173: }
0174:
0175: try {
0176: // call the authPayment method
0177: Map authPaymentResult = authPayment(dispatcher, userLogin,
0178: orh, orderPaymentPreference, totalRemaining,
0179: reAuth, transAmount);
0180:
0181: // handle the response
0182: if (authPaymentResult != null) {
0183: // not null result means either an approval or decline; null would mean error
0184: Double this Amount = (Double) authPaymentResult
0185: .get("processAmount");
0186:
0187: // process the auth results
0188: try {
0189: boolean processResult = processResult(dctx,
0190: authPaymentResult, userLogin,
0191: orderPaymentPreference);
0192: if (processResult) {
0193: Map results = ServiceUtil.returnSuccess();
0194: results.put("messages", authPaymentResult
0195: .get("customerRespMsgs"));
0196: results.put("processAmount", this Amount);
0197: results.put("finished", Boolean.TRUE);
0198: results.put("errors", Boolean.FALSE);
0199: return results;
0200: } else {
0201: boolean needsNsfRetry = needsNsfRetry(
0202: orderPaymentPreference,
0203: authPaymentResult, delegator);
0204:
0205: // if we are doing an NSF retry then also...
0206: if (needsNsfRetry) {
0207: // TODO: what do we do with this? we need to fail the auth but still allow the order through so it can be fixed later
0208: // NOTE: this is called through a different path for auto re-orders, so it should be good to go... will leave this comment here just in case...
0209: }
0210:
0211: // if we have a failure at this point and no NSF retry is needed, then try other credit cards on file, if the user has any
0212: if (!needsNsfRetry) {
0213: // is this an auto-order?
0214: if (UtilValidate
0215: .isNotEmpty(orderHeader
0216: .getString("autoOrderShoppingListId"))) {
0217: GenericValue productStore = orderHeader
0218: .getRelatedOne("ProductStore");
0219: // according to the store should we try other cards?
0220: if ("Y"
0221: .equals(productStore
0222: .getString("autoOrderCcTryOtherCards"))) {
0223: // get other credit cards for the bill to party
0224: List otherPaymentMethodAndCreditCardList = null;
0225: String billToPartyId = null;
0226: GenericValue billToParty = orh
0227: .getBillToParty();
0228: if (billToParty != null) {
0229: billToPartyId = billToParty
0230: .getString("partyId");
0231: } else {
0232: // TODO optional: any other ways to find the bill to party? perhaps look at info from OrderPaymentPreference, ie search back from other PaymentMethod...
0233: }
0234:
0235: if (UtilValidate
0236: .isNotEmpty(billToPartyId)) {
0237: otherPaymentMethodAndCreditCardList = delegator
0238: .findByAnd(
0239: "PaymentMethodAndCreditCard",
0240: UtilMisc
0241: .toMap(
0242: "partyId",
0243: billToPartyId,
0244: "paymentMethodTypeId",
0245: "CREDIT_CARD"));
0246: otherPaymentMethodAndCreditCardList = EntityUtil
0247: .filterByDate(
0248: otherPaymentMethodAndCreditCardList,
0249: true);
0250: }
0251:
0252: if (otherPaymentMethodAndCreditCardList != null
0253: && otherPaymentMethodAndCreditCardList
0254: .size() > 0) {
0255: Iterator otherPaymentMethodAndCreditCardIter = otherPaymentMethodAndCreditCardList
0256: .iterator();
0257: while (otherPaymentMethodAndCreditCardIter
0258: .hasNext()) {
0259: GenericValue otherPaymentMethodAndCreditCard = (GenericValue) otherPaymentMethodAndCreditCardIter
0260: .next();
0261:
0262: // change OrderPaymentPreference in memory only and call auth service
0263: orderPaymentPreference
0264: .set(
0265: "paymentMethodId",
0266: otherPaymentMethodAndCreditCard
0267: .getString("paymentMethodId"));
0268: Map authRetryResult = authPayment(
0269: dispatcher,
0270: userLogin,
0271: orh,
0272: orderPaymentPreference,
0273: totalRemaining,
0274: reAuth, transAmount);
0275: try {
0276: boolean processRetryResult = processResult(
0277: dctx,
0278: authPaymentResult,
0279: userLogin,
0280: orderPaymentPreference);
0281:
0282: if (processRetryResult) {
0283: // wow, we got here that means the other card was successful...
0284: // on success save the OrderPaymentPreference, and then return finished (which will break from loop)
0285: orderPaymentPreference
0286: .store();
0287:
0288: Map results = ServiceUtil
0289: .returnSuccess();
0290: results
0291: .put(
0292: "messages",
0293: authRetryResult
0294: .get("customerRespMsgs"));
0295: results
0296: .put(
0297: "processAmount",
0298: this Amount);
0299: results
0300: .put(
0301: "finished",
0302: Boolean.TRUE);
0303: results
0304: .put(
0305: "errors",
0306: Boolean.FALSE);
0307: return results;
0308: }
0309: } catch (GeneralException e) {
0310: String errMsg = "Error saving and processing payment authorization results: "
0311: + e.toString();
0312: Debug
0313: .logError(
0314: e,
0315: errMsg
0316: + "; authRetryResult: "
0317: + authRetryResult,
0318: module);
0319: Map results = ServiceUtil
0320: .returnSuccess();
0321: results
0322: .put(
0323: ModelService.ERROR_MESSAGE,
0324: errMsg);
0325: results.put("finished",
0326: Boolean.FALSE);
0327: results.put("errors",
0328: Boolean.TRUE);
0329: return results;
0330: }
0331:
0332: // if no sucess, fall through to return not finished
0333: }
0334: }
0335: }
0336: }
0337: }
0338:
0339: Map results = ServiceUtil.returnSuccess();
0340: results.put("messages", authPaymentResult
0341: .get("customerRespMsgs"));
0342: results.put("finished", Boolean.FALSE);
0343: results.put("errors", Boolean.FALSE);
0344: return results;
0345: }
0346: } catch (GeneralException e) {
0347: String errMsg = "Error saving and processing payment authorization results: "
0348: + e.toString();
0349: Debug.logError(e, errMsg + "; authPaymentResult: "
0350: + authPaymentResult, module);
0351: Map results = ServiceUtil.returnSuccess();
0352: results.put(ModelService.ERROR_MESSAGE, errMsg);
0353: results.put("finished", Boolean.FALSE);
0354: results.put("errors", Boolean.TRUE);
0355: return results;
0356: }
0357: } else {
0358: // error with payment processor; will try later
0359: String errMsg = "Invalid Order Payment Preference: maxAmount is 0";
0360: Debug.logInfo(errMsg, module);
0361: Map results = ServiceUtil.returnSuccess();
0362: results.put("finished", Boolean.FALSE);
0363: results.put("errors", Boolean.TRUE);
0364: results.put(ModelService.ERROR_MESSAGE, errMsg);
0365:
0366: orderPaymentPreference.set("statusId",
0367: "PAYMENT_CANCELLED");
0368: try {
0369: orderPaymentPreference.store();
0370: } catch (GenericEntityException e) {
0371: Debug
0372: .logError(
0373: e,
0374: "ERROR: Problem setting OrderPaymentPreference status to CANCELLED",
0375: module);
0376: }
0377: return results;
0378: }
0379: } catch (GeneralException e) {
0380: String errMsg = "Error processing payment authorization: "
0381: + e.toString();
0382: Debug.logError(e, errMsg, module);
0383: return ServiceUtil.returnError(errMsg);
0384: }
0385: }
0386:
0387: /**
0388: * Processes payments through service calls to the defined processing service for the ProductStore/PaymentMethodType
0389: * @return APPROVED|FAILED|ERROR for complete processing of ALL payment methods.
0390: */
0391: public static Map authOrderPayments(DispatchContext dctx,
0392: Map context) {
0393: GenericDelegator delegator = dctx.getDelegator();
0394: LocalDispatcher dispatcher = dctx.getDispatcher();
0395: String orderId = (String) context.get("orderId");
0396: Map result = new HashMap();
0397:
0398: // get the order header and payment preferences
0399: GenericValue orderHeader = null;
0400: List paymentPrefs = null;
0401:
0402: try {
0403: // get the OrderHeader
0404: orderHeader = delegator.findByPrimaryKey("OrderHeader",
0405: UtilMisc.toMap("orderId", orderId));
0406:
0407: // get the payments to auth
0408: Map lookupMap = UtilMisc.toMap("orderId", orderId,
0409: "statusId", "PAYMENT_NOT_AUTH");
0410: List orderList = UtilMisc.toList("maxAmount");
0411: paymentPrefs = delegator.findByAnd(
0412: "OrderPaymentPreference", lookupMap, orderList);
0413: } catch (GenericEntityException gee) {
0414: Debug.logError(gee,
0415: "Problems getting the order information", module);
0416: result.put(ModelService.RESPONSE_MESSAGE,
0417: ModelService.RESPOND_ERROR);
0418: result.put(ModelService.ERROR_MESSAGE,
0419: "ERROR: Could not get order information ("
0420: + gee.toString() + ").");
0421: return result;
0422: }
0423:
0424: // make sure we have a OrderHeader
0425: if (orderHeader == null) {
0426: return ServiceUtil
0427: .returnError("Could not find OrderHeader with orderId: "
0428: + orderId + "; not processing payments.");
0429: }
0430:
0431: // get the order amounts
0432: OrderReadHelper orh = new OrderReadHelper(orderHeader);
0433: BigDecimal orderGrandTotal = orh.getOrderGrandTotalBd();
0434: orderGrandTotal = orderGrandTotal.setScale(decimals, rounding);
0435: double totalRemaining = orderGrandTotal.doubleValue();
0436:
0437: // loop through and auth each order payment preference
0438: int finished = 0;
0439: int hadError = 0;
0440: List messages = new ArrayList();
0441: Iterator payments = paymentPrefs.iterator();
0442: while (payments.hasNext()) {
0443: GenericValue paymentPref = (GenericValue) payments.next();
0444:
0445: Map authContext = new HashMap();
0446: authContext.put("orderPaymentPreferenceId", paymentPref
0447: .getString("orderPaymentPreferenceId"));
0448: authContext.put("userLogin", context.get("userLogin"));
0449:
0450: Map results = null;
0451: try {
0452: results = dispatcher.runSync(
0453: "authOrderPaymentPreference", authContext);
0454: } catch (GenericServiceException se) {
0455: Debug.logError(se,
0456: "Error in calling authOrderPaymentPreference from authOrderPayments: "
0457: + se.toString(), module);
0458: hadError += 1;
0459: messages
0460: .add("Could not authorize OrderPaymentPreference ["
0461: + paymentPref
0462: .getString("orderPaymentPreferenceId")
0463: + "] for order ["
0464: + orderId
0465: + "]: "
0466: + se.toString());
0467: continue;
0468: }
0469:
0470: if (ServiceUtil.isError(results)) {
0471: hadError += 1;
0472: messages
0473: .add("Could not authorize OrderPaymentPreference ["
0474: + paymentPref
0475: .getString("orderPaymentPreferenceId")
0476: + "] for order ["
0477: + orderId
0478: + "]: "
0479: + results
0480: .get(ModelService.ERROR_MESSAGE));
0481: continue;
0482: }
0483:
0484: if (((Boolean) results.get("finished")).booleanValue())
0485: finished += 1;
0486: if (((Boolean) results.get("errors")).booleanValue())
0487: hadError += 1;
0488: if (results.get("messages") != null)
0489: messages.addAll((List) results.get("messages"));
0490: if (results.get("processAmount") != null)
0491: totalRemaining -= ((Double) results
0492: .get("processAmount")).doubleValue();
0493: }
0494:
0495: Debug.logInfo("Finished with auth(s) checking results", module);
0496:
0497: // add messages to the result
0498: result.put("authResultMsgs", messages);
0499:
0500: if (hadError > 0) {
0501: Debug.logError("Error(s) (" + hadError
0502: + ") during auth; returning ERROR", module);
0503: result.put(ModelService.RESPONSE_MESSAGE,
0504: ModelService.RESPOND_SUCCESS);
0505: result.put("processResult", "ERROR");
0506: return result;
0507: } else if (finished == paymentPrefs.size()) {
0508: Debug.logInfo("All auth(s) passed total remaining : "
0509: + totalRemaining, module);
0510: result.put(ModelService.RESPONSE_MESSAGE,
0511: ModelService.RESPOND_SUCCESS);
0512: result.put("processResult", "APPROVED");
0513: return result;
0514: } else {
0515: Debug.logInfo("Only (" + finished
0516: + ") passed auth; returning FAILED", module);
0517: result.put(ModelService.RESPONSE_MESSAGE,
0518: ModelService.RESPOND_SUCCESS);
0519: result.put("processResult", "FAILED");
0520: return result;
0521: }
0522: }
0523:
0524: private static Map authPayment(LocalDispatcher dispatcher,
0525: GenericValue userLogin, OrderReadHelper orh,
0526: GenericValue paymentPreference, double totalRemaining,
0527: boolean reauth, Double overrideAmount)
0528: throws GeneralException {
0529: String paymentConfig = null;
0530: String serviceName = null;
0531:
0532: // get the payment settings i.e. serviceName and config properties file name
0533: String serviceType = AUTH_SERVICE_TYPE;
0534: if (reauth) {
0535: serviceType = REAUTH_SERVICE_TYPE;
0536: }
0537:
0538: GenericValue paymentSettings = getPaymentSettings(orh
0539: .getOrderHeader(), paymentPreference, serviceType,
0540: false);
0541: if (paymentSettings != null) {
0542: serviceName = paymentSettings.getString("paymentService");
0543: paymentConfig = paymentSettings
0544: .getString("paymentPropertiesPath");
0545: } else {
0546: throw new GeneralException(
0547: "Could not find any valid payment settings for order with ID ["
0548: + orh.getOrderId()
0549: + "], and payment operation (serviceType) ["
0550: + serviceType + "]");
0551: }
0552:
0553: // make sure the service name is not null
0554: if (serviceName == null) {
0555: throw new GeneralException(
0556: "Invalid payment processor, serviceName is null: "
0557: + paymentSettings);
0558: }
0559:
0560: // make the process context
0561: Map processContext = new HashMap();
0562:
0563: // get the visit record to obtain the client's IP address
0564: GenericValue orderHeader = orh.getOrderHeader();
0565: //if (orderHeader == null) {}
0566:
0567: String visitId = orderHeader.getString("visitId");
0568: GenericValue visit = null;
0569: if (visitId != null) {
0570: try {
0571: visit = orderHeader.getDelegator().findByPrimaryKey(
0572: "Visit", UtilMisc.toMap("visitId", visitId));
0573: } catch (GenericEntityException e) {
0574: Debug.logError(e, module);
0575: }
0576: }
0577:
0578: if (visit != null && visit.get("clientIpAddress") != null) {
0579: processContext.put("customerIpAddress", visit
0580: .getString("clientIpAddress"));
0581: }
0582:
0583: GenericValue productStore = orderHeader
0584: .getRelatedOne("ProductStore");
0585:
0586: processContext.put("userLogin", userLogin);
0587: processContext.put("orderId", orh.getOrderId());
0588: processContext.put("orderItems", orh.getOrderItems());
0589: processContext.put("shippingAddress", EntityUtil.getFirst(orh
0590: .getShippingLocations())); // TODO refactor the payment API to handle support all addresses
0591: processContext.put("paymentConfig", paymentConfig);
0592: processContext.put("currency", orh.getCurrency());
0593: processContext.put("orderPaymentPreference", paymentPreference);
0594: if (paymentPreference.get("securityCode") != null) {
0595: processContext.put("cardSecurityCode", paymentPreference
0596: .get("securityCode"));
0597: }
0598:
0599: // get the billing information
0600: getBillingInformation(orh, paymentPreference, processContext);
0601:
0602: // default charge is totalRemaining
0603: double this Amount = totalRemaining;
0604:
0605: // use override or max amount available
0606: if (overrideAmount != null) {
0607: this Amount = overrideAmount.doubleValue();
0608: } else if (paymentPreference.get("maxAmount") != null) {
0609: this Amount = paymentPreference.getDouble("maxAmount")
0610: .doubleValue();
0611: }
0612:
0613: // don't authorized more then what is required
0614: if (this Amount > totalRemaining) {
0615: this Amount = totalRemaining;
0616: }
0617:
0618: // format the decimal
0619: String currencyFormat = UtilProperties.getPropertyValue(
0620: "general.properties", "currency.decimal.format",
0621: "##0.00");
0622: DecimalFormat formatter = new DecimalFormat(currencyFormat);
0623: String amountString = formatter.format(this Amount);
0624: Double processAmount = null;
0625: try {
0626: processAmount = new Double(formatter.parse(amountString)
0627: .doubleValue());
0628: } catch (ParseException e) {
0629: Debug
0630: .logError(
0631: e,
0632: "Problems parsing string formatted double to Double",
0633: module);
0634: throw new GeneralException(
0635: "ParseException in number format", e);
0636: }
0637:
0638: if (Debug.verboseOn())
0639: Debug.logVerbose("Charging amount: " + processAmount,
0640: module);
0641: processContext.put("processAmount", processAmount);
0642:
0643: // invoke the processor
0644: Map processorResult = null;
0645: try {
0646: // invoke the payment processor; allow 5 minute transaction timeout and require a new tx; we'll capture the error and pass back nicely
0647:
0648: GenericValue creditCard = (GenericValue) processContext
0649: .get("creditCard");
0650:
0651: // only try other exp dates if orderHeader.autoOrderShoppingListId is not empty, productStore.autoOrderCcTryExp=Y and this payment is a creditCard
0652: boolean tryOtherExpDates = "Y".equals(productStore
0653: .getString("autoOrderCcTryExp"))
0654: && creditCard != null
0655: && UtilValidate.isNotEmpty(orderHeader
0656: .getString("autoOrderShoppingListId"));
0657:
0658: // if we are not trying other expire dates OR if we are and the date is after today, then run the service
0659: if (!tryOtherExpDates
0660: || UtilValidate.isDateAfterToday(creditCard
0661: .getString("expireDate"))) {
0662: processorResult = dispatcher.runSync(serviceName,
0663: processContext, TX_TIME, true);
0664: }
0665:
0666: // try other expire dates if the expireDate is not after today, or if we called the auth service and resultBadExpire = true
0667: if (tryOtherExpDates
0668: && (!UtilValidate.isDateAfterToday(creditCard
0669: .getString("expireDate")) || (processorResult != null && Boolean.TRUE
0670: .equals((Boolean) processorResult
0671: .get("resultBadExpire"))))) {
0672: // try adding 2, 3, 4 years later with the same month
0673: String expireDate = creditCard.getString("expireDate");
0674: int dateSlash1 = expireDate.indexOf("/");
0675: String month = expireDate.substring(0, dateSlash1);
0676: String year = expireDate.substring(dateSlash1 + 1);
0677:
0678: // start adding 2 years, if comes back with resultBadExpire try again up to twice incrementing one year
0679: year = StringUtil.addToNumberString(year, 2);
0680: // note that this is set in memory only for now, not saved to the database unless successful
0681: creditCard.set("expireDate", month + "/" + year);
0682: // don't need to set back in the processContext, it's already there: processContext.put("creditCard", creditCard);
0683: processorResult = dispatcher.runSync(serviceName,
0684: processContext, TX_TIME, true);
0685:
0686: // note that these additional tries will only be done if the service return is not an error, in that case we let it pass through to the normal error handling
0687: if (!ServiceUtil.isError(processorResult)
0688: && Boolean.TRUE
0689: .equals((Boolean) processorResult
0690: .get("resultBadExpire"))) {
0691: // okay, try one more year...
0692: year = StringUtil.addToNumberString(year, 1);
0693: creditCard.set("expireDate", month + "/" + year);
0694: processorResult = dispatcher.runSync(serviceName,
0695: processContext, TX_TIME, true);
0696: }
0697:
0698: if (!ServiceUtil.isError(processorResult)
0699: && Boolean.TRUE
0700: .equals((Boolean) processorResult
0701: .get("resultBadExpire"))) {
0702: // okay, try one more year... and this is the last try
0703: year = StringUtil.addToNumberString(year, 1);
0704: creditCard.set("expireDate", month + "/" + year);
0705: processorResult = dispatcher.runSync(serviceName,
0706: processContext, TX_TIME, true);
0707: }
0708:
0709: // at this point if we have a successful result, let's save the new creditCard expireDate
0710: if (!ServiceUtil.isError(processorResult)
0711: && Boolean.TRUE
0712: .equals((Boolean) processorResult
0713: .get("authResult"))) {
0714: // TODO: this is bad; we should be expiring the old card and creating a new one instead of editing it
0715: creditCard.store();
0716: }
0717: }
0718: } catch (GenericServiceException e) {
0719: Debug.logError(e, "Error occurred on: " + serviceName
0720: + " => " + processContext, module);
0721: throw new GeneralException(
0722: "Problems invoking payment processor! Will retry later. Order ID is: ["
0723: + orh.getOrderId() + "", e);
0724: }
0725:
0726: if (processorResult != null) {
0727: // check for errors from the processor implementation
0728: if (ServiceUtil.isError(processorResult)) {
0729: Debug.logError("Processor failed; will retry later: "
0730: + processorResult
0731: .get(ModelService.ERROR_MESSAGE),
0732: module);
0733: // log the error message as a gateway response when it fails
0734: saveError(dispatcher, userLogin, paymentPreference,
0735: processorResult, AUTH_SERVICE_TYPE,
0736: "PGT_AUTHORIZE");
0737: // this is the one place where we want to return null because the calling method will look for this
0738: return null;
0739: }
0740:
0741: // pass the payTo partyId to the result processor; we just add it to the result context.
0742: String payToPartyId = getPayToPartyId(orh.getOrderHeader());
0743: processorResult.put("payToPartyId", payToPartyId);
0744:
0745: // add paymentSettings to result; for use by later processors
0746: processorResult.put("paymentSettings", paymentSettings);
0747:
0748: // and pass on the currencyUomId
0749: processorResult.put("currencyUomId", orh.getCurrency());
0750: }
0751:
0752: return processorResult;
0753: }
0754:
0755: private static GenericValue getPaymentSettings(
0756: GenericValue orderHeader, GenericValue paymentPreference,
0757: String paymentServiceType, boolean anyServiceType) {
0758: GenericDelegator delegator = orderHeader.getDelegator();
0759: GenericValue paymentSettings = null;
0760: String paymentMethodTypeId = paymentPreference
0761: .getString("paymentMethodTypeId");
0762:
0763: if (paymentMethodTypeId != null) {
0764: String productStoreId = orderHeader
0765: .getString("productStoreId");
0766: if (productStoreId != null) {
0767: paymentSettings = ProductStoreWorker
0768: .getProductStorePaymentSetting(delegator,
0769: productStoreId, paymentMethodTypeId,
0770: paymentServiceType, anyServiceType);
0771: }
0772: }
0773: return paymentSettings;
0774: }
0775:
0776: private static String getPayToPartyId(GenericValue orderHeader) {
0777: String payToPartyId = "Company"; // default value
0778: GenericValue productStore = null;
0779: try {
0780: productStore = orderHeader.getRelatedOne("ProductStore");
0781: } catch (GenericEntityException e) {
0782: Debug.logError(e,
0783: "Unable to get ProductStore from OrderHeader",
0784: module);
0785: return null;
0786: }
0787: if (productStore != null
0788: && productStore.get("payToPartyId") != null) {
0789: payToPartyId = productStore.getString("payToPartyId");
0790: } else {
0791: Debug.logWarning(
0792: "Using default value of [Company] for payToPartyId on order ["
0793: + orderHeader.getString("orderId") + "]",
0794: module);
0795: }
0796: return payToPartyId;
0797: }
0798:
0799: private static String getBillingInformation(OrderReadHelper orh,
0800: GenericValue paymentPreference, Map toContext)
0801: throws GenericEntityException {
0802: // gather the payment related objects.
0803: String paymentMethodTypeId = paymentPreference
0804: .getString("paymentMethodTypeId");
0805: GenericValue paymentMethod = paymentPreference
0806: .getRelatedOne("PaymentMethod");
0807: if (paymentMethod != null
0808: && "CREDIT_CARD".equals(paymentMethodTypeId)) {
0809: // type credit card
0810: GenericValue creditCard = paymentMethod
0811: .getRelatedOne("CreditCard");
0812: GenericValue billingAddress = creditCard
0813: .getRelatedOne("PostalAddress");
0814: toContext.put("creditCard", creditCard);
0815: toContext.put("billingAddress", billingAddress);
0816: } else if (paymentMethod != null
0817: && "EFT_ACCOUNT".equals(paymentMethodTypeId)) {
0818: // type eft
0819: GenericValue eftAccount = paymentMethod
0820: .getRelatedOne("EftAccount");
0821: GenericValue billingAddress = eftAccount
0822: .getRelatedOne("PostalAddress");
0823: toContext.put("eftAccount", eftAccount);
0824: toContext.put("billingAddress", billingAddress);
0825: } else if (paymentMethod != null
0826: && "GIFT_CARD".equals(paymentMethodTypeId)) {
0827: // type gift card
0828: GenericValue giftCard = paymentMethod
0829: .getRelatedOne("GiftCard");
0830: toContext.put("giftCard", giftCard);
0831: } else if ("FIN_ACCOUNT".equals(paymentMethodTypeId)) {
0832: toContext.put("finAccountId", paymentPreference
0833: .getString("finAccountId"));
0834: } else {
0835: // add other payment types here; i.e. gift cards, etc.
0836: // unknown payment type; ignoring.
0837: Debug
0838: .logError(
0839: "ERROR: Unsupported PaymentMethodType passed for authorization",
0840: module);
0841: return null;
0842: }
0843:
0844: // get some contact info.
0845: GenericValue billToPersonOrGroup = orh.getBillToParty();
0846: GenericValue billToEmail = null;
0847:
0848: Collection emails = ContactHelper.getContactMech(
0849: billToPersonOrGroup.getRelatedOne("Party"),
0850: "PRIMARY_EMAIL", "EMAIL_ADDRESS", false);
0851:
0852: if (UtilValidate.isNotEmpty(emails)) {
0853: billToEmail = (GenericValue) emails.iterator().next();
0854: }
0855:
0856: toContext.put("billToParty", billToPersonOrGroup);
0857: toContext.put("billToEmail", billToEmail);
0858:
0859: return billToPersonOrGroup.getString("partyId");
0860: }
0861:
0862: /**
0863: *
0864: * Releases authorizations through service calls to the defined processing service for the ProductStore/PaymentMethodType
0865: * @return COMPLETE|FAILED|ERROR for complete processing of ALL payments.
0866: */
0867: public static Map releaseOrderPayments(DispatchContext dctx,
0868: Map context) {
0869: GenericDelegator delegator = dctx.getDelegator();
0870: LocalDispatcher dispatcher = dctx.getDispatcher();
0871: GenericValue userLogin = (GenericValue) context
0872: .get("userLogin");
0873: String orderId = (String) context.get("orderId");
0874:
0875: Map result = new HashMap();
0876:
0877: // get the payment preferences
0878: List paymentPrefs = null;
0879:
0880: try {
0881: // get the valid payment prefs
0882: List othExpr = UtilMisc.toList(new EntityExpr(
0883: "paymentMethodTypeId", EntityOperator.EQUALS,
0884: "EFT_ACCOUNT"));
0885: othExpr.add(new EntityExpr("paymentMethodTypeId",
0886: EntityOperator.EQUALS, "GIFT_CARD"));
0887: othExpr.add(new EntityExpr("paymentMethodTypeId",
0888: EntityOperator.EQUALS, "FIN_ACCOUNT"));
0889: EntityCondition con1 = new EntityConditionList(othExpr,
0890: EntityJoinOperator.OR);
0891:
0892: EntityCondition statExpr = new EntityExpr("statusId",
0893: EntityOperator.EQUALS, "PAYMENT_SETTLED");
0894: EntityCondition con2 = new EntityConditionList(UtilMisc
0895: .toList(con1, statExpr), EntityOperator.AND);
0896:
0897: EntityCondition authExpr = new EntityExpr("statusId",
0898: EntityOperator.EQUALS, "PAYMENT_AUTHORIZED");
0899: EntityCondition con3 = new EntityConditionList(UtilMisc
0900: .toList(con2, authExpr), EntityOperator.OR);
0901:
0902: EntityExpr orderExpr = new EntityExpr("orderId",
0903: EntityOperator.EQUALS, orderId);
0904: EntityCondition con4 = new EntityConditionList(UtilMisc
0905: .toList(con3, orderExpr), EntityOperator.AND);
0906:
0907: paymentPrefs = delegator.findByCondition(
0908: "OrderPaymentPreference", con4, null, null);
0909: } catch (GenericEntityException gee) {
0910: Debug
0911: .logError(
0912: gee,
0913: "Problems getting entity record(s), see stack trace",
0914: module);
0915: result.put(ModelService.RESPONSE_MESSAGE,
0916: ModelService.RESPOND_ERROR);
0917: result.put(ModelService.ERROR_MESSAGE,
0918: "ERROR: Could not get order information ("
0919: + gee.toString() + ").");
0920: return result;
0921: }
0922:
0923: // return complete if no payment prefs were found
0924: if (paymentPrefs == null || paymentPrefs.size() == 0) {
0925: Debug
0926: .logWarning(
0927: "No OrderPaymentPreference records available for release",
0928: module);
0929: result.put("processResult", "COMPLETE");
0930: result.put(ModelService.RESPONSE_MESSAGE,
0931: ModelService.RESPOND_SUCCESS);
0932: return result;
0933: }
0934:
0935: // iterate over the prefs and release each one
0936: List finished = new ArrayList();
0937: Iterator payments = paymentPrefs.iterator();
0938: while (payments.hasNext()) {
0939: GenericValue paymentPref = (GenericValue) payments.next();
0940: Map releaseContext = UtilMisc.toMap("userLogin", userLogin,
0941: "orderPaymentPreferenceId", paymentPref
0942: .getString("orderPaymentPreferenceId"));
0943: Map releaseResult = null;
0944: try {
0945: releaseResult = dispatcher
0946: .runSync("releaseOrderPaymentPreference",
0947: releaseContext);
0948: } catch (GenericServiceException e) {
0949: String errMsg = "Problem calling releaseOrderPaymentPreference service for orderPaymentPreferenceId"
0950: + paymentPref
0951: .getString("orderPaymentPreferenceId");
0952: Debug.logError(e, errMsg, module);
0953: return ServiceUtil.returnError(errMsg);
0954: }
0955: if (ServiceUtil.isError(releaseResult)) {
0956: Debug.logError(ServiceUtil
0957: .getErrorMessage(releaseResult), module);
0958: return ServiceUtil.returnError(ServiceUtil
0959: .getErrorMessage(releaseResult));
0960: } else if (!ServiceUtil.isFailure(releaseResult)) {
0961: finished.add(paymentPref);
0962: }
0963: }
0964: result = ServiceUtil.returnSuccess();
0965: if (finished.size() == paymentPrefs.size()) {
0966: result.put("processResult", "COMPLETE");
0967: } else {
0968: result.put("processResult", "FAILED");
0969: }
0970:
0971: return result;
0972: }
0973:
0974: /**
0975: *
0976: * Releases authorization for a single OrderPaymentPreference through service calls to the defined processing service for the ProductStore/PaymentMethodType
0977: * @return SUCCESS|FAILED|ERROR for complete processing of payment.
0978: */
0979: public static Map releaseOrderPaymentPreference(
0980: DispatchContext dctx, Map context) {
0981: GenericDelegator delegator = dctx.getDelegator();
0982: LocalDispatcher dispatcher = dctx.getDispatcher();
0983: GenericValue userLogin = (GenericValue) context
0984: .get("userLogin");
0985: String orderPaymentPreferenceId = (String) context
0986: .get("orderPaymentPreferenceId");
0987:
0988: Map result = ServiceUtil.returnSuccess();
0989:
0990: // Get the OrderPaymentPreference
0991: GenericValue paymentPref = null;
0992: try {
0993: paymentPref = delegator.findByPrimaryKey(
0994: "OrderPaymentPreference", UtilMisc.toMap(
0995: "orderPaymentPreferenceId",
0996: orderPaymentPreferenceId));
0997: } catch (GenericEntityException e) {
0998: String errMsg = "Problem getting OrderPaymentPreference for orderPaymentPreferenceId "
0999: + orderPaymentPreferenceId;
1000: Debug.logWarning(e, errMsg, module);
1001: return ServiceUtil.returnError(errMsg);
1002: }
1003:
1004: // Error if no OrderPaymentPreference was found
1005: if (paymentPref == null) {
1006: String errMsg = "Could not find OrderPaymentPreference with orderPaymentPreferenceId: "
1007: + orderPaymentPreferenceId;
1008: Debug.logWarning(errMsg, module);
1009: return ServiceUtil.returnError(errMsg);
1010: }
1011:
1012: // Get the OrderHeader
1013: GenericValue orderHeader = null;
1014: try {
1015: orderHeader = delegator.findByPrimaryKey("OrderHeader",
1016: UtilMisc.toMap("orderId", paymentPref
1017: .getString("orderId")));
1018: } catch (GenericEntityException e) {
1019: String errMsg = "Problem getting OrderHeader for orderId "
1020: + paymentPref.getString("orderId");
1021: Debug.logWarning(e, errMsg, module);
1022: return ServiceUtil.returnError(errMsg);
1023: }
1024:
1025: // Error if no OrderHeader was found
1026: if (orderHeader == null) {
1027: String errMsg = "Could not find OrderHeader with orderId: "
1028: + paymentPref.getString("orderId")
1029: + "; not processing payments.";
1030: Debug.logWarning(errMsg, module);
1031: return ServiceUtil.returnError(errMsg);
1032: }
1033:
1034: OrderReadHelper orh = new OrderReadHelper(orderHeader);
1035: String currency = orh.getCurrency();
1036:
1037: // look up the payment configuration settings
1038: String serviceName = null;
1039: String paymentConfig = null;
1040:
1041: // get the payment settings i.e. serviceName and config properties file name
1042: GenericValue paymentSettings = getPaymentSettings(orderHeader,
1043: paymentPref, RELEASE_SERVICE_TYPE, false);
1044: if (paymentSettings != null) {
1045: paymentConfig = paymentSettings
1046: .getString("paymentPropertiesPath");
1047: serviceName = paymentSettings.getString("paymentService");
1048: if (serviceName == null) {
1049: String errMsg = "No payment release service for - "
1050: + paymentPref.getString("paymentMethodTypeId");
1051: Debug.logWarning(errMsg, module);
1052: return ServiceUtil.returnError(errMsg);
1053: }
1054: } else {
1055: String errMsg = "No payment release settings found for - "
1056: + paymentPref.getString("paymentMethodTypeId");
1057: Debug.logWarning(errMsg, module);
1058: return ServiceUtil.returnError(errMsg);
1059: }
1060:
1061: if (paymentConfig == null || paymentConfig.length() == 0) {
1062: paymentConfig = "payment.properties";
1063: }
1064:
1065: GenericValue authTransaction = PaymentGatewayServices
1066: .getAuthTransaction(paymentPref);
1067: Map releaseContext = new HashMap();
1068: releaseContext.put("orderPaymentPreference", paymentPref);
1069: releaseContext.put("releaseAmount", authTransaction
1070: .getDouble("amount"));
1071: releaseContext.put("currency", currency);
1072: releaseContext.put("paymentConfig", paymentConfig);
1073: releaseContext.put("userLogin", userLogin);
1074:
1075: // run the defined service
1076: Map releaseResult = null;
1077: try {
1078: releaseResult = dispatcher.runSync(serviceName,
1079: releaseContext, TX_TIME, true);
1080: } catch (GenericServiceException e) {
1081: String errMsg = "Problem releasing payment";
1082: Debug.logError(e, errMsg, module);
1083: return ServiceUtil.returnError(errMsg);
1084: }
1085:
1086: // get the release result code
1087: if (releaseResult != null
1088: && !ServiceUtil.isError(releaseResult)) {
1089: Map releaseResRes;
1090: try {
1091: ModelService model = dctx
1092: .getModelService("processReleaseResult");
1093: releaseResult
1094: .put("orderPaymentPreference", paymentPref);
1095: releaseResult.put("userLogin", userLogin);
1096: Map resCtx = model.makeValid(releaseResult,
1097: ModelService.IN_PARAM);
1098: releaseResRes = dispatcher.runSync(model.name, resCtx);
1099: } catch (GenericServiceException e) {
1100: Debug.logError(e, module);
1101: return ServiceUtil
1102: .returnError("Trouble processing the release results: "
1103: + e.getMessage());
1104: }
1105: if (releaseResRes != null
1106: && ServiceUtil.isError(releaseResRes)) {
1107: return ServiceUtil.returnError(ServiceUtil
1108: .getErrorMessage(releaseResRes));
1109: }
1110: } else if (ServiceUtil.isError(releaseResult)) {
1111: saveError(dispatcher, userLogin, paymentPref,
1112: releaseResult, RELEASE_SERVICE_TYPE, "PGT_RELEASE");
1113: result = ServiceUtil.returnError(ServiceUtil
1114: .getErrorMessage(releaseResult));
1115: }
1116:
1117: return result;
1118: }
1119:
1120: public static Map processReleaseResult(DispatchContext dctx,
1121: Map context) {
1122: GenericDelegator delegator = dctx.getDelegator();
1123:
1124: GenericValue paymentPref = (GenericValue) context
1125: .get("orderPaymentPreference");
1126: Boolean releaseResponse = (Boolean) context
1127: .get("releaseResult");
1128:
1129: // create the PaymentGatewayResponse
1130: String responseId = delegator
1131: .getNextSeqId("PaymentGatewayResponse");
1132: GenericValue pgResponse = delegator.makeValue(
1133: "PaymentGatewayResponse", null);
1134: pgResponse.set("paymentGatewayResponseId", responseId);
1135: pgResponse
1136: .set("paymentServiceTypeEnumId", RELEASE_SERVICE_TYPE);
1137: pgResponse.set("orderPaymentPreferenceId", paymentPref
1138: .get("orderPaymentPreferenceId"));
1139: pgResponse.set("paymentMethodTypeId", paymentPref
1140: .get("paymentMethodTypeId"));
1141: pgResponse.set("paymentMethodId", paymentPref
1142: .get("paymentMethodId"));
1143: pgResponse.set("transCodeEnumId", "PGT_RELEASE");
1144:
1145: // set the release info
1146: pgResponse.set("referenceNum", context.get("releaseRefNum"));
1147: pgResponse.set("altReference", context.get("releaseAltRefNum"));
1148: pgResponse.set("gatewayCode", context.get("releaseCode"));
1149: pgResponse.set("gatewayFlag", context.get("releaseFlag"));
1150: pgResponse.set("gatewayMessage", context.get("releaseMessage"));
1151: pgResponse.set("transactionDate", UtilDateTime.nowTimestamp());
1152:
1153: // store the gateway response
1154: savePgr(dctx, pgResponse);
1155:
1156: // create the internal messages
1157: List messages = (List) context.get("internalRespMsgs");
1158: if (messages != null && messages.size() > 0) {
1159: Iterator i = messages.iterator();
1160: while (i.hasNext()) {
1161: GenericValue respMsg = delegator.makeValue(
1162: "PaymentGatewayRespMsg", null);
1163: String respMsgId = delegator
1164: .getNextSeqId("PaymentGatewayRespMsg");
1165: String message = (String) i.next();
1166: respMsg.set("paymentGatewayRespMsgId", respMsgId);
1167: respMsg.set("paymentGatewayResponseId", responseId);
1168: respMsg.set("pgrMessage", message);
1169:
1170: // store the messages
1171: savePgr(dctx, respMsg);
1172: }
1173: }
1174:
1175: if (releaseResponse != null && releaseResponse.booleanValue()) {
1176: paymentPref.set("statusId", "PAYMENT_CANCELLED");
1177: try {
1178: paymentPref.store();
1179: } catch (GenericEntityException e) {
1180: Debug
1181: .logError(
1182: e,
1183: "Problem storing updated payment preference; authorization was released!",
1184: module);
1185: }
1186:
1187: // cancel any payment records
1188: List paymentList = null;
1189: try {
1190: paymentList = paymentPref.getRelated("Payment");
1191: } catch (GenericEntityException e) {
1192: Debug.logError(e,
1193: "Unable to get Payment records from OrderPaymentPreference : "
1194: + paymentPref, module);
1195: }
1196:
1197: if (paymentList != null) {
1198: Iterator pi = paymentList.iterator();
1199: while (pi.hasNext()) {
1200: GenericValue pay = (GenericValue) pi.next();
1201: pay.set("statusId", "PMNT_CANCELLED");
1202: try {
1203: pay.store();
1204: } catch (GenericEntityException e) {
1205: Debug.logError(e, "Unable to store Payment : "
1206: + pay, module);
1207: }
1208: }
1209: }
1210: } else {
1211: String errMsg = "Release failed for pref : " + paymentPref;
1212: Debug.logError(errMsg, module);
1213: return ServiceUtil.returnFailure(errMsg);
1214: }
1215:
1216: return ServiceUtil.returnSuccess();
1217: }
1218:
1219: /**
1220: * Captures payments through service calls to the defined processing service for the ProductStore/PaymentMethodType
1221: * @return COMPLETE|FAILED|ERROR for complete processing of ALL payment methods.
1222: */
1223: public static Map capturePaymentsByInvoice(DispatchContext dctx,
1224: Map context) {
1225: GenericDelegator delegator = dctx.getDelegator();
1226: LocalDispatcher dispatcher = dctx.getDispatcher();
1227: GenericValue userLogin = (GenericValue) context
1228: .get("userLogin");
1229: String invoiceId = (String) context.get("invoiceId");
1230:
1231: // lookup the invoice
1232: GenericValue invoice = null;
1233: try {
1234: invoice = delegator.findByPrimaryKey("Invoice", UtilMisc
1235: .toMap("invoiceId", invoiceId));
1236: } catch (GenericEntityException e) {
1237: Debug.logError(e, "Trouble looking up Invoice #"
1238: + invoiceId, module);
1239: return ServiceUtil
1240: .returnError("Trouble looking up Invoice #"
1241: + invoiceId);
1242: }
1243:
1244: if (invoice == null) {
1245: Debug.logError("Could not locate invoice #" + invoiceId,
1246: module);
1247: return ServiceUtil.returnError("Could not locate invoice #"
1248: + invoiceId);
1249: }
1250:
1251: // get the OrderItemBilling records for this invoice
1252: List orderItemBillings = null;
1253: try {
1254: orderItemBillings = invoice.getRelated("OrderItemBilling");
1255: } catch (GenericEntityException e) {
1256: Debug.logError(
1257: "Trouble getting OrderItemBilling(s) from Invoice #"
1258: + invoiceId, module);
1259: return ServiceUtil
1260: .returnError("Trouble getting OrderItemBilling(s) from Invoice #"
1261: + invoiceId);
1262: }
1263:
1264: // check for an associated billing account
1265: String billingAccountId = invoice.getString("billingAccountId");
1266:
1267: // make sure they are all for the same order
1268: String testOrderId = null;
1269: boolean allSameOrder = true;
1270: if (orderItemBillings != null) {
1271: Iterator oii = orderItemBillings.iterator();
1272: while (oii.hasNext()) {
1273: GenericValue oib = (GenericValue) oii.next();
1274: String orderId = oib.getString("orderId");
1275: if (testOrderId == null) {
1276: testOrderId = orderId;
1277: } else {
1278: if (!orderId.equals(testOrderId)) {
1279: allSameOrder = false;
1280: break;
1281: }
1282: }
1283: }
1284: }
1285:
1286: if (testOrderId == null || !allSameOrder) {
1287: Debug.logWarning("Attempt to settle Invoice #" + invoiceId
1288: + " which contained none/multiple orders", module);
1289: return ServiceUtil.returnSuccess();
1290: }
1291:
1292: // get the invoice amount (amount to bill)
1293: double invoiceTotal = InvoiceWorker.getInvoiceNotApplied(
1294: invoice).doubleValue();
1295: if (Debug.infoOn())
1296: Debug.logInfo("(Capture) Invoice [#" + invoiceId
1297: + "] total: " + invoiceTotal, module);
1298:
1299: // now capture the order
1300: Map serviceContext = UtilMisc.toMap("userLogin", userLogin,
1301: "orderId", testOrderId, "invoiceId", invoiceId,
1302: "captureAmount", new Double(invoiceTotal));
1303: if (UtilValidate.isNotEmpty(billingAccountId)) {
1304: serviceContext.put("billingAccountId", billingAccountId);
1305: }
1306: try {
1307: return dispatcher.runSync("captureOrderPayments",
1308: serviceContext);
1309: } catch (GenericServiceException e) {
1310: Debug.logError(e,
1311: "Trouble running captureOrderPayments service",
1312: module);
1313: return ServiceUtil
1314: .returnError("Trouble running captureOrderPayments service");
1315: }
1316: }
1317:
1318: /**
1319: * Captures payments through service calls to the defined processing service for the ProductStore/PaymentMethodType
1320: * @return COMPLETE|FAILED|ERROR for complete processing of ALL payment methods.
1321: */
1322: public static Map captureOrderPayments(DispatchContext dctx,
1323: Map context) {
1324: GenericDelegator delegator = dctx.getDelegator();
1325: LocalDispatcher dispatcher = dctx.getDispatcher();
1326: GenericValue userLogin = (GenericValue) context
1327: .get("userLogin");
1328: String orderId = (String) context.get("orderId");
1329: String invoiceId = (String) context.get("invoiceId");
1330: String billingAccountId = (String) context
1331: .get("billingAccountId");
1332: Double captureAmount = (Double) context.get("captureAmount");
1333: BigDecimal amountToCapture = new BigDecimal(captureAmount
1334: .doubleValue());
1335: amountToCapture = amountToCapture.setScale(decimals, rounding);
1336:
1337: // get the order header and payment preferences
1338: GenericValue orderHeader = null;
1339: List paymentPrefs = null;
1340: List paymentPrefsBa = null;
1341:
1342: try {
1343: orderHeader = delegator.findByPrimaryKey("OrderHeader",
1344: UtilMisc.toMap("orderId", orderId));
1345:
1346: // get the payment prefs
1347: Map lookupMap = UtilMisc.toMap("orderId", orderId,
1348: "statusId", "PAYMENT_AUTHORIZED");
1349: List orderList = UtilMisc.toList("-maxAmount");
1350: paymentPrefs = delegator.findByAnd(
1351: "OrderPaymentPreference", lookupMap, orderList);
1352:
1353: if (UtilValidate.isNotEmpty(billingAccountId)) {
1354: lookupMap = UtilMisc.toMap("orderId", orderId,
1355: "paymentMethodTypeId", "EXT_BILLACT",
1356: "statusId", "PAYMENT_NOT_RECEIVED");
1357: paymentPrefsBa = delegator.findByAnd(
1358: "OrderPaymentPreference", lookupMap, orderList);
1359: }
1360: } catch (GenericEntityException gee) {
1361: Debug
1362: .logError(
1363: gee,
1364: "Problems getting entity record(s), see stack trace",
1365: module);
1366: return ServiceUtil
1367: .returnError("ERROR: Could not get order information ("
1368: + gee.toString() + ").");
1369: }
1370:
1371: // error if no order was found
1372: if (orderHeader == null) {
1373: return ServiceUtil
1374: .returnError("Could not find OrderHeader with orderId: "
1375: + orderId + "; not processing payments.");
1376: }
1377:
1378: // Check if the outstanding amount for the order is greater than the
1379: // amount that we are going to capture.
1380: OrderReadHelper orh = new OrderReadHelper(orderHeader);
1381: BigDecimal orderGrandTotal = orh.getOrderGrandTotalBd();
1382: orderGrandTotal = orderGrandTotal.setScale(decimals, rounding);
1383: BigDecimal totalPayments = PaymentWorker.getPaymentsTotal(orh
1384: .getOrderPayments());
1385: totalPayments = totalPayments.setScale(decimals, rounding);
1386: BigDecimal remainingTotalBd = orderGrandTotal
1387: .subtract(totalPayments);
1388: if (Debug.infoOn())
1389: Debug.logInfo("The Remaining Total for order: " + orderId
1390: + " is: " + remainingTotalBd, module);
1391: // The amount to capture cannot be greater than the remaining total
1392: amountToCapture = amountToCapture.min(remainingTotalBd);
1393: if (Debug.infoOn())
1394: Debug.logInfo("Actual Expected Capture Amount : "
1395: + amountToCapture, module);
1396:
1397: // Process billing accounts payments
1398: if (UtilValidate.isNotEmpty(paymentPrefsBa)) {
1399: Iterator paymentsBa = paymentPrefsBa.iterator();
1400: while (paymentsBa.hasNext()) {
1401: GenericValue paymentPref = (GenericValue) paymentsBa
1402: .next();
1403:
1404: BigDecimal authAmount = paymentPref
1405: .getBigDecimal("maxAmount");
1406: if (authAmount == null)
1407: authAmount = ZERO;
1408: authAmount = authAmount.setScale(decimals, rounding);
1409:
1410: if (authAmount.compareTo(ZERO) == 0) {
1411: // nothing to capture
1412: Debug.logInfo("Nothing to capture; authAmount = 0",
1413: module);
1414: continue;
1415: }
1416: // the amount for *this* capture
1417: BigDecimal amountThisCapture = amountToCapture
1418: .min(authAmount);
1419:
1420: // decrease amount of next payment preference to capture
1421: amountToCapture = amountToCapture
1422: .subtract(amountThisCapture);
1423:
1424: // If we have an invoice, we find unapplied payments associated
1425: // to the billing account and we apply them to the invoice
1426: if (UtilValidate.isNotEmpty(invoiceId)) {
1427: Map captureResult = null;
1428: try {
1429: captureResult = dispatcher.runSync(
1430: "captureBillingAccountPayments",
1431: UtilMisc.toMap("invoiceId", invoiceId,
1432: "billingAccountId",
1433: billingAccountId,
1434: "captureAmount",
1435: new Double(amountThisCapture
1436: .doubleValue()),
1437: "orderId", orderId,
1438: "userLogin", userLogin));
1439: if (ServiceUtil.isError(captureResult)) {
1440: return captureResult;
1441: }
1442: } catch (GenericServiceException ex) {
1443: return ServiceUtil.returnError(ex.getMessage());
1444: }
1445: if (captureResult != null) {
1446:
1447: Double amountCaptured = (Double) captureResult
1448: .get("captureAmount");
1449: Debug
1450: .logInfo(
1451: "Amount captured for order ["
1452: + orderId
1453: + "] from unapplied payments associated to billing account ["
1454: + billingAccountId
1455: + "] is: "
1456: + amountCaptured,
1457: module);
1458:
1459: // big decimal reference to the capture amount
1460: BigDecimal amountCapturedBd = new BigDecimal(
1461: amountCaptured.doubleValue());
1462: amountCapturedBd = amountCapturedBd.setScale(
1463: decimals, rounding);
1464:
1465: if (amountCapturedBd.compareTo(ZERO) == 0) {
1466: continue;
1467: }
1468: // add the invoiceId to the result for processing
1469: captureResult.put("invoiceId", invoiceId);
1470: captureResult
1471: .put("captureResult", Boolean.TRUE);
1472: captureResult.put("orderPaymentPreference",
1473: paymentPref);
1474: captureResult.put("captureRefNum", ""); // FIXME: this is an hack to avoid a service validation error for processCaptureResult (captureRefNum is mandatory, but it is not used for billing accounts)
1475:
1476: // process the capture's results
1477: try {
1478: // the following method will set on the OrderPaymentPreference:
1479: // maxAmount = amountCapturedBd and
1480: // statusId = PAYMENT_RECEIVED
1481: processResult(dctx, captureResult,
1482: userLogin, paymentPref);
1483: } catch (GeneralException e) {
1484: Debug.logError(e,
1485: "Trouble processing the result; captureResult: "
1486: + captureResult, module);
1487: return ServiceUtil
1488: .returnError("Trouble processing the capture results");
1489: }
1490:
1491: // create any splits which are needed
1492: if (authAmount.compareTo(amountCapturedBd) == 1) {
1493: BigDecimal splitAmount = authAmount
1494: .subtract(amountCapturedBd);
1495: try {
1496: Map splitCtx = UtilMisc.toMap(
1497: "userLogin", userLogin,
1498: "orderPaymentPreference",
1499: paymentPref, "splitAmount",
1500: splitAmount);
1501: dispatcher.addCommitService(
1502: "processCaptureSplitPayment",
1503: splitCtx, true);
1504: } catch (GenericServiceException e) {
1505: Debug
1506: .logWarning(
1507: e,
1508: "Problem processing the capture split payment",
1509: module);
1510: }
1511: Debug.logInfo("Captured: "
1512: + amountThisCapture
1513: + " Remaining (re-auth): "
1514: + splitAmount, module);
1515: }
1516: } else {
1517: Debug.logError(
1518: "Payment not captured for order ["
1519: + orderId
1520: + "] from billing account ["
1521: + billingAccountId + "]",
1522: module);
1523: }
1524: }
1525: }
1526: }
1527:
1528: // iterate over the prefs and capture each one until we meet our total
1529: if (UtilValidate.isNotEmpty(paymentPrefs)) {
1530: Iterator payments = paymentPrefs.iterator();
1531: while (payments.hasNext()) {
1532: // DEJ20060708: Do we really want to just log and ignore the errors like this? I've improved a few of these in a review today, but it is being done all over...
1533: GenericValue paymentPref = (GenericValue) payments
1534: .next();
1535: GenericValue authTrans = getAuthTransaction(paymentPref);
1536: if (authTrans == null) {
1537: continue;
1538: }
1539:
1540: // check for an existing capture
1541: GenericValue captureTrans = getCaptureTransaction(paymentPref);
1542: if (captureTrans != null) {
1543: Debug.logWarning(
1544: "Attempt to capture and already captured preference: "
1545: + captureTrans, module);
1546: continue;
1547: }
1548:
1549: BigDecimal authAmount = authTrans
1550: .getBigDecimal("amount");
1551: if (authAmount == null)
1552: authAmount = ZERO;
1553: authAmount = authAmount.setScale(decimals, rounding);
1554:
1555: if (authAmount.compareTo(ZERO) == 0) {
1556: // nothing to capture
1557: Debug.logInfo("Nothing to capture; authAmount = 0",
1558: module);
1559: continue;
1560: }
1561:
1562: // the amount for *this* capture
1563: BigDecimal amountThisCapture;
1564:
1565: // determine how much for *this* capture
1566: if (authAmount.compareTo(amountToCapture) >= 0) {
1567: // if the auth amount is more then expected capture just capture what is expected
1568: amountThisCapture = amountToCapture;
1569: } else if (payments.hasNext()) {
1570: // if we have more payments to capture; just capture what was authorized
1571: amountThisCapture = authAmount;
1572: } else {
1573: // we need to capture more then what was authorized; re-auth for the new amount
1574: // TODO: add what the billing account cannot support to the re-auth amount
1575: // TODO: add support for re-auth for additional funds
1576: // just in case; we will capture the authorized amount here; until this is implemented
1577: Debug
1578: .logError(
1579: "The amount to capture was more then what was authorized; we only captured the authorized amount : "
1580: + paymentPref, module);
1581: amountThisCapture = authAmount;
1582: }
1583:
1584: Map captureResult = capturePayment(dctx, userLogin,
1585: orh, paymentPref, amountThisCapture
1586: .doubleValue());
1587: if (captureResult != null
1588: && !ServiceUtil.isError(captureResult)) {
1589: // credit card processors return captureAmount, but gift certificate processors return processAmount
1590: Double amountCaptured = (Double) captureResult
1591: .get("captureAmount");
1592: if (amountCaptured == null) {
1593: amountCaptured = (Double) captureResult
1594: .get("processAmount");
1595: }
1596:
1597: // big decimal reference to the capture amount
1598: BigDecimal amountCapturedBd = new BigDecimal(
1599: amountCaptured.doubleValue());
1600: amountCapturedBd = amountCapturedBd.setScale(
1601: decimals, rounding);
1602:
1603: // decrease amount of next payment preference to capture
1604: amountToCapture = amountToCapture
1605: .subtract(amountCapturedBd);
1606:
1607: // add the invoiceId to the result for processing
1608: captureResult.put("invoiceId", invoiceId);
1609:
1610: // process the capture's results
1611: try {
1612: processResult(dctx, captureResult, userLogin,
1613: paymentPref);
1614: } catch (GeneralException e) {
1615: Debug.logError(e,
1616: "Trouble processing the result; captureResult: "
1617: + captureResult, module);
1618: return ServiceUtil
1619: .returnError("Trouble processing the capture results");
1620: }
1621:
1622: // create any splits which are needed
1623: if (authAmount.compareTo(amountCapturedBd) == 1) {
1624: BigDecimal splitAmount = authAmount
1625: .subtract(amountCapturedBd);
1626: try {
1627: Map splitCtx = UtilMisc.toMap("userLogin",
1628: userLogin,
1629: "orderPaymentPreference",
1630: paymentPref, "splitAmount",
1631: splitAmount);
1632: dispatcher.addCommitService(
1633: "processCaptureSplitPayment",
1634: splitCtx, true);
1635: } catch (GenericServiceException e) {
1636: Debug
1637: .logWarning(
1638: e,
1639: "Problem processing the capture split payment",
1640: module);
1641: }
1642: Debug.logInfo("Captured: " + amountThisCapture
1643: + " Remaining (re-auth): "
1644: + splitAmount, module);
1645: }
1646: } else {
1647: Debug.logError("Payment not captured", module);
1648: }
1649: }
1650: }
1651:
1652: if (amountToCapture.compareTo(ZERO) == 1) {
1653: GenericValue productStore = orh.getProductStore();
1654: if (!UtilValidate.isEmpty(productStore)) {
1655: boolean shipIfCaptureFails = UtilValidate
1656: .isEmpty(productStore.get("shipIfCaptureFails"))
1657: || "Y".equalsIgnoreCase(productStore
1658: .getString("shipIfCaptureFails"));
1659: if (!shipIfCaptureFails) {
1660: return ServiceUtil
1661: .returnError("Cannot ship order because credit card captures were unsuccessful");
1662: }
1663: }
1664: Map result = ServiceUtil.returnSuccess();
1665: result.put("processResult", "FAILED");
1666: return result;
1667: } else {
1668: Map result = ServiceUtil.returnSuccess();
1669: result.put("processResult", "COMPLETE");
1670: return result;
1671: }
1672: }
1673:
1674: public static Map processCaptureSplitPayment(DispatchContext dctx,
1675: Map context) {
1676: LocalDispatcher dispatcher = dctx.getDispatcher();
1677: GenericDelegator delegator = dctx.getDelegator();
1678:
1679: GenericValue userLogin = (GenericValue) context
1680: .get("userLogin");
1681: GenericValue paymentPref = (GenericValue) context
1682: .get("orderPaymentPreference");
1683: BigDecimal splitAmount = (BigDecimal) context
1684: .get("splitAmount");
1685:
1686: String orderId = paymentPref.getString("orderId");
1687: OrderReadHelper orh = new OrderReadHelper(delegator, orderId);
1688:
1689: String statusId = ("EXT_BILLACT".equals(paymentPref
1690: .getString("paymentMethodTypeId")) ? "PAYMENT_NOT_RECEIVED"
1691: : "PAYMENT_NOT_AUTH");
1692: // create a new payment preference
1693: Debug.logInfo("Creating payment preference split", module);
1694: String newPrefId = delegator
1695: .getNextSeqId("OrderPaymentPreference");
1696: GenericValue newPref = delegator.makeValue(
1697: "OrderPaymentPreference", UtilMisc.toMap(
1698: "orderPaymentPreferenceId", newPrefId));
1699: newPref.set("orderId", paymentPref.get("orderId"));
1700: newPref.set("paymentMethodTypeId", paymentPref
1701: .get("paymentMethodTypeId"));
1702: newPref.set("paymentMethodId", paymentPref
1703: .get("paymentMethodId"));
1704: newPref.set("maxAmount", splitAmount);
1705: newPref.set("statusId", statusId);
1706: newPref.set("createdDate", UtilDateTime.nowTimestamp());
1707: if (userLogin != null) {
1708: newPref.set("createdByUserLogin", userLogin
1709: .getString("userLoginId"));
1710: }
1711: if (Debug.verboseOn())
1712: Debug.logVerbose("New preference : " + newPref, module);
1713:
1714: Map processorResult = null;
1715: try {
1716: // create the new payment preference
1717: delegator.create(newPref);
1718:
1719: if ("PAYMENT_NOT_AUTH".equals(statusId)) {
1720: // authorize the new preference
1721: processorResult = authPayment(dispatcher, userLogin,
1722: orh, newPref, splitAmount.doubleValue(), false,
1723: null);
1724: if (processorResult != null) {
1725: // process the auth results
1726: boolean authResult = processResult(dctx,
1727: processorResult, userLogin, newPref);
1728: if (!authResult) {
1729: Debug.logError("Authorization failed : "
1730: + newPref + " : " + processorResult,
1731: module);
1732: }
1733: } else {
1734: Debug
1735: .logError(
1736: "Payment not authorized : "
1737: + newPref + " : "
1738: + processorResult, module);
1739: }
1740: }
1741: } catch (GenericEntityException e) {
1742: Debug.logError(e,
1743: "ERROR: cannot create new payment preference : "
1744: + newPref, module);
1745: } catch (GeneralException e) {
1746: if (processorResult != null) {
1747: Debug.logError(e,
1748: "Trouble processing the auth result: "
1749: + newPref + " : " + processorResult,
1750: module);
1751: } else {
1752: Debug.logError(e, "Trouble authorizing the payment: "
1753: + newPref, module);
1754: }
1755: }
1756: return ServiceUtil.returnSuccess();
1757: }
1758:
1759: // Deprecated: use captureBillingAccountPayments instead of this.
1760: public static Map captureBillingAccountPayment(
1761: DispatchContext dctx, Map context) {
1762: GenericDelegator delegator = dctx.getDelegator();
1763: LocalDispatcher dispatcher = dctx.getDispatcher();
1764: GenericValue userLogin = (GenericValue) context
1765: .get("userLogin");
1766: String invoiceId = (String) context.get("invoiceId");
1767: String billingAccountId = (String) context
1768: .get("billingAccountId");
1769: Double captureAmount = (Double) context.get("captureAmount");
1770: String orderId = (String) context.get("orderId");
1771: Map results = ServiceUtil.returnSuccess();
1772:
1773: try {
1774: // Note that the partyIdFrom of the Payment should be the partyIdTo of the invoice, since you're receiving a payment from the party you billed
1775: GenericValue invoice = delegator.findByPrimaryKey(
1776: "Invoice", UtilMisc.toMap("invoiceId", invoiceId));
1777: Map paymentParams = UtilMisc.toMap("paymentTypeId",
1778: "CUSTOMER_PAYMENT", "paymentMethodTypeId",
1779: "EXT_BILLACT", "partyIdFrom", invoice
1780: .getString("partyId"), "partyIdTo", invoice
1781: .getString("partyIdFrom"), "statusId",
1782: "PMNT_RECEIVED", "effectiveDate", UtilDateTime
1783: .nowTimestamp());
1784: paymentParams.put("amount", captureAmount);
1785: paymentParams.put("currencyUomId", invoice
1786: .getString("currencyUomId"));
1787: paymentParams.put("userLogin", userLogin);
1788: Map tmpResult = dispatcher.runSync("createPayment",
1789: paymentParams);
1790: if (ServiceUtil.isError(tmpResult)) {
1791: return tmpResult;
1792: }
1793:
1794: String paymentId = (String) tmpResult.get("paymentId");
1795: tmpResult = dispatcher.runSync("createPaymentApplication",
1796: UtilMisc.toMap("paymentId", paymentId, "invoiceId",
1797: invoiceId, "billingAccountId",
1798: billingAccountId, "amountApplied",
1799: captureAmount, "userLogin", userLogin));
1800: if (ServiceUtil.isError(tmpResult)) {
1801: return tmpResult;
1802: }
1803: if (paymentId == null) {
1804: return ServiceUtil
1805: .returnError("No payment created for invoice ["
1806: + invoiceId + "] and billing account ["
1807: + billingAccountId + "]");
1808: }
1809: results.put("paymentId", paymentId);
1810:
1811: if (orderId != null && captureAmount.doubleValue() > 0) {
1812: // Create a paymentGatewayResponse, if necessary
1813: GenericValue order = delegator.findByPrimaryKey(
1814: "OrderHeader", UtilMisc.toMap("orderId",
1815: orderId));
1816: if (order == null) {
1817: return ServiceUtil
1818: .returnError("No paymentGatewayResponse created for invoice ["
1819: + invoiceId
1820: + "] and billing account ["
1821: + billingAccountId
1822: + "]: Order with ID ["
1823: + orderId
1824: + "] not found!");
1825: }
1826: // See if there's an orderPaymentPreference - there should be only one OPP for EXT_BILLACT per order
1827: List orderPaymentPreferences = delegator.findByAnd(
1828: "OrderPaymentPreference", UtilMisc.toMap(
1829: "orderId", orderId,
1830: "paymentMethodTypeId", "EXT_BILLACT"));
1831: if (orderPaymentPreferences != null
1832: && orderPaymentPreferences.size() > 0) {
1833: GenericValue orderPaymentPreference = EntityUtil
1834: .getFirst(orderPaymentPreferences);
1835:
1836: // Check the productStore setting to see if we need to do this explicitly
1837: GenericValue productStore = order
1838: .getRelatedOne("ProductStore");
1839: if (productStore.getString("manualAuthIsCapture") == null
1840: || (!productStore.getString(
1841: "manualAuthIsCapture")
1842: .equalsIgnoreCase("Y"))) {
1843: String responseId = delegator
1844: .getNextSeqId("PaymentGatewayResponse");
1845: GenericValue pgResponse = delegator.makeValue(
1846: "PaymentGatewayResponse", null);
1847: pgResponse.set("paymentGatewayResponseId",
1848: responseId);
1849: pgResponse.set("paymentServiceTypeEnumId",
1850: CAPTURE_SERVICE_TYPE);
1851: pgResponse
1852: .set(
1853: "orderPaymentPreferenceId",
1854: orderPaymentPreference
1855: .getString("orderPaymentPreferenceId"));
1856: pgResponse.set("paymentMethodTypeId",
1857: "EXT_BILLACT");
1858: pgResponse
1859: .set("transCodeEnumId", "PGT_CAPTURE");
1860: pgResponse.set("amount", captureAmount);
1861: pgResponse.set("currencyUomId", invoice
1862: .getString("currencyUomId"));
1863: pgResponse.set("transactionDate", UtilDateTime
1864: .nowTimestamp());
1865: // referenceNum holds the relation to the order.
1866: // todo: Extend PaymentGatewayResponse with a billingAccountId field?
1867: pgResponse
1868: .set("referenceNum", billingAccountId);
1869:
1870: // save the response
1871: savePgr(dctx, pgResponse);
1872:
1873: // Update the orderPaymentPreference
1874: orderPaymentPreference.set("statusId",
1875: "PAYMENT_SETTLED");
1876: orderPaymentPreference.store();
1877:
1878: results.put("paymentGatewayResponseId",
1879: responseId);
1880: }
1881: }
1882: }
1883: } catch (GenericEntityException ex) {
1884: return ServiceUtil.returnError(ex.getMessage());
1885: } catch (GenericServiceException ex) {
1886: return ServiceUtil.returnError(ex.getMessage());
1887: }
1888:
1889: return results;
1890: }
1891:
1892: public static Map captureBillingAccountPayments(
1893: DispatchContext dctx, Map context) {
1894: GenericDelegator delegator = dctx.getDelegator();
1895: LocalDispatcher dispatcher = dctx.getDispatcher();
1896: GenericValue userLogin = (GenericValue) context
1897: .get("userLogin");
1898: String invoiceId = (String) context.get("invoiceId");
1899: String billingAccountId = (String) context
1900: .get("billingAccountId");
1901: Double captureAmountDbl = (Double) context.get("captureAmount");
1902: BigDecimal captureAmount = new BigDecimal(captureAmountDbl
1903: .doubleValue());
1904: captureAmount = captureAmount.setScale(decimals, rounding);
1905: String orderId = (String) context.get("orderId");
1906: BigDecimal capturedAmount = ZERO;
1907:
1908: try {
1909: // Select all the unapplied payment applications associated to the billing account
1910: List conditionList = UtilMisc.toList(new EntityExpr(
1911: "billingAccountId", EntityOperator.EQUALS,
1912: billingAccountId), new EntityExpr("invoiceId",
1913: EntityOperator.EQUALS, GenericEntity.NULL_FIELD));
1914: EntityCondition conditions = new EntityConditionList(
1915: conditionList, EntityOperator.AND);
1916:
1917: List paymentApplications = delegator.findByCondition(
1918: "PaymentApplication", conditions, null, UtilMisc
1919: .toList("-amountApplied"));
1920: if (UtilValidate.isNotEmpty(paymentApplications)) {
1921: Iterator paymentApplicationsIt = paymentApplications
1922: .iterator();
1923: while (paymentApplicationsIt.hasNext()) {
1924: if (capturedAmount.compareTo(captureAmount) >= 0) {
1925: // we have captured all the amount required
1926: break;
1927: }
1928: GenericValue paymentApplication = (GenericValue) paymentApplicationsIt
1929: .next();
1930: GenericValue payment = paymentApplication
1931: .getRelatedOne("Payment");
1932: if (payment.getString("paymentPreferenceId") != null) {
1933: // if the payment is reserved for a specific OrderPaymentPreference,
1934: // we don't use it.
1935: continue;
1936: }
1937: // TODO: check the statusId of the payment
1938: BigDecimal paymentApplicationAmount = paymentApplication
1939: .getBigDecimal("amountApplied");
1940: BigDecimal amountToCapture = paymentApplicationAmount
1941: .min(captureAmount.subtract(capturedAmount));
1942: amountToCapture = amountToCapture.setScale(
1943: decimals, rounding);
1944: if (amountToCapture
1945: .compareTo(paymentApplicationAmount) == 0) {
1946: // apply the whole payment application to the invoice
1947: paymentApplication.set("invoiceId", invoiceId);
1948: paymentApplication.store();
1949: } else {
1950: // the amount to capture is lower than the amount available in this payment application:
1951: // split the payment application into two records and apply one to the invoice
1952: GenericValue newPaymentApplication = delegator
1953: .makeValue("PaymentApplication",
1954: paymentApplication);
1955: String paymentApplicationId = delegator
1956: .getNextSeqId("PaymentApplication");
1957: paymentApplication.set("invoiceId", invoiceId);
1958: paymentApplication.set("amountApplied",
1959: amountToCapture);
1960: paymentApplication.store();
1961: newPaymentApplication.set(
1962: "paymentApplicationId",
1963: paymentApplicationId);
1964: newPaymentApplication.set("amountApplied",
1965: paymentApplicationAmount
1966: .subtract(amountToCapture));
1967: newPaymentApplication.create();
1968: }
1969: capturedAmount = capturedAmount
1970: .add(amountToCapture);
1971: }
1972: }
1973: } catch (GenericEntityException ex) {
1974: return ServiceUtil.returnError(ex.getMessage());
1975: }
1976: capturedAmount = capturedAmount.setScale(decimals, rounding);
1977: Map results = ServiceUtil.returnSuccess();
1978: results.put("captureAmount", new Double(capturedAmount
1979: .doubleValue()));
1980: return results;
1981: }
1982:
1983: private static Map capturePayment(DispatchContext dctx,
1984: GenericValue userLogin, OrderReadHelper orh,
1985: GenericValue paymentPref, double amount) {
1986: return capturePayment(dctx, userLogin, orh, paymentPref,
1987: amount, null);
1988: }
1989:
1990: private static Map capturePayment(DispatchContext dctx,
1991: GenericValue userLogin, OrderReadHelper orh,
1992: GenericValue paymentPref, double amount,
1993: GenericValue authTrans) {
1994: LocalDispatcher dispatcher = dctx.getDispatcher();
1995: // look up the payment configuration settings
1996: String serviceName = null;
1997: String paymentConfig = null;
1998:
1999: // get the payment settings i.e. serviceName and config properties file name
2000: GenericValue paymentSettings = getPaymentSettings(orh
2001: .getOrderHeader(), paymentPref, CAPTURE_SERVICE_TYPE,
2002: false);
2003: if (paymentSettings != null) {
2004: paymentConfig = paymentSettings
2005: .getString("paymentPropertiesPath");
2006: serviceName = paymentSettings.getString("paymentService");
2007: if (serviceName == null) {
2008: Debug
2009: .logError(
2010: "Service name is null for payment setting; cannot process",
2011: module);
2012: return null;
2013: }
2014: } else {
2015: Debug
2016: .logError(
2017: "Invalid payment settings entity, no payment settings found",
2018: module);
2019: return null;
2020: }
2021:
2022: if (paymentConfig == null || paymentConfig.length() == 0) {
2023: paymentConfig = "payment.properties";
2024: }
2025:
2026: // check the validity of the authorization; re-auth if necessary
2027: if (!PaymentGatewayServices.checkAuthValidity(paymentPref,
2028: paymentConfig)) {
2029: try {
2030: // re-auth required before capture
2031: Map processorResult = PaymentGatewayServices
2032: .authPayment(dispatcher, userLogin, orh,
2033: paymentPref, amount, true, null);
2034:
2035: boolean authResult = false;
2036: if (processorResult != null) {
2037: // process the auth results
2038: try {
2039: authResult = processResult(dctx,
2040: processorResult, userLogin, paymentPref);
2041: if (!authResult) {
2042: Debug.logError("Re-Authorization failed : "
2043: + paymentPref + " : "
2044: + processorResult, module);
2045: }
2046: } catch (GeneralException e) {
2047: Debug.logError(e,
2048: "Trouble processing the re-auth result : "
2049: + paymentPref + " : "
2050: + processorResult, module);
2051: }
2052: } else {
2053: Debug.logError("Payment not re-authorized : "
2054: + paymentPref + " : " + processorResult,
2055: module);
2056: }
2057:
2058: if (!authResult) {
2059: // returning null to cancel the capture process.
2060: return null;
2061: }
2062:
2063: // get the new auth transaction
2064: authTrans = getAuthTransaction(paymentPref);
2065: } catch (GeneralException e) {
2066: String errMsg = "Error re-authorizing payment: "
2067: + e.toString();
2068: Debug.logError(e, errMsg, module);
2069: return ServiceUtil.returnError(errMsg);
2070: }
2071: }
2072:
2073: // prepare the context for the capture service (must follow the ccCaptureInterface
2074: Map captureContext = new HashMap();
2075: captureContext.put("userLogin", userLogin);
2076: captureContext.put("orderPaymentPreference", paymentPref);
2077: captureContext.put("paymentConfig", paymentConfig);
2078: captureContext.put("currency", orh.getCurrency());
2079:
2080: // this is necessary because the ccCaptureInterface uses "captureAmount" but the paymentProcessInterface uses "processAmount"
2081: try {
2082: ModelService captureService = dctx
2083: .getModelService(serviceName);
2084: Set inParams = captureService.getInParamNames();
2085: if (inParams.contains("captureAmount")) {
2086: captureContext.put("captureAmount", new Double(amount));
2087: } else if (inParams.contains("processAmount")) {
2088: captureContext.put("processAmount", new Double(amount));
2089: } else {
2090: return ServiceUtil
2091: .returnError("Service ["
2092: + serviceName
2093: + "] does not have a captureAmount or processAmount. Its parameters are: "
2094: + inParams);
2095: }
2096: } catch (GenericServiceException ex) {
2097: return ServiceUtil
2098: .returnError("Cannot get model service for "
2099: + serviceName);
2100: }
2101:
2102: if (authTrans != null) {
2103: captureContext.put("authTrans", authTrans);
2104: }
2105:
2106: Debug.logInfo("Capture [" + serviceName + "] : "
2107: + captureContext, module);
2108:
2109: // now invoke the capture service
2110: Map captureResult = null;
2111: try {
2112: captureResult = dispatcher.runSync(serviceName,
2113: captureContext, TX_TIME, true);
2114: } catch (GenericServiceException e) {
2115: Debug.logError(e,
2116: "Could not capture payment ... serviceName: "
2117: + serviceName + " ... context: "
2118: + captureContext, module);
2119: return null;
2120: }
2121:
2122: // pass the payTo partyId to the result processor; we just add it to the result context.
2123: String payToPartyId = getPayToPartyId(orh.getOrderHeader());
2124: captureResult.put("payToPartyId", payToPartyId);
2125:
2126: // add paymentSettings to result; for use by later processors
2127: captureResult.put("paymentSettings", paymentSettings);
2128:
2129: // pass the currencyUomId as well
2130: captureResult.put("currencyUomId", orh.getCurrency());
2131:
2132: // log the error message as a gateway response when it fails
2133: if (ServiceUtil.isError(captureResult)) {
2134: saveError(dispatcher, userLogin, paymentPref,
2135: captureResult, CAPTURE_SERVICE_TYPE, "PGT_CAPTURE");
2136: }
2137:
2138: return captureResult;
2139: }
2140:
2141: private static void saveError(LocalDispatcher dispatcher,
2142: GenericValue userLogin, GenericValue paymentPref,
2143: Map result, String serviceType, String transactionCode) {
2144: Map serviceContext = new HashMap();
2145: serviceContext.put("paymentServiceTypeEnumId", serviceType);
2146: serviceContext.put("orderPaymentPreference", paymentPref);
2147: serviceContext.put("transCodeEnumId", transactionCode);
2148: serviceContext.put("serviceResultMap", result);
2149: serviceContext.put("userLogin", userLogin);
2150:
2151: try {
2152: dispatcher.runAsync("processPaymentServiceError",
2153: serviceContext);
2154: } catch (GenericServiceException e) {
2155: Debug.logError(e, module);
2156: }
2157: }
2158:
2159: public static Map storePaymentErrorMessage(DispatchContext dctx,
2160: Map context) {
2161: GenericDelegator delegator = dctx.getDelegator();
2162: GenericValue paymentPref = (GenericValue) context
2163: .get("orderPaymentPreference");
2164: String serviceType = (String) context
2165: .get("paymentServiceTypeEnumId");
2166: String transactionCode = (String) context
2167: .get("transCodeEnumId");
2168: Map result = (Map) context.get("serviceResultMap");
2169:
2170: String responseId = delegator
2171: .getNextSeqId("PaymentGatewayResponse");
2172: GenericValue response = delegator.makeValue(
2173: "PaymentGatewayResponse", null);
2174: String message = ServiceUtil.getErrorMessage(result);
2175: if (message.length() > 255) {
2176: message = message.substring(0, 255);
2177: }
2178: response.set("paymentGatewayResponseId", responseId);
2179: response.set("paymentServiceTypeEnumId", serviceType);
2180: response.set("orderPaymentPreferenceId", paymentPref
2181: .get("orderPaymentPreferenceId"));
2182: response.set("paymentMethodTypeId", paymentPref
2183: .get("paymentMethodTypeId"));
2184: response.set("paymentMethodId", paymentPref
2185: .get("paymentMethodId"));
2186: response.set("transCodeEnumId", transactionCode);
2187: response.set("referenceNum", "ERROR");
2188: response.set("gatewayMessage", message);
2189: response.set("transactionDate", UtilDateTime.nowTimestamp());
2190:
2191: try {
2192: delegator.create(response);
2193: } catch (GenericEntityException e) {
2194: Debug.logError(e, module);
2195: return ServiceUtil
2196: .returnError("Unable to create PaymentGatewayResponse for failed service call!");
2197: }
2198:
2199: Debug
2200: .logInfo(
2201: "Created PaymentGatewayResponse record for returned error",
2202: module);
2203: return ServiceUtil.returnSuccess();
2204: }
2205:
2206: private static boolean processResult(DispatchContext dctx,
2207: Map result, GenericValue userLogin,
2208: GenericValue paymentPreference) throws GeneralException {
2209: Boolean authResult = (Boolean) result.get("authResult");
2210: Boolean captureResult = (Boolean) result.get("captureResult");
2211: boolean resultPassed = false;
2212: String initialStatus = paymentPreference.getString("statusId");
2213: String authServiceType = null;
2214:
2215: if (authResult != null) {
2216: processAuthResult(dctx, result, userLogin,
2217: paymentPreference);
2218: resultPassed = authResult.booleanValue();
2219: authServiceType = ("PAYMENT_NOT_AUTH".equals(initialStatus)) ? AUTH_SERVICE_TYPE
2220: : REAUTH_SERVICE_TYPE;
2221: }
2222: if (captureResult != null) {
2223: processCaptureResult(dctx, result, userLogin,
2224: paymentPreference, authServiceType);
2225: if (!resultPassed)
2226: resultPassed = captureResult.booleanValue();
2227: }
2228: return resultPassed;
2229: }
2230:
2231: private static void processAuthResult(DispatchContext dctx,
2232: Map result, GenericValue userLogin,
2233: GenericValue paymentPreference) throws GeneralException {
2234: LocalDispatcher dispatcher = dctx.getDispatcher();
2235: result.put("userLogin", userLogin);
2236: result.put("orderPaymentPreference", paymentPreference);
2237: ModelService model = dctx.getModelService("processAuthResult");
2238: Map context = model.makeValid(result, ModelService.IN_PARAM);
2239:
2240: // in case we rollback make sure this service gets called
2241: dispatcher.addRollbackService(model.name, context, true);
2242:
2243: // invoke the service
2244: Map resResp;
2245: try {
2246: resResp = dispatcher.runSync(model.name, context);
2247: } catch (GenericServiceException e) {
2248: Debug.logError(e, module);
2249: throw e;
2250: }
2251: if (ServiceUtil.isError(resResp)) {
2252: throw new GeneralException(ServiceUtil
2253: .getErrorMessage(resResp));
2254: }
2255: }
2256:
2257: public static Map processAuthResult(DispatchContext dctx,
2258: Map context) {
2259: GenericDelegator delegator = dctx.getDelegator();
2260: GenericValue orderPaymentPreference = (GenericValue) context
2261: .get("orderPaymentPreference");
2262: Boolean authResult = (Boolean) context.get("authResult");
2263: String authType = (String) context.get("serviceTypeEnum");
2264: String currencyUomId = (String) context.get("currencyUomId");
2265: Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
2266:
2267: // refresh the payment preference
2268: try {
2269: orderPaymentPreference.refresh();
2270: } catch (GenericEntityException e) {
2271: Debug.logError(e, module);
2272: return ServiceUtil.returnError(e.getMessage());
2273: }
2274:
2275: // type of auth this was can be determined by the previous status
2276: if (UtilValidate.isEmpty(authType)) {
2277: authType = ("PAYMENT_NOT_AUTH"
2278: .equals(orderPaymentPreference
2279: .getString("statusId"))) ? AUTH_SERVICE_TYPE
2280: : REAUTH_SERVICE_TYPE;
2281: }
2282:
2283: try {
2284: String paymentMethodId = orderPaymentPreference
2285: .getString("paymentMethodId");
2286: GenericValue paymentMethod = delegator.findByPrimaryKey(
2287: "PaymentMethod", UtilMisc.toMap("paymentMethodId",
2288: paymentMethodId));
2289: GenericValue creditCard = null;
2290: if (paymentMethod != null
2291: && "CREDIT_CARD".equals(paymentMethod
2292: .getString("paymentMethodTypeId"))) {
2293: creditCard = paymentMethod.getRelatedOne("CreditCard");
2294: }
2295:
2296: // create the PaymentGatewayResponse
2297: String responseId = delegator
2298: .getNextSeqId("PaymentGatewayResponse");
2299: GenericValue response = delegator.makeValue(
2300: "PaymentGatewayResponse", null);
2301: response.set("paymentGatewayResponseId", responseId);
2302: response.set("paymentServiceTypeEnumId", authType);
2303: response.set("orderPaymentPreferenceId",
2304: orderPaymentPreference
2305: .get("orderPaymentPreferenceId"));
2306: response.set("paymentMethodTypeId", orderPaymentPreference
2307: .get("paymentMethodTypeId"));
2308: response.set("paymentMethodId", orderPaymentPreference
2309: .get("paymentMethodId"));
2310: response.set("transCodeEnumId", "PGT_AUTHORIZE");
2311: response.set("currencyUomId", currencyUomId);
2312:
2313: // set the avs/fraud result
2314: response.set("gatewayAvsResult", context.get("avsCode"));
2315: response
2316: .set("gatewayScoreResult", context.get("scoreCode"));
2317:
2318: // set the auth info
2319: response.set("amount", context.get("processAmount"));
2320: response.set("referenceNum", context.get("authRefNum"));
2321: response.set("altReference", context.get("authAltRefNum"));
2322: response.set("gatewayCode", context.get("authCode"));
2323: response.set("gatewayFlag", context.get("authFlag"));
2324: response.set("gatewayMessage", context.get("authMessage"));
2325: response
2326: .set("transactionDate", UtilDateTime.nowTimestamp());
2327:
2328: if (Boolean.TRUE.equals((Boolean) context
2329: .get("resultDeclined")))
2330: response.set("resultDeclined", "Y");
2331: if (Boolean.TRUE.equals((Boolean) context.get("resultNsf")))
2332: response.set("resultNsf", "Y");
2333: if (Boolean.TRUE.equals((Boolean) context
2334: .get("resultBadExpire")))
2335: response.set("resultBadExpire", "Y");
2336: if (Boolean.TRUE.equals((Boolean) context
2337: .get("resultBadCardNumber")))
2338: response.set("resultBadCardNumber", "Y");
2339:
2340: // save the response
2341: savePgr(dctx, response);
2342:
2343: // create the internal messages
2344: List messages = (List) context.get("internalRespMsgs");
2345: if (messages != null && messages.size() > 0) {
2346: Iterator i = messages.iterator();
2347: while (i.hasNext()) {
2348: GenericValue respMsg = delegator.makeValue(
2349: "PaymentGatewayRespMsg", null);
2350: String respMsgId = delegator
2351: .getNextSeqId("PaymentGatewayRespMsg");
2352: String message = (String) i.next();
2353: respMsg.set("paymentGatewayRespMsgId", respMsgId);
2354: respMsg.set("paymentGatewayResponseId", responseId);
2355: respMsg.set("pgrMessage", message);
2356: savePgr(dctx, respMsg);
2357: }
2358: }
2359:
2360: if (response.getDouble("amount").doubleValue() != ((Double) context
2361: .get("processAmount")).doubleValue()) {
2362: Debug.logWarning(
2363: "The authorized amount does not match the max amount : Response - "
2364: + response + " : result - " + context,
2365: module);
2366: }
2367:
2368: // set the status of the OrderPaymentPreference
2369: if (context != null && authResult.booleanValue()) {
2370: orderPaymentPreference.set("statusId",
2371: "PAYMENT_AUTHORIZED");
2372: orderPaymentPreference.set("securityCode", null);
2373: } else if (context != null && !authResult.booleanValue()) {
2374: orderPaymentPreference.set("statusId",
2375: "PAYMENT_DECLINED");
2376: } else {
2377: orderPaymentPreference.set("statusId", "PAYMENT_ERROR");
2378: }
2379:
2380: boolean needsNsfRetry = needsNsfRetry(
2381: orderPaymentPreference, context, delegator);
2382: if (needsNsfRetry) {
2383: orderPaymentPreference.set("needsNsfRetry", "Y");
2384: } else {
2385: orderPaymentPreference.set("needsNsfRetry", "N");
2386: }
2387:
2388: orderPaymentPreference.store();
2389:
2390: // if the payment was declined and this is a CreditCard, save that information on the CreditCard entity
2391: if (!authResult.booleanValue()) {
2392: if (creditCard != null) {
2393: Long consecutiveFailedAuths = creditCard
2394: .getLong("consecutiveFailedAuths");
2395: if (consecutiveFailedAuths == null) {
2396: creditCard.set("consecutiveFailedAuths",
2397: new Long(1));
2398: } else {
2399: creditCard.set("consecutiveFailedAuths",
2400: new Long(consecutiveFailedAuths
2401: .longValue() + 1));
2402: }
2403: creditCard.set("lastFailedAuthDate", nowTimestamp);
2404:
2405: if (Boolean.TRUE.equals((Boolean) context
2406: .get("resultNsf"))) {
2407: Long consecutiveFailedNsf = creditCard
2408: .getLong("consecutiveFailedNsf");
2409: if (consecutiveFailedNsf == null) {
2410: creditCard.set("consecutiveFailedNsf",
2411: new Long(1));
2412: } else {
2413: creditCard.set("consecutiveFailedNsf",
2414: new Long(consecutiveFailedNsf
2415: .longValue() + 1));
2416: }
2417: creditCard.set("lastFailedNsfDate",
2418: nowTimestamp);
2419: }
2420: creditCard.store();
2421: }
2422: }
2423:
2424: // auth was successful, to clear out any failed auth or nsf info
2425: if (authResult.booleanValue()) {
2426: if ((creditCard != null)
2427: && (creditCard.get("lastFailedAuthDate") != null)) {
2428: creditCard.set("consecutiveFailedAuths",
2429: new Long(0));
2430: creditCard.set("lastFailedAuthDate", null);
2431: creditCard.set("consecutiveFailedNsf", new Long(0));
2432: creditCard.set("lastFailedNsfDate", null);
2433: creditCard.store();
2434: }
2435: }
2436: } catch (GenericEntityException e) {
2437: String errMsg = "Error updating payment status information: "
2438: + e.toString();
2439: Debug.logError(e, errMsg, module);
2440: return ServiceUtil.returnError(errMsg);
2441: }
2442:
2443: return ServiceUtil.returnSuccess();
2444: }
2445:
2446: private static boolean needsNsfRetry(
2447: GenericValue orderPaymentPreference, Map processContext,
2448: GenericDelegator delegator) throws GenericEntityException {
2449: boolean needsNsfRetry = false;
2450: if (Boolean.TRUE.equals((Boolean) processContext
2451: .get("resultNsf"))) {
2452: // only track this for auto-orders, since we will only not fail and re-try on those
2453: GenericValue orderHeader = orderPaymentPreference
2454: .getRelatedOne("OrderHeader");
2455: if (UtilValidate.isNotEmpty(orderHeader
2456: .getString("autoOrderShoppingListId"))) {
2457: GenericValue productStore = orderHeader
2458: .getRelatedOne("ProductStore");
2459: if ("Y".equals(productStore
2460: .getString("autoOrderCcTryLaterNsf"))) {
2461: // one last condition: make sure there have been less than ProductStore.autoOrderCcTryLaterMax
2462: // PaymentGatewayResponse records with the same orderPaymentPreferenceId and paymentMethodId (just in case it has changed)
2463: // and that have resultNsf = Y, ie only consider other NSF responses
2464: Long autoOrderCcTryLaterMax = productStore
2465: .getLong("autoOrderCcTryLaterMax");
2466: if (autoOrderCcTryLaterMax != null) {
2467: long failedTries = delegator
2468: .findCountByAnd(
2469: "PaymentGatewayResponse",
2470: UtilMisc
2471: .toMap(
2472: "orderPaymentPreferenceId",
2473: orderPaymentPreference
2474: .get("orderPaymentPreferenceId"),
2475: "paymentMethodId",
2476: orderPaymentPreference
2477: .get("paymentMethodId"),
2478: "resultNsf",
2479: "Y"));
2480: if (failedTries < autoOrderCcTryLaterMax
2481: .longValue()) {
2482: needsNsfRetry = true;
2483: }
2484: } else {
2485: needsNsfRetry = true;
2486: }
2487: }
2488: }
2489: }
2490: return needsNsfRetry;
2491: }
2492:
2493: private static GenericValue processAuthRetryResult(
2494: DispatchContext dctx, Map result, GenericValue userLogin,
2495: GenericValue paymentPreference) throws GeneralException {
2496: processAuthResult(dctx, result, userLogin, paymentPreference);
2497: return getAuthTransaction(paymentPreference);
2498: }
2499:
2500: private static void processCaptureResult(DispatchContext dctx,
2501: Map result, GenericValue userLogin,
2502: GenericValue paymentPreference) throws GeneralException {
2503: processCaptureResult(dctx, result, userLogin,
2504: paymentPreference, null);
2505: }
2506:
2507: private static void processCaptureResult(DispatchContext dctx,
2508: Map result, GenericValue userLogin,
2509: GenericValue paymentPreference, String authServiceType)
2510: throws GeneralException {
2511: if (result == null) {
2512: throw new GeneralException(
2513: "Null capture result sent to processCaptureResult; fatal error");
2514: }
2515:
2516: LocalDispatcher dispatcher = dctx.getDispatcher();
2517: Boolean captureResult = (Boolean) result.get("captureResult");
2518: Double amount = null;
2519: if (result.get("captureAmount") != null) {
2520: amount = (Double) result.get("captureAmount");
2521: } else if (result.get("processAmount") != null) {
2522: amount = (Double) result.get("processAmount");
2523: result.put("captureAmount", amount);
2524: }
2525:
2526: if (amount == null) {
2527: throw new GeneralException(
2528: "Unable to process null capture amount");
2529: }
2530:
2531: // setup the amount big decimal
2532: BigDecimal amtBd = new BigDecimal(amount.doubleValue());
2533: amtBd = amtBd.setScale(decimals, rounding);
2534:
2535: result.put("orderPaymentPreference", paymentPreference);
2536: result.put("userLogin", userLogin);
2537: result.put("serviceTypeEnum", authServiceType);
2538:
2539: ModelService model = dctx
2540: .getModelService("processCaptureResult");
2541: Map context = model.makeValid(result, ModelService.IN_PARAM);
2542: Map capRes;
2543: try {
2544: capRes = dispatcher
2545: .runSync("processCaptureResult", context);
2546: } catch (GenericServiceException e) {
2547: Debug.logError(e, module);
2548: throw e;
2549: }
2550: if (capRes != null && ServiceUtil.isError(capRes)) {
2551: throw new GeneralException(ServiceUtil
2552: .getErrorMessage(capRes));
2553: }
2554: if (!captureResult.booleanValue()) {
2555: // capture returned false (error)
2556: try {
2557: processReAuthFromCaptureFailure(dctx, result, amtBd,
2558: userLogin, paymentPreference);
2559: } catch (GeneralException e) {
2560: // just log this for now (same as previous implementation)
2561: Debug.logError(e, module);
2562: }
2563: }
2564: }
2565:
2566: private static void processReAuthFromCaptureFailure(
2567: DispatchContext dctx, Map result, BigDecimal amount,
2568: GenericValue userLogin, GenericValue paymentPreference)
2569: throws GeneralException {
2570: LocalDispatcher dispatcher = dctx.getDispatcher();
2571:
2572: // lookup the order header
2573: OrderReadHelper orh = null;
2574: try {
2575: GenericValue orderHeader = paymentPreference
2576: .getRelatedOne("OrderHeader");
2577: if (orderHeader != null)
2578: orh = new OrderReadHelper(orderHeader);
2579: } catch (GenericEntityException e) {
2580: throw new GeneralException(
2581: "Problems getting OrderHeader; cannot re-auth the payment",
2582: e);
2583: }
2584:
2585: // make sure the order exists
2586: if (orh == null) {
2587: throw new GeneralException(
2588: "No order found for payment preference #"
2589: + paymentPreference
2590: .get("orderPaymentPreferenceId"));
2591: }
2592:
2593: // set the re-auth amount
2594: if (amount == null) {
2595: amount = ZERO;
2596: }
2597: if (amount.compareTo(ZERO) == 0) {
2598: amount = paymentPreference.getBigDecimal("maxAmount");
2599: Debug
2600: .log(
2601: "resetting payment amount from 0.00 to correctMax amount",
2602: module);
2603: }
2604: Debug.log("reauth with amount: " + amount, module);
2605:
2606: // first re-auth the card
2607: Map authPayRes = authPayment(dispatcher, userLogin, orh,
2608: paymentPreference, amount.doubleValue(), true, null);
2609: if (authPayRes == null) {
2610: throw new GeneralException(
2611: "Null result returned from payment re-authorization");
2612: }
2613:
2614: // check the auth-response
2615: Boolean authResp = (Boolean) authPayRes.get("authResult");
2616: Boolean capResp = (Boolean) authPayRes.get("captureResult");
2617: if (authResp != null && Boolean.TRUE.equals(authResp)) {
2618: GenericValue authTrans = processAuthRetryResult(dctx,
2619: authPayRes, userLogin, paymentPreference);
2620: // check if auto-capture was enabled; process if so
2621: if (capResp != null && capResp.booleanValue()) {
2622: processCaptureResult(dctx, result, userLogin,
2623: paymentPreference);
2624: } else {
2625: // no auto-capture; do manual capture now
2626: Map capPayRes = capturePayment(dctx, userLogin, orh,
2627: paymentPreference, amount.doubleValue(),
2628: authTrans);
2629: if (capPayRes == null) {
2630: throw new GeneralException(
2631: "Problems trying to capture payment (null result)");
2632: }
2633:
2634: // process the capture result
2635: Boolean capPayResp = (Boolean) capPayRes
2636: .get("captureResult");
2637: if (capPayResp != null && capPayResp.booleanValue()) {
2638: // process the capture result
2639: processCaptureResult(dctx, capPayRes, userLogin,
2640: paymentPreference);
2641: } else {
2642: throw new GeneralException(
2643: "Capture of authorized payment failed");
2644: }
2645: }
2646: } else {
2647: throw new GeneralException(
2648: "Payment re-authorization failed");
2649: }
2650: }
2651:
2652: public static Map processCaptureResult(DispatchContext dctx,
2653: Map context) {
2654: GenericDelegator delegator = dctx.getDelegator();
2655: LocalDispatcher dispatcher = dctx.getDispatcher();
2656:
2657: GenericValue paymentPreference = (GenericValue) context
2658: .get("orderPaymentPreference");
2659: GenericValue userLogin = (GenericValue) context
2660: .get("userLogin");
2661: String invoiceId = (String) context.get("invoiceId");
2662: String payTo = (String) context.get("payToPartyId");
2663: Double amount = (Double) context.get("captureAmount");
2664: String serviceType = (String) context.get("serviceTypeEnum");
2665: String currencyUomId = (String) context.get("currencyUomId");
2666:
2667: String paymentMethodTypeId = paymentPreference
2668: .getString("paymentMethodTypeId");
2669:
2670: if (UtilValidate.isEmpty(serviceType)) {
2671: serviceType = CAPTURE_SERVICE_TYPE;
2672: }
2673:
2674: // refresh the payment preference
2675: try {
2676: paymentPreference.refresh();
2677: } catch (GenericEntityException e) {
2678: Debug.logError(e, module);
2679: return ServiceUtil.returnError(e.getMessage());
2680: }
2681:
2682: // update the status and maxAmount
2683: paymentPreference.set("statusId", ("EXT_BILLACT"
2684: .equals(paymentMethodTypeId) ? "PAYMENT_RECEIVED"
2685: : "PAYMENT_SETTLED"));
2686: paymentPreference.set("maxAmount", amount);
2687: try {
2688: paymentPreference.store();
2689: } catch (GenericEntityException e) {
2690: Debug.logError(e, module);
2691: return ServiceUtil.returnError(e.getMessage());
2692: }
2693:
2694: if (!"EXT_BILLACT".equals(paymentMethodTypeId)) {
2695: // create the PaymentGatewayResponse record
2696: String responseId = delegator
2697: .getNextSeqId("PaymentGatewayResponse");
2698: GenericValue response = delegator.makeValue(
2699: "PaymentGatewayResponse", null);
2700: response.set("paymentGatewayResponseId", responseId);
2701: response.set("paymentServiceTypeEnumId", serviceType);
2702: response.set("orderPaymentPreferenceId", paymentPreference
2703: .get("orderPaymentPreferenceId"));
2704: response.set("paymentMethodTypeId", paymentMethodTypeId);
2705: response.set("paymentMethodId", paymentPreference
2706: .get("paymentMethodId"));
2707: response.set("transCodeEnumId", "PGT_CAPTURE");
2708: response.set("currencyUomId", currencyUomId);
2709: if (context.get("authRefNum") != null) {
2710: response.set("subReference", context.get("authRefNum"));
2711: response.set("altReference", context
2712: .get("authAltRefNum"));
2713: } else {
2714: response.set("altReference", context
2715: .get("captureAltRefNum"));
2716: }
2717:
2718: // set the capture info
2719: response.set("amount", amount);
2720: response.set("referenceNum", context.get("captureRefNum"));
2721: response.set("gatewayCode", context.get("captureCode"));
2722: response.set("gatewayFlag", context.get("captureFlag"));
2723: response.set("gatewayMessage", context
2724: .get("captureMessage"));
2725: response
2726: .set("transactionDate", UtilDateTime.nowTimestamp());
2727:
2728: // save the response
2729: savePgr(dctx, response);
2730:
2731: // create the internal messages
2732: List messages = (List) context.get("internalRespMsgs");
2733: if (messages != null && messages.size() > 0) {
2734: Iterator i = messages.iterator();
2735: while (i.hasNext()) {
2736: GenericValue respMsg = delegator.makeValue(
2737: "PaymentGatewayRespMsg", null);
2738: String respMsgId = delegator
2739: .getNextSeqId("PaymentGatewayRespMsg");
2740: String message = (String) i.next();
2741: respMsg.set("paymentGatewayRespMsgId", respMsgId);
2742: respMsg.set("paymentGatewayResponseId", responseId);
2743: respMsg.set("pgrMessage", message);
2744:
2745: // save the message
2746: savePgr(dctx, respMsg);
2747: }
2748: }
2749:
2750: // get the invoice
2751: GenericValue invoice = null;
2752: if (invoiceId != null) {
2753: try {
2754: invoice = delegator.findByPrimaryKey("Invoice",
2755: UtilMisc.toMap("invoiceId", invoiceId));
2756: } catch (GenericEntityException e) {
2757: String message = "Failed to process capture result: Could not find invoice ["
2758: + invoiceId
2759: + "] due to entity error: "
2760: + e.getMessage();
2761: Debug.logError(e, message, module);
2762: return ServiceUtil.returnError(message);
2763: }
2764: }
2765:
2766: // determine the partyIdFrom for the payment, which is who made the payment
2767: String partyIdFrom = null;
2768: if (invoice != null) {
2769: // get the party from the invoice, which is the bill-to party (partyId)
2770: partyIdFrom = invoice.getString("partyId");
2771: } else {
2772: // otherwise get the party from the order's OrderRole
2773: String orderId = paymentPreference.getString("orderId");
2774: List orl = null;
2775: try {
2776: orl = delegator.findByAnd("OrderRole", UtilMisc
2777: .toMap("orderId", orderId, "roleTypeId",
2778: "BILL_TO_CUSTOMER"));
2779: } catch (GenericEntityException e) {
2780: Debug.logError(e, module);
2781: }
2782: if (orl != null && orl.size() > 0) {
2783: GenericValue orderRole = EntityUtil.getFirst(orl);
2784: partyIdFrom = orderRole.getString("partyId");
2785: }
2786: }
2787:
2788: // get the partyIdTo for the payment, which is who is receiving it
2789: String partyIdTo = null;
2790: if (!UtilValidate.isEmpty(payTo)) {
2791: // use input pay to party
2792: partyIdTo = payTo;
2793: } else if (invoice != null) {
2794: // ues the invoice partyIdFrom as the pay to party (which is who supplied the invoice)
2795: partyIdTo = invoice.getString("partyIdFrom");
2796: } else {
2797: // otherwise default to Company and print a big warning about this
2798: partyIdTo = "Company";
2799: Debug.logWarning("Using default value of ["
2800: + partyIdTo
2801: + "] for payTo on invoice ["
2802: + invoiceId
2803: + "] and orderPaymentPreference ["
2804: + paymentPreference
2805: .getString("orderPaymentPreferenceId")
2806: + "]", module);
2807: }
2808:
2809: Map paymentCtx = UtilMisc.toMap("paymentTypeId",
2810: "CUSTOMER_PAYMENT");
2811: paymentCtx.put("paymentMethodTypeId", paymentPreference
2812: .get("paymentMethodTypeId"));
2813: paymentCtx.put("paymentMethodId", paymentPreference
2814: .get("paymentMethodId"));
2815: paymentCtx.put("paymentGatewayResponseId", responseId);
2816: paymentCtx.put("partyIdTo", partyIdTo);
2817: paymentCtx.put("partyIdFrom", partyIdFrom);
2818: paymentCtx.put("statusId", "PMNT_RECEIVED");
2819: paymentCtx.put("paymentPreferenceId", paymentPreference
2820: .get("orderPaymentPreferenceId"));
2821: paymentCtx.put("amount", amount);
2822: paymentCtx.put("currencyUomId", currencyUomId);
2823: paymentCtx.put("userLogin", userLogin);
2824: paymentCtx.put("paymentRefNum", context
2825: .get("captureRefNum"));
2826:
2827: Map payRes;
2828: try {
2829: payRes = dispatcher
2830: .runSync("createPayment", paymentCtx);
2831: } catch (GenericServiceException e) {
2832: Debug.logError(e, module);
2833: return ServiceUtil
2834: .returnError("Error creating payment record");
2835: }
2836: if (ServiceUtil.isError(payRes)) {
2837: return ServiceUtil.returnError(ServiceUtil
2838: .getErrorMessage(payRes));
2839: }
2840:
2841: String paymentId = (String) payRes.get("paymentId");
2842:
2843: // create the PaymentApplication if invoiceId is available
2844: if (invoiceId != null) {
2845: Debug.logInfo("Processing Invoice #" + invoiceId,
2846: module);
2847: Map paCtx = UtilMisc.toMap("paymentId", paymentId,
2848: "invoiceId", invoiceId);
2849: paCtx
2850: .put("amountApplied", context
2851: .get("captureAmount"));
2852: paCtx.put("userLogin", userLogin);
2853: Map paRes;
2854: try {
2855: paRes = dispatcher.runSync(
2856: "createPaymentApplication", paCtx);
2857: } catch (GenericServiceException e) {
2858: Debug.logError(e, module);
2859: return ServiceUtil
2860: .returnError("Error creating invoice application");
2861: }
2862: if (paRes != null && ServiceUtil.isError(paRes)) {
2863: return ServiceUtil.returnError(ServiceUtil
2864: .getErrorMessage(paRes));
2865: }
2866: }
2867: }
2868: return ServiceUtil.returnSuccess();
2869: }
2870:
2871: public static Map refundPayment(DispatchContext dctx, Map context) {
2872: LocalDispatcher dispatcher = dctx.getDispatcher();
2873: GenericValue userLogin = (GenericValue) context
2874: .get("userLogin");
2875:
2876: GenericValue paymentPref = (GenericValue) context
2877: .get("orderPaymentPreference");
2878: Double refundAmount = (Double) context.get("refundAmount");
2879:
2880: GenericValue orderHeader = null;
2881: try {
2882: orderHeader = paymentPref.getRelatedOne("OrderHeader");
2883: } catch (GenericEntityException e) {
2884: Debug
2885: .logError(
2886: e,
2887: "Cannot get OrderHeader from OrderPaymentPreference",
2888: module);
2889: return ServiceUtil
2890: .returnError("Problems getting OrderHeader from OrderPaymentPreference: "
2891: + e.toString());
2892: }
2893:
2894: OrderReadHelper orh = new OrderReadHelper(orderHeader);
2895:
2896: GenericValue paymentSettings = null;
2897: if (orderHeader != null) {
2898: paymentSettings = getPaymentSettings(orderHeader,
2899: paymentPref, REFUND_SERVICE_TYPE, false);
2900: }
2901:
2902: if (paymentSettings != null) {
2903: String paymentConfig = paymentSettings
2904: .getString("paymentPropertiesPath");
2905: String serviceName = paymentSettings
2906: .getString("paymentService");
2907: if (serviceName != null) {
2908: Map serviceContext = new HashMap();
2909: serviceContext.put("orderPaymentPreference",
2910: paymentPref);
2911: serviceContext.put("paymentConfig", paymentConfig);
2912: serviceContext.put("currency", orh.getCurrency());
2913:
2914: // get the creditCard/address/email
2915: String payToPartyId = null;
2916: try {
2917: payToPartyId = getBillingInformation(orh,
2918: paymentPref, new HashMap());
2919: } catch (GenericEntityException e) {
2920: Debug.logError(e,
2921: "Problems getting billing information",
2922: module);
2923: return ServiceUtil
2924: .returnError("Problems getting billing information");
2925: }
2926:
2927: // format the price
2928: String currencyFormat = UtilProperties
2929: .getPropertyValue("general.properties",
2930: "currency.decimal.format", "##0.00");
2931: DecimalFormat formatter = new DecimalFormat(
2932: currencyFormat);
2933: String amountString = formatter.format(refundAmount);
2934: Double processAmount = null;
2935: try {
2936: processAmount = new Double(formatter.parse(
2937: amountString).doubleValue());
2938: } catch (ParseException e) {
2939: Debug
2940: .logError(
2941: e,
2942: "Problem parsing amount using DecimalFormat",
2943: module);
2944: return ServiceUtil
2945: .returnError("Refund processor problems; see logs");
2946: }
2947: serviceContext.put("refundAmount", processAmount);
2948: serviceContext.put("userLogin", userLogin);
2949:
2950: // call the service
2951: Map refundResponse = null;
2952: try {
2953: refundResponse = dispatcher.runSync(serviceName,
2954: serviceContext, TX_TIME, true);
2955: } catch (GenericServiceException e) {
2956: Debug
2957: .logError(
2958: e,
2959: "Problem refunding payment through processor",
2960: module);
2961: return ServiceUtil
2962: .returnError("Refund processor problems; see logs");
2963: }
2964: if (ServiceUtil.isError(refundResponse)) {
2965: saveError(dispatcher, userLogin, paymentPref,
2966: refundResponse, REFUND_SERVICE_TYPE,
2967: "PGT_REFUND");
2968: return ServiceUtil.returnError(ServiceUtil
2969: .getErrorMessage(refundResponse));
2970: }
2971:
2972: // get the pay to party ID for the order (will be the payFrom)
2973: String payFromPartyId = getPayToPartyId(orderHeader);
2974:
2975: // process the refund result
2976: Map refundResRes;
2977: try {
2978: ModelService model = dctx
2979: .getModelService("processRefundResult");
2980: Map refundResCtx = model.makeValid(context,
2981: ModelService.IN_PARAM);
2982: refundResCtx
2983: .put("currencyUomId", orh.getCurrency());
2984: refundResCtx.put("payToPartyId", payToPartyId);
2985: refundResCtx.put("payFromPartyId", payFromPartyId);
2986: refundResCtx.put("refundRefNum", refundResponse
2987: .get("refundRefNum"));
2988: refundResCtx.put("refundAltRefNum", refundResponse
2989: .get("refundAltRefNum"));
2990: refundResCtx.put("refundMessage", refundResponse
2991: .get("refundMessage"));
2992: refundResCtx.put("refundResult", refundResponse
2993: .get("refundResult"));
2994: // TODO: should we uncomment the following line?
2995: //refundResCtx.put("refundAmount", (Double)refundResponse.get("refundAmount"));
2996: refundResRes = dispatcher.runSync(model.name,
2997: refundResCtx);
2998: } catch (GenericServiceException e) {
2999: Debug.logError(e, module);
3000: return ServiceUtil
3001: .returnError("Problem processing refund result: "
3002: + e.getMessage());
3003: }
3004:
3005: return refundResRes;
3006: } else {
3007: return ServiceUtil
3008: .returnError("No refund service defined");
3009: }
3010: } else {
3011: return ServiceUtil.returnError("No payment settings found");
3012: }
3013: }
3014:
3015: public static Map processRefundResult(DispatchContext dctx,
3016: Map context) {
3017: LocalDispatcher dispatcher = dctx.getDispatcher();
3018: GenericDelegator delegator = dctx.getDelegator();
3019:
3020: GenericValue userLogin = (GenericValue) context
3021: .get("userLogin");
3022: GenericValue paymentPref = (GenericValue) context
3023: .get("orderPaymentPreference");
3024: String currencyUomId = (String) context.get("currencyUomId");
3025: String payToPartyId = (String) context.get("payToPartyId");
3026: String payFromPartyId = (String) context.get("payFromPartyId");
3027:
3028: // create the PaymentGatewayResponse record
3029: String responseId = delegator
3030: .getNextSeqId("PaymentGatewayResponse");
3031: GenericValue response = delegator.makeValue(
3032: "PaymentGatewayResponse", null);
3033: response.set("paymentGatewayResponseId", responseId);
3034: response.set("paymentServiceTypeEnumId", REFUND_SERVICE_TYPE);
3035: response.set("orderPaymentPreferenceId", paymentPref
3036: .get("orderPaymentPreferenceId"));
3037: response.set("paymentMethodTypeId", paymentPref
3038: .get("paymentMethodTypeId"));
3039: response.set("paymentMethodId", paymentPref
3040: .get("paymentMethodId"));
3041: response.set("transCodeEnumId", "PGT_REFUND");
3042:
3043: // set the capture info
3044: response.set("amount", context.get("refundAmount"));
3045: response.set("currencyUomId", currencyUomId);
3046: response.set("referenceNum", context.get("refundRefNum"));
3047: response.set("altReference", context.get("refundAltRefNum"));
3048: response.set("gatewayCode", context.get("refundCode"));
3049: response.set("gatewayFlag", context.get("refundFlag"));
3050: response.set("gatewayMessage", context.get("refundMessage"));
3051: response.set("transactionDate", UtilDateTime.nowTimestamp());
3052:
3053: // save the response
3054: savePgr(dctx, response);
3055:
3056: // create the internal messages
3057: List messages = (List) context.get("internalRespMsgs");
3058: if (messages != null && messages.size() > 0) {
3059: Iterator i = messages.iterator();
3060: while (i.hasNext()) {
3061: GenericValue respMsg = delegator.makeValue(
3062: "PaymentGatewayRespMsg", null);
3063: String respMsgId = delegator
3064: .getNextSeqId("PaymentGatewayRespMsg");
3065: String message = (String) i.next();
3066: respMsg.set("paymentGatewayRespMsgId", respMsgId);
3067: respMsg.set("paymentGatewayResponseId", responseId);
3068: respMsg.set("pgrMessage", message);
3069:
3070: // save the message
3071: savePgr(dctx, respMsg);
3072: }
3073: }
3074:
3075: // handle the (reverse) payment
3076: Boolean refundResult = (Boolean) context.get("refundResult");
3077: if (refundResult != null && refundResult.booleanValue()) {
3078: // create a payment record
3079: Map paymentCtx = UtilMisc.toMap("paymentTypeId",
3080: "CUSTOMER_REFUND");
3081: paymentCtx.put("paymentMethodTypeId", paymentPref
3082: .get("paymentMethodTypeId"));
3083: paymentCtx.put("paymentMethodId", paymentPref
3084: .get("paymentMethodId"));
3085: paymentCtx.put("paymentGatewayResponseId", responseId);
3086: paymentCtx.put("partyIdTo", payToPartyId);
3087: paymentCtx.put("partyIdFrom", payFromPartyId);
3088: paymentCtx.put("statusId", "PMNT_SENT");
3089: paymentCtx.put("paymentPreferenceId", paymentPref
3090: .get("orderPaymentPreferenceId"));
3091: paymentCtx.put("currencyUomId", currencyUomId);
3092: paymentCtx.put("amount", context.get("refundAmount"));
3093: paymentCtx.put("userLogin", userLogin);
3094: paymentCtx
3095: .put("paymentRefNum", context.get("refundRefNum"));
3096: paymentCtx.put("comments", "Refund");
3097:
3098: String paymentId = null;
3099: try {
3100: Map payRes = dispatcher.runSync("createPayment",
3101: paymentCtx);
3102: if (ModelService.RESPOND_ERROR.equals(payRes
3103: .get(ModelService.RESPONSE_MESSAGE))) {
3104: return ServiceUtil.returnError((String) payRes
3105: .get(ModelService.ERROR_MESSAGE));
3106: } else {
3107: paymentId = (String) payRes.get("paymentId");
3108: }
3109: } catch (GenericServiceException e) {
3110: Debug.logError(e, "Problem creating Payment", module);
3111: return ServiceUtil
3112: .returnError("Problem creating Payment");
3113: }
3114: //Debug.log("Payment created : " + paymentId, module);
3115:
3116: if (paymentId == null) {
3117: return ServiceUtil.returnError("Create payment failed");
3118: }
3119:
3120: Map result = ServiceUtil.returnSuccess();
3121: result.put("paymentId", paymentId);
3122: return result;
3123: } else {
3124: return ServiceUtil.returnFailure("The refund failed");
3125: }
3126: }
3127:
3128: public static Map retryFailedOrderAuth(DispatchContext dctx,
3129: Map context) {
3130: GenericDelegator delegator = dctx.getDelegator();
3131: LocalDispatcher dispatcher = dctx.getDispatcher();
3132: String orderId = (String) context.get("orderId");
3133: GenericValue userLogin = (GenericValue) context
3134: .get("userLogin");
3135:
3136: // get the order header
3137: GenericValue orderHeader = null;
3138: try {
3139: orderHeader = delegator.findByPrimaryKey("OrderHeader",
3140: UtilMisc.toMap("orderId", orderId));
3141: } catch (GenericEntityException e) {
3142: Debug.logError(e, module);
3143: return ServiceUtil.returnError(e.toString());
3144: }
3145:
3146: // make sure we have a valid order record
3147: if (orderHeader == null || orderHeader.get("statusId") == null) {
3148: return ServiceUtil
3149: .returnError("Invalid OrderHeader record for ID: "
3150: + orderId);
3151: }
3152:
3153: // check the current order status
3154: if (!"ORDER_CREATED".equals(orderHeader.getString("statusId"))) {
3155: // if we are out of the created status; then we were either cancelled, rejected or approved
3156: Debug
3157: .logWarning(
3158: "Was re-trying a failed auth for orderId ["
3159: + orderId
3160: + "] but it is not in the ORDER_CREATED status, so skipping.",
3161: module);
3162: return ServiceUtil.returnSuccess();
3163: }
3164:
3165: // run the auth service and check for failure(s)
3166: Map serviceResult = null;
3167: try {
3168: serviceResult = dispatcher.runSync("authOrderPayments",
3169: UtilMisc.toMap("orderId", orderId, "userLogin",
3170: userLogin));
3171: } catch (GenericServiceException e) {
3172: Debug.logError(e, module);
3173: return ServiceUtil.returnError(e.toString());
3174: }
3175: if (ServiceUtil.isError(serviceResult)) {
3176: return ServiceUtil.returnError(ServiceUtil
3177: .getErrorMessage(serviceResult));
3178: }
3179:
3180: // check to see if there was a processor failure
3181: String authResp = (String) serviceResult.get("processResult");
3182: if (authResp == null) {
3183: authResp = "ERROR";
3184: }
3185:
3186: if ("ERROR".equals(authResp)) {
3187: Debug
3188: .logWarning(
3189: "The payment processor had a failure in processing, will not modify any status",
3190: module);
3191: } else {
3192: if ("FAILED".equals(authResp)) {
3193: // declined; update the order status
3194: OrderChangeHelper.rejectOrder(dispatcher, userLogin,
3195: orderId);
3196: } else if ("APPROVED".equals(authResp)) {
3197: // approved; update the order status
3198: OrderChangeHelper.approveOrder(dispatcher, userLogin,
3199: orderId);
3200: }
3201: }
3202:
3203: Map result = ServiceUtil.returnSuccess();
3204: result.put("processResult", authResp);
3205:
3206: return result;
3207: }
3208:
3209: public static Map retryFailedAuths(DispatchContext dctx, Map context) {
3210: GenericDelegator delegator = dctx.getDelegator();
3211: LocalDispatcher dispatcher = dctx.getDispatcher();
3212: GenericValue userLogin = (GenericValue) context
3213: .get("userLogin");
3214:
3215: // get a list of all payment prefs still pending
3216: List exprs = UtilMisc.toList(new EntityExpr("statusId",
3217: EntityOperator.EQUALS, "PAYMENT_NOT_AUTH"),
3218: new EntityExpr("processAttempt",
3219: EntityOperator.GREATER_THAN, new Long(0)));
3220:
3221: EntityListIterator eli = null;
3222: try {
3223: eli = delegator.findListIteratorByCondition(
3224: "OrderPaymentPreference", new EntityConditionList(
3225: exprs, EntityOperator.AND), null, UtilMisc
3226: .toList("orderId"));
3227: List processList = new ArrayList();
3228: if (eli != null) {
3229: Debug.logInfo("Processing failed order re-auth(s)",
3230: module);
3231: GenericValue value = null;
3232: while (((value = (GenericValue) eli.next()) != null)) {
3233: String orderId = value.getString("orderId");
3234: if (!processList.contains(orderId)) { // just try each order once
3235: try {
3236: // each re-try is independent of each other; if one fails it should not effect the others
3237: dispatcher.runAsync("retryFailedOrderAuth",
3238: UtilMisc.toMap("orderId", orderId,
3239: "userLogin", userLogin));
3240: processList.add(orderId);
3241: } catch (GenericServiceException e) {
3242: Debug.logError(e, module);
3243: }
3244: }
3245: }
3246: }
3247: } catch (GenericEntityException e) {
3248: Debug.logError(e, module);
3249: } finally {
3250: if (eli != null) {
3251: try {
3252: eli.close();
3253: } catch (GenericEntityException e) {
3254: Debug.logError(e, module);
3255: }
3256: }
3257: }
3258:
3259: return ServiceUtil.returnSuccess();
3260: }
3261:
3262: public static Map retryFailedAuthNsfs(DispatchContext dctx,
3263: Map context) {
3264: GenericDelegator delegator = dctx.getDelegator();
3265: LocalDispatcher dispatcher = dctx.getDispatcher();
3266: GenericValue userLogin = (GenericValue) context
3267: .get("userLogin");
3268:
3269: // get the date/time for one week before now since we'll only retry once a week for NSFs
3270: Calendar calcCal = Calendar.getInstance();
3271: calcCal.setTimeInMillis(System.currentTimeMillis());
3272: calcCal.add(Calendar.WEEK_OF_YEAR, -1);
3273: Timestamp oneWeekAgo = new Timestamp(calcCal.getTimeInMillis());
3274:
3275: EntityListIterator eli = null;
3276: try {
3277: eli = delegator.findListIteratorByCondition(
3278: "OrderPaymentPreference", new EntityExpr(
3279: new EntityExpr("needsNsfRetry",
3280: EntityOperator.EQUALS, "Y"),
3281: EntityOperator.AND, new EntityExpr(
3282: ModelEntity.STAMP_FIELD,
3283: EntityOperator.LESS_THAN_EQUAL_TO,
3284: oneWeekAgo)), null, UtilMisc
3285: .toList("orderId"));
3286:
3287: List processList = new ArrayList();
3288: if (eli != null) {
3289: Debug.logInfo("Processing failed order re-auth(s)",
3290: module);
3291: GenericValue value = null;
3292: while (((value = (GenericValue) eli.next()) != null)) {
3293: String orderId = value.getString("orderId");
3294: if (!processList.contains(orderId)) { // just try each order once
3295: try {
3296: // each re-try is independent of each other; if one fails it should not effect the others
3297: dispatcher.runAsync(
3298: "retryFailedOrderAuthNsf", UtilMisc
3299: .toMap("orderId", orderId,
3300: "userLogin",
3301: userLogin));
3302: processList.add(orderId);
3303: } catch (GenericServiceException e) {
3304: Debug.logError(e, module);
3305: }
3306: }
3307: }
3308: }
3309: } catch (GenericEntityException e) {
3310: Debug.logError(e, module);
3311: } finally {
3312: if (eli != null) {
3313: try {
3314: eli.close();
3315: } catch (GenericEntityException e) {
3316: Debug.logError(e, module);
3317: }
3318: }
3319: }
3320:
3321: return ServiceUtil.returnSuccess();
3322: }
3323:
3324: public static GenericValue getCaptureTransaction(
3325: GenericValue orderPaymentPreference) {
3326: GenericValue capTrans = null;
3327: try {
3328: List order = UtilMisc.toList("-transactionDate");
3329: List transactions = orderPaymentPreference.getRelated(
3330: "PaymentGatewayResponse", null, order);
3331:
3332: List exprs = UtilMisc.toList(new EntityExpr(
3333: "paymentServiceTypeEnumId", EntityOperator.EQUALS,
3334: CAPTURE_SERVICE_TYPE));
3335:
3336: List capTransactions = EntityUtil.filterByAnd(transactions,
3337: exprs);
3338:
3339: capTrans = EntityUtil.getFirst(capTransactions);
3340: } catch (GenericEntityException e) {
3341: Debug
3342: .logError(
3343: e,
3344: "ERROR: Problem getting capture information from PaymentGatewayResponse",
3345: module);
3346: }
3347: return capTrans;
3348: }
3349:
3350: /**
3351: * Gets the chronologically latest PaymentGatewayResponse from an OrderPaymentPreference which is either a PRDS_PAY_AUTH
3352: * or PRDS_PAY_REAUTH. Used for capturing.
3353: * @param orderPaymentPreference
3354: * @return
3355: */
3356: public static GenericValue getAuthTransaction(
3357: GenericValue orderPaymentPreference) {
3358: GenericValue authTrans = null;
3359: try {
3360: List order = UtilMisc.toList("-transactionDate");
3361: List transactions = orderPaymentPreference.getRelated(
3362: "PaymentGatewayResponse", null, order);
3363:
3364: List exprs = UtilMisc.toList(new EntityExpr(
3365: "paymentServiceTypeEnumId", EntityOperator.EQUALS,
3366: AUTH_SERVICE_TYPE), new EntityExpr(
3367: "paymentServiceTypeEnumId", EntityOperator.EQUALS,
3368: REAUTH_SERVICE_TYPE));
3369:
3370: List authTransactions = EntityUtil.filterByOr(transactions,
3371: exprs);
3372:
3373: authTrans = EntityUtil.getFirst(authTransactions);
3374: } catch (GenericEntityException e) {
3375: Debug
3376: .logError(
3377: e,
3378: "ERROR: Problem getting authorization information from PaymentGatewayResponse",
3379: module);
3380: }
3381: return authTrans;
3382: }
3383:
3384: public static Timestamp getAuthTime(
3385: GenericValue orderPaymentPreference) {
3386: GenericValue authTrans = PaymentGatewayServices
3387: .getAuthTransaction(orderPaymentPreference);
3388: Timestamp authTime = null;
3389:
3390: if (authTrans != null) {
3391: authTime = authTrans.getTimestamp("transactionDate");
3392: }
3393:
3394: return authTime;
3395: }
3396:
3397: public static boolean checkAuthValidity(
3398: GenericValue orderPaymentPreference, String paymentConfig) {
3399: Timestamp authTime = PaymentGatewayServices
3400: .getAuthTime(orderPaymentPreference);
3401: if (authTime == null) {
3402: return false;
3403: }
3404:
3405: GenericValue paymentMethod = null;
3406: try {
3407: paymentMethod = orderPaymentPreference
3408: .getRelatedOne("PaymentMethod");
3409: } catch (GenericEntityException e) {
3410: Debug.logError(e, module);
3411: }
3412:
3413: if (paymentMethod != null
3414: && paymentMethod.getString("paymentMethodTypeId")
3415: .equals("CREDIT_CARD")) {
3416: GenericValue creditCard = null;
3417: try {
3418: creditCard = paymentMethod.getRelatedOne("CreditCard");
3419: } catch (GenericEntityException e) {
3420: Debug.logError(e, module);
3421: }
3422: if (creditCard != null) {
3423: String cardType = creditCard.getString("cardType");
3424: String reauthDays = null;
3425: // add more types as necessary -- maybe we should create seed data for credit card types??
3426: if ("Discover".equals(cardType)) {
3427: reauthDays = UtilProperties.getPropertyValue(
3428: paymentConfig,
3429: "payment.general.reauth.disc.days", "90");
3430: } else if ("AmericanExpress".equals(cardType)) {
3431: reauthDays = UtilProperties.getPropertyValue(
3432: paymentConfig,
3433: "payment.general.reauth.amex.days", "30");
3434: } else if ("MasterCard".equals(cardType)) {
3435: reauthDays = UtilProperties.getPropertyValue(
3436: paymentConfig,
3437: "payment.general.reauth.mc.days", "30");
3438: } else if ("Visa".equals(cardType)) {
3439: reauthDays = UtilProperties.getPropertyValue(
3440: paymentConfig,
3441: "payment.general.reauth.visa.days", "7");
3442: } else {
3443: reauthDays = UtilProperties.getPropertyValue(
3444: paymentConfig,
3445: "payment.general.reauth.other.days", "7");
3446: }
3447:
3448: int days = 0;
3449: try {
3450: days = Integer.parseInt(reauthDays);
3451: } catch (Exception e) {
3452: Debug.logError(e, module);
3453: }
3454:
3455: if (days > 0) {
3456: Calendar cal = Calendar.getInstance();
3457: cal.setTimeInMillis(authTime.getTime());
3458: cal.add(Calendar.DAY_OF_YEAR, days);
3459: Timestamp validTime = new Timestamp(cal
3460: .getTimeInMillis());
3461: Timestamp nowTime = UtilDateTime.nowTimestamp();
3462: if (nowTime.after(validTime)) {
3463: return false;
3464: }
3465: }
3466: }
3467: }
3468:
3469: return true;
3470: }
3471:
3472: // safe payment gateway store
3473: private static void savePgr(DispatchContext dctx, GenericValue pgr) {
3474: Map context = UtilMisc.toMap("paymentGatewayResponse", pgr);
3475: LocalDispatcher dispatcher = dctx.getDispatcher();
3476: GenericDelegator delegator = dctx.getDelegator();
3477:
3478: try {
3479: dispatcher.addRollbackService("savePaymentGatewayResponse",
3480: context, true);
3481: delegator.create(pgr);
3482: } catch (Exception e) {
3483: Debug.logError(e, module);
3484: }
3485: }
3486:
3487: public static Map savePaymentGatewayResponse(DispatchContext dctx,
3488: Map context) {
3489: GenericDelegator delegator = dctx.getDelegator();
3490: GenericValue pgr = (GenericValue) context
3491: .get("paymentGatewayResponse");
3492: String message = pgr.getString("gatewayMessage");
3493: if (message.length() > 255) {
3494: pgr.set("gatewayMessage", message.substring(0, 255));
3495: }
3496:
3497: try {
3498: delegator.create(pgr);
3499: } catch (GenericEntityException e) {
3500: Debug.logError(e, module);
3501: }
3502:
3503: return ServiceUtil.returnSuccess();
3504: }
3505:
3506: // manual processing service
3507:
3508: public static Map processManualCcTx(DispatchContext dctx,
3509: Map context) {
3510: GenericValue userLogin = (GenericValue) context
3511: .get("userLogin");
3512: LocalDispatcher dispatcher = dctx.getDispatcher();
3513: GenericDelegator delegator = dctx.getDelegator();
3514: Security security = dctx.getSecurity();
3515:
3516: // security check
3517: if (!security.hasEntityPermission("MANUAL", "_PAYMENT",
3518: userLogin)) {
3519: Debug.logWarning("**** Security ["
3520: + (new Date()).toString() + "]: "
3521: + userLogin.get("userLoginId")
3522: + " attempt to run manual payment transaction!",
3523: module);
3524: return ServiceUtil
3525: .returnError("You do not have permission for this transaction.");
3526: }
3527:
3528: String paymentMethodTypeId = (String) context
3529: .get("paymentMethodTypeId");
3530: String productStoreId = (String) context.get("productStoreId");
3531: String transactionType = (String) context
3532: .get("transactionType");
3533: String referenceCode = (String) context.get("referenceCode");
3534: if (referenceCode == null) {
3535: referenceCode = new Long(System.currentTimeMillis())
3536: .toString();
3537: }
3538:
3539: // check valid implemented types
3540: if (!transactionType.equals(CREDIT_SERVICE_TYPE)) {
3541: return ServiceUtil
3542: .returnError("This transaction type is not yet supported.");
3543: }
3544:
3545: // transaction request context
3546: Map requestContext = new HashMap();
3547: String paymentService = null;
3548: String paymentConfig = null;
3549:
3550: // get the transaction settings
3551: GenericValue paymentSettings = ProductStoreWorker
3552: .getProductStorePaymentSetting(delegator,
3553: productStoreId, paymentMethodTypeId,
3554: transactionType, false);
3555: if (paymentSettings == null) {
3556: return ServiceUtil
3557: .returnError("No valid payment settings found for : "
3558: + productStoreId + "/" + transactionType);
3559: } else {
3560: paymentConfig = paymentSettings
3561: .getString("paymentPropertiesPath");
3562: paymentService = paymentSettings
3563: .getString("paymentService");
3564: requestContext.put("paymentConfig", paymentConfig);
3565: }
3566:
3567: // check the service name
3568: if (paymentService == null || paymentConfig == null) {
3569: return ServiceUtil
3570: .returnError("Invalid product store payment settings");
3571: }
3572:
3573: if (paymentMethodTypeId.equals("CREDIT_CARD")) {
3574: GenericValue creditCard = delegator.makeValue("CreditCard",
3575: null);
3576: creditCard.setAllFields(context, true, null, null);
3577: if (creditCard.get("firstNameOnCard") == null
3578: || creditCard.get("lastNameOnCard") == null
3579: || creditCard.get("cardType") == null
3580: || creditCard.get("cardNumber") == null) {
3581: return ServiceUtil
3582: .returnError("Credit card is missing required fields.");
3583: }
3584: String expMonth = (String) context.get("expMonth");
3585: String expYear = (String) context.get("expYear");
3586: String expDate = expMonth + "/" + expYear;
3587: creditCard.set("expireDate", expDate);
3588: requestContext.put("creditCard", creditCard);
3589: requestContext.put("cardSecurityCode", context
3590: .get("cardSecurityCode"));
3591:
3592: GenericValue billingAddress = delegator.makeValue(
3593: "PostalAddress", null);
3594: billingAddress.setAllFields(context, true, null, null);
3595: if (billingAddress.get("address1") == null
3596: || billingAddress.get("city") == null
3597: || billingAddress.get("postalCode") == null) {
3598: return ServiceUtil
3599: .returnError("Credit card billing address is missing required fields.");
3600: }
3601: requestContext.put("billingAddress", billingAddress);
3602:
3603: /* This is not needed any more, using names on CC as a kludge instead of these kludge names until we get a firstName/lastName on the shipping PostalAddress
3604: GenericValue contactPerson = delegator.makeValue("Person", null);
3605: contactPerson.setAllFields(context, true, null, null);
3606: if (contactPerson.get("firstName") == null || contactPerson.get("lastName") == null) {
3607: return ServiceUtil.returnError("Contact person is missing required fields.");
3608: }
3609: requestContext.put("contactPerson", contactPerson);
3610: */
3611:
3612: GenericValue billToEmail = delegator.makeValue(
3613: "ContactMech", null);
3614: billToEmail.set("infoString", context.get("infoString"));
3615: if (billToEmail.get("infoString") == null) {
3616: return ServiceUtil
3617: .returnError("Email address field cannot be empty.");
3618: }
3619: requestContext.put("billToEmail", billToEmail);
3620: requestContext.put("referenceCode", referenceCode);
3621: String currency = UtilProperties.getPropertyValue(
3622: "general.properties", "currency.uom.id.default",
3623: "USD");
3624: requestContext.put("currency", currency);
3625: requestContext.put("creditAmount", context.get("amount")); // TODO fix me to work w/ other services
3626: } else {
3627: return ServiceUtil
3628: .returnError("Payment method type : "
3629: + paymentMethodTypeId
3630: + " is not yet implemented for manual transactions");
3631: }
3632:
3633: // process the transaction
3634: Map response = null;
3635: try {
3636: response = dispatcher.runSync(paymentService,
3637: requestContext, TX_TIME, true);
3638: } catch (GenericServiceException e) {
3639: Debug.logError(e, module);
3640: return ServiceUtil.returnError("Error calling service : "
3641: + paymentService + " / " + requestContext);
3642: }
3643:
3644: // check for errors
3645: if (ServiceUtil.isError(response)) {
3646: return ServiceUtil
3647: .returnError(ServiceUtil.makeErrorMessage(response,
3648: null, null, null, null));
3649: }
3650:
3651: // get the reference number // TODO add support for other tx types
3652: String refNum = (String) response.get("creditRefNum");
3653: String code = (String) response.get("creditCode");
3654: String msg = (String) response.get("creditMessage");
3655: Map returnResults = ServiceUtil
3656: .returnSuccess("Transaction result [" + msg + "/"
3657: + code + "] Ref#: " + refNum);
3658: returnResults.put("referenceNum", refNum);
3659: return returnResults;
3660: }
3661:
3662: // ****************************************************
3663: // Test Services
3664: // ****************************************************
3665:
3666: /**
3667: * Simple test processor; declines all orders < 100.00; approves all orders >= 100.00
3668: */
3669: public static Map testProcessor(DispatchContext dctx, Map context) {
3670: Map result = new HashMap();
3671: Double processAmount = (Double) context.get("processAmount");
3672:
3673: if (processAmount != null
3674: && processAmount.doubleValue() >= 100.00)
3675: result.put("authResult", Boolean.TRUE);
3676: if (processAmount != null
3677: && processAmount.doubleValue() < 100.00)
3678: result.put("authResult", Boolean.FALSE);
3679: result
3680: .put(
3681: "customerRespMsgs",
3682: UtilMisc
3683: .toList("Sorry this processor requires at least a $100.00 purchase."));
3684: if (processAmount == null)
3685: result.put("authResult", null);
3686:
3687: String refNum = UtilDateTime.nowAsString();
3688:
3689: result.put("processAmount", context.get("processAmount"));
3690: result.put("authRefNum", refNum);
3691: result.put("authAltRefNum", refNum);
3692: result.put("authFlag", "X");
3693: result
3694: .put("authMessage",
3695: "This is a test processor; no payments were captured or authorized.");
3696: result
3697: .put(
3698: "internalRespMsgs",
3699: UtilMisc
3700: .toList("This is a test processor; no payments were captured or authorized."));
3701: return result;
3702: }
3703:
3704: /**
3705: * Simple test processor; declines all orders < 100.00; approves all orders > 100.00
3706: */
3707: public static Map testProcessorWithCapture(DispatchContext dctx,
3708: Map context) {
3709: Map result = new HashMap();
3710: Double processAmount = (Double) context.get("processAmount");
3711:
3712: if (processAmount != null
3713: && processAmount.doubleValue() >= 100.00)
3714: result.put("authResult", Boolean.TRUE);
3715: result.put("captureResult", Boolean.TRUE);
3716: if (processAmount != null
3717: && processAmount.doubleValue() < 100.00)
3718: result.put("authResult", Boolean.FALSE);
3719: result.put("captureResult", Boolean.FALSE);
3720: result
3721: .put(
3722: "customerRespMsgs",
3723: UtilMisc
3724: .toList("Sorry this processor requires at least a $100.00 purchase."));
3725: if (processAmount == null)
3726: result.put("authResult", null);
3727:
3728: String refNum = UtilDateTime.nowAsString();
3729:
3730: result.put("processAmount", context.get("processAmount"));
3731: result.put("authRefNum", refNum);
3732: result.put("authAltRefNum", refNum);
3733: result.put("captureRefNum", refNum);
3734: result.put("captureAltRefNum", refNum);
3735: result.put("authCode", "100");
3736: result.put("captureCode", "200");
3737: result.put("authFlag", "X");
3738: result
3739: .put("authMessage",
3740: "This is a test processor; no payments were captured or authorized.");
3741: result
3742: .put(
3743: "internalRespMsgs",
3744: UtilMisc
3745: .toList("This is a test processor; no payments were captured or authorized."));
3746: return result;
3747: }
3748:
3749: /**
3750: * Test authorize - does random declines
3751: */
3752: public static Map testRandomAuthorize(DispatchContext dctx,
3753: Map context) {
3754: Map result = ServiceUtil.returnSuccess();
3755: String refNum = UtilDateTime.nowAsString();
3756: Random r = new Random();
3757: int i = r.nextInt(9);
3758: if (i < 5 || i % 2 == 0) {
3759: result.put("authResult", Boolean.TRUE);
3760: result.put("authFlag", "A");
3761: } else {
3762: result.put("authResult", Boolean.FALSE);
3763: result.put("authFlag", "D");
3764: }
3765:
3766: result.put("processAmount", context.get("processAmount"));
3767: result.put("authRefNum", refNum);
3768: result.put("authAltRefNum", refNum);
3769: result.put("authCode", "100");
3770: result
3771: .put("authMessage",
3772: "This is a test processor; no payments were captured or authorized.");
3773:
3774: return result;
3775: }
3776:
3777: /**
3778: * Always approve processor.
3779: */
3780: public static Map alwaysApproveProcessor(DispatchContext dctx,
3781: Map context) {
3782: Map result = new HashMap();
3783: Debug.logInfo("Test Processor Approving Credit Card", module);
3784:
3785: String refNum = UtilDateTime.nowAsString();
3786:
3787: result.put("authResult", Boolean.TRUE);
3788: result.put("processAmount", context.get("processAmount"));
3789: result.put("authRefNum", refNum);
3790: result.put("authAltRefNum", refNum);
3791: result.put("authCode", "100");
3792: result.put("authFlag", "A");
3793: result
3794: .put("authMessage",
3795: "This is a test processor; no payments were captured or authorized.");
3796: return result;
3797: }
3798:
3799: public static Map alwaysApproveWithCapture(DispatchContext dctx,
3800: Map context) {
3801: Map result = new HashMap();
3802: String refNum = UtilDateTime.nowAsString();
3803: Debug.logInfo(
3804: "Test Processor Approving Credit Card with Capture",
3805: module);
3806:
3807: result.put("authResult", Boolean.TRUE);
3808: result.put("captureResult", Boolean.TRUE);
3809: result.put("processAmount", context.get("processAmount"));
3810: result.put("authRefNum", refNum);
3811: result.put("authAltRefNum", refNum);
3812: result.put("captureRefNum", refNum);
3813: result.put("captureAltRefNum", refNum);
3814: result.put("authCode", "100");
3815: result.put("captureCode", "200");
3816: result.put("authFlag", "A");
3817: result
3818: .put("authMessage",
3819: "This is a test processor; no payments were captured or authorized.");
3820: return result;
3821: }
3822:
3823: /**
3824: * Always decline processor
3825: */
3826: public static Map alwaysDeclineProcessor(DispatchContext dctx,
3827: Map context) {
3828: Map result = ServiceUtil.returnSuccess();
3829: Double processAmount = (Double) context.get("processAmount");
3830: Debug.logInfo("Test Processor Declining Credit Card", module);
3831:
3832: String refNum = UtilDateTime.nowAsString();
3833:
3834: result.put("authResult", Boolean.FALSE);
3835: result.put("processAmount", processAmount);
3836: result.put("authRefNum", refNum);
3837: result.put("authAltRefNum", refNum);
3838: result.put("authFlag", "D");
3839: result
3840: .put("authMessage",
3841: "This is a test processor; no payments were captured or authorized");
3842: return result;
3843: }
3844:
3845: /**
3846: * Always NSF (not sufficient funds) processor
3847: */
3848: public static Map alwaysNsfProcessor(DispatchContext dctx,
3849: Map context) {
3850: Map result = ServiceUtil.returnSuccess();
3851: Double processAmount = (Double) context.get("processAmount");
3852: Debug.logInfo("Test Processor NSF Credit Card", module);
3853:
3854: String refNum = UtilDateTime.nowAsString();
3855:
3856: result.put("authResult", Boolean.FALSE);
3857: result.put("resultNsf", Boolean.TRUE);
3858: result.put("processAmount", processAmount);
3859: result.put("authRefNum", refNum);
3860: result.put("authAltRefNum", refNum);
3861: result.put("authFlag", "N");
3862: result
3863: .put("authMessage",
3864: "This is a test processor; no payments were captured or authorized");
3865: return result;
3866: }
3867:
3868: /**
3869: * Always fail/bad expire date processor
3870: */
3871: public static Map alwaysBadExpireProcessor(DispatchContext dctx,
3872: Map context) {
3873: Map result = ServiceUtil.returnSuccess();
3874: Double processAmount = (Double) context.get("processAmount");
3875: Debug.logInfo("Test Processor Bad Expire Date Credit Card",
3876: module);
3877:
3878: String refNum = UtilDateTime.nowAsString();
3879:
3880: result.put("authResult", Boolean.FALSE);
3881: result.put("resultBadExpire", Boolean.TRUE);
3882: result.put("processAmount", processAmount);
3883: result.put("authRefNum", refNum);
3884: result.put("authAltRefNum", refNum);
3885: result.put("authFlag", "E");
3886: result
3887: .put("authMessage",
3888: "This is a test processor; no payments were captured or authorized");
3889: return result;
3890: }
3891:
3892: /**
3893: * Fail/bad expire date when year is even processor
3894: */
3895: public static Map badExpireEvenProcessor(DispatchContext dctx,
3896: Map context) {
3897: GenericValue creditCard = (GenericValue) context
3898: .get("creditCard");
3899: String expireDate = creditCard.getString("expireDate");
3900: String lastNumberStr = expireDate
3901: .substring(expireDate.length() - 1);
3902: int lastNumber = Integer.parseInt(lastNumberStr);
3903:
3904: if ((float) lastNumber / 2.0 == 0.0) {
3905: return alwaysBadExpireProcessor(dctx, context);
3906: } else {
3907: return alwaysApproveProcessor(dctx, context);
3908: }
3909: }
3910:
3911: /**
3912: * Always bad card number processor
3913: */
3914: public static Map alwaysBadCardNumberProcessor(
3915: DispatchContext dctx, Map context) {
3916: Map result = ServiceUtil.returnSuccess();
3917: Double processAmount = (Double) context.get("processAmount");
3918: Debug.logInfo("Test Processor Bad Card Number Credit Card",
3919: module);
3920:
3921: String refNum = UtilDateTime.nowAsString();
3922:
3923: result.put("authResult", Boolean.FALSE);
3924: result.put("resultBadCardNumber", Boolean.TRUE);
3925: result.put("processAmount", processAmount);
3926: result.put("authRefNum", refNum);
3927: result.put("authAltRefNum", refNum);
3928: result.put("authFlag", "N");
3929: result
3930: .put("authMessage",
3931: "This is a test processor; no payments were captured or authorized");
3932: return result;
3933: }
3934:
3935: /**
3936: * Always fail (error) processor
3937: */
3938: public static Map alwaysFailProcessor(DispatchContext dctx,
3939: Map context) {
3940: return ServiceUtil
3941: .returnError("Unable to communicate with bla");
3942: }
3943:
3944: public static Map testRelease(DispatchContext dctx, Map context) {
3945: Map result = ServiceUtil.returnSuccess();
3946:
3947: String refNum = UtilDateTime.nowAsString();
3948:
3949: result.put("releaseResult", Boolean.TRUE);
3950: result.put("releaseAmount", context.get("releaseAmount"));
3951: result.put("releaseRefNum", refNum);
3952: result.put("releaseAltRefNum", refNum);
3953: result.put("releaseFlag", "U");
3954: result.put("releaseMessage",
3955: "This is a test release; no authorizations exist");
3956: return result;
3957: }
3958:
3959: /**
3960: * Test capture service (returns true)
3961: */
3962: public static Map testCapture(DispatchContext dctx, Map context) {
3963: Map result = ServiceUtil.returnSuccess();
3964: Debug.logInfo("Test Capture Process", module);
3965:
3966: String refNum = UtilDateTime.nowAsString();
3967:
3968: result.put("captureResult", Boolean.TRUE);
3969: result.put("captureAmount", context.get("captureAmount"));
3970: result.put("captureRefNum", refNum);
3971: result.put("captureAltRefNum", refNum);
3972: result.put("captureFlag", "C");
3973: result.put("captureMessage",
3974: "This is a test capture; no money was transferred");
3975: return result;
3976: }
3977:
3978: public static Map testCaptureWithReAuth(DispatchContext dctx,
3979: Map context) {
3980: GenericValue orderPaymentPreference = (GenericValue) context
3981: .get("orderPaymentPreference");
3982: GenericValue authTransaction = (GenericValue) context
3983: .get("authTrans");
3984: Debug
3985: .logInfo(
3986: "Test Capture with 2 minute delay failure/re-auth process",
3987: module);
3988:
3989: if (authTransaction == null) {
3990: authTransaction = PaymentGatewayServices
3991: .getAuthTransaction(orderPaymentPreference);
3992: }
3993:
3994: if (authTransaction == null) {
3995: return ServiceUtil
3996: .returnError("No authorization transaction found for the OrderPaymentPreference; cannot capture");
3997: }
3998: Timestamp txStamp = authTransaction
3999: .getTimestamp("transactionDate");
4000: Timestamp nowStamp = UtilDateTime.nowTimestamp();
4001:
4002: Map result = ServiceUtil.returnSuccess();
4003: result.put("captureAmount", context.get("captureAmount"));
4004: result.put("captureRefNum", UtilDateTime.nowAsString());
4005:
4006: Calendar cal = Calendar.getInstance();
4007: cal.setTimeInMillis(txStamp.getTime());
4008: cal.add(Calendar.MINUTE, 2);
4009: Timestamp twoMinAfter = new Timestamp(cal.getTimeInMillis());
4010: Debug.log("Re-Auth Capture Test : Tx Date - " + txStamp
4011: + " : 2 Min - " + twoMinAfter + " : Now - " + nowStamp,
4012: module);
4013:
4014: if (nowStamp.after(twoMinAfter)) {
4015: result.put("captureResult", Boolean.FALSE);
4016: } else {
4017: result.put("captureResult", Boolean.TRUE);
4018: result.put("captureFlag", "C");
4019: result.put("captureMessage",
4020: "This is a test capture; no money was transferred");
4021: }
4022:
4023: return result;
4024: }
4025:
4026: /**
4027: * Test refund service (returns true)
4028: */
4029: public static Map testRefund(DispatchContext dctx, Map context) {
4030: Map result = ServiceUtil.returnSuccess();
4031: Debug.logInfo("Test Refund Process", module);
4032:
4033: result.put("refundResult", Boolean.TRUE);
4034: result.put("refundAmount", context.get("refundAmount"));
4035: result.put("refundRefNum", UtilDateTime.nowAsString());
4036: result.put("refundFlag", "R");
4037: result.put("refundMessage",
4038: "This is a test refund; no money was transferred");
4039: return result;
4040: }
4041:
4042: public static Map testRefundFailure(DispatchContext dctx,
4043: Map context) {
4044: Map result = ServiceUtil.returnSuccess();
4045: Debug.logInfo("Test Refund Process", module);
4046:
4047: result.put("refundResult", Boolean.FALSE);
4048: result.put("refundAmount", context.get("refundAmount"));
4049: result.put("refundRefNum", UtilDateTime.nowAsString());
4050: result.put("refundFlag", "R");
4051: result
4052: .put("refundMessage",
4053: "This is a test refund failure; no money was transferred");
4054: return result;
4055: }
4056: }
|