BaseIgfsCg::responseXmlToObject()   A
last analyzed

Complexity

Conditions 2
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 11
ccs 5
cts 5
cp 1
crap 2
rs 10
1
<?php
2
3
namespace PagOnline;
4
5
use Illuminate\Support\Str;
6
use PagOnline\Exceptions\ConnectionException;
7
use PagOnline\Exceptions\IgfsException;
8
use PagOnline\Exceptions\IgfsMissingParException;
9
use PagOnline\Exceptions\IOException;
10
use PagOnline\Traits\HttpClient;
11
use SimpleXMLElement;
12
13
/**
14
 * Class BaseIgfsCg.
15
 */
16
abstract class BaseIgfsCg implements IgfsCgInterface
17
{
18
    use HttpClient;
19
20
    /**
21
     * Package version.
22
     *
23
     * @var string
24
     */
25
    const VERSION = '2.4.1.5';
26
27
    /**
28
     * Signature Key.
29
     *
30
     * @var string
31
     */
32
    public $kSig;
33
34
    /**
35
     * Payment Gateway server url.
36
     *
37
     * @var null|string
38
     */
39
    public $serverURL = null;
40
    public $serverURLs = null;
41
42
    public $shopID = null;
43
44
    public $tid = null;
45
    public $merID = null;
46
    public $payInstr = null;
47
48
    public $rc = null;
49
    public $error = null;
50
    public $errorDesc = null;
51
52
    protected static $soapBodyTag = 'Body';
53
    protected static $soapResponseParentTag = '';
54
    protected static $soapResponseTag = 'response';
55
56
    /**
57
     * Set the request namespace here.
58
     *
59
     * @var string
60
     */
61
    protected $requestNamespace = '';
62
63
    protected $fields2Reset = false;
64
65
    /**
66
     * BaseIgfsCg constructor.
67
     */
68 255
    public function __construct()
69
    {
70 255
        $this->generateHttpClient();
71
    }
72
73
    /**
74
     * Reset fields.
75
     */
76 21
    public function resetFields()
77
    {
78 21
        $this->tid = null;
79 21
        $this->merID = null;
80 21
        $this->payInstr = null;
81 21
        $this->shopID = null;
82 21
        $this->rc = null;
83 21
        $this->error = false;
84 21
        $this->errorDesc = null;
85 21
        $this->fields2Reset = false;
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91 21
    public static function getVersion(): string
92
    {
93 21
        return self::VERSION;
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99 35
    public function getRequest(): string
100
    {
101 35
        return (string) new $this->requestNamespace();
102
    }
103
104
    /**
105
     * TODO: Refactor this.
106
     *
107
     * @return bool
108
     */
109 8
    public function execute(): bool
110
    {
111
        try {
112 8
            $this->checkFields();
113 7
            $mapResponse = [];
114 7
            if (!empty($this->serverURL)) {
115 6
                $mapResponse = $this->executeHttp($this->serverURL);
116
            } else {
117 2
                $sURLs = $this->serverURLs;
118 2
                $sURL = \array_shift($sURLs);
119 2
                $finished = false;
120 2
                while (!$finished) {
121
                    try {
122 2
                        $mapResponse = $this->executeHttp($sURL);
123 1
                        $finished = true;
124 1
                    } catch (ConnectionException $e) {
125 1
                        if (!empty($sURLs)) {
126 1
                            $sURL = \array_shift($sURLs);
127
                        } else {
128 1
                            throw $e;
129
                        }
130
                    }
131
                }
132
            }
133
134
            // Leggiamo i campi
135 4
            $this->parseResponseMap($mapResponse);
136 4
            $this->fields2Reset = true;
137 4
            if (!$this->error) {
138
                // Verifico la signature
139 3
                if (!$this->checkResponseSignature($mapResponse)) {
140 2
                    throw new IgfsException('Invalid IGFS Response signature');
141
                }
142
143 2
                return true;
144
            }
145
146 2
            return false;
147 6
        } catch (\Throwable $e) {
148 6
            $this->resetFields();
149 6
            $this->fields2Reset = true;
150 6
            $this->error = true;
151 6
            $this->errorDesc = $e->getMessage();
152 6
            if ($e instanceof IgfsMissingParException) {
153 1
                $this->rc = Errors::IGFS_20000; // Missing data
154 1
                $this->errorDesc = $e->getMessage();
155
            }
156 6
            if ($e instanceof ConnectionException) {
157 2
                $this->rc = Errors::IGFS_007; // Communication error
158 2
                $this->errorDesc = $e->getMessage();
159
            }
160 6
            if ($this->rc === null) {
161 4
                $this->rc = Errors::IGFS_909; // System error
162
            }
163
164 6
            return false;
165
        }
166
    }
167
168
    /**
169
     * Returns public properties to array.
170
     *
171
     * @return array
172
     */
173 14
    public function toArray()
174
    {
175 14
        $propertiesArray = [];
176 14
        $publicProperties = (new \ReflectionObject($this))->getProperties(\ReflectionProperty::IS_PUBLIC);
177 14
        foreach ($publicProperties as $publicProperty) {
178 14
            $propertiesArray[$publicProperty->getName()] = $publicProperty->getValue($this);
179
        }
180
181 14
        return $propertiesArray;
182
    }
183
184
    abstract protected function getServicePort();
185
186
    /**
187
     * @return array
188
     */
189 7
    protected function getCommonRequestSignatureFields(): array
190
    {
191 7
        return [
192 7
            $this->getVersion(),
193 7
            $this->tid,
194 7
            $this->merID,
195 7
            $this->payInstr,
196 7
            $this->shopID,
197 7
        ];
198
    }
199
200
    /**
201
     * Get additional signature fields (request specific).
202
     *
203
     * @return array
204
     */
205
    abstract protected function getAdditionalRequestSignatureFields(): array;
206
207
    /***
208
     * Generates a signature
209
     *
210
     * @param $signatureFields
211
     *
212
     * @throws IgfsException
213
     *
214
     * @return string
215
     */
216 21
    protected function getSignature(array $signatureFields): string
217
    {
218
        try {
219 21
            $data = '';
220 21
            foreach ($signatureFields as $value) {
221 21
                $data .= (string) $value;
222
            }
223
224 7
            return \base64_encode(\hash_hmac('sha256', $data, $this->kSig, true));
225 14
        } catch (\Exception $e) {
226 14
            throw new IgfsException($e);
227
        }
228
    }
229
230
    /**
231
     * Set signature key on request.
232
     *
233
     * @param string $request
234
     *
235
     * @throws IgfsException
236
     *
237
     * @return void
238
     */
239 7
    protected function setRequestSignature(&$request): void
240
    {
241 7
        $signatureFields = \array_merge(
242 7
            $this->getCommonRequestSignatureFields(),
243 7
            $this->getAdditionalRequestSignatureFields()
244 7
        );
245 7
        $signature = $this->getSignature($signatureFields);
246 7
        $this->replaceRequestParameter($request, 'signature', $signature, false);
247
    }
248
249
    /**
250
     * Check required fields, if any of the required parameter is missing it'll throw an IgfsMissingParException.
251
     *
252
     * @throws IgfsMissingParException
253
     */
254 113
    protected function checkFields()
255
    {
256 113
        if (empty($this->serverURL) && (empty($this->serverURLs) || !\is_array($this->serverURLs))) {
257 17
            throw new IgfsMissingParException('Missing serverURL');
258
        }
259
260 96
        if (empty($this->kSig)) {
261 14
            throw new IgfsMissingParException('Missing kSig');
262
        }
263
264 82
        if (empty($this->tid) && (empty($this->merID) && empty($this->payInstr))) {
265 14
            throw new IgfsMissingParException('Missing tid');
266
        }
267
    }
268
269
    /**
270
     * Get configured server url.
271
     *
272
     * @param string $serverUrl
273
     *
274
     * @return string
275
     */
276 21
    protected function getServerUrl($serverUrl)
277
    {
278 21
        if (!Str::endsWith($serverUrl, '/')) {
279 21
            $serverUrl .= '/';
280
        }
281
282 21
        return $serverUrl.$this->getServicePort();
283
    }
284
285
    /**
286
     * @param string $request
287
     * @param string $parameter
288
     * @param mixed  $value
289
     * @param bool   $wrap_cdata
290
     */
291 35
    protected function replaceRequestParameter(
292
        string &$request,
293
        string $parameter,
294
        $value = null,
295
        bool $wrap_cdata = true
296
    ) {
297 35
        $value = (string) $value;
298 35
        if ($value === '') {
299 35
            $xmlTag = '';
300
        } else {
301 35
            $xmlTag = "<{$parameter}>";
302 35
            $xmlTag .= $wrap_cdata ? "<![CDATA[{$value}]]>" : $value;
303 35
            $xmlTag .= "</{$parameter}>";
304
        }
305 35
        $request = \str_replace('{'.$parameter.'}', $xmlTag, $request);
306
    }
307
308
    /**
309
     * Build request XML.
310
     *
311
     * @return mixed|string
312
     */
313 21
    protected function buildRequest()
314
    {
315 21
        $request = $this->getRequest();
316 21
        $this->replaceRequestParameter($request, 'apiVersion', $this->getVersion());
317 21
        $this->replaceRequestParameter($request, 'shopID', $this->shopID);
318 21
        $this->replaceRequestParameter($request, 'tid', $this->tid);
319 21
        $this->replaceRequestParameter($request, 'merID', $this->merID);
320 21
        $this->replaceRequestParameter($request, 'payInstr', $this->payInstr);
321
322 21
        return $request;
323
    }
324
325
    abstract protected function getResponseSignature($response);
326
327
    /**
328
     * @param string $response
329
     *
330
     * @return null|SimpleXMLElement
331
     */
332 5
    protected function responseXmlToObject(string $response): ?SimpleXMLElement
333
    {
334
        try {
335 5
            $dom = new SimpleXMLElement($response, LIBXML_NOERROR, false);
336
            /*$responseNode = $dom->children('soap', true)->{static::$soapBodyTag}
337
                ->children('ns1', true)->{static::$soapResponseParentTag}
338
                ->children()
339
                ->{self::$soapResponseTag};*/
340 5
            return $dom->xpath('//response')[0];
341 1
        } catch (\Throwable $e) {
342 1
            return null;
343
        }
344
    }
345
346
    /**
347
     * @param string $response
348
     *
349
     * @return array
350
     */
351 5
    protected function parseResponse($response): array
352
    {
353 5
        $responseNode = $this->responseXmlToObject($response);
354 5
        if ($responseNode === null || $responseNode->children()->count() === 0) {
355 1
            return [];
356
        }
357 4
        $fields = IgfsUtils::parseResponseFields($responseNode);
358 4
        if (\count($fields) > 0) {
359 4
            $fields[self::$soapResponseTag] = $responseNode->asXML();
360
        }
361
362 4
        return $fields;
363
    }
364
365
    /**
366
     * @param array $response
367
     */
368 4
    protected function parseResponseMap($response)
369
    {
370 4
        $this->tid = IgfsUtils::getValue($response, 'tid');
371 4
        $this->rc = IgfsUtils::getValue($response, 'rc');
372 4
        if (IgfsUtils::getValue($response, 'error') === null) {
373 1
            $this->error = true;
374
        } else {
375 4
            $this->error = ((string) IgfsUtils::getValue($response, 'error') === 'true');
376
        }
377 4
        $this->errorDesc = IgfsUtils::getValue($response, 'errorDesc');
378
    }
379
380
    /**
381
     * @param array $response
382
     *
383
     * @return bool
384
     */
385 3
    protected function checkResponseSignature($response)
386
    {
387 3
        $signature = IgfsUtils::getValue($response, 'signature');
388 3
        if ($signature === null) {
389 1
            return false;
390
        }
391
392 2
        if ($signature != $this->getResponseSignature($response)) {
393 1
            return false;
394
        }
395
396 2
        return true;
397
    }
398
399
    /**
400
     * @param string $url
401
     *
402
     * @throws \PagOnline\Exceptions\IOException
403
     * @throws \PagOnline\Exceptions\IgfsException
404
     *
405
     * @return array
406
     */
407 7
    protected function process($url)
408
    {
409 7
        $request = $this->buildRequest();
410 7
        $this->setRequestSignature($request);
411 7
        $response = $this->post($url, $request);
412
413 6
        if ($response === '') {
414 1
            throw new IgfsException('IGFS Response is null');
415
        }
416
417 5
        return $this->parseResponse($response);
418
    }
419
420
    /**
421
     * @return string
422
     */
423 14
    protected function getUniqueBoundaryValue()
424
    {
425 14
        return IgfsUtils::getUniqueBoundaryValue();
426
    }
427
428
    /**
429
     * Execute a POST request.
430
     *
431
     * @param string $url
432
     * @param string $request
433
     *
434
     * @throws ConnectionException
435
     *
436
     * @return string
437
     */
438 7
    private function post($url, $request): string
439
    {
440
        try {
441 7
            $response = $this->httpPost($url, $request);
442 2
        } catch (\Throwable $e) {
443 2
            throw new ConnectionException($url, $e->getMessage());
444
        }
445
446 6
        return $response->getBody()->getContents();
447
    }
448
449
    /***
450
     * @param string $url
451
     *
452
     * @throws IgfsException
453
     * @throws IOException
454
     *
455
     * @return array
456
     */
457 7
    private function executeHttp($url)
458
    {
459 7
        $mapResponse = $this->process(
460 7
            $this->getServerUrl($url)
461 7
        );
462
463 5
        if (empty($mapResponse)) {
464 1
            throw new IgfsException('Invalid IGFS Response');
465
        }
466
467 4
        return $mapResponse;
468
    }
469
}
470