A comprehensive Flutterwave payment gateway package for Laravel 9.x, 10.x, 11.x, 12.x and above.
- 💳 Payment Processing - Initialize and verify payments
- 🔄 Transfers - Create and manage transfers
- 🔁 Subscriptions - Handle recurring payments
- 🔔 Webhooks - Secure webhook handling with signature verification
- ✅ Account Verification - Verify bank accounts, BVN, and cards
- 🏦 Virtual Accounts - Create and manage virtual accounts
- 📊 Transaction Tracking - Optional database models and migrations
- 🛡️ Security - Built-in webhook signature verification
- 🔐 API Version Support - Supports both Flutterwave API v3 and v4 (with OAuth 2.0)
- 🌐 Live & Test Environments - Seamless switching between live and test modes
composer require abramcatalyst/laravel-flutterwaveIf you want to install directly from GitHub before publishing to Packagist:
- Add the repository to your
composer.json:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/abramcatalyst/laravel-flutterwave"
}
],
"require": {
"abramcatalyst/laravel-flutterwave": "dev-main"
}
}- Then install:
composer require abramcatalyst/laravel-flutterwave:dev-mainFor local testing, you can use a path repository:
- Add to your Laravel app's
composer.json:
{
"repositories": [
{
"type": "path",
"url": "../laravel-flutterwave"
}
],
"require": {
"abramcatalyst/laravel-flutterwave": "*"
}
}- Install:
composer require abramcatalyst/laravel-flutterwave:@devPublish the configuration file:
php artisan vendor:publish --tag=flutterwave-configAdd your Flutterwave credentials to your .env file:
FLUTTERWAVE_PUBLIC_KEY=your_public_key
FLUTTERWAVE_SECRET_KEY=your_secret_key
FLUTTERWAVE_ENCRYPTION_KEY=your_encryption_key
FLUTTERWAVE_WEBHOOK_SECRET_HASH=your_webhook_secret_hash
FLUTTERWAVE_ENVIRONMENT=live
FLUTTERWAVE_API_VERSION=v3
FLUTTERWAVE_DEFAULT_CURRENCY=NGN
FLUTTERWAVE_DEFAULT_COUNTRY=NGThis package supports both Flutterwave API v3 and v4:
- v3 (Default): Uses Bearer token authentication with your secret key
- v4: Uses OAuth 2.0 authentication (automatically handles token retrieval and refresh)
Set FLUTTERWAVE_API_VERSION=v3 or FLUTTERWAVE_API_VERSION=v4 in your .env file.
Note: Some endpoints (like /banks) are only available in v3. The package defaults to v3 for maximum compatibility.
The core package is API-only and works without a database. If you want to store transaction records in your database, you'll need to:
-
Install the database package (if not already installed):
composer require illuminate/database
-
Publish the migrations and models:
# Publish migrations php artisan vendor:publish --tag=flutterwave-migrations # Publish models php artisan vendor:publish --tag=flutterwave-models # Run migrations php artisan migrate
Note: The database package is optional. The core API functionality works perfectly without it.
use AbramCatalyst\Flutterwave\Facades\Flutterwave;
$payment = Flutterwave::payment()->initialize([
'tx_ref' => 'unique-transaction-reference',
'amount' => 1000,
'currency' => 'NGN',
'payment_options' => 'card,banktransfer,ussd',
'redirect_url' => 'https://your-site.com/callback',
'customer' => [
'email' => 'customer@example.com',
'name' => 'John Doe',
'phone_number' => '08012345678',
],
'customizations' => [
'title' => 'My Store',
'description' => 'Payment for items',
],
]);
// Redirect to payment link
return redirect($payment['data']['link']);$transactionId = 1234567890;
$verification = Flutterwave::payment()->verify($transactionId);
if ($verification['status'] === 'success') {
// Payment successful
}// By transaction ID
$transaction = Flutterwave::payment()->getTransaction($transactionId);
// By transaction reference
$transaction = Flutterwave::payment()->getTransactionByReference('unique-ref');$transactions = Flutterwave::payment()->listTransactions([
'from' => '2024-01-01',
'to' => '2024-12-31',
'page' => 1,
]);$refund = Flutterwave::payment()->refund([
'id' => $transactionId,
'amount' => 500, // Optional: partial refund
]);$transfer = Flutterwave::transfer()->create([
'account_bank' => '044',
'account_number' => '0690000032',
'amount' => 500,
'narration' => 'Payment for services',
'currency' => 'NGN',
'reference' => 'unique-transfer-reference',
'beneficiary_name' => 'John Doe',
]);$bulkTransfer = Flutterwave::transfer()->createBulk([
'title' => 'Bulk Transfer',
'bulk_data' => [
[
'bank_code' => '044',
'account_number' => '0690000032',
'amount' => 500,
'currency' => 'NGN',
'narration' => 'Payment 1',
'reference' => 'ref-1',
],
[
'bank_code' => '058',
'account_number' => '1234567890',
'amount' => 1000,
'currency' => 'NGN',
'narration' => 'Payment 2',
'reference' => 'ref-2',
],
],
]);$transfer = Flutterwave::transfer()->get($transferId);$subscription = Flutterwave::subscription()->create([
'card_token' => 'card_token_here',
'customer_email' => 'customer@example.com',
'amount' => 1000,
'currency' => 'NGN',
'plan' => 'plan_id_here',
]);$subscriptions = Flutterwave::subscription()->list([
'email' => 'customer@example.com',
]);$result = Flutterwave::subscription()->cancel($subscriptionId);$result = Flutterwave::subscription()->activate($subscriptionId);$verification = Flutterwave::verification()->verifyBankAccount([
'account_number' => '0690000032',
'account_bank' => '044',
]);$verification = Flutterwave::verification()->verifyBVN([
'bvn' => '12345678901',
]);$verification = Flutterwave::verification()->verifyCardBin('539983');// All banks
$banks = Flutterwave::verification()->getBanks();
// Banks by country
$banks = Flutterwave::verification()->getBanks('NG');$virtualAccount = Flutterwave::virtualAccount()->create([
'email' => 'customer@example.com',
'firstname' => 'John',
'lastname' => 'Doe',
'phonenumber' => '08012345678',
'narration' => 'John Doe',
'tx_ref' => 'unique-reference',
]);$accounts = Flutterwave::virtualAccount()->list();$account = Flutterwave::virtualAccount()->get($accountId);The package includes a webhook route that automatically verifies the signature. The route is available at /flutterwave/webhook.
You can customize the webhook handler in routes/web.php:
Route::post('/flutterwave/webhook', function () {
$webhook = Flutterwave::webhook();
$payload = $webhook->processPayment(request());
if ($payload) {
// Handle successful payment
$transactionId = $payload['data']['id'];
$amount = $payload['data']['amount'];
$customerEmail = $payload['data']['customer']['email'];
// Update your database, send notifications, etc.
return response()->json(['status' => 'success'], 200);
}
return response()->json(['status' => 'failed'], 400);
})->middleware(\AbramCatalyst\Flutterwave\Middleware\VerifyFlutterwaveWebhook::class);use Illuminate\Http\Request;
$webhook = Flutterwave::webhook();
// Verify signature
if ($webhook->verifySignature(request())) {
// Process webhook
$payload = $webhook->handle(request());
}If you've published the migrations, you can use the FlutterwaveTransaction model directly from the package:
use AbramCatalyst\Flutterwave\Models\FlutterwaveTransaction;
// Create a transaction record
FlutterwaveTransaction::create([
'transaction_id' => '1234567890',
'transaction_ref' => 'unique-ref',
'amount' => 1000,
'currency' => 'NGN',
'status' => 'successful',
'customer_email' => 'customer@example.com',
'customer_name' => 'John Doe',
]);
// Query transactions
$successful = FlutterwaveTransaction::successful()->get();
$failed = FlutterwaveTransaction::failed()->get();
$pending = FlutterwaveTransaction::pending()->get();
// Check status
$transaction = FlutterwaveTransaction::find(1);
if ($transaction->isSuccessful()) {
// Handle successful transaction
}You can also access services directly without using the facade:
use AbramCatalyst\Flutterwave\FlutterwaveService;
$flutterwave = app('flutterwave');
$payment = $flutterwave->payment()->initialize([...]);The package throws FlutterwaveException for API errors:
use AbramCatalyst\Flutterwave\Exceptions\FlutterwaveException;
try {
$payment = Flutterwave::payment()->initialize([...]);
} catch (FlutterwaveException $e) {
// Handle error
logger()->error('Flutterwave error: ' . $e->getMessage());
}Enable request/response logging by setting in your .env:
FLUTTERWAVE_LOG_REQUESTS=trueThis will log all API requests and responses to your Laravel log file.
For testing, set the environment to test in your .env:
FLUTTERWAVE_ENVIRONMENT=testUse Flutterwave test credentials from your dashboard.
After installation, verify your setup with the health check command:
php artisan flutterwave:health-checkFor detailed information:
php artisan flutterwave:health-check --verboseThis command checks:
- PHP version compatibility
- Required extensions (curl, json, openssl)
- Configuration (keys, environment, API version)
- API connectivity
- Always use HTTPS in production environments
- Never expose secret keys in client-side code or public repositories
- Store credentials in environment variables - never hardcode them
- Use different keys for test and production environments
- Enable IP whitelisting for production webhooks:
FLUTTERWAVE_WEBHOOK_ALLOWED_IPS=52.31.139.75,52.49.173.169,52.214.14.220
- Enable rate limiting to prevent abuse:
FLUTTERWAVE_WEBHOOK_RATE_LIMIT=60
- Enable timestamp validation to prevent replay attacks:
FLUTTERWAVE_WEBHOOK_VALIDATE_TIMESTAMP=true
- Set request size limits:
FLUTTERWAVE_WEBHOOK_MAX_SIZE=1048576
- Validate base URLs - The package automatically validates base URLs to prevent SSRF attacks
- Use webhook middleware - Always use
VerifyFlutterwaveWebhookmiddleware on webhook routes - Disable request logging in production:
FLUTTERWAVE_LOG_REQUESTS=false
- Transaction IDs are automatically validated to prevent injection attacks
- Endpoints are sanitized to prevent path traversal and SSRF attacks
- All user inputs should be validated before passing to the package
- Don't expose detailed error messages to end users
- Log errors securely without exposing sensitive data
- Use try-catch blocks around Flutterwave API calls
For Flutterwave API documentation, visit: https://developer.flutterwave.com/docs
After installing the package locally (using path repository), test the basic setup:
// In your Laravel app, test in tinker or a controller
use AbramCatalyst\Flutterwave\Facades\Flutterwave;
// Test service provider registration
$service = app('flutterwave');
dd($service->getConfig()); // Should show your config
// Test facade
$banks = Flutterwave::verification()->getBanks('NG');
dd($banks); // Should return banks listThe package automatically caches service instances (PaymentService, TransferService, etc.) for better performance. No configuration needed.
OAuth tokens for API v4 are automatically cached and reused until expiration. The package handles token refresh automatically.
The HTTP client is optimized for connection reuse:
- DNS caching (1 hour)
- TCP keepalive enabled
- Connection pooling for better performance
Adjust timeouts based on your needs:
FLUTTERWAVE_TIMEOUT=30For better performance, disable request logging in production:
FLUTTERWAVE_LOG_REQUESTS=false- v3: Faster (no OAuth overhead), but some newer features may not be available
- v4: More features, but requires OAuth token (automatically handled)
Run the health check command to diagnose issues:
php artisan flutterwave:health-check --verboseEnsure required extensions are installed:
# Check if extensions are loaded
php -m | grep -E "curl|json|openssl"Install missing extensions:
# Ubuntu/Debian
sudo apt-get install php-curl php-json php-openssl
# macOS (Homebrew)
brew install php-curlClear configuration cache:
php artisan config:clear
php artisan config:cache- Check your
.envfile has the required keys:FLUTTERWAVE_PUBLIC_KEY=your_public_key FLUTTERWAVE_SECRET_KEY=your_secret_key
- Clear config cache:
php artisan config:clear
- Verify your public and secret keys are correct
- Check that your keys are for the correct environment (test/live)
- The package automatically retries with exponential backoff
- Increase timeout in config:
FLUTTERWAVE_TIMEOUT=60
- Check your network connection and firewall settings
- Verify Flutterwave API is accessible from your server
If webhook routes are not loading:
- Check your config:
php artisan config:clear
- Verify routes are enabled:
FLUTTERWAVE_ENABLE_ROUTES=true
- Check route list:
php artisan route:list | grep flutterwave
- Verify webhook secret hash is configured:
FLUTTERWAVE_WEBHOOK_SECRET_HASH=your_webhook_secret
- Check that the secret hash matches your Flutterwave dashboard
- Ensure the middleware is applied to the webhook route
If webhooks are being rate limited:
- Check current rate limit setting:
FLUTTERWAVE_WEBHOOK_RATE_LIMIT=60
- Increase if needed (be careful in production)
- Check Laravel logs for rate limit messages
- Verify Flutterwave IP addresses are whitelisted:
FLUTTERWAVE_WEBHOOK_ALLOWED_IPS=52.31.139.75,52.49.173.169
- Check your server's actual IP (may be behind a proxy)
- Temporarily disable IP whitelist for testing:
FLUTTERWAVE_WEBHOOK_ALLOWED_IPS=
- Clear autoload cache:
composer dump-autoload
- Clear Laravel caches:
php artisan config:clear php artisan cache:clear
- Verify package is installed:
composer show abramcatalyst/laravel-flutterwave
- Ensure service provider is registered (auto-discovered in Laravel 5.5+)
- Check alias is registered in
config/app.php(if using older Laravel) - Clear config cache:
php artisan config:clear
- Endpoint contains invalid characters or path traversal attempts
- Ensure endpoints are properly formatted
- Check that transaction IDs are valid (alphanumeric, hyphens, underscores only)
- Custom base URLs must use HTTPS
- Only Flutterwave domains are allowed
- Transaction IDs are limited to 100 characters
- Verify your transaction IDs meet this requirement
MIT License - see LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
GitHub: https://github.com/abramcatalyst/laravel-flutterwave