CallbackDetails   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 159
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 54
dl 0
loc 159
rs 10
c 1
b 0
f 0
wmc 15

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A status() 0 3 1
A transactionId() 0 3 1
A currency() 0 3 1
A validateCallback() 0 33 5
A amountPaid() 0 3 1
A validateHash() 0 16 1
A parseCallback() 0 7 2
A setProperty() 0 9 2
1
<?php declare(strict_types=1);
2
3
namespace Getloy;
4
5
use \Exception;
6
7
/**
8
 * Callback details
9
 */
10
class CallbackDetails
11
{
12
    /** Payment completed successfully */
13
    const STATUS_SUCCESS = 'successful';
14
15
    protected $gateway;
16
    protected $transactionId;
17
    protected $status;
18
    protected $amountPaid;
19
    protected $currency;
20
21
    /**
22
     * Instantiate callback details
23
     *
24
     * @param Gateway $gateway
25
     */
26
    public function __construct(Gateway $gateway)
27
    {
28
        $this->gateway = $gateway;
29
    }
30
31
    /**
32
     * Parse and validate the body of a callback request.
33
     *
34
     * @param string $callbackBody Raw callback body.
35
     * @throws Exception If the body is invalid.
36
     */
37
    public function parseCallback(string $callbackBody): void
38
    {
39
        $callbackData = json_decode($callbackBody, true);
40
        if (is_null($callbackData)) {
41
            throw new Exception('Malformed callback body!');
42
        }
43
        $this->validateCallback($callbackData);
44
    }
45
46
    /**
47
     * Validate a parsed callback request.
48
     *
49
     * @param array $callbackBody Parsed callback body.
50
     * @throws Exception If the body is invalid.
51
     */
52
    public function validateCallback(array $callbackBody): void
53
    {
54
        $valueMap = [
55
            'transactionId' => [
56
              'name' =>'tid',
57
              'type' => 'string',
58
            ],
59
            'status' => [
60
              'name' =>'status',
61
              'type' => 'string',
62
            ],
63
            'amountPaid' => [
64
              'name' =>'amount_paid',
65
              'type' => 'float',
66
            ],
67
            'currency' => [
68
              'name' =>'currency',
69
              'type' => 'string',
70
            ],
71
        ];
72
73
        foreach ($valueMap as $callbackKey => $def) {
74
            if (!array_key_exists($callbackKey, $callbackBody)) {
75
                throw new Exception(
76
                    sprintf('Callback received without required key "%s"!', $callbackKey)
77
                );
78
            }
79
            $this->setProperty($def['name'], $callbackBody[$callbackKey], $def['type']);
80
        }
81
        if (!array_key_exists('auth_hash_ext', $callbackBody)
82
            || !$this->validateHash($callbackBody['auth_hash_ext'])
83
        ) {
84
            throw new Exception('Callback hash validation failed!');
85
        }
86
    }
87
88
    /**
89
     * Validate if the provided hash value matches the instance's callback details.
90
     *
91
     * @param string $hash The hash value.
92
     * @return boolean True if the hash value is valid.
93
     */
94
    protected function validateHash(string $hash): bool
95
    {
96
        $hashRecalc = hash_hmac(
97
            'sha512',
98
            sprintf(
99
                '%s|%s|%s|%s|%s',
100
                $this->gateway->getloyToken(),
101
                $this->transactionId,
102
                $this->amountPaid,
103
                $this->currency,
104
                $this->status
105
            ),
106
            $this->gateway->getloyToken()
107
        );
108
109
        return $hash === $hashRecalc;
110
    }
111
112
    /**
113
     * Convert and set property value.
114
     *
115
     * @param string $propName
116
     * @param string $value
117
     * @param string $type
118
     */
119
    protected function setProperty(string $propName, string $value, string $type = 'string')
120
    {
121
        switch ($type) {
122
            case 'float':
123
                $this->$propName = (float) $value;
124
                break;
125
126
            default:
127
                $this->$propName = $value;
128
        }
129
    }
130
131
    /**
132
     * Getter for transaction ID.
133
     *
134
     * @return string
135
     */
136
    public function transactionId(): string
137
    {
138
        return $this->transactionId;
139
    }
140
141
    /**
142
     * Getter for transaction status.
143
     *
144
     * @return string
145
     */
146
    public function status(): string
147
    {
148
        return $this->status;
149
    }
150
151
    /**
152
     * Getter for amount paid.
153
     *
154
     * @return float
155
     */
156
    public function amountPaid(): float
157
    {
158
        return $this->amountPaid;
159
    }
160
161
    /**
162
     * Getter for transaction currency.
163
     *
164
     * @return string
165
     */
166
    public function currency(): string
167
    {
168
        return $this->currency;
169
    }
170
}
171