jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/275043 )
Change subject: Optionally send more Minfraud parameters ...................................................................... Optionally send more Minfraud parameters List the extra fields you want to send in $wgDonationInterfaceMinFraudExtraFields. Available: 'email', 'first_name', 'last_name', 'street_address', 'amount', and 'currency'. Also cleans up some unused CCFD (old minfraud lib) references. Bug: T128902 Bug: T173876 Change-Id: I3b48de91ac417cf245fd23a5bfcf80c7cb3bbad2 --- M README.txt M extension.json M extras/custom_filters/filters/minfraud/minfraud.body.php M tests/phpunit/FraudFiltersTest.php A tests/phpunit/includes/Responses/minFraud/15points.json 5 files changed, 154 insertions(+), 59 deletions(-) Approvals: XenoRyet: Looks good to me, approved jenkins-bot: Verified diff --git a/README.txt b/README.txt index 1a5f1f5..c9f16e1 100644 --- a/README.txt +++ b/README.txt @@ -414,6 +414,16 @@ */ $wgDonationInterfaceMinFraudAlarmLimit = 25000 +/** + * Additional fields to send in each Minfraud request. + * Parameter documentation: http://dev.maxmind.com/minfraud/#Input + * We will always send city, region, postal, country, domain, email (MD5 + * hashed), transaction_id, ip_address, user_agent, and accept_language. + * Things you can put here: email (send the real address instead of a hash), + * amount, currency, first_name, last_name, and street_address. + */ +$wgDonationInterfaceMinFraudExtraFields = array() + //Referrer Filter globals $wgDonationInterfaceCustomFiltersRefRules = array() diff --git a/extension.json b/extension.json index 2c1041a..760c469 100644 --- a/extension.json +++ b/extension.json @@ -475,6 +475,7 @@ "DonationInterfaceMinFraudUserId": "", "DonationInterfaceMinFraudLicenseKey": "", "DonationInterfaceMinFraudClientOptions": [], + "DonationInterfaceMinFraudExtraFields": [], "DonationInterfaceMinFraudAlarmLimit": 25000, "DonationInterfaceCustomFiltersRefRules": [], "DonationInterfaceCustomFiltersSrcRules": [], diff --git a/extras/custom_filters/filters/minfraud/minfraud.body.php b/extras/custom_filters/filters/minfraud/minfraud.body.php index fcd5b4d..828986e 100644 --- a/extras/custom_filters/filters/minfraud/minfraud.body.php +++ b/extras/custom_filters/filters/minfraud/minfraud.body.php @@ -64,13 +64,13 @@ * @see MaxMind\MinFraud * @var array $minFraudClientOptions */ - protected $minFraudClientOptions = array(); + protected $minFraudClientOptions = []; /** - * License key for minfraud - * @var string $minfraudLicenseKey + * License key for minFraud + * @var string $minFraudLicenseKey */ - protected $minfraudLicenseKey = ''; + protected $minFraudLicenseKey = ''; /** * Instance of Gateway_Extras_CustomFilters_MinFraud @@ -83,6 +83,35 @@ * @var \Psr\Log\LoggerInterface */ protected $fraud_logger; + + /** + * Extra fields to send to minFraud. These should be our normalized field + * names, not the minFraud field names. + * When 'email' is specified as an extra, it means we will send the real + * address instead of the md5 hash. + * + * See http://dev.maxmind.com/minfraud/#Input + * @var string[] $enabledExtraFields + */ + protected $enabledExtraFields = []; + + /** + * Top level keys indicate the grouping under the minFraud scheme. + * Second level keys are our field names, values are minFraud field names. + * @var array $extraFieldsMap + */ + protected static $extraFieldsMap = [ + 'email' => [ 'email' => 'address' ], + 'billing' => [ + 'first_name' => 'first_name', + 'last_name' => 'last_name', + 'street_address' => 'address', + ], + 'order' => [ + 'amount' => 'amount', + 'currency' => 'currency' + ] + ]; /** * Constructor @@ -117,8 +146,12 @@ // Set the minFraud API options $minFraudOptions = $gateway_adapter->getGlobal( 'MinFraudClientOptions' ); - if ( !empty( $minFraudOptions ) && is_array( $minFraudOptions ) ) { + if ( is_array( $minFraudOptions ) ) { $this->minFraudClientOptions = $minFraudOptions; + } + $extraFields = $gateway_adapter->getGlobal( 'MinFraudExtraFields' ); + if ( is_array( $extraFields ) ) { + $this->enabledExtraFields = $extraFields; } } @@ -141,12 +174,14 @@ * @return array all parameters for the query */ protected function buildQuery( array $data ) { - return [ + $standardQuery = [ 'device' => $this->getDeviceParams( $data ), 'email' => $this->getEmailParams( $data ), 'billing' => $this->getBillingParams( $data ), 'event' => $this->getEventParams( $data ), ]; + $query = $this->withExtraFields( $data, $standardQuery ); + return $query; } protected function getDeviceParams( $data ) { @@ -163,7 +198,6 @@ } /** - * TODO: option to not MD5 the address * @param array $data * @return array */ @@ -190,6 +224,18 @@ ]; } + protected function withExtraFields( $data, $query ) { + foreach ( self::$extraFieldsMap as $section => $fields ) { + foreach ( $fields as $ourName => $theirName ) { + if ( array_search( $ourName, $this->enabledExtraFields ) !== false ) { + if ( !empty( $data[$ourName] ) ) { + $query[$section][$theirName] = $data[$ourName]; + } + } + } + } + return $query; + } /** * Check to see if we can bypass minFraud check * @@ -283,22 +329,6 @@ } /** - * Get instance of CreditCardFraudDetection - * @return CreditCardFraudDetection - */ - protected function get_ccfd() { - if ( !$this->ccfd ) { - $this->ccfd = new CreditCardFraudDetection(); - - // Override the minFraud API servers - if ( !empty( $this->minFraudServers ) && is_array( $this->minFraudServers ) ) { - $this->ccfd->server = $this->minFraudServers; - } - } - return $this->ccfd; - } - - /** * Logs a minFraud query and its response * * @param array $query parameters sent to minFraud @@ -343,7 +373,7 @@ } /** - * Perform the min fraud query and capture the response + * Perform the minFraud query and capture the response * * @param array $query The array you would pass to minFraud in a query * @return Score result from minFraud client score() call @@ -363,6 +393,9 @@ )->withEvent( $query['event'] ); + if ( !empty( $query['order'] ) ) { + $minFraud = $minFraud->withOrder( $query['order'] ); + } return $minFraud->score(); } diff --git a/tests/phpunit/FraudFiltersTest.php b/tests/phpunit/FraudFiltersTest.php index 3556f55..6499931 100644 --- a/tests/phpunit/FraudFiltersTest.php +++ b/tests/phpunit/FraudFiltersTest.php @@ -107,8 +107,7 @@ } /** - * When minFraud gets a blank answer, we should assign points according to - * $wgDonationInterfaceMinFraudErrorScore. + * Test we correctly add a real score from minFraud */ function testMinFraudRealScore() { $options = $this->getDonorTestData(); @@ -126,24 +125,10 @@ '"event":{"transaction_id":"' . $gateway->getData_Unstaged_Escaped( 'contribution_tracking_id' ) .'"}}' )->willReturn( [ - 200, 'application/json', '{ - "id": "5bc5d6c2-b2c8-40af-87f4-6d61af86b6ae", - "risk_score": 15.25, - "funds_remaining": 250.00, - "queries_remaining": 500000, - - "ip_address": { - "risk": 15.25 - }, - - "disposition": { - "action": "accept", - "reason": "default" - }, - - "warnings": [] - }' - ] ); + 200, 'application/json', file_get_contents( + __DIR__ . '/includes/Responses/minFraud/15points.json' + ) + ] ); $gateway->runAntifraudFilters(); @@ -177,24 +162,73 @@ ); $this->assertEquals( $expected, $message ); } -} -// Stub out Minfraud class for CI tests -if ( !class_exists( 'CreditCardFraudDetection' ) ) { - class CreditCardFraudDetection { - public $server; - public function filter_field( $a, $b ) { - return 'blah'; - } + /** + * Make sure we send the right stuff when extra fields are enabled + */ + function testMinFraudExtras() { + $options = $this->getDonorTestData(); + $options['email'] = 'someb...@wikipedia.org'; + $options['payment_method'] = 'cc'; - public function query() { - } + $gateway = $this->getFreshGatewayObject( $options ); - public function input( $a ) { - } + $this->setMwGlobals( [ + 'wgDonationInterfaceMinFraudExtraFields' => [ + 'email', + 'first_name', + 'last_name', + 'street_address', + 'amount', + 'currency' + ] + ] ); + $this->request->expects( $this->once() ) + ->method( 'post' ) + ->with( + '{"billing":{"city":"San Francisco","region":"CA","postal":"94105","country":"US",' . + '"first_name":"Firstname","last_name":"Surname","address":"123 Fake Street"},' . + '"device":{"ip_address":"127.0.0.1"},' . + '"email":{"address":"someb...@wikipedia.org","domain":"wikipedia.org"},' . + '"event":{"transaction_id":"' . + $gateway->getData_Unstaged_Escaped( 'contribution_tracking_id' ) . + '"},"order":{"amount":"1.55","currency":"USD"}}' + )->willReturn( [ + 200, 'application/json', file_get_contents( + __DIR__ . '/includes/Responses/minFraud/15points.json' + ) + ] ); - public function output() { - return array(); - } + $gateway->runAntifraudFilters(); + + $this->assertEquals( 'challenge', $gateway->getValidationAction(), 'Validation action is not as expected' ); + $exposed = TestingAccessWrapper::newFromObject( $gateway ); + $this->assertEquals( 72.75, $exposed->risk_score, 'RiskScore is not as expected for failure mode' ); + $message = QueueWrapper::getQueue( 'payments-antifraud' )->pop(); + SourceFields::removeFromMessage( $message ); + $expected = array( + 'validation_action' => 'challenge', + 'risk_score' => 72.75, + 'score_breakdown' => array( + 'initial' => 0, + 'getScoreUtmCampaignMap' => 0, + 'getScoreCountryMap' => 20, + 'getScoreUtmSourceMap' => 0, + 'getScoreUtmMediumMap' => 0, + 'getScoreEmailDomainMap' => 37.5, + 'getCVVResult' => 0, + 'getAVSResult' => 0, + 'minfraud_filter' => 15.25, + ), + 'user_ip' => '127.0.0.1', + 'gateway_txn_id' => false, + 'date' => $message['date'], + 'server' => gethostname(), + 'gateway' => 'globalcollect', + 'contribution_tracking_id' => $gateway->getData_Unstaged_Escaped( 'contribution_tracking_id' ), + 'order_id' => $gateway->getData_Unstaged_Escaped( 'order_id' ), + 'payment_method' => 'cc', + ); + $this->assertEquals( $expected, $message ); } } diff --git a/tests/phpunit/includes/Responses/minFraud/15points.json b/tests/phpunit/includes/Responses/minFraud/15points.json new file mode 100644 index 0000000..35bc1cb --- /dev/null +++ b/tests/phpunit/includes/Responses/minFraud/15points.json @@ -0,0 +1,17 @@ +{ + "id": "5bc5d6c2-b2c8-40af-87f4-6d61af86b6ae", + "risk_score": 15.25, + "funds_remaining": 250.00, + "queries_remaining": 500000, + + "ip_address": { + "risk": 15.25 + }, + + "disposition": { + "action": "accept", + "reason": "default" + }, + + "warnings": [] +} -- To view, visit https://gerrit.wikimedia.org/r/275043 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I3b48de91ac417cf245fd23a5bfcf80c7cb3bbad2 Gerrit-PatchSet: 14 Gerrit-Project: mediawiki/extensions/DonationInterface Gerrit-Branch: master Gerrit-Owner: Ejegg <ej...@ejegg.com> Gerrit-Reviewer: AndyRussG <andrew.green...@gmail.com> Gerrit-Reviewer: Cdentinger <cdentin...@wikimedia.org> Gerrit-Reviewer: Ejegg <ej...@ejegg.com> Gerrit-Reviewer: Mepps <me...@wikimedia.org> Gerrit-Reviewer: XenoRyet <dkozlow...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits