PaypalGateway   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 427
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 41
eloc 222
dl 0
loc 427
rs 9.1199
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getParametersForm() 0 50 1
A formatAmount() 0 3 1
A setGatewayInformation() 0 11 1
A getDialogURL() 0 10 2
A saveParametersForm() 0 15 3
F gatewayNotify() 0 195 25
A getRedirectURL() 0 10 2
A getCountriesList() 0 5 1
A getCheckoutFormContent() 0 51 4
A __construct() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like PaypalGateway 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 PaypalGateway, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace XoopsModules\Oledrion\Gateways\Paypal;
4
5
/*
6
 You may not change or alter any portion of this comment or credits
7
 of supporting developers from this source code or any supporting source code
8
 which is considered copyrighted (c) material of the original comment or credit authors.
9
10
 This program is distributed in the hope that it will be useful,
11
 but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
*/
14
15
/**
16
 * oledrion
17
 *
18
 * @copyright   {@link https://xoops.org/ XOOPS Project}
19
 * @license     {@link http://www.fsf.org/copyleft/gpl.html GNU public license}
20
 * @author      Hervé Thouzard (http://www.herve-thouzard.com/)
21
 */
22
23
use XoopsModules\Oledrion;
24
use XoopsModules\Oledrion\Gateways\Gateway;
25
26
/**
27
 * Paypal Gateway
28
 */
29
// defined('XOOPS_ROOT_PATH') || die('Restricted access');
30
31
class PaypalGateway extends Gateway
32
{
33
    /**
34
     * Paypal constructor.
35
     */
36
    public function __construct()
37
    {
38
        parent::__construct();
39
    }
40
41
    /**
42
     * Returns information about the payment gateway
43
     */
44
    public function setGatewayInformation()
45
    {
46
        $gateway                  = [];
47
        $gateway['name']          = 'Paypal';
48
        $gateway['foldername']    = 'Paypal';
49
        $gateway['version']       = '1.1';
50
        $gateway['description']   = 'PayPal is the safer, easier way to pay and get paid online';
51
        $gateway['author']        = 'Instant Zero (http://www.herve-thouzard.com/)';
52
        $gateway['credits']       = 'Hervé Thouzard';
53
        $gateway['releaseDate']   = 20081215;
54
        $this->gatewayInformation = $gateway;
55
    }
56
57
    /**
58
     * Returns the form used to set up the payment gateway
59
     *
60
     * @param $postUrl
61
     * @return \XoopsThemeForm
62
     */
63
    public function getParametersForm($postUrl)
64
    {
65
        require_once $this->getGatewayLanguageFile();
66
        $db                     = \XoopsDatabaseFactory::getDatabaseConnection();
67
        $gatewaysOptionsHandler = new Oledrion\GatewaysOptionsHandler($db);
68
69
        $sform = new \XoopsThemeForm(_OLEDRION_PAYPAL_PARAMETERS . ' - ' . $this->gatewayInformation['name'], 'frmPaypal', $postUrl);
70
        // You must specify the gateway folder's name
71
        $sform->addElement(new \XoopsFormHidden('gateway', $this->gatewayInformation['foldername']));
72
73
        // Paypal email address of the merchant account
74
        $paypal_email = new \XoopsFormText(_OLEDRION_PAYPAL_EMAIL, 'paypal_email', 50, 255, $gatewaysOptionsHandler->getGatewayOptionValue($this->gatewayInformation['foldername'], 'paypal_email'));
75
        $paypal_email->setDescription(_OLEDRION_PAYPAL_EMAILDSC);
76
        $sform->addElement($paypal_email, true);
77
78
        // Denomination of currency for Paypal
79
        $paypal_money = new \XoopsFormSelect(_OLEDRION_PAYPAL_MONEY_P, 'paypal_money', $gatewaysOptionsHandler->getGatewayOptionValue($this->gatewayInformation['foldername'], 'paypal_money'));
80
        $paypal_money->addOptionArray([
81
                                          'AUD' => 'Australian Dollar',
82
                                          'CAD' => 'Canadian Dollar',
83
                                          'CHF' => 'Swiss Franc',
84
                                          'CZK' => 'Czech Koruna',
85
                                          'DKK' => 'Danish Krone',
86
                                          'EUR' => 'Euro',
87
                                          'GBP' => 'Pound Sterling',
88
                                          'HKD' => 'Hong Kong Dollar',
89
                                          'HUF' => 'Hungarian Forint',
90
                                          'JPY' => 'Japanese Yen',
91
                                          'NOK' => 'Norwegian Krone',
92
                                          'NZD' => 'New Zealand Dollar',
93
                                          'PLN' => 'Polish Zloty',
94
                                          'SEK' => 'Swedish Krona',
95
                                          'SGD' => 'Singapore Dollar',
96
                                          'USD' => 'U.S. Dollar',
97
                                      ]);
98
        $sform->addElement($paypal_money, true);
99
100
        // Paypal in test mode ?
101
        $paypal_test = new \XoopsFormRadioYN(_OLEDRION_PAYPAL_TEST, 'paypal_test', $gatewaysOptionsHandler->getGatewayOptionValue($this->gatewayInformation['foldername'], 'paypal_test'));
102
        $sform->addElement($paypal_test, true);
103
104
        // Forced to true ...
105
        $sform->addElement(new \XoopsFormHidden('use_ipn', 1));
106
107
        $buttonTray = new \XoopsFormElementTray('', '');
108
        $submit_btn = new \XoopsFormButton('', 'post', _AM_OLEDRION_GATEWAYS_UPDATE, 'submit');
109
        $buttonTray->addElement($submit_btn);
110
        $sform->addElement($buttonTray);
111
112
        return $sform;
113
    }
114
115
    /**
116
     * Backing up payment gateway settings
117
     *
118
     * @param  array $data The data of the form
119
     * @return bool The result of the data recording
120
     */
121
    public function saveParametersForm($data)
122
    {
123
        $db                     = \XoopsDatabaseFactory::getDatabaseConnection();
124
        $gatewaysOptionsHandler = new Oledrion\GatewaysOptionsHandler($db);
125
        $parameters             = ['paypal_email', 'paypal_money', 'paypal_test', 'use_ipn'];
126
        // We start by deleting the current values
127
        $gatewayName = $this->gatewayInformation['foldername'];
128
        $gatewaysOptionsHandler->deleteGatewayOptions($gatewayName);
129
        foreach ($parameters as $parameter) {
130
            if (!$gatewaysOptionsHandler->setGatewayOptionValue($gatewayName, $parameter, $data[$parameter])) {
131
                return false;
132
            }
133
        }
134
135
        return true;
136
    }
137
138
    /**
139
     * Formats the amount in Paypal format
140
     * @param $amount
141
     * @return string
142
     */
143
    private function formatAmount($amount)
144
    {
145
        return number_format($amount, 2, '.', '');
146
    }
147
148
    /**
149
     * Returns the url to which to redirect the user for online payment
150
     *
151
     * @param $cmd_total
152
     * @param $cmd_id
153
     * @return string
154
     */
155
    public function getRedirectURL($cmd_total, $cmd_id)
156
    {
157
        $db                     = \XoopsDatabaseFactory::getDatabaseConnection();
158
        $gatewaysOptionsHandler = new Oledrion\GatewaysOptionsHandler($db);
159
        $test_mode              = (int)$gatewaysOptionsHandler->getGatewayOptionValue($this->gatewayInformation['foldername'], 'paypal_test');
160
        if (1 === $test_mode) {
161
            return 'https://www.sandbox.paypal.com/cgi-bin/webscr';
162
        }
163
164
        return 'https://www.paypal.com/cgi-bin/webscr';
165
    }
166
167
    /**
168
     * Returns the elements to add to the form as hidden areas
169
     *
170
     * @param array $order The sales order
171
     * @param       array
172
     * @return array
173
     */
174
    public function getCheckoutFormContent($order)
175
    {
176
        //        global $xoopsConfig;
177
        $db                     = \XoopsDatabaseFactory::getDatabaseConnection();
178
        $gatewaysOptionsHandler = new Oledrion\GatewaysOptionsHandler($db);
179
        $gatewayName            = $this->gatewayInformation['foldername'];
180
        $paypal_money           = $gatewaysOptionsHandler->getGatewayOptionValue($gatewayName, 'paypal_money');
181
        $paypal_email           = $gatewaysOptionsHandler->getGatewayOptionValue($gatewayName, 'paypal_email');
182
        $use_ipn                = (int)$gatewaysOptionsHandler->getGatewayOptionValue($gatewayName, 'use_ipn');
183
184
        // B.R. Start
185
        // Need array of product_id's for optional DB update
186
        $caddyHandler = new Oledrion\CaddyHandler($db);
187
        $caddy        = $caddyHandler->getCaddyFromCommand($order->getVar('cmd_id'));
188
        $products     = [];
189
        foreach ($caddy as $item) {
190
            $products[] = $item->getVar('caddy_product_id');
191
        }
192
        $product_ids = implode(',', $products);
193
        // B.R. End
194
195
        $ret                     = [];
196
        $ret['cmd']              = '_xclick';
197
        $ret['upload']           = '1';
198
        $ret['currency_code']    = $paypal_money;
199
        $ret['business']         = $paypal_email;
200
        $ret['return']           = OLEDRION_URL . 'thankyou.php'; // (Generic) thank you page after payment
201
        $ret['image_url']        = XOOPS_URL . '/images/logo.gif';
202
        $ret['cpp_header_image'] = XOOPS_URL . '/images/logo.gif';
203
        $ret['invoice']          = $order->getVar('cmd_id');
204
205
        // B.R. $ret['item_name']        = _OLEDRION_COMMAND . $order->getVar('cmd_id') . ' - ' . Oledrion\Utility::makeHrefTitle($xoopsConfig['sitename']);
206
        // B.R. Start
207
        $ret['item_name'] = $product_ids;
208
        // B.R. End
209
210
        $ret['item_number'] = $order->getVar('cmd_id');
211
        $ret['tax']         = 0; // added 25/03/2008
212
        $ret['amount']      = $this->formatAmount((float)$order->getVar('cmd_total', 'n'));
213
        $ret['custom']      = $order->getVar('cmd_id');
214
        //$ret['rm'] = 2;   // Resend data by POST (normally)
215
        $ret['email'] = $order->getVar('cmd_email');
216
        if ('' !== xoops_trim($order->getVar('cmd_cancel'))) {
217
            // URL to which the client's browser is brought back if the payment is canceled
218
            $ret['cancel_return'] = OLEDRION_URL . 'cancel-payment.php?id=' . $order->getVar('cmd_cancel');
219
        }
220
        if (1 === $use_ipn) {
221
            $ret['notify_url'] = OLEDRION_URL . 'gateway-notify.php'; // paypal-notify.php
222
        }
223
224
        return $ret;
225
    }
226
227
    /**
228
     * Returns the list of countries to use in the customer information entry form (checkout.php)
229
     *
230
     * @return array
231
     */
232
    public function getCountriesList()
233
    {
234
        require_once XOOPS_ROOT_PATH . '/class/xoopslists.php';
235
236
        return \XoopsLists::getCountryList();
237
    }
238
239
    /**
240
     * Used during the dialog with Paypal in the case of the use of the IPN
241
     * Note : Specific Paypal
242
     *
243
     * @return string The URL at Paypal to call for information
244
     */
245
    private function getDialogURL()
246
    {
247
        $db                     = \XoopsDatabaseFactory::getDatabaseConnection();
248
        $gatewaysOptionsHandler = new Oledrion\GatewaysOptionsHandler($db);
249
        $test_mode              = (int)$gatewaysOptionsHandler->getGatewayOptionValue($this->gatewayInformation['foldername'], 'paypal_test');
250
        if (1 === $test_mode) {
251
            return 'www.sandbox.paypal.com';
252
        }
253
254
        return 'www.paypal.com';
255
    }
256
257
    /**
258
     * Dialogue with the payment gateway to indicate the status of the order
259
     * The caller is responsible for checking that the log file exists
260
     *
261
     * @param  string $gatewaysLogPath The full path to the log file
262
     */
263
    public function gatewayNotify($gatewaysLogPath)
264
    {
265
        $db                     = \XoopsDatabaseFactory::getDatabaseConnection();
266
        $gatewaysOptionsHandler = new Oledrion\GatewaysOptionsHandler($db);
267
        $commandsHandler        = new Oledrion\CommandsHandler($db);
268
        $executionStartTime     = microtime(true);
269
        error_reporting(0);
270
        @$xoopsLogger->activated = false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $xoopsLogger seems to be never defined.
Loading history...
271
272
        $log     = '';
273
        $req     = 'cmd=_notify-validate';
274
        $slashes = get_magic_quotes_gpc();
275
        foreach ($_POST as $key => $value) {
276
            if ($slashes) {
277
                $log   .= "$key=" . stripslashes($value) . "\n";
278
                $value = urlencode(stripslashes($value));
279
            } else {
280
                $log   .= "$key=" . $value . "\n";
281
                $value = urlencode($value);
282
            }
283
            $req .= "&$key=$value";
284
        }
285
        $url          = $this->getDialogURL();
286
        $gatewayName  = $this->gatewayInformation['foldername'];
287
        $paypal_email = $gatewaysOptionsHandler->getGatewayOptionValue($gatewayName, 'paypal_email');
288
        $paypal_money = $gatewaysOptionsHandler->getGatewayOptionValue($gatewayName, 'paypal_money');
289
        $header       = '';
290
        $header       .= "POST /cgi-bin/webscr HTTP/1.1\r\n";
291
        $header       .= "Host: $url\r\n";
292
        $header       .= "Content-Type: application/x-www-form-urlencoded\r\n";
293
        $header       .= 'Content-Length: ' . mb_strlen($req) . "\r\n\r\n";
294
        $errno        = 0;
295
        $errstr       = '';
296
        $fp           = fsockopen("ssl://$url", 443, $errno, $errstr, 30);
297
        if ($fp) {
0 ignored issues
show
introduced by
$fp is of type resource, thus it always evaluated to false.
Loading history...
298
            fwrite($fp, "$header$req");
299
            while (!feof($fp)) {
300
                $res = fgets($fp, 1024);
301
                if (0 === strcmp(trim($res), 'VERIFIED')) {
302
                    $log      .= "PAYPAL VERIFIED\n";
303
                    $paypalok = true;
304
                    if ('COMPLETED' !== mb_strtoupper($_POST['payment_status'])) {
305
                        $paypalok = false;
306
                    }
307
                    if (mb_strtoupper($_POST['receiver_email']) != mb_strtoupper($paypal_email)) {
308
                        $paypalok = false;
309
                    }
310
                    if (mb_strtoupper($_POST['mc_currency']) != mb_strtoupper($paypal_money)) {
311
                        $paypalok = false;
312
                    }
313
                    if (!$_POST['custom']) {
314
                        $paypalok = false;
315
                    }
316
                    $montant = $_POST['mc_gross'];
317
318
                    //R.B. start
319
                    $ref      = (int)$_POST['custom']; // Order number
320
                    $commande = null;
321
                    $commande = $commandsHandler->get($ref);
322
323
                    if (!is_object($commande)) {
324
                        // TODO: Why is this failing?
325
                        // TODO: Is there a more appropriate response code?
326
                        //header("HTTP/1.1 500 Internal Server Error");
327
                        http_response_code(500);
328
                        $log .= sprintf("not_object: %d\n", $ref);
329
                        file_put_contents($gatewaysLogPath, $log, FILE_APPEND | LOCK_EX);
330
331
                        return;
332
                    }
333
                    //R.B. end
334
                    $pid = pcntl_fork();
335
                    switch ($pid) {
336
                        case -1:
337
                            die('could not fork');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
338
                            break;
339
                        case 0:
340
                            // In the new (child) process
341
342
                            // At this point, all PayPal session variables collected, done Paypal session
343
                            // Rest of transaction can be processed offline to decouple site load from Paypal transaction time
344
                            // PayPal requires this session to return within 30 seconds, or will retry
345
                            $PayPalEndTime = microtime(true);
346
                            if ($paypalok) {
347
                                /* R.B. start
348
                                                                $ref      = \Xmf\Request::getInt('custom', 0, 'POST'); // Numéro de la commande
349
                                                                $commande = null;
350
                                                                $commande = $commandsHandler->get($ref);
351
                                                                if (is_object($commande)) {
352
                                                                 */ //R.B. end
353
354
                                if ($montant == $commande->getVar('cmd_total')) {
355
                                    // Verified order
356
                                    $email_name = sprintf('%s/%d%s', OLEDRION_UPLOAD_PATH, $commande->getVar('cmd_id'), OLEDRION_CONFIRMATION_EMAIL_FILENAME_SUFFIX);
357
                                    if (file_exists($email_name)) {
358
                                        $commandsHandler->validateOrder($commande); // Validation of the order and inventory update
359
                                        $msg = [];
360
                                        $msg = unserialize(file_get_contents($email_name));
361
                                        // Add Transaction ID variable to email variables for templates
362
                                        $msg['TRANSACTION_ID'] = $_POST['txn_id'];
363
                                        // Send confirmation email to user
364
                                        $email_address = $commande->getVar('cmd_email');
365
                                        Oledrion\Utility::sendEmailFromTpl('command_client.tpl', $email_address, sprintf(_OLEDRION_THANKYOU_CMD, $xoopsConfig['sitename']), $msg);
366
                                        // Send mail to admin
367
                                        Oledrion\Utility::sendEmailFromTpl('command_shop.tpl', Oledrion\Utility::getEmailsFromGroup(Oledrion\Utility::getModuleOption('grp_sold')), _OLEDRION_NEW_COMMAND, $msg);
368
369
                                        //R.B. start
370
                                        // TODO: add transaction ID to SMS and online user invoice
371
                                        // Update user database
372
                                        if (file_exists(OLEDRION_DB_UPDATE_SCRIPT)) {
373
                                            include OLEDRION_DB_UPDATE_SCRIPT;
374
                                            $product_ids = $_POST['item_name'];
375
                                            $products    = [];
376
                                            $products    = explode(',', $product_ids);
377
                                            foreach ($products as $item) {
378
                                                $product_id = $item;
379
                                                // updateDB($product_id, $user_id, $transaction_id);
380
                                                $log .= updateDB($product_id, $_POST['receiver_email'], $_POST['txn_id']);
0 ignored issues
show
Bug introduced by
The function updateDB was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

380
                                                $log .= /** @scrutinizer ignore-call */ updateDB($product_id, $_POST['receiver_email'], $_POST['txn_id']);
Loading history...
381
                                            }
382
                                        }
383
                                        //R.B. end
384
385
                                        if (false === @unlink($email_name)) {
386
                                            throw new \RuntimeException('The file ' . $email_name . ' could not be deleted.');
387
                                        }
388
                                    } else {
389
                                        $duplicate_ipn = 1;
390
                                    }
391
                                } else {
392
                                    $commandsHandler->setFraudulentOrder($commande);
393
                                }
394
                            } else {
395
                                //R.B. start
396
                                // $log .= "not_object\n";
397
                                //  }
398
                                // } else {
399
                                //R.B. end
400
                                $log .= "paypal not OK\n";
401
                                if (\Xmf\Request::hasVar('custom', 'POST')) {
402
                                    // R.B. start
403
                                    // $ref      = \Xmf\Request::getInt('custom', 0, 'POST');
404
                                    // $commande = null;
405
                                    // $commande = $commandsHandler->get($ref);
406
                                    // if (is_object($commande)) {
407
                                    //R.B. end
408
                                    switch (mb_strtoupper($_POST['payment_status'])) {
409
                                        case 'PENDING':
410
                                            $commandsHandler->setOrderPending($commande);
411
                                            break;
412
                                        case 'FAILED':
413
                                            $commandsHandler->setOrderFailed($commande);
414
                                            break;
415
                                        // R.B. }
416
                                    }
417
                                }
418
                            }
419
                            // Write to the log file
420
                            $logfp = fopen($gatewaysLogPath, 'ab');
421
                            if ($logfp) {
422
                                if ($duplicate_ipn) {
423
                                    fwrite($logfp, sprintf("Duplicate paypal IPN, order: %d\n", $commande->getVar('cmd_id')));
424
                                } else {
425
                                    fwrite($logfp, str_repeat('-', 120) . "\n");
426
                                    fwrite($logfp, date('d/m/Y H:i:s') . "\n");
427
                                    if (\Xmf\Request::hasVar('txn_id', 'POST')) {
428
                                        fwrite($logfp, 'Transaction : ' . $_POST['txn_id'] . "\n");
429
                                    }
430
                                    fwrite($logfp, 'Result : ' . $log . "\n");
431
                                }
432
                                $executionEndTime = microtime(true);
433
                                $PayPalSeconds    = $PayPalEndTime - $executionStartTime;
434
                                $TotalSeconds     = $executionEndTime - $executionStartTime;
435
                                fwrite($logfp, "Paypal session took $PayPalSeconds, Total transaction took $TotalSeconds seconds.\n");
436
                                fclose($logfp);
437
                            }
438
439
                            break;
440
                        default:
441
                            // In the main (parent) process in which the script is running
442
443
                            // At this point, all PayPal session variables collected, done Paypal session
444
                            // Rest of transaction can be proccessed offline to decouple Paypal transaction time from site load
445
                            // PayPal requires this session to return within 30 seconds, or will retry
446
447
                            return;
448
                            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
449
                    }
450
                } else {
451
                    $log .= "$res\n";
452
                }
453
            }
454
            fclose($fp);
455
        } else {
456
            $errtext = "Error with the fsockopen function, unable to open communication ' : ($errno) $errstr\n";
457
            file_put_contents($gatewaysLogPath, $errtext, FILE_APPEND | LOCK_EX);
458
        }
459
    }
460
}
461