/**
  * Build a new PaymentMethod object from an name pair
  *
  * @param GatewayType $gateway
  * @param string $method_name
  * @param string $submethod_name
  * @param bool $is_recurring
  *
  * @return PaymentMethod
  */
 public static function newFromCompoundName(GatewayType $gateway, $method_name, $submethod_name, $is_recurring)
 {
     $method = new PaymentMethod();
     $method->gateway = $gateway;
     $method->name = PaymentMethod::parseCompoundMethod($method_name, $submethod_name);
     $method->is_recurring = $is_recurring;
     try {
         // FIXME: I don't like that we're couple to the gateway already.
         $spec = array();
         if ($method_name) {
             $spec = $gateway->getPaymentMethodMeta($method_name);
         }
         // When we have a more specific method, child metadata supercedes
         // parent metadata
         if ($submethod_name) {
             $spec = array_replace_recursive($spec, $gateway->getPaymentSubmethodMeta($submethod_name));
         }
         $method->spec = $spec;
     } catch (Exception $ex) {
         // Return empty method.
         $method->name = "none";
         $method->spec = array();
     }
     return $method;
 }
 protected static function singleton(GatewayType $gateway_adapter, Gateway_Extras_CustomFilters $custom_filter_object)
 {
     if (!self::$instance || $gateway_adapter->isBatchProcessor()) {
         self::$instance = new self($gateway_adapter, $custom_filter_object);
     }
     return self::$instance;
 }
 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);
             }
         }
     }
 }
 public function stage(GatewayType $adapter, $normalized, &$stagedData)
 {
     if ($adapter->isBatchProcessor()) {
         // Only makes sense for real users.
         return;
     }
     if (!empty($normalized['returnto'])) {
         $returnto = $normalized['returnto'];
     } else {
         $returnto = '';
     }
     if (isset($normalized['payment_method']) && $normalized['payment_method'] === 'cc') {
         // Add order ID to the returnto URL, only if it's not already there.
         //TODO: This needs to be more robust (like actually pulling the
         //qstring keys, resetting the values, and putting it all back)
         //but for now it'll keep us alive.
         if ($adapter->getOrderIDMeta('generate') && !empty($returnto) && !strpos($returnto, 'order_id')) {
             $queryArray = array('order_id' => $normalized['order_id']);
             $stagedData['returnto'] = wfAppendQuery($returnto, $queryArray);
         }
     } else {
         // FIXME: An empty returnto should be handled by the result switcher instead.
         $stagedData['returnto'] = ResultPages::getThankYouPage($adapter);
     }
 }
 public static function onPostProcess(GatewayType $gateway_adapter)
 {
     if (!$gateway_adapter->getGlobal('EnableConversionLog')) {
         return true;
     }
     $gateway_adapter->debugarray[] = 'conversion log onPostProcess!';
     return self::singleton($gateway_adapter)->post_process();
 }
 /**
  * @param GatewayType $gateway_adapter The adapter context to log under
  *
  * @return bool Filter chain termination on FALSE. Also indicates that the cURL transaction
  *  should not be performed.
  */
 public static function onProcessorApiCall(GatewayType $gateway_adapter)
 {
     if (!$gateway_adapter->getGlobal('EnableSessionVelocityFilter')) {
         return true;
     }
     $gateway_adapter->debugarray[] = 'Session Velocity onFilter hook!';
     return self::singleton($gateway_adapter)->filter();
 }
 /**
  * Retrieve a profiler instance which saves communication statistics
  * if the adapter's SaveCommStats global is set to true.
  * @param GatewayType $adapter
  * @return DonationProfiler
  */
 public static function getProfiler(GatewayType $adapter)
 {
     if ($adapter->getGlobal('SaveCommStats')) {
         $commLogger = self::getLogger($adapter, '_commstats');
     } else {
         $commLogger = null;
     }
     return new DonationProfiler(self::getLogger($adapter), $commLogger, $adapter->getGatewayName());
 }
 protected function stage_bank_code(GatewayType $adapter, $normalized, &$stagedData)
 {
     $submethod = $adapter->getPaymentSubmethod();
     if ($submethod) {
         $meta = $adapter->getPaymentSubmethodMeta($submethod);
         if (isset($meta['bank_code'])) {
             $stagedData['bank_code'] = $meta['bank_code'];
         }
     }
 }
 public function stage(GatewayType $adapter, $normalized, &$stagedData)
 {
     // This isn't smart enough to grab a new value here;
     // Late-arriving values have to trigger a restage via addData or
     // this will always equal the risk_score at the time of object
     // construction. Still need the formatting, though.
     if (isset($normalized['risk_score'])) {
         // Cap the score we send to Adyen since they automatically
         // decline any transaction with offset > 100
         $maximumScore = $adapter->getGlobal('MaxRiskScore');
         $stagedScore = min($normalized['risk_score'], $maximumScore);
         $stagedData['risk_score'] = (string) round($stagedScore);
     }
 }
 /**
  * Get the URL for a page to show donors who cancel their attempt
  * @param GatewayType $adapter instance to use for logger and settings
  * @return string full URL of the cancel page
  */
 public static function getCancelPage(GatewayType $adapter)
 {
     $cancelPage = $adapter->getGlobal('CancelPage');
     if (empty($cancelPage)) {
         return '';
     }
     return self::appendLanguageAndMakeURL($cancelPage, $adapter->getData_Unstaged_Escaped('language'));
 }
 /**
  * Calculate a base 64 encoded SHA256 HMAC according to the rules at
  * https://docs.adyen.com/developers/hpp-manual#hmacpaymentsetupsha256
  *
  * @param GatewayType $adapter
  * @param array $values
  * @return string
  */
 public static function calculateSignature(GatewayType $adapter, $values)
 {
     $ignoredKeys = array('sig', 'merchantSig', 'title', 'liberated');
     foreach (array_keys($values) as $key) {
         if (substr($key, 0, 7) === 'ignore.' || in_array($key, $ignoredKeys)) {
             unset($values[$key]);
         } else {
             // escape colons and backslashes
             $values[$key] = str_replace('\\', '\\\\', $values[$key]);
             $values[$key] = str_replace(':', '\\:', $values[$key]);
         }
     }
     ksort($values, SORT_STRING);
     $merged = array_merge(array_keys($values), array_values($values));
     $joined = implode(':', $merged);
     $secret = $adapter->getAccountConfig('SharedSecret');
     return base64_encode(hash_hmac('sha256', $joined, pack("H*", $secret), true));
 }
 public function testTooMuchBbd()
 {
     $this->normalized['currency_code'] = 'BBD';
     $this->normalized['amount'] = '201.00';
     $this->validate();
     $this->assertNotEmpty($this->errors, 'No error for excessive amount (BBD)');
     $expected = WmfFramework::formatMessage('donate_interface-bigamount-error', 200, 'BBD', $this->adapter->getGlobal('MajorGiftsEmail'));
     $this->assertEquals($expected, $this->errors['amount'], 'Wrong error message for excessive amount (BBD)');
 }
 /**
  * Transforms the astropay payment method into our method name
  */
 public function unstage(GatewayType $adapter, $stagedData, &$unstagedData)
 {
     $method = $stagedData['payment_method'];
     $bank = $stagedData['bank_code'];
     if (!$method || !$bank) {
         return;
     }
     $filter = function ($submethod) use($method, $bank) {
         $group = $submethod['group'];
         return $method === $group && $submethod['bank_code'] === $bank;
     };
     $candidates = array_filter($adapter->getPaymentSubmethods(), $filter);
     if (count($candidates) !== 1) {
         throw new UnexpectedValueException("No unique payment submethod defined for payment method {$method} and bank code {$bank}.");
     }
     $keys = array_keys($candidates);
     $unstagedData['payment_submethod'] = $keys[0];
 }
 public function stage(GatewayType $adapter, $normalized, &$stagedData)
 {
     // Pad some fields with zeros, to their maximum length.
     $fields = array('account_number', 'bank_code', 'branch_code');
     foreach ($fields as $field) {
         if (isset($normalized[$field])) {
             $constraints = $adapter->getDataConstraints($field);
             if (isset($constraints['length'])) {
                 $newval = DataValidator::getZeroPaddedValue($normalized[$field], $constraints['length']);
                 if ($newval !== false) {
                     $stagedData[$field] = $newval;
                 } else {
                     // Invalid value, so blank the field.
                     $stagedData[$field] = '';
                 }
             }
         }
     }
 }
 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;
 }
 /**
  * This is the class's entry point.
  *
  * @param GatewayType $gatewayAdapter
  */
 public static function onGatewayReady(GatewayType $gatewayAdapter)
 {
     if ($gatewayAdapter->getGlobal('EnableBannerHistoryLog')) {
         self::singleton($gatewayAdapter)->queueAssociationOfIds();
     }
 }
 public static function singleton(GatewayType $gateway_adapter)
 {
     if (!self::$instance || $gateway_adapter->isBatchProcessor()) {
         self::$instance = new self($gateway_adapter);
     }
     return self::$instance;
 }
 public function stage(GatewayType $adapter, $normalized, &$staged)
 {
     $returnTitle = Title::newFromText('Special:PaypalExpressGatewayResult');
     $staged['returnto'] = $returnTitle->getFullURL(array('order_id' => $normalized['order_id'], 'wmf_token' => $adapter->token_getSaltedSessionToken()), false, PROTO_CURRENT);
 }
 /**
  * Generate a hash of some data
  * @param string $data the data to hash
  * @return string The hash of the data
  */
 protected function generate_hash($data)
 {
     $salt = $this->gateway_adapter->getGlobal('Salt');
     return hash("sha512", $salt . $data);
 }
 public static function calculateSignature(GatewayType $adapter, $message)
 {
     $key = $adapter->getAccountConfig('SecretKey');
     return strtoupper(hash_hmac('sha256', pack('A*', $message), pack('A*', $key)));
 }
 /**
  * validate
  * Run all the validation rules we have defined against a (hopefully
  * normalized) DonationInterface data set.
  * @param GatewayType $gateway
  * @param array $data Normalized donation data.
  * @param array $check_not_empty An array of fields to do empty validation
  * on. If this is not populated, no fields will throw errors for being empty,
  * UNLESS they are required for a field that uses them for more complex
  * validation (the 'calculated' phase).
  * @throws BadMethodCallException
  * @return array An array of errors in a format ready for any derivative of
  * the main DonationInterface Form class to display. The array will be empty
  * if no errors were generated and everything passed OK.
  */
 public static function validate(GatewayType $gateway, $data, $check_not_empty = array())
 {
     //return the array of errors that should be generated on validate.
     //just the same way you'd do it if you were a form passing the error array around.
     /**
      * We need to run the validation in an order that makes sense.
      *
      * First: If we need to validate that some things are not empty, do that.
      * Second: Do regular data type validation on things that are not empty.
      * Third: Do validation that depends on multiple fields (making sure you
      * validated that all the required fields exist on step 1).
      *
      * How about we build an array of shit to do,
      * look at it to make sure it's complete, and in order...
      * ...and do it.
      */
     // Define all default validations.
     $validations = array('not_empty' => array('country', 'currency_code', 'gateway'), 'valid_type' => array('_cache_' => 'validate_boolean', 'account_number' => 'validate_numeric', 'anonymous' => 'validate_boolean', 'contribution_tracking_id' => 'validate_numeric', 'currency_code' => 'validate_alphanumeric', 'gateway' => 'validate_alphanumeric', 'numAttempt' => 'validate_numeric', 'optout' => 'validate_boolean', 'posted' => 'validate_boolean', 'recurring' => 'validate_boolean'), 'calculated' => array('gateway' => 'validate_gateway', 'address' => 'validate_address', 'city' => 'validate_address', 'country' => 'validate_country_allowed', 'email' => 'validate_email', 'street' => 'validate_address', 'currency_code' => 'validate_currency_code', 'fname' => 'validate_name', 'lname' => 'validate_name', 'name' => 'validate_name'));
     // Additional fields we should check for emptiness.
     if ($check_not_empty) {
         $validations['not_empty'] = array_unique(array_merge($check_not_empty, $validations['not_empty']));
     }
     $errors = array();
     $results = array();
     $language = DataValidator::guessLanguage($data);
     if (empty($data['country'])) {
         $country = null;
     } else {
         $country = $data['country'];
     }
     foreach ($validations as $phase => $fields) {
         foreach ($fields as $key => $custom) {
             // Here we decode list vs map elements.
             if (is_numeric($key)) {
                 $field = $custom;
                 $validation_function = "validate_{$phase}";
             } else {
                 $field = $key;
                 $validation_function = $custom;
             }
             if (!isset($data[$field])) {
                 if ($phase !== 'not_empty') {
                     // Skip if not required and nothing to validate.
                     continue;
                 } else {
                     // Stuff with nothing.
                     $data[$field] = null;
                 }
             }
             // Skip if we've already determined this field group is invalid.
             $errorToken = self::getErrorToken($field);
             if (array_key_exists($errorToken, $errors)) {
                 continue;
             }
             // Prepare to call the thing.
             $callable = array('DataValidator', $validation_function);
             if (!is_callable($callable)) {
                 throw new BadMethodCallException(__FUNCTION__ . " BAD PROGRAMMER. No function {$validation_function} for {$field}");
             }
             $result = null;
             // Handle special cases.
             switch ($validation_function) {
                 case 'validate_currency_code':
                     $result = call_user_func($callable, $data[$field], $gateway->getCurrencies($data));
                     break;
                 default:
                     $result = call_user_func($callable, $data[$field]);
                     break;
             }
             // Store results.
             $results[$phase][$field] = $result;
             if ($result === false) {
                 // We did the check, and it failed.
                 $errors[$errorToken] = self::getErrorMessage($field, $phase, $language, $country);
             }
         }
     }
     return $errors;
 }
 public function stage(GatewayType $adapter, $normalized, &$stagedData)
 {
     if (empty($stagedData['email'])) {
         $stagedData['email'] = $adapter->getGlobal('DefaultEmail');
     }
 }
 protected static function singleton(GatewayType $gateway_adapter, Gateway_Extras_CustomFilters $custom_filter_object = null)
 {
     # FIXME: Why always construct a new object if we're batch processing?
     if (!self::$instance || $gateway_adapter->isBatchProcessor()) {
         self::$instance = new self($gateway_adapter, $custom_filter_object);
     }
     return self::$instance;
 }