r95923 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r95922‎ | r95923 | r95924 >
Date:00:43, 1 September 2011
Author:khorn
Status:ok (Comments)
Tags:fundraising 
Comment:
More refactoring. The new globalcollect gateway is now inheriting from the common gateway abstract class.
(Also cleaned up the formatting more than somewhat)
Modified paths:
  • /branches/fundraising/extensions/DonationInterface/donationinterface.php (modified) (history)
  • /branches/fundraising/extensions/DonationInterface/gateway_common (added) (history)
  • /branches/fundraising/extensions/DonationInterface/gateway_common/gateway.adapter.php (added) (history)
  • /branches/fundraising/extensions/DonationInterface/globalcollect_gateway/globalcollect.adapter.php (modified) (history)
  • /branches/fundraising/extensions/DonationInterface/globalcollect_gateway/globalcollect_gateway.php (modified) (history)
  • /branches/fundraising/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.php (modified) (history)
  • /branches/fundraising/extensions/DonationInterface/tests (added) (history)
  • /branches/fundraising/extensions/DonationInterface/tests/DonationInterfaceTest.php (added) (history)

Diff [purge]

Index: branches/fundraising/extensions/DonationInterface/gateway_common/gateway.adapter.php
@@ -0,0 +1,380 @@
 2+<?php
 3+
 4+interface GatewayType {
 5+ //all the particulars of the child classes. Aaaaall.
 6+
 7+ /**
 8+ * return either 'xml' or 'namevalue', depending on the gateway's web API
 9+ */
 10+ function getCommunicationType();
 11+
 12+ /**
 13+ * Pretty sure it's not worth it to even try to abstract this.
 14+ * ...hm. Too general? I'm thinking "maybe".
 15+ * Actually: Yes. So...
 16+ * TODO: Get way more specific here.
 17+ */
 18+ function parseXMLResponse( $rawResponse );
 19+}
 20+
 21+abstract class GatewayAdapter implements GatewayType {
 22+
 23+ //Contains the map of THEIR var names, to OURS.
 24+ //I'd have gone the other way, but we'd run into 1:many pretty quick.
 25+ protected $var_map;
 26+ protected $accountInfo;
 27+ protected $url;
 28+ protected $transactions;
 29+ protected $postdata;
 30+ protected $postdatadefaults;
 31+ protected $xmlDoc;
 32+
 33+ const logidentifier = 'donation_gateway';
 34+
 35+ function getValue( $gateway_field_name ) {
 36+ if ( empty( $this->transactions ) ) {
 37+ //TODO: These dies should all just throw fatal errors instead.
 38+ die( 'Transactions structure is empty! Aborting.' );
 39+ }
 40+ //How do we determine the value of a field asked for in a particular transaction?
 41+ $transaction = $this->currentTransaction();
 42+
 43+ //If there's a hard-coded value in the transaction definition, use that.
 44+ if ( array_key_exists( $transaction, $this->transactions ) && is_array( $this->transactions[$transaction] ) &&
 45+ array_key_exists( 'values', $this->transactions[$transaction] ) &&
 46+ array_key_exists( $gateway_field_name, $this->transactions[$transaction]['values'] ) ) {
 47+ return $this->transactions[$transaction]['values'][$gateway_field_name];
 48+ }
 49+
 50+ //if it's account info, use that.
 51+ //$this->accountInfo;
 52+ if ( array_key_exists( $gateway_field_name, $this->accountInfo ) ) {
 53+ return $this->accountInfo[$gateway_field_name];
 54+ }
 55+
 56+
 57+ //If there's a value in the post data (name-translated by the var_map), use that.
 58+ if ( array_key_exists( $gateway_field_name, $this->var_map ) ) {
 59+ if ( array_key_exists( $this->var_map[$gateway_field_name], $this->postdata ) &&
 60+ $this->postdata[$this->var_map[$gateway_field_name]] !== '' ) {
 61+ //if it was sent, use that.
 62+ return $this->postdata[$this->var_map[$gateway_field_name]];
 63+ } else {
 64+ //return the default for that form value
 65+ return $this->postdatadefaults[$this->var_map[$gateway_field_name]];
 66+ }
 67+ }
 68+
 69+ //not in the map, or hard coded. What then?
 70+ //Complain furiously, for your code is faulty.
 71+ //TODO: Something that plays nice with others, instead of...
 72+ die( "getValue found NOTHING for $gateway_field_name, $transaction." );
 73+ }
 74+
 75+ function buildRequestXML() {
 76+ $this->xmlDoc = new DomDocument( '1.0' );
 77+ $node = $this->xmlDoc->createElement( 'XML' );
 78+
 79+ $structure = $this->transactions[$this->currentTransaction()]['request'];
 80+
 81+ $this->buildTransactionNodes( $structure, $node );
 82+ $this->xmlDoc->appendChild( $node );
 83+ return $this->xmlDoc->saveXML();
 84+ }
 85+
 86+ function buildTransactionNodes( $structure, &$node ) {
 87+ $transaction = $this->currentTransaction();
 88+
 89+ if ( !is_array( $structure ) ) { //this is a weird case that shouldn't ever happen. I'm just being... thorough. But, yeah: It's like... the base-1 case.
 90+ $this->appendNodeIfValue( $structure, $node );
 91+ } else {
 92+ foreach ( $structure as $key => $value ) {
 93+ if ( !is_array( $value ) ) {
 94+ //do not use $key. $key is meaningless in this case.
 95+ $this->appendNodeIfValue( $value, $node );
 96+ } else {
 97+ $keynode = $this->xmlDoc->createElement( $key );
 98+ $this->buildTransactionNodes( $value, $keynode );
 99+ $node->appendChild( $keynode );
 100+ }
 101+ }
 102+ }
 103+ //not actually returning anything. It's all side-effects. Because I suck like that.
 104+ }
 105+
 106+ function appendNodeIfValue( $value, &$node ) {
 107+ $nodevalue = $this->getValue( $value );
 108+ if ( $nodevalue !== '' && $nodevalue !== false ) {
 109+ $temp = $this->xmlDoc->createElement( $value, $nodevalue );
 110+ $node->appendChild( $temp );
 111+ }
 112+ }
 113+
 114+ function do_transaction( $transaction ) {
 115+ $this->currentTransaction( $transaction );
 116+ if ( $this->getCommunicationType() === 'xml' ) {
 117+ $xml = $this->buildRequestXML();
 118+ $response = $this->curl_transaction( $xml );
 119+ //put the response in a universal form, and return it.
 120+ }
 121+
 122+ //TODO: Actually pull these from somewhere legit.
 123+ if ( $response['status'] === true ) {
 124+ $response['message'] = "$transaction Transaction Successful!";
 125+ } elseif ( $response['status'] === false ) {
 126+ $response['message'] = "$transaction Transaction FAILED!";
 127+ } else {
 128+ $response['message'] = "$transaction Transaction... weird. I have no idea what happened there.";
 129+ }
 130+
 131+ return $response;
 132+
 133+ //speaking of universal form:
 134+ //$result['status'] = something I wish could be boiled down to a bool, but that's way too optimistic, I think.
 135+ //$result['message'] = whatever we want to display back?
 136+ //$result['errors'][]['code'] = their error code
 137+ //$result['errors'][]['value'] = Error message
 138+ //$result['return'][$whatever] = values they pass back to us for whatever reason. We might... log it, or pieces of it, or something?
 139+ }
 140+
 141+ function getCurlBaseOpts() {
 142+ //I chose to return this as a function so it's easy to override.
 143+ //TODO: probably this for all the junk I currently have stashed in the constructor.
 144+ //...maybe.
 145+ global $wgGlobalCollectTimeout, $wgPayflowGatewayUseHTTPProxy;
 146+ $opts = array(
 147+ CURLOPT_URL => $this->url,
 148+ //CURLOPT_USERAGENT => Http::userAgent(),
 149+ CURLOPT_HEADER => 1,
 150+ CURLOPT_RETURNTRANSFER => 1,
 151+ CURLOPT_TIMEOUT => $wgGlobalCollectTimeout,
 152+ //CURLOPT_FOLLOWLOCATION => 0,
 153+ //CURLOPT_SSL_VERIFYPEER => 0,
 154+ //CURLOPT_SSL_VERIFYHOST => 2,
 155+ //CURLOPT_FORBID_REUSE => true,
 156+ CURLOPT_POST => 1,
 157+ );
 158+
 159+ // set proxy settings if necessary
 160+ if ( $wgPayflowGatewayUseHTTPProxy ) {
 161+ $opts[CURLOPT_HTTPPROXYTUNNEL] = 1;
 162+ $opts[CURLOPT_PROXY] = $wgPayflowGatewayHTTPProxy;
 163+ }
 164+ return $opts;
 165+ }
 166+
 167+ function getCurlBaseHeaders() {
 168+ $headers = array(
 169+ 'Content-Type: text/' . $this->getCommunicationType() . '; charset=utf-8',
 170+ // 'X-VPS-Client-Timeout: 45',
 171+ // 'X-VPS-Request-ID:' . $this->postdatadefaults['order_id'],
 172+ );
 173+ return $headers;
 174+ }
 175+
 176+ protected function currentTransaction( $transaction = '' ) { //get&set in one!
 177+ static $current_transaction;
 178+ if ( $transaction != '' ) {
 179+ $current_transaction = $transaction;
 180+ }
 181+ if ( !isset( $current_transaction ) ) {
 182+ return false;
 183+ }
 184+ return $current_transaction;
 185+ }
 186+
 187+ /**
 188+ * Sends a name-value pair string to Payflow gateway
 189+ *
 190+ * @param $data String: The exact thing we want to send.
 191+ */
 192+ protected function curl_transaction( $data ) {
 193+ global $wgOut; //TODO: Uhm, this shouldn't touch the view. Something further upstream should decide what to do with this.
 194+ // TODO: This, but way before we get here.
 195+ //$this->updateContributionTracking( $data, defined( 'OWA' ) );
 196+ // assign header data necessary for the curl_setopt() function
 197+
 198+ $ch = curl_init();
 199+
 200+ $headers = $this->getCurlBaseHeaders();
 201+ $headers[] = 'Content-Length: ' . strlen( $data );
 202+
 203+ self::log( "Sending Data: " . $this->formatXmlString( $data ) );
 204+
 205+ $curl_opts = $this->getCurlBaseOpts();
 206+ $curl_opts[CURLOPT_HTTPHEADER] = $headers;
 207+ $curl_opts[CURLOPT_POSTFIELDS] = $data;
 208+
 209+ foreach ( $curl_opts as $option => $value ) {
 210+ curl_setopt( $ch, $option, $value );
 211+ }
 212+
 213+ // As suggested in the PayPal developer forum sample code, try more than once to get a response
 214+ // in case there is a general network issue
 215+ $i = 1;
 216+
 217+ $return = array( );
 218+
 219+ while ( $i++ <= 3 ) {
 220+ self::log( $this->postdatadefaults['order_id'] . ' Preparing to send transaction to GlobalCollect' );
 221+ $return['result'] = curl_exec( $ch );
 222+ $return['headers'] = curl_getinfo( $ch );
 223+
 224+ if ( $return['headers']['http_code'] != 200 && $return['headers']['http_code'] != 403 ) {
 225+ self::log( $this->postdatadefaults['order_id'] . ' Failed sending transaction to GlobalCollect, retrying' );
 226+ sleep( 1 );
 227+ } elseif ( $return['headers']['http_code'] == 200 || $return['headers']['http_code'] == 403 ) {
 228+ self::log( $this->postdatadefaults['order_id'] . ' Finished sending transaction to GlobalCollect' );
 229+ break;
 230+ }
 231+ }
 232+
 233+ if ( $return['headers']['http_code'] != 200 ) {
 234+ $return['result'] = false;
 235+ //TODO: i18n here!
 236+ $return['message'] = 'No response from credit card processor. Please try again later!';
 237+ $when = time();
 238+ self::log( $this->postdatadefaults['order_id'] . ' No response from credit card processor: ' . curl_error( $ch ) );
 239+ curl_close( $ch );
 240+ return $return;
 241+ }
 242+
 243+ curl_close( $ch );
 244+ self::log( "Results: " . print_r( $return['result'], true ) );
 245+
 246+// if ($this->getCommunicationType() === 'namevalue'){
 247+// $return['result'] = strstr( $return['result'], 'RESULT' );
 248+// //TODO: Finish this for namevalue.
 249+// }
 250+ if ( $this->getCommunicationType() === 'xml' ) {
 251+ //$return['result'] = $this->stripResponseHeaders($return['result']);
 252+ $return['status'] = $this->parseXMLResponse( $return['result'] );
 253+ }
 254+
 255+ return $return;
 256+
 257+ // parse string and display results to the user
 258+ //TODO: NO NO NO. NO DISPLAY HERE.
 259+ //$this->fnPayflowGetResults( $data, $return['result'] );
 260+ }
 261+
 262+ function stripResponseHeaders( $rawResponse ) {
 263+ $xmlStart = strpos( $rawResponse, '<?xml' );
 264+ if ( $xmlStart == false ) { //I totally saw this happen one time. No XML, just <RESPONSE>...
 265+ $xmlStart = strpos( $rawResponse, '<RESPONSE' );
 266+ }
 267+ if ( $xmlStart == false ) { //Still false. Your Head Asplode.
 268+ self::log( "Wow, that was so messed up I couldn't even parse the response, so here's the thing in its entirety:\n" . $rawResponse );
 269+ return false;
 270+ }
 271+ $justXML = substr( $rawResponse, $xmlStart );
 272+ return $justXML;
 273+ }
 274+
 275+ public static function log( $msg, $log_level=LOG_INFO ) {
 276+ global $wgGlobalCollectGatewayUseSyslog;
 277+ $c = get_called_class();
 278+ $identifier = $c::logidentifier;
 279+
 280+ // if we're not using the syslog facility, use wfDebugLog
 281+ if ( !$wgGlobalCollectGatewayUseSyslog ) {
 282+ wfDebugLog( $identifier, $msg );
 283+ return;
 284+ }
 285+
 286+ // otherwise, use syslogging
 287+ openlog( $identifier, LOG_ODELAY, LOG_SYSLOG );
 288+ syslog( $log_level, $msg );
 289+ closelog();
 290+ }
 291+
 292+ //_______________________________________________________________
 293+ //copied from payflowpro_gateway/includes/payflowUser.inc
 294+
 295+ /**
 296+ * Fetch and return the 'order_id' for a transaction
 297+ *
 298+ * Since transactions to PayPal are initially matched internally on their end
 299+ * with the 'order_id' field, but we don't actually care what the order id is,
 300+ * we generate a sufficiently random number to avoid duplication.
 301+ *
 302+ * We go ahead and always generate a random order id becuse if PayPal detects
 303+ * the same order_id more than once, it considers the request a duplicate, even
 304+ * if the data is completely different.
 305+ *
 306+ * @return int
 307+ */
 308+ function getOrderId() {
 309+ return $this->generateOrderId();
 310+ }
 311+
 312+ /**
 313+ * Generate an internal order id
 314+ *
 315+ * This is only used internally for tracking a user's 'session' with the credit
 316+ * card form. I mean 'session' in the sense of the moment a credit card page
 317+ * is loaded for the first time (nothing posted to it - a discrete donation
 318+ * session) as opposed to the $_SESSION - as the $_SESSION id could potentially
 319+ * not change between contribution attempts.
 320+ */
 321+ function getInternalOrderId() {
 322+ global $wgRequest;
 323+
 324+ // is an order_id already set?
 325+ //TODO: Change all these to look instead at $this->postdata... I think.
 326+ $i_order_id = $wgRequest->getText( 'i_order_id', 0 );
 327+
 328+ // if the form was not just posted OR there's no order_id set, generate one.
 329+ if ( !$wgRequest->wasPosted() || !$i_order_id ) {
 330+ $i_order_id = $this->generateOrderId();
 331+ }
 332+
 333+ return $i_order_id;
 334+ }
 335+
 336+ /**
 337+ * Generate an order id
 338+ */
 339+ function generateOrderId() {
 340+ return ( double ) microtime() * 1000000 . mt_rand( 1000, 9999 );
 341+ }
 342+
 343+ //To avoid reinventing the wheel: taken from http://recursive-design.com/blog/2007/04/05/format-xml-with-php/
 344+ function formatXmlString( $xml ) {
 345+ // add marker linefeeds to aid the pretty-tokeniser (adds a linefeed between all tag-end boundaries)
 346+ $xml = preg_replace( '/(>)(<)(\/*)/', "$1\n$2$3", $xml );
 347+
 348+ // now indent the tags
 349+ $token = strtok( $xml, "\n" );
 350+ $result = ''; // holds formatted version as it is built
 351+ $pad = 0; // initial indent
 352+ $matches = array( ); // returns from preg_matches()
 353+ // scan each line and adjust indent based on opening/closing tags
 354+ while ( $token !== false ) :
 355+
 356+ // test for the various tag states
 357+ // 1. open and closing tags on same line - no change
 358+ if ( preg_match( '/.+<\/\w[^>]*>$/', $token, $matches ) ) :
 359+ $indent = 0;
 360+ // 2. closing tag - outdent now
 361+ elseif ( preg_match( '/^<\/\w/', $token, $matches ) ) :
 362+ $pad--;
 363+ // 3. opening tag - don't pad this one, only subsequent tags
 364+ elseif ( preg_match( '/^<\w[^>]*[^\/]>.*$/', $token, $matches ) ) :
 365+ $indent = 1;
 366+ // 4. no indentation needed
 367+ else :
 368+ $indent = 0;
 369+ endif;
 370+
 371+ // pad the line with the required number of leading spaces
 372+ $line = str_pad( $token, strlen( $token ) + $pad, ' ', STR_PAD_LEFT );
 373+ $result .= $line . "\n"; // add to the cumulative result, with linefeed
 374+ $token = strtok( "\n" ); // get the next token
 375+ $pad += $indent; // update the pad size for subsequent lines
 376+ endwhile;
 377+
 378+ return $result;
 379+ }
 380+
 381+}
Property changes on: branches/fundraising/extensions/DonationInterface/gateway_common/gateway.adapter.php
___________________________________________________________________
Added: svn:eol-style
1382 + native
Index: branches/fundraising/extensions/DonationInterface/donationinterface.php
@@ -2,26 +2,26 @@
33
44 # Alert the user that this is not a valid entry point to MediaWiki if they try to access the special pages file directly.
55 if ( !defined( 'MEDIAWIKI' ) ) {
6 - echo <<<EOT
 6+ echo <<<EOT
77 To install the DontaionInterface extension, put the following line in LocalSettings.php:
88 require_once( "\$IP/extensions/DonationInterface/donationinterface.php" );
99 EOT;
10 - exit( 1 );
 10+ exit( 1 );
1111 }
1212
1313 // Extension credits that will show up on Special:Version
1414 $wgExtensionCredits['specialpage'][] = array(
15 - 'name' => 'Donation Interface',
16 - 'author' => 'Katie Horn',
17 - 'version' => '1.0.0',
18 - 'descriptionmsg' => 'donationinterface-desc',
19 - 'url' => 'http://www.mediawiki.org/wiki/Extension:DonationInterface',
 15+ 'name' => 'Donation Interface',
 16+ 'author' => 'Katie Horn',
 17+ 'version' => '1.0.0',
 18+ 'descriptionmsg' => 'donationinterface-desc',
 19+ 'url' => 'http://www.mediawiki.org/wiki/Extension:DonationInterface',
2020 );
2121
2222 //This is going to be a little funky.
2323 //Override this in LocalSettings.php BEFORE you include this file, if you want
2424 //to disable gateways.
25 -if (!isset($wgDonationInterfaceEnabledGateways)){
 25+if ( !isset( $wgDonationInterfaceEnabledGateways ) ) {
2626 $wgDonationInterfaceEnabledGateways = array(
2727 'paypal',
2828 'payflowpro',
@@ -33,54 +33,37 @@
3434
3535 require_once( $donationinterface_dir . 'donate_interface/donate_interface.php' );
3636
37 -foreach ($wgDonationInterfaceEnabledGateways as $gateway){
 37+foreach ( $wgDonationInterfaceEnabledGateways as $gateway ) {
3838 //include 'em
3939 require_once( $donationinterface_dir . $gateway . '_gateway/' . $gateway . '_gateway.php' );
4040 }
4141
4242 //load all possible form classes
43 -$wgAutoloadClasses[ 'Gateway_Form' ] = $donationinterface_dir . 'gateway_forms/Form.php';
44 -$wgAutoloadClasses[ 'Gateway_Form_OneStepTwoColumn' ] = $donationinterface_dir . 'gateway_forms/OneStepTwoColumn.php';
45 -$wgAutoloadClasses[ 'Gateway_Form_TwoStepTwoColumn' ] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumn.php';
46 -$wgAutoloadClasses[ 'Gateway_Form_TwoColumnPayPal' ] = $donationinterface_dir . 'gateway_forms/TwoColumnPayPal.php';
47 -$wgAutoloadClasses[ 'Gateway_Form_TwoColumnLetter' ] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter.php';
48 -$wgAutoloadClasses[ 'Gateway_Form_TwoColumnLetter2' ] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter2.php';
49 -$wgAutoloadClasses[ 'Gateway_Form_TwoColumnLetter3' ] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter3.php';
50 -$wgAutoloadClasses[ 'Gateway_Form_TwoColumnLetter4' ] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter4.php';
51 -$wgAutoloadClasses[ 'Gateway_Form_TwoColumnLetter5' ] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter5.php';
52 -$wgAutoloadClasses[ 'Gateway_Form_TwoColumnLetter6' ] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter6.php';
53 -$wgAutoloadClasses[ 'Gateway_Form_TwoColumnLetter7' ] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter7.php';
54 -$wgAutoloadClasses[ 'Gateway_Form_TwoStepTwoColumnLetter' ] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetter.php';
55 -$wgAutoloadClasses[ 'Gateway_Form_TwoStepTwoColumnLetterCA' ] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetterCA.php';
56 -$wgAutoloadClasses[ 'Gateway_Form_TwoStepTwoColumnLetter2' ] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetter2.php';
57 -$wgAutoloadClasses[ 'Gateway_Form_TwoStepTwoColumnLetter3' ] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetter3.php';
58 -$wgAutoloadClasses[ 'Gateway_Form_TwoStepTwoColumnPremium' ] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnPremium.php';
59 -$wgAutoloadClasses[ 'Gateway_Form_TwoStepTwoColumnPremiumUS' ] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnPremiumUS.php';
60 -$wgAutoloadClasses[ 'Gateway_Form_RapidHtml' ] = $donationinterface_dir . 'gateway_forms/RapidHtml.php';
61 -$wgAutoloadClasses[ 'Gateway_Form_SingleColumn' ] = $donationinterface_dir . 'gateway_forms/SingleColumn.php';
 43+$wgAutoloadClasses['Gateway_Form'] = $donationinterface_dir . 'gateway_forms/Form.php';
 44+$wgAutoloadClasses['Gateway_Form_OneStepTwoColumn'] = $donationinterface_dir . 'gateway_forms/OneStepTwoColumn.php';
 45+$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumn'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumn.php';
 46+$wgAutoloadClasses['Gateway_Form_TwoColumnPayPal'] = $donationinterface_dir . 'gateway_forms/TwoColumnPayPal.php';
 47+$wgAutoloadClasses['Gateway_Form_TwoColumnLetter'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter.php';
 48+$wgAutoloadClasses['Gateway_Form_TwoColumnLetter2'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter2.php';
 49+$wgAutoloadClasses['Gateway_Form_TwoColumnLetter3'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter3.php';
 50+$wgAutoloadClasses['Gateway_Form_TwoColumnLetter4'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter4.php';
 51+$wgAutoloadClasses['Gateway_Form_TwoColumnLetter5'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter5.php';
 52+$wgAutoloadClasses['Gateway_Form_TwoColumnLetter6'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter6.php';
 53+$wgAutoloadClasses['Gateway_Form_TwoColumnLetter7'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter7.php';
 54+$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnLetter'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetter.php';
 55+$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnLetterCA'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetterCA.php';
 56+$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnLetter2'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetter2.php';
 57+$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnLetter3'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetter3.php';
 58+$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnPremium'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnPremium.php';
 59+$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnPremiumUS'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnPremiumUS.php';
 60+$wgAutoloadClasses['Gateway_Form_RapidHtml'] = $donationinterface_dir . 'gateway_forms/RapidHtml.php';
 61+$wgAutoloadClasses['Gateway_Form_SingleColumn'] = $donationinterface_dir . 'gateway_forms/SingleColumn.php';
6262
6363
6464
65 -
66 -
67 -
68 -
69 -
70 -
71 -
72 -
73 -
74 -//$wgAutoloadClasses['GlobalCollectGateway'] = $dir . 'globalcollect_gateway.body.php';
75 -//$wgExtensionMessagesFiles['GlobalCollectGateway'] = $dir . 'globalcollect_gateway.i18n.php';
76 -//$wgExtensionAliasesFiles['GlobalCollectGateway'] = $dir . 'globalcollect_gateway.alias.php';
77 -//$wgSpecialPages['GlobalCollectGateway'] = 'GlobalCollectGateway';
78 -//$wgAjaxExportList[] = "fnGlobalCollectofofWork";
79 -
80 -
8165 // set defaults, these should be assigned in LocalSettings.php
8266 //$wgGlobalCollectURL = 'https://globalcollect.paypal.com';
8367 //$wgGlobalCollectTestingURL = 'https://pilot-globalcollect.paypal.com'; // Payflow testing URL
84 -
8568 //$wgGlobalCollectGatewayCSSVersion = 1;
8669 //
8770 //$wgGlobalCollectPartnerID = ''; // PayPal or original authorized reseller
@@ -167,7 +150,7 @@
168151 * /never/ be loaded by the rapid html form loader!
169152 * @var string
170153 */
171 -$wgPayflowAllowedHtmlForms = array( $wgPayflowHtmlFormDir . "/demo.html" );
 154+$wgPayflowAllowedHtmlForms = array( $wgPayflowHtmlFormDir . "/demo.html" );
172155
173156 /**
174157 * Configure PayflowproGateway to use syslog for log messages rather than wfDebugLog
Index: branches/fundraising/extensions/DonationInterface/tests/DonationInterfaceTest.php
@@ -0,0 +1,27 @@
 2+<?php
 3+
 4+/**
 5+ * TODO: Something.
 6+ * Something roughly test-shaped. Here.
 7+ * ...to be more precise: Test that ALL the gateway adapters (Yes: All two of them)
 8+ * are building the XML we think they are, and that they can process sample
 9+ * return XML the way we think they should.
 10+ *
 11+ * TODO: Then, write all the other tests as well. :|
 12+ *
 13+ * @group Fundraising
 14+ * @group Splunge
 15+ * @author Katie Horn <khorn@wikimedia.org>
 16+ */
 17+class DonationInterfaceTest extends MediaWikiTestCase {
 18+
 19+ /**
 20+ * This test is deeply stupid, btw.
 21+ */
 22+ function assertTestClassExists() {
 23+ $this->assertTrue( true, "WeeeooooOOOHOO!!1one1" );
 24+ }
 25+
 26+}
 27+
 28+?>
Property changes on: branches/fundraising/extensions/DonationInterface/tests/DonationInterfaceTest.php
___________________________________________________________________
Added: svn:eol-style
129 + native
Index: branches/fundraising/extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.php
@@ -2,45 +2,45 @@
33
44 # Alert the user that this is not a valid entry point to MediaWiki if they try to access the special pages file directly.
55 if ( !defined( 'MEDIAWIKI' ) ) {
6 - echo <<<EOT
 6+ echo <<<EOT
77 To install PayflowPro Gateway extension, put the following line in LocalSettings.php:
88 require_once( "\$IP/extensions/payflowpro_gateway/payflowpro_gateway.php" );
99 EOT;
10 - exit( 1 );
 10+ exit( 1 );
1111 }
1212
1313 // Extension credits that will show up on Special:Version
1414 $wgExtensionCredits['specialpage'][] = array(
15 - 'name' => 'PayflowPro Gateway',
16 - 'author' => 'Four Kitchens',
17 - 'version' => '1.0.0',
18 - 'descriptionmsg' => 'payflowpro_gateway-desc',
19 - 'url' => 'http://www.mediawiki.org/wiki/Extension:PayflowProGateway',
 15+ 'name' => 'PayflowPro Gateway',
 16+ 'author' => 'Four Kitchens',
 17+ 'version' => '1.0.0',
 18+ 'descriptionmsg' => 'payflowpro_gateway-desc',
 19+ 'url' => 'http://www.mediawiki.org/wiki/Extension:PayflowProGateway',
2020 );
2121
2222 // Set up the new special page
2323 $dir = dirname( __FILE__ ) . '/';
2424 $wgAutoloadClasses['PayflowProGateway'] = $dir . 'payflowpro_gateway.body.php';
2525
26 -$wgAutoloadClasses[ 'PayflowProGateway_Form' ] = $dir . 'forms/Form.php';
27 -$wgAutoloadClasses[ 'PayflowProGateway_Form_OneStepTwoColumn' ] = $dir . 'forms/OneStepTwoColumn.php';
28 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoStepTwoColumn' ] = $dir . 'forms/TwoStepTwoColumn.php';
29 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoColumnPayPal' ] = $dir . 'forms/TwoColumnPayPal.php';
30 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoColumnLetter' ] = $dir . 'forms/TwoColumnLetter.php';
31 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoColumnLetter2' ] = $dir . 'forms/TwoColumnLetter2.php';
32 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoColumnLetter3' ] = $dir . 'forms/TwoColumnLetter3.php';
33 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoColumnLetter4' ] = $dir . 'forms/TwoColumnLetter4.php';
34 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoColumnLetter5' ] = $dir . 'forms/TwoColumnLetter5.php';
35 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoColumnLetter6' ] = $dir . 'forms/TwoColumnLetter6.php';
36 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoColumnLetter7' ] = $dir . 'forms/TwoColumnLetter7.php';
37 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoStepTwoColumnLetter' ] = $dir . 'forms/TwoStepTwoColumnLetter.php';
38 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoStepTwoColumnLetterCA' ] = $dir . 'forms/TwoStepTwoColumnLetterCA.php';
39 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoStepTwoColumnLetter2' ] = $dir . 'forms/TwoStepTwoColumnLetter2.php';
40 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoStepTwoColumnLetter3' ] = $dir . 'forms/TwoStepTwoColumnLetter3.php';
41 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoStepTwoColumnPremium' ] = $dir . 'forms/TwoStepTwoColumnPremium.php';
42 -$wgAutoloadClasses[ 'PayflowProGateway_Form_TwoStepTwoColumnPremiumUS' ] = $dir . 'forms/TwoStepTwoColumnPremiumUS.php';
43 -$wgAutoloadClasses[ 'PayflowProGateway_Form_RapidHtml' ] = $dir . 'forms/RapidHtml.php';
44 -$wgAutoloadClasses[ 'PayflowProGateway_Form_SingleColumn' ] = $dir . 'forms/SingleColumn.php';
 26+$wgAutoloadClasses['PayflowProGateway_Form'] = $dir . 'forms/Form.php';
 27+$wgAutoloadClasses['PayflowProGateway_Form_OneStepTwoColumn'] = $dir . 'forms/OneStepTwoColumn.php';
 28+$wgAutoloadClasses['PayflowProGateway_Form_TwoStepTwoColumn'] = $dir . 'forms/TwoStepTwoColumn.php';
 29+$wgAutoloadClasses['PayflowProGateway_Form_TwoColumnPayPal'] = $dir . 'forms/TwoColumnPayPal.php';
 30+$wgAutoloadClasses['PayflowProGateway_Form_TwoColumnLetter'] = $dir . 'forms/TwoColumnLetter.php';
 31+$wgAutoloadClasses['PayflowProGateway_Form_TwoColumnLetter2'] = $dir . 'forms/TwoColumnLetter2.php';
 32+$wgAutoloadClasses['PayflowProGateway_Form_TwoColumnLetter3'] = $dir . 'forms/TwoColumnLetter3.php';
 33+$wgAutoloadClasses['PayflowProGateway_Form_TwoColumnLetter4'] = $dir . 'forms/TwoColumnLetter4.php';
 34+$wgAutoloadClasses['PayflowProGateway_Form_TwoColumnLetter5'] = $dir . 'forms/TwoColumnLetter5.php';
 35+$wgAutoloadClasses['PayflowProGateway_Form_TwoColumnLetter6'] = $dir . 'forms/TwoColumnLetter6.php';
 36+$wgAutoloadClasses['PayflowProGateway_Form_TwoColumnLetter7'] = $dir . 'forms/TwoColumnLetter7.php';
 37+$wgAutoloadClasses['PayflowProGateway_Form_TwoStepTwoColumnLetter'] = $dir . 'forms/TwoStepTwoColumnLetter.php';
 38+$wgAutoloadClasses['PayflowProGateway_Form_TwoStepTwoColumnLetterCA'] = $dir . 'forms/TwoStepTwoColumnLetterCA.php';
 39+$wgAutoloadClasses['PayflowProGateway_Form_TwoStepTwoColumnLetter2'] = $dir . 'forms/TwoStepTwoColumnLetter2.php';
 40+$wgAutoloadClasses['PayflowProGateway_Form_TwoStepTwoColumnLetter3'] = $dir . 'forms/TwoStepTwoColumnLetter3.php';
 41+$wgAutoloadClasses['PayflowProGateway_Form_TwoStepTwoColumnPremium'] = $dir . 'forms/TwoStepTwoColumnPremium.php';
 42+$wgAutoloadClasses['PayflowProGateway_Form_TwoStepTwoColumnPremiumUS'] = $dir . 'forms/TwoStepTwoColumnPremiumUS.php';
 43+$wgAutoloadClasses['PayflowProGateway_Form_RapidHtml'] = $dir . 'forms/RapidHtml.php';
 44+$wgAutoloadClasses['PayflowProGateway_Form_SingleColumn'] = $dir . 'forms/SingleColumn.php';
4545
4646 $wgExtensionMessagesFiles['PayflowProGateway'] = $dir . 'payflowpro_gateway.i18n.php';
4747 $wgExtensionMessagesFiles['PayflowProGatewayCountries'] = $dir . 'payflowpro_gateway.countries.i18n.php';
@@ -60,7 +60,6 @@
6161 $wgPayflowProVendorID = ''; // paypal merchant login ID
6262 $wgPayflowProUserID = ''; // if one or more users are set up, authorized user ID, else same as VENDOR
6363 $wgPayflowProPassword = ''; // merchant login password
64 -
6564 // a boolean to determine if we're in testing mode
6665 $wgPayflowGatewayTest = FALSE;
6766
@@ -140,7 +139,7 @@
141140 * /never/ be loaded by the rapid html form loader!
142141 * @var string
143142 */
144 -$wgPayflowAllowedHtmlForms = array( $wgPayflowHtmlFormDir . "/demo.html" );
 143+$wgPayflowAllowedHtmlForms = array( $wgPayflowHtmlFormDir . "/demo.html" );
145144
146145 /**
147146 * Configure PayflowproGateway to use syslog for log messages rather than wfDebugLog
@@ -166,8 +165,8 @@
167166 $wgHooks['DonationInterface_Page'][] = 'pfpGatewayPage';
168167
169168 // enable the API
170 -$wgAPIModules[ 'pfp' ] = 'ApiPayflowProGateway';
171 -$wgAutoloadClasses[ 'ApiPayflowProGateway' ] = $dir . 'api_payflowpro_gateway.php';
 169+$wgAPIModules['pfp'] = 'ApiPayflowProGateway';
 170+$wgAutoloadClasses['ApiPayflowProGateway'] = $dir . 'api_payflowpro_gateway.php';
172171
173172 function payflowGatewayConnection() {
174173 global $wgPayflowGatewayDBserver, $wgPayflowGatewayDBname;
@@ -176,12 +175,12 @@
177176 static $db;
178177
179178 if ( !$db ) {
180 - $db = new DatabaseMysql(
181 - $wgPayflowGatewayDBserver,
182 - $wgPayflowGatewayDBuser,
183 - $wgPayflowGatewayDBpassword,
184 - $wgPayflowGatewayDBname );
185 - $db->query( "SET names utf8" );
 179+ $db = new DatabaseMysql(
 180+ $wgPayflowGatewayDBserver,
 181+ $wgPayflowGatewayDBuser,
 182+ $wgPayflowGatewayDBpassword,
 183+ $wgPayflowGatewayDBname );
 184+ $db->query( "SET names utf8" );
186185 }
187186
188187 return $db;
@@ -193,17 +192,17 @@
194193 */
195194 function pfpGatewayValue( &$values ) {
196195 $values['payflow'] = array(
197 - 'gateway' => 'payflow',
198 - 'display_name' => 'Credit Card',
199 - 'form_value' => 'payflow',
200 - 'currencies' => array(
201 - 'GBP' => 'GBP: British Pound',
202 - 'EUR' => 'EUR: Euro',
203 - 'USD' => 'USD: U.S. Dollar',
204 - 'AUD' => 'AUD: Australian Dollar',
205 - 'CAD' => 'CAD: Canadian Dollar',
206 - 'JPY' => 'JPY: Japanese Yen',
207 - ),
 196+ 'gateway' => 'payflow',
 197+ 'display_name' => 'Credit Card',
 198+ 'form_value' => 'payflow',
 199+ 'currencies' => array(
 200+ 'GBP' => 'GBP: British Pound',
 201+ 'EUR' => 'EUR: Euro',
 202+ 'USD' => 'USD: U.S. Dollar',
 203+ 'AUD' => 'AUD: Australian Dollar',
 204+ 'CAD' => 'CAD: Canadian Dollar',
 205+ 'JPY' => 'JPY: Japanese Yen',
 206+ ),
208207 );
209208
210209 return true;
Index: branches/fundraising/extensions/DonationInterface/globalcollect_gateway/globalcollect.adapter.php
@@ -1,33 +1,29 @@
22 <?php
33
 4+$dir = dirname( __FILE__ ) . '/';
 5+require_once( $dir . '../gateway_common/gateway.adapter.php' );
46
5 -class GlobalCollectAdapter {
6 -
 7+class GlobalCollectAdapter extends GatewayAdapter {
78 //Contains the map of THEIR var names, to OURS.
89 //I'd have gone the other way, but we'd run into 1:many pretty quick.
9 - private $var_map;
10 - private $accountInfo;
11 - private $url;
12 - private $transactions;
13 - private $postdata;
14 - private $postdatadefaults;
15 - private $xmlDoc;
16 -
17 - function __construct($data){
 10+
 11+ const logidentifier = 'globalcollect_gateway';
 12+
 13+ function __construct( $data ) {
1814 //TODO: Squish ALL the globals here, in the constructor.
1915 //easier to abstract out that way.
2016 global $wgGatewayTest;
21 -
 17+
2218 $this->postdata = $data;
23 -
 19+
2420 //TODO: Make a Thing in which we do things like this.
2521 $this->postdata['amount'] = $this->postdata['amount'] * 100;
26 -
27 -
 22+
 23+
2824 $returnTitle = Title::newFromText( 'Donate-thanks/en' );
2925 $returnto = $returnTitle->getFullURL();
3026 $returnto = "http://www.katiehorn.com";
31 -
 27+
3228 //this DEFINITELY needs to be defined in the parent class, and contain everything anybody might want to know.
3329 $this->postdatadefaults = array(
3430 'order_id' => '112358' . rand(),
@@ -40,10 +36,10 @@
4137 'order_id' => $this->getOrderId(),
4238 'i_order_id' => $this->getInternalOrderId(),
4339 );
44 -
45 -
 40+
 41+
4642 ///ehh. Most of this should be broken up into functions for the sake of readibility.
47 -
 43+
4844 $this->var_map = array(
4945 'ORDERID' => 'order_id',
5046 'AMOUNT' => 'amount',
@@ -53,22 +49,21 @@
5450 'MERCHANTREFERENCE' => 'order_id',
5551 'RETURNURL' => 'returnto', //I think. It might not even BE here yet. Boo-urns.
5652 'IPADDRESS' => 'user_ip', //TODO: Not sure if this should be OUR ip, or the user's ip. Hurm.
57 -
5853 );
59 -
60 - $this->return_value_map = array(
 54+
 55+ $this->return_value_map = array(
6156 'OK' => true,
6257 'NOK' => false,
6358 );
64 -
 59+
6560 global $wgGlobalCollectURL, $wgGlobalCollectMerchantID;
66 - if ($wgGatewayTest){
 61+ if ( $wgGatewayTest ) {
6762 $this->url = $wgGlobalCollectURL;
6863 } else {
6964 $this->url = $wgGlobalCollectURL;
7065 }
71 -
72 -
 66+
 67+
7368 $this->accountInfo = array(
7469 // 'PAYMENTPRODUCTID' => '3', //actually, these are almost certainly transaction-specific.
7570 // 'HOSTEDINDICATOR' => '1',
@@ -76,20 +71,20 @@
7772 //'IPADDRESS' => '', //TODO: Not sure if this should be OUR ip, or the user's ip. Hurm.
7873 'VERSION' => "1.0",
7974 );
80 -
 75+
8176 //oof. This is getting a little long and unwieldy. Maybe we should build it. Or maybe that sucks. I can't tell yet.
8277 /* General idea here:
8378 * This bad boy will (probably) contain the structure of all possible transactions as defined by the gateway.
8479 * First array key: Some way for us to id the transaction. Doesn't actually have to be the gateway's name for it, but
8580 * I'm starting with that.
8681 * Second array key:
87 - * 'structure' contains the layout of that transaction.
88 - * 'defaults' contains default values for the leaf 'values'
89 - * I could put a 'type' in here, but I think we can assume that if 'structure' is multi-layer, we're XML.
 82+ * 'structure' contains the layout of that transaction.
 83+ * 'defaults' contains default values for the leaf 'values'
 84+ * I could put a 'type' in here, but I think we can assume that if 'structure' is multi-layer, we're XML.
9085 * Array "leaves" in 'structure' will be assigned a value according to the var_map, and the posted data.
91 - * There should also be a mechanism for assigning defaults, but I'm not entirely sure what that would look like quite yet...
 86+ * There should also be a mechanism for assigning defaults, but I'm not entirely sure what that would look like quite yet...
9287 *
93 - */
 88+ */
9489 $this->transactions = array(
9590 'INSERT_ORDERWITHPAYMENT' => array(
9691 'request' => array(
@@ -108,7 +103,7 @@
109104 'LANGUAGECODE',
110105 'COUNTRYCODE',
111106 'MERCHANTREFERENCE'
112 - ),
 107+ ),
113108 'PAYMENT' => array(
114109 'PAYMENTPRODUCTID',
115110 'AMOUNT',
@@ -137,7 +132,7 @@
138133 ),
139134 'result_data' => array(
140135 'ROW' => array(
141 - //uhh... presumably we'd look for some Stuff in here.
 136+ //uhh... presumably we'd look for some Stuff in here.
142137 )
143138 )
144139 ),
@@ -150,7 +145,7 @@
151146 // 'IPADDRESS',
152147 'VERSION'
153148 ),
154 - 'PARAMS' => array()
 149+ 'PARAMS' => array( )
155150 )
156151 ),
157152 'values' => array(
@@ -167,114 +162,36 @@
168163 ),
169164 'result_data' => array(
170165 'ROW' => array(
171 - //uhh... presumably we'd look for some Stuff in here.
 166+ //uhh... presumably we'd look for some Stuff in here.
172167 )
173168 )
174169 ),
175170 );
176 -
177171 }
178 -
179 - function getCommunicationType(){
 172+
 173+ function getCommunicationType() {
180174 return 'xml'; //'xml' or 'namevalue'.
181175 }
182 -
183 -
184 - function getValue($gateway_field_name, $transaction = ''){
185 - //How do we determine the value of a field asked for in a particular transaction?
186 -
187 - //If there's a hard-coded value in the transaction definition, use that.
188 - if(array_key_exists($transaction, $this->transactions) &&
189 - array_key_exists('values', $this->transactions[$transaction]) &&
190 - array_key_exists($gateway_field_name, $this->transactions[$transaction]['values'])){
191 - return $this->transactions[$transaction]['values'][$gateway_field_name];
192 - }
193 -
194 - //if it's account info, use that.
195 - //$this->accountInfo;
196 - if(array_key_exists($gateway_field_name, $this->accountInfo)){
197 - return $this->accountInfo[$gateway_field_name];
198 - }
199 -
200 -
201 - //If there's a value in the post data (name-translated by the var_map), use that.
202 - if (array_key_exists($gateway_field_name, $this->var_map)){
203 - if (array_key_exists($this->var_map[$gateway_field_name], $this->postdata) &&
204 - $this->postdata[$this->var_map[$gateway_field_name]] !== ''){
205 - //if it was sent, use that.
206 - return $this->postdata[$this->var_map[$gateway_field_name]];
207 - } else {
208 - //return the default for that form value
209 - return $this->postdatadefaults[$this->var_map[$gateway_field_name]];
210 - }
211 - }
212 -
213 - //not in the map, or hard coded. What then?
214 - //Complain furiously, for your code is faulty.
215 - //TODO: Something that plays nice with others, instead of...
216 - die("getValue found NOTHING for $gateway_field_name, $transaction.");
217 -
218 - }
219176
220 - function buildRequestXML(){
221 - $this->xmlDoc = new DomDocument('1.0');
222 - $node = $this->xmlDoc->createElement('XML');
223 -
224 - $structure = $this->transactions[$this->currentTransaction()]['request'];
225 -
226 - $this->buildTransactionNodes($structure, $node);
227 - $this->xmlDoc->appendChild($node);
228 - return $this->xmlDoc->saveXML();
229 - }
230 -
231 -
232 - function buildTransactionNodes($structure, &$node){
233 - $transaction = $this->currentTransaction();
234 -
235 - if (!is_array($structure)){ //this is a weird case that shouldn't ever happen. I'm just being... thorough. But, yeah: It's like... the base-1 case.
236 - $this->appendNodeIfValue($structure, $node);
237 - } else {
238 - foreach ($structure as $key => $value){
239 - if (!is_array($value)){
240 - //do not use $key. $key is meaningless in this case.
241 - $this->appendNodeIfValue($value, $node);
242 - } else {
243 - $keynode = $this->xmlDoc->createElement($key);
244 - $this->buildTransactionNodes( $value, $keynode);
245 - $node->appendChild($keynode);
246 - }
247 - }
248 - }
249 - //not actually returning anything. It's all side-effects. Because I suck like that.
250 - }
251 -
252 - function appendNodeIfValue($value, &$node){
253 - $nodevalue = $this->getValue($value, $this->currentTransaction());
254 - if ($nodevalue !== '' && $nodevalue !== false){
255 - $temp = $this->xmlDoc->createElement($value, $nodevalue);
256 - $node->appendChild($temp);
257 - }
258 - }
259 -
260 - function do_transaction($transaction){
261 - $this->currentTransaction($transaction);
262 - if ($this->getCommunicationType() === 'xml'){
 177+ function do_transaction( $transaction ) {
 178+ $this->currentTransaction( $transaction );
 179+ if ( $this->getCommunicationType() === 'xml' ) {
263180 $xml = $this->buildRequestXML();
264 - $response = $this->curl_transaction($xml);
 181+ $response = $this->curl_transaction( $xml );
265182 //put the response in a universal form, and return it.
266183 }
267 -
 184+
268185 //TODO: Actually pull these from somewhere legit.
269 - if ($response['status'] === true){
 186+ if ( $response['status'] === true ) {
270187 $response['message'] = "$transaction Transaction Successful!";
271 - } elseif ($response['status'] === false){
 188+ } elseif ( $response['status'] === false ) {
272189 $response['message'] = "$transaction Transaction FAILED!";
273190 } else {
274191 $response['message'] = "$transaction Transaction... weird. I have no idea what happened there.";
275192 }
276 -
 193+
277194 return $response;
278 -
 195+
279196 //speaking of universal form:
280197 //$result['status'] = something I wish could be boiled down to a bool, but that's way too optimistic, I think.
281198 //$result['message'] = whatever we want to display back?
@@ -282,265 +199,76 @@
283200 //$result['errors'][]['value'] = Error message
284201 //$result['return'][$whatever] = values they pass back to us for whatever reason. We might... log it, or pieces of it, or something?
285202 }
286 -
287 - function getCurlBaseOpts(){
288 - //I chose to return this as a function so it's easy to override.
289 - //TODO: probably this for all the junk I currently have stashed in the constructor.
290 - //...maybe.
291 - global $wgGlobalCollectTimeout, $wgPayflowGatewayUseHTTPProxy;
292 - $opts = array(
293 - CURLOPT_URL => $this->url,
294 - //CURLOPT_USERAGENT => Http::userAgent(),
295 - CURLOPT_HEADER => 1,
296 - CURLOPT_RETURNTRANSFER => 1,
297 - CURLOPT_TIMEOUT => $wgGlobalCollectTimeout,
298 - //CURLOPT_FOLLOWLOCATION => 0,
299 - //CURLOPT_SSL_VERIFYPEER => 0,
300 - //CURLOPT_SSL_VERIFYHOST => 2,
301 - //CURLOPT_FORBID_REUSE => true,
302 - CURLOPT_POST => 1,
303 - );
304203
305 - // set proxy settings if necessary
306 - if ( $wgPayflowGatewayUseHTTPProxy ) {
307 - $opts[CURLOPT_HTTPPROXYTUNNEL] = 1;
308 - $opts[CURLOPT_PROXY] = $wgPayflowGatewayHTTPProxy;
309 - }
310 - return $opts;
311 - }
312 -
313 -
314 - function getCurlBaseHeaders(){
315 - $headers = array (
316 - 'Content-Type: text/' . $this->getCommunicationType() . '; charset=utf-8',
317 - // 'X-VPS-Client-Timeout: 45',
318 - // 'X-VPS-Request-ID:' . $this->postdatadefaults['order_id'],
319 - );
320 - return $headers;
321 - }
322 -
323 - private function currentTransaction($transaction = ''){ //get&set in one!
324 - static $current_transaction;
325 - if ($transaction != ''){
326 - $current_transaction = $transaction;
327 - }
328 - if (!isset($current_transaction)){
329 - return false;
330 - }
331 - return $current_transaction;
332 - }
333 -
334 - /**
335 - * Sends a name-value pair string to Payflow gateway
336 - *
337 - * @param $data String: The exact thing we want to send.
338 - */
339 - private function curl_transaction( $data ) {
340 - global $wgOut; //TODO: Uhm, this shouldn't touch the view. Something further upstream should decide what to do with this.
341 -
342 - // TODO: This, but way before we get here.
343 - //$this->updateContributionTracking( $data, defined( 'OWA' ) );
344 -
345 - // assign header data necessary for the curl_setopt() function
346 -
347 - $ch = curl_init();
348 -
349 - $headers = $this->getCurlBaseHeaders();
350 - $headers[] = 'Content-Length: ' . strlen( $data );
351 -
352 - self::log("Sending Data: " . $data);
353 -
354 - $curl_opts = $this->getCurlBaseOpts();
355 - $curl_opts[CURLOPT_HTTPHEADER] = $headers;
356 - $curl_opts[CURLOPT_POSTFIELDS] = $data;
357 -
358 - foreach ($curl_opts as $option => $value){
359 - curl_setopt($ch, $option, $value);
360 - }
361 -
362 - // As suggested in the PayPal developer forum sample code, try more than once to get a response
363 - // in case there is a general network issue
364 - $i = 1;
365 -
366 - $return = array();
367 -
368 - while ( $i++ <= 3 ) {
369 - self::log( $this->postdatadefaults['order_id'] . ' Preparing to send transaction to GlobalCollect' );
370 - $return['result'] = curl_exec( $ch );
371 - $return['headers'] = curl_getinfo( $ch );
372 -
373 - if ( $return['headers']['http_code'] != 200 && $return['headers']['http_code'] != 403 ) {
374 - self::log( $this->postdatadefaults['order_id'] . ' Failed sending transaction to GlobalCollect, retrying' );
375 - sleep( 1 );
376 - } elseif ( $return['headers']['http_code'] == 200 || $return['headers']['http_code'] == 403 ) {
377 - self::log( $this->postdatadefaults['order_id'] . ' Finished sending transaction to GlobalCollect' );
378 - break;
379 - }
380 - }
381 -
382 - if ( $return['headers']['http_code'] != 200 ) {
383 - $return['result'] = false;
384 - //TODO: i18n here!
385 - $return['message'] = 'No response from credit card processor. Please try again later!';
386 - $when = time();
387 - self::log( $this->postdatadefaults['order_id'] . ' No response from credit card processor: ' . curl_error( $ch ) );
388 - curl_close( $ch );
389 - return $return;
390 - }
391 -
392 - curl_close( $ch );
393 - self::log("Results: " . print_r($return['result'], true));
394 -
395 -// if ($this->getCommunicationType() === 'namevalue'){
396 -// $return['result'] = strstr( $return['result'], 'RESULT' );
397 -// //TODO: Finish this for namevalue.
398 -// }
399 - if ($this->getCommunicationType() === 'xml'){
400 - //$return['result'] = $this->stripResponseHeaders($return['result']);
401 - $return['status'] = $this->parseXMLResponse($return['result']);
402 - }
403 -
404 - return $return;
405 -
406 - // parse string and display results to the user
407 - //TODO: NO NO NO. NO DISPLAY HERE.
408 - //$this->fnPayflowGetResults( $data, $return['result'] );
409 - }
410 -
411 - function parseXMLResponse($rawResponse){
 204+ function parseXMLResponse( $rawResponse ) {
412205 //TODO: Something.
413 - $rawXML = $this->stripResponseHeaders($rawResponse);
414 - if ($rawXML === false){
 206+ $rawXML = $this->stripResponseHeaders( $rawResponse );
 207+ if ( $rawXML === false ) {
415208 return false;
 209+ } else {
 210+ $rawXML = $this->formatXmlString( $rawXML );
416211 }
417 - $realXML = new DomDocument('1.0');
418 - self::log("Here is the Raw XML: " . $rawXML);
419 - $realXML->loadXML(trim($rawXML));
 212+ $realXML = new DomDocument( '1.0' );
 213+ self::log( "Here is the Raw XML: " . $rawXML );
 214+ $realXML->loadXML( trim( $rawXML ) );
420215
421216 $aok = true;
422 -
 217+
423218 //find the node specified by the transaction structure.
424219 //TODO: Error handling, because: Ugh.
425 - $result_structure = $this->transactions[$this->currentTransaction()]['result'];
426 - if(is_array($result_structure)){
427 - foreach ($result_structure as $key=>$value){ //should only be one.
428 - foreach($realXML->getElementsByTagName($key) as $node) {
429 - if ($value === 'value') { //...stupid. But it's 'value' as opposed to 'attribute'
430 - if (array_key_exists($node->nodeValue, $this->return_value_map) && $this->return_value_map[$node->nodeValue] !== true){
 220+ $result_structure = $this->transactions[$this->currentTransaction()]['result'];
 221+ if ( is_array( $result_structure ) ) {
 222+ foreach ( $result_structure as $key => $value ) { //should only be one.
 223+ foreach ( $realXML->getElementsByTagName( $key ) as $node ) {
 224+ if ( $value === 'value' ) { //...stupid. But it's 'value' as opposed to 'attribute'
 225+ if ( array_key_exists( $node->nodeValue, $this->return_value_map ) && $this->return_value_map[$node->nodeValue] !== true ) {
431226 $aok = false;
432 - }
 227+ }
433228 }
434 - if ($value === 'attribute') {
 229+ if ( $value === 'attribute' ) {
435230 //TODO: Figure this out. This should mean the key name of one array up, which sounds painful.
436231 }
437 - if (is_array($value)){
 232+ if ( is_array( $value ) ) {
438233 //TODO: ...this is looking like a recursive deal, here. Again. So do that.
439234 }
440235 }
441236 }
442237 }
443 -
 238+
444239 //TODO: Make this... you know: abstracty.
445 - if ($aok === false){
446 - foreach($realXML->getElementsByTagName('ERROR') as $node) {
 240+ if ( $aok === false ) {
 241+ foreach ( $realXML->getElementsByTagName( 'ERROR' ) as $node ) {
447242 $errdata = '';
448 - foreach($node->childNodes as $childnode){
 243+ foreach ( $node->childNodes as $childnode ) {
449244 $errdata .= "\n" . $childnode->nodeName . " " . $childnode->nodeValue;
450245 // if ($childnode->nodeName == "CODE"){
451246 // //Excellent place to deal with the particular codes if we want to.
452247 // }
453248 }
454 - self::log("ON NOES! ERROR: $errdata");
 249+ self::log( "ON NOES! ERROR: $errdata" );
455250 }
456251 } else {
457 - self::log("Response OK");
 252+ self::log( "Response OK" );
458253 }
459 -
 254+
460255 //TODO: The bit where you strip all the response data that we might want to save, toss it into an array, and hand it back.
461256 //We need to be handing more back here than just the... success or failure. Things need to be processed! I mean, probably.
462 -
463257
 258+
464259 return $aok;
465260 }
466261
467 - function stripResponseHeaders($rawResponse){
468 - $xmlStart = strpos($rawResponse, '<?xml');
469 - if ($xmlStart == false){ //I totally saw this happen one time. No XML, just <RESPONSE>...
470 - $xmlStart = strpos($rawResponse, '<RESPONSE');
 262+ function stripResponseHeaders( $rawResponse ) {
 263+ $xmlStart = strpos( $rawResponse, '<?xml' );
 264+ if ( $xmlStart == false ) { //I totally saw this happen one time. No XML, just <RESPONSE>...
 265+ $xmlStart = strpos( $rawResponse, '<RESPONSE' );
471266 }
472 - if ($xmlStart == false){ //Still false. Your Head Asplode.
473 - self::log("Wow, that was so messed up I couldn't even parse the response, so here's the thing in its entirety:\n" . $rawResponse, true);
 267+ if ( $xmlStart == false ) { //Still false. Your Head Asplode.
 268+ self::log( "Wow, that was so messed up I couldn't even parse the response, so here's the thing in its entirety:\n" . $rawResponse );
474269 return false;
475270 }
476 - $justXML = substr($rawResponse, $xmlStart);
 271+ $justXML = substr( $rawResponse, $xmlStart );
477272 return $justXML;
478273 }
479 -
480 -
481 - public static function log( $msg, $identifier='globalcollect_gateway', $log_level=LOG_INFO ) {
482 - global $wgGlobalCollectGatewayUseSyslog;
483 -
484 - // if we're not using the syslog facility, use wfDebugLog
485 - if ( !$wgGlobalCollectGatewayUseSyslog ) {
486 - wfDebugLog( $identifier, $msg );
487 - return;
488 - }
489 -
490 - // otherwise, use syslogging
491 - openlog( $identifier, LOG_ODELAY, LOG_SYSLOG );
492 - syslog( $log_level, $msg );
493 - closelog();
494 - }
495274
496 -
497 - //_______________________________________________________________
498 - //copied from payflowpro_gateway/includes/payflowUser.inc
499 -
500 - /**
501 - * Fetch and return the 'order_id' for a transaction
502 - *
503 - * Since transactions to PayPal are initially matched internally on their end
504 - * with the 'order_id' field, but we don't actually care what the order id is,
505 - * we generate a sufficiently random number to avoid duplication.
506 - *
507 - * We go ahead and always generate a random order id becuse if PayPal detects
508 - * the same order_id more than once, it considers the request a duplicate, even
509 - * if the data is completely different.
510 - *
511 - * @return int
512 - */
513 - function getOrderId() {
514 - return $this->generateOrderId();
515 - }
516 -
517 - /**
518 - * Generate an internal order id
519 - *
520 - * This is only used internally for tracking a user's 'session' with the credit
521 - * card form. I mean 'session' in the sense of the moment a credit card page
522 - * is loaded for the first time (nothing posted to it - a discrete donation
523 - * session) as opposed to the $_SESSION - as the $_SESSION id could potentially
524 - * not change between contribution attempts.
525 - */
526 - function getInternalOrderId() {
527 - global $wgRequest;
528 -
529 - // is an order_id already set?
530 - //TODO: Change all these to look instead at $this->postdata... I think.
531 - $i_order_id = $wgRequest->getText( 'i_order_id', 0 );
532 -
533 - // if the form was not just posted OR there's no order_id set, generate one.
534 - if ( !$wgRequest->wasPosted() || !$i_order_id ) {
535 - $i_order_id = $this->generateOrderId();
536 - }
537 -
538 - return $i_order_id;
539 - }
540 -
541 - /**
542 - * Generate an order id
543 - */
544 - function generateOrderId() {
545 - return (double) microtime() * 1000000 . mt_rand(1000, 9999);
546 - }
547275 }
Index: branches/fundraising/extensions/DonationInterface/globalcollect_gateway/globalcollect_gateway.php
@@ -2,20 +2,20 @@
33
44 # Alert the user that this is not a valid entry point to MediaWiki if they try to access the special pages file directly.
55 if ( !defined( 'MEDIAWIKI' ) ) {
6 - echo <<<EOT
 6+ echo <<<EOT
77 To install GlobalCollect Gateway extension, put the following line in LocalSettings.php:
88 require_once( "\$IP/extensions/globalcollect_gateway/globalcollect_gateway.php" );
99 EOT;
10 - exit( 1 );
 10+ exit( 1 );
1111 }
1212
1313 // Extension credits that will show up on Special:Version
1414 $wgExtensionCredits['specialpage'][] = array(
15 - 'name' => 'GlobalCollect Gateway',
16 - 'author' => 'Four Kitchens',
17 - 'version' => '1.0.0',
18 - 'descriptionmsg' => 'globalcollect_gateway-desc',
19 - 'url' => 'http://www.mediawiki.org/wiki/Extension:GlobalCollectGateway',
 15+ 'name' => 'GlobalCollect Gateway',
 16+ 'author' => 'Four Kitchens',
 17+ 'version' => '1.0.0',
 18+ 'descriptionmsg' => 'globalcollect_gateway-desc',
 19+ 'url' => 'http://www.mediawiki.org/wiki/Extension:GlobalCollectGateway',
2020 );
2121
2222 $wgGlobalCollectGatewayUseSyslog = false;
@@ -48,14 +48,11 @@
4949 $wgExtensionAliasesFiles['GlobalCollectGateway'] = $dir . '../payflowpro_gateway/payflowpro_gateway.alias.php';
5050 $wgSpecialPages['GlobalCollectGateway'] = 'GlobalCollectGateway';
5151 //$wgAjaxExportList[] = "fnGlobalCollectofofWork";
52 -
53 -
5452 // set defaults, these should be assigned in LocalSettings.php
5553 $wgGlobalCollectURL = 'https://ps.gcsip.nl/wdl/wdl';
5654 $wgGlobalCollectTestingURL = 'https://'; // GlobalCollect testing URL
5755
5856 $wgGlobalCollectMerchantID = ''; // GlobalCollect ID
59 -
6057 // a boolean to determine if we're in testing mode
6158 $wgGlobalCollectGatewayTest = FALSE;
6259
@@ -130,7 +127,7 @@
131128 * /never/ be loaded by the rapid html form loader!
132129 * @var string
133130 */
134 -$wgGlobalCollectAllowedHtmlForms = array( $wgGlobalCollectHtmlFormDir . "/demo.html" );
 131+$wgGlobalCollectAllowedHtmlForms = array( $wgGlobalCollectHtmlFormDir . "/demo.html" );
135132
136133 /**
137134 * Configure price cieling and floor for valid contribution amount. Values

Follow-up revisions

RevisionCommit summaryAuthorDate
r99589Prevents us from dying (no, really)....khorn22:21, 11 October 2011
r100896Mostly concerned with splitting currentTransaction into getCurrentTransaction...khorn23:06, 26 October 2011

Comments

#Comment by Khorn (WMF) (talk | contribs)   18:39, 23 September 2011

Please note that this branch is in the middle of a serious refactoring, and is by no means ready to be merged back into trunk. (Latest DonationInterface branch rev at the time of this comment is r97833.)

#Comment by Jpostlethwaite (talk | contribs)   19:23, 23 September 2011

This code is in development.

#Comment by Awjrichards (talk | contribs)   00:10, 30 September 2011

I am seeing things like:

die( 'Transactions structure is empty! Aborting.' );

Remember: we do not want our donors to experience death. This should probably turn into an exception that gets gracefully handled somewhere, or otherwise be handled gracefully.

#Comment by Khorn (WMF) (talk | contribs)   22:22, 11 October 2011

Fixed in r99589

#Comment by Kaldari (talk | contribs)   22:07, 21 October 2011

It looks like parseXMLResponse has been replaced with other things so that is moot.

Is there a reason we want to get and set the current transaction in the same method. This makes the code confusing, in my opinion. Would there be any downside to splitting it into 2 methods (other than a few extra lines of code)?

Some of these methods need comment descriptions as their purpose isn't obvious without carefully reading the code, for example, buildTransactionNodes and appendNodeIfValue. And since do_transaction is the most important method here, it should probably have at least a basic description.

Looks like there's lots of defensive programming here, which is great, and your comments are hilarious :)

#Comment by Khorn (WMF) (talk | contribs)   22:54, 21 October 2011

Basically, the only reason I did current transaction as both a get and a set, was so I could take advantage of a static variable in the one function. In short: It was quicker. But as it seems to be confusing, I have no problem moving it somewhere external and having a get/set pair.

Additionally: Proper function documentation has been on my TODO list for about a month now. I promise it's moving up the priority stack.

But, yes: These things should absolutely be fixed.

#Comment by Khorn (WMF) (talk | contribs)   23:15, 26 October 2011

r100896 addresses splitting currentTransaction into getCurrentTransaction and setCurrentTransaction. Also, I've commented as many functions in the main gateway.adapter class as I had time for today.

Status & tagging log