Passed
Push — main ( 4a5f0c...02f3c9 )
by MyFatoorah
06:23 queued 02:44
created

MyFatoorah::callAPI()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 56
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 26
c 1
b 0
f 0
nc 6
nop 4
dl 0
loc 56
rs 9.504

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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