-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathApiClient.php
More file actions
257 lines (216 loc) · 7.84 KB
/
Copy pathApiClient.php
File metadata and controls
257 lines (216 loc) · 7.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
<?php
declare(strict_types=1);
/**
* This file is part of the MultiFlexi package
*
* https://github.com/VitexSoftware/php-vitexsoftware-rbczpremiumapi
*
* (c) Vítězslav Dvořák <http://vitexsoftware.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace VitexSoftware\Raiffeisenbank;
use VitexSoftware\Raiffeisenbank\RateLimit\RateLimiter;
/**
* Description of ApiClient.
*
* @author vitex
*/
class ApiClient extends \GuzzleHttp\Client
{
/**
* ClientID obtained from Developer Portal - when you registered your app with us.
*/
protected string $xIBMClientId = '';
/**
* the end IP address of the client application (no server) in IPv4 or IPv6
* format. If the bank client (your user) uses a browser by which he
* accesses your server app, we need to know the IP address of his browser.
* Always provide the closest IP address to the real end-user possible.
* (optional).
*/
protected string $pSUIPAddress = '';
/**
* Use mocking for api calls ?
*/
protected bool $mockMode = false;
private RateLimiter $rateLimiter;
/**
* Initialize the API client with configuration, certificate validation, and a rate limiter.
*
* Accepted $config keys:
* - 'clientid': client ID from the Developer Portal.
* - 'cert': array with [pathToP12, password]; when omitted, CERT_FILE and CERT_PASS configuration values are used.
* - 'clientpubip': client public IP (nearest to the end user).
* - 'mocking': bool to enable mock endpoints.
* - 'debug': debug flag.
*
* @param array $config Client configuration.
* @throws \Exception If certificate file path (CERT_FILE) is not provided.
* @throws \Exception If certificate password (CERT_PASS) is not provided.
*/
public function __construct(array $config = [])
{
if (\array_key_exists('clientid', $config) === false) {
$this->xIBMClientId = \Ease\Shared::cfg('XIBMCLIENTID');
} else {
$this->xIBMClientId = $config['clientid'];
}
if (\array_key_exists('cert', $config) === false) {
$config['cert'] = [\Ease\Shared::cfg('CERT_FILE'), \Ease\Shared::cfg('CERT_PASS')];
if (empty($config['cert'][0])) {
throw new \Exception('Certificate (CERT_FILE) not specified');
}
if (empty($config['cert'][1])) {
throw new \Exception('Certificate password (CERT_PASS) not specified');
}
}
if (\array_key_exists('debug', $config) === false) {
$config['debug'] = \Ease\Shared::cfg('API_DEBUG', false);
}
if (\array_key_exists('clientpubip', $config)) {
$this->pSUIPAddress = $config['clientpubip'];
}
if (\array_key_exists('mocking', $config)) {
$this->mockMode = (bool) $config['mocking'];
}
$limitStore = new RateLimit\JsonRateLimitStore(sys_get_temp_dir().'/rbczpremiumapi_rates.json');
$this->rateLimiter = new RateLimiter($limitStore);
parent::__construct($config);
}
/**
* ClientID obtained from Developer Portal.
*
* @return string
*/
public function getXIBMClientId()
{
return $this->xIBMClientId;
}
/**
* Keep user public IP here.
*
* @return string
*/
public function getpSUIPAddress()
{
return $this->pSUIPAddress;
}
/**
* Use mocking uri for api calls ?
*
* @return bool
*/
public function getMockMode()
{
return $this->mockMode;
}
/**
* Obtain Your current Public IP.
*
* @deprecated since version 0.1 - Do not use in production Environment!
*
* @return string
*/
public static function getPublicIP()
{
$curl = curl_init();
curl_setopt($curl, \CURLOPT_URL, 'http://httpbin.org/ip');
curl_setopt($curl, \CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($curl);
curl_close($curl);
$ip = json_decode($output, true);
return $ip['origin'];
}
/**
* Source Identifier.
*
* @deprecated since version 0.1 - Do not use in production Environment!
*
* @return string
*/
public static function sourceString()
{
return substr(__FILE__.'@'.gethostname(), -50);
}
/**
* Try to check certificate readibilty.
*
* @param string $certFile path to certificate
* @param bool $die throw exception or return false ?
*
* @throws Exception - Certificate file not found
*
* @return bool certificate file
*/
public static function checkCertificatePresence(string $certFile, bool $die = false): bool
{
$found = false;
if ((file_exists($certFile) === false) || (is_readable($certFile) === false)) {
$errMsg = 'Cannot read specified certificate file: '.$certFile;
fwrite(\STDERR, $errMsg.\PHP_EOL);
if ($die) {
throw new \Exception($errMsg);
}
} else {
$found = true;
}
return $found;
}
public static function checkCertificate($certFile, $password): bool
{
return self::checkCertificatePresence($certFile) && self::checkCertificatePassword($certFile, $password);
}
public static function checkCertificatePassword(string $certFile, string $password): bool
{
$certContent = file_get_contents($certFile);
if (openssl_pkcs12_read($certContent, $certs, $password) === false) {
fwrite(\STDERR, 'Cannot read PKCS12 certificate file: '.$certFile.\PHP_EOL);
exit(1);
}
return true;
}
/**
* Produce a short request identifier used for diagnostics and testing.
*
* @deprecated since version 0.1 — Do not use in production environments.
*
* @return string The generated request identifier composed from a source token and the current timestamp, truncated to at most 59 characters.
*/
public static function getxRequestId()
{
return substr(self::sourceString().'#'.time(), -59);
}
/**
* Send an HTTP request while enforcing and updating client rate limits.
*
* @param \Psr\Http\Message\RequestInterface $request The HTTP request to send.
* @param array $options Request options to apply to the transfer. See \GuzzleHttp\RequestOptions.
* @return \Psr\Http\Message\ResponseInterface The HTTP response.
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws RateLimitExceededException If the client is rate limited and wait mode is disabled.
*/
public function send(\Psr\Http\Message\RequestInterface $request, array $options = []): \Psr\Http\Message\ResponseInterface
{
$this->rateLimiter->checkBeforeRequest($this->xIBMClientId);
$response = parent::send($request, $options);
$statusCode = $response->getStatusCode();
$responseHeaders = $response->getHeaders();
if (isset($responseHeaders['x-ratelimit-remaining-second'])) {
$remainingSecond = (int) $responseHeaders['x-ratelimit-remaining-second'][0];
$remainingDay = (int) $responseHeaders['x-ratelimit-remaining-day'][0];
$timestamp = time();
$this->rateLimiter->handleRateLimits($this->xIBMClientId, $remainingSecond, $remainingDay, $timestamp);
}
if ($statusCode === 429) { // 429 Too Many Requests
if ($this->rateLimiter->isWaitMode()) {
$this->rateLimiter->checkBeforeRequest($this->xIBMClientId);
$response = parent::send($request, $options);
} else {
throw new RateLimitExceededException('Rate limit exceeded (HTTP 429)');
}
}
return $response;
}
}