<p align="center">
  <img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="200" alt="Laravel Logo">
</p>

<h1 align="center">Payment Module</h1>

<p align="center">
  <strong>A comprehensive, enterprise-grade payment system for Laravel applications</strong>
</p>

<p align="center">
  <img src="https://img.shields.io/badge/PHP-8.2+-777BB4?style=for-the-badge&logo=php&logoColor=white" alt="PHP Version">
  <img src="https://img.shields.io/badge/Laravel-11%20|%2012-FF2D20?style=for-the-badge&logo=laravel&logoColor=white" alt="Laravel Version">
  <img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge" alt="License">
</p>

---

## Overview

The **Payment Module** is a powerful, modular payment system designed for Laravel applications. It provides a unified API for multiple payment gateways, an integrated wallet system, and comprehensive admin tools for managing transactions and withdrawals.

### Key Highlights

- **Multi-Gateway Support** - HyperPay, Tabby, Tamara, MyFatoorah, Moyasar, and Wallet
- **Unified API** - Single interface for all payment operations
- **Built-in Wallet** - Complete wallet system with deposits, withdrawals, and transfers
- **Admin Dashboard API** - Full statistics, filtering, and export capabilities
- **Event-Driven** - Comprehensive events for all payment lifecycle stages
- **Localization Ready** - Arabic and English translations included
- **Fully Tested** - Unit and Feature tests included

---

## Supported Payment Gateways

| Gateway | Type | Supported Brands | Region |
|:--------|:-----|:-----------------|:-------|
| **HyperPay** | Card Payments | Visa, Mastercard, Mada, Apple Pay | MENA |
| **Tabby** | Buy Now Pay Later | 4 Installments | MENA |
| **Tamara** | Buy Now Pay Later | 3, 4, 6 Installments | MENA |
| **MyFatoorah** | Multi-Gateway | Visa, Mastercard, Mada, Apple Pay, STC Pay, KNET | MENA |
| **Moyasar** | Card Payments | Visa, Mastercard, Mada, Apple Pay, STC Pay | Saudi Arabia |
| **Wallet** | Internal | Balance-based | Universal |

---

## Table of Contents

<details>
<summary>Click to expand</summary>

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Configuration](#configuration)
- [Usage](#usage)
- [Payment Flow](#payment-flow)
- [API Reference](#api-reference)
    - [Public Payment Routes](#public-payment-routes)
    - [Wallet Routes](#wallet-routes)
    - [Admin Routes](#admin-routes)
- [Wallet System](#wallet-system)
- [Gateway-Specific Features](#gateway-specific-features)
- [Events](#events)
- [Models & Database](#models--database)
- [Error Handling](#error-handling)
- [Creating Custom Gateways](#creating-custom-gateways)
- [Testing](#testing)
- [Localization](#localization)
- [Directory Structure](#directory-structure)
- [Enums Reference](#enums-reference)
- [Security Considerations](#security-considerations)
- [Troubleshooting](#troubleshooting)
- [License](#license)

</details>

---

## Installation

### Requirements

- PHP 8.2 or higher
- Laravel 11.x or 12.x
- Database (MySQL, PostgreSQL, SQLite)

### Step-by-Step Setup

#### 1. Add Autoloading

Add to your `composer.json`:

```json
{
    "autoload": {
        "psr-4": {
            "Modules\\Payment\\": "modules/Payment/src/"
        }
    }
}
```

Then regenerate the autoload files:

```bash
composer dump-autoload
```

#### 2. Register Service Provider

Add to `bootstrap/app.php`:

```php
return Application::configure(basePath: dirname(__DIR__))
    ->withProviders([
        \Modules\Payment\PaymentServiceProvider::class,
    ])
    // ...
```

#### 3. Run Migrations

```bash
php artisan migrate
```

#### 4. Publish Assets (Optional)

```bash
# Publish configuration
php artisan vendor:publish --tag=payment-config

# Publish migrations
php artisan vendor:publish --tag=payment-migrations

# Publish views
php artisan vendor:publish --tag=payment-views

# Publish translations
php artisan vendor:publish --tag=payment-lang
```

---

## Configuration

### Environment Variables

Add to your `.env` file:

```env
# General
PAYMENT_MODE=test  # test or live
PAYMENT_CURRENCY=SAR

# HyperPay
HYPERPAY_TEST_KEY=your_test_token
HYPERPAY_TEST_URL=https://eu-test.oppwa.com/v1/checkouts
HYPERPAY_TEST_ENTITY_ID=your_entity_id
HYPERPAY_TEST_MADA_ENTITY_ID=your_mada_entity_id
HYPERPAY_TEST_APPLEPAY_ENTITY_ID=your_applepay_entity_id

HYPERPAY_LIVE_KEY=your_live_token
HYPERPAY_LIVE_URL=https://eu-prod.oppwa.com/v1/checkouts
HYPERPAY_LIVE_ENTITY_ID=your_entity_id
HYPERPAY_LIVE_MADA_ENTITY_ID=your_mada_entity_id
HYPERPAY_LIVE_APPLEPAY_ENTITY_ID=your_applepay_entity_id

# Tabby
TABBY_TEST_PUBLIC_KEY=pk_test_xxx
TABBY_TEST_SECRET_KEY=sk_test_xxx
TABBY_TEST_MERCHANT_CODE=your_merchant_code
TABBY_TEST_URL=https://api.tabby.ai/api/v2

TABBY_LIVE_PUBLIC_KEY=pk_live_xxx
TABBY_LIVE_SECRET_KEY=sk_live_xxx
TABBY_LIVE_MERCHANT_CODE=your_merchant_code
TABBY_LIVE_URL=https://api.tabby.ai/api/v2

# Tamara
TAMARA_TEST_URL=https://api-sandbox.tamara.co
TAMARA_TEST_API_TOKEN=your_api_token
TAMARA_TEST_NOTIFICATION_TOKEN=your_notification_token

TAMARA_LIVE_URL=https://api.tamara.co
TAMARA_LIVE_API_TOKEN=your_api_token
TAMARA_LIVE_NOTIFICATION_TOKEN=your_notification_token

# MyFatoorah
MYFATOORAH_TEST_URL=https://apitest.myfatoorah.com
MYFATOORAH_TEST_API_KEY=your_api_key

MYFATOORAH_LIVE_URL=https://api.myfatoorah.com
MYFATOORAH_LIVE_API_KEY=your_api_key

# Moyasar
MOYASAR_TEST_URL=https://api.moyasar.com/v1
MOYASAR_TEST_SECRET_KEY=sk_test_xxx
MOYASAR_TEST_PUBLISHABLE_KEY=pk_test_xxx

MOYASAR_LIVE_URL=https://api.moyasar.com/v1
MOYASAR_LIVE_SECRET_KEY=sk_live_xxx
MOYASAR_LIVE_PUBLISHABLE_KEY=pk_live_xxx
```

---

## Usage

### 1. Make Your Model Payable

Implement the `PayableInterface` on any model you want to accept payments for:

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable;
use Modules\Payment\Contracts\PayableInterface;
use Modules\Payment\Traits\HasPayments;
use Modules\Payment\Models\PaymentTransaction;

class Order extends Model implements PayableInterface
{
    use HasPayments;

    /**
     * Check if this model can be paid for.
     */
    public function canBePaid(): bool
    {
        return $this->status === 'pending'
            && !$this->hasSuccessfulPayment();
    }

    /**
     * Get the payment amount.
     */
    public function getPaymentAmount(): float
    {
        return (float) $this->total_amount;
    }

    /**
     * Get the payment currency.
     */
    public function getPaymentCurrency(): string
    {
        return 'SAR';
    }

    /**
     * Get payment description.
     */
    public function getPaymentDescription(): string
    {
        return "Order #{$this->id}";
    }

    /**
     * Called when payment succeeds.
     */
    public function onPaymentCompleted(PaymentTransaction $transaction): void
    {
        $this->update([
            'status' => 'paid',
            'paid_at' => now(),
        ]);

        // Send confirmation email, etc.
    }

    /**
     * Called when payment fails.
     */
    public function onPaymentFailed(PaymentTransaction $transaction): void
    {
        $this->update(['status' => 'payment_failed']);
    }

    /**
     * Get the user for this payment.
     */
    public function getPaymentUser(): ?Authenticatable
    {
        return $this->user;
    }
}
```

### 2. PayableRegistry (Auto-Discovery)

Models implementing `PayableInterface` are automatically discovered from:
- `app/Models/`
- `modules/*/src/Models/`

```php
use Modules\Payment\Helpers\PayableRegistry;

// Get all available payable types
PayableRegistry::aliases(); // ['order', 'booking', 'wallet_charge', ...]

// Find payable by alias and id/uuid
PayableRegistry::find('order', 123);           // By ID
PayableRegistry::find('order', 'uuid-xxx');    // By UUID

// Manual registration (optional)
PayableRegistry::register('custom_order', \App\Models\CustomOrder::class);
```

#### UUID Support

If your model uses UUID, add this method:

```php
class Order extends Model implements PayableInterface
{
    // Optional: specify uuid column name (default: 'uuid')
    public function getUuidColumn(): string
    {
        return 'uuid'; // or 'public_id', etc.
    }
}
```

### 3. Process Payments

#### Using Dependency Injection

```php
<?php

namespace App\Http\Controllers;

use App\Models\Order;
use Illuminate\Http\Request;
use Modules\Payment\Services\PaymentManager;

class PaymentController extends Controller
{
    public function __construct(
        protected PaymentManager $paymentManager
    ) {}

    /**
     * Initiate checkout.
     */
    public function checkout(Request $request, Order $order)
    {
        $result = $this->paymentManager->checkout($order, $request->payment_method, [
            'brand' => $request->brand, // visa, mada, master, applepay
        ]);

        if (!$result->success) {
            return response()->json([
                'success' => false,
                'message' => $result->message,
            ], 422);
        }

        return response()->json([
            'success' => true,
            'transaction_id' => $result->transaction->merchant_transaction_id,
            'redirect_url' => $result->redirectUrl,
            'action' => $result->action, // redirect, completed, none
        ]);
    }

    /**
     * Verify payment callback.
     */
    public function verify(Request $request)
    {
        $transaction = $this->paymentManager->findTransaction($request->transaction_id);

        if (!$transaction) {
            return response()->json([
                'success' => false,
                'message' => 'Transaction not found',
            ], 404);
        }

        $result = $this->paymentManager->verify($transaction);

        return response()->json([
            'success' => $result->success,
            'message' => $result->message,
            'status' => $transaction->fresh()->status->value,
        ]);
    }
}
```

#### Using Facade

```php
use Modules\Payment\Facades\Payment;

// Checkout
$result = Payment::checkout($order, 'hyperpay', ['brand' => 'visa']);

// Verify
$transaction = Payment::findTransaction($transactionId);
$result = Payment::verify($transaction);

// Cancel
Payment::cancel($transaction, 'Cancelled by user');
```

---

## Payment Flow

```
┌─────────────────────────────────────────────────────────────────┐
│                        PAYMENT FLOW                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. Client Request (checkout)                                   │
│         │                                                       │
│         ▼                                                       │
│  2. PaymentManager::checkout()                                  │
│         │                                                       │
│         ▼                                                       │
│  3. Create PaymentTransaction (status: PENDING)                 │
│         │                                                       │
│         ▼                                                       │
│  4. Call Gateway->checkout()                                    │
│         │                                                       │
│         ▼                                                       │
│  5. Return { transaction_id, redirect_url }                     │
│         │                                                       │
│         ▼                                                       │
│     [User completes payment on gateway]                         │
│         │                                                       │
│         ▼                                                       │
│  6. Gateway redirects to callback URL                           │
│         │                                                       │
│         ▼                                                       │
│  7. PaymentManager::verify()                                    │
│         │                                                       │
│         ▼                                                       │
│  8. Gateway->verify() confirms payment                          │
│         │                                                       │
│         ▼                                                       │
│  9. Update transaction (status: COMPLETED)                      │
│         │                                                       │
│         ▼                                                       │
│  10. Call payable->onPaymentCompleted()                         │
│         │                                                       │
│         ▼                                                       │
│  11. Dispatch PaymentCompleted event                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

---

## API Endpoints

### Public Payment Routes

Base URL: `/api/payment`

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/checkout` | Initiate payment |
| POST | `/verify` | Verify payment |
| GET | `/status/{transaction_id}` | Get transaction status |
| GET | `/callback` | Handle gateway callback |

#### Checkout Request

```json
POST /api/payment/checkout

{
    "payment_method": "hyperpay",
    "brand": "visa",
    "payable_type": "order",
    "payable_id": 123,
    "meta": {
        "custom_field": "value"
    }
}
```

**Note:** `payable_id` supports both integer ID and UUID string.

#### Response

```json
{
    "success": true,
    "message": "Payment initialized successfully",
    "data": {
        "transaction_id": "txn_01HXXXXXX",
        "redirect_url": "https://...",
        "action": "redirect",
        "payment_method": "hyperpay"
    }
}
```

---

## Wallet System

### Enable Wallet for Users

```php
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Modules\Payment\Traits\HasWallet;

class User extends Authenticatable
{
    use HasWallet;
}
```

### Wallet API Endpoints

Base URL: `/api/payment/wallet` (requires authentication)

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/` | Get wallet info with recent transactions |
| GET | `/balance` | Get balance only |
| GET | `/transactions` | Get paginated transactions |
| POST | `/charge` | Charge wallet via payment gateway |
| GET | `/charges` | Get charge history |
| POST | `/withdraw` | Create withdraw request |
| GET | `/withdraw-requests` | Get user's withdraw requests |

#### Get Wallet Info

```
GET /api/payment/wallet

Response:
{
    "success": true,
    "data": {
        "balance": 500.00,
        "withdrawal_balance": 100.00,
        "total_balance": 600.00,
        "transactions": [...]
    }
}
```

#### Charge Wallet

```json
POST /api/payment/wallet/charge

{
    "amount": 100,
    "payment_method": "hyperpay",
    "brand": "visa"
}

Response:
{
    "success": true,
    "message": "Wallet charge initiated successfully.",
    "data": {
        "charge_id": 1,
        "transaction_id": "txn_01HXXXXXXXX",
        "redirect_url": "https://...",
        "action": "redirect",
        "payment_method": "hyperpay"
    }
}
```

#### Create Withdraw Request

```json
POST /api/payment/wallet/withdraw

{
    "amount": 500,
    "bank_name": "Al Rajhi Bank",
    "account_name": "Ahmed Mohamed",
    "account_number": "1234567890",
    "IBAN": "SA0380000000608010167519"
}

Response:
{
    "success": true,
    "message": "Withdraw request created successfully.",
    "data": {
        "withdraw_request": {...},
        "wallet": {...}
    }
}
```

### Wallet Operations (Code)

```php
// Get or create wallet
$wallet = $user->getOrCreateWallet();

// Check balance
$balance = $user->getWalletBalance();
// or
$balance = $user->balance();

// Deposit funds
$transaction = $user->deposit(100.00, [
    'en' => 'Bonus reward',
    'ar' => 'مكافأة',
]);

// Withdraw funds
$transaction = $user->withdraw(50.00, [
    'en' => 'Purchase',
    'ar' => 'شراء',
]);

// Check if has enough balance
if ($user->hasEnoughBalance(75.00)) {
    // Process payment
}

// Transfer between users
$result = $user->transfer($recipient, 100.00, 'Gift');
```

### Wallet Service

```php
use Modules\Payment\Services\WalletService;

$walletService = app(WalletService::class);

// Get wallet info
$info = $walletService->getWalletInfo($user);

// Create withdraw request
$result = $walletService->createWithdrawRequest($user, [
    'amount' => 500,
    'bank_name' => 'Al Rajhi Bank',
    'account_name' => 'John Doe',
    'account_number' => '1234567890',
    'IBAN' => 'SA0380000000608010167519',
]);

// Charge wallet
$transaction = $walletService->chargeWallet($user, 100.00);

// Process payment
$transaction = $walletService->processPayment($user, 50.00, 'Order #123');

// Refund
$transaction = $walletService->refund($user, 25.00, 'Order cancelled');
```

---

## Admin API

### Admin Payment Transaction Endpoints

Base URL: `/api/admin/payment` (requires authentication)

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/transactions` | List all transactions |
| GET | `/transactions/{id}` | Get single transaction |
| GET | `/transactions/statistics` | Get statistics |
| GET | `/transactions/filters` | Get filter options |
| GET | `/transactions/export` | Export to CSV |
| POST | `/transactions/{id}/refund` | Refund transaction |

#### List Transactions with Filters

```
GET /api/admin/payment/transactions?status=completed&payment_method=hyperpay&from_date=2024-01-01&search=txn_123
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `status` | string | pending, completed, failed, refunded, cancelled |
| `payment_method` | string | hyperpay, tabby, tamara, etc. |
| `user_id` | int | Filter by user |
| `payable_type` | string | Filter by payable type |
| `from_date` | date | From date |
| `to_date` | date | To date |
| `min_amount` | float | Minimum amount |
| `max_amount` | float | Maximum amount |
| `search` | string | Search transaction IDs |
| `per_page` | int | Items per page (default: 15) |

#### Statistics Response

```json
{
    "success": true,
    "data": {
        "total_transactions": 150,
        "total_amount": 25000.00,
        "completed_count": 120,
        "failed_count": 20,
        "pending_count": 10,
        "success_rate": 80.0,
        "by_payment_method": {
            "hyperpay": { "count": 80, "total": 15000 },
            "tabby": { "count": 40, "total": 10000 }
        },
        "by_status": {
            "completed": 120,
            "failed": 20,
            "pending": 10
        },
        "daily_stats": [...]
    }
}
```

#### Refund Transaction

```json
POST /api/admin/payment/transactions/123/refund

{
    "amount": 50.00,
    "reason": "Customer request"
}
```

### Admin Withdraw Request Endpoints

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/withdrawals` | List all requests |
| GET | `/withdrawals/{id}` | Get single request |
| GET | `/withdrawals/statistics` | Get statistics |
| GET | `/withdrawals/filters` | Get filter options |
| GET | `/withdrawals/export` | Export to CSV |
| POST | `/withdrawals/{id}/approve` | Approve request |
| POST | `/withdrawals/{id}/reject` | Reject request |

#### List Withdrawals with Filters

```
GET /api/admin/payment/withdrawals?status=pending&from_date=2024-01-01&search=ahmed
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `status` | string | pending, approved, rejected |
| `wallet_id` | int | Filter by wallet |
| `user_id` | int | Filter by user |
| `bank_name` | string | Filter by bank |
| `from_date` | date | From date |
| `to_date` | date | To date |
| `min_amount` | float | Minimum amount |
| `max_amount` | float | Maximum amount |
| `search` | string | Search name, email, phone, IBAN |

#### Approve Request

```json
POST /api/admin/payment/withdrawals/123/approve

{
    "note": "Transferred successfully",
    "admin_data": {
        "transfer_ref": "TRX123456"
    }
}
```

#### Reject Request

```json
POST /api/admin/payment/withdrawals/123/reject

{
    "note": "Invalid bank account details"
}
```

#### Withdraw Statistics

```json
{
    "success": true,
    "data": {
        "total_requests": 50,
        "pending": { "count": 10, "amount": 5000.00 },
        "approved": { "count": 35, "amount": 25000.00 },
        "rejected": { "count": 5, "amount": 2000.00 },
        "by_bank": {
            "Al Rajhi": { "count": 20, "total": 15000.00 },
            "Al Ahli": { "count": 15, "total": 10000.00 }
        },
        "daily_stats": {...}
    }
}
```

---

## Events

The module dispatches the following events:

| Event | When | Payload |
|-------|------|---------|
| `PaymentInitiated` | Transaction created | `PaymentTransaction` |
| `PaymentCompleted` | Payment verified successfully | `PaymentTransaction` |
| `PaymentFailed` | Payment verification failed | `PaymentTransaction` |

### Listening to Events

```php
// app/Providers/EventServiceProvider.php

use Modules\Payment\Events\PaymentCompleted;
use Modules\Payment\Events\PaymentFailed;

protected $listen = [
    PaymentCompleted::class => [
        SendPaymentConfirmationEmail::class,
        UpdateInventory::class,
    ],
    PaymentFailed::class => [
        NotifyAdminOfFailedPayment::class,
    ],
];
```

---

## PaymentTransaction Model

### Attributes

| Attribute | Type | Description |
|-----------|------|-------------|
| `id` | int | Primary key |
| `payable_type` | string | Morph class (e.g., `App\Models\Order`) |
| `payable_id` | int | Morph ID |
| `user_id` | int | User who initiated payment |
| `payment_method` | string | Gateway name |
| `payment_brand` | string | Card brand (visa, mada, etc.) |
| `merchant_transaction_id` | string | Our unique ID (txn_xxx) |
| `provider_transaction_id` | string | Gateway's transaction ID |
| `amount` | decimal | Payment amount |
| `currency` | string | Currency code (SAR) |
| `status` | enum | pending, processing, completed, failed, refunded, cancelled |
| `status_message` | string | Error or success message |
| `checkout_response` | json | Gateway checkout response |
| `verify_response` | json | Gateway verify response |
| `meta` | json | Custom metadata |
| `completed_at` | timestamp | When payment completed |
| `failed_at` | timestamp | When payment failed |

### Helper Methods

```php
$transaction = PaymentTransaction::find(1);

// Status checks
$transaction->isPending();
$transaction->isProcessing();
$transaction->isCompleted();
$transaction->isFailed();
$transaction->isRefunded();
$transaction->isCancelled();
$transaction->isFinal(); // completed, failed, refunded, or cancelled

// Scopes
PaymentTransaction::pending()->get();
PaymentTransaction::completed()->get();
PaymentTransaction::failed()->get();
PaymentTransaction::forPayable($order)->get();
PaymentTransaction::byMethod('hyperpay')->get();
PaymentTransaction::byUser($userId)->get();
```

### HasPayments Trait Methods

```php
// On your payable model (Order, Subscription, etc.)
$order->paymentTransactions();           // All transactions
$order->latestPaymentTransaction();      // Latest transaction
$order->successfulPaymentTransaction();  // Latest successful
$order->pendingPaymentTransaction();     // Latest pending
$order->hasPendingPayment();             // bool
$order->hasSuccessfulPayment();          // bool
$order->getTotalPaidAmount();            // float
$order->cancelPendingPayments('reason'); // Cancel all pending
```

---

## Gateway-Specific Features

### HyperPay

```php
// Checkout with specific brand
$result = Payment::checkout($order, 'hyperpay', [
    'brand' => 'mada', // visa, master, mada, applepay
]);

// Get payment status directly
$gateway = Payment::gateway('hyperpay');
$status = $gateway->getPaymentStatus($checkoutId, 'visa');
```

### Tabby

```php
$result = Payment::checkout($order, 'tabby');

// Get disputes
$gateway = Payment::gateway('tabby');
$disputes = $gateway->disputes();
$dispute = $gateway->getDispute($disputeId);
```

### Tamara

```php
// Checkout with instalments
$result = Payment::checkout($order, 'tamara', [
    'instalments' => 4, // 3, 4, or 6
]);

// Capture payment
$gateway = Payment::gateway('tamara');
$gateway->capture($transaction);

// Refund
$gateway->refund($transaction, 100.00, 'Customer request');
```

### MyFatoorah

```php
// Checkout with specific payment method
$result = Payment::checkout($order, 'myfatoorah', [
    'brand' => 'mada', // visa, master, mada, applepay, stcpay, knet
]);

// Get available payment methods
$gateway = Payment::gateway('myfatoorah');
$methods = $gateway->getPaymentMethods();

// Refund
$gateway->refund($transaction, 50.00, 'Partial refund');
```

### Moyasar

```php
// Card payment
$result = Payment::checkout($order, 'moyasar', [
    'brand' => 'creditcard',
    'card_name' => 'John Doe',
    'card_number' => '4111111111111111',
    'card_cvc' => '123',
    'card_month' => '12',
    'card_year' => '2025',
]);

// Apple Pay
$result = Payment::checkout($order, 'moyasar', [
    'brand' => 'applepay',
    'apple_token' => $applePayToken,
]);

// STC Pay
$result = Payment::checkout($order, 'moyasar', [
    'brand' => 'stcpay',
    'mobile' => '966500000000',
]);

// Get publishable key for frontend
$gateway = Payment::gateway('moyasar');
$publishableKey = $gateway->getPublishableKey();

// Refund
$gateway->refund($transaction, 50.00);

// Void authorized payment
$gateway->void($transaction);
```

### Wallet

```php
// Pay with wallet balance
$result = Payment::checkout($order, 'wallet');
// Completes immediately if balance sufficient

// Refund to wallet
$gateway = Payment::gateway('wallet');
$gateway->refund($transaction);
```

---

## Custom Gateway

You can create custom payment gateways:

```php
<?php

namespace App\Payment\Gateways;

use Modules\Payment\Gateways\AbstractPaymentGateway;
use Modules\Payment\Contracts\PayableInterface;
use Modules\Payment\DTOs\CheckoutResultDTO;
use Modules\Payment\DTOs\VerifyResultDTO;
use Modules\Payment\Models\PaymentTransaction;

class CustomGateway extends AbstractPaymentGateway
{
    public function getName(): string
    {
        return 'custom';
    }

    protected function loadConfig(): void
    {
        $this->config = config("payment.gateways.custom.{$this->mode}", []);
    }

    public function supportsBrand(?string $brand): bool
    {
        return true;
    }

    public function checkout(
        PayableInterface $payable,
        PaymentTransaction $transaction,
        array $options = []
    ): CheckoutResultDTO {
        // Your checkout logic
        return CheckoutResultDTO::success(
            providerTransactionId: 'xxx',
            redirectUrl: 'https://...',
            action: 'redirect',
            responseData: [],
        );
    }

    public function verify(PaymentTransaction $transaction): VerifyResultDTO
    {
        // Your verify logic
        return VerifyResultDTO::success($transaction, 'Success');
    }
}
```

Register in `AppServiceProvider`:

```php
use Modules\Payment\Services\PaymentManager;
use App\Payment\Gateways\CustomGateway;

public function boot()
{
    app(PaymentManager::class)->registerGateway('custom', new CustomGateway());
}
```

---

## Database Tables

| Table | Description |
|-------|-------------|
| `payment_transactions` | Main table for tracking all payment attempts |
| `wallets` | Stores wallet balances for users/models |
| `wallet_transactions` | Records all wallet operations |
| `wallet_charges` | Records wallet charge requests via payment gateways |
| `withdraw_requests` | Tracks withdrawal requests pending admin approval |

---

## Error Handling

```php
use Modules\Payment\Exceptions\PaymentException;

try {
    $result = Payment::checkout($order, 'hyperpay', ['brand' => 'visa']);
} catch (PaymentException $e) {
    // Handle payment-specific errors
    $message = $e->getMessage();
    $context = $e->getContext(); // Additional error data
}
```

Common exceptions:
- `PaymentException::insufficientBalance($required, $available)`
- `PaymentException::gatewayNotSupported($gateway)`
- `PaymentException::brandNotSupported($brand, $gateway)`
- `PaymentException::transactionNotFound($id)`
- `PaymentException::transactionAlreadyProcessed($id)`

---

## Configuration Options

```php
// config/payment.php

return [
    'mode' => env('PAYMENT_MODE', 'test'),
    'currency' => env('PAYMENT_CURRENCY', 'SAR'),
    'user_model' => \App\Models\User::class,
    'log_channel' => env('PAYMENT_LOG_CHANNEL', 'stack'),

    'gateways' => [
        // Gateway configurations...
    ],

    'wallet' => [
        'enabled' => true,
        'allow_negative_balance' => false,
        'minimum_withdrawal' => 10.00,
        'maximum_withdrawal' => 10000.00,
    ],

    'routes' => [
        'prefix' => 'api/payment',
        'middleware' => ['api'],
        'admin_prefix' => 'api/admin/payment',
        'admin_middleware' => ['api', 'auth:sanctum'],
    ],

    // Manual payable model registration (optional)
    'payable_models' => [
        // 'custom_order' => \App\Models\CustomOrder::class,
    ],
];
```

---

## Testing

The module includes comprehensive tests for all functionality.

### Running Tests

```bash
# Run all payment module tests
cd modules/Payment
./vendor/bin/phpunit

# Or from project root
./vendor/bin/phpunit modules/Payment/tests
```

### Test Structure

```
modules/Payment/tests/
├── Feature/
│   ├── PaymentControllerTest.php
│   ├── WalletControllerTest.php
│   └── Admin/
│       ├── PaymentTransactionControllerTest.php
│       └── WithdrawRequestControllerTest.php
├── Unit/
│   ├── PaymentManagerTest.php
│   ├── WalletServiceTest.php
│   ├── PayableRegistryTest.php
│   ├── Gateways/
│   │   └── WalletGatewayTest.php
│   └── Models/
│       ├── PaymentTransactionTest.php
│       ├── WalletTest.php
│       └── WalletTransactionTest.php
└── Mocks/
    ├── MockPaymentGateway.php
    ├── MockPayable.php
    └── MockUserWithWallet.php
```

---

## Localization

The module supports multiple languages out of the box.

### Supported Languages

- **English** (`en`)
- **Arabic** (`ar`)

### Translation Files

```
modules/Payment/resources/lang/
├── en/
│   └── payment.php
└── ar/
    └── payment.php
```

### Using Translations

```php
// In your code
__('payment::payment.success.payment_completed');

// In Blade templates
{{ __('payment::payment.errors.insufficient_balance', ['required' => 100, 'available' => 50]) }}
```

### Publishing Translations

```bash
php artisan vendor:publish --tag=payment-lang
```

---

## Enums Reference

### PaymentStatusEnum

| Value | Label (EN) | Label (AR) | Color |
|:------|:-----------|:-----------|:------|
| `pending` | Pending | قيد الانتظار | warning |
| `processing` | Processing | قيد المعالجة | info |
| `completed` | Completed | مكتمل | success |
| `failed` | Failed | فشل | danger |
| `refunded` | Refunded | مسترد | secondary |
| `cancelled` | Cancelled | ملغي | dark |

### PaymentMethodEnum

| Value | Label (EN) | Label (AR) |
|:------|:-----------|:-----------|
| `cash` | Cash | نقدي |
| `wallet` | Wallet | المحفظة |
| `hyperpay` | HyperPay | هايبر باي |
| `tabby` | Tabby | تابي |
| `tamara` | Tamara | تمارا |
| `myfatoorah` | MyFatoorah | ماي فاتورة |
| `moyasar` | Moyasar | ميسر |

### PaymentBrandEnum

| Value | Label (EN) | Label (AR) |
|:------|:-----------|:-----------|
| `visa` | Visa | فيزا |
| `master` | Mastercard | ماستر كارد |
| `mada` | Mada | مدى |
| `applepay` | Apple Pay | أبل باي |

### WalletTransactionTypeEnum

| Value | Label (EN) | Label (AR) |
|:------|:-----------|:-----------|
| `deposit` | Deposit | إيداع |
| `withdraw` | Withdraw | سحب |
| `withdraw_request` | Withdraw Request | طلب سحب |
| `charge` | Charge | شحن |
| `payment` | Payment | دفع |
| `refund` | Refund | استرداد |

---

## Security Considerations

1. **API Keys**: Never commit API keys to version control. Use environment variables.
2. **Webhooks**: Validate webhook signatures when receiving callbacks from payment providers.
3. **HTTPS**: Always use HTTPS in production for payment-related endpoints.
4. **Rate Limiting**: Consider implementing rate limiting on payment endpoints.
5. **Logging**: The module logs to a configurable channel. Ensure sensitive data is not logged.

---

## Troubleshooting

### Common Issues

<details>
<summary><strong>Transaction stuck in pending status</strong></summary>

This usually happens when the callback URL wasn't reached. Check:
- Callback URL is publicly accessible
- No firewall blocking incoming requests
- Gateway webhook settings are correct

</details>

<details>
<summary><strong>Gateway returns "Invalid credentials"</strong></summary>

Verify:
- Correct API keys in `.env`
- `PAYMENT_MODE` matches your credentials (test vs live)
- Entity IDs are correct for HyperPay

</details>

<details>
<summary><strong>Payable model not found in registry</strong></summary>

Ensure:
- Model implements `PayableInterface`
- Model is in `app/Models/` or `modules/*/src/Models/`
- Run `composer dump-autoload`

</details>

---

## Postman Collection

A Postman collection is included for easy API testing:

```
modules/Payment/payment_postman_collection.json
```

Import this collection into Postman to quickly test all endpoints.

---

## Directory Structure

```
modules/Payment/
├── composer.json
├── README.md
├── phpunit.xml
├── payment_postman_collection.json
├── config/
│   └── payment.php
├── database/
│   └── migrations/
│       ├── 2024_01_01_000001_create_payment_transactions_table.php
│       ├── 2024_01_01_000002_create_wallets_table.php
│       ├── 2024_01_01_000003_create_wallet_transactions_table.php
│       ├── 2024_01_01_000004_create_withdraw_requests_table.php
│       └── 2024_01_01_000005_create_wallet_charges_table.php
├── resources/
│   ├── lang/
│   │   ├── ar/
│   │   │   └── payment.php
│   │   └── en/
│   │       └── payment.php
│   └── views/
│       └── hyperpay.blade.php
├── routes/
│   └── api.php
├── src/
│   ├── PaymentServiceProvider.php
│   ├── Contracts/
│   │   ├── PayableInterface.php
│   │   └── PaymentGatewayInterface.php
│   ├── DTOs/
│   │   ├── CheckoutResultDTO.php
│   │   └── VerifyResultDTO.php
│   ├── Enums/
│   │   ├── PaymentStatusEnum.php
│   │   ├── PaymentMethodEnum.php
│   │   ├── PaymentBrandEnum.php
│   │   └── WalletTransactionTypeEnum.php
│   ├── Events/
│   │   ├── PaymentInitiated.php
│   │   ├── PaymentCompleted.php
│   │   └── PaymentFailed.php
│   ├── Exceptions/
│   │   └── PaymentException.php
│   ├── Facades/
│   │   └── Payment.php
│   ├── Gateways/
│   │   ├── AbstractPaymentGateway.php
│   │   ├── HyperPayGateway.php
│   │   ├── TabbyGateway.php
│   │   ├── TamaraGateway.php
│   │   ├── MyFatoorahGateway.php
│   │   ├── MoyasarGateway.php
│   │   └── WalletGateway.php
│   ├── Helpers/
│   │   └── PayableRegistry.php
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── PaymentController.php
│   │   │   ├── WalletController.php
│   │   │   └── Admin/
│   │   │       ├── PaymentTransactionController.php
│   │   │       └── WithdrawRequestController.php
│   │   ├── Requests/
│   │   │   ├── CheckoutRequest.php
│   │   │   ├── VerifyRequest.php
│   │   │   ├── WalletChargeRequest.php
│   │   │   └── CreateWithdrawRequest.php
│   │   └── Resources/
│   │       ├── PaymentTransactionResource.php
│   │       ├── WalletResource.php
│   │       ├── WalletTransactionResource.php
│   │       ├── WithdrawRequestResource.php
│   │       └── Admin/
│   │           ├── PaymentTransactionResource.php
│   │           └── WithdrawRequestResource.php
│   ├── Models/
│   │   ├── PaymentTransaction.php
│   │   ├── Wallet.php
│   │   ├── WalletTransaction.php
│   │   ├── WalletCharge.php
│   │   └── WithdrawRequest.php
│   ├── Services/
│   │   ├── PaymentManager.php
│   │   └── WalletService.php
│   └── Traits/
│       ├── HasPayments.php
│       └── HasWallet.php
└── tests/
    ├── TestCase.php
    ├── Feature/
    │   ├── PaymentControllerTest.php
    │   ├── WalletControllerTest.php
    │   └── Admin/
    │       ├── PaymentTransactionControllerTest.php
    │       └── WithdrawRequestControllerTest.php
    ├── Unit/
    │   ├── PaymentManagerTest.php
    │   ├── WalletServiceTest.php
    │   ├── PayableRegistryTest.php
    │   ├── Gateways/
    │   │   └── WalletGatewayTest.php
    │   └── Models/
    │       ├── PaymentTransactionTest.php
    │       ├── WalletTest.php
    │       └── WalletTransactionTest.php
    └── Mocks/
        ├── MockPaymentGateway.php
        ├── MockPayable.php
        └── MockUserWithWallet.php
```

---

## Contributing

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

---

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

---

<p align="center">
  <strong>Built with Laravel</strong>
</p>
