Response::paid()   A
last analyzed

Complexity

Conditions 5
Paths 11

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 0
cts 14
cp 0
rs 9.1928
c 0
b 0
f 0
cc 5
nc 11
nop 1
crap 30
1
<?php
2
3
namespace PaySys\CardPay\Security;
4
5
use Nette;
6
use Nette\Utils\Strings;
7
use PaySys\CardPay\Configuration;
8
9
10 1
final class Response
11
{
12 1
	use Nette\SmartObject;
13
14
	const PUBLIC_KEYS = "https://moja.tatrabanka.sk/e-commerce/ecdsa_keys.txt";
15
16
	/** @var callable[]  function (array $parameters); Occurs on response from bank */
17
	public $onResponse;
18
19
	/** @var callable[]  function (array $parameters); Occurs on success payment response from bank */
20
	public $onSuccess;
21
22
	/** @var callable[]  function (array $parameters); Occurs on fail payment response from bank */
23
	public $onFail;
24
25
	/** @var callable[]  function (array $parameters, \PaySys\PaySys\Exception $e); Occurs on damaged response from bank */
26
	public $onError;
27
28
	/** @var Configuration */
29
	protected $config;
30
31
32
	public function __construct(Configuration $config)
33
	{
34 1
		$this->config = $config;
35 1
	}
36
37
	public function paid(array $parameters) : bool
38
	{
39
		try {
40
			$this->checkParameters($parameters);
41
42
			if ($parameters['HMAC'] !== $this->getHmac($parameters))
43
				throw new \PaySys\PaySys\SignatureException('HMAC sign is not valid.');
44
45
46
			if (!$this->verified($parameters))
47
				throw new \PaySys\PaySys\SignatureException('ECDSA sign is not valid.');
48
49
			$this->onResponse($parameters);
0 ignored issues
show
Documentation Bug introduced by
The method onResponse does not exist on object<PaySys\CardPay\Security\Response>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
50
51
			if ($parameters['RES'] === 'OK') {
52
				$this->onSuccess($parameters);
0 ignored issues
show
Documentation Bug introduced by
The method onSuccess does not exist on object<PaySys\CardPay\Security\Response>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
53
				return TRUE;
54
			} else {
55
				$this->onFail($parameters);
0 ignored issues
show
Documentation Bug introduced by
The method onFail does not exist on object<PaySys\CardPay\Security\Response>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
56
				return FALSE;
57
			}
58
		} catch (\PaySys\PaySys\Exception $e) {
59
			$this->onError($parameters, $e);
0 ignored issues
show
Documentation Bug introduced by
The method onError does not exist on object<PaySys\CardPay\Security\Response>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
60
			throw $e;
61
		}
62
	}
63
64
	public function getSignString(array $parameters) : string
65
	{
66 1
		return $parameters['AMT']
67 1
			. $parameters['CURR']
68 1
			. $parameters['VS']
69 1
			. @$parameters['TXN']
70 1
			. $parameters['RES']
71 1
			. (($parameters['RES'] === 'OK') ? $parameters['AC'] : '')
72 1
			. @$parameters['TRES']
73 1
			. @$parameters['CID']
74 1
			. @$parameters['RC']
75 1
			. $parameters['TID']
76 1
			. $parameters['TIMESTAMP'];
77
	}
78
79
	public function getHmac(array $parameters) : string
80
	{
81 1
		return hash_hmac("sha256", $this->getSignString($parameters), $this->config->getKey());
82
	}
83
84
85
	public function verified(array $parameters) : bool
86
	{
87
		$verified = openssl_verify($this->getSignString($parameters) . $parameters['HMAC'], pack("H*", $parameters['ECDSA']), $this->getPublicKey($parameters['ECDSA_KEY']), "sha256");
88
89
		if ($verified === -1) {
90
			throw new \PaySys\PaySys\SignatureException(sprintf("Error while verify bank response: %s", openssl_error_string()));
91
		} else {
92
			return (bool) $verified;
93
		}
94
	}
95
96
	public function getPublicKey(int $id) : string
97
	{
98
		foreach (explode("\r\n\r\n", file_get_contents(self::PUBLIC_KEYS)) as $source) {
99
			preg_match('/KEY_ID: (\d+)/', $source, $tmp);
100
			$key_id = (int) $tmp[1];
101
102
			if ($key_id === $id) {
103
				if ((bool) Strings::match($source, '~VALID+~')) {
104
105
					preg_match_all("/-----BEGIN PUBLIC KEY-----(.*)-----END PUBLIC KEY-----/msU", $source, $key);
106
					return Strings::trim($key[0][0]);
107
108
				} else {
109
					throw new \PaySys\PaySys\ServerException(sprintf("Key '%d' was revoked.", $id));
110
				}
111
			}
112
		}
113
	}
114
115
	private function checkParameters(array & $parameters)
116
	{
117
		foreach (['AMT', 'CURR', 'VS', 'RES', 'TID', 'TIMESTAMP', 'HMAC', 'ECDSA_KEY', 'ECDSA'] as $key) {
118
			if (isset($parameters[$key])) {
119
				$parameters[$key] = Strings::trim($parameters[$key]);
120
			} else {
121
				throw new \PaySys\PaySys\ServerException(sprintf("Missing parameter '%s'.", $key));
122
			}
123
		}
124
	}
125
}
126