ajooda / laravel-ai-metering
AI usage metering and billing for Laravel apps (tokens, quotas, credits, billing).
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/ajooda/laravel-ai-metering
Requires
- php: ^8.1
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^8.0|^9.0
- phpunit/phpunit: ^10.0|^11.0
README
A production-ready Laravel package for AI usage metering, quotas, and billing integration. Track token usage, enforce limits, and integrate with Stripe for billing.
Repository: GitHub | Packagist
Features
- 🎯 Simple Developer Experience - Fluent API for metering AI usage
- 📊 Usage Tracking - Automatic token and cost tracking
- 🚦 Quota Management - Configurable limits (tokens, cost, per-plan, per-tenant)
- 💳 Billing Integration - Stripe/Cashier support with credit-based and subscription modes
- 🏢 Multi-Tenancy - Works with or without tenancy packages
- 📈 Dashboard - Built-in dashboard for usage monitoring
- 🔌 Provider Agnostic - Support for OpenAI, Anthropic, and custom providers
- ⚡ Performance - Caching, queue support, and optimized queries
Requirements
- PHP >= 8.1
- Laravel 10.x, 11.x, or 12.x
- Database (MySQL, PostgreSQL, SQLite, or SQL Server) - configured in your Laravel application
Optional Dependencies
For AI provider integration, you'll need the respective SDK packages:
- OpenAI:
openai-php/laravel(for OpenAI provider) - Anthropic:
anthropic-php/sdk(for Anthropic provider) - Stripe/Cashier:
laravel/cashier(for Stripe billing integration)
# For OpenAI composer require openai-php/laravel # For Anthropic composer require anthropic-php/sdk # For Stripe billing composer require laravel/cashier
Installation
Install the package via Composer:
composer require ajooda/laravel-ai-metering
Publish the configuration file:
php artisan vendor:publish --tag=ai-metering-config
Publish and run migrations:
php artisan vendor:publish --tag=ai-metering-migrations php artisan migrate
Note: Make sure your database connection is configured in
.envbefore running migrations. The package will use your default database connection unless specified otherwise viaAI_METERING_DB_CONNECTION.
Environment Variables
The package supports various environment variables for configuration. Add these to your .env file:
# Default Provider AI_METERING_DEFAULT_PROVIDER=openai # Billing Configuration AI_METERING_BILLING_DRIVER=Metrix\AiMetering\Services\Billing\NullBillingDriver AI_METERING_OVERAGE_BEHAVIOR=block AI_METERING_OVERAGE_SYNC_STRATEGY=batch AI_METERING_CREDIT_OVERDRAFT=false AI_METERING_CURRENCY=usd AI_METERING_PAYMENT_FAILURE_GRACE_PERIOD=7 # Tenant Resolver (optional) AI_METERING_TENANT_RESOLVER=Metrix\AiMetering\Resolvers\NullTenantResolver # Period Configuration AI_METERING_PERIOD_TYPE=monthly AI_METERING_PERIOD_ALIGNMENT=calendar AI_METERING_TIMEZONE=UTC # Storage Configuration AI_METERING_PRUNE_AFTER_DAYS=365 AI_METERING_DB_CONNECTION= # Performance Configuration AI_METERING_CACHE_LIMIT_CHECKS=true AI_METERING_CACHE_TTL=300 AI_METERING_QUEUE_RECORDING=false AI_METERING_BATCH_SIZE=100 # Dashboard Configuration AI_METERING_DASHBOARD_ENABLED=true AI_METERING_DASHBOARD_PREFIX=ai-metering AI_METERING_DASHBOARD_GATE= # Security Configuration AI_METERING_VALIDATE_FEATURES=true AI_METERING_SANITIZE_METADATA=true AI_METERING_RATE_LIMIT=false AI_METERING_PREVENT_RACE_CONDITIONS=true # Logging Configuration AI_METERING_LOGGING_ENABLED=true AI_METERING_LOG_LEVEL=info AI_METERING_LOG_FAILURES=true # Feature Flags AI_METERING_SOFT_DELETES=false AI_METERING_AGGREGATION_TABLES=false
Note: All environment variables are optional and have sensible defaults. You only need to set them if you want to override the default values.
Quick Start
After installation, you can start using the package immediately:
- Create a plan (optional, for quota management):
use Metrix\AiMetering\Models\AiPlan; $plan = AiPlan::create([ 'name' => 'Basic Plan', 'slug' => 'basic', 'monthly_token_limit' => 100000, 'monthly_cost_limit' => 10.00, 'is_active' => true, ]);
- Create a subscription (optional, if using plan-based billing):
use Metrix\AiMetering\Models\AiSubscription; $subscription = AiSubscription::create([ 'billable_type' => User::class, 'billable_id' => $user->id, 'ai_plan_id' => $plan->id, 'billing_mode' => 'plan', 'started_at' => now(), 'renews_at' => now()->addMonth(), ]);
- Start metering AI usage:
use Metrix\AiMetering\Facades\AiMeter; $response = AiMeter::forUser($user) ->billable($user) ->usingProvider('openai', 'gpt-4o-mini') ->feature('chat') ->call(function () { // Your AI provider call here return OpenAI::chat()->create([...]); });
Tip: If you're not using plans or subscriptions, you can still track usage. The package will work with just the
billableentity.
Configuration
The configuration file is located at config/ai-metering.php. Key settings:
Providers
Configure AI providers and their pricing:
'providers' => [ 'openai' => [ 'class' => \Metrix\AiMetering\Services\Providers\OpenAiProvider::class, 'models' => [ 'gpt-4o-mini' => [ 'input_price_per_1k' => 0.00015, 'output_price_per_1k' => 0.00060, ], ], ], ],
Billing
Configure billing driver and behavior:
'billing' => [ 'driver' => \Metrix\AiMetering\Services\Billing\CashierBillingDriver::class, 'overage_behavior' => 'block', // 'block', 'charge', 'allow' 'overage_sync_strategy' => 'batch', // 'immediate', 'batch' 'credit_overdraft_allowed' => false, ],
Period Settings
Configure usage period calculation:
'period' => [ 'type' => 'monthly', // 'monthly', 'weekly', 'daily', 'yearly', 'rolling' 'alignment' => 'calendar', // 'calendar' or 'rolling' 'timezone' => 'UTC', ],
Basic Usage
Metering AI Usage
Use the AiMeter facade to wrap your AI provider calls. The package works with any AI provider SDK - you just need to wrap your existing calls:
use Metrix\AiMetering\Facades\AiMeter; use OpenAI\Laravel\Facades\OpenAI; // Requires: composer require openai-php/laravel $response = AiMeter::forUser(auth()->user()) ->billable(auth()->user()) ->usingProvider('openai', 'gpt-4o-mini') ->feature('email_reply') ->call(function () { return OpenAI::chat()->create([ 'model' => 'gpt-4o-mini', 'messages' => [ ['role' => 'user', 'content' => 'Write a polite reply...'], ], ]); }); // Access the response $aiResponse = $response->getResponse(); // Access usage information $usage = $response->getUsage(); echo "Tokens used: " . $usage->totalTokens; echo "Cost: $" . $response->getUsage()->totalCost; // Check limits if ($response->isApproachingLimit()) { // Handle approaching limit }
Manual Usage Tracking
For providers that don't return usage automatically:
$response = AiMeter::forUser($user) ->billable($user) ->usingProvider('manual', 'custom-model') ->withManualUsage([ 'input_tokens' => 450, 'output_tokens' => 900, 'total_tokens' => 1350, ]) ->call(function () use ($client) { return $client->doStuff(); });
With Metadata
Add metadata for better tracking:
$response = AiMeter::forUser($user) ->billable($user) ->usingProvider('openai', 'gpt-4o-mini') ->feature('support_reply') ->withMeta([ 'ticket_id' => $ticket->id, 'customer_id' => $customer->id, ]) ->call(fn () => OpenAI::chat()->create([...]));
Idempotency
Prevent duplicate usage records:
$response = AiMeter::forUser($user) ->withIdempotencyKey('unique-request-id-123') ->call(fn () => OpenAI::chat()->create([...]));
Plans & Quotas
Creating Plans
use Metrix\AiMetering\Models\AiPlan; $plan = AiPlan::create([ 'name' => 'Pro Plan', 'slug' => 'pro', 'monthly_token_limit' => 1000000, 'monthly_cost_limit' => 100.00, 'overage_price_per_1k_tokens' => 0.01, 'is_active' => true, ]);
Creating Subscriptions
use Metrix\AiMetering\Models\AiSubscription; $subscription = AiSubscription::create([ 'billable_type' => User::class, 'billable_id' => $user->id, 'ai_plan_id' => $plan->id, 'billing_mode' => 'plan', // or 'credits' 'started_at' => now(), 'renews_at' => now()->addMonth(), ]);
Limit Overrides
Override limits for specific periods:
use Metrix\AiMetering\Models\AiUsageLimitOverride; AiUsageLimitOverride::create([ 'billable_type' => User::class, 'billable_id' => $user->id, 'period_start' => now()->startOfMonth(), 'period_end' => now()->endOfMonth(), 'token_limit' => 2000000, // Double the plan limit ]);
Credits
Credit-Based Billing
Set up credit wallets:
use Metrix\AiMetering\Models\AiCreditWallet; $wallet = AiCreditWallet::firstOrCreate( [ 'billable_type' => User::class, 'billable_id' => $user->id, ], [ 'balance' => 0, 'currency' => 'usd', ] ); // Add credits $wallet->addCredits(100.00, 'top-up', ['payment_id' => 'pay_123']); // Check balance if ($wallet->hasSufficientBalance(50.00)) { // Proceed with usage }
Using Credits Mode
$response = AiMeter::forUser($user) ->billable($user) ->billingMode('credits') ->usingProvider('openai', 'gpt-4o-mini') ->call(fn () => OpenAI::chat()->create([...]));
Multi-Tenancy
The package supports multi-tenancy without coupling to a specific package.
Custom Tenant Resolver
Create a tenant resolver:
namespace App\Resolvers; use Metrix\AiMetering\Contracts\TenantResolver; class CustomTenantResolver implements TenantResolver { public function resolve(mixed $context = null): mixed { // Return the current tenant return tenant(); // or your tenancy package's method } }
Register it in config/ai-metering.php:
'tenant_resolver' => \App\Resolvers\CustomTenantResolver::class,
Using with Tenants
$response = AiMeter::forUser($user) ->forTenant($tenant) ->billable($tenant) // Bill the tenant, not the user ->usingProvider('openai', 'gpt-4o-mini') ->call(fn () => OpenAI::chat()->create([...]));
Billing Integrations
Stripe/Cashier Integration
The package integrates with Laravel Cashier for Stripe billing.
- Install Cashier:
composer require laravel/cashier
- Configure your billable model:
use Laravel\Cashier\Billable; class User extends Authenticatable { use Billable; }
- Set billing driver in config:
'billing' => [ 'driver' => \Metrix\AiMetering\Services\Billing\CashierBillingDriver::class, ],
Overage Sync
Overage charges can be synced to Stripe:
Immediate sync:
'overage_sync_strategy' => 'immediate',
Batch sync (recommended for high volume):
'overage_sync_strategy' => 'batch',
Then run the sync command:
php artisan ai-metering:sync-stripe-overages
Middleware
Protect routes with quota enforcement:
use Illuminate\Support\Facades\Route; Route::middleware(['auth', 'ai.quota'])->group(function () { Route::post('/ai/chat', [AiController::class, 'chat']); });
The middleware:
- Checks usage limits before allowing the request
- Returns 429 (Too Many Requests) if limit exceeded
- Adds response headers:
X-Remaining-Tokens,X-Remaining-Cost,X-Usage-Percentage
Dashboard
Access the dashboard at /ai-metering (configurable):
- View usage statistics
- Monitor quotas and limits
- See breakdown by provider/model
- Filter and search usage history
Customizing Dashboard Access
Configure in config/ai-metering.php:
'dashboard' => [ 'enabled' => true, 'prefix' => 'ai-metering', 'middleware' => ['web', 'auth'], 'gate' => 'viewAiMetering', // Optional gate for authorization ],
Artisan Commands
Usage Report
php artisan ai-metering:report
php artisan ai-metering:report --month=2024-01
php artisan ai-metering:report --billable-type="App\Models\User" --billable=1
Cleanup Old Usage
php artisan ai-metering:cleanup php artisan ai-metering:cleanup --days=90 php artisan ai-metering:cleanup --dry-run
Sync Stripe Overages
php artisan ai-metering:sync-stripe-overages php artisan ai-metering:sync-stripe-overages --limit=50
Validate Configuration
php artisan ai-metering:validate
Migrate Plans
php artisan ai-metering:migrate-plan "App\Models\User" 1 "pro-plan" php artisan ai-metering:migrate-plan "App\Models\User" 1 "pro-plan" --from-plan="basic-plan"
Sync Plans
php artisan ai-metering:sync-plans
List all active plans in the system.
Aggregate Usage
php artisan ai-metering:aggregate
php artisan ai-metering:aggregate --period=month
php artisan ai-metering:aggregate --period=week --billable-type="App\Models\User"
Pre-computes usage aggregates for dashboard performance (requires aggregation_tables feature enabled in config).
Querying Usage
Use Eloquent scopes to query usage records:
use Metrix\AiMetering\Models\AiUsage; use Carbon\Carbon; // Query by billable entity $usage = AiUsage::forBillable($user)->get(); // Query by provider $usage = AiUsage::byProvider('openai')->get(); // Query by model $usage = AiUsage::byModel('gpt-4o-mini')->get(); // Query by feature $usage = AiUsage::byFeature('support_reply')->get(); // Query within a period $start = Carbon::now()->startOfMonth(); $end = Carbon::now()->endOfMonth(); $usage = AiUsage::inPeriod($start, $end)->get(); // Combine scopes $usage = AiUsage::forBillable($user) ->byProvider('openai') ->byFeature('email_reply') ->inPeriod($start, $end) ->get();
Plan Lifecycle
Plan Changes
When a user changes plans mid-period:
use Metrix\AiMetering\Models\AiSubscription; $subscription = AiSubscription::where('billable_id', $user->id)->first(); // Update to new plan $subscription->update([ 'ai_plan_id' => $newPlan->id, 'previous_plan_id' => $oldPlan->id, // Track previous plan ]); // Usage before plan change counts against old plan // Usage after plan change counts against new plan
Subscription Expiration
Check subscription status:
$subscription = AiSubscription::where('billable_id', $user->id)->first(); if ($subscription->isActive()) { // Subscription is active } if ($subscription->isExpired()) { // Subscription has expired } if ($subscription->isInGracePeriod()) { // Subscription is in grace period (still active) } if ($subscription->isInTrial()) { // Subscription is in trial period }
Grace Periods
Configure grace periods in subscriptions:
$subscription = AiSubscription::create([ 'billable_type' => User::class, 'billable_id' => $user->id, 'ai_plan_id' => $plan->id, 'ends_at' => now()->subDay(), // Expired yesterday 'grace_period_ends_at' => now()->addDays(7), // Grace period for 7 days ]);
During grace period, the subscription is still considered active and limits apply.
Period Configuration
The package supports various period types and alignments:
Period Types
monthly: Calendar month (1st to last day) or rolling 30 daysweekly: Calendar week (Monday-Sunday) or rolling 7 daysdaily: Calendar day (00:00-23:59) or rolling 24 hoursyearly: Calendar year or rolling 365 daysrolling: Rolling period from subscription start
Period Alignment
calendar: Period aligns to calendar boundaries (e.g., 1st of month)rolling: Period starts from a specific date and rolls forward
Using Period Class
use Metrix\AiMetering\Support\Period; // Create period from config $period = Period::fromConfig(config('ai-metering.period')); // Get period start/end $start = $period->getStart(); // Current period start $end = $period->getEnd(); // Current period end (exclusive) // Check if date is in period if ($period->contains(Carbon::now())) { // Date is within current period } // Get next/previous periods $nextPeriod = $period->getNext(); $previousPeriod = $period->getPrevious();
Events
Listen to usage events:
use Metrix\AiMetering\Events\AiUsageRecorded; use Metrix\AiMetering\Events\AiLimitApproaching; use Metrix\AiMetering\Events\AiLimitReached; Event::listen(AiUsageRecorded::class, function ($event) { // Handle usage recorded }); Event::listen(AiLimitApproaching::class, function ($event) { // Send notification when approaching limit }); Event::listen(AiLimitReached::class, function ($event) { // Handle limit reached });
Available events:
AiUsageRecorded- When usage is recordedAiLimitApproaching- When usage exceeds 80% of limitAiLimitReached- When hard limit is reachedAiProviderCallFailed- When provider call failsAiOverageCharged- When overage is chargedAiPlanChanged- When plan changesAiCreditsAdded- When credits are addedAiCreditsDeducted- When credits are deductedAiSubscriptionExpired- When subscription expires
Extending Providers
Create custom provider implementations:
namespace App\Providers; use Metrix\AiMetering\Contracts\ProviderClient; use Metrix\AiMetering\Support\ProviderUsage; class CustomProvider implements ProviderClient { public function call(callable $callback): array { $response = $callback(); // Extract usage from response $usage = new ProviderUsage( inputTokens: $response->input_tokens, outputTokens: $response->output_tokens, totalTokens: $response->total_tokens, totalCost: $response->cost, ); return [ 'response' => $response, 'usage' => $usage, ]; } }
Register in config:
'providers' => [ 'custom' => [ 'class' => \App\Providers\CustomProvider::class, ], ],
Performance
Caching
Limit checks are cached by default:
'performance' => [ 'cache_limit_checks' => true, 'cache_ttl' => 300, // seconds ],
Queue Support
Record usage asynchronously for better performance:
'performance' => [ 'queue_usage_recording' => 'default', // Queue name or false ],
Important: If you enable queue recording, make sure your queue worker is running:
php artisan queue:work
Or use a process manager like Supervisor for production environments. See Laravel Queue Documentation for more details.
Batch Recording
Record multiple usages efficiently:
use Metrix\AiMetering\Services\UsageRecorder; $recorder = app(UsageRecorder::class); $usages = [ [ 'billable_type' => User::class, 'billable_id' => $user->id, 'provider' => 'openai', 'model' => 'gpt-4o-mini', 'total_tokens' => 100, 'total_cost' => 0.1, 'occurred_at' => now(), ], // ... more usage records ]; $recorded = $recorder->recordBatch($usages);
Security Best Practices
Dashboard Authorization
Protect dashboard access with Gates:
// In AuthServiceProvider Gate::define('viewAiMetering', function ($user) { return $user->isAdmin(); // Your authorization logic });
Configure in config/ai-metering.php:
'dashboard' => [ 'gate' => 'viewAiMetering', // Gate name for authorization ],
Input Validation
The package validates:
- Feature names (alphanumeric + underscore)
- Token/cost values (non-negative)
- Metadata sanitization
Always validate user input before passing to AiMeter:
$validated = $request->validate([ 'feature' => 'required|string|max:100|regex:/^[a-zA-Z0-9_]+$/', ]);
Data Privacy (GDPR)
Delete all usage for a user:
use Metrix\AiMetering\Models\AiUsage; // Delete all usage records AiUsage::where('billable_type', User::class) ->where('billable_id', $user->id) ->delete(); // Or delete by user_id AiUsage::where('user_id', $user->id)->delete();
Anonymize old usage data:
AiUsage::where('occurred_at', '<', now()->subYears(2)) ->update([ 'billable_type' => null, 'billable_id' => null, 'user_id' => null, 'meta' => null, ]);
Error Handling
The package throws specific exceptions:
use Metrix\AiMetering\Exceptions\AiLimitExceededException; use Metrix\AiMetering\Exceptions\AiCreditsInsufficientException; try { $response = AiMeter::forUser($user)->call(fn () => OpenAI::chat()->create([...])); } catch (AiLimitExceededException $e) { // Handle limit exceeded } catch (AiCreditsInsufficientException $e) { // Handle insufficient credits }
Webhook Handling
Stripe Webhooks
The package integrates with Laravel Cashier's webhook handling. Listen to Cashier events:
// In EventServiceProvider protected $listen = [ \Laravel\Cashier\Events\WebhookReceived::class => [ \App\Listeners\HandleStripeWebhook::class, ], ];
Handle subscription updates:
use Laravel\Cashier\Events\WebhookReceived; use Metrix\AiMetering\Models\AiSubscription; public function handle(WebhookReceived $event) { if ($event->payload['type'] === 'customer.subscription.updated') { // Update AiSubscription based on Stripe subscription $stripeSubscription = $event->payload['data']['object']; AiSubscription::where('stripe_id', $stripeSubscription['id']) ->update([ 'ends_at' => Carbon::createFromTimestamp($stripeSubscription['current_period_end']), ]); } }
API Reference
AiMeter Facade
AiMeter::forUser($user) // Set user for usage tracking ->forTenant($tenant) // Set tenant (optional) ->billable($billable) // Set billable entity ->usingProvider($provider, $model) // Set AI provider and model ->feature($feature) // Set feature name ->billingMode($mode) // Set billing mode ('plan' or 'credits') ->withMeta($meta) // Add metadata (array) ->withIdempotencyKey($key) // Set idempotency key ->withManualUsage($usage) // Set manual usage data ->call($callback) // Execute AI call and record usage
MeteredResponse
$response->getResponse() // Original provider response $response->getUsage() // ProviderUsage object $response->getLimitCheck() // LimitCheckResult object $response->getRemainingTokens() // ?int - Remaining tokens in period $response->getRemainingCost() // ?float - Remaining cost in period $response->isApproachingLimit() // bool - Usage > 80% of limit $response->isLimitReached() // bool - Hard limit reached
ProviderUsage
use Metrix\AiMetering\Support\ProviderUsage; $usage = new ProviderUsage( inputTokens: 100, outputTokens: 200, totalTokens: 300, inputCost: 0.00015, outputCost: 0.00030, totalCost: 0.00045, currency: 'usd' ); // Access properties $usage->inputTokens; // ?int $usage->outputTokens; // ?int $usage->totalTokens; // ?int $usage->inputCost; // ?float $usage->outputCost; // ?float $usage->totalCost; // ?float $usage->currency; // ?string // Convert to array $array = $usage->toArray(); // Create from array $usage = ProviderUsage::fromArray($array);
LimitCheckResult
use Metrix\AiMetering\Support\LimitCheckResult; // Properties $result->allowed; // bool - Is usage allowed? $result->approaching; // bool - Is usage approaching limit (>80%)? $result->hardLimitReached; // bool - Has hard limit been reached? $result->remainingTokens; // ?int - Remaining tokens $result->remainingCost; // ?float - Remaining cost $result->usagePercentage; // float - Usage percentage (0-100) // Factory methods $result = LimitCheckResult::allowed( remainingTokens: 1000, remainingCost: 50.0, usagePercentage: 50.0 ); $result = LimitCheckResult::limitReached(); $result = LimitCheckResult::unlimited();
Model Relationships
// AiSubscription $subscription->plan; // BelongsTo AiPlan $subscription->previousPlan; // BelongsTo AiPlan (previous plan) $subscription->billable; // MorphTo (User, Tenant, etc.) // AiPlan $plan->subscriptions; // HasMany AiSubscription // AiUsage $usage->billable; // MorphTo (User, Tenant, etc.) // AiCreditWallet $wallet->billable; // MorphTo $wallet->transactions; // HasMany AiCreditTransaction // AiCreditTransaction $transaction->wallet; // BelongsTo AiCreditWallet // AiOverage $overage->billable; // MorphTo $overage->usage; // BelongsTo AiUsage (if ai_usage_id set)
Model Methods
// AiPlan $plan->hasUnlimitedTokens(); // bool $plan->hasUnlimitedCost(); // bool $plan->allowsOverage(); // bool // AiSubscription $subscription->isActive(); // bool $subscription->isExpired(); // bool $subscription->isInTrial(); // bool $subscription->isInGracePeriod(); // bool // AiCreditWallet $wallet->addCredits($amount, $reason, $meta); // AiCreditTransaction $wallet->deductCredits($amount, $reason, $meta); // AiCreditTransaction $wallet->hasSufficientBalance($amount); // bool // AiOverage $overage->isSynced(); // bool $overage->markAsSynced($stripeId); // void
Troubleshooting
Common Issues
"Provider 'xxx' is not configured"
Make sure the provider is configured in config/ai-metering.php:
'providers' => [ 'xxx' => [ 'class' => \Your\Provider\Class::class, ], ],
"AI usage limit exceeded" but limits seem fine
- Check if you're using the correct period (calendar vs rolling)
- Verify the subscription is active:
$subscription->isActive() - Clear cache:
php artisan cache:clear - Check for period-specific overrides
Credits not deducting
- Ensure billing mode is set to 'credits':
->billingMode('credits') - Verify wallet exists:
AiCreditWallet::firstOrCreate([...]) - Check if overdraft is allowed in config
- Look for exceptions in logs
Usage not being recorded
- Check if queue is enabled and jobs are processing
- Verify database connection in config
- Check logs for recording errors
- Ensure idempotency keys are unique
Dashboard not accessible
- Verify dashboard is enabled:
'dashboard.enabled' => true - Check middleware configuration
- Verify Gate authorization if configured
- Check route prefix matches config
Debug Mode
Enable detailed logging:
'logging' => [ 'enabled' => true, 'level' => 'debug', // Use 'debug' for detailed logs 'log_failures' => true, ],
Check logs in storage/logs/laravel.log for detailed information.
Health Check
Check package health:
php artisan ai-metering:validate
This validates:
- Configuration
- Database connectivity
- Data integrity
- Orphaned records
Testing
composer test
Run with coverage:
composer test-coverage
Migration Guide
Version 1.0
This is the initial release. No migrations needed.
For future versions, migration guides will be documented here. See CHANGELOG.md for detailed version history.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Before contributing:
- Read the codebase structure
- Follow PSR-12 coding standards
- Write tests for new features
- Update documentation
License
The MIT License (MIT). Please see License File for more information.
Support
For issues and questions, please open an issue on GitHub.