Completed
Push — master ( 1570a8...0eac0a )
by
unknown
12s
created

WebservicePurchaseResponse::isSuccessful()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 8
cts 8
cp 1
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 8
nc 6
nop 0
crap 6
1
<?php
2
3
namespace Omnipay\Redsys\Message;
4
5
use Omnipay\Common\Message\AbstractResponse;
6
use Omnipay\Common\Message\RequestInterface;
7
use Omnipay\Common\Exception\InvalidResponseException;
8
9
/**
10
 * Redsys Purchase Response
11
 */
12
class WebservicePurchaseResponse extends AbstractResponse
13
{
14
    /** @var string */
15
    protected $returnSignature;
16
    /** @var boolean */
17
    protected $usingUpcaseResponse = false;
18
19
    /**
20
     * Constructor
21
     *
22
     * @param RequestInterface $request the initiating request.
23
     * @param mixed $data
24
     *
25
     * @throws InvalidResponseException If resopnse format is incorrect, data is missing, or signature does not match
26
     */
27 13
    public function __construct(RequestInterface $request, $data)
28
    {
29 13
        parent::__construct($request, $data);
30
31 13
        $security = new Security;
32
33 13
        if (!isset($data['CODIGO'])) {
34 1
            throw new InvalidResponseException('Invalid response from payment gateway (no data)');
35
        }
36 12
        if (!isset($data['OPERACION'])) {
37 3
            if ($data['CODIGO'] == '0') {
38 1
                throw new InvalidResponseException('Invalid response from payment gateway (no data)');
39
            } else {
40 2
                throw new InvalidResponseException('Invalid response from payment gateway ('.$data['CODIGO'].')');
41
            }
42
        }
43
44 9
        if (isset($data['OPERACION']['DS_ORDER'])) {
45 1
            $this->usingUpcaseResponse = true;
46 1
        }
47
48 9
        $order = $this->GetKey('Ds_Order');
49 9
        if ($order === null) {
50 1
            throw new InvalidResponseException();
51
        }
52
53
        $signature_keys = array(
54 8
            'Ds_Amount',
55 8
            'Ds_Order',
56 8
            'Ds_MerchantCode',
57 8
            'Ds_Currency',
58 8
            'Ds_Response',
59 8
            'Ds_TransactionType',
60 8
            'Ds_SecurePayment',
61 8
        );
62 8
        $signature_data = '';
63 8
        foreach ($signature_keys as $key) {
64 8
            $value = $this->getKey($key);
65 8
            if ($value === null) {
66 1
                throw new InvalidResponseException('Invalid response from payment gateway (missing data)');
67
            }
68 8
            $signature_data .= $value;
69 8
        }
70
71 7
        $this->returnSignature = $security->createSignature(
72 7
            $signature_data,
73 7
            $order,
74 7
            $this->request->getHmacKey()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Omnipay\Common\Message\RequestInterface as the method getHmacKey() does only exist in the following implementations of said interface: Omnipay\Redsys\Message\CompletePurchaseRequest, Omnipay\Redsys\Message\PurchaseRequest, Omnipay\Redsys\Message\WebservicePurchaseRequest.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
75 7
        );
76
77 7
        if ($this->returnSignature != $this->GetKey('Ds_Signature')) {
78 1
            throw new InvalidResponseException('Invalid response from payment gateway (signature mismatch)');
79
        }
80 6
    }
81
82 6
    public function isSuccessful()
83
    {
84 6
        $response_code = $this->getKey('Ds_Response');
85
86
        // check for field existence as well as value
87 6
        return isset($this->data['CODIGO'])
88 6
            && $this->data['CODIGO'] == '0'
89 6
            && $response_code !== null
90 6
            && is_numeric($response_code)
91 6
            && 0 <= $response_code
92 6
            && 100 > $response_code;
93
    }
94
95
     /**
96
     * Helper method to get a specific response parameter if available.
97
     *
98
     * @param string $key The key to look up
99
     *
100
     * @return null|mixed
101
     */
102 9
    protected function getKey($key)
103
    {
104 9
        if ($this->usingUpcaseResponse) {
105 1
            $key = strtoupper($key);
106 1
        }
107 9
        return isset($this->data['OPERACION'][$key]) ? $this->data['OPERACION'][$key] : null;
108
    }
109
110
    /**
111
     * Get the authorisation code if available.
112
     *
113
     * @return null|string
114
     */
115 3
    public function getTransactionReference()
116
    {
117 3
        return $this->getKey('Ds_AuthorisationCode');
118
    }
119
120
    /**
121
     * Get the merchant response message if available.
122
     *
123
     * @return null|string
124
     */
125 6
    public function getMessage()
126
    {
127 6
        return $this->getKey('Ds_Response');
128
    }
129
130
    /**
131
     * Get the merchant data if available.
132
     *
133
     * @return null|string
134
     */
135 1
    public function getMerchantData()
136
    {
137 1
        return $this->getKey('Ds_MerchantData');
138
    }
139
140
    /**
141
     * Get the card country if available.
142
     *
143
     * @return null|string  ISO 3166-1 (3-digit numeric) format, if supplied
144
     */
145 1
    public function getCardCountry()
146
    {
147 1
        return $this->getKey('Ds_Card_Country');
148
    }
149
}
150