# 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

- ✅ 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

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:

- Success: 4242424242424242
- Declined: 4000000000000002
- Insufficient Funds: 4000000000009995
- Expired Card: 4000000000000069

# 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

- `PaymentException` - General payment processing errors
- `GatewayNotFoundException` - Gateway not configured
- `ValidationException` - Invalid payment data
- `RefundException` - 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

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

- 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`

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