Passed
Pull Request — master (#76)
by Raúl
03:03
created

prepareVariables()   B

Complexity

Conditions 8
Paths 16

Size

Total Lines 36
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 8
eloc 25
c 2
b 1
f 0
nc 16
nop 0
dl 0
loc 36
rs 8.4444
1
<?php
2
/**
3
 * This file is part of the official Pagantis module for PrestaShop.
4
 *
5
 * @author    Pagantis <[email protected]>
6
 * @copyright 2019 Pagantis
7
 * @license   proprietary
8
 */
9
10
require_once('AbstractController.php');
11
12
use Pagantis\OrdersApiClient\Client as PagantisClient;
13
use Pagantis\OrdersApiClient\Model\Order as PagantisModelOrder;
14
use Pagantis\ModuleUtils\Exception\ConcurrencyException;
15
use Pagantis\ModuleUtils\Exception\MerchantOrderNotFoundException;
16
use Pagantis\ModuleUtils\Exception\NoIdentificationException;
17
use Pagantis\ModuleUtils\Exception\OrderNotFoundException;
18
use Pagantis\ModuleUtils\Exception\QuoteNotFoundException;
19
use Pagantis\ModuleUtils\Exception\ConfigurationNotFoundException;
20
use Pagantis\ModuleUtils\Exception\UnknownException;
21
use Pagantis\ModuleUtils\Exception\WrongStatusException;
22
use Pagantis\ModuleUtils\Model\Response\JsonSuccessResponse;
23
use Pagantis\ModuleUtils\Model\Response\JsonExceptionResponse;
24
25
/**
26
 * Class PagantisNotifyModuleFrontController
27
 */
28
class PagantisNotifyModuleFrontController extends AbstractController
29
{
30
    /**
31
     * Seconds to expire a locked request
32
     */
33
    const CONCURRENCY_TIMEOUT = 10;
34
35
    /**
36
     * Seconds to expire a locked request
37
     */
38
    const MAX_TRIES = 5;
39
40
    /**
41
     * @var int $merchantOrderId
42
     */
43
    protected $merchantOrderId;
44
45
    /**
46
     * @var \Cart $merchantOrder
0 ignored issues
show
Bug introduced by
The type Cart was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
47
     */
48
    protected $merchantOrder;
49
50
    /**
51
     * @var string $pagantisOrderId
52
     */
53
    protected $pagantisOrderId;
54
55
    /**
56
     * @var string $amountMismatchError
57
     */
58
    protected $amountMismatchError = '';
59
60
    /**
61
     * @var \Pagantis\OrdersApiClient\Model\Order $pagantisOrder
62
     */
63
    protected $pagantisOrder;
64
65
    /**
66
     * @var Pagantis\OrdersApiClient\Client $orderClient
67
     */
68
    protected $orderClient;
69
70
    /**
71
     * @var mixed $config
72
     */
73
    protected $config;
74
75
    /**
76
     * @var Object $jsonResponse
77
     */
78
    protected $jsonResponse;
79
80
    /**
81
     * @throws Exception
82
     */
83
    public function postProcess()
84
    {
85
        try {
86
            if ($_SERVER['REQUEST_METHOD'] == 'POST') {
87
                // prevent colision between POST and GET requests
88
                sleep(self::CONCURRENCY_TIMEOUT + 2);
89
            }
90
            $this->prepareVariables();
91
            if (Tools::getValue('origin') == 'notification' && $_SERVER['REQUEST_METHOD'] == 'GET') {
0 ignored issues
show
Bug introduced by
The type Tools was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
92
                return $this->cancelProcess();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->cancelProcess() targeting PagantisNotifyModuleFron...roller::cancelProcess() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
93
            }
94
            if ($this->preventDOS()) {
95
                return $this->cancelProcess();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->cancelProcess() targeting PagantisNotifyModuleFron...roller::cancelProcess() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
96
            }
97
            $this->checkConcurrency();
98
            $this->getMerchantOrder();
99
            $this->getPagantisOrderId();
100
            $this->getPagantisOrder();
101
            if ($this->checkOrderStatus()) {
102
                return $this->finishProcess(false);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->finishProcess(false) targeting PagantisNotifyModuleFron...roller::finishProcess() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
103
            }
104
            $this->validateAmount();
105
            if ($this->checkMerchantOrderStatus()) {
106
                $this->processMerchantOrder();
107
            }
108
        } catch (\Exception $exception) {
109
            if ($_SERVER['REQUEST_METHOD'] == 'POST') {
110
                $this->jsonResponse = new JsonExceptionResponse();
111
                $this->jsonResponse->setMerchantOrderId($this->merchantOrderId);
112
                $this->jsonResponse->setPagantisOrderId($this->pagantisOrderId);
113
                $this->jsonResponse->setException($exception);
114
            }
115
            return $this->cancelProcess($exception);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->cancelProcess($exception) targeting PagantisNotifyModuleFron...roller::cancelProcess() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
116
        }
117
118
        try {
119
            $this->jsonResponse = new JsonSuccessResponse();
120
            $this->jsonResponse->setMerchantOrderId($this->merchantOrderId);
121
            $this->jsonResponse->setPagantisOrderId($this->pagantisOrderId);
122
            $this->confirmPagantisOrder();
123
        } catch (\Exception $exception) {
124
            $this->rollbackMerchantOrder();
125
            if ($_SERVER['REQUEST_METHOD'] == 'POST') {
126
                $this->jsonResponse = new JsonExceptionResponse();
127
                $this->jsonResponse->setMerchantOrderId($this->merchantOrderId);
128
                $this->jsonResponse->setPagantisOrderId($this->pagantisOrderId);
129
                $this->jsonResponse->setException($exception);
130
            }
131
            return $this->cancelProcess($exception);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->cancelProcess($exception) targeting PagantisNotifyModuleFron...roller::cancelProcess() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
132
        }
133
134
        try {
135
            $this->unblockConcurrency($this->merchantOrderId);
136
        } catch (\Exception $exception) {
137
            // Do nothing
138
        }
139
140
        return $this->finishProcess(false);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->finishProcess(false) targeting PagantisNotifyModuleFron...roller::finishProcess() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
141
    }
142
143
    /**
144
     * Check the concurrency of the purchase
145
     *
146
     * @throws Exception
147
     */
148
    public function checkConcurrency()
149
    {
150
        $this->unblockConcurrency();
151
        $this->blockConcurrency($this->merchantOrderId);
152
    }
153
154
    /**
155
     * Find and init variables needed to process payment
156
     *
157
     * @throws Exception
158
     */
159
    public function prepareVariables()
160
    {
161
        $callbackOkUrl = $this->context->link->getPageLink(
162
            'order-confirmation',
163
            null,
164
            null
165
        );
166
        $callbackKoUrl = $this->context->link->getPageLink(
167
            'order',
168
            null,
169
            null,
170
            array('step'=>3)
171
        );
172
        try {
173
            $this->config = array(
174
                'urlOK' => (Pagantis::getExtraConfig('PAGANTIS_URL_OK') !== '') ?
175
                    Pagantis::getExtraConfig('PAGANTIS_URL_OK') : $callbackOkUrl,
176
                'urlKO' => (Pagantis::getExtraConfig('PAGANTIS_URL_KO') !== '') ?
177
                    Pagantis::getExtraConfig('PAGANTIS_URL_KO') : $callbackKoUrl,
178
                'publicKey' => Configuration::get('pagantis_public_key'),
0 ignored issues
show
Bug introduced by
The type Configuration was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
179
                'privateKey' => Configuration::get('pagantis_private_key'),
180
                'secureKey' => Tools::getValue('key'),
181
            );
182
        } catch (\Exception $exception) {
183
            throw new ConfigurationNotFoundException();
184
        }
185
186
        $this->merchantOrderId = Tools::getValue('id_cart');
187
        if ($this->merchantOrderId == '') {
188
            throw new QuoteNotFoundException();
189
        }
190
191
192
        if (!($this->config['secureKey'] && $this->merchantOrderId && Module::isEnabled(self::PAGANTIS_CODE))) {
0 ignored issues
show
Bug introduced by
The type Module was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
193
            // This exception is only for Prestashop
194
            throw new UnknownException('Module may not be enabled');
195
        }
196
    }
197
198
    /**
199
     * Retrieve the merchant order by id
200
     *
201
     * @throws Exception
202
     */
203
    public function getMerchantOrder()
204
    {
205
        try {
206
            $this->merchantOrder = new Cart($this->merchantOrderId);
207
            if (!Validate::isLoadedObject($this->merchantOrder)) {
0 ignored issues
show
Bug introduced by
The type Validate was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
208
                // This exception is only for Prestashop
209
                throw new UnknownException('Unable to load cart');
210
            }
211
            if ($this->merchantOrder->secure_key != $this->config['secureKey']) {
212
                throw new UnknownException('Secure Key is not valid');
213
            }
214
        } catch (\Exception $exception) {
215
            throw new MerchantOrderNotFoundException();
216
        }
217
    }
218
219
    /**
220
     * Find PAGANTIS Order Id in AbstractController::PAGANTIS_ORDERS_TABLE
221
     *
222
     * @throws Exception
223
     */
224
    private function getPagantisOrderId()
225
    {
226
        try {
227
            $this->pagantisOrderId= Db::getInstance()->getValue(
0 ignored issues
show
Bug introduced by
The type Db was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
228
                'select order_id from '._DB_PREFIX_.'pagantis_order where id = '.(int)$this->merchantOrderId
0 ignored issues
show
Bug introduced by
The constant _DB_PREFIX_ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
229
            );
230
231
            if (is_null($this->pagantisOrderId)) {
232
                throw new NoIdentificationException();
233
            }
234
        } catch (\Exception $exception) {
235
            throw new NoIdentificationException();
236
        }
237
    }
238
239
    /**
240
     * Find PAGANTIS Order in Orders Server using Pagantis\OrdersApiClient
241
     *
242
     * @throws Exception
243
     */
244
    private function getPagantisOrder()
245
    {
246
        $this->orderClient = new PagantisClient($this->config['publicKey'], $this->config['privateKey']);
247
        $this->pagantisOrder = $this->orderClient->getOrder($this->pagantisOrderId);
248
        if (!($this->pagantisOrder instanceof PagantisModelOrder)) {
0 ignored issues
show
introduced by
$this->pagantisOrder is always a sub-type of Pagantis\OrdersApiClient\Model\Order.
Loading history...
249
            throw new OrderNotFoundException();
250
        }
251
    }
252
253
    /**
254
     * Compare statuses of merchant order and PAGANTIS order, witch have to be the same.
255
     *
256
     * @throws Exception
257
     */
258
    public function checkOrderStatus()
259
    {
260
        if ($this->pagantisOrder->getStatus() === PagantisModelOrder::STATUS_CONFIRMED) {
261
            $this->jsonResponse = new JsonSuccessResponse();
262
            $this->jsonResponse->setMerchantOrderId($this->merchantOrderId);
263
            $this->jsonResponse->setPagantisOrderId($this->pagantisOrderId);
264
            return true;
265
        }
266
267
        if ($this->pagantisOrder->getStatus() !== PagantisModelOrder::STATUS_AUTHORIZED) {
268
            $status = '-';
269
            if ($this->pagantisOrder instanceof \Pagantis\OrdersApiClient\Model\Order) {
0 ignored issues
show
introduced by
$this->pagantisOrder is always a sub-type of Pagantis\OrdersApiClient\Model\Order.
Loading history...
270
                $status = $this->pagantisOrder->getStatus();
271
            }
272
            throw new WrongStatusException($status);
273
        }
274
        return false;
275
    }
276
277
    /**
278
     * Check that the merchant order and the order in PAGANTIS have the same amount to prevent hacking
279
     *
280
     * @throws Exception
281
     */
282
    public function validateAmount()
283
    {
284
        $totalAmount = (string) $this->pagantisOrder->getShoppingCart()->getTotalAmount();
285
        $merchantAmount = (string) (100 * $this->merchantOrder->getOrderTotal(true));
286
        $merchantAmount = explode('.', explode(',', $merchantAmount)[0])[0];
287
        if ($totalAmount != $merchantAmount) {
288
            try {
289
                $psTotalAmount = substr_replace($merchantAmount, '.', (Tools::strlen($merchantAmount) -2), 0);
290
291
                $pgTotalAmountInCents = (string) $this->pagantisOrder->getShoppingCart()->getTotalAmount();
292
                $pgTotalAmount = substr_replace(
293
                    $pgTotalAmountInCents,
294
                    '.',
295
                    (Tools::strlen($pgTotalAmountInCents) -2),
296
                    0
297
                );
298
299
                $this->amountMismatchError = '. Amount mismatch in PrestaShop Order #'. $this->merchantOrderId .
300
                    ' compared with Pagantis Order: ' . $this->pagantisOrderId .
301
                    '. The order in PrestaShop has an amount of ' . $psTotalAmount . ' and in Pagantis ' .
302
                    $pgTotalAmount . ' PLEASE REVIEW THE ORDER';
303
                $this->saveLog(array(
304
                    'message' => $this->amountMismatchError
305
                ));
306
            } catch (\Exception $exception) {
307
                // Do nothing
308
            }
309
        }
310
    }
311
312
    /**
313
     * Check that the merchant order was not previously processes and is ready to be paid
314
     *
315
     * @throws Exception
316
     */
317
    public function checkMerchantOrderStatus()
318
    {
319
        try {
320
            if ($this->merchantOrder->orderExists() !== false) {
321
                throw new WrongStatusException('PS->orderExists() cart_id = '
322
                    . $this->merchantOrderId . ' pagantis_id = '
323
                    . $this->pagantisOrderId . '): already_processed');
324
            }
325
326
            // Double check
327
            $tableName = _DB_PREFIX_ . 'pagantis_order';
0 ignored issues
show
Bug introduced by
The constant _DB_PREFIX_ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
328
            $fieldName = 'ps_order_id';
329
            $sql = ('select ' . $fieldName . ' from `' . $tableName . '` where `id` = ' . (int)$this->merchantOrderId
330
                . ' and `order_id` = \'' . $this->pagantisOrderId . '\''
331
                . ' and `' . $fieldName . '` is not null');
332
            $results = Db::getInstance()->ExecuteS($sql);
333
            if (is_array($results) && count($results) === 1) {
334
                throw new WrongStatusException('PS->record found in ' . $tableName
335
                    . ' (cart_id = ' . $this->merchantOrderId . ' pagantis_id = '
336
                    . $this->pagantisOrderId . '): already_processed');
337
            }
338
        } catch (\Exception $exception) {
339
            throw new UnknownException($exception->getMessage());
340
        }
341
        return true;
342
    }
343
344
    /**
345
     * Process the merchant order and notify client
346
     *
347
     * @throws Exception
348
     */
349
    public function processMerchantOrder()
350
    {
351
        try {
352
            $metadataOrder = $this->pagantisOrder->getMetadata();
353
            $metadataInfo = '';
354
            foreach ($metadataOrder as $metadataKey => $metadataValue) {
355
                if ($metadataKey == 'promotedProduct') {
356
                    $metadataInfo .= $metadataValue;
357
                }
358
            }
359
360
            $this->module->validateOrder(
361
                $this->merchantOrderId,
362
                Configuration::get('PS_OS_PAYMENT'),
363
                $this->merchantOrder->getOrderTotal(true),
364
                $this->module->displayName,
365
                'pagantisOrderId: ' . $this->pagantisOrder->getId() . ' ' .
366
                'pagantisOrderStatus: '. $this->pagantisOrder->getStatus() .
367
                $this->amountMismatchError .
368
                $metadataInfo,
369
                array('transaction_id' => $this->pagantisOrderId),
370
                null,
371
                false,
372
                $this->config['secureKey']
373
            );
374
        } catch (\Exception $exception) {
375
            throw new UnknownException($exception->getMessage());
376
        }
377
        try {
378
            Db::getInstance()->update(
379
                'pagantis_order',
380
                array('ps_order_id' => $this->module->currentOrder),
381
                'id = '. (int)$this->merchantOrderId . ' and order_id = \'' . $this->pagantisOrderId . '\''
382
            );
383
        } catch (\Exception $exception) {
384
            // Do nothing
385
        }
386
    }
387
388
    /**
389
     * Confirm the order in PAGANTIS
390
     *
391
     * @throws Exception
392
     */
393
    private function confirmPagantisOrder()
394
    {
395
        try {
396
            $this->orderClient->confirmOrder($this->pagantisOrderId);
397
            try {
398
                $mode = ($_SERVER['REQUEST_METHOD'] == 'POST') ? 'NOTIFICATION' : 'REDIRECTION';
399
                $message = 'Order CONFIRMED. The order was confirmed by a ' . $mode .
400
                    '. Pagantis OrderId=' . $this->pagantisOrderId .
401
                    '. Prestashop OrderId=' . $this->module->currentOrder;
402
                $this->saveLog(array('message' => $message));
403
            } catch (\Exception $exception) {
404
                // Do nothing
405
            }
406
        } catch (\Exception $exception) {
407
            throw new UnknownException($exception->getMessage());
408
        }
409
    }
410
411
    /**
412
     * Leave the merchant order as it was previously
413
     *
414
     * @throws Exception
415
     */
416
    public function rollbackMerchantOrder()
417
    {
418
        try {
419
            $message = 'Roolback method: ' .
420
                '. Pagantis OrderId=' . $this->pagantisOrderId .
421
                '. Prestashop CartId=' . $this->merchantOrderId;
422
            if ($this->module->currentOrder) {
423
                $objOrder = new Order($this->module->currentOrder);
0 ignored issues
show
Bug introduced by
The type Order was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
424
                $history = new OrderHistory();
0 ignored issues
show
Bug introduced by
The type OrderHistory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
425
                $history->id_order = (int)$objOrder->id;
426
                $history->changeIdOrderState(8, (int)($objOrder->id));
427
                $message .= ' Prestashop OrderId=' . $this->merchantOrderId;
428
            }
429
            $this->saveLog(array('message' => $message));
430
        } catch (\Exception $exception) {
431
            $this->saveLog(array('message' => $exception->getMessage()));
432
        }
433
    }
434
435
    /**
436
     * @return bool
437
     * @throws Exception
438
     */
439
    protected function preventDOS()
440
    {
441
        if ($_SERVER['REQUEST_METHOD'] == 'GET') {
442
            $this->getPagantisOrderId();
443
            $tableName = _DB_PREFIX_ . 'pagantis_order';
0 ignored issues
show
Bug introduced by
The constant _DB_PREFIX_ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
444
            $tries = Db::getInstance()->getValue(
445
                'SELECT tries FROM ' . $tableName . ' WHERE id = ' .
446
                (int)$this->merchantOrderId . ' and order_id = \'' . $this->pagantisOrderId . '\''
447
            );
448
            if ($tries < self::MAX_TRIES) {
449
                try {
450
                    Db::getInstance()->update(
451
                        'pagantis_order',
452
                        array('tries' => $tries + 1),
453
                        'id = ' . (int)$this->merchantOrderId . ' and order_id = \'' . $this->pagantisOrderId . '\''
454
                    );
455
                } catch (\Exception $exception) {
456
                    // Do nothing
457
                }
458
                return false;
459
            }
460
            return true;
461
        }
462
        return false;
463
    }
464
465
    /**
466
     * Lock the concurrency to prevent duplicated inputs
467
     *
468
     * @param $orderId
469
     * @return bool|void
470
     * @throws ConcurrencyException
471
     */
472
    protected function blockConcurrency($orderId)
473
    {
474
        try {
475
            $table = 'pagantis_cart_process';
476
            if (Db::getInstance()->insert($table, array('id' => (int)$orderId, 'timestamp' => (time()))) === false) {
477
                if ($_SERVER['REQUEST_METHOD'] == 'POST') {
478
                    throw new ConcurrencyException();
479
                }
480
481
                $query = sprintf(
482
                    "SELECT TIMESTAMPDIFF(SECOND,NOW()-INTERVAL %s SECOND, FROM_UNIXTIME(timestamp)) as rest 
483
                            FROM %s WHERE %s",
484
                    self::CONCURRENCY_TIMEOUT,
485
                    _DB_PREFIX_.$table,
0 ignored issues
show
Bug introduced by
The constant _DB_PREFIX_ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
486
                    'id='.(int)$orderId
487
                );
488
                $resultSeconds = Db::getInstance()->getValue($query);
489
                $restSeconds = isset($resultSeconds) ? ($resultSeconds) : 0;
490
                $secondsToExpire = ($restSeconds>self::CONCURRENCY_TIMEOUT) ? self::CONCURRENCY_TIMEOUT : $restSeconds;
491
492
                $logMessage = sprintf(
493
                    "Redirect concurrency, User have to wait %s seconds, default seconds %s. CartId=" . $orderId,
494
                    $secondsToExpire,
495
                    self::CONCURRENCY_TIMEOUT,
496
                    $restSeconds
497
                );
498
499
                $this->saveLog(array(
500
                    'message' => $logMessage
501
                ));
502
                sleep($secondsToExpire+1);
503
                // After waiting...user continue the confirmation, hoping that previous call have finished.
504
                return true;
505
            }
506
        } catch (\Exception $exception) {
507
            throw new ConcurrencyException();
508
        }
509
    }
510
511
    /**
512
     * @param null $orderId
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $orderId is correct as it would always require null to be passed?
Loading history...
513
     *
514
     * @throws ConcurrencyException
515
     */
516
    private function unblockConcurrency($orderId = null)
517
    {
518
        try {
519
            if (is_null($orderId)) {
0 ignored issues
show
introduced by
The condition is_null($orderId) is always true.
Loading history...
520
                Db::getInstance()->delete(
521
                    'pagantis_cart_process',
522
                    'timestamp < ' . (time() - self::CONCURRENCY_TIMEOUT)
523
                );
524
                return;
525
            }
526
            Db::getInstance()->delete('pagantis_cart_process', 'id = ' . (int)$orderId);
527
        } catch (\Exception $exception) {
528
            throw new ConcurrencyException();
529
        }
530
    }
531
532
    /**
533
     * Do all the necessary actions to cancel the confirmation process in case of error
534
     * 1. Unblock concurrency
535
     * 2. Save log
536
     *
537
     * @param null $exception
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $exception is correct as it would always require null to be passed?
Loading history...
538
     * @return mixed
539
     */
540
    public function cancelProcess($exception = null)
541
    {
542
        $debug = debug_backtrace();
543
        $method = $debug[1]['function'];
544
        $line = $debug[1]['line'];
545
        $data = array(
546
            'merchantOrderId' => $this->merchantOrderId,
547
            'pagantisOrderId' => $this->pagantisOrderId,
548
            'message' => ($exception)? $exception->getMessage() : 'Unable to get Exception message',
0 ignored issues
show
introduced by
$exception is of type null, thus it always evaluated to false.
Loading history...
549
            'statusCode' => ($exception)? $exception->getCode() : 'Unable to get Exception statusCode',
0 ignored issues
show
introduced by
$exception is of type null, thus it always evaluated to false.
Loading history...
550
            'method' => $method,
551
            'file' => __FILE__,
552
            'line' => $line,
553
        );
554
        $this->saveLog($data);
555
        return $this->finishProcess(true);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->finishProcess(true) targeting PagantisNotifyModuleFron...roller::finishProcess() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
556
    }
557
558
    /**
559
     * Redirect the request to the e-commerce or show the output in json
560
     *
561
     * @param bool $error
562
     * @return mixed
563
     */
564
    public function finishProcess($error = true)
565
    {
566
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
567
            if (!isset($this->jsonResponse)) {
568
                if ($error) {
569
                    $this->jsonResponse = new JsonSuccessResponse();
570
                } else {
571
                    $this->jsonResponse = new JsonExceptionResponse();
572
                }
573
                $this->jsonResponse->setMerchantOrderId($this->merchantOrderId);
574
                $this->jsonResponse->setPagantisOrderId($this->pagantisOrderId);
575
            }
576
            $this->jsonResponse->printResponse();
577
        }
578
579
        $parameters = array(
580
            'id_cart' => $this->merchantOrderId,
581
            'key' => $this->config['secureKey'],
582
            'id_module' => $this->module->id,
583
            'id_order' => ($this->pagantisOrder)?$this->pagantisOrder->getId(): null,
584
        );
585
        $url = ($error)? $this->config['urlKO'] : $this->config['urlOK'];
586
        return $this->redirect($url, $parameters);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->redirect($url, $parameters) targeting AbstractController::redirect() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
587
    }
588
}
589