├── .github
└── workflows
│ └── ci-cd.yml
├── .gitignore
├── README.md
├── composer.json
├── composer.lock
├── phpstan.neon
├── phpunit.xml
├── phpunit.xml.bak
├── src
├── Events
│ ├── ApprovedRefundTransaction.php
│ ├── ApprovedSaleTransaction.php
│ ├── ApprovedTransaction.php
│ ├── ApprovedVoidRefundTransaction.php
│ ├── ApprovedVoidSaleTransaction.php
│ ├── BaseTransactionEvent.php
│ ├── DisallowedRequestEvent.php
│ ├── UnverfiedTransaction.php
│ └── VerfiedTransaction.php
├── Http
│ ├── Controllers
│ │ ├── ConfigController.php
│ │ └── NotificationController.php
│ ├── Middleware
│ │ └── AllowedIps.php
│ └── Requests
│ │ └── GenerateSecureKeyRequest.php
├── Models
│ └── MoamalatPayNotification.php
├── Pay.php
├── Providers
│ └── MoamalatPayProvider.php
├── Refund.php
├── Transaction.php
├── View
│ └── Components
│ │ └── Pay.php
├── config
│ └── moamalat-pay.php
├── database
│ ├── factories
│ │ └── MoamalatPayNotificationFactory.php.backup
│ └── migrations
│ │ └── 2022_09_12_000000_create_moamalat_pay_notifications_table.php
├── routes
│ └── api.php
└── views
│ └── pay.blade.php
└── tests
├── Feature
├── ConfigAPITest.php
├── GetTransactionTest.php
├── NotificationsAPITest.php
├── PayTest.php
└── RefundTest.php
├── TestCase.php
├── Unit
└── ExampleTest.php
└── _fixtures
└── transactions
└── verfied.json
/.github/workflows/ci-cd.yml:
--------------------------------------------------------------------------------
1 | # GitHub Action for Testing Laravel Package
2 |
3 | name: CI & Testing
4 |
5 | on:
6 | push:
7 | branches: [main]
8 | pull_request:
9 | branches: [main]
10 |
11 | jobs:
12 | laravel-ci:
13 | runs-on: ubuntu-latest
14 |
15 | strategy:
16 | fail-fast: false
17 | matrix:
18 | php-versions: ["8.2", "8.3", "8.4"]
19 |
20 | steps:
21 | - name: Setup PHP, with composer and extensions
22 | uses: shivammathur/setup-php@v2
23 | with:
24 | php-version: ${{ matrix.php-versions }}
25 | extensions: mbstring, dom, fileinfo
26 | coverage: xdebug #optional
27 |
28 | - name: Checkout
29 | uses: actions/checkout@v4
30 |
31 | - name: Show php version
32 | run: php -v
33 |
34 | - name: Install Dependencies
35 | run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
36 |
37 | - name: Static Analysis via PHPStan
38 | run: composer analyse
39 |
40 | - name: Test via PHPUnit
41 | run: composer test
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | .phpunit.result.cache
3 | .phpunit.cache/test-results
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Laravel Moamalat Pay
2 |
3 | [](https://github.com/alifaraun/laravel-moamalat-pay/actions)
4 | [](https://packagist.org/packages/alifaraun/laravel-moamalat-pay)
5 | [](https://packagist.org/packages/alifaraun/laravel-moamalat-pay)
6 | [](https://packagist.org/packages/alifaraun/laravel-moamalat-pay)
7 |
8 | This package allows you to easily use Moamalat [ligthbox](https://docs.moamalat.net/lightBox.html) to add payment gateway in your laravel project.
9 |
10 | ---
11 |
12 | **NOTE**
13 | This is not official library from Moamalat , It is just an open source Library.
14 |
15 | ---
16 |
17 | ## Table of contents
18 |
19 | - [Installation](#installation)
20 | - [Configuration File](#configuration-file)
21 | - [Configuration for testing purpose](#configuration-for-testing-purpose)
22 | - [Merchant](#merchant)
23 | - [Card](#card)
24 | - [Usage](#usage)
25 | - [Laravel componet](#laravel-componet)
26 | - [Laravel methods](#laravel-methods)
27 | - [Front end Javascript](#front-end-javascript)
28 | - [Check processing status](#check-processing-status)
29 | - [Get Transaction in back-end](#get-transaction-in-back-end)
30 | - [Examples](#examples)
31 | - [Notifications Service (Webhook)](#notifications-service-webhook)
32 | - [Available Scopes](#available-scopes)
33 | - [Events](#events)
34 | - [Refund and Void Transactions](#refund-and-void-transactions)
35 | - [Examples](#examples-1)
36 | - [Testing](#testing)
37 | - [Security](#security)
38 | - [Credits](#credits)
39 | - [License](#license)
40 |
41 | ## Installation
42 |
43 | You can install the package via composer:
44 |
45 | ```bash
46 | composer require alifaraun/laravel-moamalat-pay
47 | ```
48 |
49 | **Laravel Version Compatibility**
50 |
51 | | Laravel | Package | install command |
52 | | :------ | :------ | :------------------------------------------------------- |
53 | | 12.x.x | 6.x | `composer require alifaraun/laravel-moamalat-pay "^6.0"` |
54 | | 11.x.x | 5.x | `composer require alifaraun/laravel-moamalat-pay "^5.0"` |
55 | | 10.x.x | 4.x | `composer require alifaraun/laravel-moamalat-pay "^4.0"` |
56 | | 9.x.x | 3.x | `composer require alifaraun/laravel-moamalat-pay "^3.0"` |
57 | | 8.x.x | 2.x | `composer require alifaraun/laravel-moamalat-pay "^2.0"` |
58 | | ^7.25.0 | 1.x | `composer require alifaraun/laravel-moamalat-pay "^1.0"` |
59 |
60 | If you want to customize configurations:
61 |
62 | ```bash
63 | php artisan vendor:publish --tag="moamalat-pay"
64 | ```
65 |
66 | ## Configuration File
67 |
68 | The configuration file **moamalat-pay.php** is located in the **config** folder. Following are its contents when published:
69 |
70 | ```php
71 |
72 | return [
73 | /*
74 | |--------------------------------------------------------------------------
75 | | Moamalat Payment Gateway Config
76 | |--------------------------------------------------------------------------
77 | |
78 | | These options to set your configurations of muamalat
79 | |
80 | */
81 |
82 | // MID => merchant_id or outlet_number
83 | 'merchant_id' => env('MOAMALATPAY_MID'),
84 |
85 | // TID => terminal_id
86 | 'terminal_id' => env('MOAMALATPAY_TID'),
87 |
88 | // Secure key
89 | 'key' => env('MOAMALATPAY_KEY'),
90 |
91 | /*
92 | |--------------------------------------------------------------------------
93 | | Production
94 | |--------------------------------------------------------------------------
95 | |
96 | | If the production is set to "true", you will work on production environment
97 | | otherwise it will use testing environment
98 | |
99 | */
100 | 'production' => env('MOAMALATPAY_PRODUCTION', false),
101 |
102 | /*
103 | |--------------------------------------------------------------------------
104 | | Show
105 | |--------------------------------------------------------------------------
106 | |
107 | | If the show_logs is set to "true", you will see configurations
108 | | and response of requests in browser console
109 | |
110 | */
111 | 'show_logs' => false,
112 |
113 | /*
114 | |--------------------------------------------------------------------------
115 | | Enable Cancel Event On Success
116 | |--------------------------------------------------------------------------
117 | |
118 | | If the enable_cancel_event_on_success is set to "true", the cancel event will be triggered even if the payment is successful
119 | |
120 | */
121 | 'enable_cancel_event_on_success' => true,
122 |
123 | /*
124 | |--------------------------------------------------------------------------
125 | | Generate Secure Hash api
126 | |--------------------------------------------------------------------------
127 | |
128 | | This is service (api) to generate secureHash to be used in pay in Lightbox.
129 | |
130 | | url is route of api of generate secureHash
131 | |
132 | | route_name is name of route of api of generate secureHash
133 | |
134 | */
135 | 'generate-securekey' => [
136 | 'url' => 'moamalat-pay/securekey',
137 | 'route_name' => 'moamalat_pay.generate_securekey',
138 | ],
139 |
140 |
141 | /*
142 | |--------------------------------------------------------------------------
143 | | Notification (Webhook) api
144 | |--------------------------------------------------------------------------
145 | |
146 | | This is service from moamalat on any transaction you will receive notification
147 | | on api (webhook)
148 | |
149 | | key is your private notification key to use it in validate transaction requests
150 | |
151 | | url is route to receive notification
152 | |
153 | | table is name of table that will be used to save notifications
154 | |
155 | | allowed_ips are ips that will receive notification from them
156 | | ['*'] means receive from any ip but it is not secure to receive notifcations from anyone
157 | | you should ask moamalat on ips of their servers and use them
158 | |
159 | */
160 | 'notification' => [
161 | 'key' => env('MOAMALATPAY_NOTIFICATION_KEY'),
162 | 'url' => 'moamalat-pay/notify',
163 | 'route_name' => 'moamalat_pay.notification',
164 | 'table' => 'moamalat_pay_notifications',
165 | 'allowed_ips' => ['*'],
166 | ]
167 | ];
168 | ```
169 |
170 | set your configurations in `.env` file:
171 |
172 | ```bash
173 | MOAMALATPAY_MID=
174 | MOAMALATPAY_TID=
175 | MOAMALATPAY_KEY=
176 | MOAMALATPAY_NOTIFICATION_KEY=
177 | MOAMALATPAY_PRODUCTION=
178 | ```
179 |
180 | ## Configuration for testing purpose
181 |
182 | ### Merchant
183 |
184 | ```bash
185 | Merchant id : 10081014649
186 | Terminal Id : 99179395
187 | Secure key : 3a488a89b3f7993476c252f017c488bb
188 | ```
189 |
190 | ### Card
191 |
192 | ```bash
193 | Card : 6395043835180860
194 | Card : 6395043165725698
195 | Card : 6395043170446256
196 | EXP : 01/27
197 | OTP : 111111
198 | ```
199 |
200 | ## Usage
201 |
202 | #### Laravel componet
203 |
204 | ```blade
205 | // Initialize pay
206 |
207 |
208 | // when pass amount property, it will show pay form directly
209 |
210 | ```
211 |
212 | #### Laravel methods
213 |
214 | To call it using methods
215 |
216 | ```php
217 | // Initialize pay
218 | app('moamalat-pay')->init();
219 |
220 | // call pay
221 | // $amount int libyan dirham not dinar
222 | //$reference is optional
223 | app('moamalat-pay')->pay(int $amount,string $reference = '');
224 | ```
225 |
226 | #### Front end Javascript
227 |
228 | To call pay from js
229 |
230 | ```js
231 | _moamalatPay.pay(int amount,string reference = '')
232 | ```
233 |
234 | #### Check processing status
235 |
236 | Available events to check if operation success or fail
237 |
238 | ```js
239 | addEventListener("moamalatCompleted", function (e) {
240 | e.detail; // response data
241 | /* e.detail
242 | {
243 | "TxnDate": "250303164850",
244 | "SystemReference": "1301679",
245 | "NetworkReference": "506216680586",
246 | "MerchantReference": "test-demo",
247 | "Amount": "1000",
248 | "Currency": "434",
249 | "PaidThrough": "Card",
250 | "PayerAccount": "639504XXXXXX3733",
251 | "PayerName": "ALI",
252 | "ProviderSchemeName": "",
253 | "SecureHash": "915B22C4E2A96DB03F755D4254F65C87486AE3B1C19B9BCDE9481836832F4822",
254 | "CardToken": null,
255 | "CustomerId": null
256 | }
257 | */
258 | });
259 |
260 | addEventListener("moamalatError", function (e) {
261 | e.detail; // response data
262 | /* e.detail
263 | {
264 | "error": "CUBEEX5212216:Authentication Failed",
265 | "Amount": "200.031",
266 | "MerchantReferenece": "",
267 | "DateTimeLocalTrxn": "220818232732",
268 | "SecureHash": "1C8B1301AD4C00BE66EC25FD45A81D0C4030C79EF53CA903FA5009ECCAD08D46"
269 | }
270 | */
271 | });
272 |
273 | addEventListener("moamalatCancel", function () {});
274 | ```
275 |
276 | #### Get Transaction in back-end
277 |
278 | ```php
279 | use MoamalatPay\Transaction;
280 |
281 | // get transaction from NPG(moamalat)
282 | $transaction = new Transaction($networkReference, $merchantReference);
283 | // Throws an exception if there is a problem in loading the transaction
284 |
285 | /** available methods to interact with transaction **/
286 |
287 | /**
288 | * Get all properties of transaction
289 | * @return Array
290 | */
291 | $transaction->getAll();
292 |
293 | /**
294 | * Get property of transaction
295 | * @param $property key
296 | * @return mixed
297 | */
298 | $transaction->get($property);
299 |
300 |
301 | /**
302 | * Get property of reponse , if property not exists return default value
303 | *
304 | * @param $property
305 | * @param $default
306 | * @return mixed
307 | */
308 | $transaction->getWithDefault($property, $default = null);
309 |
310 | /**
311 | * Get all properties of reponse
312 | * @return Array
313 | */
314 | $transaction->getResponse();
315 |
316 | /**
317 | * Check status of transaction is Approved
318 | *
319 | * @param $amount
320 | * @param $card
321 | * @return boolean
322 | */
323 | $transaction->checkApproved($amount = null, $card = null);
324 |
325 | ```
326 |
327 | ##### Examples
328 |
329 | ```php
330 | use MoamalatPay\Transaction;
331 |
332 | // get transaction from NPG(moamalat)
333 | $transaction = new Transaction("223414600869","1641729671");
334 |
335 |
336 | $transaction->getAll();
337 | /* return
338 | [
339 | "TransactionChannel" => "Card",
340 | "CardNo" => "639504XXXXXX0860",
341 | "SenderName" => "OMAR",
342 | "CardType" => null,
343 | "Amnt" => "1000",
344 | "AmountTrxn" => "1000",
345 | "FeeAmnt" => "0.000",
346 | "TipAmnt" => "0.000",
347 | "Status" => "Approved",
348 | "Currency" => "LYD",
349 | "TransType" => "Sale",
350 | "IsPointTrasnaction" => false,
351 | "STAN" => "625396",
352 | "RRN" => "506118625396",
353 | "MerchantReference" => "test-demo",
354 | "ReceiptNo" => "506118625396",
355 | "MobileNumber" => null,
356 | "TransactionId" => "1301669",
357 | "ExternalTxnId" => null,
358 | "TxnDateTime" => "02/03/25 18=>25",
359 | "IsRefundEnabled" => true,
360 | "ResCodeDesc" => "Approved",
361 | "IsSend" => false,
362 | "ISForceSendCVCForRefund" => true,
363 | "HasToken" => true,
364 | "AuthCode" => null,
365 | "RefundButton" => 0,
366 | "RemainingRefundAmount" => "1000.000",
367 | "TerminalId" => 49077229,
368 | "IsRefund" => false,
369 | "RefundReason" => "",
370 | "RefundSource" => "",
371 | "RefundUserCreator" => "",
372 | "OriginalTxnId" => "",
373 | "TxnIcon" => 2,
374 | "IsMustVoidTotalAmount" => false,
375 | "IsCardPresent" => false,
376 | "RelatedTxnTotalAmount" => null,
377 | "UserDefinedData" => [],
378 | "InvoiceServiceSummary" => null
379 | ]
380 | */
381 |
382 | $transaction->get('CardNo');
383 | // return 639504XXXXXX0860
384 |
385 | $transaction->getWithDefault('Card','card-not-found');
386 | // return card-not-found
387 |
388 | $transaction->checkApproved();
389 | // if transaction status is Approved it will return true
390 |
391 | $transaction->checkApproved(10000,'639504XXXXXX0860');
392 | // if transaction is status is Approved , amount=10000 and CardNo=639504XXXXXX0860 it will return true
393 | ```
394 |
395 | ### Notifications Service (Webhook)
396 |
397 | Notification Services allow merchants to receive notifications whenever a transaction is generated for their accounts
398 |
399 | We save all notifications in database with fire events depends on transaction type and status
400 |
401 | ```php
402 | /*
403 | MoamalatPay\Models\MoamalatPayNotification \\ Eloquent Model
404 | \\ properites
405 | id
406 | MerchantId
407 | TerminalId
408 | DateTimeLocalTrxn
409 | TxnType
410 | Message
411 | PaidThrough
412 | SystemReference
413 | NetworkReference
414 | MerchantReference
415 | Amount
416 | Currency
417 | PayerAccount
418 | PayerName
419 | ActionCode
420 | request
421 | ip
422 | verified
423 | created_at
424 | */
425 | ```
426 |
427 | #### Available Scopes
428 |
429 | ```php
430 | // filter to get approved transactions (ActionCode = 00)
431 | MoamalatPay\Models\MoamalatPayNotification::approved()
432 |
433 | // filter to get verified transactions (verified = 1)
434 | MoamalatPay\Models\MoamalatPayNotification::verified()
435 |
436 | // filter to get transactions for currency terminal_id and merchant_id in config
437 | MoamalatPay\Models\MoamalatPayNotification::currentCredential()
438 | ```
439 |
440 | #### Events
441 |
442 | Example of how to add listener , check [laravel documentation](https://laravel.com/docs/events) for more info
443 |
444 | ```php
445 | // event will be fired when receive request from ip not exists in allowed_ips in config of moamalat-pay
446 | Event::listen(function (MoamalatPay\Events\DisallowedRequestEvent $event) {
447 | });
448 |
449 | // event will be fired after check secureHas is unverified
450 | Event::listen(function (MoamalatPay\Events\UnverfiedTransaction $event) {
451 | $event->notification // Eloquent Model of transaction
452 | });
453 |
454 | // event will be fired after check secureHas is verified
455 | Event::listen(function (MoamalatPay\Events\VerfiedTransaction $event) {
456 | $event->notification // Eloquent Model of transaction
457 | });
458 |
459 | // event will be fired after check secureHas is verified and transaction status is approved
460 | Event::listen(function (MoamalatPay\Events\ApprovedTransaction $event) {
461 | $event->notification // Eloquent Model of transaction
462 | });
463 |
464 | // event will be fired after check secureHas is verified and transaction status is approved
465 | // and type of transaction is : 1: Sale
466 | Event::listen(function (MoamalatPay\Events\ApprovedSaleTransaction $event) {
467 | $event->notification // Eloquent Model of transaction
468 | });
469 |
470 | // event will be fired after check secureHas is verified and transaction status is approved
471 | // and type of transaction is : 2: Refund
472 | Event::listen(function (MoamalatPay\Events\ApprovedRefundTransaction $event) {
473 | $event->notification // Eloquent Model of transaction
474 | });
475 |
476 | // event will be fired after check secureHas is verified and transaction status is approved
477 | // and type of transaction is : 3: Void Sale
478 | Event::listen(function (MoamalatPay\Events\ApprovedVoidSaleTransaction $event) {
479 | $event->notification // Eloquent Model of transaction
480 | });
481 |
482 | // event will be fired after check secureHas is verified and transaction status is approved
483 | // and type of transaction is : 4: Void Refund
484 | Event::listen(function (MoamalatPay\Events\ApprovedVoidRefundTransaction $event) {
485 | $event->notification // Eloquent Model of transaction
486 | });
487 |
488 | ```
489 |
490 | #### Refund and Void Transactions
491 |
492 | When the refund is called before settlement (usually settlement at the end of the day), it will be void, otherwise it will be refunded
493 |
494 | ```php
495 |
496 | /**
497 | * Refund transaction by system reference of transaction
498 | * @param string|integer $systemReference
499 | * @param string|integer $amount
500 | * @return array content response of moamalat
501 | */
502 | app('moamalat-pay-refund')->refundBySystemReference($systemReference, $amount)->getAll()
503 | // Throws an exception if there is a problem in refund the transaction
504 |
505 |
506 | /**
507 | * Refund transaction by network reference of transaction
508 | * @param string|integer $networkReference
509 | * @param string|integer $amount
510 | * @return array content response of moamalat
511 | */
512 | app('moamalat-pay-refund')->refundByNetworkReference($networkReference, $amount)->getAll()
513 | // Throws an exception if there is a problem in refund the transaction
514 |
515 | /* response : return of getAll() method
516 | {
517 | "SystemTxnId": 0,
518 | "ActionCode": null,
519 | "AuthCode": null,
520 | "ExternalTxnId": null,
521 | "RefNumber": "1301681", // System reference for the new refund transaction
522 | "TxnDate": null,
523 | "ReceiptNumber": null,
524 | "ReceiverAccountNumber": null,
525 | "ReceiverName": null,
526 | "ReceiverScheme": null,
527 | "MerchantReference": null,
528 | "SystemReference": 0,
529 | "NetworkReference": null,
530 | "IsEnableRefund": false,
531 | "DecimalFraction": 3,
532 | "IsYallaRefundPending": false,
533 | "TransactionId": null,
534 | "ReferenceId": null,
535 | "Success": true,
536 | "Message": "Approved",
537 | "SecureHashData": null,
538 | "SecureHash": null,
539 | "StatusCode": 200
540 | }
541 | */
542 | ```
543 |
544 | ##### Examples
545 |
546 | ```php
547 | $r = app('moamalat-pay-refund')->refundBySystemReference("1233114", "10");
548 | // or
549 | $r = app('moamalat-pay-refund')->refundByNetworkReference("223414600869", "10");
550 |
551 | // will return instance of MoamalatPay\Refund class
552 |
553 | /**
554 | * Get all properties of reponse
555 | * @return array
556 | */
557 | $r->getAll();
558 | /* response
559 | {
560 | "SystemTxnId": 0,
561 | "ActionCode": null,
562 | "AuthCode": null,
563 | "ExternalTxnId": null,
564 | "RefNumber": "1301681", // System reference for the new refund transaction
565 | "TxnDate": null,
566 | "ReceiptNumber": null,
567 | "ReceiverAccountNumber": null,
568 | "ReceiverName": null,
569 | "ReceiverScheme": null,
570 | "MerchantReference": null,
571 | "SystemReference": 0,
572 | "NetworkReference": null,
573 | "IsEnableRefund": false,
574 | "DecimalFraction": 3,
575 | "IsYallaRefundPending": false,
576 | "TransactionId": null,
577 | "ReferenceId": null,
578 | "Success": true,
579 | "Message": "Approved",
580 | "SecureHashData": null,
581 | "SecureHash": null,
582 | "StatusCode": 200
583 | }
584 | */
585 |
586 | /**
587 | * Get property of transaction
588 | * @param $property key
589 | * @return mixed
590 | */
591 | $r->get($property);
592 | $r->get('Message');
593 | // return Approved
594 |
595 |
596 | /**
597 | * Get property of reponse , if property not exists return default value
598 | *
599 | * @param $property
600 | * @param $default
601 | * @return mixed
602 | */
603 | $r->getWithDefault($property, $default = null);
604 | $r->getWithDefault('Card', 'No Card');
605 | // return No Card
606 |
607 |
608 | /**
609 | * Get SystemReference of new refund transaction
610 | * @return string|integer
611 | */
612 | $r->getRefNumber();
613 | // return 1233678
614 |
615 | ```
616 |
617 | ## Testing
618 |
619 | Run the tests with:
620 |
621 | ```
622 | composer test
623 | // or
624 | ./vendor/bin/phpunit
625 | ```
626 |
627 | Run Static Analysis Tool (PHPStan)
628 |
629 | ```
630 | composer analyse
631 | // or
632 | ./vendor/bin/phpstan analyse
633 | ```
634 |
635 |
640 |
641 | ## Security
642 |
643 | If you discover any security related issues, please email ali1996426@hotmail.com instead of using the issue tracker.
644 |
645 | ## Credits
646 |
647 | - [Ali Faraun](https://github.com/alifaraun)
648 | - [All Contributors](../../contributors)
649 |
650 | ## License
651 |
652 | The MIT License (MIT)
653 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "alifaraun/laravel-moamalat-pay",
3 | "type": "library",
4 | "description": "Easy - Moamalat Lightbox integration for Laravel.",
5 | "require": {
6 | "laravel/framework": "^12.0"
7 | },
8 | "license": "MIT",
9 | "autoload": {
10 | "psr-4": {
11 | "MoamalatPay\\": "src/"
12 | }
13 | },
14 | "autoload-dev": {
15 | "psr-4": {
16 | "MoamalatPay\\Tests\\": "tests"
17 | }
18 | },
19 | "scripts": {
20 | "test": "vendor/bin/testbench package:test",
21 | "analyse": "vendor/bin/phpstan analyse --memory-limit=2G",
22 | "lint": [
23 | "@php vendor/bin/pint",
24 | "@php vendor/bin/phpstan analyse"
25 | ]
26 | },
27 | "extra": {
28 | "laravel": {
29 | "providers": [
30 | "MoamalatPay\\Providers\\MoamalatPayProvider"
31 | ]
32 | }
33 | },
34 | "authors": [
35 | {
36 | "name": "Ali Faraun",
37 | "email": "ali1996426@hotmail.com"
38 | }
39 | ],
40 | "minimum-stability": "dev",
41 | "require-dev": {
42 | "guzzlehttp/guzzle": "7.9.2",
43 | "nunomaduro/collision": "^8.0",
44 | "laravel/legacy-factories": "1.x-dev",
45 | "laravel/pint": "dev-main",
46 | "larastan/larastan": "3.x-dev",
47 | "orchestra/testbench": "10.x-dev"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | includes:
2 | - vendor/larastan/larastan/extension.neon
3 |
4 | parameters:
5 | paths:
6 | - src
7 |
8 | # The level 9 is the highest level
9 | level: 5
10 |
11 | ignoreErrors:
12 | # Ignore "env outside of the config directory" errors
13 | -
14 | message: "#Called 'env' outside of the config directory.*#"
15 | paths:
16 | - src/config/moamalat-pay.php
17 |
18 | # Ignore "view-string" type errors
19 | -
20 | message: '#Parameter \#1 \$view of function view expects view-string\|null, string given.*#'
21 | paths:
22 | - src/Pay.php
23 | - src/View/Components/Pay.php
24 |
25 | # Uncomment to exclude specific files
26 | # excludePaths:
27 | # - ./*/*/FileToBeExcluded.php
28 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./tests/Unit
6 |
7 |
8 | ./tests/Feature
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | ./src
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/phpunit.xml.bak:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./tests/Unit
6 |
7 |
8 | ./tests/Feature
9 |
10 |
11 |
12 |
13 | ./src
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/Events/ApprovedRefundTransaction.php:
--------------------------------------------------------------------------------
1 | notification = $notification;
30 | }
31 |
32 | /**
33 | * Get the channels the event should broadcast on.
34 | *
35 | * @return \Illuminate\Broadcasting\Channel|array
36 | */
37 | public function broadcastOn()
38 | {
39 | return new PrivateChannel('channel-name');
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Events/DisallowedRequestEvent.php:
--------------------------------------------------------------------------------
1 | amount;
23 | $merchantReference = $request->merchantReference;
24 | $key = hex2bin(config('moamalat-pay.key'));
25 | $DateTimeLocalTrxn = time();
26 | $encode_data = "Amount={$amount}&DateTimeLocalTrxn={$DateTimeLocalTrxn}&MerchantId={$MerchantId}&MerchantReference={$merchantReference}&TerminalId={$TerminalId}";
27 |
28 | return response()->json([
29 | 'secureHash' => hash_hmac('sha256', $encode_data, $key),
30 | 'DateTimeLocalTrxn' => $DateTimeLocalTrxn,
31 | ]);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Http/Controllers/NotificationController.php:
--------------------------------------------------------------------------------
1 | validate([
25 | 'MerchantId' => 'nullable',
26 | 'TerminalId' => 'nullable',
27 | 'DateTimeLocalTrxn' => 'nullable',
28 | 'TxnType' => 'nullable',
29 | 'Message' => 'nullable',
30 | 'PaidThrough' => 'nullable',
31 | 'SystemReference' => 'nullable',
32 | 'NetworkReference' => 'nullable',
33 | 'MerchantReference' => 'nullable',
34 | 'Amount' => 'nullable',
35 | 'Currency' => 'nullable',
36 | 'PayerAccount' => 'nullable',
37 | 'PayerName' => 'nullable',
38 | 'ActionCode' => 'nullable',
39 | ]);
40 |
41 | $data['ip'] = $request->ip();
42 | $data['request'] = json_encode($request->all());
43 | $data['verified'] = $this->validateSecureHas(
44 | $request->input('SecureHash'),
45 | $request->input('Amount'),
46 | $request->input('Currency'),
47 | $request->input('DateTimeLocalTrxn'),
48 | $request->input('MerchantId'),
49 | $request->input('TerminalId')
50 | );
51 |
52 | $notification = MoamalatPayNotification::create($data);
53 |
54 | $this->dispatchEvents($notification);
55 |
56 | return response()->json(['Message' => 'Success', 'Success' => true]);
57 | }
58 |
59 | protected function dispatchEvents(MoamalatPayNotification $notification)
60 | {
61 | if ($notification->verified) {
62 | VerfiedTransaction::dispatch($notification);
63 | if (/* $notification->Message == 'Approved' && */ $notification->ActionCode === '00') { // aproved
64 | ApprovedTransaction::dispatch($notification);
65 | switch ($notification->TxnType) {
66 | case '1':
67 | ApprovedSaleTransaction::dispatch($notification);
68 | break;
69 | case '2':
70 | ApprovedRefundTransaction::dispatch($notification);
71 | break;
72 | case '3':
73 | ApprovedVoidSaleTransaction::dispatch($notification);
74 | break;
75 | case '4':
76 | ApprovedVoidRefundTransaction::dispatch($notification);
77 | break;
78 | }
79 | }
80 | } else {
81 | UnverfiedTransaction::dispatch($notification);
82 | }
83 | }
84 |
85 | /**
86 | * Validate if secure has correct to make sure notification is comming from Moamalat
87 | */
88 | protected function validateSecureHas($secureHash, $Amount, $Currency, $DateTimeLocalTrxn, $MerchantId, $TerminalId)
89 | {
90 | try {
91 | $encode_data = "Amount=$Amount&Currency=$Currency&DateTimeLocalTrxn=$DateTimeLocalTrxn&MerchantId=$MerchantId&TerminalId=$TerminalId";
92 | $key = pack('H*', config('moamalat-pay.notification.key'));
93 |
94 | return strtoupper(hash_hmac('sha256', $encode_data, $key)) === strtoupper($secureHash);
95 | } catch (Exception $e) {
96 | return false;
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/Http/Middleware/AllowedIps.php:
--------------------------------------------------------------------------------
1 | ip(), $allowed)) {
20 | DisallowedRequestEvent::dispatch();
21 | abort(403);
22 | }
23 |
24 | return $next($request);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Http/Requests/GenerateSecureKeyRequest.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | public function rules(): array
16 | {
17 | return [
18 | 'MID' => 'required',
19 | 'TID' => 'required',
20 | 'amount' => 'required|integer|min:1',
21 | 'merchantReference' => 'nullable',
22 | ];
23 | }
24 |
25 | /**
26 | * Get the "after" validation callables for the request.
27 | */
28 | public function after(): array
29 | {
30 | return [
31 | function (Validator $validator) {
32 |
33 | if (! $validator->errors()->has('MID') && $this->MID != config('moamalat-pay.merchant_id')) {
34 | $validator->errors()->add('MID', 'The MID is incorrect');
35 | }
36 |
37 | if (! $validator->errors()->has('TID') && $this->TID != config('moamalat-pay.terminal_id')) {
38 | $validator->errors()->add('TID', 'The TID is incorrect');
39 | }
40 | },
41 | ];
42 | }
43 |
44 | public function attributes()
45 | {
46 | return [
47 | 'MID' => 'MID',
48 | 'TID' => 'TID',
49 | ];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Models/MoamalatPayNotification.php:
--------------------------------------------------------------------------------
1 |
57 | */
58 | protected function casts(): array
59 | {
60 | return [
61 | 'MerchantId' => 'string',
62 | 'TerminalId' => 'string',
63 | 'DateTimeLocalTrxn' => 'string',
64 | 'TxnType' => 'string',
65 | 'Message' => 'string',
66 | 'PaidThrough' => 'string',
67 | 'SystemReference' => 'string',
68 | 'NetworkReference' => 'string',
69 | 'MerchantReference' => 'string',
70 | 'Amount' => 'string',
71 | 'Currency' => 'string',
72 | 'PayerAccount' => 'string',
73 | 'PayerName' => 'string',
74 | 'ActionCode' => 'string',
75 | 'request' => 'string',
76 | 'verified' => 'boolean',
77 | ];
78 | }
79 |
80 | /**
81 | * Get the table associated with the model.
82 | *
83 | * @return string
84 | */
85 | public function getTable()
86 | {
87 | return config('moamalat-pay.notification.table', parent::getTable());
88 | }
89 |
90 | /**
91 | * Scope a query to only include approved transactions.
92 | *
93 | * @param \Illuminate\Database\Eloquent\Builder $query
94 | * @return void
95 | */
96 | public function scopeApproved($query)
97 | {
98 | $query->where('ActionCode', '00');
99 | }
100 |
101 | /**
102 | * Scope a query to only include verified transactions.
103 | *
104 | * @param \Illuminate\Database\Eloquent\Builder $query
105 | * @return void
106 | */
107 | public function scopeVerified($query)
108 | {
109 | $query->where('verified', 1);
110 | }
111 |
112 | /**
113 | * Scope a query to only include transactions with current credentials terminal_id and mercahnt_id.
114 | *
115 | * @param \Illuminate\Database\Eloquent\Builder $query
116 | * @return void
117 | */
118 | public function scopeCurrentCredential($query)
119 | {
120 | $query
121 | ->where('MerchantId', config('moamalat-pay.merchant_id'))
122 | ->where('TerminalId', config('moamalat-pay.terminal_id'));
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/Pay.php:
--------------------------------------------------------------------------------
1 | render();
10 | }
11 |
12 | public function pay($amount, $reference = '')
13 | {
14 | return "";
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Providers/MoamalatPayProvider.php:
--------------------------------------------------------------------------------
1 | loadViewsFrom(__DIR__.'/../views', 'moamalat-pay');
23 | $this->loadRoutesFrom(__DIR__.'/../routes/api.php');
24 | $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
25 | $this->loadFactoriesFrom(__DIR__.'/../database/factories');
26 | Blade::component('moamalat-pay', PayComponent::class);
27 |
28 | $router = $this->app->make(Router::class);
29 | $router->aliasMiddleware('moamalat-allowed-ips', AllowedIps::class);
30 | }
31 |
32 | /**
33 | * Register any application services.
34 | *
35 | * @return void
36 | */
37 | public function register()
38 | {
39 | $this->publishes([
40 | __DIR__.'/../config/moamalat-pay.php' => config_path('moamalat-pay.php'),
41 | ], 'moamalat-pay');
42 |
43 | $this->mergeConfigFrom(__DIR__.'/../config/moamalat-pay.php', 'moamalat-pay');
44 |
45 | $this->app->singleton('moamalat-pay', function ($app) {
46 | return new Pay;
47 | });
48 |
49 | $this->app->singleton('moamalat-pay-refund', function ($app) {
50 | return new Refund;
51 | });
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Refund.php:
--------------------------------------------------------------------------------
1 | terminal_id = config('moamalat-pay.terminal_id');
46 | $this->merchant_id = config('moamalat-pay.merchant_id');
47 | $this->key = hex2bin(config('moamalat-pay.key'));
48 | }
49 |
50 | /**
51 | * Refund transaction
52 | *
53 | * @param array $extra
54 | * @return mixed
55 | */
56 | protected function refund($extra)
57 | {
58 | $DateTimeLocalTrxn = time();
59 | $encode_data = "DateTimeLocalTrxn={$DateTimeLocalTrxn}&MerchantId={$this->merchant_id}&TerminalId={$this->terminal_id}";
60 |
61 | if (config('moamalat-pay.production')) {
62 | $url = 'https://npg.moamalat.net/cube/paylink.svc/api/RefundTransaction';
63 | } else {
64 | $url = 'https://tnpg.moamalat.net/cube/paylink.svc/api/RefundTransaction';
65 | }
66 |
67 | $response = Http::post($url, array_merge([
68 | 'TerminalId' => $this->terminal_id,
69 | 'MerchantId' => $this->merchant_id,
70 | 'DateTimeLocalTrxn' => $DateTimeLocalTrxn,
71 | 'SecureHash' => hash_hmac('sha256', $encode_data, $this->key),
72 | ], $extra));
73 |
74 | if ($response->status() != 200 || $response['Success'] != true) {
75 | throw new Exception($response->offsetGet('Message'));
76 | }
77 |
78 | $this->response = $response->json();
79 |
80 | return $this;
81 | }
82 |
83 | /**
84 | * Refund transaction by system reference of transaction
85 | *
86 | * @param string|int $systemReference
87 | * @param string|int $amount
88 | * @return $this
89 | */
90 | public function refundBySystemReference($systemReference, $amount)
91 | {
92 | return $this->refund([
93 | 'SystemReference' => $systemReference,
94 | 'AmountTrxn' => $amount,
95 | ]);
96 | }
97 |
98 | /**
99 | * Refund transaction by network reference of transaction
100 | *
101 | * @param string|int $networkReference
102 | * @param string|int $amount
103 | * @return $this
104 | */
105 | public function refundByNetworkReference($networkReference, $amount)
106 | {
107 | return $this->refund([
108 | 'NetworkReference' => $networkReference,
109 | 'AmountTrxn' => $amount,
110 | ]);
111 | }
112 |
113 | /**
114 | * Get all properties of reponse
115 | *
116 | * @return array
117 | */
118 | public function getAll()
119 | {
120 | return $this->response;
121 | }
122 |
123 | /**
124 | * Get property of transaction
125 | *
126 | * @param $property key
127 | * @return mixed
128 | */
129 | public function get($property)
130 | {
131 | return $this->response[$property];
132 | }
133 |
134 | /**
135 | * Get property of reponse , if property not exists return default value
136 | *
137 | * @return mixed
138 | */
139 | public function getWithDefault($property, $default = null)
140 | {
141 | if (array_key_exists($property, $this->response)) {
142 | return $this->response[$property];
143 | }
144 |
145 | return $default;
146 | }
147 |
148 | /**
149 | * Get SystemReference of new refund transaction
150 | *
151 | * @return string|int
152 | */
153 | public function getRefNumber()
154 | {
155 | return $this->get('RefNumber');
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/Transaction.php:
--------------------------------------------------------------------------------
1 | $networkReference,
49 | 'MerchantReference' => $merchantReference,
50 | 'TerminalId' => $TerminalId,
51 | 'MerchantId' => $MerchantId,
52 | 'DisplayLength' => 1,
53 | 'DisplayStart' => 0,
54 | 'DateTimeLocalTrxn' => $DateTimeLocalTrxn,
55 | 'SecureHash' => hash_hmac('sha256', $encode_data, $key),
56 | ]);
57 |
58 | if ($response->status() != 200 || $response['TotalCountAllTransaction'] != 1) {
59 | throw new Exception($response->offsetGet('Message'));
60 | }
61 |
62 | $this->response = $response->json();
63 | $this->data = $this->response['Transactions'][0]['DateTransactions'][0];
64 | }
65 |
66 | /**
67 | * Get all properties of transaction
68 | *
69 | * @return array
70 | */
71 | public function getAll()
72 | {
73 | return $this->data;
74 | }
75 |
76 | /**
77 | * Get property of transaction
78 | *
79 | * @param $property key
80 | * @return mixed
81 | */
82 | public function get($property)
83 | {
84 | return $this->data[$property];
85 | }
86 |
87 | /**
88 | * Get property of reponse , if property not exists return default value
89 | *
90 | * @return mixed
91 | */
92 | public function getWithDefault($property, $default = null)
93 | {
94 | if (array_key_exists($property, $this->data)) {
95 | return $this->data[$property];
96 | }
97 |
98 | return $default;
99 | }
100 |
101 | /**
102 | * Get all properties of reponse
103 | *
104 | * @return \Illuminate\Http\Client\Response
105 | */
106 | public function getResponse()
107 | {
108 | return $this->response;
109 | }
110 |
111 | /**
112 | * Check status of transaction is Approved
113 | *
114 | * @return bool
115 | */
116 | public function checkApproved($amount = null, $card = null)
117 | {
118 | $result = true;
119 | if ($amount != null) {
120 | $result = /* $result && */ $this->data['AmountTrxn'] == $amount;
121 | }
122 | if ($card != null) {
123 | $result = $result && $this->data['CardNo'] == $card;
124 | }
125 |
126 | return $result && $this->data['Status'] == 'Approved';
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/View/Components/Pay.php:
--------------------------------------------------------------------------------
1 | amount = $amount;
33 | $this->reference = $reference;
34 | }
35 |
36 | /**
37 | * Get the view / contents that represent the component.
38 | *
39 | * @return \Illuminate\View\View|\Closure|string
40 | */
41 | public function render()
42 | {
43 | return view('moamalat-pay::pay');
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/config/moamalat-pay.php:
--------------------------------------------------------------------------------
1 | merchant_id or outlet_number
14 | 'merchant_id' => env('MOAMALATPAY_MID'),
15 |
16 | // TID => terminal_id
17 | 'terminal_id' => env('MOAMALATPAY_TID'),
18 |
19 | // Secure key
20 | 'key' => env('MOAMALATPAY_KEY'),
21 |
22 | /*
23 | |--------------------------------------------------------------------------
24 | | Production
25 | |--------------------------------------------------------------------------
26 | |
27 | | If the production is set to "true", you will work on production environment
28 | | otherwise it will use testing environment
29 | |
30 | */
31 | 'production' => env('MOAMALATPAY_PRODUCTION', false),
32 |
33 | /*
34 | |--------------------------------------------------------------------------
35 | | Show
36 | |--------------------------------------------------------------------------
37 | |
38 | | If the show_logs is set to "true", you will see configurations
39 | | and response of requests in browser console
40 | |
41 | */
42 | 'show_logs' => false,
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Enable Cancel Event On Success
47 | |--------------------------------------------------------------------------
48 | |
49 | | If the enable_cancel_event_on_success is set to "true", the cancel event will be triggered even if the payment is successful
50 | |
51 | */
52 | 'enable_cancel_event_on_success' => true,
53 |
54 | /*
55 | |--------------------------------------------------------------------------
56 | | Generate Secure Hash api
57 | |--------------------------------------------------------------------------
58 | |
59 | | This is service (api) to generate secureHash to be used in pay in Lightbox.
60 | |
61 | | url is route of api of generate secureHash
62 | |
63 | | route_name is name of route of api of generate secureHash
64 | |
65 | */
66 | 'generate-securekey' => [
67 | 'url' => 'moamalat-pay/securekey',
68 | 'route_name' => 'moamalat_pay.generate_securekey',
69 | ],
70 |
71 | /*
72 | |--------------------------------------------------------------------------
73 | | Notification (Webhook) api
74 | |--------------------------------------------------------------------------
75 | |
76 | | This is service from moamalat on any transaction you will receive notification
77 | | on api (webhook)
78 | |
79 | | key is your private notification key to use it in validate transaction requests
80 | |
81 | | url is route to receive notification
82 | |
83 | | table is name of table that will be used to save notifications
84 | |
85 | | allowed_ips are ips that will receive notification from them
86 | | ['*'] means receive from any ip but it is not secure to receive notifcations from anyone
87 | | you should ask moamalat on ips of their servers and use them
88 | |
89 | */
90 | 'notification' => [
91 | 'key' => env('MOAMALATPAY_NOTIFICATION_KEY'),
92 | 'url' => 'moamalat-pay/notify',
93 | 'route_name' => 'moamalat_pay.notification',
94 | 'table' => 'moamalat_pay_notifications',
95 | 'allowed_ips' => ['*'],
96 |
97 | ],
98 | ];
99 |
--------------------------------------------------------------------------------
/src/database/factories/MoamalatPayNotificationFactory.php.backup:
--------------------------------------------------------------------------------
1 | config('moamalat-pay.merchant_id'),
19 | 'TerminalId' => config('moamalat-pay.terminal_id'),
20 | 'DateTimeLocalTrxn' => now(),
21 | 'TxnType' => $this->faker->numberBetween(1, 4),
22 | 'Message' => $this->faker->word(),
23 | 'PaidThrough' => $this->faker->randomElement(['Card', 'Tahweel']),
24 | 'SystemReference' => $this->faker->uuid(),
25 | 'NetworkReference' => $this->faker->uuid(),
26 | 'MerchantReference' => $this->faker->uuid(),
27 | 'Amount' => $this->faker->randomNumber(),
28 | 'Currency' => 434,
29 | 'PayerAccount' => $this->faker->creditCardNumber(),
30 | 'PayerName' => $this->faker->word(),
31 | 'ActionCode' => $this->faker->randomNumber(2),
32 | ];
33 |
34 | return $data + [
35 | 'request' => json_encode($data),
36 | 'verified' => $this->faker->boolean(),
37 | 'ip' => $this->faker->ipv4()
38 | ];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/database/migrations/2022_09_12_000000_create_moamalat_pay_notifications_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('MerchantId', 190)->nullable();
19 | $table->string('TerminalId', 190)->nullable();
20 | $table->string('DateTimeLocalTrxn', 190)->nullable();
21 | $table->string('TxnType', 190)->nullable();
22 | $table->string('Message', 190)->nullable();
23 | $table->string('PaidThrough', 190)->nullable();
24 | $table->string('SystemReference', 190)->nullable();
25 | $table->string('NetworkReference', 190)->nullable();
26 | $table->string('MerchantReference', 190)->nullable();
27 | $table->string('Amount', 190)->nullable();
28 | $table->string('Currency', 190)->nullable();
29 | $table->string('PayerAccount', 190)->nullable();
30 | $table->string('PayerName', 190)->nullable();
31 | $table->string('ActionCode', 190)->nullable();
32 | $table->json('request')->nullable();
33 | $table->string('ip', 190)->nullable();
34 | $table->boolean('verified');
35 | $table->timestamps();
36 |
37 | $table->index(['verified', 'NetworkReference', 'MerchantReference', 'MerchantId', 'TerminalId'], 'verified_networkRef_merchantRef_mid_tid');
38 | });
39 | }
40 |
41 | /**
42 | * Reverse the migrations.
43 | *
44 | * @return void
45 | */
46 | public function down()
47 | {
48 | Schema::dropIfExists(config('moamalat-pay.notification.table'));
49 | }
50 | };
51 |
--------------------------------------------------------------------------------
/src/routes/api.php:
--------------------------------------------------------------------------------
1 | prefix('api')
20 | ->group(function () {
21 |
22 | Route::post(config('moamalat-pay.notification.url'), [NotificationController::class, 'store'])
23 | ->middleware('moamalat-allowed-ips')
24 | ->name(config('moamalat-pay.notification.route_name'));
25 |
26 | Route::get(config('moamalat-pay.generate-securekey.url'), [ConfigController::class, 'generateSecureKey'])
27 | ->name(config('moamalat-pay.generate-securekey.route_name'));
28 | });
29 |
--------------------------------------------------------------------------------
/src/views/pay.blade.php:
--------------------------------------------------------------------------------
1 | @once
2 |
3 | @if (config('moamalat-pay.production'))
4 |
5 | @else
6 |
7 | @endif
8 |
9 |
10 |
11 |
12 |
135 |
136 |
137 | @endonce
138 |
--------------------------------------------------------------------------------
/tests/Feature/ConfigAPITest.php:
--------------------------------------------------------------------------------
1 | assertNotNull(config('moamalat-pay.generate-securekey.url'));
17 | $this->assertNotNull(config('moamalat-pay.generate-securekey.route_name'));
18 | }
19 |
20 | /**
21 | * Test generating a secure key with valid parameters.
22 | *
23 | * @return void
24 | */
25 | public function test_generate_secure_key()
26 | {
27 | $params = [
28 | 'MID' => config('moamalat-pay.merchant_id'),
29 | 'TID' => config('moamalat-pay.terminal_id'),
30 | 'amount' => '1000',
31 | ];
32 |
33 | $this->getJson(route(config('moamalat-pay.generate-securekey.route_name'), $params))
34 | ->assertOk()
35 | ->assertJsonStructure(['secureHash', 'DateTimeLocalTrxn']);
36 | }
37 |
38 | /**
39 | * Test validation errors when generating a secure key without required parameters.
40 | *
41 | * @return void
42 | */
43 | public function test_generate_secure_key_validation()
44 | {
45 | $this->getJson(route(config('moamalat-pay.generate-securekey.route_name')))
46 | ->assertUnprocessable()
47 | ->assertJson([
48 | 'message' => 'The MID field is required. (and 2 more errors)',
49 | 'errors' => [
50 | 'MID' => [
51 | 'The MID field is required.',
52 | ],
53 | 'TID' => [
54 | 'The TID field is required.',
55 | ],
56 | 'amount' => [
57 | 'The amount field is required.',
58 | ],
59 | ],
60 | ], true);
61 | }
62 |
63 | /**
64 | * Test generating a secure key with incorrect merchant/terminal IDs.
65 | *
66 | * @return void
67 | */
68 | public function test_generate_secure_key_with_incrorrect_configurations()
69 | {
70 | $params = [
71 | 'MID' => '100000009',
72 | 'TID' => '400000029',
73 | 'amount' => '1000',
74 | ];
75 |
76 | $this->getJson(route(config('moamalat-pay.generate-securekey.route_name'), $params))
77 | ->assertUnprocessable()
78 | ->assertJson([
79 | 'message' => 'The MID is incorrect (and 1 more error)',
80 | 'errors' => [
81 | 'MID' => [
82 | 'The MID is incorrect',
83 | ],
84 | 'TID' => [
85 | 'The TID is incorrect',
86 | ],
87 | ],
88 | ], true);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/tests/Feature/GetTransactionTest.php:
--------------------------------------------------------------------------------
1 | null,
22 | 'Success' => true,
23 | 'TotalAmountAllTransaction' => 1000,
24 | 'TotalAmountTipsTransaction' => 0,
25 | 'TotalCountAllTransaction' => 1,
26 | 'Transactions' => [
27 | [
28 | 'Date' => '03/09/2022',
29 | 'DateTotalAmount' => '1000',
30 | 'DateTransactions' => [
31 | [
32 | 'Amnt' => '1000',
33 | 'AmountTrxn' => '1000',
34 | 'AuthCode' => null,
35 | 'CardNo' => '639499XXXXXX2740',
36 | 'CardType' => '',
37 | 'Currency' => 'LYD',
38 | 'ExternalTxnId' => null,
39 | 'FeeAmnt' => '0',
40 | 'HasToken' => true,
41 | 'ISForceSendCVCForRefund' => true,
42 | 'IsMustVoidTotalAmount' => false,
43 | 'IsPointTrasnaction' => false,
44 | 'IsRefund' => false,
45 | 'IsRefundEnabled' => true,
46 | 'IsSend' => false,
47 | 'MerchantReference' => '475217323',
48 | 'MobileNumber' => null,
49 | 'OriginalTxnId' => '',
50 | 'RRN' => '224601434990',
51 | 'ReceiptNo' => '224601434990',
52 | 'RefundButton' => 0,
53 | 'RefundReason' => '',
54 | 'RefundSource' => '',
55 | 'RefundUserCreator' => '',
56 | 'RelatedTxnTotalAmount' => null,
57 | 'RemainingRefundAmount' => '1000',
58 | 'ResCodeDesc' => 'Approved',
59 | 'STAN' => '434990',
60 | 'SenderName' => 'MS',
61 | 'Status' => 'Approved',
62 | 'TipAmnt' => '0',
63 | 'TransType' => 'Sale',
64 | 'TransactionChannel' => 'Card',
65 | 'TransactionId' => '1233317',
66 | 'TxnDateTime' => '03/09/22 01:44',
67 | 'TxnIcon' => 2,
68 | ],
69 | ],
70 | ],
71 | ],
72 |
73 | ];
74 |
75 | // set fake requests to make testing faster
76 | Http::fake([
77 | // 'https://tnpg.moamalat.net/cube/paylink.svc/api/FilterTransactions' => Http::response($respone, 200),
78 | 'https://tnpg.moamalat.net/cube/paylink.svc/api/FilterTransactions' => function ($r) use ($respone) {
79 | if ($r->offsetGet('MerchantId') == 'testing_authentication_failed') { // response for testing authentication failed
80 | return Http::response([
81 | 'Message' => 'Authentication failed.',
82 | 'StackTrace' => null,
83 | 'ExceptionType' => 'System.InvalidOperationException',
84 | ], 401);
85 | }
86 | if ($r->offsetGet('NetworkReference') == 'testing_not_found') { // response for testing transaction not found
87 | return Http::response([
88 | 'Message' => 'Transaction not found',
89 | 'Success' => true,
90 | 'TotalAmountAllTransaction' => 0,
91 | 'TotalAmountTipsTransaction' => null,
92 | 'TotalCountAllTransaction' => 0,
93 | 'Transactions' => [],
94 | ]);
95 | }
96 |
97 | return $respone; // response for testing success request
98 | },
99 | ]);
100 |
101 | $this->transaction = new Transaction('224601434990', '475217323');
102 | }
103 |
104 | /**
105 | * Test config.
106 | */
107 | public function test_transaction_not_found()
108 | {
109 | $this->expectExceptionMessage('Transaction not found');
110 | new Transaction('testing_not_found', '475217323');
111 | }
112 |
113 | /**
114 | * Test config.
115 | */
116 | public function test_authentication_failed()
117 | {
118 | $this->expectExceptionMessage('Authentication failed.');
119 | Config::set('moamalat-pay.merchant_id', 'testing_authentication_failed');
120 | new Transaction('224601434990', '475217323');
121 | }
122 |
123 | /**
124 | * Test config.
125 | */
126 | public function test_get_property()
127 | {
128 | $this->assertEquals('639499XXXXXX2740', $this->transaction->get('CardNo'));
129 | // $this->expectExceptionMessage('Undefined index: CardNotFound');
130 | // $this->assertEquals('639499XXXXXX2740', $this->transaction->get('CardNotFound'));
131 | }
132 |
133 | /**
134 | * Test config.
135 | */
136 | public function test_get_property_with_default_value()
137 | {
138 | $this->assertEquals('card-not-found', $this->transaction->getWithDefault('Card', 'card-not-found'));
139 | }
140 |
141 | /**
142 | * Test config.
143 | */
144 | public function test_check_approved()
145 | {
146 | $this->assertTrue($this->transaction->checkApproved());
147 | $this->assertTrue($this->transaction->checkApproved(1000));
148 | $this->assertTrue($this->transaction->checkApproved(1000, '639499XXXXXX2740'));
149 | $this->assertNotTrue($this->transaction->checkApproved(2000, '639499XXXXXX2740'));
150 | }
151 |
152 | /**
153 | * Test config.
154 | */
155 | public function test_get_all()
156 | {
157 | $keys = [
158 | 'Amnt',
159 | 'AmountTrxn',
160 | 'AuthCode',
161 | 'CardNo',
162 | 'CardType',
163 | 'Currency',
164 | 'ExternalTxnId',
165 | 'FeeAmnt',
166 | 'HasToken',
167 | 'ISForceSendCVCForRefund',
168 | 'IsMustVoidTotalAmount',
169 | 'IsPointTrasnaction',
170 | 'IsRefund',
171 | 'IsRefundEnabled',
172 | 'IsSend',
173 | 'MerchantReference',
174 | 'MobileNumber',
175 | 'OriginalTxnId',
176 | 'RRN',
177 | 'ReceiptNo',
178 | 'RefundButton',
179 | 'RefundReason',
180 | 'RefundSource',
181 | 'RefundUserCreator',
182 | 'RelatedTxnTotalAmount',
183 | 'RemainingRefundAmount',
184 | 'ResCodeDesc',
185 | 'STAN',
186 | 'SenderName',
187 | 'Status',
188 | 'TipAmnt',
189 | 'TransType',
190 | 'TransactionChannel',
191 | 'TransactionId',
192 | 'TxnDateTime',
193 | 'TxnIcon',
194 | ];
195 | $this->assertEquals($keys, array_keys($this->transaction->getAll()), 'Keys are not equal');
196 | }
197 |
198 | /**
199 | * Test config.
200 | */
201 | public function test_get_response()
202 | {
203 | $expected = [
204 | 'Message' => null,
205 | 'Success' => true,
206 | 'TotalAmountAllTransaction' => 1000,
207 | 'TotalAmountTipsTransaction' => 0,
208 | 'TotalCountAllTransaction' => 1,
209 | ];
210 | $actual = $this->transaction->getResponse();
211 | unset($actual['Transactions']);
212 | $this->assertEquals($expected, $actual);
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/tests/Feature/NotificationsAPITest.php:
--------------------------------------------------------------------------------
1 | assertNotNull(config('moamalat-pay.merchant_id'));
28 | $this->assertNotNull(config('moamalat-pay.terminal_id'));
29 | $this->assertNotNull(config('moamalat-pay.key'));
30 | $this->assertNotNull(config('moamalat-pay.production'));
31 | $this->assertNotNull(config('moamalat-pay.show_logs'));
32 | $this->assertNotNull(config('moamalat-pay.notification.key'));
33 | }
34 |
35 | /**
36 | * Initialize base notifications api testing
37 | *
38 | * @param array $dispatched events should dispatched
39 | * @param array $notdispatched events should not dispatched
40 | * @param array $extraData override or add extra properties to transaction
41 | * @return void
42 | */
43 | public function init_test_api_notifications_transaction($dispatched, $notdispatched, $extraData = [])
44 | {
45 | Event::fake();
46 |
47 | // load request from json to array
48 | $data = json_decode(file_get_contents(__DIR__.'./../_fixtures/transactions/verfied.json'), true);
49 | $body = array_merge($data, $extraData);
50 |
51 | // call api notificaitons
52 | // $this->postJson(route(config('moamalat-pay.notification.route_name')), $body)
53 | // There is issuse with $this->postJson it sends body empty , that is why I use $this->withHeaders
54 | $this->withHeaders([])->post(route(config('moamalat-pay.notification.route_name')), $body)
55 | ->assertStatus(200)
56 | ->assertJson(['Message' => 'Success', 'Success' => true]);
57 |
58 | // assert dispatched events
59 | foreach ($dispatched as $e) {
60 | Event::assertDispatched($e, 1);
61 | }
62 |
63 | // assert not dispatched events
64 | foreach ($notdispatched as $e) {
65 | Event::assertNotDispatched($e);
66 | }
67 |
68 | // validated transaction saved in database
69 | $this->assertDatabaseCount(config('moamalat-pay.notification.table'), 1);
70 | }
71 |
72 | /**
73 | * Test approved transaction notification
74 | */
75 | public function test_api_notifications_approved_transaction()
76 | {
77 | $this->init_test_api_notifications_transaction(
78 | [ // dispatched
79 | VerfiedTransaction::class,
80 | ApprovedTransaction::class,
81 | ApprovedSaleTransaction::class,
82 | ],
83 | [ // not dispatched
84 | ApprovedRefundTransaction::class,
85 | ApprovedVoidSaleTransaction::class,
86 | ApprovedVoidRefundTransaction::class,
87 | UnverfiedTransaction::class,
88 | DisallowedRequestEvent::class,
89 | ]
90 | );
91 | }
92 |
93 | /**
94 | * Test approved refund transaction notification
95 | */
96 | public function test_api_notifications_approved_refund_transaction()
97 | {
98 | $this->init_test_api_notifications_transaction(
99 | [ // dispatched
100 | VerfiedTransaction::class,
101 | ApprovedTransaction::class,
102 | ApprovedRefundTransaction::class,
103 | ],
104 | [ // not dispatched
105 | ApprovedSaleTransaction::class,
106 | ApprovedVoidSaleTransaction::class,
107 | ApprovedVoidRefundTransaction::class,
108 | UnverfiedTransaction::class,
109 | DisallowedRequestEvent::class,
110 | ],
111 | [
112 | 'TxnType' => 2,
113 | ]
114 | );
115 | }
116 |
117 | /**
118 | * Test approved void sale transaction notification
119 | */
120 | public function test_api_notifications_approved_void_sale_transaction()
121 | {
122 | $this->init_test_api_notifications_transaction(
123 | [ // dispatched
124 | VerfiedTransaction::class,
125 | ApprovedTransaction::class,
126 | ApprovedVoidSaleTransaction::class,
127 | ],
128 | [ // not dispatched
129 | ApprovedSaleTransaction::class,
130 | ApprovedRefundTransaction::class,
131 | ApprovedVoidRefundTransaction::class,
132 | UnverfiedTransaction::class,
133 | DisallowedRequestEvent::class,
134 | ],
135 | [
136 | 'TxnType' => 3,
137 | ]
138 | );
139 | }
140 |
141 | /**
142 | * Test approved void refund transaction notification
143 | */
144 | public function test_api_notifications_approved_void_refund_transaction()
145 | {
146 | $this->init_test_api_notifications_transaction(
147 | [ // dispatched
148 | VerfiedTransaction::class,
149 | ApprovedTransaction::class,
150 | ApprovedVoidRefundTransaction::class,
151 | ],
152 | [ // not dispatched
153 | ApprovedSaleTransaction::class,
154 | ApprovedRefundTransaction::class,
155 | ApprovedVoidSaleTransaction::class,
156 | UnverfiedTransaction::class,
157 | DisallowedRequestEvent::class,
158 | ],
159 | [
160 | 'TxnType' => 4,
161 | ]
162 | );
163 | }
164 |
165 | /**
166 | * Test approved transaction notification
167 | */
168 | public function test_api_notifications_unverfied_transaction()
169 | {
170 |
171 | $this->init_test_api_notifications_transaction(
172 | [ // dispatched
173 | UnverfiedTransaction::class,
174 | ],
175 | [ // not dispatched
176 | VerfiedTransaction::class,
177 | ApprovedTransaction::class,
178 | ApprovedSaleTransaction::class,
179 | ApprovedRefundTransaction::class,
180 | ApprovedVoidSaleTransaction::class,
181 | ApprovedVoidRefundTransaction::class,
182 | ],
183 | [
184 | 'SecureHash' => 'Invaild hash for testing unverfied',
185 | ]
186 | );
187 | }
188 |
189 | /**
190 | * Test notify from disallowed ip
191 | */
192 | public function test_api_notifications_notify_from_disallowed_ip()
193 | {
194 | // we set invalid ip as allowed ip, to check if api will catch our ip as disallowed ip
195 | Config::set('moamalat-pay.notification.allowed_ips', ['12.0.0.999']);
196 |
197 | Event::fake();
198 |
199 | // call api notificaitons
200 | $this->postJson(route(config('moamalat-pay.notification.route_name')))
201 | ->assertStatus(403);
202 |
203 | Event::assertDispatched(DisallowedRequestEvent::class, 1);
204 | Event::assertNotDispatched(UnverfiedTransaction::class);
205 | Event::assertNotDispatched(VerfiedTransaction::class);
206 | Event::assertNotDispatched(ApprovedTransaction::class);
207 | Event::assertNotDispatched(ApprovedSaleTransaction::class);
208 | Event::assertNotDispatched(ApprovedRefundTransaction::class);
209 | Event::assertNotDispatched(ApprovedVoidSaleTransaction::class);
210 | Event::assertNotDispatched(ApprovedVoidRefundTransaction::class);
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/tests/Feature/PayTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(Pay::class, app('moamalat-pay'));
19 | }
20 |
21 | /**
22 | * Test config.
23 | */
24 | public function test_render()
25 | {
26 | $blade = app('moamalat-pay')->init();
27 | $this->assertStringContainsString('class MoamalataPay', $blade);
28 | $this->assertStringContainsString('var _moamalatPay = new MoamalataPay(', $blade);
29 | $this->assertStringNotContainsString('_moamalatPay.pay(', $blade);
30 | $this->assertStringContainsString("_moamalatPay.pay(1000, '');", app('moamalat-pay')->pay(1000));
31 | $this->assertStringContainsString("_moamalatPay.pay(1000, 'test-ref');", app('moamalat-pay')->pay(1000, 'test-ref'));
32 | }
33 |
34 | /**
35 | * Test config.
36 | */
37 | public function test_component()
38 | {
39 | $view = $this->blade('');
40 | $view->assertSeeText('class MoamalataPay');
41 | $view->assertSeeText('var _moamalatPay = new MoamalataPay(');
42 | $view->assertSeeText('_moamalatPay.pay(1000, "");', false);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/Feature/RefundTest.php:
--------------------------------------------------------------------------------
1 | 'Approved',
22 | 'Success' => true,
23 | 'ActionCode' => null,
24 | 'AuthCode' => null,
25 | 'DecimalFraction' => 3,
26 | 'ExternalTxnId' => null,
27 | 'IsEnableRefund' => false,
28 | 'MerchantReference' => null,
29 | 'NetworkReference' => null,
30 | 'ReceiptNumber' => null,
31 | 'ReceiverAccountNumber' => null,
32 | 'ReceiverName' => null,
33 | 'ReceiverScheme' => null,
34 | 'RefNumber' => '1233674',
35 | 'SystemReference' => 0,
36 | 'SystemTxnId' => 0,
37 | 'TxnDate' => null,
38 | ];
39 |
40 | // set fake requests to make testing faster
41 | Http::fake([
42 | // 'https://tnpg.moamalat.net/cube/paylink.svc/api/FilterTransactions' => Http::response($respone, 200),
43 | 'https://tnpg.moamalat.net/cube/paylink.svc/api/RefundTransaction' => function ($r) use ($respone) {
44 | if ($r->offsetGet('MerchantId') == 'testing_authentication_failed') { // response for testing authentication failed
45 | return Http::response([
46 | 'Message' => 'Authentication failed.',
47 | 'StackTrace' => null,
48 | 'ExceptionType' => 'System.InvalidOperationException',
49 | ], 401);
50 | }
51 | if ($r->offsetExists('NetworkReference') && $r->offsetGet('NetworkReference') == 'testing_already_refunded') { // response for testing authentication failed
52 | return Http::response([
53 | 'Message' => 'CUBEEX5250616:Transaction Already Refunded',
54 | 'Success' => false,
55 | 'ActionCode' => null,
56 | 'AuthCode' => null,
57 | 'DecimalFraction' => 3,
58 | 'ExternalTxnId' => null,
59 | 'IsEnableRefund' => false,
60 | 'MerchantReference' => null,
61 | 'NetworkReference' => null,
62 | 'ReceiptNumber' => null,
63 | 'ReceiverAccountNumber' => null,
64 | 'ReceiverName' => null,
65 | 'ReceiverScheme' => null,
66 | 'RefNumber' => null,
67 | 'SystemReference' => 0,
68 | 'SystemTxnId' => 0,
69 | 'TxnDate' => null,
70 | ], 200);
71 | }
72 |
73 | return $respone; // response for testing success request
74 | },
75 | ]);
76 |
77 | $this->transaction = app(Refund::class);
78 | }
79 |
80 | /**
81 | * Load transaction to use it in test something
82 | */
83 | protected function loadTransaction()
84 | {
85 | $this->transaction->refundByNetworkReference('226214209277', '10');
86 | }
87 |
88 | /**
89 | * Test config.
90 | */
91 | public function test_container_instance()
92 | {
93 | $this->assertInstanceOf(Refund::class, app('moamalat-pay-refund'));
94 | }
95 |
96 | /**
97 | * Test
98 | */
99 | public function test_refund_by_system_reference()
100 | {
101 | $this->transaction->refundBySystemReference('226214209277', '10');
102 | $this->assertEquals($this->transaction->get('Message'), 'Approved');
103 | }
104 |
105 | /**
106 | * Test
107 | */
108 | public function test_refund_by_network_reference()
109 | {
110 | $this->transaction->refundByNetworkReference('226214209277', '10');
111 | $this->assertEquals($this->transaction->get('Message'), 'Approved');
112 | }
113 |
114 | /**
115 | * Test
116 | */
117 | public function test_already_refunded()
118 | {
119 | $this->expectExceptionMessage('Transaction Already Refunded');
120 | (new Refund)->refundByNetworkReference('testing_already_refunded', '10');
121 | }
122 |
123 | /**
124 | * Test
125 | */
126 | public function test_authentication_failed()
127 | {
128 | $this->expectExceptionMessage('Authentication failed.');
129 | Config::set('moamalat-pay.merchant_id', 'testing_authentication_failed');
130 | (new Refund)->refundByNetworkReference('226214209277', '10');
131 | }
132 |
133 | /**
134 | * Test
135 | */
136 | public function test_get_ref_number()
137 | {
138 | $this->loadTransaction();
139 | $this->assertEquals('1233674', $this->transaction->getRefNumber());
140 | }
141 |
142 | /**
143 | * Test
144 | */
145 | public function test_get_property()
146 | {
147 | $this->loadTransaction();
148 | $this->assertEquals('Approved', $this->transaction->get('Message'));
149 | // $this->expectExceptionMessage('Undefined index: CardNotFound');
150 | // $this->assertEquals('Approved', $this->transaction->get('CardNotFound'));
151 | }
152 |
153 | /**
154 | * Test
155 | */
156 | public function test_get_property_with_default_value()
157 | {
158 | $this->loadTransaction();
159 | $this->assertEquals('card-not-found', $this->transaction->getWithDefault('Card', 'card-not-found'));
160 | }
161 |
162 | /**
163 | * Test
164 | */
165 | public function test_get_all()
166 | {
167 | $this->loadTransaction();
168 | $keys = [
169 | 'Message',
170 | 'Success',
171 | 'ActionCode',
172 | 'AuthCode',
173 | 'DecimalFraction',
174 | 'ExternalTxnId',
175 | 'IsEnableRefund',
176 | 'MerchantReference',
177 | 'NetworkReference',
178 | 'ReceiptNumber',
179 | 'ReceiverAccountNumber',
180 | 'ReceiverName',
181 | 'ReceiverScheme',
182 | 'RefNumber',
183 | 'SystemReference',
184 | 'SystemTxnId',
185 | 'TxnDate',
186 | ];
187 | $this->assertEquals($keys, array_keys($this->transaction->getAll()), 'Keys are not equal');
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | withFactories(__DIR__.'/../src/database/factories');
14 | }
15 |
16 | protected function getPackageProviders($app)
17 | {
18 | return [
19 | MoamalatPayProvider::class,
20 | ];
21 | }
22 |
23 | protected function getEnvironmentSetUp($app)
24 | {
25 | $app['config']->set('moamalat-pay', [
26 | 'merchant_id' => '10004188779',
27 | 'terminal_id' => '49077229',
28 | 'key' => '39353638663431622D303136622D343235322D623330632D383361633838383965373965',
29 | 'production' => false,
30 | 'show_logs' => true,
31 | 'generate-securekey' => [
32 | 'url' => 'moamalat-pay/securekey',
33 | 'route_name' => 'moamalat_pay.generate_securekey',
34 | ],
35 | 'notification' => [
36 | 'key' => '39353638663431622D303136622D343235322D623330632D383361633838383965373965',
37 | 'url' => 'moamalat-pay/notify',
38 | 'route_name' => 'moamalat_pay.notification',
39 | 'table' => 'moamalat_pay_notifications',
40 | 'allowed_ips' => ['*'],
41 | ],
42 | ]);
43 | // perform environment setup
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/tests/Unit/ExampleTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/_fixtures/transactions/verfied.json:
--------------------------------------------------------------------------------
1 | {
2 | "MerchantId": "10004188779",
3 | "TerminalId": "49077229",
4 | "DateTimeLocalTrxn": "20220903014410",
5 | "AuthorizationDateTime": "20220903014409",
6 | "SecureHash": "5C919EA5A619D33101AA761B8DFB332C8993C867CB8E5E567770185B71ED72E6",
7 | "TxnType": 1,
8 | "Message": "Approved",
9 | "PaidThrough": "Card",
10 | "SystemReference": "1233317",
11 | "NetworkReference": "224601434990",
12 | "MerchantReference": "475217323",
13 | "Amount": "1000",
14 | "Tip": "0",
15 | "Currency": "434",
16 | "PayerAccount": "639499XXXXXX2740",
17 | "PayerName": "MS",
18 | "ActionCode": "00",
19 | "SID": null,
20 | "Token": null,
21 | "Email": null,
22 | "Status": "Success",
23 | "CustomerMobile": null,
24 | "MessageCustomer": null,
25 | "MerchantCustomerId": null,
26 | "CurrencyTwo": null,
27 | "CurrencyTwoValue": null,
28 | "AdditionalFees": "0",
29 | "OriginalTxnId": "0"
30 | }
31 |
--------------------------------------------------------------------------------