Completed
Pull Request — master (#1)
by Leith
04:26
created

getTransactionReference()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Omnipay\Redsys\Message;
4
5
use SimpleXMLElement;
6
use Omnipay\Common\Message\AbstractResponse;
7
use Omnipay\Common\Message\RequestInterface;
8
use Omnipay\Common\Exception\InvalidResponseException;
9
10
/**
11
 * Redsys Purchase Response
12
 */
13
class WebservicePurchaseResponse extends AbstractResponse
14
{
15
    /** @var string */
16
    protected $returnSignature;
17
    /** @var boolean */
18
    protected $usingUpcaseResponse = false;
19
20
    /**
21
     * Constructor
22
     *
23
     * @param RequestInterface $request the initiating request.
24
     * @param mixed $data
25
     *
26
     * @throws InvalidResponseException If resopnse format is incorrect, data is missing, or signature does not match
27
     */
28 13
    public function __construct(RequestInterface $request, $data)
29
    {
30 13
        parent::__construct($request, $data);
31
32 13
        $security = new Security;
33
34 13
        if (!isset($data['CODIGO'])) {
35 1
            throw new InvalidResponseException('Invalid response from payment gateway (no data)');
36
        }
37 12
        if (!isset($data['OPERACION'])) {
38 3
            if ($data['CODIGO'] == '0') {
39 1
                throw new InvalidResponseException('Invalid response from payment gateway (no data)');
40
            } else {
41 2
                throw new InvalidResponseException('Invalid response from payment gateway ('.$data['CODIGO'].')');
42
            }
43
        }
44
45 9
        if (isset($data['OPERACION']['DS_ORDER'])) {
46 1
            $this->usingUpcaseResponse = true;
47 1
        }
48
49 9
        $order = $this->GetKey('Ds_Order');
50 9
        if ($order === null) {
51 1
            throw new InvalidResponseException();
52
        }
53
54
        $signature_keys = array(
55 8
            'Ds_Amount',
56 8
            'Ds_Order',
57 8
            'Ds_MerchantCode',
58 8
            'Ds_Currency',
59 8
            'Ds_Response',
60 8
            'Ds_TransactionType',
61 8
            'Ds_SecurePayment',
62 8
        );
63 8
        $signature_data = '';
64 8
        foreach ($signature_keys as $key) {
65 8
            $value = $this->getKey($key);
66 8
            if ($value === null) {
67 1
                throw new InvalidResponseException('Invalid response from payment gateway (missing data)');
68
            }
69 8
            $signature_data .= $value;
70 8
        }
71
72 7
        $this->returnSignature = $security->createSignature(
73 7
            $signature_data,
74 7
            $order,
75 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...
76 7
        );
77
78 7
        if ($this->returnSignature != $this->GetKey('Ds_Signature')) {
79 1
            throw new InvalidResponseException('Invalid response from payment gateway (signature mismatch)');
80
        }
81 6
    }
82
83 6
    public function isSuccessful()
84
    {
85 6
        $response_code = $this->getKey('Ds_Response');
86
87
        // check for field existence as well as value
88 6
        return isset($this->data['CODIGO'])
89 6
            && $this->data['CODIGO'] == '0'
90 6
            && $response_code !== null
91 6
            && is_numeric($response_code)
92 6
            && 0 <= $response_code
93 6
            && 100 > $response_code;
94
    }
95
96
     /**
97
     * Helper method to get a specific response parameter if available.
98
     *
99
     * @param string $key The key to look up
100
     *
101
     * @return null|mixed
102
     */
103 9
    protected function getKey($key)
104
    {
105 9
        if ($this->usingUpcaseResponse) {
106 1
            $key = strtoupper($key);
107 1
        }
108 9
        return isset($this->data['OPERACION'][$key]) ? $this->data['OPERACION'][$key] : null;
109
    }
110
111
    /**
112
     * Get the authorisation code if available.
113
     *
114
     * @return null|string
115
     */
116 3
    public function getTransactionReference()
117
    {
118 3
        return $this->getKey('Ds_AuthorisationCode');
119
    }
120
121
    /**
122
     * Get the merchant response message if available.
123
     *
124
     * @return null|string
125
     */
126 6
    public function getMessage()
127
    {
128 6
        return $this->getKey('Ds_Response');
129
    }
130
131
    /**
132
     * Get the merchant data if available.
133
     *
134
     * @return null|string
135
     */
136 1
    public function getMerchantData()
137
    {
138 1
        return $this->getKey('Ds_MerchantData');
139
    }
140
141
    /**
142
     * Get the card country if available.
143
     *
144
     * @return null|string  ISO 3166-1 (3-digit numeric) format, if supplied
145
     */
146 1
    public function getCardCountry()
147
    {
148 1
        return $this->getKey('Ds_Card_Country');
149
    }
150
}
151