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
|
|
|
|