r69713 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r69712‎ | r69713 | r69714 >
Date:00:53, 22 July 2010
Author:awjrichards
Status:reverted (Comments)
Tags:
Comment:
* Removed SpecialPaypalIPNProcess.php in favor of paypal_gateway/IPN/PyapalIPNListener.php, which can be leveraged by someone else and/or some other time for more tightly integrating a PayPal IPN listener with Mediawiki
* Added paypal_gateway/IPN/PaypalIPNListener.php which is a class that can be used as a PayPal IPN listener and to push messages from the IPN to ActiveMQ
* Also added an example script for how to use the IPN listener as a stand alone script.
Modified paths:
  • /trunk/extensions/DonationInterface/SpecialPaypalIPNProcessing.php (deleted) (history)
  • /trunk/extensions/DonationInterface/paypal_gateway/IPN (added) (history)
  • /trunk/extensions/DonationInterface/paypal_gateway/IPN/PaypalIPNListener.php (added) (history)
  • /trunk/extensions/DonationInterface/paypal_gateway/IPN/StandaloneListener.php.example (added) (history)

Diff [purge]

Index: trunk/extensions/DonationInterface/SpecialPaypalIPNProcessing.php
@@ -1,261 +0,0 @@
2 -<?php
3 -/**
4 - * Special class to act as IPN listener and handler. Also pushes messages into the ActiveMQ
5 - * queueing system.
6 - *
7 - * NOTE: THIS IS EXPERIMENTAL AND INCOMPLETE
8 - *
9 - * Requires ContributionTracking extension.
10 - *
11 - * Configurable variables:
12 - * $wgPayPalIPNProcessingLogLevel - can be one of the defined LOG_LEVEL_* constants.
13 - *
14 - * PayPal IPN docs: https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_admin_IPNIntro
15 - *
16 - * @author Arthur Richards <arichards@wikimedia.org>
17 - * @TODO: add a better mechanism for changing log level
18 - */
19 -
20 -/** Set available log levels **/
21 -DEFINE( 'LOG_LEVEL_QUIET', 0 ); // output nothing
22 -DEFINE( 'LOG_LEVEL_INFO', 1 ); // output minimal info
23 -DEFINE( 'LOG_LEVEL_DEBUG', 2 ); // output lots of info
24 -
25 -class PaypalIPNProcessing extends UnlistedSpecialPage {
26 -
27 - // set the apropriate logging level
28 - protected $log_level = LOG_LEVEL_INFO;
29 -
30 - // path to Stomp
31 - protected $stomp_path = dirname( __FILE__ ) . "/../../activemq_stomp/Stomp.php";
32 -
33 - // path to pending queue
34 - protected $pending_queue = '/queue/pending_paypal';
35 -
36 - function __construct() {
37 - parent::__construct( 'PaypalIPNProcessing' );
38 - wfLoadExtensionMessages( 'PaypalIPNProcessing' );
39 - $this->out( "Loading Paypal IPN processor" );
40 -
41 - if ( isset( $wgPayPalIPNProcessingLogLevel )) {
42 - $this->log_level = $wgPayPalIPNProcessingLogLevel;
43 - }
44 -
45 - if ( isset( $wgPayPalIPNProcessingStompPath )) {
46 - $this->stomp_path = $wgPayPalIPNProcessingStompPath;
47 - }
48 -
49 - if ( isset( $wgPayPalIPNProcessingPendingQueue )) {
50 - $this->pending_queue = $wgPayPalIPNProcessingPendingQueue;
51 - }
52 - }
53 -
54 - /**
55 - * Output in plain text?
56 - */
57 - function execute( $par ) {
58 - global $wgRequest, $wgOut;
59 - $wgOut->disable();
60 - header( "Content-type: text/plain; charset=utf-8" );
61 -
62 - //make sure we're actually getting something posted to the page.
63 - if ( empty( $_POST )) {
64 - $this->out( "Received an empty post object." );
65 - return;
66 - }
67 -
68 - // connect to stomp
69 - $this->set_stomp_connection();
70 -
71 - //push message to pending queue
72 - $contribution = $this->ipn_parse( $_POST );
73 - // do the queueing - perhaps move out the tracking checking to its own func?
74 - if ( !$this->queue_message( $this->pending_queue, $contribution )){
75 - $this->out( "There was a problem queueing the message to the queue: " . $this->pending_queue );
76 - $this->out( "Message: " . print_r( $contribution, TRUE ), LOG_LEVEL_DEBUG );
77 - }
78 -
79 -
80 - //verify the message with PayPal
81 - if ( !$this->ipn_verify( $_POST )) {
82 - $this->out( "Message did not pass PayPal verification." );
83 - $this->out( "\$_POST contents: " . print_r( $_POST, TRUE ), LOG_LEVEL_DEBUG );
84 - return;
85 - }
86 -
87 - //push to donations queue, remove from pending
88 - }
89 -
90 - /**
91 - * Verify IPN's message validitiy
92 - *
93 - * Yoinked from fundcore_paypal_verify() in fundcore/gateways/fundcore_paypal.module Drupal module
94 - * @param $post_data array of post data - the message received from PayPal
95 - * @return bool
96 - */
97 - protected function ipn_verify( $post_data ) {
98 - if ( $post_data[ 'payment_status' ] != 'Completed' ) {
99 - // order not completed
100 - $this->out( "Message not marked as complete." );
101 - return FALSE;
102 - }
103 -
104 - if ( $post_data[ 'mc_gross' ] <= 0 ) {
105 - $this->out( "Message has 0 or less in the mc_gross field." );
106 - return FALSE;
107 - }
108 -
109 - // url to respond to paypal with verification response
110 - $postback_url = 'https://www.paypal.com/cgi-bin/webscr';
111 - if (isset($post_data['test_ipn'])) {
112 - $postback_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
113 - }
114 -
115 - // respond with exact same data/structure + cmd=_notify-validate
116 - $attr = $post_data;
117 - $attr['cmd'] = '_notify-validate';
118 -
119 - // send the message back to PayPal for verification
120 - $status = $this->curl_download( $postback_url, $attr );
121 - if ($status != 'VERIFIED') {
122 - $this->out( "The message could not be verified." );
123 - $this->out( "Returned with status: $status", LOG_LEVEL_DEBUG );
124 - return FALSE;
125 - }
126 -
127 - return TRUE;
128 - }
129 -
130 - /**
131 - * Parse the PayPal message/post data into the format we need for ActiveMQ
132 - *
133 - * @param $post_data array containing the $_POST data from PayPal
134 - * @return array containing the parsed/formatted message for stuffing into ActiveMQ
135 - */
136 - protected function ipn_parse( $post_data ) {
137 - $contribution = array();
138 -
139 - $timestamp = strtotime($post_data['payment_date']);
140 -
141 - // Detect if we're using the new-style
142 - if (is_numeric($post_data['option_selection1'])) {
143 - // get the database connection to the tracking table
144 - $tracking_db = contributionTrackingConnection();
145 -
146 - // Query from Drupal: $tracking_data = db_fetch_array(db_query('SELECT * FROM {contribution_tracking} WHERE id = %d', $post_data['option_selection1']));
147 - $tracking_query = $tracking_db->select(
148 - 'contribution_tracking',
149 - array( 'optout', 'anonymous', 'note' ),
150 - array( 'id' => $post_data[ 'option_selection1' ]);
151 - $tracking_data = $tracking_query->fetchRow();
152 -
153 - $contribution['contribution_tracking_id'] = $post_data['option_selection1'];
154 - $contribution['optout'] = $tracking_data['optout'];
155 - $contribution['anonymous'] = $tracking_data['anonymous'];
156 - $contribution['comment'] = $tracking_data['note'];
157 - } else {
158 - $split = explode(';', $post_data['option_selection1']);
159 - $contribution['anonymous'] = ($split[0] != 'public' && $split[0] != 'Mention my name');
160 - $contribution['comment'] = $post_data['option_selection2'];
161 - }
162 -
163 - $contribution['email'] = $post_data['payer_email'];
164 - $contribution['first_name'] = $post_data['first_name'];
165 - $contribution['last_name'] = $post_data['last_name'];
166 -
167 - $split = split("\n", str_replace("\r", '', $post_data['address_street']));
168 -
169 - $contribution['street_address'] = $split[0];
170 - $contribution['supplemental_address_1'] = $split[1];
171 - $contribution['city'] = $post_data['address_city'];
172 - $contribution['original_currency'] = $post_data['mc_currency'];
173 - $contribution['original_gross'] = $post_data['mc_gross'];
174 - $contribution['fee'] = $post_data['mc_fee'],
175 - $contribution['gross'] = $post_data['mc_gross'],
176 - $contribution['net'] = $contribution['gross'] - $contribution['fee'];
177 - $contribution['date'] = $timestamp;
178 -
179 - //print_r the contribution?
180 -
181 - return $contribution;
182 - }
183 -
184 - /**
185 - * Connect to a URL, send optional post variables, return data
186 - *
187 - * Yoinked from _fundcore_paypal_download in fundcore/gateways/fundcore_paypal.module Drupal module
188 - * @param $url String of the URL to connect to
189 - * @param $vars Array of POST variables
190 - * @return String containing the output returned from Server
191 - */
192 - protected function curl_download( $url, $vars = NULL ) {
193 - $ch = curl_init();
194 - curl_setopt($ch, CURLOPT_URL, $url);
195 - curl_setopt($ch, CURLOPT_HEADER, 0);
196 - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
197 - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
198 -
199 - if ($vars !== NULL) {
200 - curl_setopt($ch, CURLOPT_POST, 1);
201 - curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);
202 - }
203 - $data = curl_exec($ch);
204 - if (!$data) {
205 - $data = curl_error($ch);
206 - }
207 - curl_close($ch);
208 - return $data;
209 - }
210 -
211 - /**
212 - * Establishes a connection to the stomp listener
213 - *
214 - * Stomp listner URI set in config options (via command line or localSettings.php).
215 - * If a connection cannot be established, will exit with non-0 status.
216 - */
217 - protected function set_stomp_connection() {
218 - require_once( $this->stomp_path );
219 - //attempt to connect, otherwise throw exception and exit
220 - $this->out( "Attempting to connect to Stomp listener: {$this->activemq_stomp_uri}", LOG_LEVEL_DEBUG );
221 - try {
222 - //establish stomp connection
223 - $this->stomp = new Stomp( $this->activemq_stomp_uri );
224 - $this->stomp->connect();
225 - $this->out( "Successfully connected to Stomp listener", LOG_LEVEL_DEBUG );
226 - } catch (Stomp_Exception $e) {
227 - $this->out( "Stomp connection failed: " . $e->getMessage() );
228 - exit(1);
229 - }
230 - }
231 -
232 - /**
233 - * Send a message to the queue
234 - *
235 - * @param $destination string of the destination path for where to send a message
236 - * @param $message string the (formatted) message to send to the queue
237 - * @param $options array of additional Stomp options
238 - * @return bool result from send, FALSE on failure
239 - */
240 - protected function queue_message( $destination, $message, $options = array( 'persistent' => TRUE )) {
241 - $this->out( "Attempting to queue message...", LOG_LEVEL_DEBUG );
242 - $sent = $this->stomp->send( $destination, $message, $options );
243 - $this->out( "Result of queuing message: $sent", LOG_LEVEL_DEBUG );
244 - return $sent;
245 - }
246 -
247 -
248 -
249 - /**
250 - * Formats text for output.
251 - *
252 - * @param $msg String a message to output.
253 - * @param $level the Level at which the message should be output.
254 - */
255 - protected function out( $msg, $level=LOG_LEVEL_INFO ) {
256 - if ( $this->log_level >= $level ) echo date( 'c' ) . ": " . $msg . "\n";
257 - }
258 -
259 - public function __destruct() {
260 - $this->out( "Exiting gracefully." );
261 - }
262 -}
Index: trunk/extensions/DonationInterface/paypal_gateway/IPN/PaypalIPNListener.php
@@ -0,0 +1,380 @@
 2+<?php
 3+/**
 4+ * PayPal IPN listener and handler. Also pushes messages into the ActiveMQ queueing system.
 5+ *
 6+ * This is currently designed to act as a mechanism for pushing transactions received from PayPal's
 7+ * IPN system into a 'pending' queue from ActiveMQ. Once a transaction is verified, it is removed
 8+ * from the pending queue and pushed into a 'verified' queue. If it is not verified, a copy is left
 9+ * in the pending queue. This particular logic takes place in execute().
 10+ *
 11+ * Generally speaking, this should likely be abstracted to allow for more flexible use cases, as what
 12+ * is outlined above is pretty specific, but most of the other methods should allow for some flexibility -
 13+ * particularly if you were to subclass this.
 14+ *
 15+ * Also, this is close to being useable with other queueing systems that can talk Stomp. However, this
 16+ * currently employs some things that are likely unique to ActiveMQ, namely setting some custom header
 17+ * information for items going into a pending queue and then using ActiveMQ 'selectors' to pull out
 18+ * a specific message.
 19+ *
 20+ * Does not actually require Mediawiki to run, can be run as stand alone or can be integrated
 21+ * with a Mediawiki extension. See StandaloneListener.php.example for a guide on how to do this.
 22+ *
 23+ * Configurable variables:
 24+ * log_level => $int // 0, 1, or 2 (see constant definitions below for options)
 25+ * stomp_path => path to Stomp.php
 26+ * pending_queue => the queue to push pending items to
 27+ * verified_queue => the queue to push verfiied items to
 28+ * activemq_stomp_uri => the URI for the activemq broker
 29+ * contrib_db_host => the hostname where the contribution_tracking table lives
 30+ * contrib_db_username => the username for the db where contribution_tracking lives
 31+ * contrib_db_password => the pw for accessing the db where contribution_tracking lives
 32+ * conrtib_db_name => the db name where contribution_tracking lives
 33+ *
 34+ * Note that the contrib_db* variables are likely of no use to you, unless you're using CiviCRM with Drupal and
 35+ * are using a special contribution tracking module... So if you're not doing that, you can likely
 36+ * leave those out of your config.
 37+ *
 38+ * PayPal IPN docs: https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_admin_IPNIntro
 39+ *
 40+ * @author Arthur Richards <arichards@wikimedia.org>
 41+ * @TODO:
 42+ * add output for DB connection/query
 43+ * abstract out the contribution_tracking stuff so this is more flexible?
 44+ */
 45+
 46+/** Set available log levels **/
 47+DEFINE( 'LOG_LEVEL_QUIET', 0 ); // output nothing
 48+DEFINE( 'LOG_LEVEL_INFO', 1 ); // output minimal info
 49+DEFINE( 'LOG_LEVEL_DEBUG', 2 ); // output lots of info
 50+
 51+class PaypalIPNProcessor {
 52+
 53+ // set the apropriate logging level
 54+ $log_level = LOG_LEVEL_INFO;
 55+
 56+ // path to Stomp
 57+ $stomp_path = "../../activemq_stomp/Stomp.php";
 58+
 59+ // path to pending queue
 60+ $pending_queue = '/queue/pending_paypal';
 61+
 62+ // path to the verified queue
 63+ $verified_queue = '/queue/donations';
 64+
 65+ // URI to activeMQ
 66+ $activemq_stomp_uri = 'tcp://localhost:61613';
 67+
 68+ /**
 69+ * Class constructor, sets configurable parameters
 70+ *
 71+ * @param $opts array of key, value pairs where the 'key' is the parameter name and the
 72+ * value is the value you wish to set
 73+ */
 74+ function __construct( $opts = array() ) {
 75+ // set the log level
 76+ if ( array_key_exists( 'log_level', $opts )) {
 77+ $this->log_level = $opts[ 'log_level' ];
 78+ unset( $opts[ 'log_level'] );
 79+ }
 80+
 81+ $this->out( "Loading Paypal IPN processor" );
 82+
 83+ // set parameters
 84+ foreach ( $opts as $key => $value ) {
 85+ $this->{$key} = $value;
 86+
 87+ // star out passwords in the log!!!!
 88+ if ( $key == 'contrib_db_password' ) $value = '******';
 89+
 90+ $this->out( "Setting parameter $key as $value.", LOG_LEVEL_DEBUG );
 91+ }
 92+
 93+ //prepare our stomp connection
 94+ $this->set_stomp_connection();
 95+ }
 96+
 97+ /**
 98+ * Execute IPN procesing.
 99+ *
 100+ * Take the data sent from a PayPal IPN request, verify it against the IPN, then push the
 101+ * transaction to the queue. Before verifying the transaction against the IPN, this will
 102+ * place the transaction originally received in the pending queue. If the transaction is
 103+ * verified, it will be removed from the pending queue and placed in an accepted queue. If
 104+ * it is not verified, it will be left in the pending queue for dealing with in some other
 105+ * fashion.
 106+ *
 107+ * @param $data Array containing the message received from the IPN, likely the contents of
 108+ * $_POST
 109+ */
 110+ function execute( $data ) {
 111+
 112+ //make sure we're actually getting something posted to the page.
 113+ if ( empty( $data )) {
 114+ $this->out( "Received an empty object, nothing to verify." );
 115+ return;
 116+ }
 117+
 118+ // connect to stomp
 119+ $this->set_stomp_connection();
 120+
 121+ //push message to pending queue
 122+ $contribution = $this->ipn_parse( $data );
 123+
 124+ // generate a unique id for the message 2 ensure we're manipulating the correct message later on
 125+ $tx_id = time() . '_' . mt_rand(); //should be sufficiently unique...
 126+ $headers = array( 'persistent' => TRUE, 'JMSCorrelationID' => $tx_id );
 127+ $this->out( "Setting JMSCorrelationID: $tx_id", LOG_LEVEL_DEBUG );
 128+
 129+ // do the queueing - perhaps move out the tracking checking to its own func?
 130+ if ( !$this->queue_message( $this->pending_queue, json_encode( $contribution ), $headers )) {
 131+ $this->out( "There was a problem queueing the message to the queue: " . $this->pending_queue );
 132+ $this->out( "Message: " . print_r( $contribution, TRUE ), LOG_LEVEL_DEBUG );
 133+ }
 134+
 135+
 136+ //verify the message with PayPal
 137+ if ( !$this->ipn_verify( $data )) {
 138+ $this->out( "Message did not pass PayPal verification." );
 139+ $this->out( "\$_POST contents: " . print_r( $data, TRUE ), LOG_LEVEL_DEBUG );
 140+ return;
 141+ }
 142+
 143+ // pull the message off of the pending queue using a 'selector' to make sure we're getting the right msg
 144+ $properties['selector'] = "JMSCorrelationID = '$tx_id'";
 145+ $this->out( "Attempting to pull mssage from pending queue with JMSCorrelationID = $tx_id", LOG_LEVEL_DEBUG );
 146+ $msg = $this->fetch_message( $this->pending_queue, $properties );
 147+ if ( $msg ) {
 148+ $this->out( "Pulled message from pending queue: " . print_r( json_decode( $msg ), TRUE ), LOG_LEVEL_DEBUG);
 149+ } else {
 150+ $this->out( "FAILED retrieving message from pending queue.", LOG_LEVEL_DEBUG );
 151+ return;
 152+ }
 153+
 154+ // push to verified queue
 155+ if ( !$this->queue_message( $this->verified_queue, $msg->body )) {
 156+ $this->out( "There was a problem queueing the message to the quque: " . $this->verified_queue );
 157+ $this->out( "Message: " . print_r( $contribution, TRUE ), LOG_LEVEL_DEBUG );
 158+ return;
 159+ }
 160+
 161+ // remove from pending
 162+ $this->out( "Attempting to remove message from pending.", LOG_LEVEL_DEBUG );
 163+ if ( !$this->stomp->ack( $msg )) {
 164+ $this->out( "There was a problem remoivng the verified message from the pending queue: " . print_r( json_decode( $msg, TRUE )));
 165+ }
 166+ }
 167+
 168+ /**
 169+ * Verify IPN's message validitiy
 170+ *
 171+ * Yoinked from fundcore_paypal_verify() in fundcore/gateways/fundcore_paypal.module Drupal module
 172+ * @param $post_data array of post data - the message received from PayPal
 173+ * @return bool
 174+ */
 175+ public function ipn_verify( $post_data ) {
 176+ if ( $post_data[ 'payment_status' ] != 'Completed' ) {
 177+ // order not completed
 178+ $this->out( "Message not marked as complete." );
 179+ return FALSE;
 180+ }
 181+
 182+ if ( $post_data[ 'mc_gross' ] <= 0 ) {
 183+ $this->out( "Message has 0 or less in the mc_gross field." );
 184+ return FALSE;
 185+ }
 186+
 187+ // url to respond to paypal with verification response
 188+ $postback_url = 'https://www.paypal.com/cgi-bin/webscr'; // should this be configurable?
 189+ if (isset($post_data['test_ipn'])) {
 190+ $postback_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
 191+ }
 192+
 193+ // respond with exact same data/structure + cmd=_notify-validate
 194+ $attr = $post_data;
 195+ $attr['cmd'] = '_notify-validate';
 196+
 197+ // send the message back to PayPal for verification
 198+ $status = $this->curl_download( $postback_url, $attr );
 199+ if ($status != 'VERIFIED') {
 200+ $this->out( "The message could not be verified." );
 201+ $this->out( "Returned with status: $status", LOG_LEVEL_DEBUG );
 202+ return FALSE;
 203+ }
 204+
 205+ return TRUE;
 206+ }
 207+
 208+ /**
 209+ * Parse the PayPal message/post data into the format we need for ActiveMQ
 210+ *
 211+ * @param $post_data array containing the $_POST data from PayPal
 212+ * @return array containing the parsed/formatted message for stuffing into ActiveMQ
 213+ */
 214+ public function ipn_parse( $post_data ) {
 215+ $this->out( "Attempting to parse: " . print_r( $post_data, TRUE ), LOG_LEVEL_DEBUG );
 216+ $contribution = array();
 217+
 218+ $timestamp = strtotime($post_data['payment_date']);
 219+
 220+ // Detect if we're using the new-style (likely unique to Wikimedia) - this should be handled elsewhere
 221+ if (is_numeric($post_data['option_selection1'])) {
 222+ // get the database connection to the tracking table
 223+ $this->contribution_tracking_connection();
 224+ $tracking_data = $this->get_tracking_data( $post_data['option_selection1'] );
 225+ if ( !$tracking_data ) { //we have a problem!
 226+ $this->out( "There is no contribution ID associated with this transaction." );
 227+ exit();
 228+ }
 229+ $contribution['contribution_tracking_id'] = $post_data['option_selection1'];
 230+ $contribution['optout'] = $tracking_data['optout'];
 231+ $contribution['anonymous'] = $tracking_data['anonymous'];
 232+ $contribution['comment'] = $tracking_data['note'];
 233+ } else {
 234+ $split = explode(';', $post_data['option_selection1']);
 235+ $contribution['anonymous'] = ($split[0] != 'public' && $split[0] != 'Mention my name');
 236+ $contribution['comment'] = $post_data['option_selection2'];
 237+ }
 238+
 239+ $contribution['email'] = $post_data['payer_email'];
 240+ $contribution['first_name'] = $post_data['first_name'];
 241+ $contribution['last_name'] = $post_data['last_name'];
 242+
 243+ $split = split("\n", str_replace("\r", '', $post_data['address_street']));
 244+
 245+ $contribution['street_address'] = $split[0];
 246+ $contribution['supplemental_address_1'] = $split[1];
 247+ $contribution['city'] = $post_data['address_city'];
 248+ $contribution['original_currency'] = $post_data['mc_currency'];
 249+ $contribution['original_gross'] = $post_data['mc_gross'];
 250+ $contribution['fee'] = $post_data['mc_fee'];
 251+ $contribution['gross'] = $post_data['mc_gross'];
 252+ $contribution['net'] = $contribution['gross'] - $contribution['fee'];
 253+ $contribution['date'] = $timestamp;
 254+
 255+ return $contribution;
 256+ }
 257+
 258+ /**
 259+ * Connect to a URL, send optional post variables, return data
 260+ *
 261+ * Yoinked from _fundcore_paypal_download in fundcore/gateways/fundcore_paypal.module Drupal module
 262+ * @param $url String of the URL to connect to
 263+ * @param $vars Array of POST variables
 264+ * @return String containing the output returned from Server
 265+ */
 266+ public function curl_download( $url, $vars = NULL ) {
 267+ $ch = curl_init();
 268+ curl_setopt($ch, CURLOPT_URL, $url);
 269+ curl_setopt($ch, CURLOPT_HEADER, 0);
 270+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
 271+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 272+
 273+ if ($vars !== NULL) {
 274+ curl_setopt($ch, CURLOPT_POST, 1);
 275+ curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);
 276+ }
 277+ $data = curl_exec($ch);
 278+ if (!$data) {
 279+ $data = curl_error($ch);
 280+ $this->out( "Curl error: " . $data );
 281+ }
 282+ curl_close($ch);
 283+ return $data;
 284+ }
 285+
 286+ /**
 287+ * Establishes a connection to the stomp listener
 288+ *
 289+ * Stomp listner URI set in config options (via command line or localSettings.php).
 290+ * If a connection cannot be established, will exit with non-0 status.
 291+ */
 292+ protected function set_stomp_connection() {
 293+ require_once( $this->stomp_path );
 294+ //attempt to connect, otherwise throw exception and exit
 295+ $this->out( "Attempting to connect to Stomp listener: {$this->activemq_stomp_uri}", LOG_LEVEL_DEBUG );
 296+ try {
 297+ //establish stomp connection
 298+ $this->stomp = new Stomp( $this->activemq_stomp_uri );
 299+ $this->stomp->connect();
 300+ $this->out( "Successfully connected to Stomp listener", LOG_LEVEL_DEBUG );
 301+ } catch (Stomp_Exception $e) {
 302+ $this->out( "Stomp connection failed: " . $e->getMessage() );
 303+ exit(1);
 304+ }
 305+ }
 306+
 307+ /**
 308+ * Send a message to the queue
 309+ *
 310+ * @param $destination string of the destination path for where to send a message
 311+ * @param $message string the (formatted) message to send to the queue
 312+ * @param $options array of additional Stomp options
 313+ * @return bool result from send, FALSE on failure
 314+ */
 315+ public function queue_message( $destination, $message, $options = array( 'persistent' => TRUE )) {
 316+ $this->out( "Attempting to queue message to $destination", LOG_LEVEL_DEBUG );
 317+ $sent = $this->stomp->send( $destination, $message, $options );
 318+ $this->out( "Result of queuing message: $sent", LOG_LEVEL_DEBUG );
 319+ return $sent;
 320+ }
 321+
 322+ /**
 323+ * Fetch latest raw message from a queue
 324+ *
 325+ * @param $destination string of the destination path from where to fetch a message
 326+ * @return mixed raw message (Stomp_Frame object) from Stomp client or False if no msg present
 327+ */
 328+ public function fetch_message( $destination, $properties = NULL ) {
 329+ $this->out( "Attempting to connect to queue at: $destination", LOG_LEVEL_DEBUG );
 330+ if ( $properties ) $this->out( "With the following properties: " . print_r( $properties, TRUE ));
 331+ $this->stomp->subscribe( $destination, $properties );
 332+ $this->out( "Attempting to pull queued item", LOG_LEVEL_DEBUG );
 333+ $message = $this->stomp->readFrame();
 334+ return $message;
 335+ }
 336+
 337+ /**
 338+ * Establish a connection with the contribution database.
 339+ *
 340+ * The properties contrib_db_host, contrib_db_username, contrib_db_password and
 341+ * contrib_db_name should be set prior to the execution of this method.
 342+ */
 343+ protected function contribution_tracking_connection() {
 344+ $this->contrib_db = mysql_connect(
 345+ $this->contrib_db_host,
 346+ $this->contrib_db_username,
 347+ $this->contrib_db_password );
 348+ mysql_select_db( $this->contrib_db_name, $this->contrib_db );
 349+ }
 350+
 351+ /**
 352+ * Fetches tracking data we need to for this transaction from the contribution_tracking table
 353+ *
 354+ * @param int the ID of the transaction we care about
 355+ * @return array containing the key=>value pairs of data from the contribution_tracking table
 356+ */
 357+ protected function get_tracking_data( $id ) {
 358+ //sanitize the $id
 359+ $id = mysql_real_escape_string( $id );
 360+ $query = "SELECT * FROM contribution_tracking WHERE id=$id";
 361+ $this->out( "Preparing to run query on contribution_tracking: $query", LOG_LEVEL_DEBUG );
 362+ $result = mysql_query( $query );
 363+ $row = mysql_fetch_assoc( $result );
 364+ $this->out( "Query result: " . print_r( $row, TRUE ), LOG_LEVEL_DEBUG );
 365+ return $row;
 366+ }
 367+
 368+ /**
 369+ * Formats text for output.
 370+ *
 371+ * @param $msg String a message to output.
 372+ * @param $level the Level at which the message should be output.
 373+ */
 374+ protected function out( $msg, $level=LOG_LEVEL_INFO ) {
 375+ if ( $this->log_level >= $level ) echo date( 'c' ) . ": " . $msg . "\n";
 376+ }
 377+
 378+ public function __destruct() {
 379+ $this->out( "Exiting gracefully." );
 380+ }
 381+}
Index: trunk/extensions/DonationInterface/paypal_gateway/IPN/StandaloneListener.php.example
@@ -0,0 +1,54 @@
 2+<?php
 3+/**
 4+ * This is an example of how you might run the PaypalIPNListener in stand alone mode
 5+ *
 6+ * It's just an example, so do what you want. Note that the contrib_db* config settings
 7+ * are likely only important to you if you are using CiviCRM with Drupal and taking
 8+ * donations via Mediawiki with the ContributionTracking extension installed. Point being,
 9+ * it probably doesn't apply to you. Take a look in PaypalIPNListener.php to see more about
 10+ * how that stuff might get used.
 11+ *
 12+ * @author Arthur Richards <arichards@wikimedia.org>
 13+ */
 14+
 15+// turn on output buffering so we can capture the output
 16+ob_start();
 17+
 18+// require the actual listener
 19+require_once( '/path/to/PaypalIPNListener.php' );
 20+
 21+// set some configuration variables (for more info, check in PaypalIPNListener.php
 22+$config = array(
 23+ 'log_level' => 2, //debug
 24+ 'stomp_path' => '/path/to/Stomp.php',
 25+ 'pending_queue' => '/queue/pending_paypal',
 26+ 'verified_queue' => '/queue/verified_paypal',
 27+ 'activemq_stomp_uri' => 'tcp://localhost:61613/',
 28+ 'contrib_db_host' => 'localhost',
 29+ 'contrib_db_username' => 'user',
 30+ 'contrib_db_password' => 'password',
 31+ 'contrib_db_name' => 'drupal',
 32+);
 33+
 34+// define a file where you can log the output from the listener
 35+$log_file = "out_" . date( 'Ymd' ) . '.log'; //for logging the output
 36+
 37+// instantaite the listener with our config options
 38+$listener = new PaypalIPNProcessor( $config );
 39+
 40+// pass some data to the listner, usually this will be posted from PayPal's IPN
 41+$listener->execute( $_POST );
 42+
 43+// shutdown the listener
 44+unset( $listener );
 45+
 46+// get the output from the buffer
 47+$output = ob_get_clean();
 48+ob_flush();
 49+
 50+// open our log file
 51+$handle = fopen( $log_file, 'a' );
 52+
 53+// write the output to the log file
 54+fwrite( $handle, $output );
 55+?>

Comments

#Comment by Platonides (talk | contribs)   22:15, 24 July 2010

PaypalIPNListener.php has syntax errors. The class variables should be preceded by var or public/protected/private

Instead of using ob_start(); / ob_get_clean(); you should probably use something like a $listener->setOutput( $handle ); so that PaypalIPNListener::out() writes directly into the file


#Comment by Awjrichards (talk | contribs)   23:40, 5 January 2011

All files referenced here deleted as of r71652

#Comment by Catrope (talk | contribs)   12:38, 6 January 2011

That revision is in a totally different extension.

#Comment by Awjrichards (talk | contribs)   18:07, 6 January 2011

Woops - r71653

Status & tagging log