r70919 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r70918‎ | r70919 | r70920 >
Date:21:35, 11 August 2010
Author:awjrichards
Status:deferred (Comments)
Tags:
Comment:
Added conversion log script for payflowpro post processing
Added recaptcha script to display captcha on 'challenge' action in payflowpro gateway
Added abstract 'extras' class to handle shared functions like logging and hash generation for extras
Split up form generation in payflow pro gateway to facilitate possible form manipulation by extras
Updated minfraud to use new abstract extras class
Updated associated minfraud tests
Exposed a hook in payflowpro gateway for 'post procesing' which is available after a transaction is sent to payflow
Modified paths:
  • /trunk/extensions/DonationInterface/payflowpro_gateway/extras/conversion_log (added) (history)
  • /trunk/extensions/DonationInterface/payflowpro_gateway/extras/conversion_log/conversion_log.php (added) (history)
  • /trunk/extensions/DonationInterface/payflowpro_gateway/extras/extras.php (added) (history)
  • /trunk/extensions/DonationInterface/payflowpro_gateway/extras/minfraud/minfraud.php (modified) (history)
  • /trunk/extensions/DonationInterface/payflowpro_gateway/extras/minfraud/tests/minfraudTest.php (modified) (history)
  • /trunk/extensions/DonationInterface/payflowpro_gateway/extras/recaptcha (added) (history)
  • /trunk/extensions/DonationInterface/payflowpro_gateway/extras/recaptcha/recaptcha.php (added) (history)
  • /trunk/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.body.php (modified) (history)

Diff [purge]

Index: trunk/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.body.php
@@ -17,6 +17,12 @@
1818 public $action = NULL;
1919
2020 /**
 21+ * Holds the information from a Payflow transaction
 22+ * @var array
 23+ */
 24+ public $payflow_transaction = NULL;
 25+
 26+ /**
2127 * Constructor - set up the new special page
2228 */
2329 public function __construct() {
@@ -94,6 +100,8 @@
95101 'optout' => '',
96102 'token' => $token,
97103 'contribution_tracking_id' => '',
 104+ 'data_hash' => '',
 105+ 'action' => '',
98106 );
99107
100108 $error[] = '';
@@ -112,7 +120,6 @@
113121
114122 // track the number of attempts the user has made
115123 $numAttempt = ( $wgRequest->getText( 'numAttempt' ) == '' ) ? '0' : $wgRequest->getText( 'numAttempt' );
116 -
117124 // Populate from data
118125 $data = array(
119126 'amount' => $amount,
@@ -131,7 +138,7 @@
132139 'cvv' => $wgRequest->getText( 'cvv' ),
133140 'currency' => $wgRequest->getText( 'currency_code' ),
134141 'payment_method' => $wgRequest->getText( 'payment_method' ),
135 - 'order_id' => null, //will be set with $payflow_data
 142+ 'order_id' => $wgRequest->getText( 'orderid' ), //will be set with $payflow_data
136143 'numAttempt' => $numAttempt,
137144 'referrer' => $wgRequest->getText( 'referrer' ),
138145 'utm_source' => $wgRequest->getText( 'utm_source' ),
@@ -143,6 +150,8 @@
144151 'optout' => $wgRequest->getText( 'email' ),
145152 'test_string' => $wgRequest->getText( 'process' ), //for showing payflow string during testing
146153 'contribution_tracking_id' => $wgRequest->getText( 'contribution_tracking_id' ),
 154+ 'data_hash' => $wgRequest->getText( 'data_hash' ),
 155+ 'action' => $wgRequest->getText( 'action' ),
147156 );
148157
149158 // Get array of default account values necessary for Payflow
@@ -151,7 +160,7 @@
152161 $payflow_data = payflowUser();
153162
154163 // assign this order ID to the $data array as well
155 - $data['order_id'] = $payflow_data['order_id'];
 164+ if ( strlen( $data['order_id'] ) < 1 ) $data['order_id'] = $payflow_data['order_id'];
156165
157166 // Check form for errors and display
158167 // match token
@@ -171,31 +180,34 @@
172181 wfRunHooks( 'PayflowGatewayValidate', array( &$this, &$data ));
173182
174183 // if the transaction was flagged for review
175 - if ( in_array( 'review', $this->actions )) {
 184+ if ( $this->action == 'review' ) {
176185 // expose a hook for external handling of trxns flagged for review
177 - wfRunHooks( 'PayflowGatewayReview', array( &$this, $data ));
 186+ wfRunHooks( 'PayflowGatewayReview', array( &$this, &$data ));
178187 }
179188
180189 // if the transaction was flagged to be 'challenged'
181 - if ( in_array( 'challenge', $this->actions )) {
 190+ if ( $this->action == 'challenge' ) {
182191 // expose a hook for external handling of trxns flagged for challenge (eg captcha)
183 - wfRunHooks( 'PayflowGatewayChallenge', array( &$this, $data ));
 192+ wfRunHooks( 'PayflowGatewayChallenge', array( &$this, &$data ));
184193 }
185194
186195 // if the transaction was flagged for rejection
187 - if ( in_array( 'reject', $this->actions )) {
 196+ if ( $this->action == 'reject' ) {
188197 // expose a hook for external handling of trxns flagged for rejection
189 - wfRunHooks( 'PayflowGatewayReject', array( &$this, $data ));
 198+ wfRunHooks( 'PayflowGatewayReject', array( &$this, &$data ));
190199
191200 $this->fnPayflowDisplayDeclinedResults( '' );
192201 }
193202
194203 // if the transaction was flagged for processing
195 - if ( in_array( 'process', $this->actions )) {
 204+ if ( $this->action == 'process' ) {
196205 // expose a hook for external handling of trxns ready for processing
197 - wfRunHooks( 'PayflowGatewayProcess', array( &$this, $data ));
 206+ wfRunHooks( 'PayflowGatewayProcess', array( &$this, &$data ));
198207 $this->fnPayflowProcessTransaction( $data, $payflow_data );
199208 }
 209+
 210+ // expose a hook for any post processing
 211+ wfRunHooks( 'PayflowGatewayPostProcess', array( &$this, &$data ));
200212 }
201213 } else {
202214 //Display form for the first time
@@ -205,14 +217,14 @@
206218 }
207219
208220 /**
209 - * Displays form to user
 221+ * Build the submission form sans submit button
210222 *
211 - * @param $data Array: array of posted user input
212 - * @param $error Array: array of error messages returned by validate_form function
213 - *
214 - * The message at the top of the form can be edited in the payflow_gateway.i18.php file
 223+ * This allows for additional form elements/processing to be handled
 224+ * by extra modules (eg during 'challenge' action)
 225+ *
 226+ * See $this->fnPayflowDisplayForm
215227 */
216 - private function fnPayflowDisplayForm( $data, &$error ) {
 228+ public function fnPayflowGenerateFormBody( $data, &$error ) {
217229 require_once( 'includes/stateAbbreviations.inc' );
218230 require_once( 'includes/countryCodes.inc' );
219231
@@ -428,10 +440,19 @@
429441 Xml::hidden( 'token', $data['token'] ) .
430442 Xml::hidden( 'orderid', $data['order_id'] ) .
431443 Xml::hidden( 'numAttempt', $data['numAttempt'] ) .
432 - Xml::hidden( 'contribution_tracking_id', $data['contribution_tracking_id'] );
433 -
 444+ Xml::hidden( 'contribution_tracking_id', $data['contribution_tracking_id'] ) .
 445+ Xml::hidden( 'data_hash', $data[ 'data_hash' ] ) .
 446+ Xml::hidden( 'action', $data[ 'action' ] );
 447+ return $form;
 448+ }
 449+
 450+ /**
 451+ * Build the submit portion of the submission form
 452+ * See $this->fnPayflowDiplayForm
 453+ */
 454+ public function fnPayflowGenerateFormSubmit( $data, &$error ) {
434455 // submit button and close form
435 - $form .= Xml::openElement( 'div', array( 'class' => 'mw-donate-submessage' ) ) .
 456+ $form = Xml::openElement( 'div', array( 'class' => 'mw-donate-submessage' ) ) .
436457 wfMsg( 'payflowpro_gateway-donate-click' ) .
437458 Xml::tags( 'div', array( 'id' => 'mw-donate-submit-button' ),
438459 Xml::submitButton( wfMsg( 'payflowpro_gateway-submit-button' ) ) ) .
@@ -442,7 +463,21 @@
443464 Xml::closeElement( 'form' ) .
444465 Xml::closeElement( 'div' ) .
445466 Xml::closeElement( 'div' );
 467+ return $form;
 468+ }
446469
 470+ /**
 471+ * Build and displays form to user
 472+ *
 473+ * @param $data Array: array of posted user input
 474+ * @param $error Array: array of error messages returned by validate_form function
 475+ *
 476+ * The message at the top of the form can be edited in the payflow_gateway.i18.php file
 477+ */
 478+ public function fnPayflowDisplayForm( $data, &$error ) {
 479+ global $wgOut;
 480+ $form = $this->fnpayflowGenerateFormBody( &$data, &$error );
 481+ $form .= $this->fnPayflowGenerateFormSubmit( &$data, &$error );
447482 $wgOut->addHTML( $form );
448483 }
449484
@@ -581,7 +616,7 @@
582617 'LASTNAME' => $data['lname'],
583618 'STREET' => $data['street'],
584619 'ZIP' => $data['zip'],
585 - 'INVNUM' => $payflow_data['order_id'],
 620+ 'INVNUM' => $data['order_id'],
586621 'CVV2' => $data['cvv'],
587622 'CURRENCY' => $data['currency'],
588623 'VERBOSITY' => $payflow_data['verbosity'],
@@ -601,7 +636,7 @@
602637 $headers[] = 'Content-Type: text/namevalue';
603638 $headers[] = 'Content-Length : ' . strlen( $payflow_query );
604639 $headers[] = 'X-VPS-Client-Timeout: 45';
605 - $headers[] = 'X-VPS-Request-ID:' . $payflow_data['order_id'];
 640+ $headers[] = 'X-VPS-Request-ID:' . $data['order_id'];
606641 $ch = curl_init();
607642 $paypalPostTo = isset ( $wgDonationTestingMode ) ? 'testingurl' : 'paypalurl';
608643 curl_setopt( $ch, CURLOPT_URL, $payflow_data[ $paypalPostTo ] );
@@ -794,6 +829,9 @@
795830 // hook to call stomp functions
796831 wfRunHooks( 'gwStomp', array( &$transaction ) );
797832
 833+ // set the object property for the transaction
 834+ $this->payflow_transaction = $transaction;
 835+
798836 if ( $wgExternalThankYouPage ) {
799837 $wgOut->redirect( $wgExternalThankYouPage . "/" . $data['language'] );
800838 } else {
Index: trunk/extensions/DonationInterface/payflowpro_gateway/extras/minfraud/tests/minfraudTest.php
@@ -4,7 +4,8 @@
55 class minfraudTest extends PHPUnit_Framework_TestCase
66 {
77 protected function setUp() {
8 - require_once( __FILE__ . '/../../minfraud.php');
 8+ $dir = dirname( __FILE__ ) . '/';
 9+ require_once( $dir . '../minfraud.php');
910 global $wgMinFraudLog;
1011 $wgMinFraudLog = dirname(__FILE__) . "/test_log";
1112 $license_key = 'XBCKSF4gnHA7';
@@ -149,9 +150,9 @@
150151
151152 public function testLogging() {
152153 global $wgMinFraudLog;
153 - $this->fixture->log( "foo" );
 154+ $this->fixture->log( '', '', "\"foo\"" );
154155 $new_fh = fopen( $wgMinFraudLog, 'r' );
155 - $this->assertEquals("foo\n", fread( $new_fh, filesize( $wgMinFraudLog ) ));
 156+ $this->assertEquals('"'.date('c').'"'."\t\"\"\t\"\"\t\"foo\"\n", fread( $new_fh, filesize( $wgMinFraudLog ) ));
156157 fclose( $new_fh );
157158 }
158159
@@ -173,12 +174,12 @@
174175 $wgMinFraudSalt = 'salt';
175176 $data = array(
176177 'action' => '4bd7857c851039d1e07a434800fe752c6bd99aec61c325aef460441be1b95c3ab5236e43c8d06f41d77715dbd3cf94e679b86422ec3204f00ad433501e5005e9',
177 - 'data_hash' => '8a42092df24db59b67ab2c6408e00553de93e79dc0d77467f5b91501a392cff99922a0b24f9d0cc5853ae874bbc79c20eb22814ce3a912d743d4137b1f795ac3',
 178+ 'data_hash' => '029ef6f5c2a165215b5a92ff1a194e4a6de8c668d6193582da42713f119c1b07d8358b5cd94a3bd51c9aa50709c8533295215ce3cce8c2b61e69078d789bc3f3',
178179 'foo'
179180 );
180181 $this->assertTrue( $this->fixture->bypass_minfraud( &$this->fixture, &$data ));
181182 $this->assertEquals( 'challenge', $this->fixture->action );
182 - $this->assertEquals( '8a42092df24db59b67ab2c6408e00553de93e79dc0d77467f5b91501a392cff99922a0b24f9d0cc5853ae874bbc79c20eb22814ce3a912d743d4137b1f795ac3', $data[ 'data_hash' ]);
 183+ $this->assertEquals( '029ef6f5c2a165215b5a92ff1a194e4a6de8c668d6193582da42713f119c1b07d8358b5cd94a3bd51c9aa50709c8533295215ce3cce8c2b61e69078d789bc3f3', $data[ 'data_hash' ]);
183184
184185 $data[] = 'bar';
185186 $this->assertFalse( $this->fixture->bypass_minfraud( &$this->fixture, &$data ));
Index: trunk/extensions/DonationInterface/payflowpro_gateway/extras/minfraud/minfraud.php
@@ -1,6 +1,11 @@
22 <?php
 3+/**
 4+ * Validates a transaction against MaxMind's minFraud service
 5+ */
36
4 -class PayflowProGateway_Extras_MinFraud {
 7+$dir = dirname( __FILE__ ) . "/";
 8+require_once( $dir . "../extras.php" );
 9+class PayflowProGateway_Extras_MinFraud extends PayflowProGateway_Extras {
510
611 /**
712 * Full response from minFraud
@@ -15,13 +20,9 @@
1621 public $minfraud_license_key = NULL;
1722
1823 /**
19 - * File handle for log file
20 - * @var public
21 - */
22 - public $log_fh = NULL;
23 -
24 - /**
2524 * User-definable riskScore ranges for actions to take
 25+ *
 26+ * Overload with $wgMinFraudActionRanges
2627 * @var public array
2728 */
2829 public $action_ranges = array(
@@ -32,9 +33,11 @@
3334 );
3435
3536 function __construct( $license_key = NULL ) {
36 - require_once( __FILE__ . "/../ccfd/CreditCardFraudDetection.php" );
 37+ parent::__construct();
 38+ $dir = dirname( __FILE__ ) .'/';
 39+ require_once( $dir . "ccfd/CreditCardFraudDetection.php" );
3740
38 - global $wgMinFraudLicenseKey, $wgMinFraudActionRanges, $wgMinFraudLog;
 41+ global $wgMinFraudLicenseKey, $wgMinFraudActionRanges;
3942
4043 // set the minfraud license key, go no further if we don't have it
4144 if ( !$license_key && !$wgMinFraudLicenseKey ) {
@@ -43,9 +46,6 @@
4447 $this->minfraud_license_key = ( $license_key ) ? $license_key : $wgMinFraudLicenseKey;
4548
4649 if ( isset( $wgMinFraudActionRanges )) $this->action_ranges = $wgMinFraudActionRanges;
47 -
48 - // prepare the log file if the user has specified one
49 - if ( $wgMinFraudLog ) $this->prepare_log_file( $wgMinFraudLog );
5050 }
5151
5252 /**
@@ -63,23 +63,19 @@
6464 $this->query_minfraud( $minfraud_hash );
6565 $pfp_gateway_object->action = $this->determine_action( $this->minfraud_response[ 'riskScore' ] );
6666
67 - // reset the data hash and the action
 67+ // reset the data hash
6868 if ( isset( $data[ 'data_hash' ] )) unset( $data[ 'data_hash' ] );
69 - if ( isset( $data[ 'action' ] )) unset( $data[ 'action' ] );
70 - $data[ 'data_hash' ] = $this->genereate_hash( serialize( $data ));
71 - $data[ 'action' ] = $this->genereate_hash( $pfp_gateway_object->action );
 69+ $data[ 'action' ] = $this->generate_hash( $pfp_gateway_object->action );
 70+ $data[ 'data_hash' ] = $this->generate_hash( serialize( $data ));
7271
7372 // log the message if the user has specified a log file
7473 if ( $this->log_fh ) {
75 - $log_message = '"'. date('c') . '"';
76 - $log_message .= "\t" . '"' . $data[ 'contribution_tracking_id' ] . '"';
77 - $log_message .= "\t" . '"' . "minFraud query" . '"';
78 - $log_message .= "\t" . '"' . $data[ 'comment' ] . '"';
 74+ $log_message = '"' . $data[ 'comment' ] . '"';
7975 $log_message .= "\t" . '"' . $data[ 'amount' ] . ' ' . $data[ 'currency' ] . '"';
8076 $log_message .= "\t" . '"' . serialize( $minfraud_hash ) . '"';
8177 $log_message .= "\t" . '"' . serialize( $this->minfraud_response ) . '"';
8278 $log_message .= "\t" . '"' . serialize( $pfp_gateway_object->actions ) . '"';
83 - $this->log( $log_message );
 79+ $this->log( $data[ 'contribution_tracking_id' ], 'minFraud query', $log_message );
8480 }
8581 return TRUE;
8682 }
@@ -99,19 +95,21 @@
10096 */
10197 public function bypass_minfraud( &$pfp_gateway_object, &$data ) {
10298 // if the data bits data_hash and action are not set, we need to hit minFraud
103 - if ( isset( $data[ 'data_hash' ] ) && isset( $data[ 'action' ] )) {
 99+ if ( strlen( $data[ 'data_hash' ] ) > 0 && strlen( $data[ 'action' ] ) > 0 ) {
104100 $data_hash = $data[ 'data_hash' ]; // the data hash passed in by the form submission
105 - $action_hash = $data[ 'action' ]; // a hash of the action to take passed in by the form submission
106 -
 101+ $num_attempt = $data[ 'numAttempt' ]; // the num_attempt has been increased by one, so we have to adjust slightly
 102+ $data[ 'numAttempt' ] = $num_attempt - 1;
 103+
107104 // unset these values from the data aray since they are not part of the overall data hash
108105 unset( $data[ 'data_hash' ] );
109 - unset( $data[ 'action' ] );
110106 // compare the data hash to make sure it's legit
111107 if ( $this->compare_hash( $data_hash, serialize( $data ))) {
112 - $data[ 'data_hash' ] = $data_hash;
 108+ $data[ 'numAttempt' ] = $num_attempt; // reset the current num attempt
 109+ $data[ 'data_hash' ] = $this->generate_hash( serialize( $data )); // hash the data array
113110
114111 // check to see if we have a valid action set for us to bypass minfraud
115112 $actions = array( 'process', 'challenge', 'review', 'reject' );
 113+ $action_hash = $data[ 'action' ]; // a hash of the action to take passed in by the form submission
116114 foreach ( $actions as $action ) {
117115 if ( $this->compare_hash( $action_hash, $action )) {
118116 // set the action that should be taken
@@ -119,6 +117,9 @@
120118 return TRUE;
121119 }
122120 }
 121+ } else {
 122+ // log potential tamporing
 123+ if ( $this->log_fh ) $this->log( $data[ 'contribution_tracking_id'], 'Data hash/action mismatch' );
123124 }
124125 }
125126 return FALSE;
@@ -238,61 +239,4 @@
239240 }
240241 }
241242 }
242 -
243 - /**
244 - * Prepares a log file
245 - *
246 - * @param string path to log file
247 - * @return resource Pointer for the log file
248 - */
249 - protected function prepare_log_file( $log_file ) {
250 - $this->log_fh = fopen( $log_file, 'a+' );
251 - }
252 -
253 - /**
254 - * Writes message to a log file
255 - *
256 - * If a log file does not exist and could not be created,
257 - * do nothing.
258 - * @fixme Perhaps lack of log file can be handled better,
259 - * or maybe it doesn't matter?
260 - * @param string The message to log
261 - */
262 - public function log( $msg ) {
263 - if ( !$this->log_fh ) {
264 - return;
265 - }
266 - $msg .= "\n";
267 - fwrite( $this->log_fh, $msg );
268 - }
269 -
270 - /**
271 - * Generate a hash of some data
272 - * @param string the data to hash
273 - * @return string The hash of the data
274 - */
275 - public function generate_hash( $data ) {
276 - global $wgMinFraudSalt;
277 - return hash( "sha512", $wgMinFraudSalt . $data );
278 - }
279 -
280 - /**
281 - * Compare a hash to the hash of some given data
282 - * @param string $hash A given hash
283 - * @param strin $data The data to hash and compare to $hash
284 - * @return bool
285 - */
286 - public function compare_hash( $hash, $data ) {
287 - if ( $hash == $this->generate_hash( $data )) {
288 - return TRUE;
289 - }
290 - return FALSE;
291 - }
292 -
293 - /**
294 - * Close the open log file handler if it's open
295 - */
296 - public function __destruct() {
297 - if ( $this->log_fh ) fclose( $this->log_fh );
298 - }
299243 }
Index: trunk/extensions/DonationInterface/payflowpro_gateway/extras/conversion_log/conversion_log.php
@@ -0,0 +1,22 @@
 2+<?php
 3+/**
 4+ * Extra to log payflow conversion during post processing hook
 5+ */
 6+require_once( dirname( __FILE__ ) . "/../extras.php" );
 7+class PayflowProGateway_Extras_ConversionLog extends PayflowProGateway_Extras {
 8+
 9+ /**
 10+ * Logs the transaction info for a conversion for payflow
 11+ */
 12+ public function post_process( &$pfp_gateway_object, &$data ) {
 13+ //make sure the transaction property has been set (signifying conversion)
 14+ if ( !$pfp_gateway_object->payflow_transaction ) return FALSE;
 15+
 16+ $this->log(
 17+ $data[ 'contribution_tracking_id' ],
 18+ 'Conversion',
 19+ '"' . $pfp_gateway_object->payflow_transaction[ 'PNREF' ] . '"'
 20+ );
 21+ return TRUE;
 22+ }
 23+}
Index: trunk/extensions/DonationInterface/payflowpro_gateway/extras/recaptcha/recaptcha.php
@@ -0,0 +1,51 @@
 2+<?php
 3+/**
 4+ * Validates a transaction against MaxMind's minFraud service
 5+ */
 6+
 7+require_once( dirname( __FILE__ ) . "/../extras.php" );
 8+class PayflowProGateway_Extras_reCaptcha extends PayflowProGateway_Extras {
 9+ /**
 10+ * Handle the challenge logic
 11+ */
 12+ public function challenge( &$pfp_gateway_object, &$data ) {
 13+ // if captcha posted, validate
 14+ if ( isset( $_POST[ 'recaptcha_response_field' ] )) {
 15+ if ( $this->validate_captcha() ){
 16+ // if validated, update the action and move on
 17+ $this->log( $data[ 'contribution_tracking_id' ], 'Captcha passed' );
 18+ $pfp_gateway_object->action = "process";
 19+ return TRUE;
 20+ } else {
 21+ $this->log( $data[ 'contribution_tracking_id' ], 'Captcha failed' );
 22+ }
 23+ }
 24+ // display captcha
 25+ $this->display_captcha( &$pfp_gateway_object, &$data );
 26+ return TRUE;
 27+ }
 28+
 29+ /**
 30+ * Display the submission form with the captcha injected into it
 31+ */
 32+ public function display_captcha( &$pfp_gateway_object, &$data, $error=array() ) {
 33+ global $wgOut, $wgPayflowCaptcha;
 34+ $form = $pfp_gateway_object->fnPayflowGenerateFormBody( $data, &$error );
 35+ $form .= Xml::openElement( 'div', array( 'id' => 'mw-donate-captcha' ));
 36+
 37+ // get the captcha
 38+ $form .= $wgPayflowCaptcha->getForm();
 39+ $form .= '<span class="creditcard-error-msg">' . wfMsg( 'payflowpro_gateway-error-msg-captcha-please') . '</span>';
 40+ $form .= Xml::closeElement( 'div' );
 41+ $form .= $pfp_gateway_object->fnPayflowGenerateFormSubmit( $data, &$error );
 42+ $wgOut->addHTML( $form );
 43+ }
 44+
 45+ /**
 46+ * Wrapper for validating the captcha submission
 47+ */
 48+ public function validate_captcha() {
 49+ global $wgPayflowCaptcha;
 50+ return $wgPayflowCaptcha->passCaptcha();
 51+ }
 52+}
Index: trunk/extensions/DonationInterface/payflowpro_gateway/extras/extras.php
@@ -0,0 +1,75 @@
 2+<?php
 3+
 4+abstract class PayflowProGateway_Extras {
 5+ /**
 6+ * File handle for log file
 7+ * @var public
 8+ */
 9+ public $log_fh = NULL;
 10+
 11+ public function __construct() {
 12+ global $wgPayflowGatewayLog;
 13+ // prepare the log file if the user has specified one
 14+ if ( $wgPayflowGatewayLog ) $this->prepare_log_file( $wgPayflowGatewayLog );
 15+ }
 16+
 17+ /**
 18+ * Prepared a log file
 19+ *
 20+ * @param string path to log file
 21+ * @return resource Pointer for the log file
 22+ */
 23+ protected function prepare_log_file( $log_file ){
 24+ $this->log_fh = fopen( $log_file, 'a+' );
 25+ }
 26+
 27+ /**
 28+ * Writes message to a log file
 29+ *
 30+ * If a log file does not exist and could not be created,
 31+ * do nothing.
 32+ * @fixme Perhaps lack of log file can be handled better?
 33+ * @param string The message to log
 34+ */
 35+ public function log( $id='', $status='', $data='' ) {
 36+ if ( !$this->log_fh ) {
 37+ return;
 38+ }
 39+ $msg = '"' . date( 'c' ) . '"';
 40+ $msg .= "\t" . '"' . $id . '"';
 41+ $msg .= "\t" . '"' . $status . '"';
 42+ $msg .= "\t" . $data . "\n";
 43+ fwrite( $this->log_fh, $msg );
 44+ }
 45+
 46+ /**
 47+ * Generate a hash of some data
 48+ * @param string the data to hash
 49+ * @return string The hash of the data
 50+ */
 51+ public function generate_hash( $data ) {
 52+ global $wgPayflowGatewaySalt;
 53+ return hash( "sha512", $wgPayflowGatewaySalt . $data );
 54+ }
 55+
 56+ /**
 57+ * Compare a hash to the hash of some given data
 58+ * @param string $hash A given hash
 59+ * @param string $data The data to hash and compare to $hash
 60+ * @return bool
 61+ */
 62+ public function compare_hash( $hash, $data ) {
 63+ if ( $hash == $this->generate_hash( $data )) {
 64+ return TRUE;
 65+ }
 66+
 67+ return FALSE;
 68+ }
 69+
 70+ /**
 71+ * Cloase the open log file handler if it's open
 72+ */
 73+ public function __destruct() {
 74+ if ( $this->log_fh ) fclose( $this->log_fh );
 75+ }
 76+}

Comments

#Comment by Tim Starling (talk | contribs)   07:20, 16 August 2010

In conversion_log.php and recaptcha.php: same comments here as to standard MW extension style as in minfraud.php. Set $wgHooks in the extension setup file, don't make the user do it. Don't create objects before MW is fully set up and the relevant functionality is required. Defer the loading of as much code as possible using the autoloader.

		$this->display_captcha( &$pfp_gateway_object, &$data );

Using ampersands in function calls to force arguments to be references is deprecated and will generate a warning in PHP 5.3.0+, see http://www.php.net/manual/en/language.references.pass.php . You should use an ampersand only in the function declaration. I think there were some more examples of this elsewhere in your code, you should fix it everywhere.


#Comment by Awjrichards (talk | contribs)   20:05, 26 August 2010

Fixed in r71725

#Comment by Tim Starling (talk | contribs)   08:02, 16 August 2010

You will need to change this to use recaptchalib directly. The MediaWiki reCAPTCHA plugin bundles its own copy of ConfirmEdit, which will conflict with the ConfirmEdit already installed on Wikimedia websites. Including the extension setup file for the MW reCAPTCHA extension has unintended consequences, such as adding a captcha to edit and account creation forms. The code here will be basically the same: the MW plugin is a very thin wrapper around recaptchalib, basically the only things it adds are things we don't want.

#Comment by Awjrichards (talk | contribs)   20:04, 26 August 2010

Fixed in r71735

#Comment by MZMcBride (talk | contribs)   20:08, 26 August 2010
#Comment by Awjrichards (talk | contribs)   00:23, 28 August 2010

In this particular case (the credit card gateway in the Foundation's fundraising system), reCaptcha (or any other captcha solution we might use) will only be seen by a small minority of users.

If we could find an open source PHP captcha solution that works as well as reCaptcha, I'd love to use it instead. However, the existing solutions that I've been able to find have weaknesses that are exploitable while reCaptcha does not. Because of the sensitivity of the data going through our credit card processing pipeline and the need to reduce fraudulent uses of our gateway, this is of particular concern. We've also ruled out using the FancyCaptcha solution that's packaged with the ConfirmEdit extension because of the Python dependency and the issues that come with that (http://www.mediawiki.org/wiki/Extension:ConfirmEdit#How_to_avoid_common_problems_running_Python).

For these reasons (plus reCaptcha's free culture roots) make reCaptcha the current logical choice for this particular implementation.

If someone were willing to port FancyCaptcha to natively use PHP or someone found a reasonable, php-based open source captcha solution that provided the same (or better) level of security as reCaptcha, we'd love to use it.

#Comment by Platonides (talk | contribs)   23:22, 8 October 2010

> We've also ruled out using the FancyCaptcha solution that's packaged with the ConfirmEdit extension because of the Python dependency and the issues that come with that > If someone were willing to port FancyCaptcha to natively use PHP or someone found a reasonable, php-based open source captcha solution that provided the same (or better) level of security as reCaptcha, we'd love to use it.

It's a working solution for us, already working in the servers.

From [1] only "does not work on new python versions" seems relevant, but that was fixed a year ago in r56008. It works fine here in 2.6.5.

You will need python + PIL, but that's what packages are for (and there's already a good bunch of generated images at the servers, but I understand you may want to use different ones).

It would obviously be nice to have that directly in php but rejecting python for favoring an external server is wrong IMHO.

#Comment by Tim Starling (talk | contribs)   01:40, 11 October 2010

You don't need to be able to use Python to use FancyCaptcha, you can just use the existing captcha directory which is available by NFS in the Wikimedia network. But in any case, it's not a problem to use Python at Wikimedia, we do lots of things with it.

As a closed-source project operated by a commercial entity, reCaptcha's "free culture roots" are well-hidden.

#Comment by Ryan Kaldari (WMF) (talk | contribs)   01:21, 28 August 2010

As I understand it, the reCAPTCHA CAPTCHA will only be displayed to users that trigger the fraud flag, so the external server dependency isn't much of an issue. If the reCAPTCHA servers went down, we might loose a small handful of donations, but that would be the worst case scenario. I think Arthur covered the other issues above. Given the options for this particular problem (both closed and open source), reCAPTCHA seems to be the best solution, especially given the timeframe that we need to get this into place by.

Status & tagging log