Laravel Payments Documentation
A comprehensive Laravel package for integrating multiple payment gateways into your application with a unified, easy-to-use interface.
Features
- ✅ 20+ Payment Gateways - Support for major global and regional payment providers
- ✅ Unified API - Single interface for all payment gateways
- ✅ Secure - Built with security best practices
- ✅ Webhook Support - Built-in webhook handling for all gateways
- ✅ Test Friendly - Comprehensive testing support
- ✅ Type Safe - Full PHP 8.1 type declarations
- ✅ Extensible - Easy to add new payment gateways
Installation
Requirements
- Laravel 9.0 or higher
- PHP 8.1 or higher
- Composer package manager
Step 1: Install the Package
composer require mdiqbal/laravel-payments
Step 2: Publish Configuration
php artisan vendor:publish --provider="Mdiqbal\LaravelPayments\PaymentsServiceProvider"
Step 3: Add Environment Variables
Add your payment gateway credentials to your .env file. Here's an example for Stripe:
# Stripe Configuration
STRIPE_MODE=test # or 'live' for production
STRIPE_SANDBOX_SECRET=sk_test_xxxxxxxxxxxxxx
STRIPE_SANDBOX_KEY=pk_test_xxxxxxxxxxxxxx
STRIPE_LIVE_SECRET=sk_live_xxxxxxxxxxxxxx
STRIPE_LIVE_KEY=pk_live_xxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxx
Configuration
The package configuration is located at config/payments.php. This file contains configuration for all supported payment gateways.
Default Gateway
You can set a default gateway that will be used when no specific gateway is specified:
'default' => env('PAYMENT_DEFAULT_GATEWAY', 'stripe'),
Gateway Configuration
Each gateway has its own configuration section. Here's an example configuration structure:
return [
'default' => env('PAYMENT_DEFAULT_GATEWAY', 'stripe'),
'gateways' => [
'stripe' => [
'mode' => env('STRIPE_MODE', 'sandbox'),
'sandbox' => [
'secret_key' => env('STRIPE_SANDBOX_SECRET'),
'api_key' => env('STRIPE_SANDBOX_KEY'),
],
'live' => [
'secret_key' => env('STRIPE_LIVE_SECRET'),
'api_key' => env('STRIPE_LIVE_KEY'),
],
'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'),
],
'paypal' => [
'mode' => env('PAYPAL_MODE', 'sandbox'),
'sandbox' => [
'client_id' => env('PAYPAL_SANDBOX_CLIENT_ID'),
'client_secret' => env('PAYPAL_SANDBOX_CLIENT_SECRET'),
],
'live' => [
'client_id' => env('PAYPAL_LIVE_CLIENT_ID'),
'client_secret' => env('PAYPAL_LIVE_CLIENT_SECRET'),
],
'webhook_id' => env('PAYPAL_WEBHOOK_ID'),
],
],
];
Basic Usage
Making a Payment
use Mdiqbal\LaravelPayments\Facades\Payment;
use Mdiqbal\LaravelPayments\DTO\PaymentRequest;
// Create a payment request
$paymentRequest = new PaymentRequest(
amount: 100.00,
currency: 'USD',
description: 'Payment for Order #12345'
);
// Set optional parameters
$paymentRequest->setTransactionId('order_' . uniqid());
$paymentRequest->setReturnUrl(route('payment.success'));
$paymentRequest->setCancelUrl(route('payment.cancel'));
// Process payment with default gateway
$response = Payment::pay($paymentRequest);
// Or process with a specific gateway
$response = Payment::gateway('stripe')->pay($paymentRequest);
// Handle response
if ($response->isSuccess()) {
if ($response->isRedirect()) {
return redirect($response->getRedirectUrl());
}
$transactionId = $response->getTransactionId();
// Save transaction ID to database
} else {
$errorMessage = $response->getMessage();
// Handle error
}
Retrieving Payment Status
use Mdiqbal\LaravelPayments\Facades\Payment;
$transactionId = 'txn_1234567890';
$response = Payment::gateway('stripe')->retrievePayment($transactionId);
if ($response->isSuccess()) {
$status = $response->getData()['status'];
$amount = $response->getData()['amount'];
// Update order status
}
Processing Refunds
use Mdiqbal\LaravelPayments\Facades\Payment;
$transactionId = 'txn_1234567890';
$refundAmount = 50.00;
$reason = 'Customer requested refund';
try {
$success = Payment::gateway('stripe')->refund($transactionId, $refundAmount, $reason);
if ($success) {
// Refund processed successfully
}
} catch (\Exception $e) {
// Handle refund failure
}
Supported Payment Gateways
Stripe
Global payment gateway with support for cards, digital wallets, and local payment methods.
Regions: Global
View Docs →PayPal
Trusted payment solution with PayPal accounts, cards, and alternative payment methods.
Regions: Global
View Docs →Paystack
Leading African payment gateway supporting cards, bank transfers, and mobile money.
Regions: Africa
View Docs →Razorpay
India's payment solution supporting UPI, cards, net banking, and popular wallets.
Regions: India
View Docs →Flutterwave
African payment platform with support for cards, mobile money, and bank transfers.
Regions: Africa
View Docs →Mollie
European payment gateway supporting iDEAL, credit cards, and local methods.
Regions: Europe
View Docs →Stripe Gateway
Stripe is a comprehensive payment platform that supports credit/debit cards, digital wallets (Apple Pay, Google Pay), and various local payment methods.
Configuration
Add these variables to your .env file:
STRIPE_MODE=test # or 'live' for production
STRIPE_SANDBOX_SECRET=sk_test_xxxxxxxxxxxxxx
STRIPE_SANDBOX_KEY=pk_test_xxxxxxxxxxxxxx
STRIPE_LIVE_SECRET=sk_live_xxxxxxxxxxxxxx
STRIPE_LIVE_KEY=pk_live_xxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxx
Quick Start
use Mdiqbal\LaravelPayments\Facades\Payment;
use Mdiqbal\LaravelPayments\DTO\PaymentRequest;
$paymentRequest = new PaymentRequest(
amount: 100.00,
currency: 'USD',
description: 'Payment for Order #12345'
);
$response = Payment::gateway('stripe')->pay($paymentRequest);
if ($response->isSuccess()) {
$clientSecret = $response->getData()['client_secret'];
// Pass clientSecret to frontend for Stripe Elements
}
Frontend Integration
Include Stripe.js and create a payment form:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('{{ config("payments.gateways.stripe." . config("payments.gateways.stripe.mode") . ".api_key") }}');
const clientSecret = '{{ $clientSecret }}';
const elements = stripe.elements({
clientSecret: clientSecret,
appearance: { theme: 'stripe' }
});
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
// Handle form submission
const form = document.getElementById('payment-form');
form.addEventListener('submit', async (event) => {
event.preventDefault();
const {error} = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: '{{ route("payment.success") }}',
},
});
});
</script>
Webhook Setup
- Go to Stripe Dashboard → Developers → Webhooks
- Add endpoint:
https://your-domain.com/payment/webhook/stripe - Select events:
payment_intent.succeeded,payment_intent.payment_failed - Copy the webhook secret to your
.envfile
Testing
Use Stripe's test cards for testing:
- Success: 4242424242424242
- Declined: 4000000000000002
- Insufficient Funds: 4000000000009995
- Expired Card: 4000000000000069
PayPal Gateway
PayPal provides a trusted payment solution with support for PayPal accounts, credit/debit cards, and alternative payment methods like Venmo.
Configuration
PAYPAL_MODE=sandbox # or 'live' for production
PAYPAL_SANDBOX_CLIENT_ID=your_sandbox_client_id
PAYPAL_SANDBOX_CLIENT_SECRET=your_sandbox_client_secret
PAYPAL_LIVE_CLIENT_ID=your_live_client_id
PAYPAL_LIVE_CLIENT_SECRET=your_live_client_secret
PAYPAL_WEBHOOK_ID=your_webhook_id
Quick Start
$paymentRequest = new PaymentRequest(
amount: 100.00,
currency: 'USD',
description: 'Payment for Order #12345'
);
$response = Payment::gateway('paypal')->pay($paymentRequest);
if ($response->isRedirect()) {
return redirect($response->getRedirectUrl());
}
Testing
Running Tests
# Run all tests
composer test
# Run specific test
composer test -- --filter StripePaymentTest
# Generate coverage report
composer test -- --coverage
Test Configuration
Create a .env.testing file for test environment:
PAYMENT_DEFAULT_GATEWAY=stripe
STRIPE_MODE=test
STRIPE_SANDBOX_SECRET=sk_test_xxxxxxxxxxxxxx
STRIPE_SANDBOX_KEY=pk_test_xxxxxxxxxxxxxx
Test Example
public function test_stripe_payment()
{
$paymentRequest = new PaymentRequest(
amount: 10.00,
currency: 'USD',
description: 'Test Payment'
);
$paymentRequest->setTransactionId('test_order_123');
$response = Payment::gateway('stripe')->pay($paymentRequest);
$this->assertTrue($response->isSuccess());
$this->assertNotNull($response->getData()['client_secret']);
}
Error Handling
The package provides comprehensive error handling for various scenarios:
Exception Types
PaymentException- General payment processing errorsGatewayNotFoundException- Gateway not configuredValidationException- Invalid payment dataRefundException- Refund processing errors
Handling Errors
use Mdiqbal\LaravelPayments\Exceptions\PaymentException;
try {
$response = Payment::gateway('stripe')->pay($paymentRequest);
} catch (PaymentException $e) {
Log::error('Payment failed: ' . $e->getMessage());
return response()->json([
'error' => 'Payment processing failed',
'message' => $e->getMessage()
], 400);
} catch (\Exception $e) {
Log::error('Unexpected error: ' . $e->getMessage());
return response()->json([
'error' => 'An unexpected error occurred'
], 500);
}
Error Responses
When a payment fails, the response object contains:
if (!$response->isSuccess()) {
$errorCode = $response->getErrorCode();
$errorMessage = $response->getMessage();
$gatewayResponse = $response->getData(); // Raw gateway response
// Handle specific error codes
switch ($errorCode) {
case 'insufficient_funds':
return 'Insufficient funds in your account';
case 'expired_card':
return 'Your card has expired';
case 'card_declined':
return 'Your card was declined';
default:
return 'Payment failed. Please try again.';
}
}
Webhook Handling
Webhooks allow payment gateways to notify your application about payment events in real-time.
Setting Up Webhooks
- Create a webhook endpoint in your gateway's dashboard
- Point it to your application (e.g.,
https://your-domain.com/payment/webhook/{gateway}) - Configure which events to send
- Copy the webhook secret to your
.envfile
Creating Webhook Routes
// routes/api.php
Route::post('/payment/webhook/{gateway}', [WebhookController::class, 'handle'])
->middleware(['api', 'throttle:60,1']);
Webhook Controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Mdiqbal\LaravelPayments\Facades\Payment;
class WebhookController extends Controller
{
public function handle(Request $request, string $gateway)
{
$payload = $request->json()->all();
try {
$response = Payment::gateway($gateway)->verify($payload);
if ($response->isSuccess()) {
$eventData = $response->getData();
$eventType = $eventData['event_type'] ?? $eventData['type'] ?? null;
$transactionId = $response->getTransactionId();
// Find related order
$order = Order::where('transaction_id', $transactionId)->first();
if ($order) {
switch ($eventType) {
case 'payment_intent.succeeded':
$order->status = 'paid';
$order->paid_at = now();
$order->save();
break;
case 'payment_intent.payment_failed':
$order->status = 'failed';
$order->save();
break;
}
}
}
return response()->json(['status' => 'success']);
} catch (\Exception $e) {
\Log::error("Webhook error for {$gateway}: " . $e->getMessage());
return response()->json(['error' => $e->getMessage()], 400);
}
}
}
Common Webhook Events
- Payment Successful:
payment_intent.succeeded,payment.completed - Payment Failed:
payment_intent.payment_failed,payment.failed - Refund:
charge.dispute.created,refund.succeeded - Subscription:
invoice.payment_succeeded,customer.subscription.deleted