Laravel Payments Documentation

A comprehensive Laravel package for integrating multiple payment gateways into your application with a unified, easy-to-use interface.

Current Version: 1.0.0 | Laravel Version: 9.x+ | PHP Version: 8.1+

Features

Installation

Requirements

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
Security Note: Never commit your API keys to version control. Always use environment variables for sensitive credentials.

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

Global
Cards
Digital Wallets

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

  1. Go to Stripe Dashboard → Developers → Webhooks
  2. Add endpoint: https://your-domain.com/payment/webhook/stripe
  3. Select events: payment_intent.succeeded, payment_intent.payment_failed
  4. Copy the webhook secret to your .env file

Testing

Use Stripe's test cards for testing:

PayPal Gateway

Global
PayPal Account
Cards
Venmo

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

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

  1. Create a webhook endpoint in your gateway's dashboard
  2. Point it to your application (e.g., https://your-domain.com/payment/webhook/{gateway})
  3. Configure which events to send
  4. Copy the webhook secret to your .env file

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

Note: Always verify webhook signatures to ensure requests are coming from the payment gateway.