Test Failed
Push — query-string ( 95048c...99ed40 )
by
unknown
01:57
created

TransactionStatusRequest   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 137
Duplicated Lines 0 %

Test Coverage

Coverage 35.7%

Importance

Changes 11
Bugs 1 Features 2
Metric Value
eloc 54
c 11
b 1
f 2
dl 0
loc 137
ccs 20
cts 56
cp 0.357
rs 10
wmc 14

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getData() 0 7 1
B getTransactionStatusFromPostBack() 0 33 6
A sendData() 0 21 2
A validateSecurityHashMatch() 0 13 3
A getPossibleValidHashes() 0 18 2
1
<?php
2
3
namespace Omnipay\IcepayPayments\Message;
4
5
use Omnipay\Common\Message\ResponseInterface;
6
use Omnipay\IcepayPayments\Exception\PostBackException;
0 ignored issues
show
Bug introduced by
The type Omnipay\IcepayPayments\Exception\PostBackException 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...
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 1
        $transactionStatusResponse = $this->getTransactionStatusFromPostBack();
32
33 1
        if ($transactionStatusResponse !== null) {
34
            return $transactionStatusResponse;
35
        }
36
37 1
        $this->sendRequest(
38 1
            Request::METHOD_POST,
39 1
            sprintf(
40 1
                '/transaction/%s',
41 1
                $this->getTransactionReference()
42
            ),
43 1
            $data
44
        );
45
46 1
        return new TransactionStatusResponse(
47 1
            $this,
48 1
            $this->getResponseBody(),
49 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

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