/** * @param string $filterListGlobal Run filters listed in a DonationInterface * global variable with name * @return bool */ protected function filter($filterListGlobal) { $functions = $this->gateway_adapter->getGlobal($filterListGlobal); if (!$this->gateway_adapter->getGlobal('EnableFunctionsFilter') || !count($functions)) { return true; } foreach ($functions as $function_name => $risk_score_modifier) { //run the function specified, if it exists. if (method_exists($this->gateway_adapter, $function_name)) { $score = $this->gateway_adapter->{$function_name}(); if (is_null($score)) { $score = 0; //TODO: Is this the correct behavior? } elseif (is_bool($score)) { $score = $score ? 0 : $risk_score_modifier; } elseif (is_numeric($score) && $score <= 100) { $score = $score * $risk_score_modifier / 100; } else { // error_log("Function Filter: $function_name returned $score"); throw new UnexpectedValueException("Filter functions are returning somekinda nonsense."); } $this->cfo->addRiskScore($score, $function_name); } } return TRUE; }
static function singleton(&$gateway_adapter) { if (!self::$instance || $gateway_adapter->isBatchProcessor()) { self::$instance = new self($gateway_adapter); } return self::$instance; }
protected function filter() { // pull out the source from the filter object $source = $this->gateway_adapter->getData_Unstaged_Escaped('utm_source'); // a very complex filtering algorithm for sources $srcRules = $this->gateway_adapter->getGlobal('CustomFiltersSrcRules'); foreach ($srcRules as $regex => $risk_score_modifier) { /** * Note that regex pattern does not include delimiters. * These will need to be included in your custom regex patterns. */ if (preg_match("{$regex}", $source)) { $this->cfo->addRiskScore($risk_score_modifier, 'source'); // log it $log_msg = "\"" . addslashes($source) . "\""; $log_msg .= "\t\"" . addslashes($regex) . "\""; $log_msg .= "\t\"" . $this->cfo->getRiskScore() . "\""; $this->log($this->gateway_adapter->getData_Unstaged_Escaped('contribution_tracking_id'), 'Filter: Source', $log_msg); } } return TRUE; }
protected function filter() { // pull out the referrer from the gateway_adapter $referrer = $this->gateway_adapter->getData_Unstaged_Escaped('referrer'); // a very complex filtering algorithm for referrers $refRules = $this->gateway_adapter->getGlobal('CustomFiltersRefRules'); foreach ($refRules as $regex => $risk_score_modifier) { /** * note that the regex pattern does NOT include delimiters. * these will need to be included in your custom regex patterns. */ if (preg_match("{$regex}", $referrer)) { $this->cfo->addRiskScore($risk_score_modifier, 'referrer'); // log it //TODO: This sucks. $log_msg = "\"" . addslashes($referrer) . "\""; $log_msg .= "\t\"" . addslashes($regex) . "\""; $log_msg .= "\t\"" . $this->cfo->getRiskScore() . "\""; $this->log($this->gateway_adapter->getData_Unstaged_Escaped('contribution_tracking_id'), 'Filter: Referrer', $log_msg); } } return TRUE; }
/** * Execute the minFraud filter * * @return bool true */ protected function filter() { // see if we can bypass minfraud if ($this->can_bypass_minfraud()) { return TRUE; } $minfraud_query = $this->build_query($this->gateway_adapter->getData_Unstaged_Escaped()); $this->query_minfraud($minfraud_query); // Write the query/response to the log before we go mad. $this->log_query(); $this->health_check(); try { if (!isset($this->minfraudResponse['riskScore'])) { throw new RuntimeException("No response at all from minfraud."); } $this->cfo->addRiskScore($this->minfraudResponse['riskScore'], 'minfraud_filter'); } catch (Exception $ex) { //log out the whole response to the error log so we can tell what the heck happened... and fail closed. $log_message = 'Minfraud filter came back with some garbage. Assigning all the points.'; $this->fraud_logger->error('"addRiskScore" ' . $log_message); $this->cfo->addRiskScore(100, 'minfraud_filter'); } return TRUE; }
/** * 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); } }
/** * For Adyen, we only call this on the donor's return to the ResultSwitcher * @param array $response GET/POST params from request * @throws ResponseProcessingException */ public function processResponse($response) { // Always called outside do_transaction, so just make a new response object $this->transaction_response = new PaymentTransactionResponse(); if (empty($response)) { $this->logger->info("No response from gateway"); throw new ResponseProcessingException('No response from gateway', ResponseCodes::NO_RESPONSE); } $this->logger->info("Processing user return data: " . print_r($response, TRUE)); if (!$this->checkResponseSignature($response)) { $this->logger->info("Bad signature in response"); throw new ResponseProcessingException('Bad signature in response', ResponseCodes::BAD_SIGNATURE); } $this->logger->debug('Good signature'); // Overwrite the order ID we have with the return data, in case the // donor opened a second window. $orderId = $response['merchantReference']; $this->addRequestData(array('order_id' => $orderId)); $gateway_txn_id = isset($response['pspReference']) ? $response['pspReference'] : ''; $this->transaction_response->setGatewayTransactionId($gateway_txn_id); $result_code = isset($response['authResult']) ? $response['authResult'] : ''; if ($result_code == 'PENDING' || $result_code == 'AUTHORISED') { // Both of these are listed as pending because we have to submit a capture // request on 'AUTHORIZATION' ipn message receipt. // We should still have risk scores in the session from before we // showed the iframe. What did we decide then? Show a fail page if // the donation was fishy enough that our listener isn't going to // auto-capture it, so as not to tell carders the auth worked. // FIXME: need to keep action ranges in sync between DI and listener. $action = Gateway_Extras_CustomFilters::determineStoredAction($this); if ($action === 'process') { $this->logger->info("User came back as pending or authorised, placing in payments-init queue"); $this->finalizeInternalStatus(FinalStatus::PENDING); } else { $this->logger->info("User came back authorized but with action {$action}. " . "Showing a fail page, but leaving details in case of manual capture."); $this->finalizeInternalStatus(FinalStatus::FAILED); } } else { $this->finalizeInternalStatus(FinalStatus::FAILED); $this->logger->info("Negative response from gateway. Full response: " . print_r($response, TRUE)); } $this->postProcessDonation(); }
/** * Runs all the fraud filters that have been enabled and configured in * donationdata.php and/or LocalSettings.php * This function is most likely to be called through * executeFunctionIfExists, early on in do_transaction. */ function runAntifraudFilters() { //extra layer of Stop Doing This. $errors = $this->getTransactionErrors(); if (!empty($errors)) { $this->logger->info('Skipping antifraud filters: Transaction is already in error'); return; } // allow any external validators to have their way with the data $this->logger->info('Preparing to run custom filters'); Gateway_Extras_CustomFilters::onValidate($this); $this->logger->info('Finished running custom filters'); }