GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 60b97c...87165c )
by Daniel Neis
02:21
created

ipn.php ➔ availability_paypal_ipn_exception_handler()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 7
nc 2
nop 1
dl 0
loc 10
rs 9.4285
c 2
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 296 and the first side effect is on line 32.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17
/**
18
 * Listens for Instant Payment Notification from PayPal
19
 *
20
 * This script waits for Payment notification from PayPal,
21
 * then double checks that data by sending it back to PayPal.
22
 * If PayPal verifies this then sets the activity as completed.
23
 *
24
 * @package    availability_paypal
25
 * @copyright  2010 Eugene Venter
26
 * @copyright  2015 Daniel Neis
27
 * @author     Eugene Venter - based on code by others
28
 * @author     Daniel Neis - based on code by others
29
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30
 */
31
32
require("../../../config.php");
33
require_once($CFG->libdir.'/eventslib.php');
34
require_once($CFG->libdir . '/filelib.php');
35
36
// PayPal does not like when we return error messages here,
37
// the custom handler just logs exceptions and stops.
38
set_exception_handler('availability_paypal_ipn_exception_handler');
39
40
// Keep out casual intruders.
41
if (empty($_POST) or !empty($_GET)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
42
    print_error("Sorry, you can not use the script that way.");
43
}
44
45
46
// Read all the data from PayPal and get it ready for later;
47
// we expect only valid UTF-8 encoding, it is the responsibility
48
// of user to set it up properly in PayPal business account,
49
// it is documented in docs wiki.
50
$req = 'cmd=_notify-validate';
51
52
foreach ($_POST as $key => $value) {
53
        $req .= "&$key=".urlencode($value);
54
}
55
56
$data = new stdclass();
57
$data->business             = optional_param('business', '', PARAM_TEXT);
58
$data->receiver_email       = optional_param('receiver_email', '', PARAM_TEXT);
59
$data->receiver_id          = optional_param('receiver_id', '', PARAM_TEXT);
60
$data->item_name            = optional_param('item_name', '', PARAM_TEXT);
61
$data->memo                 = optional_param('memo', '', PARAM_TEXT);
62
$data->tax                  = optional_param('tax', '', PARAM_TEXT);
63
$data->option_name1         = optional_param('option_name1', '', PARAM_TEXT);
64
$data->option_selection1_x  = optional_param('option_selection1_x', '', PARAM_TEXT);
65
$data->option_name2         = optional_param('option_name2', '', PARAM_TEXT);
66
$data->option_selection2_x  = optional_param('option_selection2_x', '', PARAM_TEXT);
67
$data->payment_status       = optional_param('payment_status', '', PARAM_TEXT);
68
$data->pending_reason       = optional_param('pending_reason', '', PARAM_TEXT);
69
$data->reason_code          = optional_param('reason_code', '', PARAM_TEXT);
70
$data->txn_id               = optional_param('txn_id', '', PARAM_TEXT);
71
$data->parent_txn_id        = optional_param('parent_txn_id', '', PARAM_TEXT);
72
$data->payment_type         = optional_param('payment_type', '', PARAM_TEXT);
73
$data->payment_gross        = optional_param('mc_gross', '', PARAM_TEXT);
74
$data->payment_currency     = optional_param('mc_currency', '', PARAM_TEXT);
75
$custom = optional_param('custom', '', PARAM_TEXT);
76
$custom = explode('-', $custom);
77
$data->userid           = (int)$custom[0];
78
$data->contextid       = (int)$custom[1];
79
$data->timeupdated      = time();
80
81
if (! $user = $DB->get_record("user", array("id" => $data->userid))) {
82
    availability_paypal_message_error_to_admin("Not a valid user id", $data);
83
    die;
84
}
85
86
if (! $context = context::instance_by_id($data->contextid, IGNORE_MISSING)) {
87
    availability_paypal_message_error_to_admin("Not a valid context id", $data);
88
    die;
89
}
90
91
$instanceid = $context->instanceid;
92 View Code Duplication
if ($context instanceof context_module) {
0 ignored issues
show
Bug introduced by
The class context_module does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
93
    $availability = $DB->get_field('course_modules', 'availability', array('id' => $instanceid), MUST_EXIST);
94
    $availability = json_decode($availability);
95
    foreach ($availability->c as $condition) {
96
        if ($condition->type == 'paypal') {
97
            // TODO: handle more than one paypal for this context.
98
            $paypal = $condition;
99
            break;
100
        } else {
101
            availability_paypal_message_error_to_admin("Not a valid context id", $data);
102
        }
103
    }
104
} else {
105
    // TODO: handle sections.
106
    print_error('support to sections not yet implemented.');
107
}
108
109
// Open a connection back to PayPal to validate the data.
110
$paypaladdr = empty($CFG->usepaypalsandbox) ? 'www.paypal.com' : 'www.sandbox.paypal.com';
111
$c = new curl();
112
$options = array(
113
    'returntransfer' => true,
114
    'httpheader' => array('application/x-www-form-urlencoded', "Host: $paypaladdr"),
115
    'timeout' => 30,
116
    'CURLOPT_HTTP_VERSION' => CURL_HTTP_VERSION_1_1,
117
);
118
$location = "https://{$paypaladdr}/cgi-bin/webscr";
119
$result = $c->post($location, $req, $options);
120
121
if (!$result) {  // Could not connect to PayPal - FAIL.
122
    echo "<p>Error: could not access paypal.com</p>";
123
    availability_paypal_message_error_to_admin("Could not access paypal.com to verify payment", $data);
124
    die;
125
}
126
127
// Connection is OK, so now we post the data to validate it.
128
129
// Now read the response and check if everything is OK.
130
131
if (strlen($result) > 0) {
132
    if (strcmp($result, "VERIFIED") == 0) {          // VALID PAYMENT!
133
134
        $DB->insert_record("availability_paypal_tnx", $data);
135
136
        // Check the payment_status and payment_reason.
137
138
        // If status is not completed, just tell admin, transaction will be saved later.
139
        if ($data->payment_status != "Completed" and $data->payment_status != "Pending") {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
140
            availability_paypal_message_error_to_admin("Status not completed or pending. User payment status updated", $data);
141
        }
142
143
        // If currency is incorrectly set then someone maybe trying to cheat the system.
144
        if ($data->payment_currency != $paypal->currency) {
145
            availability_paypal_message_error_to_admin("Currency does not match course settings, received: ".$data->payment_currency, $data);
146
            die;
147
        }
148
149
        // If status is pending and reason is other than echeck,
150
        // then we are on hold until further notice.
151
        // Email user to let them know. Email admin.
152
        if ($data->payment_status == "Pending" and $data->pending_reason != "echeck") {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
153
154
            $eventdata = new \core\message\message();
155
            $eventdata->component         = 'availability_paypal';
156
            $eventdata->name              = 'payment_pending';
157
            $eventdata->userfrom          = get_admin();
158
            $eventdata->userto            = $user;
159
            $eventdata->subject           = get_string("paypalpaymentpendingsubject", 'availability_paypal');
160
            $eventdata->fullmessage       = get_string('paypalpaymentpendingmessage', 'availability_paypal');
161
            $eventdata->fullmessageformat = FORMAT_PLAIN;
162
            $eventdata->fullmessagehtml   = '';
163
            $eventdata->smallmessage      = '';
164
            message_send($eventdata);
165
        }
166
167
        // If our status is not completed or not pending on an echeck clearance then ignore and die.
168
        // This check is redundant at present but may be useful if paypal extend the return codes in the future.
169
        if (! ( $data->payment_status == "Completed" or
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
170
               ($data->payment_status == "Pending" and $data->pending_reason == "echeck") ) ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
171
            die;
172
        }
173
174
        // At this point we only proceed with a status of completed or pending with a reason of echeck.
175
176
        // Make sure this transaction doesn't exist already.
177
        if ($existing = $DB->get_record("availability_paypal_tnx", array("txn_id" => $data->txn_id))) {
178
            availability_paypal_message_error_to_admin("Transaction $data->txn_id is being repeated!", $data);
179
            die;
180
        }
181
182
        // Check that the email is the one we want it to be.
183
        if (core_text::strtolower($data->business) !== core_text::strtolower($paypal->businessemail)) {
184
            availability_paypal_message_error_to_admin("Business email is {$data->business} (not ".
185
                                            $paypal->businessemail.")", $data);
186
            die;
187
        }
188
189
        // Check that user exists.
190
        if (!$user = $DB->get_record('user', array('id' => $data->userid))) {
191
            availability_paypal_message_error_to_admin("User {$data->userid} doesn't exist", $data);
192
            die;
193
        }
194
195
        // Check that course exists.
196
        if (!$course = $DB->get_record('course', array('id' => $data->courseid))) {
197
            availability_paypal_message_error_to_admin("Course {$data->courseid} doesn't exist", $data);
198
            die;
199
        }
200
201
        $coursecontext = context_course::instance($course->id, IGNORE_MISSING);
202
203
        // Check that amount paid is the correct amount.
204
        if ( (float) $paypal->cost < 0 ) {
205
            $cost = (float) 0;
206
        } else {
207
            $cost = (float) $paypal->cost;
208
        }
209
210
        // Use the same rounding of floats as on the plugin form.
211
        $cost = format_float($cost, 2, false);
212
213
        if ($data->payment_gross < $cost) {
214
            availability_paypal_message_error_to_admin("Amount paid is not enough ({$data->payment_gross} < {$cost}))", $data);
215
            die;
216
        }
217
218
        // All clear!
219
220
        // Pass $view=true to filter hidden caps if the user cannot see them.
221
        if ($users = get_users_by_capability($context, 'moodle/course:update', 'u.*', 'u.id ASC',
222
                                             '', '', '', '', false, true)) {
223
            $users = sort_by_roleassignment_authority($users, $context);
224
            $teacher = array_shift($users);
225
        } else {
226
            $teacher = false;
227
        }
228
229
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
230
        $mailstudents = $paypal->mailstudents;
231
        $mailteachers = $paypal->mailteachers;
232
        $mailadmins   = $paypal->mailadmins;
233
        $shortname = format_string($course->shortname, true, array('context' => $context));
234
235
        if (!empty($mailstudents)) {
236
            $a = new stdClass();
237
            $a->coursename = format_string($course->fullname, true, array('context' => $coursecontext));
238
            $a->profileurl = "$CFG->wwwroot/user/view.php?id=$user->id";
239
240
            $eventdata = new \core\message\message();
241
            $eventdata->component         = 'availability_paypal';
242
            $eventdata->name              = 'payment_completed';
243
            $eventdata->userfrom          = empty($teacher) ? core_user::get_support_user() : $teacher;
244
            $eventdata->userto            = $user;
245
            $eventdata->subject           = get_string("paypalpaymentcompletedsubject", 'paypal');
246
            $eventdata->fullmessage       = get_string('paypalpaymentcompletedmessage', 'paypal');
247
            $eventdata->fullmessageformat = FORMAT_PLAIN;
248
            $eventdata->fullmessagehtml   = '';
249
            $eventdata->smallmessage      = '';
250
            message_send($eventdata);
251
        }
252
253
        if (!empty($mailteachers) && !empty($teacher)) {
254
            $a->course = format_string($course->fullname, true, array('context' => $coursecontext));
255
            $a->user = fullname($user);
256
257
            $eventdata = new \core\message\message();
258
            $eventdata->component         = 'availability_paypal';
259
            $eventdata->name              = 'payment_completed';
260
            $eventdata->userfrom          = $user;
261
            $eventdata->userto            = $teacher;
262
            $eventdata->subject           = get_string("paypalpaymentcompletedsubject", 'paypal');
263
            $eventdata->fullmessage       = get_string('paypalpaymentcompletedmessage', 'paypal');
264
            $eventdata->fullmessageformat = FORMAT_PLAIN;
265
            $eventdata->fullmessagehtml   = '';
266
            $eventdata->smallmessage      = '';
267
            message_send($eventdata);
268
        }
269
270
        if (!empty($mailadmins)) {
271
            $a->course = format_string($course->fullname, true, array('context' => $coursecontext));
272
            $a->user = fullname($user);
273
            $admins = get_admins();
274
            foreach ($admins as $admin) {
275
                $eventdata = new \core\message\message();
276
                $eventdata->component         = 'availability_paypal';
277
                $eventdata->name              = 'payment_completed';
278
                $eventdata->userfrom          = $user;
279
                $eventdata->userto            = $admin;
280
                $eventdata->subject           = get_string("paypalpaymentcompletedsubject", 'paypal');
281
                $eventdata->fullmessage       = get_string('paypalpaymentcompletedmessage', 'paypal');
282
                $eventdata->fullmessageformat = FORMAT_PLAIN;
283
                $eventdata->fullmessagehtml   = '';
284
                $eventdata->smallmessage      = '';
285
                message_send($eventdata);
286
            }
287
        }
288
        */
289
290
    } else if (strcmp ($result, "INVALID") == 0) { // ERROR.
291
        $DB->insert_record("availability_paypal_tnx", $data, false);
292
        availability_paypal_message_error_to_admin("Received an invalid payment notification!! (Fake payment?)", $data);
293
    }
294
}
295
296
function availability_paypal_message_error_to_admin($subject, $data) {
297
    $admin = get_admin();
298
    $site = get_site();
299
300
    $message = "$site->fullname:  Transaction failed:{$subject}";
301
302
    foreach ($data as $key => $value) {
303
        $message .= "{$key} => {$value};";
304
    }
305
306
    $eventdata = new stdClass();
307
    $eventdata->component         = 'availability_paypal';
308
    $eventdata->name              = 'payment_error';
309
    $eventdata->userfrom          = $admin;
310
    $eventdata->userto            = $admin;
311
    $eventdata->subject           = "PayPal ERROR: ".$subject;
312
    $eventdata->fullmessage       = $message;
313
    $eventdata->fullmessageformat = FORMAT_PLAIN;
314
    $eventdata->fullmessagehtml   = '';
315
    $eventdata->smallmessage      = '';
316
    message_send($eventdata);
317
}
318
319
/**
320
 * Silent exception handler.
321
 *
322
 * @param Exception $ex
323
 * @return void - does not return. Terminates execution!
324
 */
325
function availability_paypal_ipn_exception_handler($ex) {
326
    $info = get_exception_info($ex);
327
328
    $logerrmsg = "availability_paypal IPN exception handler: ".$info->message;
329
    if (debugging('', DEBUG_NORMAL)) {
330
        $logerrmsg .= ' Debug: '.$info->debuginfo."\n".format_backtrace($info->backtrace, true);
331
    }
332
    mtrace($logerrmsg);
333
    exit(0);
0 ignored issues
show
Coding Style Compatibility introduced by
The function availability_paypal_ipn_exception_handler() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
334
}
335