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')));
 }
 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;
 }
 /**
  * languageSpecificFallback - returns the text of the first existant message
  * in the requested language. If no messages are found in that language, the
  * function returns the first existant fallback message.
  *
  * @param string $language the code of the requested language
  * @param array $msg_keys
  * @param array $params extra message parameters
  * @throws InvalidArgumentException
  * @return String the text of the first existant message
  */
 public static function languageSpecificFallback($language = 'en', $msg_keys = array(), $params = array())
 {
     if (count($msg_keys) < 1) {
         throw new InvalidArgumentException(__FUNCTION__ . " BAD PROGRAMMER. No message keys given.");
     }
     # look for the first message that exists
     foreach ($msg_keys as $m) {
         if (WmfFramework::messageExists($m, $language)) {
             return WmfFramework::formatMessage($m, $params);
         }
     }
     # we found nothing in the requested language, return the first fallback message that exists
     foreach ($msg_keys as $m) {
         if (WmfFramework::messageExists($m, $language)) {
             return WmfFramework::formatMessage($m, $params);
         }
     }
     # somehow we still don't have a message, return a default error message
     return WmfFramework::formatMessage($msg_keys[0], $params);
 }
 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'];
 }
 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'];
             }
         }
     }
 }
 /**
  * getErrorMessage - returns the translated error message appropriate for a
  * validation error on the specified field, of the specified type.
  * @param string $field - The common name of the field containing the data
  * that is causing the error.
  * @param string $type - The type of error being caused, from a set.
  *    Possible values are:
  *        'not_empty' - the value is required and not currently present
  *        'valid_type' - in general, the wrong format
  *        'calculated' - fields that failed some kind of multiple-field data
  * integrity check.
  * @param string $language MediaWiki language code
  * @param string $country ISO country code
  * @return String
  */
 public static function getErrorMessage($field, $type, $language, $country = null)
 {
     //this is gonna get ugly up in here.
     //error_log( __FUNCTION__ . " $field, $type, $value " );
     //NOTE: We are just using the next bit because it's convenient.
     //getErrorToken is actually for something entirely different:
     //Figuring out where on the form the error should land.
     $message_field = self::getErrorToken($field);
     if ($field === 'expiration') {
         ///the inevitable special case.
         $message_field = $field;
     }
     //postal code is a weird one. More L10n than I18n.
     //'donate_interface-error-msg-postal' => 'postal code',
     $error_message_field_key = 'donate_interface-error-msg-' . $message_field;
     if ($country !== null) {
         $translated_field_name = MessageUtils::getCountrySpecificMessage($error_message_field_key, $country, $language);
     } else {
         if (WmfFramework::messageExists($error_message_field_key, $language)) {
             $translated_field_name = WmfFramework::formatMessage($error_message_field_key);
         } else {
             $translated_field_name = false;
         }
     }
     //Empty messages should get:
     //'donate_interface-error-msg' => 'Please enter your $1';
     //If they have no defined error message, give 'em the default.
     if ($type === 'not_empty') {
         if ($message_field != 'general' && $translated_field_name) {
             return WmfFramework::formatMessage('donate_interface-error-msg', $translated_field_name);
         }
     }
     if ($type === 'valid_type' || $type === 'calculated') {
         //NOTE: We are just using the next bit because it's convenient.
         //getErrorToken is actually for something entirely different:
         //Figuring out where on the form the error should land.
         $token = self::getErrorToken($field);
         $error_key_calc = 'donate_interface-error-msg-' . $token . '-calc';
         if ($type === 'calculated') {
             // try for the special "calculated" error message.
             if (WmfFramework::messageExists($error_key_calc, $language)) {
                 return WmfFramework::formatMessage($error_key_calc);
             }
         }
         //try for new more specific default correction message
         if ($message_field != 'general' && $translated_field_name && WmfFramework::messageExists('donate_interface-error-msg-field-correction', $language)) {
             return WmfFramework::formatMessage('donate_interface-error-msg-field-correction', $translated_field_name);
         }
     }
     //ultimate defaultness.
     return WmfFramework::formatMessage('donate_interface-error-msg-general');
 }
 /**
  * getErrorMap
  *
  * This will also return an error message if a $code is passed.
  *
  * If the error code does not exist, the default message will be returned.
  *
  * A default message should always exist with an index of 0.
  *
  * NOTE: This method will check to see if the message exists in translation
  * and use that message instead of the default. This would override error_map.
  *
  * @param    string    $code    The error code to look up in the map
  * @param    array     $options
  * @return   array|string    Returns @see GatewayAdapter::$error_map
  */
 public function getErrorMap($code = null, $options = array())
 {
     if (is_null($code)) {
         return $this->error_map;
     }
     $defaults = array('translate' => false);
     $options = array_merge($defaults, $options);
     $response_message = $this->getIdentifier() . '_gateway-response-' . $code;
     $translatedMessage = WmfFramework::formatMessage($response_message);
     // FIXME: don't do this.
     // Check to see if an error message exists in translation
     if (substr($translatedMessage, 0, 3) !== '&lt;') {
         // Message does not exist
         $translatedMessage = '';
     }
     if (isset($this->error_map[$code])) {
         $mapped = $this->error_map[$code];
         // Errors with complicated formatting can map to a function
         if (is_callable($mapped)) {
             return $mapped();
         }
         $messageKey = $mapped;
     } else {
         // If the $code does not exist, use the default message
         $messageKey = 'donate_interface-processing-error';
     }
     $translatedMessage = $options['translate'] && empty($translatedMessage) ? WmfFramework::formatMessage($messageKey) : $translatedMessage;
     // Check to see if we return the translated message.
     $message = $options['translate'] ? $translatedMessage : $messageKey;
     return $message;
 }
 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)');
 }
 /**
  * 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)));
     }
 }