Professional Laravel payment gateway package for Iranian payment providers with full SOLID principles and Clean Architecture.
- Supported Gateways
- Key Features
- Requirements
- Installation
- Configuration
- Usage
- Testing
- Architecture
- Contributing
- Security
- License
- ✅ Bank Mellat (Bank-e Mellat) - SOAP
- ✅ Mabna Card (Sepehr) - REST API
- ✅ ZarinPal - REST API
- 🏗️ SOLID Principles - Full adherence to SOLID principles
- 🎨 Clean Architecture - Clean and maintainable architecture
- 🎯 Design Patterns - Repository, Factory, Facade patterns
- 🧩 Contract-Based - Interface-driven programming
- 📦 Auto Storage - Automatic transaction storage in database
- 🔄 Easy Switching - Switch between gateways easily
- 🛡️ Error Handling - Professional exception handling
- ✨ Value Objects - Type-safe value objects
- 🧪 Test Coverage - Comprehensive unit and feature tests
- 🔌 Extensible - Easy to add custom gateways
- 🌐 Sandbox Support - Full sandbox/test environment support
- 📝 Full Documentation - Comprehensive docs with examples
- 🎓 Educational - Self-documented, readable code
- 🔧 Simple Config - Easy configuration via .env
- PHP 8.1 or higher
- Laravel 10.x or 11.x
- PHP SOAP extension (for Mellat gateway)
- PHP JSON extension
composer require fiachehr/laravel-pardakht# Publish all (config + migrations)
php artisan vendor:publish --provider="Fiachehr\Pardakht\PardakhtServiceProvider"Or separately:
# Config only
php artisan vendor:publish --tag=pardakht-config
# Migrations only
php artisan vendor:publish --tag=pardakht-migrationsphp artisan migrateThis creates the pardakht_transactions table for storing transactions.
Add to your .env file:
# Default gateway
PARDAKHT_DEFAULT_GATEWAY=mellat
# ========================================
# Bank Mellat (Production)
# ========================================
MELLAT_TERMINAL_ID=your_terminal_id
MELLAT_USERNAME=your_username
MELLAT_PASSWORD=your_password
MELLAT_CALLBACK_URL=https://yoursite.com/payment/callback
MELLAT_SANDBOX=false
# ========================================
# Mabna Card / Sepehr (Production)
# ========================================
MABNA_TERMINAL_ID=your_terminal_id
MABNA_CALLBACK_URL=https://yoursite.com/payment/callback
MABNA_SANDBOX=false
# ========================================
# ZarinPal (Production)
# ========================================
ZARINPAL_MERCHANT_ID=your_merchant_id
ZARINPAL_CALLBACK_URL=https://yoursite.com/payment/callback
ZARINPAL_SANDBOX=false
ZARINPAL_DESCRIPTION="Payment via ZarinPal"For development and testing, enable sandbox mode:
# Bank Mellat - Test Mode
MELLAT_SANDBOX=true
MELLAT_TERMINAL_ID=test_terminal_id
MELLAT_USERNAME=test_username
MELLAT_PASSWORD=test_password
# Mabna - Test Mode
MABNA_SANDBOX=true
MABNA_TERMINAL_ID=test_terminal_id
# ZarinPal - Test Mode
ZARINPAL_SANDBOX=true
ZARINPAL_MERCHANT_ID=test_merchant_idGet Test Credentials:
- For Bank Mellat and Mabna: BankTest.ir
- For ZarinPal: ZarinPal Sandbox
The config/pardakht.php file:
return [
// Default gateway
'default' => env('PARDAKHT_DEFAULT_GATEWAY', 'mellat'),
// Auto-store transactions
'store_transactions' => true,
// Gateway configurations
'gateways' => [
'mellat' => [
'driver' => 'mellat',
'terminal_id' => env('MELLAT_TERMINAL_ID'),
'username' => env('MELLAT_USERNAME'),
'password' => env('MELLAT_PASSWORD'),
'callback_url' => env('MELLAT_CALLBACK_URL'),
'sandbox' => env('MELLAT_SANDBOX', false),
],
// ...
],
];use Fiachehr\Pardakht\Facades\Pardakht;
use Fiachehr\Pardakht\ValueObjects\PaymentRequest;
public function payment()
{
// Create payment request
$paymentRequest = new PaymentRequest(
amount: 100000, // Amount in Rials
orderId: 'ORDER-12345', // Order ID
callbackUrl: route('payment.callback'),
description: 'Order payment #12345',
mobile: '09123456789', // Optional
email: 'user@example.com', // Optional
metadata: [ // Optional
'user_id' => auth()->id(),
'product_id' => 5
]
);
try {
// Send request to default gateway
$response = Pardakht::request($paymentRequest);
// Or use specific gateway
// $response = Pardakht::request($paymentRequest, 'zarinpal');
if ($response->isSuccessful()) {
// Store tracking code in session
session(['payment_tracking_code' => $response->trackingCode]);
// Redirect user to payment gateway
return redirect($response->getPaymentUrl());
}
} catch (\Fiachehr\Pardakht\Exceptions\GatewayException $e) {
// Handle error
\Log::error('Payment request failed', [
'gateway' => $e->getGatewayName(),
'message' => $e->getMessage(),
'code' => $e->getGatewayCode()
]);
return back()->with('error', 'Payment request failed: ' . $e->getMessage());
}
}use Fiachehr\Pardakht\Facades\Pardakht;
use Fiachehr\Pardakht\ValueObjects\VerificationRequest;
use Illuminate\Http\Request;
public function callback(Request $request)
{
// Get tracking code from session
$trackingCode = session('payment_tracking_code');
if (!$trackingCode) {
return redirect()->route('payment.failed')
->with('error', 'Payment information not found');
}
// Create verification request
$verificationRequest = new VerificationRequest(
trackingCode: $trackingCode,
gatewayData: $request->all() // All data returned from gateway
);
try {
// Verify payment
$response = Pardakht::verify($verificationRequest);
// Or specify gateway
// $response = Pardakht::verify($verificationRequest, 'mellat');
if ($response->isSuccessful()) {
// Payment successful - perform required operations
// Example: Update order status
$order = Order::where('id', $orderId)->first();
$order->update([
'status' => 'paid',
'payment_reference' => $response->referenceId,
'paid_at' => now()
]);
// Clear session
session()->forget('payment_tracking_code');
return view('payment.success', [
'referenceId' => $response->referenceId,
'cardNumber' => $response->getMaskedCardNumber(),
'amount' => $response->amount,
'transactionId' => $response->transactionId,
]);
}
} catch (\Fiachehr\Pardakht\Exceptions\GatewayException $e) {
\Log::error('Payment verification failed', [
'gateway' => $e->getGatewayName(),
'message' => $e->getMessage(),
'code' => $e->getGatewayCode()
]);
return view('payment.failed', [
'message' => $e->getMessage(),
'code' => $e->getGatewayCode(),
]);
}
}// Get list of available gateways
$gateways = Pardakht::available();
// ['mellat', 'mabna', 'zarinpal']
// Get specific gateway instance
$mellatGateway = Pardakht::gateway('mellat');
$zarinpalGateway = Pardakht::gateway('zarinpal');
// Use gateway directly
$response = $mellatGateway->request($paymentRequest);use Fiachehr\Pardakht\Contracts\TransactionRepositoryInterface;
class PaymentController extends Controller
{
public function __construct(
protected TransactionRepositoryInterface $transactionRepository
) {}
public function history()
{
// Get successful transactions
$successful = $this->transactionRepository->getSuccessful();
// Get failed transactions
$failed = $this->transactionRepository->getFailed();
// Find by tracking code
$transaction = $this->transactionRepository->findByTrackingCode($trackingCode);
// Find by order ID
$transaction = $this->transactionRepository->findByOrderId($orderId);
}
}use Fiachehr\Pardakht\Models\Transaction;
// Get successful transactions for specific gateway
$transactions = Transaction::gateway('mellat')
->successful()
->latest()
->get();
// Get pending transactions
$pending = Transaction::pending()->get();
// Filter by date
$transactions = Transaction::whereDate('created_at', today())
->successful()
->get();
// Check transaction status
$transaction = Transaction::find(1);
if ($transaction->isSuccessful()) {
// Transaction was successful
}use Fiachehr\Pardakht\Facades\Pardakht;
use Fiachehr\Pardakht\Gateways\AbstractGateway;
class CustomGateway extends AbstractGateway
{
public function getName(): string
{
return 'custom';
}
public function request(PaymentRequest $request): PaymentResponse
{
// Implement payment request
}
public function verify(VerificationRequest $request): VerificationResponse
{
// Implement payment verification
}
protected function validateConfig(): void
{
// Validate configuration
}
}
// Register custom gateway
Pardakht::extend('custom', CustomGateway::class);# Run all tests
composer test
# Run with coverage
composer test-coverage
# Run only unit tests
vendor/bin/phpunit --testsuite=Unit
# Run only feature tests
vendor/bin/phpunit --testsuite=Feature- ✅ Value Objects Tests
- ✅ Gateway Manager Tests
- ✅ Transaction Model Tests
- ✅ Exception Handling Tests
- ✅ Facade Tests
- ✅ Repository Tests
If you discover a security vulnerability, please email fiachehr@example.com.
- ✅ Always store credentials in
.env - ✅ Never commit credentials
- ✅ Always use SSL in production
- ✅ Ensure
SANDBOX=falsein production - ✅ Review payment logs regularly
Contributions are welcome! Please:
- Fork the repository
- Create a new branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Code must follow PSR-12
- Add necessary tests
- Update documentation
- Record changes in CHANGELOG.md
See CHANGELOG.md for full changelog.
This package is open-sourced software licensed under the MIT license. See LICENSE for details.
Fiachehr
- GitHub: @fiachehr
- Email: mailbox@fiachehr.ir
- Laravel community
- Payment gateway developers
- All contributors
- Laravel Documentation
- BankTest.ir - Bank gateway testing environment
- ZarinPal Documentation
// Method 1: Change default gateway in .env
PARDAKHT_DEFAULT_GATEWAY=zarinpal
// Method 2: Specify gateway at runtime
Pardakht::request($paymentRequest, 'zarinpal');// In config/pardakht.php
'store_transactions' => false,try {
$response = Pardakht::request($paymentRequest);
} catch (\Fiachehr\Pardakht\Exceptions\GatewayException $e) {
// Gateway error
$e->getMessage();
$e->getGatewayName();
$e->getGatewayCode();
} catch (\Exception $e) {
// General error
}If you find this package useful, give it a ⭐️!
Made by Fiachehr with ❤️ for Laravel