/**
  * Add a hit to this IP's history for a toxic card.  This is designed to be
  * called outside of the usual filter callbacks so we record nasty attempts
  * even when the filters aren't called.
  * @param GatewayType $gateway adapter instance with user_ip set
  */
 public static function penalize(GatewayType $gateway)
 {
     $logger = DonationLoggerFactory::getLogger($gateway);
     $logger->info('IPVelocityFilter penalizing IP address ' . $gateway->getData_Unstaged_Escaped('user_ip') . ' for toxic card attempt.');
     $velocity = Gateway_Extras_CustomFilters_IP_Velocity::singleton($gateway, Gateway_Extras_CustomFilters::singleton($gateway));
     if ($velocity->connectToMemcache()) {
         $velocity->addNowToMemcachedValue(null, false, true);
     }
 }
 /**
  * Process the response and set transaction_response properties
  *
  * @param DOMDocument $response Cleaned-up XML from the GlobalCollect API
  *
  * @throws ResponseProcessingException with code and potentially retry vars.
  */
 public function processResponse($response)
 {
     $this->transaction_response->setCommunicationStatus($this->parseResponseCommunicationStatus($response));
     $errors = $this->parseResponseErrors($response);
     $this->transaction_response->setErrors($errors);
     $data = $this->parseResponseData($response);
     $this->transaction_response->setData($data);
     //set the transaction result message
     $responseStatus = isset($data['STATUSID']) ? $data['STATUSID'] : '';
     $this->transaction_response->setTxnMessage("Response Status: " . $responseStatus);
     //TODO: Translate for GC.
     $this->transaction_response->setGatewayTransactionId($this->getData_Unstaged_Escaped('order_id'));
     $retErrCode = null;
     $retErrMsg = '';
     $retryVars = array();
     // We are also curious to know if there were any recoverable errors
     foreach ($errors as $errCode => $errObj) {
         $errMsg = $errObj['message'];
         $messageFromProcessor = $errObj['debugInfo'];
         $retryOrderId = false;
         switch ($errCode) {
             case 400120:
                 // INSERTATTEMPT PAYMENT FOR ORDER ALREADY FINAL FOR COMBINATION.
                 $transaction = $this->getCurrentTransaction();
                 if ($transaction !== 'INSERT_ORDERWITHPAYMENT') {
                     // Don't regenerate order ID if it's too late, just steam
                     // right through and let regular error handling deal
                     // with it.
                     $this->logger->error('Order ID already processed, remain calm.');
                     $retErrCode = $errCode;
                     $retErrMsg = $errMsg;
                     break;
                 }
                 $this->logger->error('InsertAttempt on a finalized order! Starting again.');
                 $retryOrderId = true;
                 break;
             case 400490:
                 // INSERTATTEMPT_MAX_NR_OF_ATTEMPTS_REACHED
                 $this->logger->error('InsertAttempt - max attempts reached! Starting again.');
                 $retryOrderId = true;
                 break;
             case 300620:
                 // Oh no! We've already used this order # somewhere else! Restart!
                 $this->logger->error('Order ID collision! Starting again.');
                 $retryOrderId = true;
                 break;
             case 430260:
                 // wow: If we were a point of sale, we'd be calling security.
             // wow: If we were a point of sale, we'd be calling security.
             case 430349:
                 // TRANSACTION_CANNOT_BE_COMPLETED_VIOLATION_OF_LAW (EXTERMINATE!)
             // TRANSACTION_CANNOT_BE_COMPLETED_VIOLATION_OF_LAW (EXTERMINATE!)
             case 430357:
                 // lost or stolen card
             // lost or stolen card
             case 430410:
                 // CHALLENGED (GC docs say fraud)
             // CHALLENGED (GC docs say fraud)
             case 430415:
                 // Security violation
             // Security violation
             case 430418:
                 // Stolen card
             // Stolen card
             case 430421:
                 // Suspected fraud
             // Suspected fraud
             case 430697:
                 // Suspected fraud
             // Suspected fraud
             case 485020:
                 // DO_NOT_TRY_AGAIN (or else EXTERMINATE!)
             // DO_NOT_TRY_AGAIN (or else EXTERMINATE!)
             case 4360022:
                 // ECARD_FRAUD
             // ECARD_FRAUD
             case 4360023:
                 // ECARD_ONLINE_FRAUD
                 // These naughty codes get all the cancel treatment below, plus some extra
                 // IP velocity spanking.
                 if ($this->getGlobal('EnableIPVelocityFilter')) {
                     Gateway_Extras_CustomFilters_IP_Velocity::penalize($this);
                 }
             case 430306:
                 // Expired card.
             // Expired card.
             case 430330:
                 // invalid card number
             // invalid card number
             case 430354:
                 // issuer unknown
                 // All of these should stop us from retrying at all
                 // Null out the retry vars and throw error immediately
                 $retryVars = null;
                 $this->logger->info("Got error code {$errCode}, not retrying to avoid MasterCard fines.");
                 // TODO: move forceCancel - maybe to the exception?
                 $this->transaction_response->setForceCancel(true);
                 $this->transaction_response->setErrors(array('internal-0003' => array('message' => $this->getErrorMapByCodeAndTranslate('internal-0003'))));
                 throw new ResponseProcessingException("Got error code {$errCode}, not retrying to avoid MasterCard fines.", $errCode);
             case 430285:
                 //most common declined cc code.
             //most common declined cc code.
             case 430396:
                 //not authorized to cardholder, whatever that means.
             //not authorized to cardholder, whatever that means.
             case 430409:
                 //Declined, because "referred". We're not going to call the bank to push it through.
             //Declined, because "referred". We're not going to call the bank to push it through.
             case 430424:
                 //Declined, because "SYSTEM_MALFUNCTION". I have no words.
             //Declined, because "SYSTEM_MALFUNCTION". I have no words.
             case 430692:
                 //cvv2 declined
                 break;
                 //don't need to hear about these at all.
             //don't need to hear about these at all.
             case 20001000:
                 //REQUEST {0} NULL VALUE NOT ALLOWED FOR {1} : Validation pain. Need more.
                 //look in the message for more clues.
                 //Yes: That's an 8-digit error code that buckets a silly number of validation issues, some of which are legitimately ours.
                 //The only way to tell is to search the English message.
                 //@TODO: Refactor all 3rd party error handling for GC. This whole switch should definitely be in parseResponseErrors; It is very silly that this is here at all.
                 $not_errors = array('/NULL VALUE NOT ALLOWED FOR EXPIRYDATE/', '/DID NOT PASS THE LUHNCHECK/');
                 foreach ($not_errors as $regex) {
                     if (preg_match($regex, $errObj['debugInfo'])) {
                         //not a system error, but definitely the end of the payment attempt. Log it to info and leave.
                         $this->logger->info(__FUNCTION__ . ": {$errObj['debugInfo']}");
                         throw new ResponseProcessingException($errMsg, $errCode);
                     }
                 }
             case 21000050:
                 //REQUEST {0} VALUE {2} OF FIELD {1} IS NOT A NUMBER WITH MINLENGTH {3}, MAXLENGTH {4} AND PRECISION {5}  : More validation pain.
                 //say something painful here.
                 $errMsg = 'Blocking validation problems with this payment. Investigation required! ' . "Original error: '{$messageFromProcessor}'.  Our data: " . $this->getLogDebugJSON();
             default:
                 $this->logger->error(__FUNCTION__ . " Error {$errCode} : {$errMsg}");
                 break;
         }
         if ($retryOrderId) {
             $retryVars[] = 'order_id';
             $retErrCode = $errCode;
             $retErrMsg = $errMsg;
         }
     }
     if ($retErrCode) {
         throw new ResponseProcessingException($retErrMsg, $retErrCode, $retryVars);
     }
     // Unstage any data that we've whitelisted.
     // TODO: This should be generalized into the base class.
     $whitelisted_keys = $this->transaction_option('response');
     if ($whitelisted_keys) {
         $filtered_data = array_intersect_key($data, array_flip($whitelisted_keys));
         $unstaged = $this->unstageKeys($filtered_data);
         $this->addResponseData($unstaged);
     }
 }
 /**
  * Runs all the post-process logic that has been enabled and configured in
  * donationdata.php and/or LocalSettings.php, including the ActiveMQ/Stomp
  * queue message.
  * This function is most likely to be called through
  * executeFunctionIfExists, later on in do_transaction.
  */
 protected function postProcessDonation()
 {
     Gateway_Extras_CustomFilters_IP_Velocity::onPostProcess($this);
     Gateway_Extras_ConversionLog::onPostProcess($this);
     try {
         $this->doStompTransaction();
     } catch (Exception $ex) {
         $this->logger->alert("Failure queueing final status message: {$ex->getMessage()}");
     }
 }
 protected function runFilters($phase)
 {
     switch ($phase) {
         case self::PHASE_INITIAL:
             Gateway_Extras_CustomFilters_Referrer::onInitialFilter($this->gateway_adapter, $this);
             Gateway_Extras_CustomFilters_Source::onInitialFilter($this->gateway_adapter, $this);
             Gateway_Extras_CustomFilters_Functions::onInitialFilter($this->gateway_adapter, $this);
             Gateway_Extras_CustomFilters_IP_Velocity::onInitialFilter($this->gateway_adapter, $this);
             break;
         case self::PHASE_VALIDATE:
             Gateway_Extras_CustomFilters_Functions::onFilter($this->gateway_adapter, $this);
             Gateway_Extras_CustomFilters_MinFraud::onFilter($this->gateway_adapter, $this);
             Gateway_Extras_CustomFilters_IP_Velocity::onFilter($this->gateway_adapter, $this);
             break;
     }
 }