public function validate(GatewayType $adapter, $normalized, &$errors)
 {
     if (!isset($normalized['amount']) || !isset($normalized['currency_code'])) {
         // Not enough info to validate
         return;
     }
     if (isset($errors['currency_code'])) {
         // Already displaying an error
         return;
     }
     $value = $normalized['amount'];
     if (self::isZeroIsh($value)) {
         $errors['amount'] = DataValidator::getErrorMessage('amount', 'not_empty', $normalized['language']);
         return;
     }
     $currency = $normalized['currency_code'];
     $min = self::convert($adapter->getGlobal('PriceFloor'), $currency);
     $max = self::convert($adapter->getGlobal('PriceCeiling'), $currency);
     if (!is_numeric($value) || $value < 0) {
         $errors['amount'] = WmfFramework::formatMessage('donate_interface-error-msg-invalid-amount');
     } else {
         if ($value > $max) {
             // FIXME: should format the currency values in this message
             $errors['amount'] = WmfFramework::formatMessage('donate_interface-bigamount-error', $max, $currency, $adapter->getGlobal('MajorGiftsEmail'));
         } else {
             if ($value < $min) {
                 $locale = $normalized['language'] . '_' . $normalized['country'];
                 $formattedMin = self::format($min, $currency, $locale);
                 $errors['amount'] = WmfFramework::formatMessage('donate_interface-smallamount-error', $formattedMin);
             }
         }
     }
 }
 function defineTransactions()
 {
     $this->transactions = array();
     $this->transactions['Donate'] = array('request' => array('amount', 'currency_code', 'country', 'business', 'cancel_return', 'cmd', 'item_name', 'item_number', 'no_note', 'return', 'custom', 'lc'), 'values' => array('business' => $this->account_config['AccountEmail'], 'cancel_return' => ResultPages::getCancelPage($this), 'cmd' => '_donations', 'item_number' => 'DONATE', 'item_name' => WmfFramework::formatMessage('donate_interface-donation-description'), 'no_note' => 0, 'return' => ResultPages::getThankYouPage($this)));
     $this->transactions['DonateXclick'] = array('request' => array('cmd', 'item_number', 'item_name', 'cancel_return', 'no_note', 'return', 'business', 'no_shipping', 'amount', 'currency_code', 'country', 'custom'), 'values' => array('item_number' => 'DONATE', 'item_name' => WmfFramework::formatMessage('donate_interface-donation-description'), 'cancel_return' => ResultPages::getCancelPage($this), 'no_note' => '1', 'return' => ResultPages::getThankYouPage($this), 'business' => $this->account_config['AccountEmail'], 'cmd' => '_xclick', 'no_shipping' => '1'));
     $this->transactions['DonateRecurring'] = array('request' => array('a3', 'currency_code', 'country', 'business', 'cancel_return', 'cmd', 'item_name', 'item_number', 'no_note', 'return', 'custom', 't3', 'p3', 'src', 'srt', 'lc'), 'values' => array('business' => $this->account_config['AccountEmail'], 'cancel_return' => ResultPages::getCancelPage($this), 'cmd' => '_xclick-subscriptions', 'item_number' => 'DONATE', 'item_name' => WmfFramework::formatMessage('donate_interface-donation-description'), 'no_note' => 0, 'return' => ResultPages::getThankYouPage($this), 't3' => 'M', 'p3' => '1', 'src' => '1', 'srt' => $this->getGlobal('RecurringLength')));
 }
 /**
  * @param bool $rapidFail if true, render a form as a fail page rather than redirect
  * @param string $failPage either a wiki page title, or a URL to an external wiki
  *                         page title.
  * @param array $data information about the current request.
  *                    language, gateway, payment_method, and payment_submethod must be set
  * @param Psr\Log\LoggerInterface $logger
  * @return string full URL of the fail page, or just form name in case of rapidFail
  */
 private static function getFailPageFromParams($rapidFail, $failPage, $data, LoggerInterface $logger)
 {
     if (isset($data['language'])) {
         $language = $data['language'];
     } else {
         $language = WmfFramework::getLanguageCode();
     }
     // Prefer RapidFail.
     if ($rapidFail) {
         // choose which fail page to go for.
         try {
             $fail_ffname = GatewayFormChooser::getBestErrorForm($data['gateway'], $data['payment_method'], $data['payment_submethod']);
             return $fail_ffname;
         } catch (Exception $e) {
             $logger->error('Cannot determine best error form. ' . $e->getMessage());
         }
     }
     if (filter_var($failPage, FILTER_VALIDATE_URL)) {
         return self::appendLanguageAndMakeURL($failPage, $language);
     }
     // FIXME: either add Special:FailPage to avoid depending on wiki content,
     // or update the content on payments to be consistent with the /lang
     // format of ThankYou pages so we can use appendLanguageAndMakeURL here.
     $failTitle = Title::newFromText($failPage);
     $url = wfAppendQuery($failTitle->getFullURL(), array('uselang' => $language));
     return $url;
 }
 public function setUp()
 {
     parent::setUp();
     $this->queue_name = 'test-' . mt_rand();
     $this->setMwGlobals(array('wgDonationInterfaceEnableQueue' => true, 'wgDonationInterfaceDefaultQueueServer' => array('type' => 'TestingQueue'), 'wgDonationInterfaceQueues' => array($this->queue_name => array())));
     $this->transaction = array('amount' => '1.24', 'city' => 'Dunburger', 'contribution_tracking_id' => mt_rand(), 'correlation-id' => 'testgateway-' . mt_rand(), 'country' => 'US', 'currency_code' => 'USD', 'date' => time(), 'email' => '*****@*****.**', 'fname' => 'Jen', 'gateway_account' => 'default', 'gateway' => 'testgateway', 'gateway_txn_id' => mt_rand(), 'order_id' => mt_rand(), 'language' => 'en', 'lname' => 'Russ', 'payment_method' => 'cc', 'payment_submethod' => 'visa', 'php-message-class' => 'SmashPig\\CrmLink\\Messages\\DonationInterfaceMessage', 'response' => 'Gateway response something', 'state' => 'AK', 'street' => '1 Fake St.', 'user_ip' => '127.0.0.1', 'utm_source' => 'testing', 'zip' => '12345');
     $this->expected_message = array('contribution_tracking_id' => $this->transaction['contribution_tracking_id'], 'utm_source' => 'testing', 'language' => 'en', 'email' => '*****@*****.**', 'first_name' => 'Jen', 'last_name' => 'Russ', 'street_address' => '1 Fake St.', 'city' => 'Dunburger', 'state_province' => 'AK', 'country' => 'US', 'postal_code' => '12345', 'gateway' => 'testgateway', 'gateway_account' => 'default', 'gateway_txn_id' => $this->transaction['gateway_txn_id'], 'order_id' => $this->transaction['order_id'], 'payment_method' => 'cc', 'payment_submethod' => 'visa', 'response' => 'Gateway response something', 'currency' => 'USD', 'fee' => '0', 'gross' => '1.24', 'user_ip' => '127.0.0.1', 'date' => (int) $this->transaction['date'], 'source_host' => WmfFramework::getHostname(), 'source_name' => 'DonationInterface', 'source_run_id' => getmypid(), 'source_type' => 'payments', 'source_version' => DonationQueue::getVersionStamp());
 }
 public static function getByCountry($country)
 {
     if (isset(self::$list[$country])) {
         $divisions = self::$list[$country];
         // Localize subdivisions where possible
         if (isset(self::$keyBase[$country])) {
             foreach ($divisions as $abbr => $name) {
                 $key = self::$keyBase[$country] . $abbr;
                 if (WmfFramework::messageExists($key)) {
                     $divisions[$abbr] = WmfFramework::formatMessage($key);
                 }
             }
         }
         return $divisions;
     }
     return false;
 }
 /**
  * Queue a message with the banner history ID sent on the URL, the
  * contribution tracking ID from DonationData, and some additional data.
  */
 protected function queueAssociationOfIds()
 {
     $this->logger->debug('BannerHistoryLogIdProcessor::queueAssociationOfIds(): will ' . 'push to banner-history queue if required info is available.');
     $bannerHistoryId = WmfFramework::getRequestValue(self::BANNER_HISTORY_LOG_ID_PARAM, null);
     // Campaigns may not have banner history enabled. For now, at least,
     // bow out silently if no banner history ID was sent.
     if (!$bannerHistoryId) {
         return;
     }
     $contributionTrackingId = $this->gatewayAdapter->getData_Unstaged_Escaped('contribution_tracking_id');
     if (!$contributionTrackingId) {
         $this->logger->info('No contribution tracking ID for ' . 'banner-history queue ' . $bannerHistoryId . '.');
         return;
     }
     $data = array('freeform' => true, 'banner_history_id' => $bannerHistoryId, 'contribution_tracking_id' => $contributionTrackingId);
     $this->logger->info('Pushing to banner-history queue.');
     DonationQueue::instance()->push($data, 'banner-history');
 }
 public function stage(GatewayType $adapter, $normalized, &$stagedData)
 {
     if (!isset($normalized['language'])) {
         return;
     }
     $language = $normalized['language'];
     $adapterLanguages = $adapter->getAvailableLanguages();
     if (!in_array($language, $adapterLanguages)) {
         $fallbacks = WmfFramework::getLanguageFallbacks($language);
         foreach ($fallbacks as $fallback) {
             if (in_array($fallback, $adapterLanguages)) {
                 $language = $fallback;
                 break;
             }
         }
     }
     if (!in_array($language, $adapterLanguages)) {
         $language = 'en';
     }
     $stagedData['language'] = $language;
 }
 /**
  * Returns a valid mediawiki language code to use for all the DonationInterface translations.
  *
  * Will only look at the currently configured language if the 'language' key
  * doesn't exist in the data set: Users may not have a language preference
  * set if we're bouncing between mediawiki instances for payments.
  * @param array $data A normalized DonationInterface data set.
  * @return string A valid mediawiki language code.
  */
 public static function guessLanguage($data)
 {
     if (array_key_exists('language', $data) && WmfFramework::isValidBuiltInLanguageCode($data['language'])) {
         return $data['language'];
     } else {
         return WmfFramework::getLanguageCode();
     }
 }
 protected function write(array $record)
 {
     WmfFramework::debugLog($this->identifier, $record['message'], $record['level_name']);
 }
 /**
  * This function limits the possible characters passed as template keys and
  * values to letters, numbers, hyphens and underscores. The function also
  * performs standard escaping of the passed values.
  *
  * @param string $string The unsafe string to escape and check for invalid characters
  * @return string Sanitized version of input
  */
 public static function makeSafe($string)
 {
     $stripped = preg_replace('/[^-_\\w]/', '', $string);
     // theoretically this is overkill, but better safe than sorry
     return WmfFramework::sanitize(htmlspecialchars($stripped));
 }
 protected function authorizeOnBillingAgreement()
 {
     $billingAgreementId = $this->getData_Staged('subscr_id');
     $this->logger->info("Authorizing and capturing payment on billing agreement {$billingAgreementId}");
     $authResponse = $this->callPwaClient('authorizeOnBillingAgreement', array('amazon_billing_agreement_id' => $billingAgreementId, 'authorization_amount' => $this->getData_Staged('amount'), 'currency_code' => $this->getData_Staged('currency_code'), 'capture_now' => true, 'authorization_reference_id' => $this->getData_Staged('order_id'), 'seller_order_id' => $this->getData_Staged('order_id'), 'seller_note' => WmfFramework::formatMessage('donate_interface-monthly-donation-description'), 'transaction_timeout' => 0));
     return $authResponse['AuthorizeOnBillingAgreementResult']['AuthorizationDetails'];
 }
 /**
  * Although this function actually does the filtering, as this is a singleton pattern
  * we only want one instance actually using it.
  *
  * @return bool false if we should stop processing
  */
 private function filter()
 {
     $user_ip = $this->gateway_adapter->getData_Unstaged_Escaped('user_ip');
     // Determine IP status before doing anything complex
     $wl = DataValidator::ip_is_listed($user_ip, $this->gateway_adapter->getGlobal('IPWhitelist'));
     $bl = DataValidator::ip_is_listed($user_ip, $this->gateway_adapter->getGlobal('IPBlacklist'));
     if ($wl) {
         $this->gateway_adapter->debugarray[] = "SessionVelocity: IP present in whitelist.";
         return true;
     }
     if ($bl) {
         $this->gateway_adapter->debugarray[] = "SessionVelocity: IP present in blacklist.";
         return false;
     }
     // Open a session if it doesn't already exist
     $this->gateway_adapter->session_ensure();
     // Obtain some useful information
     $gateway = $this->gateway_adapter->getIdentifier();
     $transaction = $this->gateway_adapter->getCurrentTransaction();
     $cRequestTime = $_SERVER['REQUEST_TIME'];
     $decayRate = $this->getVar('DecayRate', $transaction);
     $threshold = $this->getVar('Threshold', $transaction);
     $multiplier = $this->getVar('Multiplier', $transaction);
     // Initialize the filter
     $sessionData = WmfFramework::getSessionValue(self::SESS_ROOT);
     if (!is_array($sessionData)) {
         $sessionData = array();
     }
     if (!array_key_exists($gateway, $sessionData)) {
         $sessionData[$gateway] = array();
     }
     if (!array_key_exists($transaction, $sessionData[$gateway])) {
         $sessionData[$gateway][$transaction] = array($this::SESS_SCORE => 0, $this::SESS_TIME => $cRequestTime, $this::SESS_MULTIPLIER => 1);
     }
     $lastTime = $sessionData[$gateway][$transaction][self::SESS_TIME];
     $score = $sessionData[$gateway][$transaction][self::SESS_SCORE];
     $lastMultiplier = $sessionData[$gateway][$transaction][self::SESS_MULTIPLIER];
     // Update the filter if it's stale
     if ($cRequestTime != $lastTime) {
         $score = max(0, $score - ($cRequestTime - $lastTime) * $decayRate);
         $score += $this->getVar('HitScore', $transaction) * $lastMultiplier;
         $sessionData[$gateway][$transaction][$this::SESS_SCORE] = $score;
         $sessionData[$gateway][$transaction][$this::SESS_TIME] = $cRequestTime;
         $sessionData[$gateway][$transaction][$this::SESS_MULTIPLIER] = $lastMultiplier * $multiplier;
     }
     // Store the results
     WmfFramework::setSessionValue(self::SESS_ROOT, $sessionData);
     // Analyze the filter results
     if ($score >= $threshold) {
         // Ahh!!! Failure!!! Sloooooooow doooowwwwnnnn
         $this->fraud_logger->alert("SessionVelocity: Rejecting request due to score of {$score}");
         $this->sendAntifraudMessage('reject', $score, array('SessionVelocity' => $score));
         $retval = false;
     } else {
         $retval = true;
     }
     $this->fraud_logger->debug("SessionVelocity: ({$gateway}, {$transaction}) Score: {$score}, " . "AllowAction: {$retval}, DecayRate: {$decayRate}, " . "Threshold: {$threshold}, Multiplier: {$lastMultiplier}");
     return $retval;
 }
 function defineTransactions()
 {
     $this->transactions = array();
     // https://developer.paypal.com/docs/classic/api/merchant/SetExpressCheckout_API_Operation_NVP/
     $this->transactions['SetExpressCheckout'] = array('request' => array('USER', 'PWD', 'VERSION', 'METHOD', 'RETURNURL', 'CANCELURL', 'REQCONFIRMSHIPPING', 'NOSHIPPING', 'LOCALECODE', 'EMAIL', 'L_PAYMENTREQUEST_0_AMT0', 'L_PAYMENTREQUEST_0_DESC0', 'PAYMENTREQUEST_0_AMT', 'PAYMENTREQUEST_0_CURRENCYCODE', 'PAYMENTREQUEST_0_CUSTOM', 'PAYMENTREQUEST_0_DESC', 'PAYMENTREQUEST_0_INVNUM', 'PAYMENTREQUEST_0_ITEMAMT', 'PAYMENTREQUEST_0_PAYMENTACTION', 'PAYMENTREQUEST_0_PAYMENTREASON'), 'values' => array('USER' => $this->account_config['User'], 'PWD' => $this->account_config['Password'], 'VERSION' => self::API_VERSION, 'METHOD' => 'SetExpressCheckout', 'CANCELURL' => ResultPages::getCancelPage($this), 'REQCONFIRMSHIPPING' => 0, 'NOSHIPPING' => 1, 'L_PAYMENTREQUEST_0_DESC0' => WmfFramework::formatMessage('donate_interface-donation-description'), 'PAYMENTREQUEST_0_DESC' => WmfFramework::formatMessage('donate_interface-donation-description'), 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale', 'PAYMENTREQUEST_0_PAYMENTREASON' => 'None'), 'response' => array('TOKEN'));
     // https://developer.paypal.com/docs/classic/api/merchant/SetExpressCheckout_API_Operation_NVP/
     $this->transactions['SetExpressCheckout_recurring'] = array('request' => array('USER', 'PWD', 'VERSION', 'METHOD', 'RETURNURL', 'CANCELURL', 'REQCONFIRMSHIPPING', 'NOSHIPPING', 'LOCALECODE', 'EMAIL', 'L_BILLINGTYPE0', 'L_BILLINGAGREEMENTDESCRIPTION0', 'L_BILLINGAGREEMENTCUSTOM0', 'L_PAYMENTREQUEST_0_AMT0', 'L_PAYMENTREQUEST_0_NAME0', 'L_PAYMENTREQUEST_0_QTY0', 'MAXAMT', 'PAYMENTREQUEST_0_AMT', 'PAYMENTREQUEST_0_CURRENCYCODE', 'PAYMENTREQUEST_0_ITEMAMT'), 'values' => array('USER' => $this->account_config['User'], 'PWD' => $this->account_config['Password'], 'VERSION' => self::API_VERSION, 'METHOD' => 'SetExpressCheckout', 'CANCELURL' => ResultPages::getCancelPage($this), 'REQCONFIRMSHIPPING' => 0, 'NOSHIPPING' => 1, 'L_BILLINGTYPE0' => 'RecurringPayments', 'L_BILLINGAGREEMENTDESCRIPTION0' => WmfFramework::formatMessage('donate_interface-monthly-donation-description'), 'L_PAYMENTREQUEST_0_DESC0' => WmfFramework::formatMessage('donate_interface-monthly-donation-description'), 'L_PAYMENTREQUEST_0_NAME0' => WmfFramework::formatMessage('donate_interface-monthly-donation-description'), 'L_PAYMENTREQUEST_0_QTY0' => 1, 'PAYMENTREQUEST_0_DESC' => WmfFramework::formatMessage('donate_interface-monthly-donation-description'), 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale', 'PAYMENTREQUEST_0_PAYMENTREASON' => 'None'), 'response' => array('TOKEN'));
     // Incoming parameters after returning from the PayPal workflow
     $this->transactions['ProcessReturn'] = array('request' => array('token', 'PayerID'));
     // https://developer.paypal.com/docs/classic/api/merchant/GetExpressCheckoutDetails_API_Operation_NVP/
     $this->transactions['GetExpressCheckoutDetails'] = array('request' => array('USER', 'PWD', 'VERSION', 'METHOD', 'TOKEN'), 'values' => array('USER' => $this->account_config['User'], 'PWD' => $this->account_config['Password'], 'VERSION' => self::API_VERSION, 'METHOD' => 'GetExpressCheckoutDetails'), 'response' => array('ACK', 'TOKEN', 'CORRELATIONID', 'TIMESTAMP', 'CUSTOM', 'INVNUM', 'BILLINGAGREEMENTACCEPTEDSTATUS', 'REDIRECTREQUIRED', 'CHECKOUTSTATUS', 'EMAIL', 'PAYERID', 'COUNTRYCODE', 'FIRSTNAME', 'MIDDLENAME', 'LASTNAME', 'SUFFIX', 'PAYMENTREQUEST_0_AMT', 'PAYMENTREQUEST_0_CURRENCYCODE', 'PAYMENTREQUEST_0_INVNUM', 'PAYMENTREQUEST_0_TRANSACTIONID'));
     // https://developer.paypal.com/docs/classic/api/merchant/DoExpressCheckoutPayment_API_Operation_NVP/
     $this->transactions['DoExpressCheckoutPayment'] = array('request' => array('USER', 'PWD', 'VERSION', 'METHOD', 'TOKEN', 'PAYERID', 'PAYMENTREQUEST_0_PAYMENTACTION', 'PAYMENTREQUEST_0_AMT', 'PAYMENTREQUEST_0_CURRENCYCODE', 'PAYMENTREQUEST_0_CUSTOM', 'PAYMENTREQUEST_0_DESC', 'PAYMENTREQUEST_0_INVNUM', 'PAYMENTREQUEST_0_ITEMAMT', 'PAYMENTREQUEST_0_PAYMENTACTION', 'PAYMENTREQUEST_0_PAYMENTREASON'), 'values' => array('USER' => $this->account_config['User'], 'PWD' => $this->account_config['Password'], 'VERSION' => self::API_VERSION, 'METHOD' => 'DoExpressCheckoutPayment', 'PAYMENTREQUEST_0_DESC' => WmfFramework::formatMessage('donate_interface-donation-description'), 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale', 'PAYMENTREQUEST_0_PAYMENTREASON' => 'None'));
     // https://developer.paypal.com/docs/classic/api/merchant/CreateRecurringPaymentsProfile_API_Operation_NVP/
     $this->transactions['CreateRecurringPaymentsProfile'] = array('request' => array('USER', 'PWD', 'VERSION', 'METHOD', 'TOKEN', 'DESC', 'PROFILESTARTDATE', 'PROFILEREFERENCE', 'AUTOBILLOUTAMT', 'BILLINGPERIOD', 'BILLINGFREQUENCY', 'TOTALBILLINGCYCLES', 'MAXFAILEDPAYMENTS', 'AMT', 'CURRENCYCODE', 'EMAIL'), 'values' => array('USER' => $this->account_config['User'], 'PWD' => $this->account_config['Password'], 'VERSION' => self::API_VERSION, 'METHOD' => 'CreateRecurringPaymentsProfile', 'DESC' => WmfFramework::formatMessage('donate_interface-monthly-donation-description'), 'AUTOBILLOUTAMT' => 'NoAutoBill', 'BILLINGPERIOD' => 'Month', 'BILLINGFREQUENCY' => 1, 'TOTALBILLINGCYCLES' => 0, 'MAXFAILEDPAYMENTS' => 3), 'response' => array('PROFILEID', 'PROFILESTATUS', 'TRANSACTIONID'));
     // Add the Signature field to all API calls, if necessary.
     // Note that this gives crappy security, vulnerable to replay attacks.
     // The signature is static, not a checksum of the request.
     if (!$this->isCertificateAuthentication()) {
         foreach ($this->transactions as $_name => &$info) {
             if (isset($info['request'])) {
                 $info['request'][] = 'SIGNATURE';
                 $info['values']['SIGNATURE'] = $this->account_config['Signature'];
             }
         }
     }
 }
 protected function __construct()
 {
     $this->source_fields = array('source_host' => WmfFramework::getHostname(), 'source_name' => 'DonationInterface', 'source_run_id' => getmypid(), 'source_type' => 'payments', 'source_version' => self::getVersionStamp());
 }
 /**
  * Sets communication status and errors for responses to NewInvoice
  * @param array $response
  */
 protected function processNewInvoiceResponse($response)
 {
     // Increment sequence number so next NewInvoice call gets a new order ID
     $this->incrementSequenceNumber();
     if (!isset($response['status'])) {
         $this->transaction_response->setCommunicationStatus(false);
         $this->logger->error('AstroPay response does not have a status code');
         throw new ResponseProcessingException('AstroPay response does not have a status code', ResponseCodes::MISSING_REQUIRED_DATA);
     }
     $this->transaction_response->setCommunicationStatus(true);
     if ($response['status'] === '0') {
         if (!isset($response['link'])) {
             $this->logger->error('AstroPay NewInvoice success has no link');
             throw new ResponseProcessingException('AstroPay NewInvoice success has no link', ResponseCodes::MISSING_REQUIRED_DATA);
         }
     } else {
         $logme = 'AstroPay response has non-zero status.  Full response: ' . print_r($response, true);
         $this->logger->warning($logme);
         $code = 'internal-0000';
         $message = $this->getErrorMapByCodeAndTranslate($code);
         $context = null;
         if (isset($response['desc'])) {
             // error codes are unreliable, so we have to examine the description
             if (preg_match('/^invoice already used/i', $response['desc'])) {
                 $this->logger->error('Order ID collision! Starting again.');
                 throw new ResponseProcessingException('Order ID collision! Starting again.', ResponseCodes::DUPLICATE_ORDER_ID, array('order_id'));
             } else {
                 if (preg_match('/^could not (register user|make the deposit)/i', $response['desc'])) {
                     // AstroPay is overwhelmed.  Tell the donor to try again soon.
                     $message = WmfFramework::formatMessage('donate_interface-try-again');
                 } else {
                     if (preg_match('/^user (unauthorized|blacklisted)/i', $response['desc'])) {
                         // They are blacklisted by AstroPay for shady doings,
                         // or listed delinquent by their government.
                         // Either way, we can't process 'em through AstroPay
                         $this->finalizeInternalStatus(FinalStatus::FAILED);
                     } else {
                         if (preg_match('/^the user limit has been exceeded/i', $response['desc'])) {
                             // They've spent too much via AstroPay today.
                             // Setting context to 'amount' will tell the form to treat
                             // this like a validation error and make amount editable.
                             $context = 'amount';
                             $message = WmfFramework::formatMessage('donate_interface-error-msg-limit');
                         } else {
                             if (preg_match('/param x_cpf$/i', $response['desc'])) {
                                 // Something wrong with the fiscal number
                                 $context = 'fiscal_number';
                                 $language = $this->dataObj->getVal_Escaped('language');
                                 $country = $this->dataObj->getVal_Escaped('country');
                                 $message = DataValidator::getErrorMessage('fiscal_number', 'calculated', $language, $country);
                             } else {
                                 if (preg_match('/invalid control/i', $response['desc'])) {
                                     // They think we screwed up the signature.  Log what we signed.
                                     $signed = AstroPaySignature::getNewInvoiceMessage($this->getData_Staged());
                                     $signature = $this->getData_Staged('control');
                                     $this->logger->error("{$logme} Signed message: '{$signed}' Signature: '{$signature}'");
                                 } else {
                                     // Some less common error.  Also log message at 'error' level
                                     $this->logger->error($logme);
                                 }
                             }
                         }
                     }
                 }
             }
         }
         $this->transaction_response->setErrors(array($code => array('message' => $message, 'debugInfo' => $logme, 'logLevel' => LogLevel::WARNING, 'context' => $context)));
     }
 }
 /**
  * Gets the action calculated on the last filter run. If there are no
  * risk scores stored in session, throws a RuntimeException. Even if
  * all filters are disabled, we should have stored 'initial' => 0.
  *
  * @param GatewayType $gateway_adapter
  * @return string
  */
 public static function determineStoredAction(GatewayType $gateway_adapter)
 {
     if (!WmfFramework::getSessionValue('risk_scores')) {
         throw new RuntimeException('No stored risk scores');
     }
     return self::singleton($gateway_adapter)->determineAction();
 }
 /**
  * Builds minfraud query from user input
  *
  * Required:
  * - city
  * - country
  * - i: Client IPA
  * - license_key
  * - postal
  * - region
  *
  * Optional that we are sending:
  * - bin: First 6 digits of the card
  * - domain: send the domain of the email address
  * - emailMD5: send an MD5 of the email address
  * - txnID: The internal transaction id of the contribution.
  *
  * @param array $data
  * @return array containing hash for minfraud query
  */
 protected function build_query(array $data)
 {
     // mapping of data keys -> minfraud array keys
     $map = array("city" => "city", "region" => "state", "postal" => "zip", "country" => "country", "domain" => "email", "emailMD5" => "email", "bin" => "card_num", "txnID" => "contribution_tracking_id");
     $this->minfraudQuery = array();
     // minfraud license key
     $this->minfraudQuery["license_key"] = $this->minfraudLicenseKey;
     // user's IP address
     $this->minfraudQuery["i"] = $this->gateway_adapter->getData_Unstaged_Escaped('user_ip');
     // We only have access to these fields when the user's request is still
     // present, but not when in batch mode.
     if (!$this->gateway_adapter->isBatchProcessor()) {
         // user's user agent
         $this->minfraudQuery['user_agent'] = WmfFramework::getRequestHeader('user-agent');
         // user's language
         $this->minfraudQuery['accept_language'] = WmfFramework::getRequestHeader('accept-language');
     }
     // fetch the array of country codes
     $country_codes = CountryCodes::getCountryCodes();
     // loop through the map and add pertinent values from $data to the hash
     foreach ($map as $key => $value) {
         // do some data processing to clean up values for minfraud
         switch ($key) {
             case "domain":
                 // get just the domain from the email address
                 $newdata[$value] = substr(strstr($data[$value], '@'), 1);
                 break;
             case "bin":
                 // get just the first 6 digits from CC#... if we have one.
                 $bin = '';
                 if (isset($data[$value])) {
                     $bin = substr($data[$value], 0, 6);
                 }
                 $newdata[$value] = $bin;
                 break;
             case "country":
                 $newdata[$value] = $country_codes[$data[$value]];
                 break;
             case "emailMD5":
                 $newdata[$value] = $this->get_ccfd()->filter_field($key, $data[$value]);
                 break;
             default:
                 $newdata[$value] = $data[$value];
         }
         $this->minfraudQuery[$key] = $newdata[$value];
     }
     return $this->minfraudQuery;
 }
 /**
  * buildOrderIDSources: Uses the 'alt_locations' array in the order id
  * metadata, to build an array of all possible candidates for order_id.
  * This will also weed out candidates that do not meet the
  * gateway-specific data constraints for that field, and are therefore
  * invalid.
  *
  * @TODO: Data Item Class. There should be a class that keeps track of
  * the metadata for every field we use (everything that currently comes
  * back from DonationData), that can be overridden per gateway. Revisit
  * this in a more universal way when that time comes.
  */
 public function buildOrderIDSources()
 {
     static $built = false;
     if ($built && isset($this->order_id_candidates)) {
         //once per request is plenty
         return;
     }
     //pull all order ids and variants from all their usual locations
     $locations = array('request' => 'order_id', 'session' => array('Donor' => 'order_id'));
     $alt_locations = $this->getOrderIDMeta('alt_locations');
     if ($alt_locations && is_array($alt_locations)) {
         foreach ($alt_locations as $var => $key) {
             $locations[$var] = $key;
         }
     }
     if ($this->isBatchProcessor()) {
         // Can't use request or session from here.
         $locations = array_diff_key($locations, array_flip(array('request', 'session')));
     }
     //Now pull all the locations and populate the candidate array.
     $oid_candidates = array();
     foreach ($locations as $var => $key) {
         switch ($var) {
             case "request":
                 $value = WmfFramework::getRequestValue($key, '');
                 if ($value !== '') {
                     $oid_candidates[$var] = $value;
                 }
                 break;
             case "session":
                 if (is_array($key)) {
                     foreach ($key as $subkey => $subvalue) {
                         $parentVal = WmfFramework::getSessionValue($subkey);
                         if (is_array($parentVal) && array_key_exists($subvalue, $parentVal)) {
                             $oid_candidates['session' . $subkey . $subvalue] = $parentVal[$subvalue];
                         }
                     }
                 } else {
                     $val = WmfFramework::getSessionValue($key);
                     if (!is_null($val)) {
                         $oid_candidates[$var] = $val;
                     }
                 }
                 break;
             default:
                 if (!is_array($key) && array_key_exists($key, ${$var})) {
                     //simple case first. This is a direct key in $var.
                     $oid_candidates[$var] = ${$var}[$key];
                 }
                 if (is_array($key)) {
                     foreach ($key as $subkey => $subvalue) {
                         if (array_key_exists($subkey, ${$var}) && array_key_exists($subvalue, ${$var}[$subkey])) {
                             $oid_candidates[$var . $subkey . $subvalue] = ${$var}[$subkey][$subvalue];
                         }
                     }
                 }
                 break;
         }
     }
     //unset every invalid candidate
     foreach ($oid_candidates as $source => $value) {
         if (empty($value) || !$this->validateDataConstraintsMet('order_id', $value)) {
             unset($oid_candidates[$source]);
         }
     }
     $this->order_id_candidates = $oid_candidates;
     $built = true;
 }
 public function testTooLittleBbd()
 {
     $this->normalized['currency_code'] = 'BBD';
     $this->normalized['amount'] = '2.95';
     $this->validate();
     $this->assertNotEmpty($this->errors, 'No error for diminutive amount (BBD)');
     $formattedMin = Amount::format(3.0, 'BBD', 'en_US');
     $expected = WmfFramework::formatMessage('donate_interface-smallamount-error', $formattedMin);
     $this->assertEquals($expected, $this->errors['amount'], 'Wrong error message for diminutive amount (BBD)');
 }
 /**
  * Basically, this is a wrapper for the WebRequest wasPosted function that
  * won't give us notices if we weren't even a web request.
  * I realize this is pretty lame.
  * Notices, however, are more lame.
  * @staticvar string $posted Keeps track so we don't have to figure it out twice.
  */
 public function wasPosted()
 {
     static $posted = null;
     if ($posted === null) {
         $posted = array_key_exists('REQUEST_METHOD', $_SERVER) && WmfFramework::isPosted();
     }
     return $posted;
 }
 /**
  * Run the filter if we haven't for this session, and set a flag
  * @param GatewayType $gateway_adapter
  * @param Gateway_Extras_CustomFilters $custom_filter_object
  * @return bool
  */
 public static function onInitialFilter($gateway_adapter, $custom_filter_object)
 {
     if (!$gateway_adapter->getGlobal('EnableIPVelocityFilter')) {
         return true;
     }
     if (WmfFramework::getSessionValue(self::RAN_INITIAL)) {
         return true;
     }
     WmfFramework::setSessionValue(self::RAN_INITIAL, true);
     $gateway_adapter->debugarray[] = 'IP Velocity onFilter!';
     return self::singleton($gateway_adapter, $custom_filter_object)->filter();
 }