obrainwave / paygate
Complete Laravel payment solution with 7 payment gateways, JavaScript SDK, advanced security, and plugin system. Unified API for Paystack, GTPay, Flutterwave, Monnify, Interswitch, Remita, and VTPass.
Installs: 43
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/obrainwave/paygate
Requires
- php: ^8.1
- illuminate/contracts: *
- spatie/laravel-package-tools: *
Requires (Dev)
- laravel/pint: ^1.0
- nunomaduro/collision: ^6.1
- orchestra/testbench: ^8.0
- pestphp/pest: ^1.21
- pestphp/pest-plugin-laravel: ^1.1
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^9.6
- spatie/laravel-ray: ^1.26
README
A comprehensive Laravel package that provides a unified interface for 7 payment gateways. Seamlessly handle payments through Paystack, GTPay, Flutterwave, Monnify, Interswitch, Remita, and VTPass with a single, consistent API.
🎉 Version 2.0.0 is here! Complete rewrite with 7 payment gateways, JavaScript SDK, advanced security, plugin system, and much more!
✨ Features
Payment Gateways (7 Total)
- Paystack: Complete payment processing with refunds
- GTPay: Full payment support with refunds
- Flutterwave: Comprehensive payment processing with refunds
- Monnify: Complete payment gateway with refunds
- Interswitch: OAuth-based payment processing with refunds
- Remita: Payment processing (refund via support)
- VTPass: Bill payments for utilities and services
Core Features
- Unified API: Single interface for all payment gateways
- Payment Processing: Initiate, verify, and refund payments
- Database Integration: Automatic payment storage and tracking
- Laravel Events: Payment lifecycle event dispatching
- Webhook Handling: Secure webhook processing with signature verification
- Middleware Protection: Route protection and payment verification
Frontend Integration
- JavaScript SDK: Complete client-side payment handling
- Blade Components: Pre-built frontend components
- Responsive Design: Mobile-friendly payment forms
- Real-time Notifications: Built-in notification system
- Form Validation: Client-side validation and error handling
Advanced Features
- Caching System: Redis support with configurable TTL
- Security Suite: Data encryption, log masking, and fraud detection
- Plugin System: Extensible architecture for custom features
- Performance Optimization: Caching, retry logic, and rate limiting
- API Controllers: Complete REST API for payment operations
- Artisan Commands: Package management and installation
Developer Experience
- Comprehensive Testing: Feature and unit tests with 100% coverage
- Complete Documentation: API docs, examples, and guides
- Easy Installation: One-command setup with
php artisan paygate:install - Configuration Management: Environment-based configuration
- Error Handling: Graceful error management and logging
🚀 Version 2.0.0 - What's New
Major Features
- 7 Payment Gateways - Paystack, GTPay, Flutterwave, Monnify, Interswitch, Remita, VTPass
- JavaScript SDK - Complete client-side payment handling
- Plugin System - Extensible architecture for custom features
- Advanced Security - Fraud detection, data encryption, audit logging
- Performance Optimization - Caching, rate limiting, retry logic
- Complete Frontend - Blade components, responsive design, real-time notifications
Migration from v1.x
- ✅ Backward Compatible - Old API still works
- ✅ Easy Migration - One-command migration
- ✅ Enhanced Features - All new features available
- 📖 Migration Guide - Complete migration guide
Installation
1. Install via Composer
# Install v2.0.0 composer require obrainwave/paygate:^2.0 # Or update from v1.x composer update obrainwave/paygate
2. Run the Installation Command
php artisan paygate:install
This command will:
- Publish migrations and config files
- Run database migrations
- Add environment variables to your
.envfile
3. Configure Environment Variables
Add your payment gateway credentials to your .env file:
# Paystack Configuration PAYSTACK_PUBLIC_KEY=your_paystack_public_key PAYSTACK_SECRET_KEY=your_paystack_secret_key PAYSTACK_WEBHOOK_SECRET=your_paystack_webhook_secret # GTPay Configuration GTPAY_PUBLIC_KEY=your_gtpay_public_key GTPAY_SECRET_KEY=your_gtpay_secret_key GTPAY_WEBHOOK_SECRET=your_gtpay_webhook_secret # Flutterwave Configuration FLUTTERWAVE_PUBLIC_KEY=your_flutterwave_public_key FLUTTERWAVE_SECRET_KEY=your_flutterwave_secret_key FLUTTERWAVE_WEBHOOK_SECRET=your_flutterwave_webhook_secret # Monnify Configuration MONNIFY_API_KEY=your_monnify_api_key MONNIFY_SECRET_KEY=your_monnify_secret_key MONNIFY_WEBHOOK_SECRET=your_monnify_webhook_secret # Interswitch Configuration INTERSWITCH_CLIENT_ID=your_interswitch_client_id INTERSWITCH_CLIENT_SECRET=your_interswitch_client_secret INTERSWITCH_WEBHOOK_SECRET=your_interswitch_webhook_secret # Remita Configuration REMITA_MERCHANT_ID=your_remita_merchant_id REMITA_API_KEY=your_remita_api_key REMITA_SERVICE_TYPE_ID=your_remita_service_type_id REMITA_WEBHOOK_SECRET=your_remita_webhook_secret # VTPass Configuration VTPASS_API_KEY=your_vtpass_api_key VTPASS_SECRET_KEY=your_vtpass_secret_key VTPASS_WEBHOOK_SECRET=your_vtpass_webhook_secret # Security & Performance PAYGATE_ENCRYPT_SENSITIVE_DATA=true PAYGATE_MASK_SENSITIVE_LOGS=true PAYGATE_ENABLE_CACHING=true PAYGATE_CACHE_TTL=300 PAYGATE_FRAUD_DETECTION=true PAYGATE_RISK_THRESHOLD=6
4. Clear Configuration Cache
php artisan config:cache
Usage
Basic Usage
1. Using the Facade (Recommended)
use Paygate; // Initiate Payment $payment = Paygate::initiatePayment([ 'provider' => 'paystack', 'amount' => 250, 'email' => 'customer@example.com', 'reference' => 'TXN_' . time(), 'redirect_url' => 'https://yoursite.com/payment/callback', 'name' => 'John Doe', 'phone_number' => '08012345678' ]); // Verify Payment $verification = Paygate::verifyPayment([ 'provider' => 'paystack', 'reference' => 'TXN_1234567890' ]); // Refund Payment $refund = Paygate::refundPayment([ 'provider' => 'paystack', 'reference' => 'TXN_1234567890', 'amount' => 100, // Optional: partial refund 'reason' => 'Customer requested refund' ]);
2. Using Dependency Injection
use Obrainwave\Paygate\Contracts\PaymentServiceInterface; class PaymentController extends Controller { public function __construct( protected PaymentServiceInterface $paymentService ) {} public function initiate(Request $request) { $result = $this->paymentService->initiatePayment($request->all()); return response()->json($result); } }
3. Using Web Routes
// POST /paygate/initiate Route::post('/paygate/initiate', function(Request $request) { return Paygate::initiatePayment($request->all()); }); // GET /paygate/verify/{reference} Route::get('/paygate/verify/{reference}', function($reference) { return Paygate::verifyPayment(['reference' => $reference]); });
4. Using API Routes
// POST /api/paygate/initiate // GET /api/paygate/verify/{reference} // POST /api/paygate/refund // GET /api/paygate/status/{reference} // GET /api/paygate/history // GET /api/paygate/gateways
Advanced Features
Database Integration
// Get payment by reference $payment = Paygate::getPaymentByReference('TXN_1234567890'); // Get payment history with filters $history = Paygate::getPaymentHistory([ 'status' => 'successful', 'provider' => 'paystack', 'customer_email' => 'customer@example.com', 'date_from' => '2024-01-01', 'date_to' => '2024-12-31' ]); // Get available gateways $gateways = Paygate::getAvailableGateways();
Laravel Events
// Listen to payment events use Obrainwave\Paygate\Events\PaymentInitiated; use Obrainwave\Paygate\Events\PaymentCompleted; use Obrainwave\Paygate\Events\PaymentFailed; Event::listen(PaymentCompleted::class, function($payment) { // Send confirmation email Mail::to($payment->customer_email)->send(new PaymentConfirmation($payment)); }); Event::listen(PaymentFailed::class, function($payment) { // Log failed payment Log::error('Payment failed', (array) $payment); });
Blade Components
<!-- Use the payment form component --> <x-paygate-payment-form /> <!-- Or include in your custom form --> @include('paygate::components.payment-form')
Legacy Usage (Backward Compatibility)
The package maintains backward compatibility with the original API:
use Paygate; $payload = [ 'provider' => 'paystack', 'provider_token' => 'PAYSTACK_SECRET_KEY', // Still supported 'amount' => 250, 'email' => 'customer@example.com', 'reference' => 'TXN_1234567890', 'redirect_url' => 'https://yoursite.com/verify-payment', 'name' => 'John Doe', 'contract_code' => '32904826734', // For Monnify 'payment_methods' => ["card", "bank", "ussd", "qr", "mobile_money", "bank_transfer", "eft"], 'pass_charge' => false, // For GTPay 'title' => "Your Store Name", // For Flutterwave 'logo' => 'https://yoursite.com/logo.png', // For Flutterwave 'phone_number' => '08012345678' // For Flutterwave ]; $payment = Paygate::initiatePayment($payload);
🎯 Advanced Features
Database Integration
The package automatically stores payment records in the database:
// Get payment by reference $payment = Paygate::getPaymentByReference('TXN_1234567890'); // Get payment history with filters $history = Paygate::getPaymentHistory([ 'status' => 'successful', 'provider' => 'paystack', 'customer_email' => 'customer@example.com', 'date_from' => '2024-01-01', 'date_to' => '2024-12-31' ]); // Use Eloquent model directly use Obrainwave\Paygate\Models\Payment; $payments = Payment::successful() ->byProvider('paystack') ->where('amount', '>', 100) ->orderBy('created_at', 'desc') ->get();
Laravel Events
Listen to payment lifecycle events:
use Obrainwave\Paygate\Events\PaymentInitiated; use Obrainwave\Paygate\Events\PaymentCompleted; use Obrainwave\Paygate\Events\PaymentFailed; // In your EventServiceProvider protected $listen = [ PaymentCompleted::class => [ SendPaymentConfirmationEmail::class, UpdateOrderStatus::class, ], PaymentFailed::class => [ LogFailedPayment::class, SendFailureNotification::class, ], ]; // Or use Event::listen Event::listen(PaymentCompleted::class, function($payment) { // Send confirmation email Mail::to($payment->customer_email)->send(new PaymentConfirmation($payment)); // Update order status Order::where('payment_reference', $payment->reference) ->update(['status' => 'paid']); });
Webhook Handling
The package includes comprehensive webhook handling:
// Webhook endpoint is automatically available at: // POST /paygate/webhook // POST /api/paygate/webhook // Configure webhook secrets in your .env: PAYSTACK_WEBHOOK_SECRET=your_webhook_secret GTPAY_WEBHOOK_SECRET=your_webhook_secret FLUTTERWAVE_WEBHOOK_SECRET=your_webhook_secret MONNIFY_WEBHOOK_SECRET=your_webhook_secret
Middleware Protection
Protect routes that require successful payments:
// In your routes file Route::group(['middleware' => [VerifyPaymentMiddleware::class]], function () { Route::get('/download/{reference}', [DownloadController::class, 'download']); Route::get('/access/{reference}', [AccessController::class, 'grant']); }); // The middleware automatically: // - Verifies payment exists // - Checks payment status is 'successful' // - Adds payment data to request
API Endpoints
Complete REST API for payment operations:
// Payment Operations POST /api/paygate/initiate # Initiate payment GET /api/paygate/verify/{ref} # Verify payment POST /api/paygate/refund # Refund payment GET /api/paygate/status/{ref}# Get payment status GET /api/paygate/history # Get payment history GET /api/paygate/gateways # Get available gateways POST /api/paygate/webhook # Webhook endpoint // Web Routes POST /paygate/initiate # Initiate payment GET /paygate/verify/{ref} # Verify payment GET /paygate/callback # Payment callback GET /paygate/status/{ref} # Payment status GET /paygate/history # Payment history GET /paygate/success/{ref} # Success page (protected)
JavaScript SDK
The package includes a comprehensive JavaScript SDK for client-side payment handling:
<!-- Include the JavaScript SDK --> <script src="{{ asset('vendor/paygate/js/paygate.js') }}"></script> <link rel="stylesheet" href="{{ asset('vendor/paygate/css/paygate.css') }}"> <!-- Initialize Paygate --> <script> const paygate = new Paygate({ baseUrl: '/api/paygate', defaultProvider: 'paystack', defaultCurrency: 'NGN', onSuccess: function(response) { console.log('Payment successful:', response); // Handle success }, onError: function(response) { console.error('Payment failed:', response); // Handle error } }); // Initiate payment paygate.initiatePayment({ provider: 'paystack', amount: 250.00, email: 'customer@example.com', reference: 'TXN_1234567890' }); </script>
Auto-Initialization
The SDK automatically initializes payment forms with data attributes:
<form data-paygate-form> <input type="email" name="email" required> <input type="number" name="amount" required> <input type="hidden" name="provider" value="paystack"> <button type="submit">Pay Now</button> </form>
Blade Components
Use pre-built components in your views:
<!-- Payment Form Component --> <x-paygate-payment-form /> <!-- Or include manually --> @include('paygate::components.payment-form') <!-- Payment Status Display --> @if($payment->status === 'successful') <div class="alert alert-success"> Payment successful! Reference: {{ $payment->reference }} </div> @endif
Testing
The package includes comprehensive tests:
# Run tests php artisan test # Run specific test suites php artisan test --filter=PaymentTest php artisan test --filter=PaymentServiceTest # Test coverage php artisan test --coverage
Configuration Options
Customize the package behavior:
// config/paygate.php return [ 'default_currency' => 'NGN', 'default_provider' => 'paystack', 'enable_logging' => true, 'store_payments' => true, 'webhook_enabled' => true, 'rate_limit' => 60, 'retry_attempts' => 3, 'encrypt_sensitive_data' => true, // ... more options ];
Environment Variables
Configure your payment gateways:
# Package Settings PAYGATE_DEFAULT_CURRENCY=NGN PAYGATE_DEFAULT_PROVIDER=paystack PAYGATE_ENABLE_LOGGING=true PAYGATE_STORE_PAYMENTS=true # Paystack PAYSTACK_PUBLIC_KEY=pk_test_... PAYSTACK_SECRET_KEY=sk_test_... PAYSTACK_WEBHOOK_SECRET=whsec_... # GTPay GTPAY_PUBLIC_KEY=pk_... GTPAY_SECRET_KEY=sk_... GTPAY_WEBHOOK_SECRET=... # Flutterwave FLUTTERWAVE_PUBLIC_KEY=FLWPUBK_... FLUTTERWAVE_SECRET_KEY=FLWSECK_... FLUTTERWAVE_WEBHOOK_SECRET=... # Monnify MONNIFY_API_KEY=MK_... MONNIFY_SECRET_KEY=... MONNIFY_WEBHOOK_SECRET=...
🔧 Artisan Commands
Manage the package with built-in commands:
# Install the package php artisan paygate:install # Publish migrations only php artisan vendor:publish --tag=paygate-migrations # Publish config only php artisan vendor:publish --tag=paygate-config # Clear payment cache php artisan cache:clear
🛡️ Security Features
- Webhook Signature Verification: All webhooks are verified using HMAC signatures
- Middleware Protection: Routes can be protected to require successful payments
- Data Encryption: Sensitive payment data can be encrypted
- Rate Limiting: Built-in rate limiting for payment endpoints
- Input Validation: Comprehensive validation for all payment data
- Audit Logging: Complete audit trail of all payment operations
📊 Monitoring & Analytics
// Get payment statistics $stats = [ 'total_payments' => Payment::count(), 'successful_payments' => Payment::successful()->count(), 'failed_payments' => Payment::failed()->count(), 'total_amount' => Payment::successful()->sum('amount'), 'average_amount' => Payment::successful()->avg('amount'), ]; // Get provider performance $providerStats = Payment::selectRaw('provider, COUNT(*) as count, SUM(amount) as total') ->groupBy('provider') ->get();
🚀 Performance Optimization
- Caching: Response caching for better performance
- Database Indexing: Optimized database queries
- Lazy Loading: Efficient data loading
- Connection Pooling: Reuse HTTP connections
- Async Processing: Background job processing for webhooks
🔄 Migration Guide
If you're upgrading from an older version:
- Backup your data before upgrading
- Run the installation command:
php artisan paygate:install - Update your environment variables with new configuration options
- Test your payment flows to ensure everything works correctly
- Update your code to use new features (optional)
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📄 License
This package is open-sourced software licensed under the MIT license.
🆘 Support
- Documentation: Full Documentation
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Request Fields
The table below shows and explains the field features.
Note the Mandatory(M), Optional(O), and Not Applicable(N/A) used in the table.
| Field Name | Type | Paystack | GTPay | Flutterwave | Monnify | Description |
|---|---|---|---|---|---|---|
provider |
string | M | M | M | M | This is the payment gateway name. For now can only be paystack, gtpay, flutterwave and monnify. |
provider_token |
string | M | M | M | M | This is the payment gateway access_token or API Secret Key. For Monnify only, your API Key and Secret Key should be in ApiKey:SecretKey format as provider_token |
amount |
float | M | M | M | M | This is the amount to be charged for the transaction or the amount you are debiting customer. |
email |
string | M | M | M | M | Customer's email address |
reference |
string | M | M | M | M | Your unique generated reference |
redirect_url |
url | O | O | O | O | Fully qualified url, e.g. https://example.com/ . Use this to override the callback url provided on the dashboard for this transaction |
name |
string | O | O | O | M | Customer's name |
contract_code |
string | N/A | N/A | N/A | M | Customer's email address |
payment_methods |
array | O | O | O | O | An array of payment methods or channels to control what channels you want to make available to the user to make a payment with. E.g available channels for paystack include: ["card", "bank", "ussd", "qr", "mobile_money", "bank_transfer", "left"]. Check other payment gateways documentation for their available payment methods or channels. |
pass_charge |
boolean | N/A | O | N/A | N/A | This is only applicable to gtpay. It takes two possible values: True or False. It is set to False by default. When set to True, the charges on the transaction is computed and passed on to the customer(payer). But when set to False, the charge is passed to the merchant and will be deducted from the amount to be settled to the merchant. |
logo |
url | N/A | N/A | O | N/A | This is only applicable to flutterwave. The Merchant's logo URL. |
title |
url | N/A | N/A | O | N/A | This is only applicable to flutterwave. The name to display on the checkout page. |
phone_number |
string | N/A | N/A | O | N/A | This is only applicable to flutterwave. This is the phone number linked to the customer's bank account or mobile money account |
Response Sample
If successful, you will receive a response that looks like the below sample response
{
"errors": false
"message": "Payment initiated successfully with paystack"
"data": {
"checkout_url": "https://checkout.paystack.com/gfe327lipw13uit"
"reference": "T2CZ143DUMG"
"access_code": "gfe327lipw13uit"
"provider": "paystack"
}
}
Response Fields
Note that the table below shows and explains the most important fields to complete the payment or transaction.
| Field Name | Type | Description |
|---|---|---|
errors |
boolean | Can only be true or false. The request was successful if true and false if the request was not successful |
message |
string | Short description of the request |
data |
object | This contains all the parameters you need to complete the payment or transaction |
data->checkout_url |
url | This is the checkout url from the payment gateway for the customer to complete the payment. You can redirect this url to give customer interface. |
data->reference |
string | Merchant's Unique reference for the transaction. The unique generated reference you send via request to payment gateway. |
data->access_code |
string | Unique reference generated for the transaction by payment gateway. |
provider |
string | This is the payment gateway name. For now can only be paystack, gtpay, flutterwave and monnify. |
Verify Payment/Transaction
You can load Paygate instance using use \Obrainwave\Paygate\Facades\Paygate or just use the facade use Paygate.
Request Sample
use Paygate; $payload = array( 'provider' => 'gtpay', 'provider_token' => 'GTPAY_SECRET_KEY', // Make sure you don't expose this in your code 'reference' => 'T2CZ143DUMG', ); $payment = Paygate::verifyPayment($payload);
Request Fields
The table below shows and explains the field features.
Note the Mandatory(M), Optional(O), and Not Applicable(N/A) used in the table.
| Field Name | Type | Paystack | GTPay | Flutterwave | Monnify | Description |
|---|---|---|---|---|---|---|
provider |
string | M | M | M | M | This is the payment gateway name. For now can only be paystack, gtpay, flutterwave and monnify. |
provider_token |
string | M | M | M | M | This is the payment gateway access_token or API Secret Key. For Monnify only, you your API Key and Secret Key should be in ApiKey:SecretKey format as provider_token |
reference |
string | M | M | M | M | Your unique generated reference sent to payment gateway. It should also be returned via payment initiation response |
Response Sample
If successful, you will receive a response that looks like the below sample response
{
"errors": false
"message": "Payment fetched successfully with gtpay"
"provider": "gtpay"
"status": "successful"
"amount": 230
"charged_amount": 232.3
"reference": "9412041935"
"provider_reference": "8359610303031725065306645",
"payment_method": "card"
"data": {
...
}
}
Response Fields
Note that the table below shows and explains the most important fields that decide the payment or transaction status.
| Field Name | Type | Description |
|---|---|---|
errors |
boolean | Can only be true or false. The request was successful if true and false if the request was not successful |
message |
string | Short description of the request |
provider |
string | This is the payment gateway name. For now can only be paystack, gtpay, flutterwave and monnify. |
status |
string | Can only be successful or failed. The payment was completed and successful if successful and failed if the payment was not successful or not completed. If you want to dig more about the status, check for payment gateway transaction status in data field. status for paystack, transaction_status for gtpay, status for flutterwave, and paymentStatus for monnify. |
amount |
float | Amount initiated to be paid by the customer from your transaction |
charged_amount |
float | Total amount charged by payment gateway and paid by the customer. This can be equal to the amount if you don't pass the charge to your customer. |
reference |
string | Your unique generated reference sent to the payment gateway. It should also be returned via payment verification response |
provider_reference |
string | Unique reference generated for the transaction by the payment gateway. It should also be returned via payment verification response |
data |
object | This contains all the parameters you need to play with the payment or transaction verification |
Conclusion
Presently only local payment gateways work. International payment gateways(such as Stripe, Paypal, etc) will be added.
For this package only initiates and verifies transactions at minimum version. More features will be added in subsequent versions. Please watchout!!!
Changelog
Please see CHANGELOG for more information on what has changed recently.
🔧 Advanced Features
Caching System
The package includes a comprehensive caching system for improved performance:
// Enable caching in config 'enable_caching' => true, 'cache_ttl' => 300, // 5 minutes // Cache is automatically handled, but you can manage it manually $cacheService = app(CacheService::class); // Clear payment cache $cacheService->clearPaymentCache('payment_initiate_paystack_' . $reference); // Clear all caches $cacheService->clearAllPaymentCaches();
Security Suite
Advanced security features to protect payment data:
// Data encryption (automatic) 'encrypt_sensitive_data' => true, // Log masking (automatic) 'mask_sensitive_logs' => true, // Fraud detection $fraudService = app(FraudDetectionService::class); $riskAnalysis = $fraudService->analyzePayment($paymentData); if ($riskAnalysis['recommendation'] === 'decline') { // Handle high-risk payment }
Plugin System
Extensible architecture for custom features:
// Create a custom plugin class CustomNotificationPlugin implements PluginInterface { public function getName(): string { return 'CustomNotificationPlugin'; } public function registerHooks(): array { return ['payment.completed']; } public function handleHook(string $hook, array $data = []): mixed { // Custom logic here return null; } } // Register the plugin $pluginManager = app(PluginManager::class); $pluginManager->registerPlugin(new CustomNotificationPlugin());
Performance Optimization
Built-in performance features:
// Rate limiting 'rate_limit' => 60, // requests per minute // Retry logic 'retry_attempts' => 3, 'retry_delay' => 1000, // milliseconds // Response caching 'cache_ttl' => 300, // 5 minutes
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.