Ejegg has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/286261

Change subject: WIP encapsulated amount validation for better messages
......................................................................

WIP encapsulated amount validation for better messages

TODO: clean up some special cases in DataValidator
'Amount' desperately needs a namespace

Bug: T105618
Change-Id: If9d60491cdc992a38a0ea459f5ece3f38ce08184
---
M DonationInterface.php
M adyen_gateway/config/transformers.yaml
M amazon_gateway/config/transformers.yaml
M astropay_gateway/config/transformers.yaml
A gateway_common/Amount.php
M gateway_common/DataValidator.php
M gateway_common/DonationData.php
M gateway_common/FiscalNumber.php
M gateway_common/ValidationHelper.php
M globalcollect_gateway/config/transformers.yaml
M paypal_gateway/config/transformers.yaml
M tests/DataValidatorTest.php
M worldpay_gateway/config/transformers.yaml
13 files changed, 133 insertions(+), 99 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DonationInterface 
refs/changes/61/286261/1

diff --git a/DonationInterface.php b/DonationInterface.php
index ad0a0dd..7d16eec 100644
--- a/DonationInterface.php
+++ b/DonationInterface.php
@@ -37,6 +37,7 @@
 /**
  * CLASSES
  */
+$wgAutoloadClasses['Amount'] = __DIR__ . '/gateway_common/Amount.php';
 $wgAutoloadClasses['AmountInCents'] = __DIR__ . 
'/gateway_common/AmountInCents.php';
 $wgAutoloadClasses['ArrayHelper'] = __DIR__ . 
'/gateway_common/ArrayHelper.php';
 $wgAutoloadClasses['FiscalNumber'] = __DIR__ . 
'/gateway_common/FiscalNumber.php';
diff --git a/adyen_gateway/config/transformers.yaml 
b/adyen_gateway/config/transformers.yaml
index 7150041..5a85ddd 100644
--- a/adyen_gateway/config/transformers.yaml
+++ b/adyen_gateway/config/transformers.yaml
@@ -1,4 +1,5 @@
 # Core
+- Amount
 - DonorEmail
 - DonorFullName
 - AmountInCents
diff --git a/amazon_gateway/config/transformers.yaml 
b/amazon_gateway/config/transformers.yaml
index 89e70d8..260f807 100644
--- a/amazon_gateway/config/transformers.yaml
+++ b/amazon_gateway/config/transformers.yaml
@@ -1,4 +1,5 @@
 # Skip AmountInCents, we want to pass the real amount x1.
+- Amount
 - DonorEmail
 - DonorFullName
 - StreetAddress
diff --git a/astropay_gateway/config/transformers.yaml 
b/astropay_gateway/config/transformers.yaml
index caaa153..bae2a9c 100644
--- a/astropay_gateway/config/transformers.yaml
+++ b/astropay_gateway/config/transformers.yaml
@@ -1,7 +1,9 @@
 # Skip AmountInCents.
+- Amount
 - AstroPayFinancialNumbers
 - AstroPayMethodCodec
 - FiscalNumber
+- DonorEmail
 - DummyFiscalNumber # see class comment
 - DonorFullName
 - StreetAddress
diff --git a/gateway_common/Amount.php b/gateway_common/Amount.php
new file mode 100644
index 0000000..1026d03
--- /dev/null
+++ b/gateway_common/Amount.php
@@ -0,0 +1,106 @@
+<?php
+
+class Amount implements ValidationHelper {
+
+       function validate( GatewayType $gateway, $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( $gateway->getGlobal( 'PriceFloor' ), 
$currency );
+               $max = self::convert( $gateway->getGlobal( 'PriceCeiling' ), 
$currency );
+               if (
+                       !is_numeric( $value ) ||
+                       $value > $max
+               ) {
+                       $errors['amount'] = WmfFramework::formatMessage(
+                               'donate_interface-error-msg-invalid-amount'
+                       );
+               } else if ( $value < $min ) {
+                       $errors['amount'] = WmfFramework::formatMessage(
+                               'donate_interface-smallamount-error',
+                               self::round( $min, $currency ) . ' ' . $currency
+                       );
+               }
+       }
+
+       /**
+        * Checks if the $value is missing or equivalent to zero.
+        *
+        * @param string $value The value to check for zero-ness
+        * @return boolean True if the $value is missing or zero, otherwise 
false
+        */
+       protected static function isZeroIsh( $value ) {
+               if (
+                       $value === null ||
+                       $value === '' ||
+                       preg_match( '/^0+(\.0+)?$/', $value )
+               ) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       /**
+        * Convert an amount in USD to a particular currency
+        *
+        * This is grossly rudimentary and likely wildly inaccurate.
+        * This mimics the hard-coded values used by the WMF to convert 
currencies
+        * for validation on the front-end on the first step landing pages of 
their
+        * donation process - the idea being that we can get a close 
approximation
+        * of converted currencies to ensure that contributors are not going 
above
+        * or below the price ceiling/floor, even if they are using a non-US 
currency.
+        *
+        * In reality, this probably ought to use some sort of webservice to 
get real-time
+        * conversion rates.
+        *
+        * @param float $amount
+        * @param string $currency
+        * @return float
+        * @throws UnexpectedValueException
+        */
+       public static function convert( $amount, $currency ) {
+               $rates = CurrencyRates::getCurrencyRates();
+               $code = strtoupper( $currency );
+               if ( array_key_exists( $code, $rates ) ) {
+                       return $amount * $rates[$code];
+               }
+               throw new UnexpectedValueException(
+                       'Bad programmer!  Bad currency made it too far through 
the portcullis'
+               );
+       }
+
+       /**
+       * Some currencies, like JPY, don't exist in fractional amounts.
+       * This rounds an amount to the appropriate number of decimal places.
+       *
+       * @param float $amount
+       * @param string $currencyCode
+       * @return string rounded amount
+       */
+       public static function round( $amount, $currencyCode ) {
+               if ( DataValidator::is_fractional_currency( $currencyCode ) ){
+                       return number_format( $amount, 2, '.', '' );
+               }
+               return (string) floor( $amount );
+       }
+}
diff --git a/gateway_common/DataValidator.php b/gateway_common/DataValidator.php
index 009b8dd..ba90f84 100644
--- a/gateway_common/DataValidator.php
+++ b/gateway_common/DataValidator.php
@@ -25,10 +25,6 @@
         */
        public static function getErrorToken( $field ){
                switch ( $field ) {
-                       case 'amountGiven' :
-                       case 'amountOther' :
-                               $error_token = 'amount';
-                               break;
                        case 'email' :
                        case 'amount' :
                        case 'currency_code' :
@@ -200,7 +196,6 @@
                // Define all default validations.
                $validations = array(
                        'not_empty' => array(
-                               'amount' => 'validate_non_zero',
                                'country',
                                'currency_code',
                                'gateway',
@@ -208,9 +203,6 @@
                        'valid_type' => array(
                                '_cache_' => 'validate_boolean',
                                'account_number' => 'validate_numeric',
-                               'amount' => 'validate_numeric',
-                               'amountGiven' => 'validate_numeric',
-                               'amountOther' => 'validate_numeric',
                                'anonymous' => 'validate_boolean',
                                'contribution_tracking_id' => 
'validate_numeric',
                                'currency_code' => 'validate_alphanumeric',
@@ -232,13 +224,8 @@
                                'fname' => 'validate_name',
                                'lname' => 'validate_name',
                                'name' => 'validate_name',
-
-                               // Depends on currency_code and gateway.
-                               'amount' => 'validate_amount',
-
                        ),
                );
-
 
                $errors = array();
                $results = array();
@@ -285,13 +272,6 @@
                                $result = null;
                                // Handle special cases.
                                switch ( $validation_function ) {
-                                       case 'validate_amount':
-                                               if ( 
self::checkValidationPassed( array( 'currency_code', 'gateway' ), $results ) ){
-                                                       $priceFloor = 
$gateway->getGlobal( 'PriceFloor' );
-                                                       $priceCeiling = 
$gateway->getGlobal( 'PriceCeiling' );
-                                                       $result = 
call_user_func( $callable, $data[$field], $data['currency_code'], $priceFloor, 
$priceCeiling );
-                                               } //otherwise, just don't do 
the validation. The other stuff will be complaining already. 
-                                               break;
                                        case 'validate_currency_code':
                                                $result = call_user_func( 
$callable, $data[$field], $gateway->getCurrencies( $data ) );
                                                break;
@@ -347,31 +327,6 @@
        protected static function validate_email( $value ) {
                return WmfFramework::validateEmail( $value )
                        && !DataValidator::cc_number_exists_in_str( $value );
-       }
-
-       /**
-        * validate_amount
-        *
-        * Determines if the $value passed in is a valid amount. 
-        * @param string $value The piece of data that is supposed to be an 
amount. 
-        * @param string $currency_code The amount was given in this currency.
-        * @param float $priceFloor Minimum valid amount (USD).
-        * @param float $priceCeiling Maximum valid amount (USD).
-        *
-        * @return boolean True if $value is a valid amount, otherwise false.  
-        */
-       protected static function validate_amount( $value, $currency_code, 
$priceFloor, $priceCeiling ) {
-               if ( !$value || !$currency_code || !is_numeric( $value ) ) {
-                       return false;
-               }
-
-               if ( !preg_match( '/^\d+(\.(\d+)?)?$/', $value ) ||
-                       ( ( float ) self::convert_to_usd( $currency_code, 
$value ) < ( float ) $priceFloor ||
-                       ( float ) self::convert_to_usd( $currency_code, $value 
) > ( float ) $priceCeiling ) ) {
-                       return false;
-               }
-               
-               return true;
        }
 
        protected static function validate_currency_code( $value, 
$acceptedCurrencies ) {
@@ -557,22 +512,6 @@
        }
 
        /**
-        * Checks to make sure that the $value is present in the $data array, 
and not equivalent to zero.
-        * @param string $value The value to check for non-zeroness.
-        * @param array $data The whole data set.
-        * @return boolean True if the $value is not missing or zero, otherwise 
false.
-        */
-       public static function validate_non_zero( $value ) {
-               if ( $value === null || $value === ''
-                       || preg_match( '/^0+(\.0+)?$/', $value )
-               ) {
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
         * Analyzes a string to see if any credit card numbers are hiding out 
in it
         *
         * @param $str
@@ -660,35 +599,6 @@
                        $odd = !$odd;
                }
                return( ( $sum % 10 ) == 0 );
-       }
-
-       /**
-        * Convert an amount for a particular currency to an amount in USD
-        *
-        * This is grosley rudimentary and likely wildly inaccurate.
-        * This mimicks the hard-coded values used by the WMF to convert 
currencies
-        * for validatoin on the front-end on the first step landing pages of 
their
-        * donation process - the idea being that we can get a close 
approximation
-        * of converted currencies to ensure that contributors are not going 
above
-        * or below the price ceiling/floor, even if they are using a non-US 
currency.
-        *
-        * In reality, this probably ought to use some sort of webservice to 
get real-time
-        * conversion rates.
-        *
-        * @param string $currency_code
-        * @param float $amount
-        * @return float
-        * @throws UnexpectedValueException
-        */
-       public static function convert_to_usd( $currency_code, $amount ) {
-               $rates = CurrencyRates::getCurrencyRates();
-               $code = strtoupper( $currency_code );
-               if ( array_key_exists( $code, $rates ) ) {
-                       $usd_amount = $amount / $rates[$code];
-               } else {
-                       throw new UnexpectedValueException( 'Bad programmer!  
Bad currency made it too far through the portcullis' );
-               }
-               return $usd_amount;
        }
        
        
diff --git a/gateway_common/DonationData.php b/gateway_common/DonationData.php
index 9337673..3bc3345 100644
--- a/gateway_common/DonationData.php
+++ b/gateway_common/DonationData.php
@@ -545,12 +545,11 @@
                        $this->setVal('amount', 'invalid');
                        return;
                }
-               
-               if ( DataValidator::is_fractional_currency( $this->getVal( 
'currency_code' ) ) ){
-                       $this->setVal( 'amount', number_format( $this->getVal( 
'amount' ), 2, '.', '' ) );
-               } else {
-                       $this->setVal( 'amount', floor( $this->getVal( 'amount' 
) ) );
-               }
+
+               $this->setVal(
+                       'amount',
+                       Amount::round( $this->getVal( 'amount' ), 
$this->getVal( 'currency_code' ) )
+               );
        }
 
        /**
@@ -981,7 +980,11 @@
                        $transformers = $this->gateway->getDataTransformers();
                        foreach ( $transformers as $transformer ) {
                                if ( $transformer instanceof ValidationHelper ) 
{
-                                       $transformer->validate( 
$this->normalized, $this->validationErrors );
+                                       $transformer->validate(
+                                               $this->gateway,
+                                               $this->normalized,
+                                               $this->validationErrors
+                                       );
                                }
                        }
                }
diff --git a/gateway_common/FiscalNumber.php b/gateway_common/FiscalNumber.php
index 5bb458b..eb4dff2 100644
--- a/gateway_common/FiscalNumber.php
+++ b/gateway_common/FiscalNumber.php
@@ -50,10 +50,11 @@
        /**
         * Rudimentary tests of fiscal number format for different countries
         *
+        * @param GatewayType $unused
         * @param array $normalized Normalized donation data
         * @param array $errors Results of validation to this point
         */
-       public function validate( $normalized, &$errors ) {
+       public function validate( GatewayType $unused, $normalized, &$errors ) {
                if (
                        !isset( $normalized['country'] ) ||
                        !isset( $normalized['fiscal_number'] )
diff --git a/gateway_common/ValidationHelper.php 
b/gateway_common/ValidationHelper.php
index 8926315..e2c0c50 100644
--- a/gateway_common/ValidationHelper.php
+++ b/gateway_common/ValidationHelper.php
@@ -4,6 +4,11 @@
        /**
         * Run validation on whatever normalized data we're responsible for,
         * and set errors per field, or under the "general" key.
+        *
+        * @param GatewayType $adapter
+        * @param array $normalized Donation data in normalized form.
+        * @param array $errors Reference to error array
+        * @return void
         */
-       function validate( $normalized, &$errors );
+       function validate( GatewayType $adapter, $normalized, &$errors );
 }
diff --git a/globalcollect_gateway/config/transformers.yaml 
b/globalcollect_gateway/config/transformers.yaml
index cea8f45..082adfe 100644
--- a/globalcollect_gateway/config/transformers.yaml
+++ b/globalcollect_gateway/config/transformers.yaml
@@ -1,4 +1,5 @@
 # Core
+- Amount
 - DonorEmail
 - DonorFullName
 - AmountInCents
diff --git a/paypal_gateway/config/transformers.yaml 
b/paypal_gateway/config/transformers.yaml
index 5dbeeca..a69272f 100644
--- a/paypal_gateway/config/transformers.yaml
+++ b/paypal_gateway/config/transformers.yaml
@@ -1,4 +1,5 @@
 # Core
+- Amount
 - DonorEmail
 - DonorFullName
 - AmountInCents
diff --git a/tests/DataValidatorTest.php b/tests/DataValidatorTest.php
index 0a2ecb3..221a64c 100644
--- a/tests/DataValidatorTest.php
+++ b/tests/DataValidatorTest.php
@@ -137,6 +137,7 @@
                $validator = new FiscalNumber();
                $errors = array();
                $validator->validate(
+                       new TestingGenericAdapter(),
                        array( 'country' => $country, 'fiscal_number' => 
$value, 'language' => 'en' ),
                        $errors
                );
diff --git a/worldpay_gateway/config/transformers.yaml 
b/worldpay_gateway/config/transformers.yaml
index 3cc2749..6fe36d3 100644
--- a/worldpay_gateway/config/transformers.yaml
+++ b/worldpay_gateway/config/transformers.yaml
@@ -1,4 +1,5 @@
 # Core
+- Amount
 - DonorEmail
 - DonorFullName
 - AmountInCents

-- 
To view, visit https://gerrit.wikimedia.org/r/286261
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: If9d60491cdc992a38a0ea459f5ece3f38ce08184
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/DonationInterface
Gerrit-Branch: master
Gerrit-Owner: Ejegg <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to