/** * 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; } }