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