• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mlocati / nexi / 8551769251

04 Apr 2024 08:40AM UTC coverage: 2.179% (+2.2%) from 0.0%
8551769251

push

github

mlocati
Improve JSON generation, add ways to enable insecure HTTPS connections

14 of 153 new or added lines in 7 files covered. (9.15%)

2 existing lines in 2 files now uncovered.

37 of 1698 relevant lines covered (2.18%)

0.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Client.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace MLocati\Nexi;
6

7
use MLocati\Nexi\HttpClient\Response;
8
use MLocati\Nexi\Service\QueryEntityInterface;
9
use stdClass;
10

11
/*
12
 * WARNING: DO NOT EDIT THIS FILE
13
 * It has been generated automaticlly from a template.
14
 * Edit the template instead.
15
 */
16

17
class Client
18
{
19
    /**
20
     * @var \MLocati\Nexi\Configuration
21
     */
22
    protected $configuration;
23

24
    /**
25
     * @var \MLocati\Nexi\HttpClient
26
     */
27
    protected $httpClient;
28

29
    /**
30
     * @var \MLocati\Nexi\CorrelationProvider
31
     */
32
    protected $correlationProvider;
33

34
    /**
35
     * @var \MLocati\Nexi\Entity\Webhook\Request|null
36
     */
37
    private $notificationRequest;
38

39
    /**
40
     * @throws \MLocati\Nexi\Exception\NoHttpClient if $httpClient is NULL and no HTTP client is available
41
     */
42
    public function __construct(
43
        Configuration $configuration,
44
        ?HttpClient $httpClient = null,
45
        ?CorrelationProvider $correlationProvider = null
46
    ) {
47
        $this->configuration = $configuration;
×
48
        $this->httpClient = $httpClient ?? $this->buildHttpClient();
×
49
        $this->correlationProvider = $correlationProvider ?? $this->buildCorrelationProvider();
×
50
    }
×
51

52
    /**
53
     * @throws \MLocati\Nexi\Exception\InvalidJson is no (valid) request data is detected
54
     * @throws \MLocati\Nexi\Exception\MissingField is the received data does not contain a security token
55
     */
56
    public function getNotificationRequest(): Entity\Webhook\Request
57
    {
58
        if ($this->notificationRequest === null) {
×
NEW
59
            $data = $this->decodeJsonToObject(file_get_contents('php://input') ?: '');
×
60
            $notificationRequest = new Entity\Webhook\Request($data);
×
61
            if ((string) $notificationRequest->getSecurityToken() === '') {
×
62
                throw new Exception\MissingField('securityToken');
×
63
            }
64
            $this->notificationRequest = $notificationRequest;
×
65
        }
66

67
        return $this->notificationRequest;
×
68
    }
69

70
    /**
71
     * Initiating payment through the Hosted Payment Page solution, the API returns the URL to redirect the customer to complete the transaction. For more information, please refer to the dedicated page Hosted Payment Page.
72
     *
73
     * @see https://developer.nexi.it/en/api/post-orders-hpp
74
     *
75
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
76
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
77
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
78
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
79
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
80
     * @throws \MLocati\Nexi\Exception\ErrorResponse
81
     */
82
    public function createOrderForHostedPayment(Entity\CreateOrderForHostedPayment\Request $requestBody): Entity\CreateOrderForHostedPayment\Response
83
    {
84
        $url = $this->buildUrl('/orders/hpp');
×
85
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
86
        if ($response->getStatusCode() === 200) {
×
NEW
87
            $data = $this->decodeJsonToObject($response->getBody());
×
88

89
            return new Entity\CreateOrderForHostedPayment\Response($data);
×
90
        }
91
        $this->throwErrorResponse($response, [
×
92
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
93
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
94
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
95
        ]);
96
    }
×
97

98
    /**
99
     * Generate a link to share with the customer to execute a payment. For more information, please refer to the dedicated page Pay-by-Link.
100
     *
101
     * @see https://developer.nexi.it/en/api/post-orders-paybylink
102
     *
103
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
104
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
105
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
106
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
107
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
108
     * @throws \MLocati\Nexi\Exception\ErrorResponse
109
     */
110
    public function createOrderForPayByLink(Entity\CreateOrderForPayByLink\Request $requestBody): Entity\PayByLinkResponse
111
    {
112
        $url = $this->buildUrl('/orders/paybylink');
×
113
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
114
        if ($response->getStatusCode() === 200) {
×
NEW
115
            $data = $this->decodeJsonToObject($response->getBody());
×
116

117
            return new Entity\PayByLinkResponse($data);
×
118
        }
119
        $this->throwErrorResponse($response, [
×
120
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
121
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
122
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
123
        ]);
124
    }
×
125

126
    /**
127
     * Cancel a Pay-By-Link that has not yet been paid. For more information on the Pay-by-Link solution, please refer to the dedicated page Pay-by-Link.
128
     *
129
     * @param string $linkId Unpaid Pay-By-Link ID to be canceled.
130
     *
131
     * @see https://developer.nexi.it/en/api/post-paybylink-linkId-cancels
132
     *
133
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
134
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
135
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Request rejected (HTTP code: 400)
136
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
137
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
138
     * @throws \MLocati\Nexi\Exception\ErrorResponse
139
     *
140
     * @return bool returns FALSE if not found
141
     */
142
    public function cancelPayByLink(string $linkId): bool
143
    {
144
        $url = $this->buildUrl('/paybylink/{linkId}/cancels', ['linkId' => $linkId]);
×
145
        $response = $this->invoke('POST', $url, 3);
×
146
        if ($response->getStatusCode() === 200) {
×
147
            return true;
×
148
        }
149
        if ($response->getStatusCode() === 404) {
×
150
            return false;
×
151
        }
152
        $this->throwErrorResponse($response, [
×
153
            ['from' => 400, 'to' => 400, 'description' => 'Request rejected', 'detailed' => true],
×
154
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
155
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
156
        ]);
157
    }
×
158

159
    /**
160
     * Verify registration with the 3DS protocol. This operation initiates the 2-step Server-to-Server payment flow. For more information, please refer to the dedicated page Payment 2 Steps.
161
     *
162
     * @see https://developer.nexi.it/en/api/post-orders-2steps-init
163
     *
164
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
165
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
166
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
167
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
168
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
169
     * @throws \MLocati\Nexi\Exception\ErrorResponse
170
     */
171
    public function twoSteps3DSInit(Entity\MultiStepInitRequest $requestBody): Entity\MultiStepInitResponse
172
    {
173
        $url = $this->buildUrl('/orders/2steps/init');
×
174
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
175
        if ($response->getStatusCode() === 200) {
×
NEW
176
            $data = $this->decodeJsonToObject($response->getBody());
×
177

178
            return new Entity\MultiStepInitResponse($data);
×
179
        }
180
        $this->throwErrorResponse($response, [
×
181
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
182
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
183
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
184
        ]);
185
    }
×
186

187
    /**
188
     * Payment Authorization. This completes the 2-step Server-to-Server payment flow. For more information, refer to the dedicated page Pagamento 2 Steps.
189
     *
190
     * @see https://developer.nexi.it/en/api/post-orders-2steps-payment
191
     *
192
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
193
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
194
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
195
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
196
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
197
     * @throws \MLocati\Nexi\Exception\ErrorResponse
198
     */
199
    public function twoSteps3DSPayment(Entity\MultiStepPaymentRequest $requestBody): Entity\OperationResult
200
    {
201
        $url = $this->buildUrl('/orders/2steps/payment');
×
202
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
203
        if ($response->getStatusCode() === 200) {
×
NEW
204
            $data = $this->decodeJsonToObject($response->getBody());
×
205

206
            return new Entity\OperationResult($data);
×
207
        }
208
        $this->throwErrorResponse($response, [
×
209
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
210
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
211
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
212
        ]);
213
    }
×
214

215
    /**
216
     * Verify registration with the 3DS protocol. This operation initiates the 3-step Server-to-Server payment flow. For more information, please refer to the dedicated page Payment 3 Steps.
217
     *
218
     * @see https://developer.nexi.it/en/api/post-orders-3steps-init
219
     *
220
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
221
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
222
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
223
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
224
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
225
     * @throws \MLocati\Nexi\Exception\ErrorResponse
226
     */
227
    public function threeSteps3DSInit(Entity\MultiStepInitRequest $requestBody): Entity\MultiStepInitResponse
228
    {
229
        $url = $this->buildUrl('/orders/3steps/init');
×
230
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
231
        if ($response->getStatusCode() === 200) {
×
NEW
232
            $data = $this->decodeJsonToObject($response->getBody());
×
233

234
            return new Entity\MultiStepInitResponse($data);
×
235
        }
236
        $this->throwErrorResponse($response, [
×
237
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
238
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
239
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
240
        ]);
241
    }
×
242

243
    /**
244
     * Validation of the 3DS authentication outcome. This operation is required during the 3-step Server-to-Server payment flow. For more information, please refer to the dedicated page Payment 3 Steps.
245
     *
246
     * @see https://developer.nexi.it/en/api/post-orders-3steps-validation
247
     *
248
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
249
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
250
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
251
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
252
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
253
     * @throws \MLocati\Nexi\Exception\ErrorResponse
254
     */
255
    public function threeSteps3DSValidation(Entity\ThreeSteps3DSValidation\Request $requestBody): Entity\ThreeSteps3DSValidation\Response
256
    {
257
        $url = $this->buildUrl('/orders/3steps/validation');
×
258
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
259
        if ($response->getStatusCode() === 200) {
×
NEW
260
            $data = $this->decodeJsonToObject($response->getBody());
×
261

262
            return new Entity\ThreeSteps3DSValidation\Response($data);
×
263
        }
264
        $this->throwErrorResponse($response, [
×
265
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
266
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
267
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
268
        ]);
269
    }
×
270

271
    /**
272
     * Payment authorization. This operation completes the 3-step Server-to-Server payment flow. For more information, please refer to the dedicated page Payment 3 Steps.
273
     *
274
     * @see https://developer.nexi.it/en/api/post-orders-3steps-payment
275
     *
276
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
277
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
278
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
279
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
280
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
281
     * @throws \MLocati\Nexi\Exception\ErrorResponse
282
     */
283
    public function threeSteps3DSPayment(Entity\MultiStepPaymentRequest $requestBody): Entity\OperationResult
284
    {
285
        $url = $this->buildUrl('/orders/3steps/payment');
×
286
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
287
        if ($response->getStatusCode() === 200) {
×
NEW
288
            $data = $this->decodeJsonToObject($response->getBody());
×
289

290
            return new Entity\OperationResult($data);
×
291
        }
292
        $this->throwErrorResponse($response, [
×
293
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
294
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
295
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
296
        ]);
297
    }
×
298

299
    /**
300
     * MIT (Merchant Initiated Transaction) payment, a recurring payment method used in services such as subscriptions. For more information, please refer to the dedicated page Recurring Payments.
301
     *
302
     * @param string $idempotencyKey an identifier of the request (to be used on subsequent retries); if empty, it will be set as output
303
     *
304
     * @see https://developer.nexi.it/en/api/post-orders-mit
305
     *
306
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
307
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
308
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
309
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
310
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
311
     * @throws \MLocati\Nexi\Exception\ErrorResponse
312
     */
313
    public function createOrderForMerchantInitiatedTransaction(Entity\CreateOrderForMerchantInitiatedTransaction\Request $requestBody, string &$idempotencyKey = ''): Entity\OperationResult
314
    {
315
        $url = $this->buildUrl('/orders/mit');
×
316
        $response = $this->invoke('POST', $url, 7, $requestBody, $idempotencyKey);
×
317
        if ($response->getStatusCode() === 200) {
×
NEW
318
            $data = $this->decodeJsonToObject($response->getBody());
×
319

320
            return new Entity\OperationResult($data);
×
321
        }
322
        $this->throwErrorResponse($response, [
×
323
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
324
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
325
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
326
        ]);
327
    }
×
328

329
    /**
330
     * Card verification operation, without any charge, with the sole purpose of confirming the validity of the card data entered by the customer.This API is a feature dedicated to the Server to Server integration mode, and is therefore subject to obtaining PCI DSS security certification (SAQ D questionnaire).This API is not subject to 3D Secure authentication.
331
     *
332
     * @see https://developer.nexi.it/en/api/post-orders-card_verification
333
     *
334
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
335
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
336
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
337
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
338
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
339
     * @throws \MLocati\Nexi\Exception\ErrorResponse
340
     */
341
    public function cardVerification(Entity\CardVerification\Request $requestBody): Entity\OperationResult
342
    {
343
        $url = $this->buildUrl('/orders/card_verification');
×
344
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
345
        if ($response->getStatusCode() === 200) {
×
NEW
346
            $data = $this->decodeJsonToObject($response->getBody());
×
347

348
            return new Entity\OperationResult($data);
×
349
        }
350
        $this->throwErrorResponse($response, [
×
351
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
352
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
353
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
354
        ]);
355
    }
×
356

357
    /**
358
     * Server-to-Server MOTO payment. For more information, please refer to the dedicated page M.O.T.O..
359
     *
360
     * @see https://developer.nexi.it/en/api/post-orders-moto
361
     *
362
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
363
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
364
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
365
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
366
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
367
     * @throws \MLocati\Nexi\Exception\ErrorResponse
368
     */
369
    public function createOrderForMotoPayment(Entity\CreateOrderForMotoPayment\Request $requestBody): Entity\OperationResult
370
    {
371
        $url = $this->buildUrl('/orders/moto');
×
372
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
373
        if ($response->getStatusCode() === 200) {
×
NEW
374
            $data = $this->decodeJsonToObject($response->getBody());
×
375

376
            return new Entity\OperationResult($data);
×
377
        }
378
        $this->throwErrorResponse($response, [
×
379
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
380
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
381
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
382
        ]);
383
    }
×
384

385
    /**
386
     * Server to Server payment (PCI certification required) via virtual card.
387
     *
388
     * @see https://developer.nexi.it/en/api/post-orders-virtual_card
389
     *
390
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
391
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
392
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
393
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
394
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
395
     * @throws \MLocati\Nexi\Exception\ErrorResponse
396
     */
397
    public function createVirtualCartOrder(Entity\CreateVirtualCartOrder\Request $requestBody): Entity\OperationResult
398
    {
399
        $url = $this->buildUrl('/orders/virtual_card');
×
400
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
401
        if ($response->getStatusCode() === 200) {
×
NEW
402
            $data = $this->decodeJsonToObject($response->getBody());
×
403

404
            return new Entity\OperationResult($data);
×
405
        }
406
        $this->throwErrorResponse($response, [
×
407
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
408
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
409
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
410
        ]);
411
    }
×
412

413
    /**
414
     * Create an order and initiate the payment using the XPay Build solution.
415
     * WARNING: for the proper functioning of XPay Build solution, it is necessary for the browser used to allow the use of third-party cookies.
416
     *
417
     * @see https://developer.nexi.it/en/api/post-orders-build
418
     *
419
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
420
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
421
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
422
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
423
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
424
     * @throws \MLocati\Nexi\Exception\ErrorResponse
425
     */
426
    public function createXPayBuildOrder(Entity\CreateXPayBuildOrder\Request $requestBody): Entity\FieldSet
427
    {
428
        $url = $this->buildUrl('/orders/build');
×
429
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
430
        if ($response->getStatusCode() === 200) {
×
NEW
431
            $data = $this->decodeJsonToObject($response->getBody());
×
432

433
            return new Entity\FieldSet($data);
×
434
        }
435
        $this->throwErrorResponse($response, [
×
436
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
437
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
438
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
439
        ]);
440
    }
×
441

442
    /**
443
     * Conclude a payment using the XPay Build solution.
444
     *
445
     * @see https://developer.nexi.it/en/api/post-build-finalize-payment
446
     *
447
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
448
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
449
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
450
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
451
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
452
     * @throws \MLocati\Nexi\Exception\ErrorResponse
453
     */
454
    public function finalizeXPayBuildOrder(Entity\Session $requestBody): Entity\FinalizeXPayBuildOrder\Response
455
    {
456
        $url = $this->buildUrl('/build/finalize_payment');
×
457
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
458
        if ($response->getStatusCode() === 200) {
×
NEW
459
            $data = $this->decodeJsonToObject($response->getBody());
×
460

461
            return new Entity\FinalizeXPayBuildOrder\Response($data);
×
462
        }
463
        $this->throwErrorResponse($response, [
×
464
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
465
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
466
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
467
        ]);
468
    }
×
469

470
    /**
471
     * Cancel a payment session XPay Build. Once a session is invalidated, any further submissions of requests related to the canceled session are destined to fail. Therefore, it will be necessary to invoke the API POST /orders/build for the generation of a new sessionId. This API can be applied to a payment cancellation button placed in the merchant's checkout: it is not mandatory to implement the API; in case it is not used, any session will be invalidated upon session expiration (5 minutes).
472
     *
473
     * @see https://developer.nexi.it/en/api/post-build-cancel
474
     *
475
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
476
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
477
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
478
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
479
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
480
     * @throws \MLocati\Nexi\Exception\ErrorResponse
481
     */
482
    public function cancelXPayBuildOrder(Entity\Session $requestBody): Entity\CancelXPayBuildOrder\Response
483
    {
484
        $url = $this->buildUrl('/build/cancel');
×
485
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
486
        if ($response->getStatusCode() === 200) {
×
NEW
487
            $data = $this->decodeJsonToObject($response->getBody());
×
488

489
            return new Entity\CancelXPayBuildOrder\Response($data);
×
490
        }
491
        $this->throwErrorResponse($response, [
×
492
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
493
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
494
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
495
        ]);
496
    }
×
497

498
    /**
499
     * Get current payment status via XPay Build solution.
500
     *
501
     * @see https://developer.nexi.it/en/api/get-build-state
502
     *
503
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
504
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
505
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
506
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
507
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
508
     * @throws \MLocati\Nexi\Exception\ErrorResponse
509
     */
510
    public function getXPayBuildOrderStatus(?Entity\Session $query = null): Entity\GetXPayBuildOrderStatus\Response
511
    {
512
        $url = $this->buildUrl('/build/state', [], $query);
×
513
        $response = $this->invoke('GET', $url, 3);
×
514
        if ($response->getStatusCode() === 200) {
×
NEW
515
            $data = $this->decodeJsonToObject($response->getBody());
×
516

517
            return new Entity\GetXPayBuildOrderStatus\Response($data);
×
518
        }
519
        $this->throwErrorResponse($response, [
×
520
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
521
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
522
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
523
        ]);
524
    }
×
525

526
    /**
527
     * List of orders in reverse chronological order. For more information about orders, please refer to the dedicated page Orders.
528
     *
529
     * @see https://developer.nexi.it/en/api/get-orders
530
     *
531
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
532
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
533
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
534
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
535
     * @throws \MLocati\Nexi\Exception\ErrorResponse
536
     */
537
    public function findOrders(?Entity\FindOrders\Query $query = null): Entity\FindOrders\Response
538
    {
539
        $url = $this->buildUrl('/orders', [], $query);
×
540
        $response = $this->invoke('GET', $url, 3);
×
541
        if ($response->getStatusCode() === 200) {
×
NEW
542
            $data = $this->decodeJsonToObject($response->getBody());
×
543

544
            return new Entity\FindOrders\Response($data);
×
545
        }
546
        $this->throwErrorResponse($response, [
×
547
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
548
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
549
        ]);
550
    }
×
551

552
    /**
553
     * Searches for an order and returns its details. For more information about orders, please refer to the dedicated page Orders.
554
     *
555
     * @param string $orderId Identification code sent during the payment initialization phase (it must be unique).
556
     *
557
     * @see https://developer.nexi.it/en/api/get-orders-orderId
558
     *
559
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
560
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
561
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
562
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
563
     * @throws \MLocati\Nexi\Exception\ErrorResponse
564
     *
565
     * @return \MLocati\Nexi\Entity\FindOrderById\Response|null returns NULL if not found
566
     */
567
    public function findOrderById(string $orderId): ?Entity\FindOrderById\Response
568
    {
569
        $url = $this->buildUrl('/orders/{orderId}', ['orderId' => $orderId]);
×
570
        $response = $this->invoke('GET', $url, 3);
×
571
        if ($response->getStatusCode() === 200) {
×
NEW
572
            $data = $this->decodeJsonToObject($response->getBody());
×
573

574
            return new Entity\FindOrderById\Response($data);
×
575
        }
576
        if ($response->getStatusCode() === 404) {
×
577
            return null;
×
578
        }
579
        $this->throwErrorResponse($response, [
×
580
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
581
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
582
        ]);
583
    }
×
584

585
    /**
586
     * List of all operations created within the specified time range. For more information on operations, refer to the dedicated page Operations.
587
     *
588
     * @see https://developer.nexi.it/en/api/get-operations
589
     *
590
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
591
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
592
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
593
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
594
     * @throws \MLocati\Nexi\Exception\ErrorResponse
595
     */
596
    public function findOperations(?Entity\FindOperations\Query $query = null): Entity\FindOperations\Response
597
    {
598
        $url = $this->buildUrl('/operations', [], $query);
×
599
        $response = $this->invoke('GET', $url, 3);
×
600
        if ($response->getStatusCode() === 200) {
×
NEW
601
            $data = $this->decodeJsonToObject($response->getBody());
×
602

603
            return new Entity\FindOperations\Response($data);
×
604
        }
605
        $this->throwErrorResponse($response, [
×
606
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
607
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
608
        ]);
609
    }
×
610

611
    /**
612
     * Searches for an operation and returns its details. For more information on operations, please refer to the dedicated page Operations.
613
     *
614
     * @param string $operationId Identification code of the operation.
615
     *
616
     * @see https://developer.nexi.it/en/api/get-operations-operationId
617
     *
618
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
619
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
620
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
621
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
622
     * @throws \MLocati\Nexi\Exception\ErrorResponse
623
     *
624
     * @return \MLocati\Nexi\Entity\Operation|null returns NULL if not found
625
     */
626
    public function findOperationById(string $operationId): ?Entity\Operation
627
    {
628
        $url = $this->buildUrl('/operations/{operationId}', ['operationId' => $operationId]);
×
629
        $response = $this->invoke('GET', $url, 3);
×
630
        if ($response->getStatusCode() === 200) {
×
NEW
631
            $data = $this->decodeJsonToObject($response->getBody());
×
632

633
            return new Entity\Operation($data);
×
634
        }
635
        if ($response->getStatusCode() === 404) {
×
636
            return null;
×
637
        }
638
        $this->throwErrorResponse($response, [
×
639
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
640
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
641
        ]);
642
    }
×
643

644
    /**
645
     * Retrieve the allowed actions associated to an operation and the range of value acceptable. For more information on operations, please refer to the dedicated page Operations.
646
     *
647
     * @param string $operationId Operation id
648
     *
649
     * @see https://developer.nexi.it/en/api/get-operations-operationId-actions
650
     *
651
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
652
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
653
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
654
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
655
     * @throws \MLocati\Nexi\Exception\ErrorResponse
656
     *
657
     * @return \MLocati\Nexi\Entity\GetOperationActions\Response|null returns NULL if not found
658
     */
659
    public function getOperationActions(string $operationId): ?Entity\GetOperationActions\Response
660
    {
661
        $url = $this->buildUrl('/operations/{operationId}/actions', ['operationId' => $operationId]);
×
662
        $response = $this->invoke('GET', $url, 3);
×
663
        if ($response->getStatusCode() === 200) {
×
NEW
664
            $data = $this->decodeJsonToObject($response->getBody());
×
665

666
            return new Entity\GetOperationActions\Response($data);
×
667
        }
668
        if ($response->getStatusCode() === 404) {
×
669
            return null;
×
670
        }
671
        $this->throwErrorResponse($response, [
×
672
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
673
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
674
        ]);
675
    }
×
676

677
    /**
678
     * Partial/total refund or cancellation based on the order status. For more information on operations, please refer to the dedicated page Operations.
679
     *
680
     * @param string $operationId Identification code of the operation.
681
     * @param string $idempotencyKey an identifier of the request (to be used on subsequent retries); if empty, it will be set as output
682
     *
683
     * @see https://developer.nexi.it/en/api/post-operations-operationId-refunds
684
     *
685
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
686
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
687
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Request rejected (HTTP code: 400)
688
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
689
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
690
     * @throws \MLocati\Nexi\Exception\ErrorResponse
691
     *
692
     * @return \MLocati\Nexi\Entity\OperationInfo|null returns NULL if not found
693
     */
694
    public function refund(string $operationId, Entity\AmountWithDescription $requestBody, string &$idempotencyKey = ''): ?Entity\OperationInfo
695
    {
696
        $url = $this->buildUrl('/operations/{operationId}/refunds', ['operationId' => $operationId]);
×
697
        $response = $this->invoke('POST', $url, 7, $requestBody, $idempotencyKey);
×
698
        if ($response->getStatusCode() === 200) {
×
NEW
699
            $data = $this->decodeJsonToObject($response->getBody());
×
700

701
            return new Entity\OperationInfo($data);
×
702
        }
703
        if ($response->getStatusCode() === 404) {
×
704
            return null;
×
705
        }
706
        $this->throwErrorResponse($response, [
×
707
            ['from' => 400, 'to' => 400, 'description' => 'Request rejected', 'detailed' => true],
×
708
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
709
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
710
        ]);
711
    }
×
712

713
    /**
714
     * Partial or total accounting. For more information on operations, refer to the dedicated page Operations..
715
     *
716
     * @param string $operationId Identification code of the operation.
717
     * @param string $idempotencyKey an identifier of the request (to be used on subsequent retries); if empty, it will be set as output
718
     *
719
     * @see https://developer.nexi.it/en/api/post-operations-operationId-captures
720
     *
721
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
722
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
723
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Request rejected (HTTP code: 400)
724
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
725
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
726
     * @throws \MLocati\Nexi\Exception\ErrorResponse
727
     *
728
     * @return \MLocati\Nexi\Entity\OperationInfo|null returns NULL if not found
729
     */
730
    public function capture(string $operationId, Entity\AmountWithDescription $requestBody, string &$idempotencyKey = ''): ?Entity\OperationInfo
731
    {
732
        $url = $this->buildUrl('/operations/{operationId}/captures', ['operationId' => $operationId]);
×
733
        $response = $this->invoke('POST', $url, 7, $requestBody, $idempotencyKey);
×
734
        if ($response->getStatusCode() === 200) {
×
NEW
735
            $data = $this->decodeJsonToObject($response->getBody());
×
736

737
            return new Entity\OperationInfo($data);
×
738
        }
739
        if ($response->getStatusCode() === 404) {
×
740
            return null;
×
741
        }
742
        $this->throwErrorResponse($response, [
×
743
            ['from' => 400, 'to' => 400, 'description' => 'Request rejected', 'detailed' => true],
×
744
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
745
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
746
        ]);
747
    }
×
748

749
    /**
750
     * Cancels a accounting operation. Does not apply to any other type of operation. For more information about operations, please refer to the dedicated page Operations.
751
     *
752
     * @param string $operationId Operation id of a capture operation to be cancelled.
753
     *
754
     * @see https://developer.nexi.it/en/api/post-operations-operationId-cancels
755
     *
756
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
757
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
758
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Request rejected (HTTP code: 400)
759
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
760
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
761
     * @throws \MLocati\Nexi\Exception\ErrorResponse
762
     *
763
     * @return \MLocati\Nexi\Entity\OperationInfo|null returns NULL if not found
764
     */
765
    public function cancel(string $operationId, Entity\Cancel\Request $requestBody): ?Entity\OperationInfo
766
    {
767
        $url = $this->buildUrl('/operations/{operationId}/cancels', ['operationId' => $operationId]);
×
768
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
769
        if ($response->getStatusCode() === 200) {
×
NEW
770
            $data = $this->decodeJsonToObject($response->getBody());
×
771

772
            return new Entity\OperationInfo($data);
×
773
        }
774
        if ($response->getStatusCode() === 404) {
×
775
            return null;
×
776
        }
777
        $this->throwErrorResponse($response, [
×
778
            ['from' => 400, 'to' => 400, 'description' => 'Request rejected', 'detailed' => true],
×
779
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
780
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
781
        ]);
782
    }
×
783

784
    /**
785
     * Search for contracts using the customer's identification. For more information on contracts, refer to the dedicated Contracts page.
786
     *
787
     * @param string $customerId Customer identification code.
788
     *
789
     * @see https://developer.nexi.it/en/api/get-contracts-customers-customerId
790
     *
791
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
792
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
793
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
794
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
795
     * @throws \MLocati\Nexi\Exception\ErrorResponse
796
     *
797
     * @return \MLocati\Nexi\Entity\FindRecurringContractsByCustomerId\Response|null returns NULL if not found
798
     */
799
    public function findRecurringContractsByCustomerId(string $customerId): ?Entity\FindRecurringContractsByCustomerId\Response
800
    {
801
        $url = $this->buildUrl('/contracts/customers/{customerId}', ['customerId' => $customerId]);
×
802
        $response = $this->invoke('GET', $url, 3);
×
803
        if ($response->getStatusCode() === 200) {
×
NEW
804
            $data = $this->decodeJsonToObject($response->getBody());
×
805

806
            return new Entity\FindRecurringContractsByCustomerId\Response($data);
×
807
        }
808
        if ($response->getStatusCode() === 404) {
×
809
            return null;
×
810
        }
811
        $this->throwErrorResponse($response, [
×
812
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
813
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
814
        ]);
815
    }
×
816

817
    /**
818
     * Disable a contract. For more information on contracts, please refer to the dedicated Contracts page.
819
     *
820
     * @param string $contractId Recurring contract ID.
821
     *
822
     * @see https://developer.nexi.it/en/api/post-contracts-contractId-deactivation
823
     *
824
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
825
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
826
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
827
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
828
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
829
     * @throws \MLocati\Nexi\Exception\ErrorResponse
830
     */
831
    public function disableContract(string $contractId): void
832
    {
833
        $url = $this->buildUrl('/contracts/{contractId}/deactivation', ['contractId' => $contractId]);
×
834
        $response = $this->invoke('POST', $url, 3);
×
835
        if ($response->getStatusCode() === 200) {
×
836
            return;
×
837
        }
838
        $this->throwErrorResponse($response, [
×
839
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
840
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
841
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
842
        ]);
843
    }
×
844

845
    /**
846
     * List of payment methods supported by the merchant terminal along with associated attributes.
847
     *
848
     * @see https://developer.nexi.it/en/api/post-payment_methods
849
     *
850
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
851
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
852
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
853
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
854
     * @throws \MLocati\Nexi\Exception\ErrorResponse
855
     */
856
    public function listSupportedPaymentMethods(): Entity\ListSupportedPaymentMethods\Response
857
    {
858
        $url = $this->buildUrl('/payment_methods');
×
859
        $response = $this->invoke('GET', $url, 3);
×
860
        if ($response->getStatusCode() === 200) {
×
NEW
861
            $data = $this->decodeJsonToObject($response->getBody());
×
862

863
            return new Entity\ListSupportedPaymentMethods\Response($data);
×
864
        }
865
        $this->throwErrorResponse($response, [
×
866
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
867
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
868
        ]);
869
    }
×
870

871
    /**
872
     * Create Terms and Condition for orders.
873
     *
874
     * @see https://developer.nexi.it/en/api/post-termsAndConditions
875
     *
876
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
877
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
878
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
879
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
880
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
881
     * @throws \MLocati\Nexi\Exception\ErrorResponse
882
     */
883
    public function createTermsAndConditions(Entity\CreateTermsAndConditions\Request $requestBody): Entity\CreateTermsAndConditions\Response
884
    {
885
        $url = $this->buildUrl('/termsAndConditions');
×
886
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
887
        if ($response->getStatusCode() === 200) {
×
NEW
888
            $data = $this->decodeJsonToObject($response->getBody());
×
889

890
            return new Entity\CreateTermsAndConditions\Response($data);
×
891
        }
892
        $this->throwErrorResponse($response, [
×
893
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
894
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
895
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
896
        ]);
897
    }
×
898

899
    /**
900
     * Retrieve one Terms and Conditions details.
901
     *
902
     * @param string $termsAndConditionsId Terms and Conditions ID.
903
     *
904
     * @see https://developer.nexi.it/en/api/get-terms_and_conditions-termsAndConditionsId
905
     *
906
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
907
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
908
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
909
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
910
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
911
     * @throws \MLocati\Nexi\Exception\ErrorResponse
912
     */
913
    public function findTermsAndConditionsById(string $termsAndConditionsId): Entity\FindTermsAndConditionsById\Response
914
    {
915
        $url = $this->buildUrl('/terms_and_conditions/{termsAndConditionsId}', ['termsAndConditionsId' => $termsAndConditionsId]);
×
916
        $response = $this->invoke('GET', $url, 3);
×
917
        if ($response->getStatusCode() === 200) {
×
NEW
918
            $data = $this->decodeJsonToObject($response->getBody());
×
919

920
            return new Entity\FindTermsAndConditionsById\Response($data);
×
921
        }
922
        $this->throwErrorResponse($response, [
×
923
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
924
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
925
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
926
        ]);
927
    }
×
928

929
    /**
930
     * Incremental of a pre-authorization.
931
     *
932
     * @param string $idempotencyKey an identifier of the request (to be used on subsequent retries); if empty, it will be set as output
933
     *
934
     * @see https://developer.nexi.it/en/api/post-incrementals
935
     *
936
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
937
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
938
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
939
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
940
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
941
     * @throws \MLocati\Nexi\Exception\ErrorResponse
942
     */
943
    public function incrementOrder(Entity\ChangeAmountRequest $requestBody, string &$idempotencyKey = ''): Entity\OperationResult
944
    {
945
        $url = $this->buildUrl('/incrementals');
×
946
        $response = $this->invoke('POST', $url, 7, $requestBody, $idempotencyKey);
×
947
        if ($response->getStatusCode() === 200) {
×
NEW
948
            $data = $this->decodeJsonToObject($response->getBody());
×
949

950
            return new Entity\OperationResult($data);
×
951
        }
952
        $this->throwErrorResponse($response, [
×
953
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
954
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
955
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
956
        ]);
957
    }
×
958

959
    /**
960
     * Cancel a T&C not associated to any operation.
961
     *
962
     * @param string $termsAndConditionsId Terms and Conditions ID.
963
     *
964
     * @see https://developer.nexi.it/en/api/post-terms_and_conditions-termsAndConditionsId-cancels
965
     *
966
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
967
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
968
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Request rejected (HTTP code: 400)
969
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
970
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
971
     * @throws \MLocati\Nexi\Exception\ErrorResponse
972
     *
973
     * @return bool returns FALSE if not found
974
     */
975
    public function cancelTermsAndConditions(string $termsAndConditionsId): bool
976
    {
977
        $url = $this->buildUrl('/terms_and_conditions/{termsAndConditionsId}/cancels', ['termsAndConditionsId' => $termsAndConditionsId]);
×
978
        $response = $this->invoke('POST', $url, 3);
×
979
        if ($response->getStatusCode() === 200) {
×
980
            return true;
×
981
        }
982
        if ($response->getStatusCode() === 404) {
×
983
            return false;
×
984
        }
985
        $this->throwErrorResponse($response, [
×
986
            ['from' => 400, 'to' => 400, 'description' => 'Request rejected', 'detailed' => true],
×
987
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
988
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
989
        ]);
990
    }
×
991

992
    /**
993
     * Possibility of making additional charges for example for a customer who has used the mini bar or has caused damage to the property.
994
     *
995
     * @param string $idempotencyKey an identifier of the request (to be used on subsequent retries); if empty, it will be set as output
996
     *
997
     * @see https://developer.nexi.it/en/api/post-delay_charges
998
     *
999
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1000
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1001
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1002
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1003
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1004
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1005
     */
1006
    public function delayedCharge(Entity\ChangeAmountRequest $requestBody, string &$idempotencyKey = ''): Entity\OperationResult
1007
    {
1008
        $url = $this->buildUrl('/delay_charges');
×
1009
        $response = $this->invoke('POST', $url, 7, $requestBody, $idempotencyKey);
×
1010
        if ($response->getStatusCode() === 200) {
×
NEW
1011
            $data = $this->decodeJsonToObject($response->getBody());
×
1012

1013
            return new Entity\OperationResult($data);
×
1014
        }
1015
        $this->throwErrorResponse($response, [
×
1016
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1017
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1018
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1019
        ]);
1020
    }
×
1021

1022
    /**
1023
     * If the customer does not show up and the reservation has not been canceled within the defined terms and conditions, you can charge the card for the amount corresponding to an overnight stay.
1024
     *
1025
     * @param string $idempotencyKey an identifier of the request (to be used on subsequent retries); if empty, it will be set as output
1026
     *
1027
     * @see https://developer.nexi.it/en/api/post-no_shows
1028
     *
1029
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1030
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1031
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1032
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1033
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1034
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1035
     */
1036
    public function noShowCharge(Entity\ChangeAmountRequest $requestBody, string &$idempotencyKey = ''): Entity\OperationResult
1037
    {
1038
        $url = $this->buildUrl('/no_shows');
×
1039
        $response = $this->invoke('POST', $url, 7, $requestBody, $idempotencyKey);
×
1040
        if ($response->getStatusCode() === 200) {
×
NEW
1041
            $data = $this->decodeJsonToObject($response->getBody());
×
1042

1043
            return new Entity\OperationResult($data);
×
1044
        }
1045
        $this->throwErrorResponse($response, [
×
1046
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1047
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1048
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1049
        ]);
1050
    }
×
1051

1052
    /**
1053
     * Create reservation.
1054
     *
1055
     * @see https://developer.nexi.it/en/api/post-reservations
1056
     *
1057
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1058
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1059
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1060
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1061
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1062
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1063
     */
1064
    public function createReservation(Entity\CreateReservation\Request $requestBody): Entity\CreateReservation\Response
1065
    {
1066
        $url = $this->buildUrl('/reservations');
×
1067
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
1068
        if ($response->getStatusCode() === 200) {
×
NEW
1069
            $data = $this->decodeJsonToObject($response->getBody());
×
1070

1071
            return new Entity\CreateReservation\Response($data);
×
1072
        }
1073
        $this->throwErrorResponse($response, [
×
1074
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1075
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1076
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1077
        ]);
1078
    }
×
1079

1080
    /**
1081
     * Find reservations.
1082
     *
1083
     * @see https://developer.nexi.it/en/api/get-reservations
1084
     *
1085
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1086
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1087
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1088
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1089
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1090
     */
1091
    public function findReservations(?Entity\FindReservations\Query $query = null): Entity\FindReservations\Response
1092
    {
1093
        $url = $this->buildUrl('/reservations', [], $query);
×
1094
        $response = $this->invoke('GET', $url, 3);
×
1095
        if ($response->getStatusCode() === 200) {
×
NEW
1096
            $data = $this->decodeJsonToObject($response->getBody());
×
1097

1098
            return new Entity\FindReservations\Response($data);
×
1099
        }
1100
        $this->throwErrorResponse($response, [
×
1101
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
1102
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1103
        ]);
1104
    }
×
1105

1106
    /**
1107
     * Generate PayByLink for a specific reservation. In case of the first payment for the reservation, a contractId is created. In case of the following payment the existing reservation contractId is used.
1108
     *
1109
     * @param string $reservationId Merchant reservation id, unique in the merchant domain
1110
     *
1111
     * @see https://developer.nexi.it/en/api/post-reservations-reservationId-paybylink
1112
     *
1113
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1114
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1115
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1116
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1117
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1118
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1119
     */
1120
    public function createPayByLinkForReservation(string $reservationId, Entity\CreatePayByLinkForReservation\Request $requestBody): Entity\PayByLinkResponse
1121
    {
1122
        $url = $this->buildUrl('/reservations/{reservationId}/paybylink', ['reservationId' => $reservationId]);
×
1123
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
1124
        if ($response->getStatusCode() === 200) {
×
NEW
1125
            $data = $this->decodeJsonToObject($response->getBody());
×
1126

1127
            return new Entity\PayByLinkResponse($data);
×
1128
        }
1129
        $this->throwErrorResponse($response, [
×
1130
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1131
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1132
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1133
        ]);
1134
    }
×
1135

1136
    /**
1137
     * Find reservation by ID.
1138
     *
1139
     * @param string $reservationId Merchant reservation id
1140
     *
1141
     * @see https://developer.nexi.it/en/api/get-reservations-reservationId
1142
     *
1143
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1144
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1145
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1146
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1147
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1148
     *
1149
     * @return \MLocati\Nexi\Entity\FindReservationById\Response|null returns NULL if not found
1150
     */
1151
    public function findReservationById(string $reservationId): ?Entity\FindReservationById\Response
1152
    {
1153
        $url = $this->buildUrl('/reservations/{reservationId}', ['reservationId' => $reservationId]);
×
1154
        $response = $this->invoke('GET', $url, 3);
×
1155
        if ($response->getStatusCode() === 200) {
×
NEW
1156
            $data = $this->decodeJsonToObject($response->getBody());
×
1157

1158
            return new Entity\FindReservationById\Response($data);
×
1159
        }
1160
        if ($response->getStatusCode() === 404) {
×
1161
            return null;
×
1162
        }
1163
        $this->throwErrorResponse($response, [
×
1164
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
1165
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1166
        ]);
1167
    }
×
1168

1169
    /**
1170
     * Create a structure.
1171
     *
1172
     * @see https://developer.nexi.it/en/api/post-structures
1173
     *
1174
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1175
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1176
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1177
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1178
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1179
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1180
     */
1181
    public function createStructure(Entity\StructureInfo $requestBody): void
1182
    {
1183
        $url = $this->buildUrl('/structures');
×
1184
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
1185
        if ($response->getStatusCode() === 200) {
×
1186
            return;
×
1187
        }
1188
        $this->throwErrorResponse($response, [
×
1189
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1190
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1191
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1192
        ]);
1193
    }
×
1194

1195
    /**
1196
     * Find structure in reversed chronological order.
1197
     *
1198
     * @see https://developer.nexi.it/en/api/get-structures
1199
     *
1200
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1201
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1202
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1203
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1204
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1205
     *
1206
     * @return \MLocati\Nexi\Entity\StructureInfo|null returns NULL if not found
1207
     */
1208
    public function findStructures(): ?Entity\StructureInfo
1209
    {
1210
        $url = $this->buildUrl('/structures');
×
1211
        $response = $this->invoke('GET', $url, 3);
×
1212
        if ($response->getStatusCode() === 200) {
×
NEW
1213
            $data = $this->decodeJsonToObject($response->getBody());
×
1214

1215
            return new Entity\StructureInfo($data);
×
1216
        }
1217
        if ($response->getStatusCode() === 404) {
×
1218
            return null;
×
1219
        }
1220
        $this->throwErrorResponse($response, [
×
1221
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
1222
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1223
        ]);
1224
    }
×
1225

1226
    /**
1227
     * Retrieve information of a specific structure identified by structure id.
1228
     *
1229
     * @param string $structureId Identification code of the structure.
1230
     *
1231
     * @see https://developer.nexi.it/en/api/get-structures-structureId
1232
     *
1233
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1234
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1235
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1236
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1237
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1238
     *
1239
     * @return \MLocati\Nexi\Entity\StructureInfo|null returns NULL if not found
1240
     */
1241
    public function findStructureById(string $structureId): ?Entity\StructureInfo
1242
    {
1243
        $url = $this->buildUrl('/structures/{structureId}', ['structureId' => $structureId]);
×
1244
        $response = $this->invoke('GET', $url, 3);
×
1245
        if ($response->getStatusCode() === 200) {
×
NEW
1246
            $data = $this->decodeJsonToObject($response->getBody());
×
1247

1248
            return new Entity\StructureInfo($data);
×
1249
        }
1250
        if ($response->getStatusCode() === 404) {
×
1251
            return null;
×
1252
        }
1253
        $this->throwErrorResponse($response, [
×
1254
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
1255
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1256
        ]);
1257
    }
×
1258

1259
    /**
1260
     * No Show validation.
1261
     *
1262
     * @see https://developer.nexi.it/en/api/post-noshow_validation
1263
     *
1264
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1265
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1266
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1267
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1268
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1269
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1270
     */
1271
    public function noShowValidation(Entity\ReservationValidation $requestBody): void
1272
    {
1273
        $url = $this->buildUrl('/noshow_validation');
×
1274
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
1275
        if ($response->getStatusCode() === 200) {
×
1276
            return;
×
1277
        }
1278
        $this->throwErrorResponse($response, [
×
1279
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1280
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1281
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1282
        ]);
1283
    }
×
1284

1285
    /**
1286
     * Delay charge validation.
1287
     *
1288
     * @see https://developer.nexi.it/en/api/post-delaycharge_validation
1289
     *
1290
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1291
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1292
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1293
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1294
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1295
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1296
     */
1297
    public function delayedChargeValidation(Entity\ReservationValidation $requestBody): void
1298
    {
1299
        $url = $this->buildUrl('/delaycharge_validation');
×
1300
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
1301
        if ($response->getStatusCode() === 200) {
×
1302
            return;
×
1303
        }
1304
        $this->throwErrorResponse($response, [
×
1305
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1306
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1307
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1308
        ]);
1309
    }
×
1310

1311
    /**
1312
     * Incremental validation
1313
     *
1314
     * @see https://developer.nexi.it/en/api/post-incremental_validation
1315
     *
1316
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1317
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1318
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1319
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1320
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1321
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1322
     */
1323
    public function incrementOrderValidation(Entity\ReservationValidation $requestBody): void
1324
    {
1325
        $url = $this->buildUrl('/incremental_validation');
×
1326
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
1327
        if ($response->getStatusCode() === 200) {
×
1328
            return;
×
1329
        }
1330
        $this->throwErrorResponse($response, [
×
1331
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1332
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1333
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1334
        ]);
1335
    }
×
1336

1337
    /**
1338
     * Create terms and conditions defined for a unique structure.
1339
     *
1340
     * @see https://developer.nexi.it/en/api/post-structure_conditions
1341
     *
1342
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1343
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1344
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1345
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1346
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1347
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1348
     */
1349
    public function createStructureTermsAndConditions(Entity\CreateStructureTermsAndConditions\Request $requestBody): Entity\CreateStructureTermsAndConditions\Response
1350
    {
1351
        $url = $this->buildUrl('/structure_conditions');
×
1352
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
1353
        if ($response->getStatusCode() === 200) {
×
NEW
1354
            $data = $this->decodeJsonToObject($response->getBody());
×
1355

1356
            return new Entity\CreateStructureTermsAndConditions\Response($data);
×
1357
        }
1358
        $this->throwErrorResponse($response, [
×
1359
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1360
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1361
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1362
        ]);
1363
    }
×
1364

1365
    /**
1366
     * Retrieve the list of terms and conditions associated to a specific structure.
1367
     *
1368
     * @param string $structureid Unique identifier for the structure.
1369
     *
1370
     * @see https://developer.nexi.it/en/api/get-structure_conditions-structures-structureid
1371
     *
1372
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1373
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1374
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1375
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1376
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1377
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1378
     */
1379
    public function findStructureTermsAndConditionsByStructureId(string $structureid): array
1380
    {
1381
        $url = $this->buildUrl('/structure_conditions/structures/{structureid}', ['structureid' => $structureid]);
×
1382
        $response = $this->invoke('GET', $url, 3);
×
1383
        if ($response->getStatusCode() === 200) {
×
1384
            $data = $this->decodeJsonToArray($response->getBody());
×
1385

1386
            return array_map(static function (array $item) { return new Entity\StructureTermsAndConditions($item); }, $data);
1387
        }
1388
        $this->throwErrorResponse($response, [
×
1389
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1390
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1391
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1392
        ]);
1393
    }
×
1394

1395
    /**
1396
     * Retrieve all the details for a given terms and conditions.
1397
     *
1398
     * @param string $structureConditionId Unique identifier of terms and conditions for the structure.
1399
     *
1400
     * @see https://developer.nexi.it/en/api/get-structure_conditions-structureConditionId
1401
     *
1402
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1403
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1404
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1405
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1406
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1407
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1408
     */
1409
    public function findStructureTermsAndConditionsById(string $structureConditionId): Entity\FindStructureTermsAndConditionsById\Response
1410
    {
1411
        $url = $this->buildUrl('/structure_conditions/{structureConditionId}', ['structureConditionId' => $structureConditionId]);
×
1412
        $response = $this->invoke('GET', $url, 3);
×
1413
        if ($response->getStatusCode() === 200) {
×
NEW
1414
            $data = $this->decodeJsonToObject($response->getBody());
×
1415

1416
            return new Entity\FindStructureTermsAndConditionsById\Response($data);
×
1417
        }
1418
        $this->throwErrorResponse($response, [
×
1419
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1420
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1421
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1422
        ]);
1423
    }
×
1424

1425
    /**
1426
     * Cancel terms and condition not associated to any operation.
1427
     *
1428
     * @param string $structureConditionId Unique identifier of terms and conditions for the structure.
1429
     *
1430
     * @see https://developer.nexi.it/en/api/post-structure_conditions-structureConditionId-cancels
1431
     *
1432
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1433
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1434
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Request rejected (HTTP code: 400)
1435
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1436
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1437
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1438
     *
1439
     * @return bool returns FALSE if not found
1440
     */
1441
    public function cancelStructureTermsAndConditions(string $structureConditionId): bool
1442
    {
1443
        $url = $this->buildUrl('/structure_conditions/{structureConditionId}/cancels', ['structureConditionId' => $structureConditionId]);
×
1444
        $response = $this->invoke('POST', $url, 3);
×
1445
        if ($response->getStatusCode() === 200) {
×
1446
            return true;
×
1447
        }
1448
        if ($response->getStatusCode() === 404) {
×
1449
            return false;
×
1450
        }
1451
        $this->throwErrorResponse($response, [
×
1452
            ['from' => 400, 'to' => 400, 'description' => 'Request rejected', 'detailed' => true],
×
1453
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1454
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1455
        ]);
1456
    }
×
1457

1458
    /**
1459
     * Activate or deactivate the service.
1460
     *
1461
     * @see https://developer.nexi.it/en/api/post-services
1462
     *
1463
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1464
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1465
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1466
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1467
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1468
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1469
     */
1470
    public function toggleService(Entity\ServiceRequest $requestBody): void
1471
    {
1472
        $url = $this->buildUrl('/services');
×
1473
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
1474
        if ($response->getStatusCode() === 200) {
×
1475
            return;
×
1476
        }
1477
        $this->throwErrorResponse($response, [
×
1478
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1479
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1480
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1481
        ]);
1482
    }
×
1483

1484
    /**
1485
     * Retrieve the information service.
1486
     *
1487
     * @see https://developer.nexi.it/en/api/get-services
1488
     *
1489
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1490
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1491
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1492
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1493
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1494
     *
1495
     * @return \MLocati\Nexi\Entity\ServiceRequest|null returns NULL if not found
1496
     */
1497
    public function getService(Entity\GetService\Query $query): ?Entity\ServiceRequest
1498
    {
1499
        $url = $this->buildUrl('/services', [], $query);
×
1500
        $response = $this->invoke('GET', $url, 3);
×
1501
        if ($response->getStatusCode() === 200) {
×
NEW
1502
            $data = $this->decodeJsonToObject($response->getBody());
×
1503

1504
            return new Entity\ServiceRequest($data);
×
1505
        }
1506
        if ($response->getStatusCode() === 404) {
×
1507
            return null;
×
1508
        }
1509
        $this->throwErrorResponse($response, [
×
1510
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
×
1511
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1512
        ]);
1513
    }
×
1514

1515
    /**
1516
     * Create custom fields.
1517
     *
1518
     * @see https://developer.nexi.it/en/api/post-custom_fields
1519
     *
1520
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1521
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1522
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1523
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1524
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1525
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1526
     */
1527
    public function createCustomField(Entity\CustomFieldDetails $requestBody): Entity\CreateCustomField\Response
1528
    {
1529
        $url = $this->buildUrl('/custom_fields');
×
1530
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
1531
        if ($response->getStatusCode() === 200) {
×
NEW
1532
            $data = $this->decodeJsonToObject($response->getBody());
×
1533

1534
            return new Entity\CreateCustomField\Response($data);
×
1535
        }
1536
        $this->throwErrorResponse($response, [
×
1537
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1538
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1539
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1540
        ]);
1541
    }
×
1542

1543
    /**
1544
     * Retrieve custom fields details
1545
     *
1546
     * @see https://developer.nexi.it/en/api/get-custom_fields-customFieldId
1547
     *
1548
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1549
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1550
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1551
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1552
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1553
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1554
     */
1555
    public function getCustomField(string $customFieldId): Entity\CustomFieldDetails
1556
    {
1557
        $url = $this->buildUrl('/custom_fields/{customFieldId}', ['customFieldId' => $customFieldId]);
×
1558
        $response = $this->invoke('GET', $url, 3);
×
1559
        if ($response->getStatusCode() === 200) {
×
NEW
1560
            $data = $this->decodeJsonToObject($response->getBody());
×
1561

1562
            return new Entity\CustomFieldDetails($data);
×
1563
        }
1564
        $this->throwErrorResponse($response, [
×
1565
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1566
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1567
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1568
        ]);
1569
    }
×
1570

1571
    /**
1572
     * Retrieval of the terms and conditions with .pdf extension of the structure.
1573
     *
1574
     * @param string $structureConditionId Terms and condition of the structure unique identifier.
1575
     *
1576
     * @see https://developer.nexi.it/en/api/get-structure_conditions-structureConditionId-pdf
1577
     *
1578
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1579
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1580
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1581
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1582
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1583
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1584
     */
1585
    public function getStructureTermsAndConditionsPdf(string $structureConditionId): void
1586
    {
1587
        $url = $this->buildUrl('/structure_conditions/{structureConditionId}/pdf', ['structureConditionId' => $structureConditionId]);
×
1588
        $response = $this->invoke('GET', $url, 3);
×
1589
        if ($response->getStatusCode() === 200) {
×
1590
            return;
×
1591
        }
1592
        $this->throwErrorResponse($response, [
×
1593
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1594
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1595
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1596
        ]);
1597
    }
×
1598

1599
    /**
1600
     * Recurring Payment (MIT) on a reservation.
1601
     *
1602
     * @param string $reservationId Reservation identification code.
1603
     * @param string $idempotencyKey an identifier of the request (to be used on subsequent retries); if empty, it will be set as output
1604
     *
1605
     * @see https://developer.nexi.it/en/api/post-reservation-reservationId-mit
1606
     *
1607
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1608
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1609
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1610
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1611
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1612
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1613
     */
1614
    public function payRecurringReservation(string $reservationId, Entity\PayRecurringReservation\Request $requestBody, string &$idempotencyKey = ''): void
1615
    {
1616
        $url = $this->buildUrl('/reservation/{reservationId}/mit', ['reservationId' => $reservationId]);
×
1617
        $response = $this->invoke('POST', $url, 7, $requestBody, $idempotencyKey);
×
1618
        if ($response->getStatusCode() === 200) {
×
1619
            return;
×
1620
        }
1621
        $this->throwErrorResponse($response, [
×
1622
            ['from' => 400, 'to' => 400, 'description' => 'Invalid request data', 'detailed' => true],
×
1623
            ['from' => 401, 'to' => 401, 'description' => 'Unauthorized'],
1624
            ['from' => 500, 'to' => 500, 'description' => 'Internal Server Error', 'detailed' => true],
1625
        ]);
1626
    }
×
1627

1628
    /**
1629
     * @throws \MLocati\Nexi\Exception\NoHttpClient
1630
     */
1631
    protected function buildHttpClient(): HttpClient
1632
    {
1633
        $flags = 0
NEW
1634
            | ($this->configuration->allowUnsafeHttps() ? HttpClient::FLAG_ALLOWINSECUREHTTPS : 0)
×
NEW
1635
            | 0;
×
1636
        if (HttpClient\Curl::isAvailable()) {
×
NEW
1637
            return new HttpClient\Curl($flags);
×
1638
        }
1639
        if (HttpClient\StreamWrapper::isAvailable()) {
×
NEW
1640
            return new HttpClient\StreamWrapper($flags);
×
1641
        }
1642
        throw new Exception\NoHttpClient();
×
1643
    }
1644

1645
    protected function buildCorrelationProvider(): CorrelationProvider
1646
    {
1647
        if (CorrelationProvider\UUID::isAvailable()) {
×
1648
            return new CorrelationProvider\UUID();
×
1649
        }
1650

1651
        return new CorrelationProvider\Random();
×
1652
    }
1653

1654
    protected function buildUrl(string $path, array $pathParams = [], ?QueryEntityInterface $query = null): string
1655
    {
1656
        $matches = null;
×
1657
        if (preg_match_all('/\\{(?<name>[^\\}]+)\\}/', $path, $matches) !== 0) {
×
1658
            $names = $matches['name'];
×
1659
            while ($names !== []) {
×
1660
                $name = array_shift($names);
×
1661
                if (!array_key_exists($name, $pathParams)) {
×
1662
                    throw new \RuntimeException('Missing required URL parameter: ' . $name);
×
1663
                }
1664
                $path = str_replace('{' . $name . '}', rawurlencode((string) $pathParams[$name]), $path);
×
1665
                unset($pathParams[$name]);
×
1666
            }
1667
        }
1668
        if ($pathParams !== []) {
×
1669
            throw new \RuntimeException("Unexpected URL parameters:\n- " . implode("\n- ", array_keys($pathParams)));
×
1670
        }
1671
        $url = rtrim($this->configuration->getBaseUrl(), '/') . '/' . ltrim($path, '/');
×
1672
        $qs = $query === null ? '' : $query->getQuerystring();
×
1673
        if ($qs !== '') {
×
1674
            $url .= '?' . $qs;
×
1675
        }
1676

1677
        return $url;
×
1678
    }
1679

1680
    /**
1681
     * @param \MLocati\Nexi\Entity|\MLocati\Nexi\Entity[]|null $requestBody
1682
     */
1683
    protected function invoke(string $method, string $url, int $headerFlags, $requestBody = null, string &$idempotencyKey = ''): HttpClient\Response
1684
    {
1685
        if ($requestBody === null) {
×
1686
            $requestBodyJson = '';
×
1687
        } else {
1688
            $requestBodyJson = json_encode($requestBody, JSON_UNESCAPED_SLASHES);
×
1689
            if ($requestBodyJson === false) {
×
1690
                throw new \RuntimeException('Failed to create the JSON data: ' . (json_last_error_msg() ?: 'unknown reason'));
×
1691
            }
1692
        }
1693
        $headers = $this->buildHeaders($method, $url, $requestBodyJson, $headerFlags, $idempotencyKey);
×
1694

1695
        return $this->httpClient->invoke($method, $url, $headers, $requestBodyJson);
×
1696
    }
1697

1698
    protected function buildHeaders(string $method, string $url, string $requestBody, int $flags, string &$idempotencyKey): array
1699
    {
1700
        $headers = [
1701
            'Content-Type' => 'application/json',
×
1702
            'Accept' => 'application/json',
1703
        ];
1704
        if ($flags & 1) {
×
1705
            $headers['X-Api-Key'] = $this->configuration->getApiKey();
×
1706
        }
1707
        if ($flags & 2) {
×
1708
            $headers['Correlation-Id'] = $this->correlationProvider->getCorrelationID($method, $url, $requestBody);
×
1709
        }
1710
        if ($flags & 4) {
×
1711
            if ($idempotencyKey === '') {
×
1712
                $idempotencyKey = $this->generateIdempotencyKey();
×
1713
            }
1714
            $headers['Idempotency-Key'] = $idempotencyKey;
×
1715
        }
1716

1717
        return $headers;
×
1718
    }
1719

1720
    /**
1721
     * @throws \MLocati\Nexi\Exception\InvalidJson
1722
     */
1723
    protected function decodeJsonToArray(string $json): array
1724
    {
NEW
1725
        $data = $this->decodeJson($json);
×
NEW
1726
        if (is_array($data)) {
×
NEW
1727
            return $data;
×
1728
        }
NEW
1729
        throw new Exception\InvalidJson($json, 'The JSON does NOT represent an array');
×
1730
    }
1731

1732
    /**
1733
     * @throws \MLocati\Nexi\Exception\InvalidJson
1734
     */
1735
    protected function decodeJsonToObject(string $json): stdClass
1736
    {
NEW
1737
        $data = $this->decodeJson($json);
×
NEW
1738
        if ($data instanceof stdClass) {
×
NEW
1739
            return $data;
×
1740
        }
1741

NEW
1742
        throw new Exception\InvalidJson($json, 'The JSON does NOT represent an object');
×
1743
    }
1744

1745
    /**
1746
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed
1747
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1748
     */
1749
    protected function throwErrorResponse(Response $response, array $cases): void
1750
    {
1751
        foreach ($cases as $case) {
×
1752
            if ($case['from'] <= $response->getStatusCode() && $response->getStatusCode() <= $case['from']) {
×
1753
                $message = $case['description'];
×
1754
                if ($message === '') {
×
1755
                    $message = "Request failed with return code {$response->getStatusCode()}";
×
1756
                }
1757
                if (empty($case['detailed'])) {
×
1758
                    throw new Exception\ErrorResponse($response->getStatusCode(), $message);
×
1759
                }
1760
                $data = $this->decodeJsonToArray($response->getBody());
×
1761
                $errors = new Entity\Errors($data);
×
1762

1763
                throw new Exception\ErrorResponse\Detailed($response->getStatusCode(), $message, $errors);
×
1764
            }
1765
        }
1766

1767
        throw new Exception\ErrorResponse($response->getStatusCode(), "Request failed with return code {$response->getStatusCode()}");
×
1768
    }
1769

1770
    /**
1771
     * Maximum length: 63 characters
1772
     */
1773
    protected function generateIdempotencyKey(): string
1774
    {
1775
        return bin2hex(random_bytes(31));
×
1776
    }
1777

1778
    /**
1779
     * @throws \MLocati\Nexi\Exception\InvalidJson
1780
     */
1781
    private function decodeJson(string $json)
1782
    {
NEW
1783
        if ($json === 'null') {
×
NEW
1784
            return null;
×
1785
        }
NEW
1786
        $decoded = json_decode($json);
×
NEW
1787
        if ($decoded === null) {
×
NEW
1788
            throw new Exception\InvalidJson($json);
×
1789
        }
1790

NEW
1791
        return $decoded;
×
1792
    }
1793
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc