Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Credits Laravel Package

climactic/laravel-credits

Ledger-based credits for Laravel: manage virtual currencies, reward points, and other credit systems with deposits, withdrawals, transfers, transaction history, historical balances, and metadata-powered querying.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require climactic/laravel-credits
    php artisan vendor:publish --tag="credits-migrations"
    php artisan migrate
    
  2. Model Setup: Add the HasCredits trait to your model (e.g., User):

    use Climactic\Credits\Traits\HasCredits;
    
    class User extends Model
    {
        use HasCredits;
    }
    
  3. First Transaction:

    $user = User::first();
    $user->creditAdd(100.00, 'Initial deposit');
    

Where to Look First

  • Basic CRUD: Focus on creditAdd(), creditDeduct(), and creditBalance().
  • Querying: Use creditHistory() for transaction logs.
  • Metadata: Explore whereMetadata() for filtering transactions.

First Use Case

Subscription System:

// Add credits on subscription
$user->creditAdd(1000, 'Annual subscription', ['plan' => 'premium']);

// Deduct credits for purchases
if ($user->hasCredits(50)) {
    $user->creditDeduct(50, 'Product purchase', ['product_id' => 123]);
}

// Check balance
$balance = $user->creditBalance();

Implementation Patterns

Core Workflows

  1. Credit Management:

    // Add credits with metadata
    $user->creditAdd(50, 'Referral bonus', ['referrer_id' => 42]);
    
    // Deduct credits with validation
    if ($user->hasCredits(25)) {
        $user->creditDeduct(25, 'Service fee');
    }
    
  2. Transfers:

    // Transfer between users
    $user1->creditTransfer($user2, 30, 'Collaboration payment');
    
  3. Historical Queries:

    // Get last 5 transactions
    $user->creditHistory(5);
    
    // Filter by metadata
    $user->credits()
        ->whereMetadata('source', 'purchase')
        ->get();
    

Integration Tips

  • Events: Listen to CreditsAdded, CreditsDeducted, or CreditsTransferred for real-time actions.

    event(new CreditsAdded($user, $amount, $description));
    
  • Middleware: Validate credits before critical actions:

    public function handle(Request $request, Closure $next) {
        if (!$request->user()->hasCredits(10)) {
            abort(403, 'Insufficient credits');
        }
        return $next($request);
    }
    
  • Commands: Automate credit adjustments (e.g., nightly interest):

    $users = User::all();
    foreach ($users as $user) {
        $user->creditAdd($user->creditBalance() * 0.01, 'Nightly interest');
    }
    

Advanced Patterns

  • Batch Processing:

    $users = User::where('tier', 'premium')->get();
    foreach ($users as $user) {
        $user->creditAdd(100, 'Premium tier bonus');
    }
    
  • Custom Metadata Validation: Extend the trait to validate metadata before transactions:

    protected function validateMetadata(array $metadata): void {
        if (empty($metadata['order_id'])) {
            throw new \InvalidArgumentException('Order ID required');
        }
    }
    

Gotchas and Tips

Pitfalls

  1. Concurrency Issues:

    • SQLite ignores row-level locks. Use MySQL/PostgreSQL for production.
    • Fix: Wrap critical operations in transactions:
      DB::transaction(function () use ($user) {
          $user->creditDeduct(50, 'Purchase');
      });
      
  2. Metadata Query Performance:

    • Unindexed metadata queries scan the entire credits table.
    • Fix: Add virtual columns (MySQL) or GIN indexes (PostgreSQL) for frequently queried keys.
  3. Negative Balances:

    • Disabled by default (allow_negative_balance = false).
    • Fix: Enable in config if needed, but handle edge cases in business logic.
  4. Metadata Key Format:

    • Invalid keys (e.g., user->id) throw exceptions.
    • Fix: Use dot notation (user.id) and validate keys early.

Debugging Tips

  • Transaction Logs:

    $user->credits()->latest()->take(10)->get();
    

    Use dd() to inspect metadata structure.

  • Balance Mismatches:

    • Verify running balance with raw SQL:
      SELECT SUM(amount) FROM credits WHERE creditable_id = $userId;
      
  • Event Debugging: Listen to events in Tinker:

    event(new CreditsAdded($user, 100, 'Test'));
    

Extension Points

  1. Custom Events: Extend the package’s events for your use case:

    class CustomCreditEvent extends CreditsAdded {
        public function __construct($creditable, $amount, $description, $customData) {
            parent::__construct($creditable, $amount, $description);
            $this->customData = $customData;
        }
    }
    
  2. Query Scopes: Add custom scopes to the HasCredits trait:

    public function scopeRecentTransactions($query, $days = 7) {
        return $query->where('created_at', '>=', now()->subDays($days));
    }
    
  3. Metadata Serialization: Override serializeMetadata() to customize how metadata is stored:

    protected function serializeMetadata(array $metadata): string {
        return json_encode($metadata, JSON_UNESCAPED_SLASHES);
    }
    

Configuration Quirks

  • Table Name: Change config('credits.table_name') if you renamed the migration table.

  • Decimal Precision: Ensure your database column for amount uses DECIMAL(19, 4) for precision.

  • Time Zones: Historical balances (creditBalanceAt()) respect the model’s usesTimezone setting.

Performance Optimization

  • Indexing Strategy: Prioritize indexes for:

    • creditable_id, creditable_type (foreign keys).
    • Frequently queried metadata (e.g., source, order_id).
    • Composite indexes for common query patterns.
  • Batch Inserts: Use DB::transaction() for bulk operations to avoid lock contention:

    DB::transaction(function () {
        foreach ($users as $user) {
            $user->creditAdd(10, 'Batch bonus');
        }
    });
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport