Completed
Push — master ( 9d3fbd...af269e )
by Michael
09:48
created

admin/gateways/paypal/gateway.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
/**
13
 * oledrion
14
 *
15
 * @copyright   {@link https://xoops.org/ XOOPS Project}
16
 * @license     {@link http://www.fsf.org/copyleft/gpl.html GNU public license}
17
 * @author      Hervé Thouzard (http://www.herve-thouzard.com/)
18
 */
19
20
/**
21
 * Paypal Gateway
22
 */
23
// defined('XOOPS_ROOT_PATH') || exit('XOOPS root path not defined');
24
25
class Oledrion_paypal extends Oledrion_gateway
26
{
27
    /**
28
     * Oledrion_paypal constructor.
29
     */
30
    public function __construct()
31
    {
32
        parent::__construct();
33
    }
34
35
    /**
36
     * Retourne des informations sur la passerelle de paiement
37
     *
38
     * @return array
39
     */
40 View Code Duplication
    public function setGatewayInformation()
41
    {
42
        $gateway                  = array();
43
        $gateway['name']          = 'Paypal';
44
        $gateway['foldername']    = 'paypal';
45
        $gateway['version']       = '1.1';
46
        $gateway['description']   = 'PayPal is the safer, easier way to pay and get paid online';
47
        $gateway['author']        = 'Instant Zero (http://www.herve-thouzard.com/)';
48
        $gateway['credits']       = 'Hervé Thouzard';
49
        $gateway['releaseDate']   = 20081215;
50
        $this->gatewayInformation = $gateway;
51
    }
52
53
    /**
54
     * Retourne le formulaire utilisé pour paramétrer la passerelle de paiement
55
     *
56
     * @param $postUrl
57
     * @return object de type XoopsThemeForm
58
     */
59
    public function getParametersForm($postUrl)
60
    {
61
        require $this->getGatewayLanguageFile();
62
63
        $sform = new XoopsThemeForm(_OLEDRION_PAYPAL_PARAMETERS . ' - ' . $this->gatewayInformation['name'], 'frmPaypal', $postUrl);
64
        // You must specify the gateway folder's name
65
        $sform->addElement(new XoopsFormHidden('gateway', $this->gatewayInformation['foldername']));
66
67
        // Adresse email Paypal du compte marchand
68
        $paypal_email = new XoopsFormText(_OLEDRION_PAYPAL_EMAIL, 'paypal_email', 50, 255, $this->handlers->h_oledrion_gateways_options->getGatewayOptionValue($this->gatewayInformation['foldername'], 'paypal_email'));
69
        $paypal_email->setDescription(_OLEDRION_PAYPAL_EMAILDSC);
70
        $sform->addElement($paypal_email, true);
71
72
        // Libellé de la monnaie pour Paypal
73
        $paypal_money = new XoopsFormSelect(_OLEDRION_PAYPAL_MONEY_P, 'paypal_money', $this->handlers->h_oledrion_gateways_options->getGatewayOptionValue($this->gatewayInformation['foldername'], 'paypal_money'));
74
        $paypal_money->addOptionArray(array(
75
                                          'AUD' => 'Australian Dollar',
76
                                          'CAD' => 'Canadian Dollar',
77
                                          'CHF' => 'Swiss Franc',
78
                                          'CZK' => 'Czech Koruna',
79
                                          'DKK' => 'Danish Krone',
80
                                          'EUR' => 'Euro',
81
                                          'GBP' => 'Pound Sterling',
82
                                          'HKD' => 'Hong Kong Dollar',
83
                                          'HUF' => 'Hungarian Forint',
84
                                          'JPY' => 'Japanese Yen',
85
                                          'NOK' => 'Norwegian Krone',
86
                                          'NZD' => 'New Zealand Dollar',
87
                                          'PLN' => 'Polish Zloty',
88
                                          'SEK' => 'Swedish Krona',
89
                                          'SGD' => 'Singapore Dollar',
90
                                          'USD' => 'U.S. Dollar'
91
                                      ));
92
        $sform->addElement($paypal_money, true);
93
94
        // Paypal en mode test ?
95
        $paypal_test = new XoopsFormRadioYN(_OLEDRION_PAYPAL_TEST, 'paypal_test', $this->handlers->h_oledrion_gateways_options->getGatewayOptionValue($this->gatewayInformation['foldername'], 'paypal_test'));
96
        $sform->addElement($paypal_test, true);
97
98
        // Forcé à vrai ...
99
        $sform->addElement(new XoopsFormHidden('use_ipn', 1));
100
101
        $button_tray = new XoopsFormElementTray('', '');
102
        $submit_btn  = new XoopsFormButton('', 'post', _AM_OLEDRION_GATEWAYS_UPDATE, 'submit');
103
        $button_tray->addElement($submit_btn);
104
        $sform->addElement($button_tray);
105
106
        return $sform;
107
    }
108
109
    /**
110
     * Sauvegarde des paramètres de la passerelle de paiement
111
     *
112
     * @param  array $data Les données du formulaire
113
     * @return boolean Le résultat de l'enregistrement des données
114
     */
115
    public function saveParametersForm($data)
116
    {
117
        $parameters = array('paypal_email', 'paypal_money', 'paypal_test', 'use_ipn');
118
        // On commence par supprimer les valeurs actuelles
119
        $gatewayName = $this->gatewayInformation['foldername'];
120
        $this->handlers->h_oledrion_gateways_options->deleteGatewayOptions($gatewayName);
121
        foreach ($parameters as $parameter) {
122
            if (!$this->handlers->h_oledrion_gateways_options->setGatewayOptionValue($gatewayName, $parameter, $data[$parameter])) {
123
                return false;
124
            }
125
        }
126
127
        return true;
128
    }
129
130
    /**
131
     * Formate le montant au format Paypal
132
     * @param $amount
133
     * @return string
134
     */
135
    private function formatAmount($amount)
136
    {
137
        return number_format($amount, 2, '.', '');
138
    }
139
140
    /**
141
     * Retourne l'url vers laquelle rediriger l'utilisateur pour le paiement en ligne
142
     *
143
     * @param $cmd_total
144
     * @param $cmd_id
145
     * @return string
146
     */
147 View Code Duplication
    public function getRedirectURL($cmd_total, $cmd_id)
148
    {
149
        $test_mode = (int)$this->handlers->h_oledrion_gateways_options->getGatewayOptionValue($this->gatewayInformation['foldername'], 'paypal_test');
150
        if ($test_mode == 1) {
151
            return 'https://www.sandbox.paypal.com/cgi-bin/webscr';
152
        } else {
153
            return 'https://www.paypal.com/cgi-bin/webscr';
154
        }
155
    }
156
157
    /**
158
     * Retourne les éléments à ajouter au formulaire en tant que zones cachées
159
     *
160
     * @param array $order La commande client
161
     * @param       array
162
     * @return array
163
     */
164
    public function getCheckoutFormContent($order)
165
    {
166
        global $xoopsConfig;
167
        $gatewayName  = $this->gatewayInformation['foldername'];
168
        $paypal_money = $this->handlers->h_oledrion_gateways_options->getGatewayOptionValue($gatewayName, 'paypal_money');
169
        $paypal_email = $this->handlers->h_oledrion_gateways_options->getGatewayOptionValue($gatewayName, 'paypal_email');
170
        $use_ipn      = (int)$this->handlers->h_oledrion_gateways_options->getGatewayOptionValue($gatewayName, 'use_ipn');
171
172
        $ret                     = array();
173
        $ret['cmd']              = '_xclick';
174
        $ret['upload']           = '1';
175
        $ret['currency_code']    = $paypal_money;
176
        $ret['business']         = $paypal_email;
177
        $ret['return']           = OLEDRION_URL . 'thankyou.php'; // Page (générique) de remerciement après paiement
178
        $ret['image_url']        = XOOPS_URL . '/images/logo.gif';
179
        $ret['cpp_header_image'] = XOOPS_URL . '/images/logo.gif';
180
        $ret['invoice']          = $order->getVar('cmd_id');
181
        $ret['item_name']        = _OLEDRION_COMMAND . $order->getVar('cmd_id') . ' - ' . OledrionUtility::makeHrefTitle($xoopsConfig['sitename']);
182
        $ret['item_number']      = $order->getVar('cmd_id');
183
        $ret['tax']              = 0; // ajout 25/03/2008
184
        $ret['amount']           = $this->formatAmount((float)$order->getVar('cmd_total', 'n'));
185
        $ret['custom']           = $order->getVar('cmd_id');
186
        //$ret['rm'] = 2;   // Renvoyer les données par POST (normalement)
187
        $ret['email'] = $order->getVar('cmd_email');
188
        if (xoops_trim($order->getVar('cmd_cancel')) != '') { // URL à laquelle le navigateur du client est ramené si le paiement est annulé
189
            $ret['cancel_return'] = OLEDRION_URL . 'cancel-payment.php?id=' . $order->getVar('cmd_cancel');
190
        }
191
        if ($use_ipn == 1) {
192
            $ret['notify_url'] = OLEDRION_URL . 'gateway-notify.php'; // paypal-notify.php
193
        }
194
        return $ret;
195
    }
196
197
    /**
198
     * Retourne la liste des pays à utiliser dans le formulaire de saisie des informations client (checkout.php)
199
     *
200
     * @return array
201
     */
202
    public function getCountriesList()
203
    {
204
        require_once XOOPS_ROOT_PATH . '/class/xoopslists.php';
205
206
        return XoopsLists::getCountryList();
207
    }
208
209
    /**
210
     * Utilisée lors du dialog avec Paypal dans le cas de l'utilisation de l'IPN
211
     * Note : Spécifique Paypal
212
     *
213
     * @return string L'URL chez Paypal à appeler pour obtenir des informations
214
     */
215 View Code Duplication
    private function getdialogURL()
216
    {
217
        $test_mode = (int)$this->handlers->h_oledrion_gateways_options->getGatewayOptionValue($this->gatewayInformation['foldername'], 'paypal_test');
218
        if ($test_mode == 1) {
219
            return 'www.sandbox.paypal.com';
220
        } else {
221
            return 'www.paypal.com';
222
        }
223
    }
224
225
    /**
226
     * Dialogue avec la passerelle de paiement pour indiquer l'état de la commande
227
     * L'appellant se charge de vérifier que le fichier log existe
228
     *
229
     * @param  string $gatewaysLogPath Le chemin d'accès complet au fichier log
230
     * @return void
231
     */
232
233
    public function gatewayNotify($gatewaysLogPath)
234
    {
235
        global $xoopsConfig;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
236
        $executionStartTime = microtime(true);
237
        error_reporting(0);
238
        @$xoopsLogger->activated = false;
239
240
        $log     = '';
241
        $req     = 'cmd=_notify-validate';
242
        $slashes = get_magic_quotes_gpc();
243
        foreach ($_POST as $key => $value) {
244
            if ($slashes) {
245
                $log   .= "$key=" . stripslashes($value) . "\n";
246
                $value = urlencode(stripslashes($value));
247
            } else {
248
                $log   .= "$key=" . $value . "\n";
249
                $value = urlencode($value);
250
            }
251
            $req .= "&$key=$value";
252
        }
253
        $url          = $this->getdialogURL();
254
        $gatewayName  = $this->gatewayInformation['foldername'];
255
        $paypal_email = $this->handlers->h_oledrion_gateways_options->getGatewayOptionValue($gatewayName, 'paypal_email');
256
        $paypal_money = $this->handlers->h_oledrion_gateways_options->getGatewayOptionValue($gatewayName, 'paypal_money');
257
        $header       = '';
258
        $header       .= "POST /cgi-bin/webscr HTTP/1.1\r\n";
259
        $header       .= "Host: $url\r\n";
260
        $header       .= "Content-Type: application/x-www-form-urlencoded\r\n";
261
        $header       .= 'Content-Length: ' . strlen($req) . "\r\n\r\n";
262
        $errno        = 0;
263
        $errstr       = '';
264
        $fp           = fsockopen("ssl://$url", 443, $errno, $errstr, 30);
265
        if ($fp) {
266
            fwrite($fp, "$header$req");
267
            while (!feof($fp)) {
268
                $res = fgets($fp, 1024);
269
                if (strcmp(trim($res), 'VERIFIED') == 0) {
270
                    $log      .= "PAYPAL VERIFIED\n";
271
                    $paypalok = true;
272
                    if (strtoupper($_POST['payment_status']) !== 'COMPLETED') {
273
                        $paypalok = false;
274
                    }
275
                    if (strtoupper($_POST['receiver_email']) != strtoupper($paypal_email)) {
276
                        $paypalok = false;
277
                    }
278
                    if (strtoupper($_POST['mc_currency']) != strtoupper($paypal_money)) {
279
                        $paypalok = false;
280
                    }
281
                    if (!$_POST['custom']) {
282
                        $paypalok = false;
283
                    }
284
                    $montant = $_POST['mc_gross'];
285
                    $pid     = pcntl_fork();
286
                    switch ($pid) {
287
                        case -1:    // pcntl_fork() failed
288
                            die('could not fork');
0 ignored issues
show
Coding Style Compatibility introduced by
The method gatewayNotify() 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...
289
                            break;
0 ignored issues
show
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
290
                        case 0:
291
                            // In the new (child) process
292
                            // At this point, all PayPal session variables collected, done Paypal session
293
                            // Rest of transaction can be processed offline to decouple site load from Paypal transaction time
294
                            // PayPal requires this session to return within 30 seconds, or will retry
295
                            $PayPalEndTime = microtime(true);
296
                            if ($paypalok) {
297
                                $ref      = (int)$_POST['custom']; // Numéro de la commande
298
                                $commande = null;
299
                                $commande = $this->handlers->h_oledrion_commands->get($ref);
300
                                if (is_object($commande)) {
301
                                    if ($montant == $commande->getVar('cmd_total')) { // Commande vérifiée
302
                                        $email_name = sprintf('%s/%d%s', OLEDRION_UPLOAD_PATH, $commande->getVar('cmd_id'), OLEDRION_CONFIRMATION_EMAIL_FILENAME_SUFFIX);
303
                                        if (file_exists($email_name)) {
304
                                            $this->handlers->h_oledrion_commands->validateOrder($commande); // Validation de la commande et mise à jour des stocks
305
                                            $msg = array();
0 ignored issues
show
$msg is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
306
                                            $msg = unserialize(file_get_contents($email_name));
307
                                            // Add Transaction ID variable to email variables for templates
308
                                            $msg['TRANSACTION_ID'] = $_POST['txn_id'];
309
                                            // Send confirmation email to user 
310
                                            $email_address = $commande->getVar('cmd_email');
311
                                            OledrionUtility::sendEmailFromTpl('command_client.tpl', $email_address, sprintf(_OLEDRION_THANKYOU_CMD, $xoopsConfig['sitename']), $msg);
312
                                            // Send mail to admin
313
                                            OledrionUtility::sendEmailFromTpl('command_shop.tpl', OledrionUtility::getEmailsFromGroup(OledrionUtility::getModuleOption('grp_sold')), _OLEDRION_NEW_COMMAND, $msg);
314
                                            unlink($email_name);
315
                                            // TODO: add transaction ID to online user invoice
316
                                            // TODO: update user database
317
                                        } else {
318
                                            $duplicate_ipn = 1;
319
                                        }
320
                                    } else {
321
                                        $this->handlers->h_oledrion_commands->setFraudulentOrder($commande);
322
                                    }
323
                                } else {
324
                                    $log .= "not_object\n";
325
                                }
326
                            } else {
327
                                $log .= "paypal not OK\n";
328
                                if (isset($_POST['custom'])) {
329
                                    $ref      = (int)$_POST['custom'];
330
                                    $commande = null;
331
                                    $commande = $this->handlers->h_oledrion_commands->get($ref);
332
                                    if (is_object($commande)) {
333
                                        switch (strtoupper($_POST['payment_status'])) {
334
                                            case 'PENDING':
335
                                                $this->handlers->h_oledrion_commands->setOrderPending($commande);
336
                                                break;
337
                                            case 'FAILED':
338
                                                $this->handlers->h_oledrion_commands->setOrderFailed($commande);
339
                                                break;
340
                                        }
341
                                    }
342
                                }
343
                            }
344
                            // Ecriture dans le fichier log
345
                            $logfp = fopen($gatewaysLogPath, 'a');
346
                            if ($logfp) {
347
                                if ($duplicate_ipn) {
0 ignored issues
show
The variable $duplicate_ipn does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
348
                                    fwrite($logfp, sprintf("Duplicate paypal IPN, order: %d\n", $commande->getVar('cmd_id')));
0 ignored issues
show
The variable $commande does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
349
                                } else {
350
                                    fwrite($logfp, str_repeat('-', 120) . "\n");
351
                                    fwrite($logfp, date('d/m/Y H:i:s') . "\n");
352
                                    if (isset($_POST['txn_id'])) {
353
                                        fwrite($logfp, 'Transaction : ' . $_POST['txn_id'] . "\n");
354
                                    }
355
                                    fwrite($logfp, 'Result : ' . $log . "\n");
356
                                }
357
                                $executionEndTime = microtime(true);
358
                                $PayPalSeconds    = $PayPalEndTime - $executionStartTime;
359
                                $TotalSeconds     = $executionEndTime - $executionStartTime;
360
                                fwrite($logfp, "Paypal session took $PayPalSeconds, Total transaction took $TotalSeconds seconds.\n");
361
                                fclose($logfp);
362
                            }
363
                            break;
364
                        default:
365
                            // In the main (parent) process in which the script is running
366
                            // At this point, all PayPal session variables collected, done Paypal session
367
                            // Rest of transaction can be proccessed offline to decouple Paypal transaction time from site load
368
                            // PayPal requires this session to return within 30 seconds, or will retry
369
                            return;
370
                            break;
0 ignored issues
show
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...
371
                    }
372
                } else {
373
                    $log .= "$res\n";
374
                }
375
            }
376
            fclose($fp);
377
        } else {
378
            $errtext = "Error with the fsockopen function, unable to open communication ' : ($errno) $errstr\n";
379
            file_put_contents($gatewaysLogPath, $errtext, FILE_APPEND | LOCK_EX);
0 ignored issues
show
FILE_APPEND | LOCK_EX is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
380
        }
381
    }
382
}
383