MyFatoorah::getAPIError()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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