| Total Complexity | 45 |
| Total Lines | 536 |
| Duplicated Lines | 0 % |
| Changes | 9 | ||
| Bugs | 0 | Features | 0 |
Complex classes like ipn_paypal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ipn_paypal, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 20 | class ipn_paypal |
||
| 21 | { |
||
| 22 | /** |
||
| 23 | * Args from PayPal notify return URL |
||
| 24 | * |
||
| 25 | * @var string |
||
| 26 | */ |
||
| 27 | private $args_return_uri = []; |
||
| 28 | /** Production and Sandbox Postback URL |
||
| 29 | * |
||
| 30 | * @var array |
||
| 31 | */ |
||
| 32 | private static $remote_uri = [ |
||
| 33 | ['hostname' => 'www.paypal.com', 'uri' => 'https://www.paypal.com/cgi-bin/webscr', 'type' => 'live'], |
||
| 34 | ['hostname' => 'www.sandbox.paypal.com', 'uri' => 'https://www.sandbox.paypal.com/cgi-bin/webscr', 'type' => 'sandbox'], |
||
| 35 | ['hostname' => 'ipnpb.paypal.com', 'uri' => 'https://ipnpb.paypal.com/cgi-bin/webscr', 'type' => 'live'], |
||
| 36 | ['hostname' => 'ipnpb.sandbox.paypal.com', 'uri' => 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr', 'type' => 'sandbox'], |
||
| 37 | ]; |
||
| 38 | |||
| 39 | protected $config; |
||
| 40 | protected $language; |
||
| 41 | protected $ppde_ext_manager; |
||
| 42 | protected $ppde_ipn_log; |
||
| 43 | protected $request; |
||
| 44 | /** |
||
| 45 | * @var array |
||
| 46 | */ |
||
| 47 | private $curl_fsock = ['curl' => false, 'none' => true]; |
||
| 48 | /** |
||
| 49 | * @var array |
||
| 50 | */ |
||
| 51 | private $postback_args = []; |
||
| 52 | /** |
||
| 53 | * Full PayPal response for include in text report |
||
| 54 | * |
||
| 55 | * @var string |
||
| 56 | */ |
||
| 57 | private $report_response = ''; |
||
| 58 | /** |
||
| 59 | * PayPal response (VERIFIED or INVALID) |
||
| 60 | * |
||
| 61 | * @var string |
||
| 62 | */ |
||
| 63 | private $response = ''; |
||
| 64 | /** |
||
| 65 | * PayPal response status (code 200 or other) |
||
| 66 | * |
||
| 67 | * @var string |
||
| 68 | */ |
||
| 69 | private $response_status = ''; |
||
| 70 | /** |
||
| 71 | * PayPal URL |
||
| 72 | * Could be Sandbox URL ou normal PayPal URL. |
||
| 73 | * |
||
| 74 | * @var string |
||
| 75 | */ |
||
| 76 | private $u_paypal = ''; |
||
| 77 | |||
| 78 | /** |
||
| 79 | * Constructor |
||
| 80 | * |
||
| 81 | * @param config $config Config object |
||
| 82 | * @param language $language Language user object |
||
| 83 | * @param extension_manager $ppde_ext_manager Extension manager object |
||
| 84 | * @param ipn_log $ppde_ipn_log IPN log |
||
| 85 | * @param request $request Request object |
||
| 86 | * |
||
| 87 | * @access public |
||
| 88 | */ |
||
| 89 | public function __construct( |
||
| 102 | } |
||
| 103 | |||
| 104 | /** |
||
| 105 | * @return array |
||
| 106 | */ |
||
| 107 | public static function get_remote_uri(): array |
||
| 110 | } |
||
| 111 | |||
| 112 | /** |
||
| 113 | * Initiate communication with PayPal. |
||
| 114 | * We use cURL. If it is not available we log an error. |
||
| 115 | * |
||
| 116 | * @param array $data |
||
| 117 | * |
||
| 118 | * @return void |
||
| 119 | * @access public |
||
| 120 | */ |
||
| 121 | public function initiate_paypal_connection($data): void |
||
| 130 | } |
||
| 131 | |||
| 132 | private function log_paypal_connection_error($data): void |
||
| 133 | { |
||
| 134 | $this->ppde_ipn_log->log_error( |
||
| 135 | $this->language->lang('NO_CONNECTION_DETECTED'), |
||
| 136 | $this->ppde_ipn_log->is_use_log_error(), |
||
| 137 | true, |
||
| 138 | E_USER_ERROR, |
||
| 139 | $data |
||
| 140 | ); |
||
| 141 | } |
||
| 142 | |||
| 143 | /** |
||
| 144 | * Post Back Using cURL |
||
| 145 | * |
||
| 146 | * Sends the post back to PayPal using the cURL library. Called by |
||
| 147 | * the validate_transaction() method if the curl_fsock['curl'] property is true. |
||
| 148 | * Throws an exception if the post fails. Populates the response and response_status properties on success. |
||
| 149 | * |
||
| 150 | * @param string $encoded_data The post data as a URL encoded string |
||
| 151 | * |
||
| 152 | * @return void |
||
| 153 | * @access private |
||
| 154 | */ |
||
| 155 | private function curl_post($encoded_data): void |
||
| 156 | { |
||
| 157 | $ch = $this->init_curl_session($encoded_data); |
||
| 158 | $this->valuate_response(curl_exec($ch), $ch); |
||
| 159 | if ($this->ppde_ipn_log->is_use_log_error()) |
||
| 160 | { |
||
| 161 | $this->parse_curl_response(); |
||
| 162 | } |
||
| 163 | curl_close($ch); |
||
| 164 | } |
||
| 165 | |||
| 166 | /** |
||
| 167 | * Initializes a cURL session with the specified encoded data. |
||
| 168 | * |
||
| 169 | * @param string $encoded_data The encoded data to be sent in the cURL request. |
||
| 170 | * |
||
| 171 | * @return resource Returns a cURL session handle on success, false on failure. |
||
| 172 | * @access private |
||
| 173 | */ |
||
| 174 | private function init_curl_session($encoded_data) |
||
| 175 | { |
||
| 176 | $ch = curl_init($this->u_paypal); |
||
| 177 | |||
| 178 | curl_setopt_array($ch, [ |
||
| 179 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, |
||
| 180 | CURLOPT_POST => true, |
||
| 181 | CURLOPT_RETURNTRANSFER => true, |
||
| 182 | CURLOPT_POSTFIELDS => $encoded_data, |
||
| 183 | CURLOPT_SSLVERSION => 6, |
||
| 184 | CURLOPT_SSL_VERIFYPEER => 1, |
||
| 185 | CURLOPT_SSL_VERIFYHOST => 2, |
||
| 186 | CURLOPT_FORBID_REUSE => true, |
||
| 187 | CURLOPT_CONNECTTIMEOUT => 30, |
||
| 188 | CURLOPT_HTTPHEADER => [ |
||
| 189 | 'User-Agent: PHP-IPN-Verification-Script', |
||
| 190 | 'Connection: Close', |
||
| 191 | ], |
||
| 192 | ]); |
||
| 193 | |||
| 194 | if ($this->ppde_ipn_log->is_use_log_error()) |
||
| 195 | { |
||
| 196 | curl_setopt($ch, CURLOPT_HEADER, true); |
||
| 197 | curl_setopt($ch, CURLINFO_HEADER_OUT, true); |
||
| 198 | } |
||
| 199 | |||
| 200 | return $ch; |
||
| 201 | } |
||
| 202 | |||
| 203 | /** |
||
| 204 | * Updates the response status and logs any cURL errors. |
||
| 205 | * |
||
| 206 | * @param mixed $response The response received from the API call. |
||
| 207 | * @param resource $ch The cURL handle used to make the API call. |
||
| 208 | * @return void |
||
| 209 | */ |
||
| 210 | private function valuate_response($response, $ch) |
||
| 211 | { |
||
| 212 | $this->report_response = $response; |
||
| 213 | if (curl_errno($ch) != 0) |
||
| 214 | { |
||
| 215 | $this->log_curl_error($ch); |
||
| 216 | } |
||
| 217 | else |
||
| 218 | { |
||
| 219 | $this->response_status = curl_getinfo($ch)['http_code']; |
||
| 220 | } |
||
| 221 | } |
||
| 222 | |||
| 223 | /** |
||
| 224 | * Log the error message from a cURL request. |
||
| 225 | * |
||
| 226 | * @param resource $ch The cURL handle. |
||
| 227 | * @return void |
||
| 228 | */ |
||
| 229 | private function log_curl_error($ch): void |
||
| 230 | { |
||
| 231 | $this->ppde_ipn_log->log_error( |
||
| 232 | $this->language->lang('CURL_ERROR', curl_errno($ch) . ' (' . curl_error($ch) . ')'), |
||
| 233 | $this->ppde_ipn_log->is_use_log_error() |
||
| 234 | ); |
||
| 235 | } |
||
| 236 | |||
| 237 | /** |
||
| 238 | * Parses the cURL response and separates the response headers from the payload. |
||
| 239 | * |
||
| 240 | * This method splits the response by the double line-break "\r\n\r\n". It then trims the response headers and |
||
| 241 | * stores the trimmed payload as the new response. |
||
| 242 | * |
||
| 243 | * @access private |
||
| 244 | * @return void |
||
| 245 | */ |
||
| 246 | private function parse_curl_response(): void |
||
| 247 | { |
||
| 248 | // Split response headers and payload, a better way for strcmp |
||
| 249 | $tokens = explode("\r\n\r\n", trim($this->report_response)); |
||
| 250 | $this->response = trim(end($tokens)); |
||
| 251 | } |
||
| 252 | |||
| 253 | /** |
||
| 254 | * Set property 'curl_fsock' to use cURL based on config settings. |
||
| 255 | * If cURL is not available we use default value of the property 'curl_fsock'. |
||
| 256 | * |
||
| 257 | * @return bool |
||
| 258 | * @access public |
||
| 259 | */ |
||
| 260 | public function is_remote_detected(): bool |
||
| 261 | { |
||
| 262 | if ($this->config['ppde_curl_detected']) |
||
| 263 | { |
||
| 264 | $this->curl_fsock = ['curl' => true, 'none' => false]; |
||
| 265 | } |
||
| 266 | |||
| 267 | return array_search(true, $this->curl_fsock); |
||
| 268 | } |
||
| 269 | |||
| 270 | /** |
||
| 271 | * Set the property '$u_paypal' |
||
| 272 | * |
||
| 273 | * @param string $u_paypal |
||
| 274 | * |
||
| 275 | * @return void |
||
| 276 | * @access public |
||
| 277 | */ |
||
| 278 | public function set_u_paypal($u_paypal): void |
||
| 281 | } |
||
| 282 | |||
| 283 | /** |
||
| 284 | * Get the property '$u_paypal' |
||
| 285 | * |
||
| 286 | * @return string |
||
| 287 | * @access public |
||
| 288 | */ |
||
| 289 | public function get_u_paypal(): string |
||
| 290 | { |
||
| 291 | return $this->u_paypal; |
||
| 292 | } |
||
| 293 | |||
| 294 | /** |
||
| 295 | * Get the service that will be used to contact PayPal |
||
| 296 | * Returns the name of the key that is set to true. |
||
| 297 | * |
||
| 298 | * @return string |
||
| 299 | * @access public |
||
| 300 | */ |
||
| 301 | public function get_remote_used(): string |
||
| 302 | { |
||
| 303 | return array_search(true, $this->curl_fsock); |
||
| 304 | } |
||
| 305 | |||
| 306 | /** |
||
| 307 | * Full PayPal response for include in text report |
||
| 308 | * |
||
| 309 | * @return string |
||
| 310 | * @access public |
||
| 311 | */ |
||
| 312 | public function get_report_response(): string |
||
| 313 | { |
||
| 314 | return $this->report_response; |
||
| 315 | } |
||
| 316 | |||
| 317 | /** |
||
| 318 | * PayPal response status |
||
| 319 | * |
||
| 320 | * @return string |
||
| 321 | * @access public |
||
| 322 | */ |
||
| 323 | public function get_response_status(): string |
||
| 324 | { |
||
| 325 | return $this->response_status; |
||
| 326 | } |
||
| 327 | |||
| 328 | /** |
||
| 329 | * Check if the response status is equal to "200". |
||
| 330 | * |
||
| 331 | * @return bool |
||
| 332 | * @access public |
||
| 333 | */ |
||
| 334 | public function check_response_status(): bool |
||
| 335 | { |
||
| 336 | return $this->response_status != 200; |
||
| 337 | } |
||
| 338 | |||
| 339 | /** |
||
| 340 | * If cURL is available we use strcmp() to get the Pay |
||
| 341 | * |
||
| 342 | * @param string $arg |
||
| 343 | * |
||
| 344 | * @return bool |
||
| 345 | * @access public |
||
| 346 | */ |
||
| 347 | public function is_curl_strcmp($arg): bool |
||
| 350 | } |
||
| 351 | |||
| 352 | /** |
||
| 353 | * Check TLS configuration. |
||
| 354 | * |
||
| 355 | * This method checks the TLS configuration using a CURL request to a specified TLS host. |
||
| 356 | * If the TLS version matches one of the allowed versions, it sets the 'ppde_tls_detected' config value to true. |
||
| 357 | * Otherwise, it sets it to false. |
||
| 358 | * |
||
| 359 | * @return void |
||
| 360 | * @access public |
||
| 361 | */ |
||
| 362 | public function check_tls(): void |
||
| 363 | { |
||
| 364 | $ext_meta = $this->ppde_ext_manager->get_ext_meta(); |
||
| 365 | |||
| 366 | // Reset settings to false |
||
| 367 | $this->config->set('ppde_tls_detected', false); |
||
|
|
|||
| 368 | $this->response = ''; |
||
| 369 | |||
| 370 | $this->check_curl($ext_meta['extra']['security-check']['tls']['tls-host']); |
||
| 371 | |||
| 372 | // Analyse response |
||
| 373 | $json = json_decode($this->response); |
||
| 374 | |||
| 375 | if ($json !== null && in_array($json->tls_version, $ext_meta['extra']['security-check']['tls']['tls-version'], true)) |
||
| 376 | { |
||
| 377 | $this->config->set('ppde_tls_detected', true); |
||
| 378 | } |
||
| 379 | } |
||
| 380 | |||
| 381 | /** |
||
| 382 | * Set the remote detected value. |
||
| 383 | * |
||
| 384 | * This method retrieves the extension metadata using the PPDE Extension Manager |
||
| 385 | * and checks if curl is detected on the remote server. It then sets the value |
||
| 386 | * of 'ppde_curl_detected' in the configuration based on the check result. |
||
| 387 | * |
||
| 388 | * @return void |
||
| 389 | * @access public |
||
| 390 | */ |
||
| 391 | public function set_remote_detected(): void |
||
| 392 | { |
||
| 393 | $ext_meta = $this->ppde_ext_manager->get_ext_meta(); |
||
| 394 | $this->config->set('ppde_curl_detected', $this->check_curl($ext_meta['extra']['version-check']['host'])); |
||
| 395 | } |
||
| 396 | |||
| 397 | /** |
||
| 398 | * Check if cURL is available |
||
| 399 | * |
||
| 400 | * @param string $host |
||
| 401 | * |
||
| 402 | * @return bool |
||
| 403 | * @access public |
||
| 404 | */ |
||
| 405 | public function check_curl($host): bool |
||
| 406 | { |
||
| 407 | if ($this->is_curl_available()) |
||
| 408 | { |
||
| 409 | return $this->execute_curl_request($host); |
||
| 410 | } |
||
| 411 | |||
| 412 | return false; |
||
| 413 | } |
||
| 414 | |||
| 415 | /** |
||
| 416 | * Check if cURL is available. |
||
| 417 | * |
||
| 418 | * @return bool Returns true if cURL is available, false otherwise. |
||
| 419 | */ |
||
| 420 | private function is_curl_available(): bool |
||
| 423 | } |
||
| 424 | |||
| 425 | /** |
||
| 426 | * Execute a cURL request. |
||
| 427 | * |
||
| 428 | * @param string $host The host to send the cURL request to. |
||
| 429 | * |
||
| 430 | * @return bool Returns true if the cURL request is successful or false otherwise. |
||
| 431 | */ |
||
| 432 | private function execute_curl_request(string $host): bool |
||
| 444 | } |
||
| 445 | |||
| 446 | /** |
||
| 447 | * Set config value for cURL version |
||
| 448 | * |
||
| 449 | * @return void |
||
| 450 | * @access public |
||
| 451 | */ |
||
| 452 | public function set_curl_info(): void |
||
| 453 | { |
||
| 454 | // Get cURL version informations |
||
| 455 | if ($curl_info = $this->check_curl_info()) |
||
| 456 | { |
||
| 457 | $this->config->set('ppde_curl_version', $curl_info['version']); |
||
| 458 | $this->config->set('ppde_curl_ssl_version', $curl_info['ssl_version']); |
||
| 459 | } |
||
| 460 | } |
||
| 461 | |||
| 462 | /** |
||
| 463 | * Get cURL version if available |
||
| 464 | * |
||
| 465 | * @return array|bool |
||
| 466 | * @access public |
||
| 467 | */ |
||
| 468 | public function check_curl_info() |
||
| 469 | { |
||
| 470 | if (function_exists('curl_version')) |
||
| 471 | { |
||
| 472 | return curl_version(); |
||
| 473 | } |
||
| 474 | |||
| 475 | return false; |
||
| 476 | } |
||
| 477 | |||
| 478 | /** |
||
| 479 | * Get all args and build the return URI |
||
| 480 | * |
||
| 481 | * @return void |
||
| 482 | * @access public |
||
| 483 | */ |
||
| 484 | public function set_args_return_uri(): void |
||
| 485 | { |
||
| 486 | // Add the cmd=_notify-validate for PayPal |
||
| 487 | $this->args_return_uri = 'cmd=_notify-validate'; |
||
| 488 | |||
| 489 | // Grab the post data form and set in an array to be used in the uri to PayPal |
||
| 490 | $postback_args = $this->get_postback_args(); |
||
| 491 | $query_strings = http_build_query($postback_args); |
||
| 492 | |||
| 493 | // Append the uri with the query strings |
||
| 494 | $this->args_return_uri .= '&' . $query_strings; |
||
| 495 | } |
||
| 496 | |||
| 497 | /** |
||
| 498 | * Sets the postback arguments for the current object. |
||
| 499 | * |
||
| 500 | * This is used to Postback args to PayPal or for tracking errors. |
||
| 501 | * Based on official PayPal IPN class. |
||
| 502 | * Ref. https://github.com/paypal/ipn-code-samples/blob/master/php/PaypalIPN.php#L67-L81 |
||
| 503 | * |
||
| 504 | * @return void |
||
| 505 | * @access public |
||
| 506 | */ |
||
| 507 | public function set_postback_args(): void |
||
| 519 | } |
||
| 520 | |||
| 521 | /** |
||
| 522 | * Retrieves the decoded input parameters. |
||
| 523 | * |
||
| 524 | * @return array The input parameters after being decoded. |
||
| 525 | * @access private |
||
| 526 | */ |
||
| 527 | private function get_decoded_input_params(): array |
||
| 528 | { |
||
| 529 | parse_str(file_get_contents('php://input'), $params); |
||
| 530 | return array_map('urldecode', $params); |
||
| 531 | } |
||
| 532 | |||
| 533 | /** |
||
| 534 | * Check if the given key is 'payment_date' and the value contains a '+'. |
||
| 535 | * |
||
| 536 | * @param string $key The key to check. |
||
| 537 | * @param string $value The value to check. |
||
| 538 | * |
||
| 539 | * @return bool Returns true if the key is 'payment_date' and the value contains a '+', otherwise returns false. |
||
| 540 | * @access private |
||
| 541 | */ |
||
| 542 | private function is_payment_date_and_has_plus(string $key, string $value): bool |
||
| 543 | { |
||
| 544 | return $key === 'payment_date' && strpos($value, '+') !== false; |
||
| 545 | } |
||
| 546 | |||
| 547 | /** |
||
| 548 | * Retrieves the postback arguments. |
||
| 549 | * |
||
| 550 | * @return array The postback arguments. |
||
| 551 | * @access public |
||
| 552 | */ |
||
| 553 | public function get_postback_args(): array |
||
| 556 | } |
||
| 557 | } |
||
| 558 |