Weave 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

Credit Bundle Laravel Package

tourze/credit-bundle

tourze/credit-bundle provides a reusable credit and points ledger for PHP applications, with account balances, transaction records, and service APIs for awarding, deducting, and querying credits. Integrate it to add auditable credit management.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is installed via Composer and follows the standard bundle/package conventions:

composer require tourze/credit-bundle

Because this is a 1.0.x release, treat the surface as stable but small — start by reading, in this order:

  1. The service provider / bundle registration — this tells you what is auto-discovered, what config keys exist, and which migrations are loaded.
  2. The published config file — usually the deciding factor for table names, default currency/point unit, and whether balances are stored on the model or derived from a ledger.
  3. The migrations — the schema is the contract. Look for a credit/balance column versus a transactions/ledger table; that single detail determines almost every downstream pattern.
  4. The domain service or manager class — the entry point (something like CreditManager, CreditService, or a facade) is what you will actually call from controllers and jobs.

Publish whatever is publishable before writing code:

php artisan vendor:publish --provider="Tourze\CreditBundle\CreditServiceProvider"
php artisan migrate

First use case: give a user points and read them back. Wire the service into a controller, grant a fixed amount, redirect, and render the balance on a dashboard. Once that round trip works, add a spend operation and a "insufficient balance" failure path — those two flows cover roughly 80% of what a credits system does in a real app.

If the package is Symfony-flavored under the hood (Tourze publishes many *-bundle packages), verify early that the service provider is genuinely registered with Laravel's container and not just a Symfony-style bundle class. A five-minute php artisan tinker session — resolve the service, call a method, dump the result — will save you an hour later.

Implementation Patterns

Wrap every mutation in a transaction. Credits are a financial primitive wearing a costume. Any operation that reads a balance and then writes to it must be inside DB::transaction(), with a row lock on the account:

DB::transaction(function () use ($user, $amount) {
    $account = CreditAccount::where('user_id', $user->id)->lockForUpdate()->firstOrFail();
    $account->decrement('balance', $amount); // or the package's service call
});

Prefer the ledger, derive the balance. If the package ships a transaction/ledger table, treat it as the source of truth and the balance column as a cache. Reconciling SUM(amount) against balance is a cheap nightly check and catches nearly every bug in this class of code.

Idempotency for anything triggered by a job or webhook. Payment succeeded, order shipped, user signed up — each of these can fire twice. Store an idempotency key (order ID, event ID) with the credit transaction and reject duplicates at the unique-index level, not in PHP:

CreditTransaction::firstOrCreate(
    ['reference' => $order->id, 'type' => 'purchase'],
    ['user_id' => $user->id, 'amount' => $order->credit_amount]
);

Separate earning from spending. Two methods, two permission checks, two failure modes. Spending can fail (insufficient funds, expired credits, daily cap); earning almost never should. Conflating them into one adjustBalance($delta) produces code that is impossible to audit later.

Use domain events, not model observers, for side effects. "User crossed 1000 credits → unlock badge" belongs in a listener on a credit-changed event. Observers on the transaction model fire on reconciliation and admin adjustments too, which is usually wrong.

Admin adjustments deserve their own type. Give manual corrections a distinct type/actor field so support can answer "why did this balance change" without reading application logs. Every ledger row should carry type, actor_id (nullable for system), reference, and created_at.

Test with factories and SQLite in-memory. Credit logic is pure enough to unit test well. Assert the balance and the ledger row count — a test that only checks the balance will pass while silently double-writing.

Gotchas and Tips

  • Never use floats. A credit is an integer in its smallest unit. 0.1 + 0.2 problems in a points system produce support tickets that are genuinely hard to explain. If the package stores a decimal, cast to int at the boundary and keep it there.
  • 1.0.1 is a patch release with no published changelog detail — the release notes are only a compare link. Before bumping, run composer update tourze/credit-bundle --with-dependencies on a branch and read the diff, or at minimum git diff 1.0.0..1.0.1 in vendor. Patch versions on young packages sometimes carry schema or signature changes that the version number doesn't advertise.
  • Pin the version in composer.json ("tourze/credit-bundle": "^1.0.1" rather than *) while the package is pre-2.0. You want a human in the loop for minor bumps.
  • Check whether migrations are publishable or load-bearing. If the package loads migrations from its own directory, php artisan migrate will run them but you cannot edit them — any schema change must be a separate migration in your app.
  • Negative balances are the classic failure. Enforce at three levels: the service check, a CHECK (balance >= 0) constraint or model guard, and a database row lock. Any one alone will eventually fail.
  • Concurrency under load is the first thing to break and the last thing to show up in testing. A single-user test suite will pass while two parallel spend requests overdraw the account in production. Write one test with two overlapping transactions.
  • Time-based credits (expiry) need a scheduled job. If the package models expiring credits, register php artisan schedule:run handling and verify it in staging — an unrun expiry job looks identical to correct behavior until the accounting is audited.
  • Config caching bites during setup. After publishing config, run php artisan config:clear before assuming your changes did nothing.
  • Extension points to look for: custom transaction types, an event/listener hook on balance change, and a swappable account model. If the package hardcodes its own model, bind an interface in a service provider rather than subclassing — subclassing Eloquent models with package-internal queries is a reliable source of surprises.
  • Debugging tip: when a balance looks wrong, query the ledger directly rather than trusting the UI or the accessor. CreditTransaction::where('user_id', $id)->orderBy('id')->get(['type','amount','created_at']) will show you the exact sequence, and the wrong row is usually visible within seconds.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect