NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Credits for Laravel

Configuration

The package ships with sensible defaults and works with zero host configuration. The published config/credits.php in full:

<?php

declare(strict_types=1);

use RoundlyConsulting\Credits\Models\Credit;

return [

    'model' => Credit::class,

    'key_type' => env('CREDITS_KEY_TYPE', 'bigint'),

    'primary_key_type' => env('CREDITS_PRIMARY_KEY_TYPE', 'bigint'),

    'allow_overdraft' => env('CREDITS_ALLOW_OVERDRAFT', false),

    'minimum_balance' => 0,

    'default_bucket' => 'default',

    'scale' => 0,

    'rounding' => env('CREDITS_ROUNDING', 'half_away_from_zero'),

    'currencies' => [
        // 'store_credit' => 'EUR',
        // 'points' => 'PTS',
    ],

    'modifiable' => [
        //
    ],

];

Every key

KeyDefaultEnvPurpose
modelCredit::class—The Eloquent model for ledger rows. Point it at your own subclass of Credit to customise behaviour or the table; anything that is not Credit or a subclass throws InvalidConfigurationException.
key_typebigintCREDITS_KEY_TYPEKey type of your creditable models (the creditable_id morph column): bigint, uuid or ulid; anything else throws InvalidConfigurationException. Fixed when the migration runs.
primary_key_typebigintCREDITS_PRIMARY_KEY_TYPEThe credits table’s own id: bigint, uuid or ulid; anything else throws InvalidConfigurationException. Fixed when the migration runs.
allow_overdraftfalseCREDITS_ALLOW_OVERDRAFTWhen false, a deduction that would take a bucket below minimum_balance throws InsufficientCreditsException. True permits deductions below minimum_balance globally. The env value is read as a boolean: 1, true, on and yes enable it; 0, false, off and no leave it off; anything else throws InvalidConfigurationException naming the key.
minimum_balance0—The floor enforced when overdraft is disallowed. An int or a canonical integer string ('50', '-100'); blank ('') is not set, so 0 applies; anything else ('fifty', '50.5') throws InvalidConfigurationException instead of becoming 0.
default_bucket'default'—The bucket for reads and writes that omit one. A bucket-less balance returns this bucket only — it never sums across buckets. A blank value is not set, so 'default' applies; a non-string value throws InvalidConfigurationException.
scale0—Decimal places the stored integers encode. Read only by the display helpers; a currency-denominated bucket ignores it. An integer from 0 to 36 (or its canonical string); anything else throws InvalidConfigurationException.
roundinghalf_away_from_zeroCREDITS_ROUNDINGDefault display rounding mode — a \RoundingMode case in snake_case. Not set (absent, null or a blank CREDITS_ROUNDING=) means half_away_from_zero; an unknown value throws InvalidMoneyConfiguration on first use.
currencies[]—Bucket name → money currency code (ISO or custom). Resolved lazily, on first use.
modifiable[]—Resolver closures for credits:modify. Each receives a $modify callback to apply the change to a Creditable entity. A non-array value or a non-callable entry throws InvalidConfigurationException.

Environment

The deployment-sensitive keys are env-backed:

CREDITS_KEY_TYPE=bigint
CREDITS_PRIMARY_KEY_TYPE=bigint
CREDITS_ALLOW_OVERDRAFT=false
CREDITS_ROUNDING=half_away_from_zero

Things to decide early

  • key_type and primary_key_type are read when the migration runs — choose them before you publish and migrate. Changing either afterwards is a data migration, not a config change.
  • An unrecognized key type throws InvalidConfigurationException naming the key — it never falls back to bigint.
  • Every other setting is read strictly too: a key that is not set — absent, null or blank (a host’s KEY=) — takes its default, and a present value of the wrong shape — fifty for the floor, an array for the bucket, a string where the resolver list belongs — throws InvalidConfigurationException naming the key. A typo is never cast to 0 or swapped for the default.
  • credits.currencies is resolved lazily on every use, never at boot, so a custom currency registered in your own provider’s boot() is accepted.
  • credits.rounding must name a \RoundingMode case in snake_case; the legacy PHP_ROUND_* integers are refused.
  • CREDITS_ALLOW_OVERDRAFT is read as a boolean — off, no and false really leave overdraft off, and php artisan about reports exactly what the guard enforces.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.