azozzalfiras/payment-gateway

A unified PHP library for integrating with MENA payment gateways: MyFatoorah, Paylink, and EdfaPay

Maintainers

Package info

github.com/AzozzALFiras/payment-gateway

pkg:composer/azozzalfiras/payment-gateway

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-03-16 21:56 UTC

This package is auto-updated.

Last update: 2026-03-16 21:56:51 UTC


README

PHP Version License Composer

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


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

Feature
Purchase โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
Payment Status โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…
Refund โœ… โ€” โœ… โœ… โœ… โœ… โ€” โœ… โ€” โ€” โœ…
Recurring โ€” โ€” โœ… โœ… โœ… โ€” โœ… โœ… โ€” โ€” โœ…
Auth/Capture โœ… โ€” โœ… โœ… โœ… โœ… โ€” โ€” โ€” โ€” โœ…
Invoices โœ… โœ… โ€” โœ… โ€” โ€” โ€” โ€” โ€” โ€” โ€”
Customer CRUD โœ… โ€” โ€” โœ… โ€” โ€” โœ… โ€” โ€” โ€” โœ…
Tokenization โ€” โ€” โ€” โœ… โœ… โ€” โ€” โœ… โ€” โ€” โœ…
Webhook โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ… โœ…

๐Ÿ”’ 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

  1. Never log raw card numbers โ€” use PayloadSanitizer::maskCardNumber()
  2. Always verify webhooks โ€” call $gateway->verifyWebhook() before processing
  3. Use HTTPS for all callback/return URLs
  4. Store API keys in environment variables, never in source code
  5. Rotate keys immediately if any key may have been compromised
  6. 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