r71653 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r71652‎ | r71653 | r71654 >
Date:19:19, 25 August 2010
Author:awjrichards
Status:ok
Tags:
Comment:
Removed PayPal IPN listener and PayflowPro/Stomp processor. These were providing very custom functionality which is now being handled outside of MW and should not be maintained here.
Modified paths:
  • /trunk/extensions/DonationInterface/payflowpro_gateway/stompPFPPendingProcessor.php (deleted) (history)
  • /trunk/extensions/DonationInterface/paypal_gateway/IPN (deleted) (history)

Diff [purge]

Index: trunk/extensions/DonationInterface/payflowpro_gateway/stompPFPPendingProcessor.php
@@ -1,413 +0,0 @@
2 -<?php
3 -/**
4 - * Processes pending PayflowPro transactions in a queueing service using Stomp
5 - *
6 - * Uses the MediaWiki maintenance system for command line execution. Requires, at the
7 - * very least, the Maintenance.php file found in Mediawiki > 1.16. If you are running
8 - * MW < 1.16, you can grab a copy of Maintenance.php and place it somewhere accessible
9 - * and run this script with:
10 - * php stompPFPPendingProcessor.php -m "/path/to/Maintenance.php"
11 - *
12 - * Also note all the other config options that can be passed in via the CLI arguments
13 - * below.
14 - *
15 - * This was built to verify pending PayflowPro transactions in an ActiveMQ queue system.
16 - * It pulls a transaction out of a 'pending' queue and submits the message to PayflowPro
17 - * for verification. If PayflowPro verifies the transaction, it is then passed off to a
18 - * 'confirmed' queue for processing elsewhere. If PayflowPro rejects for a small variety
19 - * of reasons (ones that require some user intervention), the message is reloaded to the
20 - * queue. If PayflowPro completely rejects the message, it is pruned from the queue
21 - * altogether.
22 - *
23 - * This performs some logging (depending on the log_level setting), which if not set to 0
24 - * just gets output to the screen.
25 - *
26 - * Config options (key = var name for localSettings.php, value = command line arg name):
27 - * $options = array (
28 - * 'wgPayflowProURL' => 'pfp-url',
29 - * 'wgPayflowProPartnerID' => 'pfp-partner-id',
30 - * 'wgPayflowProVendorID' => 'pfp-vendor-id',
31 - * 'wgPayflowProUserID' => 'pfp-user-id',
32 - * 'wgPayflowProPassword' => 'pfp-password',
33 - * 'wgActiveMQStompURI' => 'activemq-stomp-uri',
34 - * 'wgActiveMQPendingPath' => 'activemq-pending-queue',
35 - * 'wgActiveMQConfirmedPath' => 'activemq-confirmed-queue',
36 - * 'wgActiveMQPendingProcessingBatchSize' => 'batch-size',
37 - * 'wgActiveMQPendingProcessLogLevel' => 'log-level' );
38 - * Each of these config options gets set as a class property with the name of the command line arg,
39 - * with the '-' replaced with a '_' (eg 'pfp-url' becomes $this->pfp_url).
40 - *
41 - * @author: Arthur Richards <arichards@wikimedia.org>
42 - */
43 -
44 -// add optional Maintenance.php argument for cli (useful for systems < 1.16)
45 -$opts = getopt( "m:" );
46 -// if the path is not specified from CLI, default Maintenance.php path
47 -$maint = ( $opts['m'] ) ? $opts['m'] : MW_INSTALL_PATH . "/maintenance/Maintenance.php";
48 -require_once( $maint );
49 -
50 -// load necessary stomp files from DonationInterface/active_mq
51 -//require_once( dirname( __FILE__ ) . "/../extensions/DonationInterface/activemq_stomp/Stomp.php" );
52 -require_once('/var/www/sites/all/modules/queue2civicrm/Stomp.php'); // why are these Stomps different?!
53 -//require_once( dirname( __FILE__ ) . "/../extensions/DonationInterface/activemq_stomp/Stomp/Exception.php" );
54 -require_once('/var/www/sites/all/modules/queue2civicrm/Stomp/Exception.php');
55 -
56 -define( 'LOG_LEVEL_QUIET', 0 ); // disables all logging
57 -define( 'LOG_LEVEL_INFO', 1 ); // some useful logging information
58 -define( 'LOG_LEVEL_DEBUG', 2 ); // verbose logging for debug
59 -
60 -class StompPFPPendingProcessor extends Maintenance {
61 -
62 - /** If TRUE, output extra information for debug purposes **/
63 - protected $log_level = LOG_LEVEL_INFO;
64 -
65 - /** Holds our Stomp connection instance **/
66 - protected $stomp;
67 -
68 - /** The number of items to process **/
69 - protected $batch_size = 50;
70 -
71 - public function __construct() {
72 - parent::__construct();
73 -
74 - // register command line params with the parent class
75 - $this->register_params();
76 - }
77 -
78 - public function execute() {
79 - // load configuration options
80 - $this->load_config_options();
81 - $this->log( "Pending queue processor bootstrapped and ready to go!" );
82 -
83 - // estamplish a connection to the stomp listener
84 - $this->set_stomp_connection();
85 -
86 - $this->log( "Preparing to process up to {$this->batch_size} pending transactions.", LOG_LEVEL_DEBUG );
87 -
88 - // batch process pending transactions
89 - for ( $i = 0; $i < $this->batch_size; $i++ ) {
90 - // empty pending_transaction
91 - if ( isset( $message )) unset( $message );
92 -
93 - // fetch the latest pending transaction from the queue (Stomp_Frame object)
94 - $message = $this->fetch_message( $this->activemq_pending_queue );
95 - // if we do not get a pending transaction back...
96 - if ( !$message ) {
97 - $this->log( "There are no more pending transactions to process.", LOG_LEVEL_DEBUG );
98 - break;
99 - }
100 -
101 - // the message is in it's raw format, we need to decode just it's body
102 - $pending_transaction = json_decode( $message->body, TRUE );
103 - $this->log( "Pending transaction: " . print_r( $pending_transaction, TRUE ), LOG_LEVEL_DEBUG );
104 -
105 - // fetch the payflow pro status of this transaction
106 - $status = $this->fetch_payflow_transaction_status( $pending_transaction['gateway_txn_id'] );
107 -
108 - // determine the result code from the payflow pro status message
109 - $result_code = $this->parse_payflow_transaction_status( $status );
110 -
111 - // handle the pending transaction based on the payflow pro result code
112 - $this->handle_pending_transaction( $result_code, json_encode( $pending_transaction ));
113 -
114 - $ack_response = $this->stomp->ack( $message );
115 - $this->log( "Ack response: $ack_response", LOG_LEVEL_DEBUG );
116 - }
117 - $this->log( "Processed $i messages." );
118 - }
119 -
120 - /**
121 - * Fetch latest raw message from a queue
122 - *
123 - * @param $destination string of the destination path from where to fetch a message
124 - * @return mixed raw message (Stomp_Frame object) from Stomp client or False if no msg present
125 - */
126 - protected function fetch_message( $destination ) {
127 - $this->log( "Attempting to connect to queue at: $destination", LOG_LEVEL_DEBUG );
128 -
129 - $this->stomp->subscribe( $destination );
130 -
131 - $this->log( "Attempting to pull queued item", LOG_LEVEL_DEBUG );
132 - $message = $this->stomp->readFrame();
133 - return $message;
134 - }
135 -
136 - /**
137 - * Send a message to the queue
138 - *
139 - * @param $destination string of the destination path for where to send a message
140 - * @param $message string the (formatted) message to send to the queue
141 - * @param $options array of additional Stomp options
142 - * @return bool result from send, FALSE on failure
143 - */
144 - protected function queue_message( $destination, $message, $options = array( 'persistent' => TRUE )) {
145 - $this->log( "Attempting to queue message...", LOG_LEVEL_DEBUG );
146 - $sent = $this->stomp->send( $destination, $message, $options );
147 - $this->log( "Result of queuing message: $sent", LOG_LEVEL_DEBUG );
148 - return $sent;
149 - }
150 -
151 - /**
152 - * Fetch the PayflowPro status of a transaction.
153 - *
154 - * @param $transaction_id string of the original ID of the transaction to status check
155 - * @return string containing the raw status message returned by PayflowPro
156 - */
157 - protected function fetch_payflow_transaction_status( $transaction_id ) {
158 - $this->log( "Transaction ID: $transaction_id", LOG_LEVEL_DEBUG );
159 - // create payflow query string, include string lengths
160 - $queryArray = array(
161 - 'TRXTYPE' => 'I',
162 - 'TENDER' => 'C',
163 - 'USER' => $this->pfp_user_id, //$payflow_data['user'],
164 - 'VENDOR' => $this->pfp_vendor_id, //$payflow_data['vendor'],
165 - 'PARTNER' => $this->pfp_partner_id, //$payflow_data['partner'],
166 - 'PWD' => $this->pfp_password, //$payflow_data['password'],
167 - 'ORIGID' => $transaction_id,
168 - );
169 - $this->log( "PayflowPro query array: " . print_r( $queryArray, TRUE ), LOG_LEVEL_DEBUG );
170 -
171 - // format the query string for PayflowPro
172 - foreach ( $queryArray as $name => $value ) {
173 - $query[] = $name . '[' . strlen( $value ) . ']=' . $value;
174 - }
175 - $payflow_query = implode( '&', $query );
176 - $this->log( "PayflowPro query array (formatted): " . print_r( $payflow_query, TRUE ), LOG_LEVEL_DEBUG );
177 -
178 - // assign header data necessary for the curl_setopt() function
179 - $order_id = date( 'ymdH' ) . rand( 1000, 9999 ); //why?
180 - $user_agent = $_SERVER['HTTP_USER_AGENT'];
181 - $headers[] = 'Content-Type: text/namevalue';
182 - $headers[] = 'Content-Length : ' . strlen( $payflow_query );
183 - $headers[] = 'X-VPS-Client-Timeout: 45';
184 - $headers[] = 'X-VPS-Request-ID:' . $order_id;
185 - $ch = curl_init();
186 -
187 - curl_setopt( $ch, CURLOPT_URL, $this->pfp_url );
188 - curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
189 - curl_setopt( $ch, CURLOPT_USERAGENT, $user_agent );
190 - curl_setopt( $ch, CURLOPT_HEADER, 1 );
191 - curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
192 - curl_setopt( $ch, CURLOPT_TIMEOUT, 90 );
193 - curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 0 );
194 - curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
195 - curl_setopt( $ch, CURLOPT_POSTFIELDS, $payflow_query );
196 - curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 );
197 - curl_setopt( $ch, CURLOPT_FORBID_REUSE, true );
198 - curl_setopt( $ch, CURLOPT_POST, 1 );
199 -
200 - // As suggested in the PayPal developer forum sample code, try more than once to get a response
201 - // in case there is a general network issue
202 - for ( $i=1; $i <=3; $i++ ) {
203 - $this->log( "Attempt #$i to connect to PayflowPro...", LOG_LEVEL_DEBUG );
204 - $status = curl_exec( $ch );
205 - $headers = curl_getinfo( $ch );
206 -
207 - if ( $headers['http_code'] != 200 && $headers['http_code'] != 403 ) {
208 - sleep( 5 );
209 - } elseif ( $headers['http_code'] == 200 || $headers['http_code'] == 403 ) {
210 - $this->log( "Succesfully connected to PayflowPro", LOG_LEVEL_DEBUG );
211 - break;
212 - }
213 - }
214 -
215 - if ( $headers['http_code'] != 200 ) {
216 - $this->log( "No response from PayflowPro after $i attempts." );
217 - curl_close( $ch );
218 - exit(1);
219 - }
220 -
221 - curl_close( $ch );
222 -
223 - $this->log( "PayflowPro reported status: $status", LOG_LEVEL_DEBUG );
224 - return $status;
225 - }
226 -
227 - /**
228 - * Parse the result code out of PayflowPro's status message.
229 - *
230 - * This is modified from queue2civicrm_payflow_result() in the Drupal queue2civicrm module.
231 - * That code, however, seemed to be cataloging all of the key/value pairs in the status
232 - * message. Since we only care about the result code, that's all I'm looking for.
233 - * Perhaps it is more favorable to return an aray of the key/value pairs in the status
234 - * message...
235 - *
236 - * @status string The full status message returned by a PayflowPro queyry
237 - * @return int PayflowPro result code, FALSE on failure
238 - */
239 - protected function parse_payflow_transaction_status( $status ) {
240 - // we only really care about the 'RESULT' portion of the status message
241 - $result = strstr( $status, 'RESULT' );
242 -
243 - // log the result string?
244 - $this->log( "PayflowPro RESULT string: $result", LOG_LEVEL_DEBUG );
245 -
246 - // establish our key/value positions in the string to facilitate extracting the value
247 - $key_position = strpos( $result, '=' );
248 - $value_position = strpos( $result, '&' ) ? strpos( $result, '&' ) : strlen( $result) ;
249 -
250 - $result_code = substr( $result, $key_position + 1, $value_position - $key_position - 1 );
251 - $this->log( "PayflowPro result code: $result_code", LOG_LEVEL_DEBUG );
252 - return $result_code;
253 - }
254 -
255 - /**
256 - * Apropriately handles pending transactions based on the PayflowPro result code
257 - *
258 - * @param int PayflowPro result code
259 - * @param string Formatted message to send to a queue
260 - */
261 - protected function handle_pending_transaction( $result_code, $message ) {
262 - switch ( $result_code ) {
263 - case "0": // push to confirmed queue
264 - $this->log( "Attempting to push message to confirmed queue: " . print_r( $message, TRUE ), LOG_LEVEL_DEBUG );
265 - if ( $this->queue_message( $this->activemq_confirmed_queue, $message )) {
266 - $this->log( "Succesfully pushed message to confirmed queue.", LOG_LEVEL_DEBUG );
267 - }
268 - break;
269 - case "126": // push back to pending queue
270 - case "26": //push back to pending queue
271 - $this->log( "Attempting to push message back to pending queue: " . print_r( $message, TRUE ), LOG_LEVEL_DEBUG );
272 - if ( $this->queue_message( $this->activemq_pending_queue, $message )) {
273 - $this->log( "Succesfully pushed message back to pending queue", LOG_LEVEL_DEBUG );
274 - }
275 - break;
276 - default:
277 - $this->log( "Message ignored: " . print_r( $message, TRUE ), LOG_LEVEL_DEBUG );
278 - break;
279 - }
280 - }
281 -
282 - /**
283 - * Loads configuration options
284 - *
285 - * Config options can be set in localSettings.php or with arguments passed in via the command
286 - * line. Command line arguments will override localSettings.php defined options.
287 - */
288 - protected function load_config_options() {
289 - // Associative array of mediawiki option => maintenance arg name
290 - $options = array (
291 - 'wgPayflowProURL' => 'pfp-url',
292 - 'wgPayflowProPartnerID' => 'pfp-partner-id',
293 - 'wgPayflowProVendorID' => 'pfp-vendor-id',
294 - 'wgPayflowProUserID' => 'pfp-user-id',
295 - 'wgPayflowProPassword' => 'pfp-password',
296 - 'wgActiveMQStompURI' => 'activemq-stomp-uri',
297 - 'wgActiveMQPendingPath' => 'activemq-pending-queue',
298 - 'wgActiveMQConfirmedPath' => 'activemq-confirmed-queue',
299 - 'wgActiveMQPendingProcessingBatchSize' => 'batch-size',
300 - 'wgActiveMQPendingProcessLogLevel' => 'log-level' );
301 -
302 - // loop through our options and set their values,
303 - // overrides local settings with command line options
304 - foreach ( $options as $mw_option => $m_arg_name ) {
305 - global ${$mw_option};
306 -
307 - // replace - with _ from CL args to map to class properties
308 - $property = str_replace( "-", "_", $m_arg_name );
309 -
310 - // set class property with the config option
311 - $this->$property = parent::getOption( $m_arg_name, ${$mw_option} );
312 -
313 - $this->log( "$property = $mw_option, $m_arg_name, ${$mw_option}", LOG_LEVEL_DEBUG );
314 - $this->log( "$property = {$this->$property}", LOG_LEVEL_DEBUG );
315 - }
316 - }
317 -
318 - /**
319 - * Registers parameters with the maintenance system
320 - */
321 - protected function register_params() {
322 - //parent::addOption( $name, $description, $required=false, $withArg=false )
323 - parent::addOption( 'pfp-url', 'The PayflowPro URL', FALSE, TRUE );
324 - parent::addOption(
325 - 'pfp-partner-id',
326 - 'Authorized reseller ID for PayflowPro (eg "PayPal")',
327 - FALSE,
328 - TRUE );
329 - parent::addOption( 'pfp-vendor-id', 'The PayflowPro merchant login ID', FALSE, TRUE );
330 - parent::addOption(
331 - 'pfp-user-id',
332 - "If one or more users are set up, this should be the authorized PayflowPro user ID, otherwise this should be the same as pfp-vendor-id",
333 - FALSE,
334 - TRUE );
335 - parent::addOption(
336 - 'pfp-password',
337 - 'The PayflowPro merchant login password. ** Declaring this here could be a security risk.',
338 - FALSE,
339 - TRUE );
340 - parent::addOption(
341 - 'activemq-stomp-uri',
342 - 'The URI to the ActiveMQ Stomp listener',
343 - FALSE,
344 - TRUE );
345 - parent::addOption(
346 - 'activemq-pending-queue',
347 - 'The path to the ActiveMQ pending queue',
348 - FALSE,
349 - TRUE );
350 - parent::addOption(
351 - 'activemq-confirmed-queue',
352 - 'The path to the ActiveMQ confirmed queue',
353 - FALSE,
354 - TRUE );
355 - // I know there is a method to handle batch size this in the parent class, but it makes me
356 - // do things I don't want to do.
357 - parent::addOption(
358 - 'batch-size',
359 - 'The number of queue items to process. Default: ' . $this->batch_size,
360 - FALSE,
361 - TRUE );
362 - parent::addOption(
363 - 'log-level',
364 - "The level of logging you would like to enable. Options:\n\t0 - No output\n\t1 - Normal, minimal output\n\t2 - Debug, verbose output",
365 - FALSE,
366 - TRUE );
367 - }
368 -
369 - /**
370 - * Establishes a connection to the stomp listener
371 - *
372 - * Stomp listner URI set in config options (via command line or localSettings.php).
373 - * If a connection cannot be established, will exit with non-0 status.
374 - */
375 - protected function set_stomp_connection() {
376 - //attempt to connect, otherwise throw exception and exit
377 - $this->log( "Attempting to connect to Stomp listener: {$this->activemq_stomp_uri}", LOG_LEVEL_DEBUG );
378 - try {
379 - //establish stomp connection
380 - $this->stomp = new Stomp( $this->activemq_stomp_uri );
381 - $this->stomp->connect();
382 - $this->log( "Successfully connected to Stomp listener", LOG_LEVEL_DEBUG );
383 - } catch (Stomp_Exception $e) {
384 - $this->log( "Stomp connection failed: " . $e->getMessage() );
385 - exit(1);
386 - }
387 - }
388 -
389 - /**
390 - * Logs messages of less than or equal value to the defined log level.
391 - *
392 - * Log levels available are defined by the constants LOG_LEVEL_*. The log level for the script
393 - * defaults to LOG_LEVEL_INFO but can be overridden in LocalSettings.php or via a command line
394 - * argument passed in at run time.
395 - *
396 - * @param $message string containing the message you wish to log
397 - * @param $level int of the highest log level you wish to output messages for
398 - */
399 - protected function log( $message, $level=LOG_LEVEL_INFO ) {
400 - if ( $this->log_level >= $level ) echo date('c') . ": " . $level . " : " . $message . "\n";
401 - }
402 -
403 - public function __destruct() {
404 - // clean up our stomp connection
405 - $this->log( "Cleaning up stomp connection...", LOG_LEVEL_DEBUG );
406 - if ( isset( $this->stomp )) $this->stomp->disconnect();
407 - $this->log( "Stomp connection cleaned up", LOG_LEVEL_DEBUG );
408 - $this->log( "Exiting gracefully" );
409 -
410 - }
411 -}
412 -
413 -$maintClass = "StompPFPPendingProcessor";
414 -require_once( DO_MAINTENANCE );

Status & tagging log