/[drupal]/contributions/modules/paypal_framework/paypal_framework.module
ViewVC logotype

Contents of /contributions/modules/paypal_framework/paypal_framework.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.26 - (show annotations) (download) (as text)
Tue May 9 21:25:34 2006 UTC (3 years, 6 months ago) by eaton
Branch: MAIN
CVS Tags: DRUPAL-4-7--1-0, DRUPAL-5--1-0, HEAD
Branch point for: DRUPAL-5, DRUPAL-4-7
Changes since 1.25: +84 -22 lines
File MIME type: text/x-php
A quick stab at a 4.7 conversion for the paypal framework. Committed with the OK of nji@njivy.org, the previous committer. Further testing needed.
1 <?php
2 // $Id
3
4 /* PayPal Module
5 *
6 * By Kevin Landreth (CrackerJackMack)
7 *
8 * This modules purpose isn't to replace the Ecommerce module
9 * but to offer an extention to Drupal.
10 *
11 * I would like to thank the authors of the PHP Toolkit 0.50
12 * you can visit their project here at http://paypal.sourceforge.net/
13 *
14 * Some of the code contained in the module is from this toolkit.
15 * I tried my best to comment the code taken from the toolkit
16 * in my code, and I am sorry if I missed any sections.
17 */
18
19 /*
20 * I tried to clean up the code syntax, clarify some help text, and
21 * fortify the SQL statements. I also rearranged the menu hook a bit.
22 * Beyond that, I introduced the 'paypal_transaction' hook so modules
23 * may act immediately upon transaction activity.
24 *
25 * While writing paypal_tipjar.module and paypal_subscription.module,
26 * paypal_framework.module has been very helpful. Thanks, Kevin!
27 *
28 * ~ Nic Ivy (njivy)
29 */
30
31
32 function paypal_framework_help($section) {
33
34 switch ($section) {
35 case 'admin/modules#description':
36 $output = t('Allow use of the PayPal Developer Network tools such as IPN');
37 break;
38 case 'admin/settings/paypal_framework#title':
39 case 'admin/help#paypal_framework':
40 $output = t('PayPal framework');
41 break;
42 case 'admin/system/modules/paypal_framework':
43 case 'admin/settings/paypal_framework':
44 $output = t('Giving credit where credit is due is very important. The developers of this module would like to thank the PayPal Developer Network for providing useful PHP code that made this module possible. %pdn Please view the changelog AND todo files to see if the paypal method you are looking for is supported', array('%pdn' => l(t('PHP project for PDN.'), 'http://paypal.sourceforge.net/')) );
45 break;
46 }
47
48 return $output;
49
50 }
51
52 function paypal_framework_perm() {
53
54 return array('administer paypal framework');
55
56 }
57
58 function paypal_framework_menu($may_cache) {
59 $menu = array();
60
61 if ($may_cache) {
62 $menu[] = array('path' => 'admin/paypal',
63 'title' => t('PayPal'),
64 'access' => user_access('administer paypal framework'),
65 'callback' => '_paypal_page_stats');
66 $menu[] = array('path' => 'paypal/ipn',
67 'title' => t('IPN post'),
68 'type' => MENU_CALLBACK,
69 'access' => true,
70 'callback' => '_paypal_ipn_receive');
71 }
72 else {
73 // These paths depend on the URL, so we cannot cache them.
74 if (arg(0) == 'admin' && arg(1) == 'paypal') {
75 if (arg(2) == 'payer' && arg(3)) {
76 $menu[] = array('path' => 'admin/paypal/payer/'.arg(3),
77 'title' => t('Payer stats'),
78 'access' => user_access('administer paypal framework'),
79 'callback' => '_paypal_payer_stats',
80 'type' => MENU_CALLBACK);
81 }
82 elseif (arg(2) == 'item' && arg(3)) {
83 $menu[] = array('path' => 'admin/paypal/item/'.arg(3),
84 'title' => t('Item stats'),
85 'access' => user_access('administer paypal framework'),
86 'callback' => '_paypal_item_stats',
87 'type' => MENU_CALLBACK);
88 }
89 elseif (arg(2) == 'transaction' && arg(3)) {
90 $menu[] = array('path' => 'admin/paypal/transaction/'.arg(3),
91 'title' => t('Transaction information'),
92 'access' => user_access('administer paypal framework'),
93 'callback' => '_paypal_txn_stats',
94 'type' => MENU_CALLBACK);
95 }
96 }
97 }
98
99 return $menu;
100
101 }
102
103 function paypal_framework_cron() {
104
105 /*
106 * Have our own function for it, gives us room to
107 * expand if we scale to more queues and whatnot.
108 */
109 _paypal_process_queue();
110
111 }
112
113 function paypal_framework_settings() {
114 global $db_type;
115
116 $form = array();
117
118 /* START Basic Paypal Settings */
119 $form['paypal_emails'] = array(
120 '#type' => 'textarea',
121 '#title' => t('PayPal email'),
122 '#default_value' => variable_get('paypal_emails', ''),
123 '#cols' => 32,
124 '#rows' => 4,
125 '#description' => t('Please enter on separate lines the PayPal email addresses whose transaction activities this module will record.'),
126 );
127
128 // I removed the paypal_maxentries form element because nothing used
129 // that variable. As a bonus, we have less UI clutter.
130 /* END Basic Paypal Settings */
131
132
133 /* START IPN Verification Form */
134 $form['ipn'] = array(
135 '#type' => 'fieldset',
136 '#title' => t('PayPal instant payment notification (IPN) settings'),
137 );
138
139 $form['ipn']['paypal_postmethod'] = array(
140 '#type' => 'select',
141 '#title' => t('IPN reply method'),
142 '#default_value' => variable_get('paypal_postmethod', 'fsock'),
143 '#options' => array('fsock' => 'PHP fsock', 'libCurl' => 'PHP LibCurl', 'curl' => 'Curl Binary'),
144 '#description' => t('This is the method with which Drupal will attempt to reply back to the PayPal server, verifying that the transaction data is valid. Please note that <em>libCurl</em> must be compiled into PHP but <em>Curl binary</em> implies access to the operating system\'s curl binary, usually in /usr/bin/ on unix-like systems. If your server was compiled with --enable-sockets then you can probably use <em>fsockopen</em> properly. If in doubt, please contact your system administrator.'),
145 );
146
147 $form['ipn']['paypal_curl'] = array(
148 '#type' => 'textfield',
149 '#title' => t('Curl binary'),
150 '#default_value' => variable_get('paypal_curl', ''),
151 '#size' => 10,
152 '#maxlength' => 100,
153 '#description' => t("If you are using the <em>Curl binary</em>, enter the path to curl. E.g. /usr/bin/curl."),
154 );
155
156 $form['ipn']['paypal_post'] = array(
157 '#type' => 'textfield',
158 '#title' => t('Reply POST address'),
159 '#default_value' => variable_get('paypal_post', 'https://www.paypal.com/cgi-bin/webscr'),
160 '#size' => 45,
161 '#maxlength' => 100,
162 '#description' => t('You can get this address from the <em>technical overview</em> section for the <em>instant payment notification</em> system on %site', array('%site' => l('paypal.com', 'http://www.paypal.com/')) ),
163 );
164
165 /* END IPN Verification Form */
166
167
168 /* START Tweaks Configuration */
169
170 $form['tweaks'] = array(
171 '#type' => 'fieldset',
172 '#title' => t('Performance tweaks'),
173 );
174
175 $form['tweaks']['paypal_ver_queue'] = array(
176 '#type' => 'checkbox',
177 '#title' => t('Enable verification queueing'),
178 '#return_value' => 1,
179 '#default_value' => variable_get('paypal_ver_queue', 0),
180 '#description' => t('Instead of verifying transactions right away, this method will save everything to the database and then verify the transactions when cron runs.'),
181 );
182
183 $form['tweaks']['paypal_ver_limit'] = array(
184 '#type' => 'textfield',
185 '#title' => t('Verifications per hour'),
186 '#default_value' => variable_get('paypal_ver_limit', 5),
187 '#size' => 3,
188 '#maxlength' => 5,
189 '#description' => t('If you enabled <em>verification queueing</em>, then specify the average number of transactions to verify per hour. For example, if you enter 3 and 6 hours elapse between cron updates, this module will verify <em>up to</em> 18 transactions when cron updates. Any remaining transactions will be kept in the queue until the next cron update.'),
190 );
191
192 $form['tweaks']['paypal_ver_max'] = array(
193 '#type' => 'textfield',
194 '#title' => t('Verifications limit'),
195 '#default_value' => variable_get('paypal_ver_max', 20),
196 '#size' => 3,
197 '#maxlength' => 5,
198 '#description' => t("If you enabled <em>verification queueing</em>, then specify the maximum number of transactions to verify in any given hour. The smaller of this value and <em>verifications per hour</em> x <em>hours between cron updates</em> will determine how many transactions are processed at once. This value is useful when cron has not been run in a long time. Set to 0 to disable."),
199 );
200
201 if ($db_type == 'mysql') {
202 $form['tweaks']['paypal_mysql_delayed'] = array(
203 '#type' => 'checkbox',
204 '#title' => t('Delayed commits'),
205 '#return_value' => 1,
206 '#default_value' => variable_get('paypal_mysql_delayed', 0),
207 '#description' => t('If you receive a fairly high volume of payments or have a slow database server, then it is recommended that you check this box. This option will use delayed database inserts to reduce the instantaneous load on your server. Since the database table for logging PayPal transactions can be large, checking this box is a good idea for a slower database servers or sites with a large volume of payments. %ref', array('%ref'=>l(t('Click here for more information about delayed inserts.'), 'http://dev.mysql.com/doc/mysql/en/INSERT_DELAYED.html', array('target' => '_new')))),
208 );
209 }
210
211 /* END Tweaks Configuration */
212
213 return $form;
214 }
215
216
217
218
219
220 /**********************************************************************************/
221
222 function _paypal_txn_stats($txn_id = NULL) {
223
224 if (!$txn_id) {
225 $txn_id = arg(3);
226 }
227
228 $txn = db_fetch_array(db_query('SELECT ppl.*, UNIX_TIMESTAMP(ppl.transaction_date) AS txn_date, ppp.* FROM {paypal_log} ppl INNER JOIN {paypal_payer} ppp ON ppl.payer_id=ppp.payer_id WHERE txn_id="%s"', $txn_id));
229
230 if (!$txn['txn_id']) {
231 // We have no data.
232 drupal_set_message('Invalid transaction ID: '.$txn_id, 'error');
233 drupal_goto('admin/paypal');
234 return;
235 }
236
237
238 if ($txn['parent_txn_id']) {
239 // Prepare to show a list of related transactions.
240 $result = db_query('SELECT ppl.txn_id, UNIX_TIMESTAMP(ppl.transaction_date) AS transaction_date, ppl.payment_status FROM {paypal_log} ppl WHERE (ppl.txn_id="%s" OR ppl.parent_txn_id="%s") AND ppl.txn_id!="%s" ORDER BY transaction_date', $txn['parent_txn_id'], $txn['parent_txn_id'], $txn['txn_id']);
241
242 $related_headers = array(t('Transaction ID'), t('Status'), t('Transaction date'));
243 while ($related_txn = db_fetch_object($result)) {
244 $related_rows[] = array(l($related_txn->txn_id, 'admin/paypal/transaction/'.$related_txn->txn_id),
245 $related_txn->payment_status,
246 format_date($related_txn->transaction_date));
247 }
248 }
249 else {
250 // Look for child transactions.
251 $result = db_query('SELECT ppl.txn_id, UNIX_TIMESTAMP(ppl.transaction_date) AS transaction_date, ppl.payment_status FROM {paypal_log} ppl WHERE ppl.parent_txn_id="%s" ORDER BY transaction_date', $txn['txn_id']);
252
253 $related_headers = array(t('Transaction ID'), t('Status'), t('Transaction date'));
254 while ($sub_transaction = db_fetch_object($result)) {
255 $related_rows[] = array(l($sub_transaction->txn_id, 'admin/paypal/transaction/'.$sub_transaction->txn_id),
256 $sub_transaction->payment_status,
257 format_date($sub_transaction->transaction_date));
258 }
259 }
260
261
262 // Prepare certain fields
263 $txn['transaction_date'] = format_date($txn['txn_date'], 'large');
264 unset($txn['txn_date']);
265 $txn['payer_id'] = l($txn['payer_id'], 'admin/paypal/payer/'.$txn['payer_id']);
266 $txn['item_name'] = l($txn['item_name'], 'admin/paypal/item/'.$txn['item_number']);
267 $txn['item_number'] = l($txn['item_number'], 'admin/paypal/item/'.$txn['item_number']);
268 $txn['parent_txn_id'] = l($txn['parent_txn_id'], 'admin/paypal/transaction/'.$txn['parent_txn_id']);
269
270 ksort($txn);
271
272 $headers = array(t('Attribute'), t('Value'));
273 foreach ($txn AS $key => $value) {
274 $rows[] = array($key, $value);
275 }
276
277 $output .= theme('table', $headers, $rows);
278
279 if ($related_rows) {
280 $output .= '<br />';
281 $output .= theme('box', t('Related transactions'), theme('table', $related_headers, $related_rows));
282 }
283
284 drupal_set_title($title);
285 print theme('page', $output);
286 }
287
288
289 function _paypal_item_stats($item_id = NULL) {
290
291 if (!$item_id) {
292 $item_id = arg(3);
293 }
294
295 if (db_num_rows(db_query("SELECT payer_id FROM {paypal_log} WHERE item_number='%s'", $item_id)) < 1) {
296 drupal_set_message(t('Invalid item ID: '.$item_id), 'error');
297 drupal_goto('admin/paypal');
298 return;
299 }
300
301 // These are the only currencies that PayPal supports.
302 $currencies = array('USD' => 'US$', 'CAD' => 'CA$', 'GBP' => '&pound;', 'EUR' => '&euro;', 'YEN' => '&yen;');
303
304 $result = db_query('SELECT SUM(mc_gross) AS gross, SUM(mc_fee) AS fee, mc_currency AS currency FROM {paypal_log} WHERE item_number="%s" GROUP BY mc_currency', $item_id);
305
306 while ($totals = db_fetch_object($result)) {
307 $output['gross'][] = round($totals->gross, 2).' '.$currencies[$totals->currency];
308 $output['fees'][] = round($totals->fee, 2).' '.$currencies[$totals->currency];
309 $output['net'][] = round(($totals->gross - $totals->fee), 2).' '.$currencies[$totals->currency];
310 }
311 $output['gross'] = t('Gross received: %gross', array('%gross' => implode(', ', $output['gross'])));
312 $output['fees'] = t('PayPal fees: %fees', array('%fees' => implode(', ', $output['fees'])));
313 $output['net'] = t('Net received: %net', array('%net' => implode(', ', $output['net'])));
314
315 $output = theme('box', t('Totals'), implode('<br />', $output));
316
317 $header = array(array('data' => t('Username'), 'field' => 'u.name'),
318 array('data' => t('Real name'), 'field' => 'last_name'),
319 array('data' => t('Email address'), 'field' => 'payer_email'),
320 array('data' => t('Gross'), 'field' => 'mc_gross'),
321 array('data' => t('Transaction date'), 'field' => 'transaction_date', 'sort' => 'desc'),
322 array('data' => t('Status'), 'field' => 'ppl.payment_status')
323 );
324
325 $result = pager_query('SELECT u.uid, u.name as username, ppl.payer_email AS email_address,
326 concat(ppp.first_name, " ", ppp.last_name) as realname, ppp.payer_id,
327 ppl.mc_gross AS gross, ppl.item_name, ppl.txn_id,
328 UNIX_TIMESTAMP(ppl.transaction_date) AS transaction_date, ppl.payment_status,
329 ppl.mc_currency AS currency
330 FROM {paypal_log} ppl
331 LEFT JOIN {users} u ON ppl.payer_email=u.mail
332 LEFT JOIN {paypal_payer} ppp ON ppl.payer_id=ppp.payer_id
333 WHERE item_number="%s"
334 %s',
335 50, 0, NULL, $item_id, tablesort_sql($header));
336
337 while ($txn = db_fetch_object($result)) {
338 $item_name = $txn->item_name;
339 $currency = $currencies[$txn->currency];
340 $view_stats = ($txn->payer_id ? l(t('view user stats'), 'admin/paypal/payer/'.$txn->payer_id) : '');
341
342 $rows[] = array(l($txn->username, 'user/'.$txn->uid),
343 l($txn->realname, 'admin/paypal/payer/'.$txn->payer_id),
344 $txn->email_address,
345 $txn->gross.' '.$currency,
346 l(format_date($txn->transaction_date), 'admin/paypal/transaction/'.$txn->txn_id),
347 $txn->payment_status);
348 }
349
350 if (!$rows) {
351 $rows[] = array(array('data' => t('No transactions were found.'), 'colspan' => 6));
352
353 }
354 if ($pager = theme('pager', NULL, 25, 0)) {
355 $rows[] = array(array('data' => $pager, 'colspan' => '6'));
356 }
357
358 $output .= '<br />';
359 $output .= theme('box', t('Transaction history'), theme('table', $header, $rows));
360
361 drupal_set_title(t('Item stats: %item', array('%item' => $item_name)));
362 print theme('page', $output);
363
364 }
365
366
367 function _paypal_payer_stats($payer_id = NULL) {
368
369 if (!$payer_id) {
370 $payer_id = arg(3);
371 }
372
373 if (db_num_rows(db_query("SELECT payer_id FROM {paypal_payer} WHERE payer_id='%s'", $payer_id)) < 1) {
374 drupal_set_message(t('Invalid user ID: '.$payer_id), 'error');
375 drupal_goto('admin/paypal');
376 return;
377 }
378
379 $e = db_query("SELECT payer_email from {paypal_email} WHERE payer_id='%s'", $payer_id);
380 while ($r = db_fetch_object($e)) {
381 $email .= l($r->payer_email, 'mailto:'.$r->payer_email).'<br/>';
382 }
383
384 $order = db_fetch_object(db_query("SELECT count(payer_id) as trans, sum(mc_gross) as gross, sum(mc_fee) as fee from {paypal_log} WHERE payer_id='%s'", $payer_id));
385
386 $order_sum = t('Gross received from payer: %payout <br/>PayPal fees: %fees <br/> Net received from payer: %net',
387 array('%payout' => number_format($order->gross, 2), '%fees' => number_format($order->fee, 2), '%net' => number_format($order->gross - $order->fee, 2))
388 );
389
390 $page = theme('box', t('Emails used'), $email);
391 $page .= '<br/>';
392 $page .= theme('box', t('Totals'), $order_sum);
393
394 $header = array(t('Username'),
395 array('data' => t('Email address'), 'field' => 'payer_email'),
396 array('data' => t('Gross'), 'field' => 'mc_gross'),
397 array('data' => t('Transaction date'), 'field' => 'transaction_date', 'sort' => 'desc'),
398 array('data' => t('Status'), 'field' => 'ppl.payment_status'),
399 array('data' => t('Item'), 'field' => 'ppl.item_name')
400 );
401
402 $result = pager_query('SELECT u.uid, u.name as username, ppl.payer_email AS email_address,
403 concat(ppp.first_name, " ", ppp.last_name) as realname,
404 ppl.mc_gross AS gross, ppl.item_name, ppl.item_number, ppl.txn_id,
405 UNIX_TIMESTAMP(ppl.transaction_date) AS transaction_date, ppl.payment_status,
406 ppl.mc_currency AS currency
407 FROM {paypal_log} ppl
408 LEFT JOIN {users} u ON ppl.payer_email=u.mail
409 LEFT JOIN {paypal_payer} ppp ON ppl.payer_id=ppp.payer_id
410 WHERE ppl.payer_id="%s"
411 %s',
412 50, 0, NULL, $payer_id, tablesort_sql($header));
413
414 $currencies = array('USD' => 'US$', 'CAD' => 'CA$', 'GBP' => '&pound;', 'EUR' => '&euro;', 'YEN' => '&yen;');
415
416 while ($txn = db_fetch_object($result)) {
417 // I'm saving these for use outside the while() loop.
418 // The last iteration of the loop leaves $txn empty.
419 $item_name = $txn->item_name;
420 $realname = $txn->realname;
421
422 $currency = $currencies[$txn->currency];
423
424 $rows[] = array(l($txn->username, 'user/'.$txn->uid),
425 $txn->email_address,
426 $txn->gross.' '.$currency,
427 l(format_date($txn->transaction_date), 'admin/paypal/transaction/'.$txn->txn_id),
428 $txn->payment_status,
429 l($txn->item_name, 'admin/paypal/item/'.$txn->item_number));
430 }
431
432 if (!$rows) {
433 $rows[] = array(array('data' => t('No transactions were found.'), 'colspan' => 7));
434
435 }
436 if ($pager = theme('pager', NULL, 50, 0)) {
437 $rows[] = array(array('data' => $pager, 'colspan' => '7'));
438 }
439
440 $page .= '<br />';
441 $page .= theme('box', t('Transaction history'), theme('table', $header, $rows));
442
443 drupal_set_title(t('Payer stats: %payer', array('%payer' => $realname)));
444 print theme('page', $page);
445
446 }
447
448
449 function _paypal_page_stats() {
450
451 $queue_size = 'SELECT count(log) as queue from {paypal_queue}';
452 $parent_transactions = 'SELECT count(txn_id) as txn, sum(mc_gross) as gross, sum(mc_fee) as fee from {paypal_log} as b WHERE b.parent_txn_id is NULL';
453 $sub_transactions = 'SELECT count(txn_id) as txn, sum(mc_gross) as gross, sum(mc_fee) as fee from {paypal_log} as b WHERE b.parent_txn_id is not NULL';
454 $top_payer = "SELECT sum(a.mc_gross) as gross, sum(a.mc_fee) as fee, count(distinct a.txn_id) as tran, b.payer_id, concat(b.first_name, ' ', b.last_name) as name from {paypal_log} as a LEFT JOIN {paypal_payer} as b ON a.payer_id = b.payer_id GROUP BY a.payer_id ORDER BY gross DESC, tran DESC LIMIT 5";
455
456 $t = db_fetch_object(db_query($queue_size));
457 $q_size = $t->queue;
458 unset($t);
459
460 $t = db_fetch_object(db_query($parent_transactions));
461 $p_trans = $t->txn;
462 $p_gross = $t->gross;
463 $p_fee = $t->fee;
464 unset($t);
465
466 $t = db_fetch_object(db_query($sub_transactions));
467 $s_trans = $t->txn;
468 $s_gross = $t->gross;
469 $s_fee = $t->fee;
470 unset($t);
471
472 $tran_stats = t('Items remaining in queue: %q_size <br/>Total unique transactions: %p_trans<br/>Total sub transactions: %s_trans<br/>Total Transactions: %total ', array('%total' => ($p_trans+$s_trans), '%q_size' => $q_size, '%p_size' => $p_size, '%s_trans' => $s_trans, '%p_trans' => $p_trans ));
473
474 $payer_row = array();
475 $t = db_query($top_payer);
476 $p = 0;
477 $payer_header = array(t('Payer'), t('Net total'), t('Transactions'));
478 while ($r = db_fetch_object($t)) {
479 $payer_row[$p] = array();
480 $payer_row[$p][] = l($r->name, 'admin/paypal/payer/'.$r->payer_id);
481 $net = floatval($r->gross - $r->fee);
482 $net_tra = ($p_gross-$p_fee)+($s_gross-$s_fee);
483 $net_per = ($net_tra == 0)?0:100 *$net/ $net_tra; // Check for div0
484 $payer_row[$p][] = '$'. number_format($net, 2) .' ('. round(abs($net_per), 2) .'%)';
485 $payer_row[$p][] = $r->tran .' ('. round((100*$r->tran/($s_trans + $p_trans)), 2).'%)';
486 $p++;
487 }
488 unset($t);
489
490
491 $txn_header = array(array('data' => t('Username'), 'field' => 'u.name'),
492 array('data' => t('Real name'), 'field' => 'last_name'),
493 array('data' => t('Email address'), 'field' => 'payer_email'),
494 array('data' => t('Gross'), 'field' => 'mc_gross'),
495 array('data' => t('Transaction date'), 'field' => 'transaction_date', 'sort' => 'desc'),
496 array('data' => t('Transaction ID'), 'field' => 'txn_id'),
497 array('data' => t('Item'), 'field' => 'item_name'),
498 array('data' => t('Status'), 'field' => 'ppl.payment_status')
499 );
500
501 $result = pager_query('SELECT u.uid, u.name as username, ppl.payer_email AS email_address,
502 concat(ppp.first_name, " ", ppp.last_name) as realname, ppp.payer_id,
503 ppl.mc_gross AS gross, ppl.item_name, ppl.item_number, ppl.txn_id,
504 UNIX_TIMESTAMP(ppl.transaction_date) AS transaction_date, ppl.payment_status,
505 ppl.mc_currency AS currency
506 FROM {paypal_log} ppl
507 LEFT JOIN {users} u ON ppl.payer_email=u.mail
508 LEFT JOIN {paypal_payer} ppp ON ppl.payer_id=ppp.payer_id
509 %s',
510 15, 0, NULL, tablesort_sql($txn_header));
511
512 while ($txn = db_fetch_object($result)) {
513 $item_name = $txn->item_name;
514 $currency = $currencies[$txn->currency];
515 $view_stats = ($txn->payer_id ? l(t('view user stats'), 'admin/paypal/payer/'.$txn->payer_id) : '');
516
517 $txn_rows[] = array(l($txn->username, 'user/'.$txn->uid),
518 l($txn->realname, 'admin/paypal/payer/'.$txn->payer_id),
519 $txn->email_address,
520 $txn->gross.' '.$currency,
521 format_date($txn->transaction_date),
522 l($txn->txn_id, 'admin/paypal/transaction/'.$txn->txn_id),
523 l($txn->item_name, 'admin/paypal/item/'.$txn->item_number),
524 $txn->payment_status);
525 }
526
527 if (!$txn_rows) {
528 $txn_rows[] = array(array('data' => t('No transactions were found.'), 'colspan' => 8));
529
530 }
531 if ($txn_pager = theme('pager', NULL, 15, 0)) {
532 $txn_rows[] = array(array('data' => $txn_pager, 'colspan' => '8'));
533 }
534
535
536 $page = theme('box', t('Transaction stats'), $tran_stats);
537 $page .= '<br/>';
538 $page .= theme('box', t('Top 5 payees'), theme('table', $payer_header, $payer_row));
539 $page .= '<br />';
540 $page .= theme('box', t('Transaction history'), theme('table', $txn_header, $txn_rows));
541
542 print theme('page', $page);
543
544 }
545
546
547 function _paypal_log($field = array()) {
548
549 /**
550 * Logging transaction information to the database.
551 * Queueing and rate limiting have already occurred.
552 */
553
554 /**
555 * Init table layouts
556 * to be used later for INSERTs
557 * and UPDATEs.
558 */
559
560 $table = array();
561 $table['payer'] = array('payer_id' => '',
562 'first_name' => '',
563 'last_name' => '',
564 'address_street' => '',
565 'address_city' => '',
566 'address_zip' => '',
567 'address_country' => '',
568 'address_status' => '',
569 'payer_status' => ''
570 );
571
572 $table['log'] = array('txn_id' => '',
573 'parent_txn_id' => '',
574 'payment_date' => '',
575 'txn_type' => '',
576 'business' => '',
577 'receiver_id' => '',
578 'receiver_email' => '',
579 'payer_id' => '',
580 'payer_email' => '',
581 'option_name1' => '',
582 'option_selection1' => '',
583 'option_name2' => '',
584 'option_selection2' => '',
585 'item_name' => '',
586 'mc_currency' => '',
587 'payment_type' => '',
588 'payment_gross' => '',
589 'payment_fee' => '',
590 'mc_gross' => '',
591 'mc_fee' => '',
592 'tax' => '',
593 'item_number' => '',
594 'quantity' => '',
595 'payment_status' => '',
596 'reason_code' => '',
597 'memo' => '',
598 'verify_sign' => '',
599 'notify_version' => ''
600 );
601
602 $table['email'] = array('payer_id' => '',
603 'payer_email' => '',
604 );
605
606 $insert = array('log' => '',
607 'email' => '',
608 'payer' => ''
609 );
610
611 $values = array('log' => '',
612 'email' => '',
613 'payer' => ''
614 );
615
616 if (variable_get('paypal_mysql_delayed', 0) )
617 $delayed = 'DELAYED';
618 else
619 $delayed = '';
620
621 /**
622 * The purpose of this is to insert into the tables JUST
623 * when there is data to insert. We don't want to insert ''
624 * because that is not NULL.
625 *
626 * $insert[$key] = '('.implode(',', array_keys($table[$key])).')'
627 * will not work.
628 *
629 * We also init the table values so we can do an easy insert.
630 *
631 */
632
633 foreach( array_keys($insert) as $key ) {
634 foreach( array_keys($table[$key]) as $k ) {
635 if ( $field[$k] != '' ) {
636 $insert[$key][] = $k;
637 $values[$key][] = $field[$k];
638 $table[$key][$k] = $field[$k]; // Just incase we end up doing UPDATE
639 }
640 }
641 }
642
643 /**
644 * We have a problem here because the %s and %d variables are automatically passed through check_query(),
645 * but the check is applied to the whole variable. We need to check_query() each value individually, and
646 * only once. This means we must pass each value to db_query() individually, and we should not do check_query()
647 * before then.
648 *
649 * I think we can pass $insert['log'] as a variable, which will be expanded in the arg list. But do we need
650 * more than one %s? If so, how to we make the right number of %s's?
651 *
652 * Or we could take our lives in our hands--and those of everyone who will use this after us--and perform
653 * security checks ourselves.
654 *
655 * Or we could be really sneaky and pull a 'hack'. :] We're going to use str_repeat() and the special
656 * nature of check_query() when arg1 is an array.
657 */
658
659 /**
660 * Construct the SQL string into which sprintf() will insert variables. There is no user-supplied text here.
661 * The next line creates a string with the right number of quoted %s's and then strips off the last 2 characters.
662 */
663 $value_string = substr(str_repeat('"%s", ', count($values['log'])), 0, -2);
664
665 /**
666 * check_query(), if it determines arg1 is an array, will behave more like vsprintf() than sprintf().
667 * I'd like to take this opportunity to thank the genius who wrote check_query() with this helpful exception!
668 */
669 $args = array_merge(array($delayed, implode(', ', $insert['log'])), $values['log']);
670
671 db_query("INSERT %s INTO {paypal_log} (%s) VALUES ($value_string)", $args);
672
673 $doupdate = db_fetch_object(db_query("SELECT count(*) as payer from {paypal_payer} where payer_id='%s'", $table['payer']['payer_id']));
674
675 if ( $doupdate->payer > 0 ) {
676 /*
677 * We already checked for empty strings when we created $insert.
678 * We'll use our new 'hack' as discussed above to supply a variable number of arguments to db_query().
679 *
680 * The real question is, "What happens when a number is passed to %s?" PHP should be able to handle it,
681 * but can the database? I think so. I haven't received any errors yet during my testing with MySQL.
682 */
683
684 $v = '';
685 /**
686 * There is no user-supplied text here, so we'll put this variable right into the SQL statement.
687 */
688 foreach($insert['payer'] as $key) {
689 $v .= $key.'="%s", ';
690 }
691 $v = substr($v, 0, -2);
692 $args = array_merge($values['payer'], array($table['payer']['payer_id']));
693
694 db_query("UPDATE {paypal_payer} SET $v WHERE payer_id='%s'", $args);
695 }
696 else {
697 $value_string = substr(str_repeat('"%s", ', count($values['payer'])), 0, -2);
698 $args = array_merge(array($delayed), array(implode(', ', $insert['payer'])), $values['payer']);
699
700 db_query("INSERT %s INTO {paypal_payer} (%s) VALUES ($value_string)", $args);
701 }
702
703 unset($doupdate );
704 unset($v);
705
706 $doupdate = db_fetch_object(db_query("SELECT count(*) as payer from {paypal_email} where payer_email='%s'", $table['email']['payer_email']));
707
708 if ( $doupdate->payer > 0 ) {
709 $v = '';
710 foreach($table['email'] as $key => $value) {
711 $v .= $key.'="%s", ';
712 }
713 $v = substr($v, 0, -2);
714 $args = array_merge($values['email'], array($table['email']['payer_email']));
715
716 db_query("UPDATE {paypal_email} set $v WHERE payer_email='%s'", $args);
717 }
718 else {
719 $value_string = substr(str_repeat('"%s", ', count($values['email'])), 0, -2);
720 $args = array_merge(array($delayed), array(implode(', ', $insert['email'])), $values['email']);
721
722 db_query("INSERT %s INTO {paypal_email} (%s) VALUES ($value_string)", $args);
723 }
724
725 /**
726 * Allow other paypal-related modules to act immediately upon this transaction by
727 * calling hook_paypal_transaction($field) for all modules; This hook is called
728 * only after information has been logged to the database which--if you have enabled
729 * database queueing--may not be the same instant that PayPal posts
730 * IPN information to the web site.
731 *
732 * This hook is invoked once for every PayPal transaction, so be careful what you write!
733 */
734 module_invoke_all('paypal_transaction', $field);
735
736 }
737
738 function _paypal_post($data = array()) {
739
740 $post = '';
741 foreach($data as $key => $value) {
742 $post .= $key .'='. urlencode($value).'&';
743 }
744 $post .= 'cmd=_notify-validate';
745
746 return $post;
747
748 }
749
750 function _paypal_ipn_receive($vars = 0, $op = 'queue') {
751
752 $vars = ($vars == 0 ? _paypal_init($_POST) : $vars); // If not processing the queue
753
754
755 // If the email isn't in our ACL, ppffft, done
756 if (!_paypal_verify_email($vars)) {
757 return false; // can't exit here, must return false
758 // so that we can process the queue
759 }
760
761 // if Queueing active, store it, and stop
762 if ( variable_get('paypal_ver_queue', 0) && isset($vars['receiver_email']) && $op == 'queue') {
763 _paypal_queue($vars);
764 exit;
765 }
766
767
768
769 switch(variable_get('paypal_postmethod', 'fsock') ) {
770 case 'curl':
771 $info = _paypal_post_curl($vars);
772 break;
773 case 'libCurl':
774 $info = _paypal_post_libcurl($vars);
775 break;
776 case 'fsock':
777 default:
778 $info = _paypal_post_fsock($vars);
779 break;
780 }
781
782 if (eregi('VERIFIED', $info)) { // well, if paypal verifies it, its worth logging
783 _paypal_log($vars); // if not, what do I care?
784 }
785
786 }
787
788
789 function _paypal_post_fsock($var = array()) {
790
791 $url = parse_url(variable_get('paypal_post', 'https://www.paypal.com/cgi-bin/webscr'));
792 if ($url['scheme'] == 'https') {
793 $url['port'] = '443';
794 $ssl = 'ssl://';
795 }
796 else {
797 $url['port'] = '80';
798 }
799
800 $sess = @fsockopen($ssl . $url['host'], $url['port'], $errno, $errstr, 30);
801 if ( $sess ) {
802 fputs($sess, "POST $url[path] HTTP/1.1\r\n");
803 fputs($sess, "Host: $url[host]\r\n");
804 fputs($sess, "Content-type: application/x-www-form-urlencoded\r\n");
805 fputs($sess, 'Content-length: '.strlen(_paypal_post($var))."\r\n");
806 fputs($sess, "Connection: close\r\n\r\n");
807 fputs($sess, _paypal_post($var) . "\r\n\r\n");
808
809 while (!feof($sess)) {
810 $info[] = @fgets($sess, 1024);
811 }
812
813 $info = implode(',', $info);
814 }
815 fclose($sess);
816
817 return $info;
818
819 }
820
821 function _paypal_post_libcurl($var = array()) {
822
823 $url = variable_get('paypal_post', 'https://www.paypal.com/cgi-bin/webscr');
824 // this code was from the php_pdn
825 $ch = curl_init();
826
827 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
828 curl_setopt($ch, CURLOPT_URL, $url);
829 curl_setopt($ch, CURLOPT_POST, 1);
830 curl_setopt($ch, CURLOPT_POSTFIELDS, _paypal_post($var));
831
832 //Start ob to prevent curl_exec from displaying stuff.
833 ob_start();
834 curl_exec($ch);
835
836 //Get contents of output buffer
837 $info = ob_get_contents();
838 curl_close($ch);
839
840 //End ob and erase contents.
841 ob_end_clean();
842
843 return $info;
844
845 }
846
847 function _paypal_post_curl($var = array()) {
848
849 $url = variable_get('paypal_post', 'https://www.paypal.com/cgi-bin/webscr');
850 exec(variable_get('paypal_curl', '/usr/bin/curl') .' -d "'._paypal_post($var)."\" $url", $info);
851 $info = implode(',', $info);
852 return $info;
853
854 }
855
856 function _paypal_init($field = array()) {
857
858 $paypal = array();
859 foreach( $field as $key => $value ) {
860 $paypal[$key] = $value;
861 }
862
863 return $paypal;
864
865 }
866
867 function _paypal_verify_email($field = array()) {
868
869 /**
870 * All we are doing here is making sure that we aren't logging
871 * useless information. We want to make sure that all the transaction
872 * information logged from PayPal is only for the PayPal accounts
873 * that you want to track.
874 */
875 $email = explode("\n", variable_get('paypal_emails', ''));
876 foreach( $email as $addr) {
877 if (strtolower(rtrim($field['receiver_email'])) == strtolower(rtrim($addr))) {
878 return true;
879 }
880 }
881 return false;
882 }
883
884
885 function _paypal_queue($field) {
886
887 if (variable_get('paypal_mysql_delayed', 0)) {
888 $delayed = 'DELAYED';
889 }
890 else {
891 $delayed = '';
892 }
893
894 db_query("INSERT %s INTO {paypal_queue} (value) VALUES('%s')", $delayed, serialize($field));
895
896 }
897
898
899 function _paypal_process_queue() {
900
901 $hours = variable_get('paypal_last_queue', time());
902 $lapse = intval(((time() - $hours )/60)/60); // set the number of runs.
903 $lapse = ($lapse < 1) ? 1 : $lapse; // Make sure that you run atleast every time cron.php is accessed.
904 $limit = variable_get('paypal_ver_limit', 5) * $lapse; // Hours lapses * Limit/hour.
905 $hardlimit = variable_get('paypal_ver_max', 20);
906 if (variable_get('paypal_ver_max', 0) != 0) { // See if the limiter is enabled.
907 $limit = ($hardlimit < $limit) ? $hardlimit : $limit;
908 }
909
910 // Fetch a limited number of queued log entries.
911 $result = db_query('SELECT log, value FROM {paypal_queue} LIMIT %d', $limit);
912
913 // Process queued log entries individually.
914 while ($row = db_fetch_object($result) ) {
915 _paypal_ipn_receive(unserialize($row->value), 'process');
916 }
917
918 /**
919 * We need to delete the same log entries we just processed. If we impose
920 * a non-standard order to the results in the 'SELECT', we need to do the
921 * same in the 'DELETE ... LIMIT'.
922 *
923 * We could use 'ORDER BY log', except MySQL < 4.0 does not support it. So
924 * I propose we do not use 'ORDER BY' in the 'SELECT' statement.
925 *
926 * ~ Nic Ivy
927 */
928 db_query('DELETE FROM {paypal_queue} LIMIT %d', $limit);
929 variable_set('paypal_last_queue', time());
930
931 }

  ViewVC Help
Powered by ViewVC 1.1.2