This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
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 | // This file do not require login because paypal service will use to confirm transactions. |
||
33 | // @codingStandardsIgnoreLine |
||
34 | require("../../../config.php"); |
||
35 | |||
36 | require_once($CFG->libdir . '/filelib.php'); |
||
37 | |||
38 | // PayPal does not like when we return error messages here, |
||
39 | // the custom handler just logs exceptions and stops. |
||
40 | set_exception_handler('availability_paypal_ipn_exception_handler'); |
||
41 | |||
42 | // Keep out casual intruders. |
||
43 | if (empty($_POST) or !empty($_GET)) { |
||
44 | print_error("Sorry, you can not use the script that way."); |
||
45 | } |
||
46 | |||
47 | // Read all the data from PayPal and get it ready for later; |
||
48 | // we expect only valid UTF-8 encoding, it is the responsibility |
||
49 | // of user to set it up properly in PayPal business account, |
||
50 | // it is documented in docs wiki. |
||
51 | $req = 'cmd=_notify-validate'; |
||
52 | |||
53 | foreach ($_POST as $key => $value) { |
||
54 | $req .= "&$key=".urlencode($value); |
||
55 | } |
||
56 | |||
57 | $data = new stdclass(); |
||
58 | $data->business = optional_param('business', '', PARAM_TEXT); |
||
59 | $data->receiver_email = optional_param('receiver_email', '', PARAM_TEXT); |
||
60 | $data->receiver_id = optional_param('receiver_id', '', PARAM_TEXT); |
||
61 | $data->item_name = optional_param('item_name', '', PARAM_TEXT); |
||
62 | $data->memo = optional_param('memo', '', PARAM_TEXT); |
||
63 | $data->tax = optional_param('tax', '', PARAM_TEXT); |
||
64 | $data->option_name1 = optional_param('option_name1', '', PARAM_TEXT); |
||
65 | $data->option_selection1_x = optional_param('option_selection1_x', '', PARAM_TEXT); |
||
66 | $data->option_name2 = optional_param('option_name2', '', PARAM_TEXT); |
||
67 | $data->option_selection2_x = optional_param('option_selection2_x', '', PARAM_TEXT); |
||
68 | $data->payment_status = optional_param('payment_status', '', PARAM_TEXT); |
||
69 | $data->pending_reason = optional_param('pending_reason', '', PARAM_TEXT); |
||
70 | $data->reason_code = optional_param('reason_code', '', PARAM_TEXT); |
||
71 | $data->txn_id = optional_param('txn_id', '', PARAM_TEXT); |
||
72 | $data->parent_txn_id = optional_param('parent_txn_id', '', PARAM_TEXT); |
||
73 | $data->payment_type = optional_param('payment_type', '', PARAM_TEXT); |
||
74 | $data->payment_gross = optional_param('mc_gross', '', PARAM_TEXT); |
||
75 | $data->payment_currency = optional_param('mc_currency', '', PARAM_TEXT); |
||
76 | $custom = optional_param('custom', '', PARAM_TEXT); |
||
77 | $custom = explode('-', $custom); |
||
78 | $data->userid = (int)$custom[0]; |
||
79 | $data->contextid = (int)$custom[1]; |
||
80 | $data->timeupdated = time(); |
||
81 | |||
82 | View Code Duplication | if (! $user = $DB->get_record("user", array("id" => $data->userid))) { |
|
0 ignored issues
–
show
|
|||
83 | availability_paypal_message_error_to_admin("Not a valid user id", $data); |
||
84 | die; |
||
85 | } |
||
86 | |||
87 | if (! $context = context::instance_by_id($data->contextid, IGNORE_MISSING)) { |
||
88 | availability_paypal_message_error_to_admin("Not a valid context id", $data); |
||
89 | die; |
||
90 | } |
||
91 | |||
92 | $instanceid = $context->instanceid; |
||
93 | View Code Duplication | if ($context instanceof context_module) { |
|
0 ignored issues
–
show
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 dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. ![]() 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. ![]() |
|||
94 | $availability = $DB->get_field('course_modules', 'availability', array('id' => $instanceid), MUST_EXIST); |
||
95 | $availability = json_decode($availability); |
||
96 | foreach ($availability->c as $condition) { |
||
97 | if ($condition->type == 'paypal') { |
||
98 | // TODO: handle more than one paypal for this context. |
||
99 | $paypal = $condition; |
||
100 | break; |
||
101 | } else { |
||
102 | availability_paypal_message_error_to_admin("Not a valid context id", $data); |
||
103 | } |
||
104 | } |
||
105 | } else { |
||
106 | // TODO: handle sections. |
||
107 | print_error('support to sections not yet implemented.'); |
||
108 | } |
||
109 | |||
110 | // Open a connection back to PayPal to validate the data. |
||
111 | $paypaladdr = empty($CFG->usepaypalsandbox) ? 'www.paypal.com' : 'www.sandbox.paypal.com'; |
||
112 | $c = new curl(); |
||
113 | $options = array( |
||
114 | 'returntransfer' => true, |
||
115 | 'httpheader' => array('application/x-www-form-urlencoded', "Host: $paypaladdr"), |
||
116 | 'timeout' => 30, |
||
117 | 'CURLOPT_HTTP_VERSION' => CURL_HTTP_VERSION_1_1, |
||
118 | ); |
||
119 | $location = "https://{$paypaladdr}/cgi-bin/webscr"; |
||
120 | $result = $c->post($location, $req, $options); |
||
121 | |||
122 | if (!$result) { // Could not connect to PayPal - FAIL. |
||
123 | echo "<p>Error: could not access paypal.com</p>"; |
||
124 | availability_paypal_message_error_to_admin("Could not access paypal.com to verify payment", $data); |
||
125 | die; |
||
126 | } |
||
127 | |||
128 | // Connection is OK, so now we post the data to validate it. |
||
129 | |||
130 | // Now read the response and check if everything is OK. |
||
131 | |||
132 | if (strlen($result) > 0) { |
||
133 | if (strcmp($result, "VERIFIED") == 0) { // VALID PAYMENT! |
||
134 | |||
135 | $DB->insert_record("availability_paypal_tnx", $data); |
||
136 | |||
137 | // Check the payment_status and payment_reason. |
||
138 | |||
139 | // If status is not completed, just tell admin, transaction will be saved later. |
||
140 | if ($data->payment_status != "Completed" and $data->payment_status != "Pending") { |
||
141 | availability_paypal_message_error_to_admin("Status not completed or pending. User payment status updated", $data); |
||
142 | } |
||
143 | |||
144 | // If currency is incorrectly set then someone maybe trying to cheat the system. |
||
145 | if ($data->payment_currency != $paypal->currency) { |
||
146 | $str = "Currency does not match course settings, received: " . $data->payment_currency; |
||
147 | availability_paypal_message_error_to_admin($str, $data); |
||
148 | die; |
||
149 | } |
||
150 | |||
151 | // If status is pending and reason is other than echeck, |
||
152 | // then we are on hold until further notice. |
||
153 | // Email user to let them know. Email admin. |
||
154 | if ($data->payment_status == "Pending" and $data->pending_reason != "echeck") { |
||
155 | |||
156 | $eventdata = new \core\message\message(); |
||
157 | $eventdata->component = 'availability_paypal'; |
||
158 | $eventdata->name = 'payment_pending'; |
||
159 | $eventdata->userfrom = get_admin(); |
||
160 | $eventdata->userto = $user; |
||
161 | $eventdata->subject = get_string("paypalpaymentpendingsubject", 'availability_paypal'); |
||
162 | $eventdata->fullmessage = get_string('paypalpaymentpendingmessage', 'availability_paypal'); |
||
163 | $eventdata->fullmessageformat = FORMAT_PLAIN; |
||
164 | $eventdata->fullmessagehtml = ''; |
||
165 | $eventdata->smallmessage = ''; |
||
166 | message_send($eventdata); |
||
167 | } |
||
168 | |||
169 | // If our status is not completed or not pending on an echeck clearance then ignore and die. |
||
170 | // This check is redundant at present but may be useful if paypal extend the return codes in the future. |
||
171 | if (! ( $data->payment_status == "Completed" or |
||
172 | ($data->payment_status == "Pending" and $data->pending_reason == "echeck") ) ) { |
||
173 | die; |
||
174 | } |
||
175 | |||
176 | // At this point we only proceed with a status of completed or pending with a reason of echeck. |
||
177 | |||
178 | // Make sure this transaction doesn't exist already. |
||
179 | if ($existing = $DB->get_record("availability_paypal_tnx", array("txn_id" => $data->txn_id))) { |
||
180 | availability_paypal_message_error_to_admin("Transaction $data->txn_id is being repeated!", $data); |
||
181 | die; |
||
182 | } |
||
183 | |||
184 | // Check that the email is the one we want it to be. |
||
185 | if (core_text::strtolower($data->business) !== core_text::strtolower($paypal->businessemail)) { |
||
186 | availability_paypal_message_error_to_admin("Business email is {$data->business} (not ". |
||
187 | $paypal->businessemail.")", $data); |
||
188 | die; |
||
189 | } |
||
190 | |||
191 | // Check that user exists. |
||
192 | View Code Duplication | if (!$user = $DB->get_record('user', array('id' => $data->userid))) { |
|
0 ignored issues
–
show
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. ![]() |
|||
193 | availability_paypal_message_error_to_admin("User {$data->userid} doesn't exist", $data); |
||
194 | die; |
||
195 | } |
||
196 | |||
197 | // Check that course exists. |
||
198 | if (!$course = $DB->get_record('course', array('id' => $data->courseid))) { |
||
199 | availability_paypal_message_error_to_admin("Course {$data->courseid} doesn't exist", $data); |
||
200 | die; |
||
201 | } |
||
202 | |||
203 | $coursecontext = context_course::instance($course->id, IGNORE_MISSING); |
||
204 | |||
205 | // Check that amount paid is the correct amount. |
||
206 | if ( (float) $paypal->cost < 0 ) { |
||
207 | $cost = (float) 0; |
||
208 | } else { |
||
209 | $cost = (float) $paypal->cost; |
||
210 | } |
||
211 | |||
212 | // Use the same rounding of floats as on the plugin form. |
||
213 | $cost = format_float($cost, 2, false); |
||
214 | |||
215 | if ($data->payment_gross < $cost) { |
||
216 | availability_paypal_message_error_to_admin("Amount paid is not enough ({$data->payment_gross} < {$cost}))", $data); |
||
217 | die; |
||
218 | } |
||
219 | |||
220 | // All clear! |
||
221 | |||
222 | // Pass $view=true to filter hidden caps if the user cannot see them. |
||
223 | if ($users = get_users_by_capability($context, 'moodle/course:update', 'u.*', 'u.id ASC', |
||
224 | '', '', '', '', false, true)) { |
||
225 | $users = sort_by_roleassignment_authority($users, $context); |
||
226 | $teacher = array_shift($users); |
||
227 | } else { |
||
228 | $teacher = false; |
||
229 | } |
||
230 | |||
231 | } else if (strcmp ($result, "INVALID") == 0) { // ERROR. |
||
232 | $DB->insert_record("availability_paypal_tnx", $data, false); |
||
233 | availability_paypal_message_error_to_admin("Received an invalid payment notification!! (Fake payment?)", $data); |
||
234 | } |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Sends message to admin about error |
||
239 | * |
||
240 | * @param string $subject |
||
241 | * @param stdClass $data |
||
242 | */ |
||
243 | function availability_paypal_message_error_to_admin($subject, $data) { |
||
244 | $admin = get_admin(); |
||
245 | $site = get_site(); |
||
246 | |||
247 | $message = "$site->fullname: Transaction failed:{$subject}"; |
||
248 | |||
249 | foreach ($data as $key => $value) { |
||
0 ignored issues
–
show
|
|||
250 | $message .= "{$key} => {$value};"; |
||
251 | } |
||
252 | |||
253 | $eventdata = new stdClass(); |
||
254 | $eventdata->component = 'availability_paypal'; |
||
255 | $eventdata->name = 'payment_error'; |
||
256 | $eventdata->userfrom = $admin; |
||
257 | $eventdata->userto = $admin; |
||
258 | $eventdata->subject = "PayPal ERROR: ".$subject; |
||
259 | $eventdata->fullmessage = $message; |
||
260 | $eventdata->fullmessageformat = FORMAT_PLAIN; |
||
261 | $eventdata->fullmessagehtml = ''; |
||
262 | $eventdata->smallmessage = ''; |
||
263 | message_send($eventdata); |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Silent exception handler. |
||
268 | * |
||
269 | * @param Exception $ex |
||
270 | * @return void - does not return. Terminates execution! |
||
271 | */ |
||
272 | function availability_paypal_ipn_exception_handler($ex) { |
||
273 | $info = get_exception_info($ex); |
||
274 | |||
275 | $logerrmsg = "availability_paypal IPN exception handler: ".$info->message; |
||
276 | if (debugging('', DEBUG_NORMAL)) { |
||
277 | $logerrmsg .= ' Debug: '.$info->debuginfo."\n".format_backtrace($info->backtrace, true); |
||
278 | } |
||
279 | mtrace($logerrmsg); |
||
280 | exit(0); |
||
281 | } |
||
282 |
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.