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 Laravel package for credit systems like virtual currency, reward points, and balances. Supports deposits/withdrawals, transfers, transaction history, metadata, and querying—ideal for building auditable, credit-based features in your app.

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->creditAdd(100.00, 'Initial deposit');
    

Where to Look First

  • Basic CRUD: Focus on creditAdd(), creditDeduct(), and creditBalance() methods.
  • Configuration: Check config/credits.php for settings like allow_negative_balance.
  • Database: Verify the credits table migration matches your needs.

First Use Case

Subscription System:

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

// Deduct credits on purchase
if ($user->hasCredits(50)) {
    $user->creditDeduct(50, 'Product purchase', [
        'product_id' => $product->id,
        'order_id' => $order->id,
    ]);
}

Implementation Patterns

Core Workflows

  1. Credit Management:

    // Add credits with metadata
    $user->creditAdd(50, 'Referral bonus', ['referrer_id' => $referrer->id]);
    
    // Deduct credits with validation
    if ($user->hasCredits(25)) {
        $user->creditDeduct(25, 'Content download');
    }
    
  2. Transfers Between Users:

    $sender->creditTransfer($recipient, 10, 'Tip', [
        'source' => 'user_tip',
        'transaction_id' => $tip->id,
    ]);
    
  3. Audit Trails:

    // Get recent transactions
    $transactions = $user->creditHistory(10, 'desc');
    
    // Filter by metadata
    $purchases = $user->credits()
        ->whereMetadata('type', 'purchase')
        ->get();
    

Integration Tips

  • Events: Listen to credits.added, credits.deducted, and credits.transferred for side effects (e.g., notifications, analytics).

    event(new CreditAdded($user, 100, 'Deposit'));
    
  • Validation: Use hasCredits() before deductions to avoid negative balances:

    if ($user->hasCredits($amount)) {
        $user->creditDeduct($amount, 'Reason');
    }
    
  • Batch Operations:

    // Bulk add credits (e.g., for promotions)
    User::where('role', 'premium')->each(function ($user) {
        $user->creditAdd(50, 'Promotional bonus');
    });
    
  • Historical Queries:

    // Get balance at a specific time
    $balance = $user->creditBalanceAt(new DateTime('2023-01-01'));
    

Advanced Patterns

  1. Custom Metadata Queries:

    // Find all transactions with premium tags
    $user->credits()
        ->whereMetadataContains('tags', 'premium')
        ->get();
    
  2. Composite Filters:

    $user->credits()
        ->whereMetadata('source', 'purchase')
        ->where('amount', '>', 50)
        ->orderBy('created_at', 'desc')
        ->get();
    
  3. Performance Optimization:

    • For high-traffic apps, add virtual columns/indexes (see Gotchas).
    • Use creditHistoryWithMetadata() for complex filters:
      $user->creditHistoryWithMetadata([
          ['key' => 'source', 'value' => 'subscription'],
          ['key' => 'amount', 'operator' => '>', 'value' => 100],
      ]);
      

Gotchas and Tips

Pitfalls

  1. Concurrency Issues:

    • Problem: Race conditions can occur if multiple requests modify credits simultaneously (e.g., two users trying to deduct credits from the same account).
    • Solution: Use transactions for critical operations:
      DB::transaction(function () use ($user, $amount) {
          $user->creditDeduct($amount, 'Safe transaction');
      });
      
    • Database Note: SQLite ignores row-level locking; avoid concurrent operations in production.
  2. Metadata Query Performance:

    • Problem: Unindexed metadata queries scan the entire credits table, slowing down applications with >10k transactions.
    • Solution: Add indexes (see Performance Optimization in the README).
  3. Negative Balances:

    • Problem: If allow_negative_balance is false (default), creditDeduct() throws an exception when insufficient credits exist.
    • Solution: Check hasCredits() first or set allow_negative_balance to true in config.
  4. Metadata Key Validation:

    • Problem: Invalid keys (e.g., empty, with quotes, or using ->) throw InvalidArgumentException.
    • Solution: Validate keys before querying:
      $validKey = trim('source'); // Valid
      $invalidKey = 'user->id';   // Invalid (throws exception)
      
  5. Transaction History Order:

    • Problem: creditHistory() defaults to desc (newest first). Use 'asc' for chronological order:
      $user->creditHistory(10, 'asc'); // Oldest first
      

Debugging Tips

  1. Check Balance Mismatches:

    • Verify running balances with raw SQL:
      SELECT * FROM credits WHERE creditable_id = ? AND creditable_type = ? ORDER BY created_at;
      
    • Compare with creditBalance() to spot discrepancies.
  2. Metadata Query Issues:

    • Use dd() to inspect metadata structure:
      $transaction = $user->credits()->first();
      dd($transaction->metadata); // Debug nested keys
      
    • For complex queries, test with a single condition first.
  3. Event Debugging:

    • Listen to events in AppServiceProvider@boot():
      event(new CreditAdded($user, 100, 'Test'));
      
    • Use php artisan event:listen to check listeners.

Extension Points

  1. Custom Events: Extend the default events to include additional data:

    class CustomCreditAdded implements ShouldBroadcast
    {
        public function __construct(
            public User $user,
            public float $amount,
            public string $reason,
            public array $metadata = []
        ) {}
    }
    
  2. Model Observers: Track credit changes in a separate table:

    class CreditObserver
    {
        public function saved(HasCredits $model)
        {
            // Log to audit table
        }
    }
    
  3. Query Scopes: Add reusable scopes to your model:

    class User extends Model
    {
        public function scopeRecentCredits($query)
        {
            return $query->where('created_at', '>', now()->subDay());
        }
    }
    
  4. Database Triggers: For complex logic, use database triggers (e.g., auto-logging to a secondary table).

Configuration Quirks

  1. Table Name: Override the default credits table name in config/credits.php:

    'table_name' => 'user_credits',
    
  2. Negative Balances: Enable with caution:

    'allow_negative_balance' => true,
    
    • Risk: May require additional validation in business logic.
  3. Metadata Storage: The package stores metadata as JSON. For large datasets, consider:

    • Normalizing metadata into separate tables.
    • Using a document store (e.g., MongoDB) for complex metadata.

Performance Optimization

  1. Indexing Strategy:

    • High-Frequency Queries: Index metadata keys used in WHERE clauses (e.g., source, order_id).
    • Composite Indexes: Combine frequently queried columns (e.g., [creditable_id, created_at, metadata->source]).
  2. Batch Inserts: For bulk operations (e.g., promotions), use DB::transaction with batch inserts:

    DB::transaction(function () {
        $users->each(function ($user) {
            $user->creditAdd(50, 'Bulk promotion');
        });
    });
    
  3. Caching: Cache balances for read-heavy applications:

    $balance
    
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.
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui