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 credit system for Laravel: manage virtual currencies, reward points, and balances with deposits, withdrawals, transfers, transaction history, historical balance checks, and metadata support. Ideal for credit-based features in any app.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require climactic/laravel-credits
    php artisan vendor:publish --tag="credits-migrations"
    php artisan migrate
    
  2. Add Trait to Model:

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

    $user->creditAdd(100, 'Initial deposit');
    

Where to Look First

  • Core Methods: creditAdd(), creditDeduct(), creditTransfer(), creditBalance()
  • Transaction History: creditHistory()
  • Metadata: whereMetadata() scopes
  • Events: CreditsAdded, CreditsDeducted, CreditsTransferred

First Use Case

Implement a subscription system where users earn credits for purchases and redeem them for premium features:

// User makes purchase
$user->creditAdd(50, 'Purchase #123', ['order_id' => 123, 'product' => 'Premium']);

// Check balance before redemption
if ($user->hasCredits(30)) {
    $user->creditDeduct(30, 'Premium feature access');
}

Implementation Patterns

Core Workflows

Credit Management

// Add credits with metadata
$user->creditAdd(100, 'Referral bonus', ['referrer_id' => 5]);

// Deduct with validation
if ($user->hasCredits(20)) {
    $user->creditDeduct(20, 'Service fee');
}

// Transfer between users
$sender->creditTransfer($recipient, 50, 'Gift');

Transaction History

// Paginated history with metadata
$history = $user->creditHistory()
    ->whereMetadata('source', 'purchase')
    ->paginate(10);

// Filter by date range
$recent = $user->creditHistory()
    ->whereBetween('created_at', [now()->subDays(7), now()])
    ->get();

Batch Operations

// Bulk add credits to multiple users
User::where('role', 'premium')->each(function ($user) {
    $user->creditAdd(100, 'Premium welcome bonus');
});

// Process refunds
$refunds = Order::where('status', 'refunded')->get();
foreach ($refunds as $order) {
    $order->user->creditAdd($order->amount, 'Refund', ['order_id' => $order->id]);
}

Integration Patterns

Event Listeners

// Track credit changes in logs
event(new CreditsAdded($user, $amount, $description));

// Send notifications
event(new CreditsDeducted($user, $amount, $description))
    ->then(function () use ($user) {
        Notification::send($user, new CreditDeductedNotification());
    });

API Responses

// Return balance in API
return response()->json([
    'balance' => $user->creditBalance(),
    'transactions' => $user->creditHistory()->take(5)->get()
]);

Command Processing

// Process credit adjustments via Artisan
php artisan credits:adjust --user=1 --amount=50 --reason="Admin adjustment"

Advanced Patterns

Custom Validation

// Validate before deduction
$required = $subscription->requiredCredits();
if (!$user->hasCredits($required)) {
    throw new \Exception("Insufficient credits for subscription");
}

Historical Analysis

// Calculate monthly credit trends
$monthly = $user->credits()
    ->selectRaw('MONTH(created_at) as month, SUM(amount) as total')
    ->groupBy('month')
    ->get();

Multi-Currency Support

// Extend for currency-aware credits
class User extends Model
{
    use HasCredits;

    public function creditAdd(float $amount, string $description, array $metadata = [], string $currency = 'USD')
    {
        $metadata['currency'] = $currency;
        return $this->creditAdd($amount, $description, $metadata);
    }
}

Gotchas and Tips

Common Pitfalls

  1. Concurrency Issues

    • Without proper locking, concurrent operations can cause race conditions
    • Solution: Use creditAdd()/creditDeduct() in transactions or with explicit locking:
      DB::transaction(function () use ($user) {
          $user->creditDeduct(10);
      });
      
  2. Metadata Query Performance

    • Unindexed metadata queries scan entire tables
    • Solution: Implement database-specific optimizations (see README) or limit metadata usage
  3. Negative Balances

    • Disabled by default (allow_negative_balance = false)
    • Solution: Enable if needed or validate before operations
  4. Large Transaction Volumes

    • Batch operations may hit memory limits
    • Solution: Process in chunks:
      $users->chunk(100, function ($chunk) {
          foreach ($chunk as $user) {
              $user->creditAdd(50, 'Batch bonus');
          }
      });
      
  5. Event Ordering

    • Events fire after database operations
    • Solution: Use retryUntil for critical operations:
      $user->creditDeduct(10)->retryUntil(function () {
          return $user->fresh()->hasCredits(10);
      });
      

Debugging Tips

  1. Transaction Logs

    • Enable query logging to debug slow metadata queries:
      DB::enableQueryLog();
      $user->credits()->whereMetadata('source', 'purchase')->get();
      dd(DB::getQueryLog());
      
  2. Balance Mismatches

    • Verify running balance matches manual calculations:
      $manual = $user->credits()->sum('amount');
      $running = $user->creditBalance();
      
  3. Metadata Validation

    • Use MetadataValidator directly for custom validation:
      use Climactic\Credits\Support\MetadataValidator;
      $validator = new MetadataValidator();
      $validator->validate('user.id', 123);
      

Configuration Quirks

  1. Table Name Overrides

    • Customize via config:
      'table_name' => 'custom_credits',
      
    • Update migrations if changed
  2. Negative Balance Handling

    • Set allow_negative_balance = true in config for overdraft scenarios
    • Consider adding validation middleware for API endpoints
  3. Event Customization

    • Bind custom listeners:
      CreditsAdded::listen(function ($event) {
          // Custom logic
      });
      

Extension Points

  1. Custom Transaction Types

    // Add custom transaction logic
    $user->credits()->create([
        'amount' => 100,
        'description' => 'Custom transaction',
        'metadata' => ['type' => 'custom'],
        'creditable_type' => User::class,
        'creditable_id' => $user->id,
        'running_balance' => $user->creditBalance() + 100
    ]);
    
  2. Metadata Serialization

    • Override serialization for complex metadata:
      use Climactic\Credits\Support\MetadataSerializer;
      
      class CustomSerializer extends MetadataSerializer
      {
          public function serialize($metadata)
          {
              // Custom serialization
          }
      }
      
      // Register in service provider
      $this->app->bind(MetadataSerializer::class, CustomSerializer::class);
      
  3. Custom Query Scopes

    // Add reusable scope
    class User extends Model
    {
        public function scopeRecentCredits($query, $days = 7)
        {
            return $query->where('created_at', '>=', now()->subDays($days));
        }
    }
    
    // Usage
    $user->credits()->recentCredits()->get();
    
  4. Audit Logging

    • Extend events for comprehensive auditing:
      CreditsAdded::listen(function ($event) {
          AuditLog::create([
              'user_id' => $event->user->id,
              'action' => 'credits_added',
              'details' => [
                  'amount' => $event->amount,
                  'description' => $event->description,
                  'metadata' => $event->metadata
              ]
          ]);
      });
      

Performance Optimization

  1. Indexing Strategy

    • For high-volume systems, create composite indexes:
      CREATE INDEX idx_credits_user_type ON credits(creditable_type, creditable_id, created_at);
      
  2. Batch Inserts

    • Use queue jobs for bulk operations:
      CreditBatchJob::dispatch($users, 100, 'Batch bonus');
      
  3. Caching

    • Cache frequent balance checks:
      $balance = Cache::remember("user_{$user->
      
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle