azozzalfiras / payment-gateway
A unified PHP library for integrating with MENA payment gateways: MyFatoorah, Paylink, and EdfaPay
Requires
- php: ^8.1
- ext-curl: *
- ext-json: *
- ext-mbstring: *
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
This package is auto-updated.
Last update: 2026-03-16 21:56:51 UTC
README
A unified, extensible PHP Composer package for integrating with 10 MENA-region payment gateways. Built with clean architecture, full API coverage, zero external dependencies, and enterprise-grade security.
๐ Table of Contents
- Supported Gateways
- Installation
- Quick Start & Enums
- Gateway Reference
- Architecture
- Factory Pattern
- Feature Matrix
- Security Architecture
- Webhook Handling
- Database Integration Guide
- Testing
โจ Supported Gateways
![]() MyFatoorah |
![]() Paylink |
![]() EdfaPay |
![]() Tap |
![]() ClickPay |
![]() Tamara |
![]() Thawani |
![]() Fatora |
![]() Payzaty |
![]() Payzah |
![]() Stripe |
||||
| Gateway | Classes | Key Features | Authentication | Supported Regions |
|---|---|---|---|---|
| MyFatoorah | 5 | Payments, Sessions, Invoices, Customers, Webhooks | API Key (Bearer) | ๐ฐ๐ผ KWT ยท ๐ธ๐ฆ SAU ยท ๐ฆ๐ช ARE ยท ๐ง๐ญ BHR ยท ๐ถ๐ฆ QAT ยท ๐ด๐ฒ OMN ยท ๐ช๐ฌ EGY ยท ๐ฏ๐ด JOR |
| Paylink | 5 | Invoices, Digital Products, Reconciliation, Webhooks | API ID + Secret Key | ๐ธ๐ฆ SAU |
| EdfaPay | 6 | Checkout, Embedded S2S, Apple Pay, Recurring, Auth/Capture, Refunds | Client Key + Password | ๐ธ๐ฆ SAU ยท MENA |
| Tap | 7 | Charges, Authorize/Void, Refunds, Invoices, Customers, Tokens | Secret Key (Bearer) | ๐ฐ๐ผ KWT ยท ๐ธ๐ฆ SAU ยท ๐ฆ๐ช ARE ยท ๐ง๐ญ BHR ยท ๐ถ๐ฆ QAT ยท ๐ด๐ฒ OMN ยท ๐ช๐ฌ EGY ยท ๐ฏ๐ด JOR |
| ClickPay | 3 | Hosted Page, Auth/Capture/Void/Refund, Tokenization | Server Key | ๐ธ๐ฆ SAU ยท ๐ฆ๐ช ARE ยท ๐ช๐ฌ EGY ยท ๐ด๐ฒ OMN ยท ๐ฏ๐ด JOR |
| Tamara | 4 | BNPL Checkout, Order Authorize/Capture/Cancel, Refunds | API Token (Bearer) | ๐ธ๐ฆ SAU ยท ๐ฆ๐ช ARE ยท ๐ฐ๐ผ KWT ยท ๐ง๐ญ BHR ยท ๐ถ๐ฆ QAT |
| Thawani | 4 | Checkout Sessions, Payment Intents, Customer Management | Secret + Publishable Key | ๐ด๐ฒ OMN |
| Fatora | 4 | Checkout, Verify, Refunds, Recurring (Card Tokenization) | API Key | ๐ธ๐ฆ SAU ยท ๐ฆ๐ช ARE ยท ๐ถ๐ฆ QAT ยท ๐ง๐ญ BHR ยท ๐ฐ๐ผ KWT ยท ๐ด๐ฒ OMN ยท ๐ฎ๐ถ IRQ ยท ๐ฏ๐ด JOR ยท ๐ช๐ฌ EGY |
| Payzaty | 3 | Secure Checkout, Mada/VISA/MC/Apple Pay/STC Pay | Account No + Secret Key | ๐ธ๐ฆ SAU |
| Payzah | 3 | Hosted Checkout, K-Net/VISA/MC/Apple Pay/Amex, Multi-vendor | Private Key | ๐ฐ๐ผ KWT |
| Stripe | 6 | Checkout Sessions, Charges, Customers, Refunds, PaymentIntents, Webhooks | Secret Key (Bearer) | ๐ 46+ countries |
50 PHP classes across 11 gateways, with consistent multi-file architecture per gateway.
๐ฆ Installation
composer require azozzalfiras/payment-gateway
System Requirements:
| Requirement | Version |
|---|---|
| PHP | โฅ 8.1 |
| ext-curl | Required |
| ext-json | Required |
| ext-mbstring | Required |
๐ Quick Start
Using Enums (Type-Safe Gateway Selection)
use AzozzALFiras\PaymentGateway\PaymentGateway; use AzozzALFiras\PaymentGateway\Enums\Gateway; use AzozzALFiras\PaymentGateway\Enums\PaymentStatus; use AzozzALFiras\PaymentGateway\Enums\Currency; // Create gateway from enum $gateway = PaymentGateway::create(Gateway::TAP->value, [ 'secret_key' => 'sk_test_xxx', 'testMode' => true, ]); // Gateway metadata from enum echo Gateway::TAP->label(); // "Tap Payments" echo Gateway::TAP->countries(); // ['KWT', 'SAU', 'ARE', ...] echo Gateway::TAP->supportsRefund(); // true echo Gateway::TAP->isBNPL(); // false // Normalize any vendor-specific status string to unified enum $status = PaymentStatus::normalize('CAPTURED'); // PaymentStatus::CAPTURED $status->isSuccessful(); // true $status->isRefundable(); // true $status->isFinal(); // true // Currency helpers with smallest-unit conversion $amount = Currency::OMR->toSmallestUnit(5.500); // 5500 (baisa) $back = Currency::OMR->fromSmallestUnit(5500); // 5.5 echo Currency::SAR->format(100.50); // "100.50 SAR" echo Currency::KWD->decimals(); // 3
๐ Gateway Reference
Each gateway section below provides: configuration, full code example, and available sub-modules.
MyFatoorah
| Property | Details |
|---|---|
| Classes | MyFatoorahGateway, MyFatoorahSession, MyFatoorahInvoice, MyFatoorahCustomer, MyFatoorahWebhook |
| Authentication | API Key (Bearer Token) โ separate key per country |
| Regions | Kuwait, Saudi Arabia, UAE, Bahrain, Qatar, Oman, Egypt, Jordan |
| Currencies | KWD, SAR, AED, BHD, QAR, OMR, EGP, JOD |
| Test Mode | Separate test API base per country |
$gateway = PaymentGateway::myfatoorah([ 'api_key' => 'YOUR_API_KEY', 'country' => 'SAU', // KWT, SAU, ARE, QAT, BHR, OMN, JOR, EGY 'testMode' => true, ]); $response = $gateway->purchase(new PaymentRequest( amount: 100.00, currency: 'SAR', orderId: 'ORDER-001', description: 'Premium Subscription', callbackUrl: 'https://yoursite.com/callback', customer: new Customer(name: 'Ahmed', email: 'ahmed@example.com'), )); // Sub-modules $gateway->sessions()->create(100.00); $gateway->customers()->getDetails($ref);
Paylink
| Property | Details |
|---|---|
| Classes | PaylinkGateway, PaylinkAuth, PaylinkInvoice, PaylinkReconcile, PaylinkWebhook |
| Authentication | API ID + Secret Key (OAuth-style token) |
| Regions | Saudi Arabia |
| Currencies | SAR |
$gateway = PaymentGateway::paylink([ 'api_id' => 'YOUR_API_ID', 'secret_key' => 'YOUR_SECRET_KEY', 'testMode' => true, ]); $invoice = $gateway->createInvoice(new InvoiceRequest( amount: 250.00, currency: 'SAR', orderId: 'INV-001', callbackUrl: 'https://yoursite.com/callback', customer: new Customer(name: 'Mohammed', email: 'mohammed@example.com'), items: [['name' => 'Web Design', 'price' => 250.00, 'quantity' => 1]], )); // Sub-modules $gateway->reconcile()->getByDateRange($from, $to); $gateway->reconcile()->getSettlements($from, $to);
EdfaPay
| Property | Details |
|---|---|
| Classes | EdfaPayGateway, EdfaPayCheckout, EdfaPayEmbedded, EdfaPayApplePay, EdfaPayHash, EdfaPayWebhook |
| Authentication | Client Key + Password (HMAC signature) |
| Regions | Saudi Arabia, MENA |
| Features | Hosted Checkout, Server-to-Server (Direct Card), Apple Pay, Recurring, Auth/Capture |
$gateway = PaymentGateway::edfapay([ 'client_key' => 'YOUR_CLIENT_KEY', 'password' => 'YOUR_SECRET_PASSWORD', 'testMode' => true, ]); // Hosted Checkout $response = $gateway->purchase(new PaymentRequest(...)); // Embedded S2S (Direct Card Payment) $result = $gateway->embedded()->sale([...]); $gateway->embedded()->authorize([...]); $gateway->embedded()->capture($transId); // Apple Pay $gateway->applePay()->sale([...]);
Tap Payments
| Property | Details |
|---|---|
| Classes | TapGateway, TapCharge, TapAuthorize, TapCustomer, TapInvoice, TapToken, TapWebhook |
| Authentication | Secret Key (Bearer Token) |
| Regions | Kuwait, Saudi Arabia, UAE, Bahrain, Qatar, Oman, Egypt, Jordan |
| Currencies | KWD, SAR, AED, BHD, QAR, OMR, EGP |
$gateway = PaymentGateway::tap([ 'secret_key' => 'sk_test_xxx', 'webhook_secret' => 'whsec_xxx', // Optional: for webhook verification 'testMode' => true, ]); $response = $gateway->purchase(new PaymentRequest( amount: 100.00, currency: 'SAR', orderId: 'ORDER-001', description: 'Premium Plan', callbackUrl: 'https://yoursite.com/callback', customer: new Customer(name: 'Ahmed', email: 'ahmed@example.com'), )); // Sub-modules โ full API coverage $gateway->charges()->create($request); // Create charge $gateway->charges()->retrieve($chargeId); // Get charge details $gateway->charges()->update($id, $data); // Update charge $gateway->charges()->list($filters); // List charges $gateway->authorizations()->create($params); // Pre-auth hold $gateway->authorizations()->void($authId); // Release hold $gateway->customers()->create($data); $gateway->customers()->retrieve($id); $gateway->customers()->update($id, $data); $gateway->customers()->delete($id); $gateway->invoices()->create($data); $gateway->invoices()->retrieve($id); $gateway->tokens()->create($cardData); // Tokenize a card
ClickPay
| Property | Details |
|---|---|
| Classes | ClickPayGateway, ClickPayTransaction, ClickPayWebhook |
| Authentication | Server Key (header) + Profile ID |
| Regions | SAU, ARE, EGY, OMN, JOR (region-specific endpoints) |
| Currencies | SAR, AED, EGP, OMR, JOD |
$gateway = PaymentGateway::clickpay([ 'server_key' => 'YOUR_SERVER_KEY', 'profile_id' => 'YOUR_PROFILE_ID', 'region' => 'SAU', // SAU, ARE, EGY, OMN, JOR 'testMode' => true, ]); $response = $gateway->purchase(new PaymentRequest(...)); // Sub-module: Transaction lifecycle $gateway->transactions()->authorize($params); // Pre-auth $gateway->transactions()->capture($ref, 100.00); // Capture $gateway->transactions()->void($ref, 100.00); // Void $gateway->transactions()->query($ref); // Status query
Tamara (BNPL)
| Property | Details |
|---|---|
| Classes | TamaraGateway, TamaraCheckout, TamaraOrder, TamaraWebhook |
| Authentication | API Token (Bearer) |
| Type | Buy Now Pay Later (Installments) |
| Regions | Saudi Arabia, UAE, Kuwait, Bahrain, Qatar |
$gateway = PaymentGateway::tamara([ 'api_token' => 'YOUR_API_TOKEN', 'notification_token' => 'YOUR_WEBHOOK_TOKEN', // Optional 'testMode' => true, ]); $response = $gateway->purchase(new PaymentRequest( amount: 500.00, currency: 'SAR', orderId: 'ORD-001', callbackUrl: 'https://yoursite.com/callback', customer: new Customer(name: 'Sara', email: 'sara@example.com'), metadata: ['payment_type' => 'PAY_BY_INSTALMENTS'], )); // Sub-modules: Order lifecycle $gateway->orders()->authorise($orderId); $gateway->orders()->capture($orderId, 500.00, 'SAR'); $gateway->orders()->cancel($orderId, 500.00); $gateway->orders()->refund($orderId, 250.00, 'SAR', 'Partial refund'); $gateway->checkouts()->paymentOptions($orderData);
Thawani
| Property | Details |
|---|---|
| Classes | ThawaniGateway, ThawaniSession, ThawaniCustomer, ThawaniWebhook |
| Authentication | Secret Key + Publishable Key |
| Region | Oman exclusively |
| Currency | OMR (amounts converted to baisa: 1 OMR = 1000 baisa) |
$gateway = PaymentGateway::thawani([ 'secret_key' => 'YOUR_SECRET_KEY', 'publishable_key' => 'YOUR_PUBLISHABLE_KEY', 'testMode' => true, ]); // Amounts auto-converted to baisa internally $response = $gateway->purchase(new PaymentRequest( amount: 5.500, currency: 'OMR', orderId: 'ORD-001', callbackUrl: 'https://yoursite.com/callback', )); // Sub-modules $gateway->sessions()->retrieve($sessionId); $gateway->customers()->create($data); $gateway->customers()->getPaymentMethods($customerId); // Payment intents (charge saved cards) $gateway->createPaymentIntent(5500, 'ref-001', $paymentMethodId, $returnUrl);
Fatora
| Property | Details |
|---|---|
| Classes | FatoraGateway, FatoraCheckout, FatoraRecurring, FatoraWebhook |
| Authentication | API Key |
| Regions | SAU, ARE, QAT, BHR, KWT, OMN, IRQ, JOR, EGY |
| Special | Card tokenization for recurring payments |
$gateway = PaymentGateway::fatora([ 'api_key' => 'YOUR_API_KEY', 'testMode' => true, ]); // Save card token for recurring $response = $gateway->purchase(new PaymentRequest( amount: 50.00, currency: 'SAR', orderId: 'ORD-001', callbackUrl: 'https://yoursite.com/callback', metadata: ['save_token' => true], )); $savedToken = $response->recurringToken; // Charge saved token (recurring) $gateway->recurring(new PaymentRequest( amount: 50.00, currency: 'SAR', orderId: 'REC-001', recurringToken: $savedToken, )); // Sub-modules $gateway->checkouts()->verify($orderId); $gateway->recurringPayments()->deactivateToken($token);
Payzaty
| Property | Details |
|---|---|
| Classes | PayzatyGateway, PayzatyCheckout, PayzatyWebhook |
| Authentication | X-AccountNo + X-SecretKey headers |
| Region | Saudi Arabia |
| Payment Methods | Mada, VISA, MasterCard, Apple Pay, STC Pay |
$gateway = PaymentGateway::payzaty([ 'account_no' => 'YOUR_ACCOUNT_NO', 'secret_key' => 'YOUR_SECRET_KEY', 'language' => 'ar', // Optional: ar/en 'testMode' => true, ]); $response = $gateway->purchase(new PaymentRequest( amount: 200.00, currency: 'SAR', orderId: 'ORD-001', callbackUrl: 'https://yoursite.com/callback', )); $gateway->checkouts()->status($paymentId);
Payzah
| Property | Details |
|---|---|
| Classes | PayzahGateway, PayzahPayment, PayzahWebhook |
| Authentication | Private Key |
| Region | Kuwait |
| Payment Methods | K-Net, VISA, MasterCard, Apple Pay, Amex |
| Special | Multi-vendor support |
$gateway = PaymentGateway::payzah([ 'private_key' => 'YOUR_PRIVATE_KEY', 'testMode' => true, ]); $response = $gateway->purchase(new PaymentRequest( amount: 15.000, currency: 'KWD', orderId: 'ORD-001', callbackUrl: 'https://yoursite.com/callback', metadata: [ 'payment_method' => 'knet', 'vendors' => [['id' => 'V1', 'amount' => 10.000], ['id' => 'V2', 'amount' => 5.000]], ], )); $gateway->payments()->status($paymentId);
Stripe
| Property | Details |
|---|---|
| Classes | StripeGateway, StripeCheckout, StripeCharge, StripeCustomer, StripeRefund, StripeWebhook |
| Authentication | Secret Key (Bearer Token) |
| Regions | 46+ countries globally (USA, GBR, DEU, FRA, ARE, SAU, etc.) |
| Currencies | 135+ currencies (USD, EUR, GBP, SAR, AED, KWD, BHD, etc.) |
| Features | Checkout Sessions, Charges, PaymentIntents, Customers, Refunds |
$gateway = PaymentGateway::stripe([ 'secret_key' => 'sk_test_xxx', 'webhook_secret' => 'whsec_xxx', // Optional 'testMode' => true, ]); $response = $gateway->purchase(new PaymentRequest( amount: 99.99, currency: 'USD', orderId: 'ORDER-001', description: 'Pro Subscription', callbackUrl: 'https://yoursite.com/webhook/stripe', returnUrl: 'https://yoursite.com/success', cancelUrl: 'https://yoursite.com/cancel', customer: new Customer(name: 'John', email: 'john@example.com'), )); // Sub-modules โ full API coverage $gateway->checkouts()->create($params); // Create checkout session $gateway->checkouts()->retrieve($sessionId); // Get session $gateway->checkouts()->expire($sessionId); // Cancel session $gateway->charges()->create($params); // Direct charge $gateway->charges()->capture($chargeId); // Capture pre-auth $gateway->charges()->list(['limit' => 10]); // List charges $gateway->customers()->create($data); // Create customer $gateway->customers()->update($id, $data); // Update $gateway->customers()->delete($id); // Delete $gateway->refunds()->create(['payment_intent' => 'pi_xxx', 'amount' => 5000]); $gateway->refunds()->retrieve($refundId); // PaymentIntents (server-side) $gateway->createPaymentIntent(['amount' => 5000, 'currency' => 'usd']); $gateway->retrievePaymentIntent('pi_xxx');
๐๏ธ Architecture
src/
โโโ PaymentGateway.php โ Factory (10 drivers)
โโโ Enums/
โ โโโ Gateway.php โ Gateway enum (10 drivers + metadata)
โ โโโ PaymentStatus.php โ Unified status (12 states + normalize())
โ โโโ TransactionType.php โ Transaction types (8 types)
โ โโโ Currency.php โ MENA currencies (11 + baisa/fils conversion)
โโโ Contracts/
โ โโโ GatewayInterface.php โ Core interface (purchase, status)
โ โโโ SupportsRefund.php โ Refund capability contract
โ โโโ SupportsRecurring.php โ Recurring capability contract
โ โโโ SupportsWebhook.php โ Webhook capability contract
โ โโโ SupportsInvoice.php โ Invoice capability contract
โโโ Config/GatewayConfig.php โ Immutable configuration object
โโโ DTOs/ โ 8 Data Transfer Objects
โ โโโ PaymentRequest.php
โ โโโ PaymentResponse.php
โ โโโ RefundRequest.php
โ โโโ RefundResponse.php
โ โโโ WebhookPayload.php
โ โโโ Customer.php
โ โโโ InvoiceRequest.php
โ โโโ InvoiceResponse.php
โโโ Exceptions/ โ Typed exception hierarchy
โ โโโ PaymentException.php โ Base exception
โ โโโ AuthenticationException.php
โ โโโ ValidationException.php
โ โโโ ApiException.php
โโโ Http/HttpClient.php โ Zero-dependency cURL client
โโโ Security/
โ โโโ SignatureVerifier.php โ HMAC-SHA256/512, MD5, Bearer token
โ โโโ PayloadSanitizer.php โ XSS/injection prevention, card masking
โโโ Support/
โ โโโ Arr.php โ Array dot-notation helpers
โ โโโ Currency.php โ Currency utility
โโโ Gateways/
โโโ MyFatoorah/ (5 classes) โ Gateway, Session, Invoice, Customer, Webhook
โโโ Paylink/ (5 classes) โ Gateway, Auth, Invoice, Reconcile, Webhook
โโโ EdfaPay/ (6 classes) โ Gateway, Checkout, Embedded, ApplePay, Hash, Webhook
โโโ Tap/ (7 classes) โ Gateway, Charge, Authorize, Customer, Invoice, Token, Webhook
โโโ ClickPay/ (3 classes) โ Gateway, Transaction, Webhook
โโโ Tamara/ (4 classes) โ Gateway, Checkout, Order, Webhook
โโโ Thawani/ (4 classes) โ Gateway, Session, Customer, Webhook
โโโ Fatora/ (4 classes) โ Gateway, Checkout, Recurring, Webhook
โโโ Payzaty/ (3 classes) โ Gateway, Checkout, Webhook
โโโ Payzah/ (3 classes) โ Gateway, Payment, Webhook
โโโ Stripe/ (6 classes) โ Gateway, Checkout, Charge, Customer, Refund, Webhook
๐ญ Factory Pattern
The PaymentGateway class provides two ways to instantiate any gateway:
use AzozzALFiras\PaymentGateway\PaymentGateway; // 1. Generic factory โ accepts any driver name string $gateway = PaymentGateway::create('tap', ['secret_key' => '...']); // 2. Typed static factory โ full IDE autocomplete per gateway $gateway = PaymentGateway::myfatoorah([...]); $gateway = PaymentGateway::paylink([...]); $gateway = PaymentGateway::edfapay([...]); $gateway = PaymentGateway::tap([...]); $gateway = PaymentGateway::clickpay([...]); $gateway = PaymentGateway::tamara([...]); $gateway = PaymentGateway::thawani([...]); $gateway = PaymentGateway::fatora([...]); $gateway = PaymentGateway::payzaty([...]); $gateway = PaymentGateway::payzah([...]); $gateway = PaymentGateway::stripe([...]); // List all 11 available driver names PaymentGateway::getAvailableDrivers(); // Returns: ['myfatoorah', 'paylink', 'edfapay', 'tap', 'clickpay', 'tamara', 'thawani', 'fatora', 'payzaty', 'payzah', 'stripe']
๐ Feature Matrix
๐ Security Architecture
Signature Verification
All webhook signatures use the centralized SignatureVerifier class, which provides timing-safe comparison via hash_equals() to prevent timing attacks:
use AzozzALFiras\PaymentGateway\Security\SignatureVerifier; // HMAC-SHA256 โ used by Tap, ClickPay, Thawani, Fatora, Payzaty SignatureVerifier::verifyHmacSha256($payload, $signature, $secret); // HMAC-SHA512 SignatureVerifier::verifyHmacSha512($payload, $signature, $secret); // Bearer Token โ used by Tamara SignatureVerifier::verifyBearerToken($authHeader, $expectedToken); // MD5 Hash โ used by EdfaPay SignatureVerifier::verifyMd5($computed, $received); // Extract signature from headers (case-insensitive) SignatureVerifier::extractSignature($headers, ['Tap-Signature', 'tap-signature']);
Input Sanitization
use AzozzALFiras\PaymentGateway\Security\PayloadSanitizer; PayloadSanitizer::string($input); // Strip HTML/JS, trim PayloadSanitizer::email($input); // Sanitized email PayloadSanitizer::phone($input); // Digits, +, spaces only PayloadSanitizer::url($input); // HTTPS validation PayloadSanitizer::amount($input); // Positive float, max 3 decimals PayloadSanitizer::ip($input); // Valid IPv4/IPv6 only PayloadSanitizer::maskCardNumber($card); // 512345****2346 PayloadSanitizer::metadata($array); // Recursive XSS clean
Security Best Practices
- Never log raw card numbers โ use
PayloadSanitizer::maskCardNumber() - Always verify webhooks โ call
$gateway->verifyWebhook()before processing - Use HTTPS for all callback/return URLs
- Store API keys in environment variables, never in source code
- Rotate keys immediately if any key may have been compromised
- IP Whitelist gateway webhook IPs when the gateway supports it
๐ Webhook Handling
All 10 gateways support webhook processing through a unified interface:
// In your webhook endpoint controller (e.g. POST /webhook/tap) $payload = json_decode(file_get_contents('php://input'), true); $headers = getallheaders(); // 1. Verify signature + parse payload $webhook = $gateway->handleWebhook($payload, $headers); // 2. Check validity if (!$webhook->isValid) { http_response_code(401); exit('Invalid signature'); } // 3. Use unified PaymentStatus enum $status = PaymentStatus::normalize($webhook->status); echo "Transaction: {$webhook->transactionId}\n"; echo "Order: {$webhook->orderId}\n"; echo "Amount: {$webhook->amount} {$webhook->currency}\n"; echo "Status: {$status->value}\n"; echo "Successful: " . ($status->isSuccessful() ? 'yes' : 'no') . "\n"; echo "Final: " . ($status->isFinal() ? 'yes' : 'no') . "\n";
๐๏ธ Database Integration Guide
The full step-by-step guide is available in docs/DATABASE.md โ includes SQL schema, PaymentTransaction Model with enum casting, PaymentService class, and WebhookController.
What's covered in the database guide:
- 3 SQL tables โ
payment_transactions,payment_gateway_configs,payment_webhook_logs - VARCHAR columns instead of SQL
ENUMโ add new statuses in PHP without DB migrations - PaymentTransaction Model โ casts
VARCHARโ PHP backed enums (Gateway,PaymentStatus,TransactionType,Currency) - PaymentService โ
createPayment(),refundPayment(),loadGateway()from DB - WebhookController โ handles webhooks from any gateway with audit logging
- Full usage examples โ payment creation โ redirect โ webhook โ status check โ refund
๐งช Testing
# Install dependencies composer install # Run PHPUnit test suite composer test # Run PHPStan static analysis (Level 5) composer analyse # Lint all PHP files find src/ -name '*.php' -exec php -l {} \;
Current test results:
- โ PHP Lint: 72/72 files โ zero syntax errors
- โ PHPUnit: 16 tests, 36 assertions โ all passing
- โ PHPStan Level 5: 0 errors
๐ License
MIT License โ see LICENSE file.
๐จโ๐ป Author
AzozzALFiras โ GitHub