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

mlocati / nexi / 8551303918

04 Apr 2024 08:05AM UTC coverage: 2.295% (+2.3%) from 0.0%
8551303918

push

github

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

12 of 89 new or added lines in 7 files covered. (13.48%)

2 existing lines in 2 files now uncovered.

39 of 1699 relevant lines covered (2.3%)

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

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

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

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

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

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

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

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

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

69
    /**
70
     * 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.
71
     *
72
     * @see https://developer.nexi.it/en/api/post-orders-hpp
73
     *
74
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
75
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
76
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
77
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
78
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
79
     * @throws \MLocati\Nexi\Exception\ErrorResponse
80
     */
81
    public function createOrderForHostedPayment(Entity\CreateOrderForHostedPayment\Request $requestBody): Entity\CreateOrderForHostedPayment\Response
82
    {
83
        $url = $this->buildUrl('/orders/hpp');
×
84
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
85
        if ($response->getStatusCode() === 200) {
×
86
            $data = $this->decodeJsonToArray($response->getBody());
×
87

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

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

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

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

158
    /**
159
     * 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.
160
     *
161
     * @see https://developer.nexi.it/en/api/post-orders-2steps-init
162
     *
163
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
164
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
165
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
166
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
167
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
168
     * @throws \MLocati\Nexi\Exception\ErrorResponse
169
     */
170
    public function twoSteps3DSInit(Entity\MultiStepInitRequest $requestBody): Entity\MultiStepInitResponse
171
    {
172
        $url = $this->buildUrl('/orders/2steps/init');
×
173
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
174
        if ($response->getStatusCode() === 200) {
×
175
            $data = $this->decodeJsonToArray($response->getBody());
×
176

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

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

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

214
    /**
215
     * 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.
216
     *
217
     * @see https://developer.nexi.it/en/api/post-orders-3steps-init
218
     *
219
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
220
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
221
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
222
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
223
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
224
     * @throws \MLocati\Nexi\Exception\ErrorResponse
225
     */
226
    public function threeSteps3DSInit(Entity\MultiStepInitRequest $requestBody): Entity\MultiStepInitResponse
227
    {
228
        $url = $this->buildUrl('/orders/3steps/init');
×
229
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
230
        if ($response->getStatusCode() === 200) {
×
231
            $data = $this->decodeJsonToArray($response->getBody());
×
232

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

242
    /**
243
     * 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.
244
     *
245
     * @see https://developer.nexi.it/en/api/post-orders-3steps-validation
246
     *
247
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
248
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
249
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
250
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
251
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
252
     * @throws \MLocati\Nexi\Exception\ErrorResponse
253
     */
254
    public function threeSteps3DSValidation(Entity\ThreeSteps3DSValidation\Request $requestBody): Entity\ThreeSteps3DSValidation\Response
255
    {
256
        $url = $this->buildUrl('/orders/3steps/validation');
×
257
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
258
        if ($response->getStatusCode() === 200) {
×
259
            $data = $this->decodeJsonToArray($response->getBody());
×
260

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

270
    /**
271
     * 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.
272
     *
273
     * @see https://developer.nexi.it/en/api/post-orders-3steps-payment
274
     *
275
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
276
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
277
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
278
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
279
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
280
     * @throws \MLocati\Nexi\Exception\ErrorResponse
281
     */
282
    public function threeSteps3DSPayment(Entity\MultiStepPaymentRequest $requestBody): Entity\OperationResult
283
    {
284
        $url = $this->buildUrl('/orders/3steps/payment');
×
285
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
286
        if ($response->getStatusCode() === 200) {
×
287
            $data = $this->decodeJsonToArray($response->getBody());
×
288

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

298
    /**
299
     * 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.
300
     *
301
     * @param string $idempotencyKey an identifier of the request (to be used on subsequent retries); if empty, it will be set as output
302
     *
303
     * @see https://developer.nexi.it/en/api/post-orders-mit
304
     *
305
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
306
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
307
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
308
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
309
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
310
     * @throws \MLocati\Nexi\Exception\ErrorResponse
311
     */
312
    public function createOrderForMerchantInitiatedTransaction(Entity\CreateOrderForMerchantInitiatedTransaction\Request $requestBody, string &$idempotencyKey = ''): Entity\OperationResult
313
    {
314
        $url = $this->buildUrl('/orders/mit');
×
315
        $response = $this->invoke('POST', $url, 7, $requestBody, $idempotencyKey);
×
316
        if ($response->getStatusCode() === 200) {
×
317
            $data = $this->decodeJsonToArray($response->getBody());
×
318

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

328
    /**
329
     * 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.
330
     *
331
     * @see https://developer.nexi.it/en/api/post-orders-card_verification
332
     *
333
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
334
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
335
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
336
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
337
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
338
     * @throws \MLocati\Nexi\Exception\ErrorResponse
339
     */
340
    public function cardVerification(Entity\CardVerification\Request $requestBody): Entity\OperationResult
341
    {
342
        $url = $this->buildUrl('/orders/card_verification');
×
343
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
344
        if ($response->getStatusCode() === 200) {
×
345
            $data = $this->decodeJsonToArray($response->getBody());
×
346

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

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

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

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

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

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

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

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

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

469
    /**
470
     * 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).
471
     *
472
     * @see https://developer.nexi.it/en/api/post-build-cancel
473
     *
474
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
475
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
476
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
477
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
478
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
479
     * @throws \MLocati\Nexi\Exception\ErrorResponse
480
     */
481
    public function cancelXPayBuildOrder(Entity\Session $requestBody): Entity\CancelXPayBuildOrder\Response
482
    {
483
        $url = $this->buildUrl('/build/cancel');
×
484
        $response = $this->invoke('POST', $url, 3, $requestBody);
×
485
        if ($response->getStatusCode() === 200) {
×
486
            $data = $this->decodeJsonToArray($response->getBody());
×
487

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

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

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

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

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

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

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

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

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

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

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

643
    /**
644
     * 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.
645
     *
646
     * @param string $operationId Operation id
647
     *
648
     * @see https://developer.nexi.it/en/api/get-operations-operationId-actions
649
     *
650
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
651
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
652
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
653
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
654
     * @throws \MLocati\Nexi\Exception\ErrorResponse
655
     *
656
     * @return \MLocati\Nexi\Entity\GetOperationActions\Response|null returns NULL if not found
657
     */
658
    public function getOperationActions(string $operationId): ?Entity\GetOperationActions\Response
659
    {
660
        $url = $this->buildUrl('/operations/{operationId}/actions', ['operationId' => $operationId]);
×
661
        $response = $this->invoke('GET', $url, 3);
×
662
        if ($response->getStatusCode() === 200) {
×
663
            $data = $this->decodeJsonToArray($response->getBody());
×
664

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1021
    /**
1022
     * 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.
1023
     *
1024
     * @param string $idempotencyKey an identifier of the request (to be used on subsequent retries); if empty, it will be set as output
1025
     *
1026
     * @see https://developer.nexi.it/en/api/post-no_shows
1027
     *
1028
     * @throws \MLocati\Nexi\Exception\HttpRequestFailed if the HTTP request could not be made
1029
     * @throws \MLocati\Nexi\Exception\InvalidJson if we couldn't decode the response body as JSON
1030
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Invalid request data (HTTP code: 400)
1031
     * @throws \MLocati\Nexi\Exception\ErrorResponse Unauthorized (HTTP code: 401)
1032
     * @throws \MLocati\Nexi\Exception\ErrorResponse\Detailed Internal Server Error (HTTP code: 500)
1033
     * @throws \MLocati\Nexi\Exception\ErrorResponse
1034
     */
1035
    public function noShowCharge(Entity\ChangeAmountRequest $requestBody, string &$idempotencyKey = ''): Entity\OperationResult
1036
    {
1037
        $url = $this->buildUrl('/no_shows');
×
1038
        $response = $this->invoke('POST', $url, 7, $requestBody, $idempotencyKey);
×
1039
        if ($response->getStatusCode() === 200) {
×
1040
            $data = $this->decodeJsonToArray($response->getBody());
×
1041

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1676
        return $url;
×
1677
    }
1678

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

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

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

1716
        return $headers;
×
1717
    }
1718

1719
    /**
1720
     * @throws \MLocati\Nexi\Exception\InvalidJson
1721
     */
1722
    protected function decodeJsonToArray(string $json): array
1723
    {
1724
        if ($json === 'null') {
×
1725
            $decoded = null;
×
1726
        } else {
1727
            $decoded = json_decode($json, true);
×
1728
            if ($decoded === null) {
×
1729
                throw new Exception\InvalidJson($json);
×
1730
            }
1731
        }
1732
        if (!is_array($decoded)) {
×
1733
            throw new Exception\InvalidJson($json, 'The JSON does NOT represent an array');
×
1734
        }
1735

1736
        return $decoded;
×
1737
    }
1738

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

1757
                throw new Exception\ErrorResponse\Detailed($response->getStatusCode(), $message, $errors);
×
1758
            }
1759
        }
1760

1761
        throw new Exception\ErrorResponse($response->getStatusCode(), "Request failed with return code {$response->getStatusCode()}");
×
1762
    }
1763

1764
    /**
1765
     * Maximum length: 63 characters
1766
     */
1767
    protected function generateIdempotencyKey(): string
1768
    {
1769
        return bin2hex(random_bytes(31));
×
1770
    }
1771
}
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