Completed
Push — master ( 801390...e9e6cc )
by Barry vd.
02:08
created

AbstractRequest::setMoney()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * Abstract Request
4
 */
5
6
namespace Omnipay\Common\Message;
7
8
use Money\Currencies\ISOCurrencies;
9
use Money\Currency;
10
use Money\Formatter\DecimalMoneyFormatter;
11
use Money\Money;
12
use Money\Number;
13
use Money\Parser\DecimalMoneyParser;
14
use Omnipay\Common\CreditCard;
15
use Omnipay\Common\Exception\InvalidRequestException;
16
use Omnipay\Common\Exception\RuntimeException;
17
use Omnipay\Common\Helper;
18
use Omnipay\Common\Http\Client;
19
use Omnipay\Common\Http\ClientInterface;
20
use Omnipay\Common\ItemBag;
21
use Symfony\Component\HttpFoundation\ParameterBag;
22
use Symfony\Component\HttpFoundation\Request as HttpRequest;
23
24
/**
25
 * Abstract Request
26
 *
27
 * This abstract class implements RequestInterface and defines a basic
28
 * set of functions that all Omnipay Requests are intended to include.
29
 *
30
 * Requests of this class are usually created using the createRequest
31
 * function of the gateway and then actioned using methods within this
32
 * class or a class that extends this class.
33
 *
34
 * Example -- creating a request:
35
 *
36
 * <code>
37
 *   class MyRequest extends \Omnipay\Common\Message\AbstractRequest {};
38
 *
39
 *   class MyGateway extends \Omnipay\Common\AbstractGateway {
40
 *     function myRequest($parameters) {
41
 *       $this->createRequest('MyRequest', $parameters);
42
 *     }
43
 *   }
44
 *
45
 *   // Create the gateway object
46
 *   $gw = Omnipay::create('MyGateway');
47
 *
48
 *   // Create the request object
49
 *   $myRequest = $gw->myRequest($someParameters);
50
 * </code>
51
 *
52
 * Example -- validating and sending a request:
53
 *
54
 * <code>
55
 *   try {
56
 *     $myRequest->validate();
57
 *     $myResponse = $myRequest->send();
58
 *   } catch (InvalidRequestException $e) {
59
 *     print "Something went wrong: " . $e->getMessage() . "\n";
60
 *   }
61
 *   // now do something with the $myResponse object, test for success, etc.
62
 * </code>
63
 *
64
 * @see RequestInterface
65
 * @see AbstractResponse
66
 */
67
abstract class AbstractRequest implements RequestInterface
68
{
69
    /**
70
     * The request parameters
71
     *
72
     * @var \Symfony\Component\HttpFoundation\ParameterBag
73
     */
74
    protected $parameters;
75
76
    /**
77
     * The request client.
78
     *
79
     * @var ClientInterface
80
     */
81
    protected $httpClient;
82
83
    /**
84
     * The HTTP request object.
85
     *
86
     * @var \Symfony\Component\HttpFoundation\Request
87
     */
88
    protected $httpRequest;
89
90
    /**
91
     * An associated ResponseInterface.
92
     *
93
     * @var ResponseInterface
94
     */
95
    protected $response;
96
97
    /**
98
     * @var ISOCurrencies
99
     */
100
    protected $currencies;
101
102
    /**
103
     * @var bool
104
     */
105
    protected $zeroAmountAllowed = true;
106
107
    /**
108
     * @var bool
109
     */
110
    protected $negativeAmountAllowed = false;
111
112
    /**
113
     * Create a new Request
114
     *
115
     * @param ClientInterface $httpClient  A HTTP client to make API calls with
116
     * @param HttpRequest     $httpRequest A Symfony HTTP request object
117
     */
118 18
    public function __construct(ClientInterface $httpClient, HttpRequest $httpRequest)
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $httpRequest. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
119
    {
120 18
        $this->httpClient = $httpClient;
121 18
        $this->httpRequest = $httpRequest;
122 18
        $this->initialize();
123 18
    }
124
125
    /**
126
     * Initialize the object with parameters.
127
     *
128
     * If any unknown parameters passed, they will be ignored.
129
     *
130
     * @param array $parameters An associative array of parameters
131
     *
132
     * @return $this
133
     * @throws RuntimeException
134
     */
135 171
    public function initialize(array $parameters = array())
136
    {
137 171
        if (null !== $this->response) {
138 3
            throw new RuntimeException('Request cannot be modified after it has been sent!');
139
        }
140
141 171
        $this->parameters = new ParameterBag;
142
143 171
        Helper::initialize($this, $parameters);
144
145 171
        return $this;
146
    }
147
148
    /**
149
     * Get all parameters as an associative array.
150
     *
151
     * @return array
152
     */
153 9
    public function getParameters()
154
    {
155 9
        return $this->parameters->all();
156
    }
157
158
    /**
159
     * Get a single parameter.
160
     *
161
     * @param string $key The parameter key
162
     * @return mixed
163
     */
164 138
    protected function getParameter($key)
165
    {
166 138
        return $this->parameters->get($key);
167
    }
168
169
    /**
170
     * Set a single parameter
171
     *
172
     * @param string $key The parameter key
173
     * @param mixed $value The value to set
174
     * @return $this
175
     * @throws RuntimeException if a request parameter is modified after the request has been sent.
176
     */
177 150
    protected function setParameter($key, $value)
178
    {
179 150
        if (null !== $this->response) {
180 3
            throw new RuntimeException('Request cannot be modified after it has been sent!');
181
        }
182
183 147
        $this->parameters->set($key, $value);
184
185 147
        return $this;
186
    }
187
188
    /**
189
     * Gets the test mode of the request from the gateway.
190
     *
191
     * @return boolean
192
     */
193 3
    public function getTestMode()
194
    {
195 3
        return $this->getParameter('testMode');
196
    }
197
198
    /**
199
     * Sets the test mode of the request.
200
     *
201
     * @param boolean $value True for test mode on.
202
     * @return $this
203
     */
204 12
    public function setTestMode($value)
205
    {
206 12
        return $this->setParameter('testMode', $value);
207
    }
208
209
    /**
210
     * Validate the request.
211
     *
212
     * This method is called internally by gateways to avoid wasting time with an API call
213
     * when the request is clearly invalid.
214
     *
215
     * @param string ... a variable length list of required parameters
216
     * @throws InvalidRequestException
217
     */
218 12
    public function validate()
219
    {
220 12
        foreach (func_get_args() as $key) {
221 12
            $value = $this->parameters->get($key);
222 12
            if (! isset($value)) {
223 12
                throw new InvalidRequestException("The $key parameter is required");
224
            }
225
        }
226 9
    }
227
228
    /**
229
     * Get the card.
230
     *
231
     * @return CreditCard
232
     */
233 6
    public function getCard()
234
    {
235 6
        return $this->getParameter('card');
236
    }
237
238
    /**
239
     * Sets the card.
240
     *
241
     * @param CreditCard $value
242
     * @return $this
243
     */
244 6
    public function setCard($value)
245
    {
246 6
        if ($value && !$value instanceof CreditCard) {
247 3
            $value = new CreditCard($value);
248
        }
249
250 6
        return $this->setParameter('card', $value);
251
    }
252
253
    /**
254
     * Get the card token.
255
     *
256
     * @return string
257
     */
258 3
    public function getToken()
259
    {
260 3
        return $this->getParameter('token');
261
    }
262
263
    /**
264
     * Sets the card token.
265
     *
266
     * @param string $value
267
     * @return $this
268
     */
269 9
    public function setToken($value)
270
    {
271 9
        return $this->setParameter('token', $value);
272
    }
273
274
    /**
275
     * Get the card reference.
276
     *
277
     * @return string
278
     */
279 3
    public function getCardReference()
280
    {
281 3
        return $this->getParameter('cardReference');
282
    }
283
284
    /**
285
     * Sets the card reference.
286
     *
287
     * @param string $value
288
     * @return $this
289
     */
290 3
    public function setCardReference($value)
291
    {
292 3
        return $this->setParameter('cardReference', $value);
293
    }
294
295
    /**
296
     * @return ISOCurrencies
297
     */
298 72
    protected function getCurrencies()
299
    {
300 72
        if ($this->currencies === null) {
301 72
            $this->currencies = new ISOCurrencies();
302
        }
303
304 72
        return $this->currencies;
305
    }
306
307
    /**
308
     * @param  string|int|null $amount
309
     * @return null|Money
310
     * @throws InvalidRequestException
311
     */
312 66
    private function getMoney($amount = null)
313
    {
314 66
        $currencyCode = $this->getCurrency() ?: 'USD';
315 66
        $currency = new Currency($currencyCode);
316
317 66
        $amount = $amount !== null ? $amount : $this->getParameter('amount');
318
319 66
        if ($amount === null) {
320 3
            return null;
321 63
        } elseif ($amount instanceof Money) {
322 3
            $money = $amount;
323 60
        } elseif (is_integer($amount)) {
324 3
            $money = new Money($amount, $currency);
325
        } else {
326 57
            $moneyParser = new DecimalMoneyParser($this->getCurrencies());
327
328 57
            $number = Number::fromString($amount);
329
330
            // Check for rounding that may occur if too many significant decimal digits are supplied.
331 51
            $decimal_count = strlen($number->getFractionalPart());
332 51
            $subunit = $this->getCurrencies()->subunitFor($currency);
333 51
            if ($decimal_count > $subunit) {
334 6
                throw new InvalidRequestException('Amount precision is too high for currency.');
335
            }
336
337 45
            $money = $moneyParser->parse((string) $number, $currency);
338
        }
339
340
        // Check for a negative amount.
341 51
        if (!$this->negativeAmountAllowed && $money->isNegative()) {
342 6
            throw new InvalidRequestException('A negative amount is not allowed.');
343
        }
344
345
        // Check for a zero amount.
346 45
        if (!$this->zeroAmountAllowed && $money->isZero()) {
347 3
            throw new InvalidRequestException('A zero amount is not allowed.');
348
        }
349
350 42
        return $money;
351
    }
352
353
    /**
354
     * Validates and returns the formatted amount.
355
     *
356
     * @throws InvalidRequestException on any validation failure.
357
     * @return string The amount formatted to the correct number of decimal places for the selected currency.
358
     */
359 54
    public function getAmount()
360
    {
361 54
        $money = $this->getMoney();
362
363 33
        if ($money !== null) {
364 30
            $moneyFormatter = new DecimalMoneyFormatter($this->getCurrencies());
365
366 30
            return $moneyFormatter->format($money);
367
        }
368 3
    }
369
370
    /**
371
     * Sets the payment amount.
372
     *
373
     * @param string|null $value
374
     * @return $this
375
     */
376 54
    public function setAmount($value)
377
    {
378 54
        return $this->setParameter('amount', $value !== null ? (string) $value : null);
379
    }
380
381
    /**
382
     * Get the payment amount as an integer.
383
     *
384
     * @return integer
385
     */
386 12
    public function getAmountInteger()
387
    {
388 12
        $money = $this->getMoney();
389
390 12
        if ($money !== null) {
391 12
            return (int) $money->getAmount();
392
        }
393
    }
394
395
    /**
396
     * Sets the payment amount as integer.
397
     *
398
     * @param int $value
399
     * @return $this
400
     */
401 6
    public function setAmountInteger($value)
402
    {
403 6
        return $this->setParameter('amount', (int) $value);
404
    }
405
406
    /**
407
     * Sets the payment amount as integer.
408
     *
409
     * @param Money $value
410
     * @return $this
411
     */
412 3
    public function setMoney(Money $value)
413
    {
414 3
        $currency = $value->getCurrency()->getCode();
415
416 3
        $this->setCurrency($currency);
417
418 3
        return $this->setParameter('amount', $value);
419
    }
420
421
    /**
422
     * Get the payment currency code.
423
     *
424
     * @return string
425
     */
426 90
    public function getCurrency()
427
    {
428 90
        return $this->getParameter('currency');
429
    }
430
431
    /**
432
     * Sets the payment currency code.
433
     *
434
     * @param string $value
435
     * @return $this
436
     */
437 45
    public function setCurrency($value)
438
    {
439 45
        if ($value !== null) {
440 39
            $value = strtoupper($value);
441
        }
442 45
        return $this->setParameter('currency', $value);
443
    }
444
445
    /**
446
     * Get the payment currency number.
447
     *
448
     * @return string|null
449
     */
450 12 View Code Duplication
    public function getCurrencyNumeric()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
451
    {
452 12
        if (! $this->getCurrency()) {
453 6
            return null;
454
        }
455
456 6
        $currency = new Currency($this->getCurrency());
457
458 6
        if ($this->getCurrencies()->contains($currency)) {
459 3
            return (string) $this->getCurrencies()->numericCodeFor($currency);
460
        }
461 3
    }
462
463
    /**
464
     * Get the number of decimal places in the payment currency.
465
     *
466
     * @return integer
467
     */
468 3 View Code Duplication
    public function getCurrencyDecimalPlaces()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
469
    {
470 3
        if ($this->getCurrency()) {
471 3
            $currency = new Currency($this->getCurrency());
472 3
            if ($this->getCurrencies()->contains($currency)) {
473 3
                return $this->getCurrencies()->subunitFor($currency);
474
            }
475
        }
476
477
        return 2;
478
    }
479
480
    /**
481
     * Format an amount for the payment currency.
482
     *
483
     * @param string $amount
484
     * @return string
485
     */
486 6
    public function formatCurrency($amount)
487
    {
488 6
        $money = $this->getMoney((string) $amount);
489 6
        $formatter = new DecimalMoneyFormatter($this->getCurrencies());
490
491 6
        return $formatter->format($money);
0 ignored issues
show
Bug introduced by
It seems like $money defined by $this->getMoney((string) $amount) on line 488 can be null; however, Money\Formatter\DecimalMoneyFormatter::format() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
492
    }
493
494
    /**
495
     * Get the request description.
496
     *
497
     * @return string
498
     */
499 3
    public function getDescription()
500
    {
501 3
        return $this->getParameter('description');
502
    }
503
504
    /**
505
     * Sets the request description.
506
     *
507
     * @param string $value
508
     * @return $this
509
     */
510 3
    public function setDescription($value)
511
    {
512 3
        return $this->setParameter('description', $value);
513
    }
514
515
    /**
516
     * Get the transaction ID.
517
     *
518
     * The transaction ID is the identifier generated by the merchant website.
519
     *
520
     * @return string
521
     */
522 3
    public function getTransactionId()
523
    {
524 3
        return $this->getParameter('transactionId');
525
    }
526
527
    /**
528
     * Sets the transaction ID.
529
     *
530
     * @param string $value
531
     * @return $this
532
     */
533 3
    public function setTransactionId($value)
534
    {
535 3
        return $this->setParameter('transactionId', $value);
536
    }
537
538
    /**
539
     * Get the transaction reference.
540
     *
541
     * The transaction reference is the identifier generated by the remote
542
     * payment gateway.
543
     *
544
     * @return string
545
     */
546 3
    public function getTransactionReference()
547
    {
548 3
        return $this->getParameter('transactionReference');
549
    }
550
551
    /**
552
     * Sets the transaction reference.
553
     *
554
     * @param string $value
555
     * @return $this
556
     */
557 3
    public function setTransactionReference($value)
558
    {
559 3
        return $this->setParameter('transactionReference', $value);
560
    }
561
562
    /**
563
     * A list of items in this order
564
     *
565
     * @return ItemBag|null A bag containing items in this order
566
     */
567 6
    public function getItems()
568
    {
569 6
        return $this->getParameter('items');
570
    }
571
572
    /**
573
     * Set the items in this order
574
     *
575
     * @param ItemBag|array $items An array of items in this order
576
     * @return $this
577
     */
578 6
    public function setItems($items)
579
    {
580 6
        if ($items && !$items instanceof ItemBag) {
581 3
            $items = new ItemBag($items);
582
        }
583
584 6
        return $this->setParameter('items', $items);
585
    }
586
587
    /**
588
     * Get the client IP address.
589
     *
590
     * @return string
591
     */
592 3
    public function getClientIp()
593
    {
594 3
        return $this->getParameter('clientIp');
595
    }
596
597
    /**
598
     * Sets the client IP address.
599
     *
600
     * @param string $value
601
     * @return $this
602
     */
603 3
    public function setClientIp($value)
604
    {
605 3
        return $this->setParameter('clientIp', $value);
606
    }
607
608
    /**
609
     * Get the request return URL.
610
     *
611
     * @return string
612
     */
613 3
    public function getReturnUrl()
614
    {
615 3
        return $this->getParameter('returnUrl');
616
    }
617
618
    /**
619
     * Sets the request return URL.
620
     *
621
     * @param string $value
622
     * @return $this
623
     */
624 3
    public function setReturnUrl($value)
625
    {
626 3
        return $this->setParameter('returnUrl', $value);
627
    }
628
629
    /**
630
     * Get the request cancel URL.
631
     *
632
     * @return string
633
     */
634 3
    public function getCancelUrl()
635
    {
636 3
        return $this->getParameter('cancelUrl');
637
    }
638
639
    /**
640
     * Sets the request cancel URL.
641
     *
642
     * @param string $value
643
     * @return $this
644
     */
645 3
    public function setCancelUrl($value)
646
    {
647 3
        return $this->setParameter('cancelUrl', $value);
648
    }
649
650
    /**
651
     * Get the request notify URL.
652
     *
653
     * @return string
654
     */
655 3
    public function getNotifyUrl()
656
    {
657 3
        return $this->getParameter('notifyUrl');
658
    }
659
660
    /**
661
     * Sets the request notify URL.
662
     *
663
     * @param string $value
664
     * @return $this
665
     */
666 3
    public function setNotifyUrl($value)
667
    {
668 3
        return $this->setParameter('notifyUrl', $value);
669
    }
670
671
    /**
672
     * Get the payment issuer.
673
     *
674
     * This field is used by some European gateways, and normally represents
675
     * the bank where an account is held (separate from the card brand).
676
     *
677
     * @return string
678
     */
679 3
    public function getIssuer()
680
    {
681 3
        return $this->getParameter('issuer');
682
    }
683
684
    /**
685
     * Set the payment issuer.
686
     *
687
     * This field is used by some European gateways, and normally represents
688
     * the bank where an account is held (separate from the card brand).
689
     *
690
     * @param string $value
691
     * @return $this
692
     */
693 3
    public function setIssuer($value)
694
    {
695 3
        return $this->setParameter('issuer', $value);
696
    }
697
698
    /**
699
     * Get the payment issuer.
700
     *
701
     * This field is used by some European gateways, which support
702
     * multiple payment providers with a single API.
703
     *
704
     * @return string
705
     */
706 3
    public function getPaymentMethod()
707
    {
708 3
        return $this->getParameter('paymentMethod');
709
    }
710
711
    /**
712
     * Set the payment method.
713
     *
714
     * This field is used by some European gateways, which support
715
     * multiple payment providers with a single API.
716
     *
717
     * @param string $value
718
     * @return $this
719
     */
720 3
    public function setPaymentMethod($value)
721
    {
722 3
        return $this->setParameter('paymentMethod', $value);
723
    }
724
725
    /**
726
     * Send the request
727
     *
728
     * @return ResponseInterface
729
     */
730 12
    public function send()
731
    {
732 12
        $data = $this->getData();
733
734 12
        return $this->sendData($data);
735
    }
736
737
    /**
738
     * Get the associated Response.
739
     *
740
     * @return ResponseInterface
741
     */
742 6
    public function getResponse()
743
    {
744 6
        if (null === $this->response) {
745 3
            throw new RuntimeException('You must call send() before accessing the Response!');
746
        }
747
748 3
        return $this->response;
749
    }
750
}
751