itul/slick-payment-gateway

A Laravel package for integrating multiple payment gateways with PCI compliant tokenization

0.3.0 2025-08-26 01:35 UTC

This package is auto-updated.

Last update: 2025-08-27 09:35:56 UTC


README

Latest Version on Packagist Total Downloads License

A PCI DSS compliant Laravel package for payment processing with multiple gateway support. Currently in active development - production deployment should only be done after thorough testing.

🚧 Current Development Status

FeatureStatusNotes
Stripe Integrationβœ… CompletePayment processing, refunds, webhook verification
PayPal Integration🟑 PartialBasic payments, webhook verification enhanced
Authorize.Net Integration🟑 PartialBasic payments, webhook verification enhanced
PCI Complianceβœ… CompliantClient-side tokenization only, no raw card data
Multi-Tenant SaaSβœ… CompleteFull tenant isolation and configuration
Webhook Securityβœ… SecuredProper signature verification implemented
Testing Suite🟑 BasicCore tests implemented, expanding coverage

✨ Key Features

  • πŸ”’ PCI DSS Compliant - Client-side tokenization only, zero raw card data processing
  • 🏒 Multi-Tenant SaaS Ready - Built-in tenant isolation and configuration management
  • πŸ’³ Multiple Payment Gateways - Stripe (complete), PayPal & Authorize.Net (basic)
  • πŸ” Enterprise Security - Laravel encryption, audit logging, proper webhook verification
  • 🎯 Developer Friendly - Clean APIs, comprehensive error handling
  • ⚑ Production Focused - Rate limiting, webhook handling, comprehensive logging

πŸš€ Quick Start

1. Installation

composer require itul/slick-payment-gateway

2. Publish & Migrate

# Publish configuration
php artisan vendor:publish --tag=slick-payment-gateway-config

# Run migrations
php artisan migrate

3. Configure Environment

# Choose your default gateway
SLICK_PAYMENT_GATEWAY_DRIVER=stripe

# Stripe Configuration (Complete Support)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

# PayPal Configuration (Basic Support)
PAYPAL_CLIENT_ID=your_client_id
PAYPAL_CLIENT_SECRET=your_client_secret
PAYPAL_MODE=sandbox

# Authorize.Net Configuration (Basic Support)
AUTHORIZE_NET_API_LOGIN_ID=your_login_id
AUTHORIZE_NET_TRANSACTION_KEY=your_transaction_key
AUTHORIZE_NET_SANDBOX=true

4. Your First Payment ⚑

use Itul\SlickPaymentGateway\Facades\SlickPaymentGateway;
use Itul\SlickPaymentGateway\DTOs\PaymentRequest;

// Process a payment (using pre-tokenized payment method)
$payment = SlickPaymentGateway::processPayment(
    PaymentRequest::make([
        'payment_method_id' => '1', // Your stored payment method ID
        'amount' => 29.99,
        'currency' => 'USD',
        'description' => 'Pro Plan Subscription'
    ])
);

if ($payment->success) {
    echo "Transaction ID: " . $payment->transactionId;
    echo "Status: " . $payment->status;
} else {
    echo "Error: " . $payment->message;
}

πŸ’³ Payment Method Management

Client-Side Tokenization (PCI Compliant)

Frontend (Stripe Elements):

// Create payment method on client-side (PCI compliant)
const {paymentMethod} = await stripe.createPaymentMethod({
    type: 'card',
    card: cardElement,
    billing_details: {
        name: 'John Doe',
        email: 'john@example.com'
    }
});

// Send only the token to backend
fetch('/api/payment-methods', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({
        payment_method_token: paymentMethod.id,
        owner_type: 'App\\Models\\User',
        owner_id: user.id
    })
});

Backend Processing:

use Itul\SlickPaymentGateway\Facades\SlickPaymentGateway;

public function storePaymentMethod(Request $request)
{
    $request->validate([
        'payment_method_token' => 'required|string',
        'owner_type' => 'required|string',
        'owner_id' => 'required|integer'
    ]);

    try {
        $paymentMethodId = SlickPaymentGateway::attachPaymentMethod(
            $request->payment_method_token,
            [
                'owner_type' => $request->owner_type,
                'owner_id' => $request->owner_id
            ]
        );

        return response()->json([
            'payment_method_id' => $paymentMethodId,
            'message' => 'Payment method saved successfully'
        ]);
    } catch (\Exception $e) {
        return response()->json([
            'error' => 'Failed to save payment method: ' . $e->getMessage()
        ], 400);
    }
}

πŸ’° Payment Processing

Stripe Payments (Complete Implementation)

use Itul\SlickPaymentGateway\DTOs\PaymentRequest;
use Itul\SlickPaymentGateway\Facades\SlickPaymentGateway;

// Configure for Stripe (most complete implementation)
config(['slick-payment-gateway.default_driver' => 'stripe']);

$result = SlickPaymentGateway::processPayment(
    PaymentRequest::make([
        'payment_method_id' => '1',
        'amount' => 99.99,
        'currency' => 'USD',
        'description' => 'Annual Pro Subscription',
        'metadata' => [
            'customer_id' => auth()->id(),
            'plan' => 'pro-annual'
        ]
    ])
);

if ($result->success) {
    // Payment completed
    $transactionId = $result->transactionId;
    $status = $result->status; // 'completed', 'pending', etc.
    
    // Access Stripe-specific data
    $clientSecret = $result->gatewayResponse['client_secret'];
    
    return redirect()->route('payment.success', $transactionId);
} else {
    return back()->withErrors(['payment' => $result->message]);
}

PayPal & Authorize.Net Payments (Basic Implementation)

// PayPal and Authorize.Net have basic payment processing
// More advanced features are planned for future releases
$payment = SlickPaymentGateway::processPayment(
    PaymentRequest::make([
        'payment_method_id' => '1',
        'amount' => 50.00,
        'currency' => 'USD',
        'description' => 'Service Payment'
    ])
);

πŸ” Refund Processing

use Itul\SlickPaymentGateway\DTOs\RefundRequest;

// Full refund
$refund = SlickPaymentGateway::refundPayment(
    RefundRequest::make([
        'transaction_id' => 'pi_1234567890', // Stripe payment intent ID
        'reason' => 'Customer requested cancellation'
    ])
);

// Partial refund
$partialRefund = SlickPaymentGateway::refundPayment(
    RefundRequest::make([
        'transaction_id' => 'pi_1234567890',
        'amount' => 25.00, // Refund $25 of original $100
        'reason' => 'Partial service credit'
    ])
);

if ($refund->success) {
    echo "Refund ID: " . $refund->transactionId;
} else {
    echo "Refund failed: " . $refund->message;
}

🏒 Multi-Tenant SaaS Usage

Enable SaaS Mode

// config/slick-payment-gateway.php
'sass_mode' => [
    'enabled' => true,
    'tenant_model' => App\Models\Company::class,
    'tenant_key' => 'company_id', // Key in User model
],

Tenant-Specific Gateway Configuration

use Itul\SlickPaymentGateway\Models\SlickTenantConnection;

// Set up gateway for a tenant
$tenant = Company::find(1);
$connection = SlickTenantConnection::create([
    'tenant_type' => Company::class,
    'tenant_id' => $tenant->id,
    'name' => 'Acme Corp Payment Gateway',
    'is_active' => true
]);

// Configure Stripe for this tenant
$connection->setGatewayCredentials('stripe', [
    'secret_key' => 'sk_test_tenant_specific_key',
    'publishable_key' => 'pk_test_tenant_specific_key',
    'webhook_secret' => 'whsec_tenant_specific_secret'
]);

// Configure PayPal for this tenant  
$connection->setGatewayCredentials('paypal', [
    'client_id' => 'tenant_paypal_client_id',
    'client_secret' => 'tenant_paypal_secret',
    'mode' => 'sandbox'
]);

// Configure Authorize.Net for this tenant
$connection->setGatewayCredentials('authorize_net', [
    'api_login_id' => 'tenant_authnet_login_id',
    'transaction_key' => 'tenant_authnet_transaction_key',
    'sandbox' => true
]);

πŸ“‘ Webhook Security

Secure Webhook Endpoints

The package now includes proper webhook signature verification:

// Webhook URLs with enhanced security:
// Stripe: https://yourapp.com/api/slick/payment-webhooks/stripe
// PayPal: https://yourapp.com/api/slick/payment-webhooks/paypal
// Authorize.Net: https://yourapp.com/api/slick/payment-webhooks/authorize-net

Webhook Verification

All webhooks now use cryptographic signature verification:

  • Stripe: Uses Webhook::constructEvent() with shared secret
  • PayPal: Validates required headers and transmission signatures
  • Authorize.Net: Uses SHA-512 HMAC signature verification

πŸ§ͺ Testing

# Run the test suite
php artisan test

# Run specific tests
php artisan test --filter=StripeDriverTest
php artisan test --filter=GatewayConnectionTest

Test Gateway Connections

// Test your gateway configurations
Route::post('/test-gateway/{driver}', function($driver) {
    $controller = new \Itul\SlickPaymentGateway\Http\Controllers\GatewayController();
    
    try {
        $result = $controller->test(request(), $driver);
        return response()->json(['success' => true, 'message' => 'Connection successful']);
    } catch (\Exception $e) {
        return response()->json(['success' => false, 'message' => $e->getMessage()], 400);
    }
});

🚨 Error Handling

The package includes comprehensive error handling:

use Itul\SlickPaymentGateway\Exceptions\{
    PaymentGatewayException,
    InvalidPaymentMethodException,
    InsufficientFundsException,
    GatewayConfigurationException,
    WebhookVerificationException
};

try {
    $payment = SlickPaymentGateway::processPayment($request);
} catch (InvalidPaymentMethodException $e) {
    return response()->json(['error' => 'Please update your payment method'], 400);
} catch (InsufficientFundsException $e) {
    return response()->json(['error' => 'Payment declined - insufficient funds'], 400);
} catch (GatewayConfigurationException $e) {
    Log::error('Gateway configuration error', ['error' => $e->getMessage()]);
    return response()->json(['error' => 'Payment system temporarily unavailable'], 500);
} catch (PaymentGatewayException $e) {
    return response()->json(['error' => 'Payment processing error'], 500);
}

βš™οΈ Configuration

Complete Configuration

// config/slick-payment-gateway.php
return [
    'default_driver' => env('SLICK_PAYMENT_GATEWAY_DRIVER', 'stripe'),
    
    'drivers' => [
        'stripe' => [
            'secret_key' => env('STRIPE_SECRET_KEY'),
            'publishable_key' => env('STRIPE_PUBLISHABLE_KEY'), 
            'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'),
            'currency' => env('STRIPE_CURRENCY', 'usd'),
        ],
        'paypal' => [
            'client_id' => env('PAYPAL_CLIENT_ID'),
            'client_secret' => env('PAYPAL_CLIENT_SECRET'),
            'mode' => env('PAYPAL_MODE', 'sandbox'),
            'currency' => env('PAYPAL_CURRENCY', 'USD'),
        ],
        'authorize_net' => [
            'api_login_id' => env('AUTHORIZE_NET_API_LOGIN_ID'),
            'transaction_key' => env('AUTHORIZE_NET_TRANSACTION_KEY'),
            'sandbox' => env('AUTHORIZE_NET_SANDBOX', true),
            'currency' => env('AUTHORIZE_NET_CURRENCY', 'USD'),
        ],
    ],
    
    'sass_mode' => [
        'enabled' => env('SLICK_SASS_MODE_ENABLED', false),
        'tenant_model' => env('SLICK_TENANT_MODEL', 'App\\Models\\Company'),
        'tenant_key' => env('SLICK_TENANT_KEY', 'company_id'),
    ],
    
    'security' => [
        'require_https' => env('SLICK_REQUIRE_HTTPS', true),
        'audit_logging' => env('SLICK_AUDIT_LOGGING', true),
        'rate_limiting' => [
            'payments' => env('SLICK_RATE_LIMIT_PAYMENTS', 60), // per minute
            'webhooks' => env('SLICK_RATE_LIMIT_WEBHOOKS', 1000), // per minute
        ],
    ],
];

πŸ“ˆ Roadmap

Version 2.1 (Planned)

  • Complete PayPal subscription support
  • Enhanced Authorize.Net features
  • GraphQL API support

Version 2.2 (Planned)

  • Apple Pay & Google Pay integration
  • Advanced fraud detection
  • Expanded webhook event handling

Version 2.3 (Planned)

  • Advanced analytics dashboard
  • Multi-currency conversion
  • Enhanced reporting features

⚠️ Important Notes

Development Status

  • Stripe integration is complete and production-ready
  • PayPal and Authorize.Net have basic functionality - test thoroughly before production
  • Webhook security has been significantly enhanced from initial versions
  • Multi-tenant features are fully implemented and tested

Before Production Deployment

  1. βœ… Test all payment flows with your chosen gateways
  2. βœ… Verify webhook endpoints are working correctly
  3. βœ… Configure proper SSL certificates for webhook security
  4. βœ… Set up monitoring for payment failures and webhook issues
  5. βœ… Test multi-tenant isolation if using SaaS mode

Security Best Practices

  • Never process raw card data - always use client-side tokenization
  • Regularly rotate webhook secrets
  • Monitor PCI audit logs for suspicious activity
  • Use HTTPS everywhere - no exceptions

πŸ“„ License

The MIT License (MIT). Please see License File for more information.

πŸ† Credits

Slick Payment Gateway is created and maintained by iTul.

Core Team

  • Brandon Moore - Lead Developer & Architect

Made by iTul

Secure β€’ Scalable β€’ PCI Compliant β€’ Developer Friendly

⬆ Back to Top

Local demo & tests (Null driver)

The package includes a Null gateway driver and a seeding command so you can demo the full flow without touching real payment APIs.

1) Install dev dependencies

composer install

2) Ensure the Null driver is registered

In config/slick-payment-gateway.php, the services array contains 'null' => Itul\SlickPaymentGateway\Services\NullGatewayService::class.
Optionally set the default driver for quick demos:

// config/slick-payment-gateway.php
return [
    'default_driver' => 'null',
    'services' => [
        'stripe'        => Itul\SlickPaymentGateway\Services\StripeGatewayService::class,
        'paypal'        => Itul\SlickPaymentGateway\Services\PayPalGatewayService::class,
        'authorize_net' => Itul\SlickPaymentGateway\Services\AuthorizeNetGatewayService::class,
        'quickbooks'    => Itul\SlickPaymentGateway\Services\QuickBooksGatewayService::class,
        'null'          => Itul\SlickPaymentGateway\Services\NullGatewayService::class,
    ],
];

3) Migrate package tables

php artisan migrate

4) Seed fake β€œremote” records

Create remote-only records in the Null driver and print their IDs:

php artisan slick:seed-null-gateway --type=payment --count=3

Or create remote records and local rows pointing to them:

php artisan slick:seed-null-gateway --type=order --count=2 --create-local

5) Exercise the model trait

use Itul\SlickPaymentGateway\Models\SlickPayment;

// Create a local model and send to the gateway (create or update)
$pay = new SlickPayment([
    'amount' => 12.34,
    'currency' => 'USD',
    'description' => 'Demo',
]);
$pay->save();
$pay->gatewaySend();   // sets gateway_record_*

// Read from the gateway and sync back into the model
$pay->gatewayRead();

// Delete in the gateway and clear local gateway_record_* fields
$pay->gatewayDelete();

6) Run tests

./vendor/bin/phpunit

Note: Field mappings are centralized and immutable inside FieldMappingRegistry. Developers cannot customize mappings via config or by altering app models.