walletable/walletable

A package to create e-wallets for managing and analysing digital financial assets per entity in your laravel application.

v0.0.5 2024-12-29 20:48 UTC

README

Github Total Downloads Latest Stable Version License

Walletable

A robust Laravel package for creating e-wallets to manage and analyze digital financial assets per entity in your Laravel application.

Table of Contents

  • Features
  • Requirements
  • Installation
  • Configuration
  • Basic Usage
  • Advanced Usage
  • Architecture (Docs WIP)
  • Database Schema (Docs WIP)
  • Events (Docs WIP)
  • Exception Handling (Docs WIP)
  • Testing (Docs WIP)
  • Security (Docs WIP)
  • Contributing (Docs WIP)
  • License (Docs WIP)

Features

  • Multiple wallets per entity with polymorphic relations
  • Multi-currency support with proper money handling
  • Secure transaction processing with optimistic locking
  • Flexible transaction actions system
  • Event-driven architecture
  • Support for both auto-increment and UUID primary keys
  • Comprehensive exception handling
  • Transaction history and balance tracking
  • Detailed meta information for transactions
  • Transaction reversal capabilities

Requirements

  • PHP 7.1+
  • Laravel 7.0+ | 8.0+ | 9.0+ | 10.0+ | 11.0+
  • PHP ext-intl extension

Via Composer

composer require walletable/walletable

Post-Installation

Run the installation command:

php artisan walletable:install

This will

  • Publish the configuration file to config/walletable.php
  • Publish migration files to database/migrations
  • Publish model files to app/Models
  • Optionally configure UUID support

Configuration

Basic Configuration

// config/walletable.php
return [
    'locker' => env('WALLETABLE_LOCKER', 'optimistic'),
    'models' => [
        'wallet' => \App\Models\Wallet::class,
        'transaction' => \App\Models\Transaction::class,
    ],
    'model_uuids' => false,
];

Basic Usage

Making a Model Walletable

use Walletable\Contracts\Walletable;

class User extends Model implements Walletable
{
    public function getOwnerName()
    {
        return $this->name;
    }

    public function getOwnerEmail()
    {
        return $this->email;
    }

    public function getOwnerID()
    {
        return $this->id;
    }

    public function getOwnerMorphName()
    {
        return 'user';
    }
}

Creating a Wallet

use Walletable\Facades\Walletable;

$wallet = Walletable::create(
    $user,         // Walletable entity
    'Main Wallet', // Label
    'main',        // Tag
    'USD'          // Currency
);

Basic Transactions

// Credit transaction
$wallet->action('credit_debit')->credit(
    1000,                      // Amount
    new ActionData('payment'), // Transaction data
    'Payment received'         // Remarks
);

// Debit transaction
$wallet->action('credit_debit')->debit(
    Money::USD(500),          // Amount as Money object
    new ActionData('withdrawal'),
    'ATM withdrawal'
);

Checking Balances

// Get raw amount
$balance = $wallet->amount;

// Get Money object
$money = $wallet->money();

// Formatted balance
$formatted = $wallet->money()->format(); // "$100.00"

// Check sufficient balance
$isEnough = $wallet->money()->greaterThanOrEqual(
    Money::USD(1000)
);

Advanced Usage

Custom Transaction Actions

use Walletable\Internals\Actions\ActionInterface;
use Walletable\Models\Transaction;
use Walletable\Internals\Actions\ActionData;

class PaymentAction implements ActionInterface
{
    public function apply(Transaction $transaction, ActionData $data)
    {
        $transaction->meta = [
            'payment_type' => $data->argument(0)->getValue(),
            'reference' => $data->argument(1)->getValue()
        ];
    }

    public function title(Transaction $transaction)
    {
        return "Payment via {$transaction->meta['payment_type']}";
    }

    public function image(Transaction $transaction)
    {
        return "/images/payment-icon.png";
    }

    public function supportDebit(): bool
    {
        return true;
    }

    public function supportCredit(): bool
    {
        return true;
    }

    public function reversable(Transaction $transaction): bool
    {
        return true;
    }

    public function reverse(Transaction $transaction, Transaction $new): ActionInterface
    {
        $new->meta = [
            'original_transaction_id' => $transaction->id,
            'reversal_reason' => 'customer_request'
        ];
        return $this;
    }

    public function methodResource(Transaction $transaction)
    {
        return $transaction->method;
    }
}

// Register the action
Walletable::action('payment', PaymentAction::class);