Skip to content

A flexible payment processing system for Laravel using the Factory Pattern. This package provides a unified interface for multiple payment processors, making it easy to add new payment gateways without changing your application code.

License

Notifications You must be signed in to change notification settings

asciisd/cashier-core

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cashier Core

Latest Version on Packagist Total Downloads License

A flexible payment processing system for Laravel using the Factory Pattern. This package provides a unified interface for multiple payment processors, making it easy to add new payment gateways without changing your application code.

Features

  • Factory Pattern: Easy to extend with new payment processors
  • Unified Interface: Consistent API across all payment processors
  • Laravel Integration: Native Laravel package with service provider
  • Database Models: Built-in models for transactions, payments, and payment methods
  • Configurable: Flexible configuration system
  • Testable: Comprehensive test suite included
  • Multiple Processors: Support for custom processors
  • Refund Support: Full and partial refunds
  • Authorization & Capture: Pre-authorization and later capture
  • Payment Methods: Store and manage customer payment methods
  • Webhooks: Built-in webhook handling support
  • Security: Encrypted sensitive data storage
  • Logging: Comprehensive payment logging

Installation

composer require asciisd/cashier-core

Configuration

Publish the configuration file:

php artisan vendor:publish --provider="Asciisd\CashierCore\CashierCoreServiceProvider" --tag="config"

Publish and run the migrations:

php artisan vendor:publish --provider="Asciisd\CashierCore\CashierCoreServiceProvider" --tag="migrations"
php artisan migrate

Environment Configuration

Add the following environment variables to your .env file:

# Default processor
CASHIER_DEFAULT_PROCESSOR=stripe

# General Settings
CASHIER_CURRENCY=USD
CASHIER_LOGGING_ENABLED=true

Usage

Basic Payment Processing

use Asciisd\CashierCore\Facades\PaymentFactory;

// Create a payment processor
$processor = PaymentFactory::create('stripe');

// Process a payment
$result = $processor->charge([
    'amount' => 20, // $20.00
    'currency' => 'USD',
    'source' => 'tok_visa',
    'description' => 'Order #12345',
    'metadata' => [
        'order_id' => '12345',
        'customer_id' => 'cust_123',
    ],
]);

if ($result->isSuccessful()) {
    echo "Payment successful! Transaction ID: {$result->transactionId}";
} else {
    echo "Payment failed: {$result->message}";
}

Working with Different Processors

// Check available processors
$processors = PaymentFactory::getProcessorNames();
// Returns: []

Refund Processing

$processor = PaymentFactory::create('stripe');

// Full refund
$refundResult = $processor->refund('ch_transaction_id');

// Partial refund
$partialRefundResult = $processor->refund('ch_transaction_id', 500); // $5.00

if ($refundResult->isSuccessful()) {
    echo "Refund successful! Refund ID: {$refundResult->refundId}";
}

Authorization and Capture

$processor = PaymentFactory::create('stripe');

// Authorize payment
$authResult = $processor->authorize([
    'amount' => 3000,
    'currency' => 'USD',
    'source' => 'tok_visa',
    'description' => 'Pre-authorization',
]);

if ($authResult->isSuccessful()) {
    // Later, capture the payment
    $captureResult = $processor->capture($authResult->transactionId, 2500);
}

Database Models

Using the Payable Trait

Add the Payable trait to your models that can make payments:

use Asciisd\CashierCore\Traits\Payable;

class User extends Model
{
    use Payable;
    
    // Your model code...
}

This provides helpful methods:

$user = User::find(1);

// Get all transactions
$transactions = $user->transactions;

// Get successful transactions
$successful = $user->getSuccessfulTransactions();

// Get total amount spent
$totalSpent = $user->getTotalSpent(); // Returns total amount
$formattedTotal = $user->getFormattedTotalSpent(); // Returns formatted string

// Payment methods
$paymentMethods = $user->paymentMethods;
$defaultMethod = $user->getDefaultPaymentMethod();

Working with Transactions

use Asciisd\CashierCore\Models\Transaction;

// Create a transaction record
$transaction = Transaction::create([
    'processor_name' => 'stripe',
    'processor_transaction_id' => $result->transactionId,
    'payable_type' => 'App\\Models\\User',
    'payable_id' => $user->id,
    'amount' => $result->amount,
    'currency' => $result->currency,
    'status' => $result->status,
    'description' => 'Order payment',
    'processed_at' => now(),
]);

// Query transactions
$successfulTransactions = Transaction::successful()->get();
$stripeTransactions = Transaction::byProcessor('stripe')->get();
$recentTransactions = Transaction::where('created_at', '>=', now()->subDays(7))->get();

Managing Payment Methods

use Asciisd\CashierCore\Models\PaymentMethod;

// Create a payment method
$paymentMethod = PaymentMethod::create([
    'user_type' => 'App\\Models\\User',
    'user_id' => $user->id,
    'processor_name' => 'stripe',
    'processor_payment_method_id' => 'pm_1234567890',
    'type' => 'credit_card',
    'brand' => 'visa',
    'last_four' => '4242',
    'exp_month' => 12,
    'exp_year' => 2025,
    'is_default' => true,
]);

// Make it the default
$paymentMethod->makeDefault();

// Check expiration
if ($paymentMethod->is_expired) {
    // Handle expired payment method
}

Adding Custom Payment Processors

  1. Create a class that extends AbstractPaymentProcessor:
use Asciisd\CashierCore\Abstracts\AbstractPaymentProcessor;
use Asciisd\CashierCore\DataObjects\PaymentResult;
use Asciisd\CashierCore\DataObjects\RefundResult;

class CustomProcessor extends AbstractPaymentProcessor
{
    protected array $supportedFeatures = ['charge', 'refund'];

    public function getName(): string
    {
        return 'custom';
    }

    public function charge(array $data): PaymentResult
    {
        $validatedData = $this->validatePaymentData($data);
        
        // Your payment processing logic here
        
        return $this->createSuccessResult(
            transactionId: 'custom_' . uniqid(),
            amount: $validatedData['amount'],
            currency: $validatedData['currency']
        );
    }

    public function refund(string $transactionId, ?int $amount = null): RefundResult
    {
        // Your refund logic here
    }
}
  1. Register it in your configuration:
// config/cashier-core.php
'processors' => [
    'custom' => [
        'class' => \App\PaymentProcessors\CustomProcessor::class,
        'config' => [
            'api_key' => env('CUSTOM_API_KEY'),
            'secret' => env('CUSTOM_SECRET'),
        ],
    ],
],
  1. Use it through the factory:
$processor = PaymentFactory::create('custom');

Error Handling

use Asciisd\CashierCore\Exceptions\InvalidPaymentDataException;
use Asciisd\CashierCore\Exceptions\PaymentProcessingException;
use Asciisd\CashierCore\Exceptions\ProcessorNotFoundException;

try {
    $processor = PaymentFactory::create('stripe');
    $result = $processor->charge($paymentData);
    
} catch (InvalidPaymentDataException $e) {
    // Handle validation errors
    echo "Invalid payment data: {$e->getMessage()}";
    
} catch (PaymentProcessingException $e) {
    // Handle payment processing errors
    echo "Payment failed: {$e->getMessage()}";
    if ($e->getTransactionId()) {
        echo "Transaction ID: {$e->getTransactionId()}";
    }
    
} catch (ProcessorNotFoundException $e) {
    // Handle unknown processor
    echo "Processor not found: {$e->getMessage()}";
}

Testing

Run the test suite:

composer test

Run tests with coverage:

composer test-coverage

Configuration Options

The package provides extensive configuration options. See the published config file for all available settings:

  • Processors: Configure multiple payment processors
  • Currency: Set default and supported currencies
  • Database: Customize table names and connections
  • Webhooks: Configure webhook handling
  • Logging: Control payment logging
  • Security: Configure data encryption and masking
  • Retry Logic: Set up retry mechanisms for failed payments
  • Feature Flags: Enable/disable specific features

Supported Payment Processors

Built-in Processors

Extensible Architecture

The Factory Pattern makes it easy to add new processors:

  1. Implement the PaymentProcessorInterface
  2. Extend AbstractPaymentProcessor for common functionality
  3. Register in configuration
  4. Use immediately through the factory

Security Features

  • Data Encryption: Sensitive data is encrypted before storage
  • Card Masking: Credit card numbers are automatically masked
  • Webhook Verification: Secure webhook signature verification
  • Input Validation: Comprehensive input validation for all processors

Examples

Check the examples/BasicUsage.php file for comprehensive usage examples covering:

  • Basic payment processing
  • Refund handling
  • Authorization and capture
  • Payment method management
  • Error handling
  • Custom processor registration
  • Database queries

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

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

Support

For support, please open an issue on GitHub or contact us at [email protected].

About

A flexible payment processing system for Laravel using the Factory Pattern. This package provides a unified interface for multiple payment processors, making it easy to add new payment gateways without changing your application code.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages