Passed
Push — query-string ( e4a3de...95048c )
by
unknown
01:36
created

getTransactionStatusFromPostBack()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 18.4094

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 5
eloc 16
c 3
b 0
f 0
nc 4
nop 0
dl 0
loc 27
ccs 3
cts 16
cp 0.1875
crap 18.4094
rs 9.4222
1
<?php
2
3
namespace Omnipay\IcepayPayments\Message;
4
5
use Omnipay\Common\Message\ResponseInterface;
6
use Omnipay\IcepayPayments\Exception\PostBackException;
7
use Symfony\Component\HttpFoundation\Request;
8
9
/**
10
 * The request for getting the transaction status at Icepay.
11
 */
12
class TransactionStatusRequest extends AbstractRequest
13
{
14
    /**
15
     * {@inheritdoc}
16
     */
17 1
    public function getData(): array
18
    {
19 1
        $data = parent::getData();
20
21 1
        $data['ContractProfileId'] = $this->getContractProfileId();
22
23 1
        return $data;
24
    }
25
26
    /**
27
     * {@inheritdoc}
28
     */
29 1
    public function sendData($data): ResponseInterface
30
    {
31
        try {
32 1
            $transactionStatusResponse = $this->getTransactionStatusFromPostBack();
33
        } catch (PostBackException $exception) {
34
            // Optional parameter to throw the error instead of fallback.
35
            if (isset($data['throwOnPostBackError'])) {
36
                throw $exception;
37
            }
38
            $transactionStatusResponse = false;
39
        }
40
41 1
        if ($transactionStatusResponse !== false) {
42
            return $transactionStatusResponse;
43
        }
44
45 1
        $this->sendRequest(
46 1
            Request::METHOD_POST,
47 1
            sprintf(
48 1
                '/transaction/%s',
49 1
                $this->getTransactionReference()
50
            ),
51 1
            $data
52
        );
53
54 1
        return new TransactionStatusResponse(
55 1
            $this,
56 1
            $this->getResponseBody(),
57 1
            $this->getResponse()->getStatusCode()
0 ignored issues
show
Bug introduced by
The method getStatusCode() does not exist on Omnipay\Common\Message\ResponseInterface. ( Ignorable by Annotation )

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

57
            $this->getResponse()->/** @scrutinizer ignore-call */ getStatusCode()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
58
        );
59
    }
60
61
    /**
62
     * Use the data sent by Icepay in the post back to check the status.
63
     * This is necessary because Icepay has a delay in their backend if you request the status immediately after the signal.
64
     *
65
     * @see http://docs2.icepay.com/payment-process/handling-the-postback/postback-sample/
66
     *
67
     * @return TransactionStatusResponse|bool false when the data is is not sent or not correct
68
     *
69
     * @throws PostBackException by call to self::validateSecurityHashMatch()
70
     */
71 1
    private function getTransactionStatusFromPostBack()
72
    {
73 1
        if (stripos($this->httpRequest->getContentType(), 'json') === false) {
74 1
            return false;
75
        }
76
77
        try {
78
            $contentAsArray = json_decode($this->httpRequest->getContent(), true);
79
        } catch (\LogicException $exception) {
80
            return false;
81
        }
82
83
        if (is_array($contentAsArray) === false || isset($contentAsArray['StatusCode']) === false) {
84
            return false;
85
        }
86
87
        $this->validateSecurityHashMatch($this->httpRequest, $contentAsArray);
88
89
        $camelCasedKeysContent = array_combine(
90
            array_map('lcfirst', array_keys($contentAsArray)),
91
            array_values($contentAsArray)
92
        );
93
94
        return new TransactionStatusResponse(
95
            $this,
96
            $camelCasedKeysContent,
97
            200
98
        );
99
    }
100
101
    /**
102
     * Get the security hash from the request and match it against a generated hash from the sent values.
103
     * Will throw an exception if it does not match.
104
     * Needs the POSTed Json as a php array.
105
     *
106
     * @param Request $request
107
     * @param array   $contentAsArray
108
     *
109
     * @return bool
110
     *
111
     * @throws PostBackException
112
     */
113
    private function validateSecurityHashMatch(Request $request, $contentAsArray): bool
114
    {
115
        $sentSecurityHash = $request->headers->get('checksum');
116
117
        $possibleHashes = $this->getPossibleValidHashes($request, $contentAsArray);
118
119
        foreach ($possibleHashes as $generatedHash) {
120
            if ($generatedHash === $sentSecurityHash) {
121
                return true;
122
            }
123
        }
124
125
        throw new PostBackException(
126
            sprintf(
127
                'Sent security hash %s did not match generated hashes: %s',
128
                $sentSecurityHash,
129
                implode(', ', $possibleHashes)
130
            )
131
        );
132
    }
133
134
    /**
135
     * They way Icepay generates the hash is by using our notification url.
136
     * Though, they might add a trailing slash. Unsure of this at this point, so check both.
137
     *
138
     * @param Request $request
139
     * @param array   $contentAsArray
140
     *
141
     * @return array
142
     */
143
    private function getPossibleValidHashes(Request $request, $contentAsArray): array
144
    {
145
        $notifyUrls = [
146
            $request->getSchemeAndHttpHost().$request->getRequestUri(),
147
            $request->getSchemeAndHttpHost().$request->getRequestUri().'/',
148
        ];
149
150
        $hashes = [];
151
        foreach ($notifyUrls as $notifyUrl) {
152
            $hashes[] = $this->getSecurityHash(
153
                $request->getMethod(),
154
                $notifyUrl,
155
                $contentAsArray,
156
                true
157
            );
158
        }
159
160
        return $hashes;
161
    }
162
}
163