Completed
Push — master ( 9097d5...0ce82e )
by Florian
06:13
created

Addresscheck::isLifetimeStillValid()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * PAYONE Magento 2 Connector is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * PAYONE Magento 2 Connector is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with PAYONE Magento 2 Connector. If not, see <http://www.gnu.org/licenses/>.
16
 *
17
 * PHP version 5
18
 *
19
 * @category  Payone
20
 * @package   Payone_Magento2_Plugin
21
 * @author    FATCHIP GmbH <[email protected]>
22
 * @copyright 2003 - 2016 Payone GmbH
23
 * @license   <http://www.gnu.org/licenses/> GNU Lesser General Public License
24
 * @link      http://www.payone.de
25
 */
26
27
namespace Payone\Core\Model\Risk;
28
29
use Magento\Quote\Api\Data\AddressInterface;
0 ignored issues
show
Bug introduced by
The type Magento\Quote\Api\Data\AddressInterface 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...
30
use Magento\Quote\Model\Quote;
0 ignored issues
show
Bug introduced by
The type Magento\Quote\Model\Quote 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...
31
use Magento\Framework\Exception\LocalizedException;
32
33
/**
34
 * Model for calling the addresscheck request
35
 */
36
class Addresscheck
37
{
38
    /**
39
     * Response from the PAYONE addresscheck request
40
     *
41
     * @var array
42
     */
43
    protected $aResponse = null;
44
45
    /**
46
     * Determines if the request was NOT executed because the lifetime of the last was still valid
47
     *
48
     * @var bool
49
     */
50
    protected $blIsLifetimeValid;
51
52
    /**
53
     * Saves if the address was corrected
54
     *
55
     * @var bool
56
     */
57
    protected $addressCorrected;
58
59
    /**
60
     * PAYONE addresscheck request model
61
     *
62
     * @var \Payone\Core\Model\Api\Request\Addresscheck
63
     */
64
    protected $addresscheck;
65
66
    /**
67
     * PAYONE database helper
68
     *
69
     * @var \Payone\Core\Helper\Database
70
     */
71
    protected $databaseHelper;
72
73
    /**
74
     * PAYONE toolkit helper
75
     *
76
     * @var \Payone\Core\Helper\Toolkit
77
     */
78
    protected $toolkitHelper;
79
80
    /**
81
     * Checkout session
82
     *
83
     * @var \Magento\Checkout\Model\Session
0 ignored issues
show
Bug introduced by
The type Magento\Checkout\Model\Session 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...
84
     */
85
    protected $checkoutSession;
86
87
    /**
88
     * Constructor
89
     *
90
     * @param \Payone\Core\Model\Api\Request\Addresscheck $addresscheck
91
     * @param \Payone\Core\Helper\Database                $databaseHelper
92
     * @param \Payone\Core\Helper\Toolkit                 $toolkitHelper
93
     * @param \Magento\Checkout\Model\Session             $checkoutSession
94
     */
95
    public function __construct(
96
        \Payone\Core\Model\Api\Request\Addresscheck $addresscheck,
97
        \Payone\Core\Helper\Database $databaseHelper,
98
        \Payone\Core\Helper\Toolkit $toolkitHelper,
99
        \Magento\Checkout\Model\Session $checkoutSession
100
    ) {
101
        $this->addresscheck = $addresscheck;
102
        $this->databaseHelper = $databaseHelper;
103
        $this->toolkitHelper = $toolkitHelper;
104
        $this->checkoutSession = $checkoutSession;
105
    }
106
107
    /**
108
     * Return if check was not executed because the lifetime of the last check was still valid
109
     *
110
     * @return bool
111
     */
112
    public function isLifetimeStillValid()
113
    {
114
        return $this->blIsLifetimeValid;
115
    }
116
117
    /**
118
     * Return addressCorrected property
119
     *
120
     * @return bool
121
     */
122
    public function isAddressCorrected()
123
    {
124
        return $this->addressCorrected;
125
    }
126
127
    /**
128
     * Get addresscheck config parameter
129
     *
130
     * @param  string $sParam
131
     * @return string
132
     */
133
    public function getConfigParam($sParam)
134
    {
135
        return $this->databaseHelper->getConfigParam($sParam, 'address_check', 'payone_protect');
136
    }
137
138
    /**
139
     * Correct a single address field if needed
140
     *
141
     * @param  AddressInterface $oAddress
142
     * @param  array            $aResponse
143
     * @param  string           $sArrayKey
144
     * @param  string           $sPropertyName
145
     * @return AddressInterface
146
     */
147
    protected function correctSingleField(AddressInterface $oAddress, $aResponse, $sArrayKey, $sPropertyName)
148
    {
149
        if (isset($aResponse[$sArrayKey]) && $aResponse[$sArrayKey] != $oAddress->getData($sPropertyName)) {
150
            $oAddress->setData($sPropertyName, $aResponse[$sArrayKey]);
151
            $this->addressCorrected = true;
152
        }
153
154
        return $oAddress;
155
    }
156
157
    /**
158
     * Change the address according to the response
159
     *
160
     * @param  AddressInterface $oAddress
161
     * @return AddressInterface
162
     */
163
    public function correctAddress(AddressInterface $oAddress)
164
    {
165
        $aResponse = $this->getResponse($oAddress);
166
        if (!is_array($aResponse)) {
167
            return $oAddress;
168
        }
169
170
        $this->addressCorrected = false;
171
        $oAddress = $this->correctSingleField($oAddress, $aResponse, 'firstname', 'firstname');
172
        $oAddress = $this->correctSingleField($oAddress, $aResponse, 'lastname', 'lastname');
173
        $oAddress = $this->correctSingleField($oAddress, $aResponse, 'zip', 'postcode');
174
        $oAddress = $this->correctSingleField($oAddress, $aResponse, 'city', 'city');
175
176
        if (isset($aResponse['street'])) {
177
            $sStreet = $oAddress->getStreet();
178
            if (is_array($sStreet)) {
179
                $sStreet = implode(' ', $sStreet);
180
            }
181
182
            if ($aResponse['street'] != $sStreet) {
183
                $oAddress->setStreet($aResponse['street']);
184
                $this->addressCorrected = true;
185
            }
186
        }
187
188
        return $oAddress;
189
    }
190
191
    /**
192
     * Check if the addresscheck needs to be executed for this quote
193
     *
194
     * @param  bool   $isBillingAddress
195
     * @param  bool   $isVirtual
196
     * @param  double $dTotal
197
     * @return bool
198
     */
199
    public function isCheckNeededForQuote($isBillingAddress, $isVirtual, $dTotal)
200
    {
201
        $dMinBasketValue = $this->getConfigParam('min_order_total');
202
        if (!empty($dMinBasketValue) && is_numeric($dMinBasketValue) && $dTotal < $dMinBasketValue) {
203
            return false;
204
        }
205
206
        $dMaxBasketValue = $this->getConfigParam('max_order_total');
207
        if (!empty($dMaxBasketValue) && is_numeric($dMaxBasketValue) && $dTotal > $dMaxBasketValue) {
208
            return false;
209
        }
210
211
        $blCheckVirtual = (bool)$this->getConfigParam('check_billing_for_virtual_order');
212
        if ($isBillingAddress === true && $isVirtual === true && $blCheckVirtual === false) {
213
            return false;
214
        }
215
216
        return true;
217
    }
218
219
    /**
220
     * Returns the personstatus mapping from addresscheck admin config
221
     *
222
     * @return array
223
     */
224
    public function getPersonstatusMapping()
225
    {
226
        $aReturnMappings = [];
227
228
        $sMappings = $this->getConfigParam('mapping_personstatus');
229
        $aMappings = $this->toolkitHelper->unserialize($sMappings);
230
        if (!is_array($aMappings)) {
231
            $aMappings = [];
232
        }
233
234
        foreach ($aMappings as $aMapping) {
235
            $aReturnMappings[$aMapping['personstatus']] = $aMapping['score'];
236
        }
237
238
        return $aReturnMappings;
239
    }
240
241
    /**
242
     * Get formatted invalid message
243
     *
244
     * @param  string $sCustomermessage
245
     * @return string
246
     */
247
    public function getInvalidMessage($sCustomermessage)
248
    {
249
        $sInvalidMessage = $this->getConfigParam('message_response_invalid');
250
        if (!empty($sInvalidMessage)) {
251
            $aSubstitutionArray = [
252
                '{{payone_customermessage}}' => __($sCustomermessage),
0 ignored issues
show
Bug introduced by
The function __ 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

252
                '{{payone_customermessage}}' => /** @scrutinizer ignore-call */ __($sCustomermessage),
Loading history...
253
            ];
254
            return $this->toolkitHelper->handleSubstituteReplacement($sInvalidMessage, $aSubstitutionArray, 255);
0 ignored issues
show
Bug introduced by
$aSubstitutionArray of type array is incompatible with the type string expected by parameter $aSubstitutionArray of Payone\Core\Helper\Toolk...SubstituteReplacement(). ( Ignorable by Annotation )

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

254
            return $this->toolkitHelper->handleSubstituteReplacement($sInvalidMessage, /** @scrutinizer ignore-type */ $aSubstitutionArray, 255);
Loading history...
255
        }
256
257
        return __($sCustomermessage);
258
    }
259
260
    /**
261
     * Return error message
262
     *
263
     * @return string
264
     */
265
    public function getErrorMessage()
266
    {
267
        $sErrorMessage = $this->getConfigParam('stop_checkout_message');
268
        if (empty($sErrorMessage)) { // add default errormessage if none is configured
269
            $sErrorMessage = 'An error occured during the addresscheck.';
270
        }
271
        return $sErrorMessage;
272
    }
273
274
    /**
275
     * Get error message by the given response
276
     *
277
     * @param  array $aResponse
278
     * @return string
279
     */
280
    public function getErrorMessageByResponse($aResponse)
281
    {
282
        $sErrorMessage = false;
283
        if (!isset($aResponse['status'])) { // the response doesnt have the expected format
284
            $sErrorMessage = 'An error occured during the addresscheck.';
285
        } elseif ($aResponse['status'] == 'INVALID') {
286
            $sErrorMessage = $this->getInvalidMessage($aResponse['customermessage']);
287
        } elseif ($aResponse['status'] == 'ERROR') {
288
            if ($this->getConfigParam('handle_response_error') == 'stop_checkout') {
289
                $sErrorMessage = $this->getErrorMessage();
290
            }
291
        }
292
        return $sErrorMessage;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $sErrorMessage could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
293
    }
294
295
    /**
296
     * Execute addresscheck and return the response
297
     *
298
     * @param  AddressInterface $oAddress
299
     * @return array
300
     */
301
302
    /**
303
     *
304
     * @param AddressInterface $oAddress
305
     * @return type
0 ignored issues
show
Bug introduced by
The type Payone\Core\Model\Risk\type 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...
306
     * @throws LocalizedException
307
     */
308
    protected function handleAddresscheck(AddressInterface $oAddress)
309
    {
310
        $aResponse = $this->getResponse($oAddress);
311
        if (is_array($aResponse)) {
312
            $sErrorMessage = $this->getErrorMessageByResponse($aResponse);
313
            if (!empty($sErrorMessage)) {
314
                throw new LocalizedException(__($sErrorMessage));
0 ignored issues
show
Bug introduced by
The function __ 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

314
                throw new LocalizedException(/** @scrutinizer ignore-call */ __($sErrorMessage));
Loading history...
315
            }
316
        }
317
        return $aResponse;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $aResponse returns the type boolean|array which is incompatible with the documented return type Payone\Core\Model\Risk\type.
Loading history...
318
    }
319
320
    /**
321
     * Get score from session or from a new addresscheck and add it to the address
322
     *
323
     * @param  AddressInterface $oAddress
324
     * @param  Quote            $oQuote
325
     * @param  bool             $blIsBillingAddress
326
     * @return AddressInterface
327
     */
328
    public function handleAddressManagement(AddressInterface $oAddress, Quote $oQuote, $blIsBillingAddress = true)
329
    {
330
        $sScore = $this->checkoutSession->getPayoneBillingAddresscheckScore();
331
        if ($blIsBillingAddress === false) {
332
            $sScore = $this->checkoutSession->getPayoneShippingAddresscheckScore();
333
        }
334
        $this->checkoutSession->unsPayoneBillingAddresscheckScore();
335
        $this->checkoutSession->unsPayoneShippingAddresscheckScore();
336
337
        if (!$sScore && empty($oAddress->getPayoneAddresscheckScore())) {
338
            if ($this->isCheckNeededForQuote(false, $oQuote->isVirtual(), $oQuote->getSubtotal())) {
339
                $aResponse = $this->handleAddresscheck($oAddress);
340
                if (isset($aResponse['status']) && $aResponse['status'] == 'VALID') {
341
                    $oAddress = $this->correctAddress($oAddress);
342
                }
343
                $sScore = $this->getScore($oAddress);
344
            }
345
        }
346
        if ($sScore) {
347
            $oAddress->setPayoneAddresscheckScore($sScore);
348
        }
349
        return $oAddress;
350
    }
351
352
    /**
353
     * Get score from response or an old saved score from the database
354
     *
355
     * @param  AddressInterface $oAddress
356
     * @return string
357
     */
358
    public function getScore(AddressInterface $oAddress)
359
    {
360
        $aResponse = $this->getResponse($oAddress);
361
362
        $sScore = 'G';
363
        if (isset($aResponse['status']) && $aResponse['status'] == 'INVALID') {
364
            $sScore = 'R';
365
        } elseif (isset($aResponse['personstatus'])) {
366
            $sPersonStatus = $aResponse['personstatus'];
367
            if ($sPersonStatus != 'NONE') {
368
                $aMapping = $this->getPersonstatusMapping();
369
                if (array_key_exists($sPersonStatus, $aMapping)) {
370
                    $sScore = $aMapping[$sPersonStatus];
371
                }
372
            }
373
        } elseif ($this->isLifetimeStillValid()) {
374
            $sScore = $this->databaseHelper->getOldAddressStatus($oAddress, false);
375
        }
376
        return $sScore;
377
    }
378
379
    /**
380
     * Perform the PAYONE addresscheck request and return the response
381
     *
382
     * @param  AddressInterface $oAddress
383
     * @param  bool             $blIsBillingAddress
384
     * @return array|bool
385
     */
386
    public function getResponse(AddressInterface $oAddress, $blIsBillingAddress = false)
387
    {
388
        if ($this->aResponse === null) {
389
            $this->aResponse = $this->addresscheck->sendRequest($oAddress, $blIsBillingAddress);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->addresscheck->sen...s, $blIsBillingAddress) can also be of type boolean. However, the property $aResponse is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
390
            if ($this->aResponse === true) {
391
                $this->blIsLifetimeValid = true;
392
            }
393
        }
394
        return $this->aResponse;
395
    }
396
}
397