/* * GSAEFTCreditTenderActionImpl * * 08/05/2003 * * Copyright: * Licensed Materials - Property of IBM * "Restricted Materials of IBM" * 5724-AEF * (C) Copyright IBM Corp. 2003. * * %W% %E% */ package com.ibm.retail.AEF.action; import com.ibm.retail.AEF.automation.*; import com.ibm.retail.AEF.util.*; import com.ibm.retail.si.util.*; import com.ibm.retail.si.Copyright; import com.ibm.retail.AEF.factory.*; import com.ibm.retail.AEF.thread.*; import com.ibm.retail.AEF.data.*; import com.ibm.retail.AEF.session.*; import com.ibm.retail.AEF.event.*; import java.util.*; import java.rmi.*; import java.text.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * GSAEFTCreditTenderActionImpl is a class which the POSAutomationProvider uses to accomplish * adding an EFT and credit tender into a transaction on the POS application. * */ public class GSAEFTCreditTenderActionImpl extends GSAActionImpl { static String copyright() { return com.ibm.retail.si.Copyright.IBM_COPYRIGHT_SHORT;} /** * Constructor * * @param request The ActionRequest which contains a HashMap of arguments. * @exception com.ibm.retail.AEF.util.AEFException * AEFException return codes are: * <br>AEFConst.INVALID_ARGUMENT, AEFConst.ACCOUNT_NUMBER_REQUIRED * <br>AEFConst.INVALID_ARGUMENT, AEFConst.INVALID_EXPIRY_DATE * <br>AEFConst.INVALID_ARGUMENT, AEFConst.INVALID_TENDER_TYPE * <br>AEFConst.PROCEDURE_NOT_ALLOWED, AEFConst.NOT_IMPLEMENTED */ public GSAEFTCreditTenderActionImpl(ActionRequest request) throws AEFException { super(request); if (log.isTraceEnabled()) { tempAEFMessage.setMessage("+Enter GSAEFTCreditTenderActionImpl.GSAEFTCreditTenderActionImpl()."); log.trace(tempAEFMessage); } TenderIdentifier tenderID = (TenderIdentifier)(request.getArguments()); if (!(tenderID instanceof CreditIdentifier)) { AEFException tempException = new AEFException(AEFConst.PROCEDURE_NOT_ALLOWED, AEFConst.NOT_IMPLEMENTED, "GSAEFTCreditTenderActionImpl(): ERROR: Tendering credit with MSR track data is not implemented."); tempAEFMessage.setMessage("AN AEF Exception was thrown in GSAEFTCreditTenderActionImpl.GSAEFTCreditTenderActionImpl()."); log.error(tempAEFMessage, tempException); throw tempException; } creditID = (CreditIdentifier) tenderID; setAmount(); setAccountNumber(); setExpirationDate(); setCardType(); setTenderFuntionCode(); if (log.isTraceEnabled()) { tempAEFMessage.setMessage("-Exit GSAEFTCreditTenderActionImpl.GSAEFTCreditTenderActionImpl()."); log.trace(tempAEFMessage); } } /** * Perform the action represented by the ActionRequest and return an ActionResult. * * * @return Object The Tender instance. * @exception com.ibm.retail.AEF.util.AEFException * Among the possible AEFException error codes are: * <br>AEFConst.INVALID_ARGUMENT, AEFConst.INVALID_TENDER_TYPE * <br>AEFConst.APPLICATION_LIMIT_EXCEEDED, AEFConst.ACCOUNT_TENDER_LIMIT * <br>AEFConst.APPLICATION_LIMIT_EXCEEDED, AEFConst.CHANGE_AMOUNT_LIMIT * <br>AEFConst.APPLICATION_LIMIT_EXCEEDED, AEFConst.NUMBER_OF_TENDERS_LIMIT * <br>AEFConst.APPLICATION_LIMIT_EXCEEDED, AEFConst.STAND_IN_COUNT_LIMIT * <br>AEFConst.APPLICATION_LIMIT_EXCEEDED, AEFConst.STAND_IN_AMOUNT_LIMIT * <br>AEFConst.APPLICATION_LIMIT_EXCEEDED, AEFConst.TENDER_FLOOR_LIMIT * <br>AEFConst.APPLICATION_LIMIT_EXCEEDED, AEFConst.CARD_EXPIRED * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.TENDER_NOT_AUTHORIZED * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.RISK_1 * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.RISK_2 * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.RISK_3 * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.RISK_4 * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.VERIFICATION_TIMEOUT * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.CREDIT_NOT_AVAILABLE * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.TOO_LONG_IN_STANDIN * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.PAYMENT_SYSTEM_OFFLINE * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.UNKNOWN_SERVICER * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.SERVICER_CLOSED * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.CARD_TYPE_UNKNOWN * <br>AEFConst.INVALID_MANAGER_OVERRIDE_NUMBER * <br>AEFConst.KEYLOCK_ERROR, AEFConst.MANAGER_KEY_REQUIRED * <br>AEFConst.INVALID_ARGUMENT * <br>AEFConst.PROCEDURE_NOT_ALLOWED, AEFConst.EXTERNAL_TENDER_AUTHORIZATION_SUSPENDED * <br>AEFConst.TENDER_NOT_ACCEPTED, AEFConst.UNKNOWN_SERVICER * <br><a href="../commonerrorcodes.html">Common Errors</a> */ public Object performAction() throws AEFException { if (log.isTraceEnabled()) { tempAEFMessage.setMessage("+Enter GSAEFTCreditTenderActionImpl.performAction()."); log.trace(tempAEFMessage); } super.performAction(); // Call super to perform any common processing and clear any errors before we start. Object retVal = null; Condition[] goodConditions = { new PropertyEqualsCondition(POSDeviceProperties.CATEGORY, POSDeviceProperties.POS_STATE, State.getState("ID")), new PropertyEqualsCondition(POSDeviceProperties.CATEGORY, POSDeviceProperties.POS_STATE, State.getState("ENTER_TENDER_AMOUNT")), new PropertyEqualsCondition(POSDeviceProperties.CATEGORY, POSDeviceProperties.POS_STATE, State.getState("TRANSACTION_SELECT")) }; try { // Create the tender detector. detector = ((DetectorAccess)(SessionContext.getSession())).getTenderDetector(); //Check to see if we need to send the Total Key Sequence String currentState = getCurrentState(); if (currentState.equals(State.getState("ITEMENTRY"))) { sendTotalSequence(); } // Verify that we are in the tendering state. currentState = getCurrentState(); if (currentState.equals(State.getState("ENTER_TENDER_AMOUNT"))) { sendTenderSequence(); Object tmpRetVal = new Integer("1"); int dummyVal = 0; while (tmpRetVal instanceof Integer) { tmpRetVal = waitForTender(); if (tmpRetVal instanceof Integer) { AEFErrorHandler errorHandler = new AEFErrorHandler("Wait for Tender or Error", creditID); errorHandler.handleError(lock, keySequenceAction, GSANormalConditionsImpl.getInstance().getShortConditions(goodConditions), BadConditionsImpl.getInstance().getBadConditions(), getTimeout(), dummyVal); } else { retVal = tmpRetVal; } } } else if (currentState.equals(State.getState("TRANSACTION_SELECT")) || currentState.equals(State.getState("ITEMENTRY"))) { // We had a 0 balance and have gone back to TRANSACTION_SELECT or ITEMENTRY state. throw new AEFException(AEFConst.APPLICATION_NOT_IN_PROPER_STATE, 0, "GSAEFTCreditTenderActionImpl.performAction(): Transaction balance is zero so the application went back to state " + currentState + "."); } else { // Not in valid state for tender entry. throw new AEFException(AEFConst.APPLICATION_NOT_IN_PROPER_STATE, 0, "GSAEFTCreditTenderActionImpl.performAction(): POS application not in proper state to tender credit.\n Application state is " + currentState + "."); } } catch (AEFException e) { tempAEFMessage.setMessage("There was an AEF exception thrown in performAction of GSAEFTCreditTenderActionImpl-."); log.error(tempAEFMessage, e); throw e; } if (log.isTraceEnabled()) { tempAEFMessage.setMessage("-Exit GSAEFTCreditTenderActionImpl.performAction()."); log.trace(tempAEFMessage); } return retVal; } /** * Sends the "total" key sequence. * * @exception com.ibm.retail.AEF.util.AEFException * Among the possible AEFException error codes are: * <br>AEFConst.OPERATION_TIMEOUT * <br>AEFConst.WAIT_INTERRUPTED * <br>AEFConst.CONFIG_ERROR, AEFConst.INVALID_KEY_SEQUENCE * <br><a href="../commonerrorcodes.html">Common Errors</a> */ public void sendTotalSequence() throws AEFException { if (log.isTraceEnabled()) { tempAEFMessage.setMessage("+Enter GSAEFTCreditTenderActionImpl.sendTotalSequence()."); log.trace(tempAEFMessage); } int retVal = -1; int currentAmount = Integer.valueOf(amount).intValue(); args.clear(); args.put("SEQUENCE_ID", "total"); keySequenceAction = (AEFAction)(actionFactory.makeAction(new ActionRequest("SimpleKeySequenceAction", args))); if (currentAmount != 0) { Condition[] goodConditions = { new PropertyEqualsCondition(POSDeviceProperties.CATEGORY, POSDeviceProperties.POS_STATE, State.getState("ENTER_TENDER_AMOUNT")) }; lock = new ConditionLock(); retVal = lock.performActionAndWait("wait-for-trans-total-substate", keySequenceAction, goodConditions, BadConditionsImpl.getInstance().getBadConditions(), getTimeout()); if (retVal < 0) { AEFErrorHandler errorHandler = new AEFErrorHandler("Send Total Sequence Error", creditID); retVal = errorHandler.handleError(lock, keySequenceAction, goodConditions, BadConditionsImpl.getInstance().getBadConditions(), getTimeout(), retVal); } } else { AEFException tempException = new AEFException(AEFConst.TENDER_NOT_ACCEPTED, AEFConst.INVALID_TENDER_AMOUNT, "GSAEFTCreditTenderActionImpl.performAction(): The transaction balance is zero so the tender will not be accepted."); tempAEFMessage.setMessage("AN AEF Exception was thrown in GSAEFTCreditTenderActionImpl.sendTotalSequence()."); log.error(tempAEFMessage, tempException); throw tempException; } if (log.isTraceEnabled()) { tempAEFMessage.setMessage("-Exit GSAEFTCreditTenderActionImpl.sendTotalSequence()."); log.trace(tempAEFMessage); } } /** * Waits for a new tender line item to be detected. * * * @return Object The newly detected tender. (Note in this case, it's an ArrayList that * contains the tender object). * @exception com.ibm.retail.AEF.util.AEFException * Among the possible AEFException error codes are: * <br>AEFConst.OPERATION_TIMEOUT * <br>AEFConst.WAIT_INTERRUPTED * <br><a href="../commonerrorcodes.html">Common Errors</a> */ public Object waitForTender() throws AEFException { if (log.isTraceEnabled()) { tempAEFMessage.setMessage("+Enter GSAEFTCreditTenderActionImpl.waitForTender()."); log.trace(tempAEFMessage); } Object retVal = null; ObjectDetectorLock objLock = new ObjectDetectorLock(); retVal = objLock.waitForNewObjectOrError("wait-for-tender", detector, instanceNumber, BadConditionsImpl.getInstance().getBadConditions(), true, getTimeout()); if (log.isTraceEnabled()) { tempAEFMessage.setMessage("-Exit GSAEFTCreditTenderActionImpl.waitForTender()."); log.trace(tempAEFMessage); } return retVal; } /** * Sets the amount of the credit. * * @exception AEFException */ public void setAmount() throws AEFException { if (log.isTraceEnabled()) { tempAEFMessage.setMessage("+Enter GSAEFTCreditTenderActionImpl.setAmount()."); log.trace(tempAEFMessage); } amount = creditID.getAmount(); amount = amount.trim(); // Strip out any non-numeric portion of the amount (currency symbol & decimal separator) amount = AEFUtilities.formatNumericOnly(amount); if (log.isTraceEnabled()) { tempAEFMessage.setMessage("-Exit GSAEFTCreditTenderActionImpl.setAmount()."); log.trace(tempAEFMessage); } } /** * Sets the account number of the credit card. * * @exception AEFException * Among the possible AEFException error codes are: * <br>AEFConst.INVALID_ARGUMENT, AEFConst.ACCOUNT_NUMBER_REQUIRED */ public void setAccountNumber() throws AEFException { if (log.isTraceEnabled()) { tempAEFMessage.setMessage("+Enter GSAEFTCreditTenderActionImpl.setAccountNumber()."); log.trace(tempAEFMessage); } accountNumber = creditID.getAccountNumber(); if (accountNumber == null) { AEFException tempException = new AEFException(AEFConst.INVALID_ARGUMENT, AEFConst.ACCOUNT_NUMBER_REQUIRED, "GSAEFTCreditTenderActionImpl.setAccountNumber(): Account number argument is required."); tempAEFMessage.setMessage("AN AEF Exception was thrown in GSAEFTCreditTenderActionImpl.setAccountNumber()."); log.error(tempAEFMessage, tempException); throw tempException; } if (log.isTraceEnabled()) { tempAEFMessage.setMessage("-Exit GSAEFTCreditTenderActionImpl.setAccountNumber()."); log.trace(tempAEFMessage); } } /** * Sets the expiration date of the credit card. * * @exception AEFException * Among the possible AEFException error codes are: * <br>AEFConst.INVALID_ARGUMENT, AEFConst.EXPIRATION_DATE_REQUIRED * <br>AEFConst.INVALID_ARGUMENT, AEFConst.INVALID_EXPIRY_DATE */ public void setExpirationDate() throws AEFException { if (log.isTraceEnabled()) { tempAEFMessage.setMessage("+Enter GSAEFTCreditTenderActionImpl.setExpirationDate()."); log.trace(tempAEFMessage); } expDate = creditID.getExpirationDate(); if (expDate == null) { AEFException tempException = new AEFException(AEFConst.INVALID_ARGUMENT, AEFConst.EXPIRATION_DATE_REQUIRED, "GSAEFTCreditTenderActionImpl.setExpirationDate(): Expiration date argument is required."); tempAEFMessage.setMessage("AN AEF Exception was thrown in GSAEFTCreditTenderActionImpl.setExpirationDate()."); log.error(tempAEFMessage, tempException); throw tempException; } try { FormatContext.getExpiryDateInstance().parse(expDate); } catch (ParseException pe) { AEFException tempException = new AEFException(AEFConst.INVALID_ARGUMENT, AEFConst.INVALID_EXPIRY_DATE, "GSAEFTCreditTenderActionImpl.setExpirationDate(): Invalid expiry date: " + expDate); tempAEFMessage.setMessage("AN AEF Exception was thrown in GSAEFTCreditTenderActionImpl.setExpirationDate()."); log.error(tempAEFMessage, tempException); throw tempException; } if (log.isTraceEnabled()) { tempAEFMessage.setMessage("-Exit GSAEFTCreditTenderActionImpl.setExpirationDate()."); log.trace(tempAEFMessage); } } /** * Sets the card type of the credit card. * * @exception AEFException * Among the possible AEFException error codes are: * <br>AEFConst.INVALID_ARGUMENT, AEFConst.CREDIT_CARD_TYPE_REQUIRED */ public void setCardType() throws AEFException { if (log.isTraceEnabled()) { tempAEFMessage.setMessage("+Enter GSAEFTCreditTenderActionImpl.setCardType()."); log.trace(tempAEFMessage); } cardType = creditID.getCardType(); if (cardType == null) { AEFException tempException = new AEFException(AEFConst.INVALID_ARGUMENT, AEFConst.CREDIT_CARD_TYPE_REQUIRED, "GSAEFTCreditTenderActionImpl.setCardType(): Card type argument is required."); tempAEFMessage.setMessage("AN AEF Exception was thrown in GSAEFTCreditTenderActionImpl.setCardType()."); log.error(tempAEFMessage, tempException); throw tempException; } if (log.isTraceEnabled()) { tempAEFMessage.setMessage("-Exit GSAEFTCreditTenderActionImpl.setCardType()."); log.trace(tempAEFMessage); } } /** * Sets the tender fuction code for this credit card. * * @exception AEFException * Among the possible AEFException error codes are: * <br>AEFConst.CONFIG_ERROR */ public void setTenderFuntionCode() throws AEFException { if (log.isTraceEnabled()) { tempAEFMessage.setMessage("+Enter GSAEFTCreditTenderActionImpl.setTenderFuntionCode()."); log.trace(tempAEFMessage); } TenderMapper tempMapper = TenderMapperImpl.getInstance(); String tenderKey = tempMapper.getKey(cardType); tenderKey = tenderKey.toUpperCase(); tenderFuntionCode = FunctionCode.getFunctionCode(tenderKey); if (tenderFuntionCode == null) { AEFException tempException = new AEFException(AEFConst.CONFIG_ERROR, 0, "No function code defined in " + AEFConst.FCODE_BUNDLE + " properties chain for " + tenderFuntionCode + "."); tempAEFMessage.setMessage("AN AEF Exception was thrown in GSAEFTCreditTenderActionImpl.setTenderFuntionCode()."); log.error(tempAEFMessage, tempException); throw tempException; } else { tenderFuntionCode = "<" + tenderFuntionCode + ">"; } if (log.isTraceEnabled()) { tempAEFMessage.setMessage("-Exit GSAEFTCreditTenderActionImpl.setTenderFuntionCode()."); log.trace(tempAEFMessage); } } /** * Sends the key sequence for the credit tender amount. * * @exception com.ibm.retail.AEF.util.AEFException * Among the possible AEFException error codes are: * <br>AEFConst.OPERATION_TIMEOUT * <br>AEFConst.WAIT_INTERRUPTED * <br><a href="../commonerrorcodes.html">Common Errors</a> */ public void sendTenderSequence() throws AEFException { if (log.isTraceEnabled()) { tempAEFMessage.setMessage("+Enter GSAEFTCreditTenderActionImpl.sendTenderSequence()."); log.trace(tempAEFMessage); } int retVal = -1; lock = new ConditionLock(); args.clear(); args.put("%0", amount); args.put("%1", tenderFuntionCode); args.put("SEQUENCE_ID", "tender"); keySequenceAction = (AEFAction)(actionFactory.makeAction(new ActionRequest("SimpleKeySequenceAction", args))); Condition[] goodConditions = { new PropertyEqualsCondition(POSDeviceProperties.CATEGORY, POSDeviceProperties.POS_STATE, State.getState("ID")), new PropertyEqualsCondition(POSDeviceProperties.CATEGORY, POSDeviceProperties.POS_STATE, State.getState("ENTER_TENDER_AMOUNT")), new PropertyEqualsCondition(POSDeviceProperties.CATEGORY, POSDeviceProperties.POS_STATE, State.getState("TRANSACTION_SELECT")) }; // Save any current item instance number so we are sure to only get an item created after this key sequence is sent. instanceNumber = detector.getInstanceNumber(); // Report performance data. if (perfTrace.isEnabled(AEFPerfTrace.COARSE)) { perfTrace.reportTimer(AEFPerfTrace.COARSE, sessionID, "addTender", ">>>Sending credit tender sequence to application."); } // Send the key sequence to GSA. retVal = lock.performActionAndWait("wait-after-send-tender", keySequenceAction, GSANormalConditionsImpl.getInstance().getShortConditions(goodConditions), BadConditionsImpl.getInstance().getBadConditions(), getTimeout()); if (retVal < 0) { AEFErrorHandler errorHandler = new AEFErrorHandler("Send Credit Tender Sequence Error", creditID); retVal = errorHandler.handleError(lock, keySequenceAction, GSANormalConditionsImpl.getInstance().getShortConditions(goodConditions), BadConditionsImpl.getInstance().getBadConditions(), getTimeout(), retVal); } if (log.isTraceEnabled()) { tempAEFMessage.setMessage("-Exit GSAEFTCreditTenderActionImpl.sendTenderSequence()."); log.trace(tempAEFMessage); } } /* Instance and Class Variables */ protected AEFAction keySequenceAction = null; protected CreditIdentifier creditID = null; protected ConditionLock lock = null; protected ObjectDetector detector = null; protected int instanceNumber = 0; protected String amount = null; protected String accountNumber = null; protected String expDate = null; protected String cardType = null; protected String tenderFuntionCode = null; private static Log log = LogFactory.getLog(GSAEFTCreditTenderActionImpl.class); private static AEFPerfTrace perfTrace = AEFPerfTrace.getInstance(); }