Passed
Push — main ( ad062e...2930ba )
by MyFatoorah
10:01
created

MyFatoorah::getJsonErrors()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 36
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 10
c 1
b 0
f 0
nc 8
nop 1
dl 0
loc 36
rs 9.6111
1
<?php
2
3
namespace MyFatoorah\Library;
4
5
use Exception;
6
7
/**
8
 * MyFatoorah is responsible for handling calling MyFatoorah API endpoints.
9
 * Also, It has necessary library functions that help in providing the correct parameters used endpoints.
10
 *
11
 * MyFatoorah offers a seamless business experience by offering a technology put together by our tech team. It enables smooth business operations involving sales activity, product invoicing, shipping, and payment processing. MyFatoorah invoicing and payment gateway solution trigger your business to greater success at all levels in the new age world of commerce. Leverage your sales and payments at all e-commerce platforms (ERPs, CRMs, CMSs) with transparent and slick applications that are well-integrated into social media and telecom services. For every closing sale click, you make a business function gets done for you, along with generating factual reports and statistics to fine-tune your business plan with no-barrier low-cost.
12
 * Our technology experts have designed the best GCC E-commerce solutions for the native financial instruments (Debit Cards, Credit Cards, etc.) supporting online sales and payments, for events, shopping, mall, and associated services.
13
 *
14
 * Created by MyFatoorah http://www.myfatoorah.com/
15
 * Developed By [email protected]
16
 * Date: 05/12/2023
17
 * Time: 12:00
18
 *
19
 * API Documentation on https://myfatoorah.readme.io/docs
20
 * Library Documentation and Download link on https://myfatoorah.readme.io/docs/php-library
21
 *
22
 * @author    MyFatoorah <[email protected]>
23
 * @copyright 2021 MyFatoorah, All rights reserved
24
 * @license   GNU General Public License v3.0
25
 */
26
class MyFatoorah extends MyFatoorahHelper
27
{
28
29
    /**
30
     * The configuration used to connect to MyFatoorah test/live API server
31
     *
32
     * @var array
33
     */
34
    protected $config = [];
35
36
    /**
37
     * The URL used to connect to MyFatoorah test/live API server
38
     *
39
     * @var string
40
     */
41
    protected $apiURL = '';
42
43
    /**
44
     * The MyFatoorah PHP Library version
45
     *
46
     * @var string
47
     */
48
    protected $version = '2.2';
49
50
    //-----------------------------------------------------------------------------------------------------------------------------------------
51
52
    /**
53
     * Constructor that initiates a new MyFatoorah API process
54
     *
55
     * @param array $config It has the required keys (apiKey, isTest, and countryCode) to process a MyFatoorah API request.
56
     */
57
    public function __construct($config)
58
    {
59
60
        $mfCountries = self::getMFCountries();
61
62
        $this->setApiKey($config);
63
        $this->setIsTest($config);
64
        $this->setCountryCode($config);
65
66
        self::$loggerObj            = $this->config['loggerObj']  = empty($config['loggerObj']) ? null : $config['loggerObj'];
67
        self::$loggerFunc           = $this->config['loggerFunc'] = empty($config['loggerFunc']) ? null : $config['loggerFunc'];
68
69
        $code         = $this->config['countryCode'];
70
        $this->apiURL = $this->config['isTest'] ? $mfCountries[$code]['testv2'] : $mfCountries[$code]['v2'];
71
    }
72
73
    /**
74
     * Get the API URL
75
     * The URL used to connect to MyFatoorah test/live API server
76
     *
77
     * @return string
78
     */
79
    public function getApiURL()
80
    {
81
        return $this->apiURL;
82
    }
83
84
    //-----------------------------------------------------------------------------------------------------------------------------------------
85
86
    /**
87
     * Set the API token Key
88
     * The API Token Key is the authentication which identify a user that is using the app. To generate one follow instruction here https://myfatoorah.readme.io/docs/live-token
89
     *
90
     * @param array $config It has the required keys (apiKey, isTest, and countryCode) to process a MyFatoorah API request.
91
     *
92
     * @return void
93
     *
94
     * @throws Exception
95
     */
96
    protected function setApiKey($config)
97
    {
98
        if (empty($config['apiKey'])) {
99
            throw new Exception('Config array must have the "apiKey" key.');
100
        }
101
102
        $config['apiKey'] = trim($config['apiKey']);
103
        if (empty($config['apiKey'])) {
104
            throw new Exception('The "apiKey" key is required and must be a string.');
105
        }
106
107
        $this->config['apiKey'] = $config['apiKey'];
108
    }
109
110
    //-----------------------------------------------------------------------------------------------------------------------------------------
111
112
    /**
113
     * Set the test mode. Set it to false for live mode
114
     *
115
     * @param array $config It has the required keys (apiKey, isTest, and countryCode) to process a MyFatoorah API request.
116
     *
117
     * @return void
118
     *
119
     * @throws Exception
120
     */
121
    protected function setIsTest($config)
122
    {
123
        if (!isset($config['isTest'])) {
124
            throw new Exception('Config array must have the "isTest" key.');
125
        }
126
127
        if (!is_bool($config['isTest'])) {
128
            throw new Exception('The "isTest" key must be boolean.');
129
        }
130
131
        $this->config['isTest'] = $config['isTest'];
132
    }
133
134
    //-----------------------------------------------------------------------------------------------------------------------------------------
135
136
    /**
137
     * Set the country code of the used MyFatoorah account
138
     *
139
     * @param array $config It has the required keys (apiKey, isTest, and countryCode) to process a MyFatoorah API request.
140
     *
141
     * @return void
142
     *
143
     * @throws Exception
144
     */
145
    protected function setCountryCode($config)
146
    {
147
        if (empty($config['countryCode'])) {
148
            throw new Exception('Config array must have the "countryCode" key.');
149
        }
150
151
        $mfCountries    = self::getMFCountries();
152
        $countriesCodes = array_keys($mfCountries);
153
154
        $config['countryCode'] = strtoupper($config['countryCode']);
155
        if (!in_array($config['countryCode'], $countriesCodes)) {
156
            throw new Exception('The "countryCode" key must be one of (' . implode(', ', $countriesCodes) . ').');
157
        }
158
159
        $this->config['countryCode'] = $config['countryCode'];
160
    }
161
162
    //-----------------------------------------------------------------------------------------------------------------------------------------
163
164
    /**
165
     * It calls the MyFatoorah API endpoint request and handles the MyFatoorah API endpoint response.
166
     *
167
     * @param string          $url        MyFatoorah API endpoint URL
168
     * @param array|null      $postFields POST request parameters array. It should be set to null if the request is GET.
169
     * @param int|string|null $orderId    The order id or the payment id of the process, used for the events logging.
170
     * @param string|null     $function   The requester function name, used for the events logging.
171
     *
172
     * @return mixed       The response object as the result of a successful calling to the API.
173
     *
174
     * @throws Exception    Throw exception if there is any curl/validation error in the MyFatoorah API endpoint URL.
175
     */
176
    public function callAPI($url, $postFields = null, $orderId = null, $function = null)
177
    {
178
179
        //to prevent json_encode adding lots of decimal digits
180
        ini_set('precision', '14');
181
        ini_set('serialize_precision', '-1');
182
183
        $request = isset($postFields) ? 'POST' : 'GET';
184
        $fields  = empty($postFields) ? json_encode($postFields, JSON_FORCE_OBJECT) : json_encode($postFields);
185
186
        $msgLog = "Order #$orderId ----- $function";
187
        $this->log("$msgLog - Request: $fields");
188
189
        //***************************************
190
        //call url
191
        //***************************************
192
        $curl = curl_init($url);
193
194
        $option = [
195
            CURLOPT_CUSTOMREQUEST  => $request,
196
            CURLOPT_POSTFIELDS     => $fields,
197
            CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $this->config['apiKey'], 'Content-Type: application/json'],
198
            CURLOPT_RETURNTRANSFER => true
199
        ];
200
201
        curl_setopt_array($curl, $option);
202
203
        $res = curl_exec($curl);
204
        $err = curl_error($curl);
205
206
        curl_close($curl);
207
208
        //example set a local ip to host apitest.myfatoorah.com
209
        if ($err) {
210
            $this->log("$msgLog - cURL Error: $err");
211
            throw new Exception($err);
212
        }
213
214
        $this->log("$msgLog - Response: $res");
215
216
        $json = json_decode((string) $res);
217
218
        //***************************************
219
        //check for errors
220
        //***************************************
221
        //Check for the reponse errors
222
        $error = self::getAPIError($json, (string) $res);
223
        if ($error) {
224
            $this->log("$msgLog - Error: $error");
225
            throw new Exception($error);
226
        }
227
228
        //***************************************
229
        //Success
230
        //***************************************
231
        return $json;
232
    }
233
234
    //-----------------------------------------------------------------------------------------------------------------------------------------
235
236
    /**
237
     * Handle Endpoint Errors Function
238
     *
239
     * @param mixed  $json MyFatoorah response JSON object.
240
     * @param string $res  MyFatoorah response string.
241
     *
242
     * @return string
243
     */
244
    protected static function getAPIError($json, $res)
245
    {
246
247
        $isSuccess = $json->IsSuccess ?? false;
248
        if ($isSuccess) {
249
            return '';
250
        }
251
252
        //Check for the HTML errors
253
        $hErr = self::getHtmlErrors($res);
254
        if ($hErr) {
255
            return $hErr;
256
        }
257
258
        //Check for the JSON errors
259
        if (is_string($json)) {
260
            return $json;
261
        }
262
263
        if (empty($json)) {
264
            return (!empty($res) ? $res : 'Kindly review your MyFatoorah admin configuration due to a wrong entry.');
265
        }
266
267
        return self::getJsonErrors($json);
268
    }
269
270
    //-----------------------------------------------------------------------------------------------------------------------------------------
271
272
    /**
273
     * Check for the HTML (response model) errors
274
     *
275
     * @param string $res MyFatoorah response string.
276
     *
277
     * @return string
278
     */
279
    protected static function getHtmlErrors($res)
280
    {
281
        //to avoid blocked IP like:
282
        //<html>
283
        //<head><title>403 Forbidden</title></head>
284
        //<body>
285
        //<center><h1>403 Forbidden</h1></center><hr><center>Microsoft-Azure-Application-Gateway/v2</center>
286
        //</body>
287
        //</html>
288
        //and, skip apple register <YourDomainName> tag error
289
        $stripHtmlStr = strip_tags($res);
290
        if ($res != $stripHtmlStr && stripos($stripHtmlStr, 'apple-developer-merchantid-domain-association') !== false) {
291
            return trim(preg_replace('/\s+/', ' ', $stripHtmlStr));
292
        }
293
        return '';
294
    }
295
296
    //-----------------------------------------------------------------------------------------------------------------------------------------
297
298
    /**
299
     * Check for the json (response model) errors
300
     *
301
     * @param mixed $json MyFatoorah response JSON object.
302
     *
303
     * @return string
304
     */
305
    protected static function getJsonErrors($json)
306
    {
307
308
        $errorsVar = isset($json->ValidationErrors) ? 'ValidationErrors' : 'FieldsErrors';
309
        if (isset($json->$errorsVar)) {
310
            $blogDatas = array_column($json->$errorsVar, 'Error', 'Name');
311
312
            $mapFun = function ($k, $v) {
313
                return "$k: $v";
314
            };
315
            $errArr = array_map($mapFun, array_keys($blogDatas), array_values($blogDatas));
316
317
            return implode(', ', $errArr);
318
            //return implode(', ', array_column($json->ValidationErrors, 'Error'));
319
        }
320
321
        if (isset($json->Data->ErrorMessage)) {
322
            return $json->Data->ErrorMessage;
323
        }
324
325
        //if not, get the message.
326
        //sometimes Error value of ValidationErrors is null, so either get the "Name" key or get the "Message"
327
        //example {
328
        //"IsSuccess":false,
329
        //"Message":"Invalid data",
330
        //"ValidationErrors":[{"Name":"invoiceCreate.InvoiceItems","Error":""}],
331
        //"Data":null
332
        //}
333
        //example {
334
        //"Message":
335
        //"No HTTP resource was found that matches the request URI 'https://apitest.myfatoorah.com/v2/SendPayment222'.",
336
        //"MessageDetail":
337
        //"No route providing a controller name was found to match request URI 'https://apitest.myfatoorah.com/v2/SendPayment222'"
338
        //}
339
340
        return empty($json->Message) ? '' : $json->Message;
341
    }
342
343
    //-----------------------------------------------------------------------------------------------------------------------------------------
344
345
    /**
346
     * Log the events
347
     *
348
     * @param string $msg It is the string message that will be written in the log file.
349
     */
350
    public static function log($msg)
351
    {
352
353
        $loggerObj  = self::$loggerObj;
354
        $loggerFunc = self::$loggerFunc;
355
356
        if (empty($loggerObj)) {
357
            return;
358
        }
359
360
        if (is_string($loggerObj)) {
361
            error_log(PHP_EOL . date('d.m.Y h:i:s') . ' - ' . $msg, 3, $loggerObj);
362
        } elseif (method_exists($loggerObj, $loggerFunc)) {
363
            $loggerObj->{$loggerFunc}($msg);
364
        }
365
    }
366
367
    //-----------------------------------------------------------------------------------------------------------------------------------------
368
}
369