valentin-morice/laravel-billing-repository
Composer 安装命令:
composer require valentin-morice/laravel-billing-repository
包简介
Config-as-code for billing providers (Stripe, Paddle, etc.) in Laravel. Define products, prices, tax rates, webhooks, and billing settings in versioned files, then plan/apply with artisan commands.
README 文档
README
Version-controlled billing configuration with type-safe access to provider IDs.
Define products and prices in PHP config files, deploy them to your billing provider (Stripe), and access provider IDs safely throughout your codebase—no more hardcoded strings or magic values.
Why This Package?
The Problem: Managing billing products/prices requires:
- Hardcoding provider IDs (
price_1ABC...) scattered across your codebase - Manual syncing between provider dashboard and application
- No version control or audit trail for billing changes
- Slow API calls every time you need a price ID
The Solution: This package provides:
- Config-as-code: Define products/prices in versioned PHP files
- DB caching: Fast local lookups without API calls
- Type-safe facade: Access provider IDs using constant keys
- Auto-generated constants: IDE autocomplete and refactor-safe code
- Deploy workflow: Plan and apply billing changes like infrastructure-as-code
Quick Example
1. Define your products in config/billing.php:
'products' => [ 'premium' => [ 'name' => 'Premium Plan', 'prices' => [ 'monthly' => [ 'amount' => 999, 'currency' => 'eur', 'recurring' => ['interval' => 'month'], ], 'yearly' => [ 'amount' => 9900, 'currency' => 'eur', 'recurring' => ['interval' => 'year'], ], ], ], ],
2. Deploy to Stripe and cache locally:
php artisan billing:deploy
This automatically:
- Creates/updates products and prices in Stripe
- Caches them in your local database
- Generates type-safe enums for your products and prices
3. Use type-safe accessors in your code:
use App\Enums\Billing\{ProductKey, PriceKey}; use ValentinMorice\LaravelBillingRepository\Facades\BillingRepository; // Option 1: Direct facade access with string keys $priceId = BillingRepository::priceId('premium', 'monthly'); // Option 2: Type-safe enums (auto-generated after deploy) $priceId = BillingRepository::priceId( ProductKey::Premium, // 'premium' PriceKey::Monthly // 'monthly' ); // Use in your application $user->checkout($priceId, ['quantity' => 1]);
Before vs After:
// ❌ Before: Hardcoded magic strings $checkout = $user->checkout('price_1ABCxyz123', ['quantity' => 1]); // ✅ After: Type-safe, refactor-friendly $checkout = $user->checkout( BillingRepository::priceId(ProductKey::Premium, PriceKey::Monthly), ['quantity' => 1] );
How It Works
1. Database Cache Layer
The package stores products and prices locally in billing_products and billing_prices tables:
- Fast lookups: No API calls needed to get provider IDs
- Reference data: Use as relationships in your models (subscriptions, invoices, etc.)
- Audit trail: Track what's deployed and when
2. Config-as-Code Workflow
Define your billing structure in config/billing.php using simple arrays:
return [ 'provider' => env('BILLING_PROVIDER', 'stripe'), 'api_key' => env('BILLING_API_KEY'), 'products' => [ 'basic' => [ 'name' => 'Basic Plan', 'description' => 'Perfect for individuals', 'prices' => [ 'monthly' => [ 'amount' => 499, 'currency' => 'eur', 'recurring' => ['interval' => 'month'], ], ], ], 'premium' => [ 'name' => 'Premium Plan', 'description' => 'For power users', 'prices' => [ 'monthly' => [ 'amount' => 999, 'currency' => 'eur', 'recurring' => ['interval' => 'month'], ], 'yearly' => [ 'amount' => 9900, 'currency' => 'eur', 'recurring' => ['interval' => 'year'], ], ], ], ], ];
3. Two-Way Sync Commands
billing:import - Pull from provider
Import existing products/prices from your billing provider into the database:
# Import to database only (default) php artisan billing:import --db-only # Import and generate config file from provider php artisan billing:import --generate-config
Use --generate-config when starting with existing Stripe products, or to sync your config with provider changes.
billing:deploy - Push to provider
Deploy config changes to your billing provider and sync to database:
# Preview changes (dry-run) php artisan billing:deploy --dry-run # Deploy changes php artisan billing:deploy # CI/Non-interactive: Auto-archive for immutable field changes php artisan billing:deploy --archive-all # CI/Non-interactive: Auto-duplicate for immutable field changes php artisan billing:deploy --duplicate-all
The command shows a plan of changes (create/update/archive) before applying them.
Handling Immutable Fields:
Stripe prices have immutable fields (amount, currency, recurring interval) that cannot be updated once created. When you modify these fields in your config, the deploy command detects this and prompts you to choose a strategy:
- Archive: Archives the old price in Stripe and creates a new price with the same key. Existing subscriptions using the old price will continue to work, but new subscriptions will use the new price.
- Duplicate: Keeps the old price active and creates a new price with an incremented key (e.g.,
monthly→monthly_1). Useful when you need both prices to remain available.
In CI environments, use --archive-all or --duplicate-all to skip interactive prompts.
4. Auto-Generated Type-Safe Enums
After running billing:deploy or billing:import, the package automatically generates backed enum files in your application:
app/Enums/Billing/ProductKey.php (auto-generated):
namespace App\Enums\Billing; enum ProductKey: string { case Basic = 'basic'; case Premium = 'premium'; }
app/Enums/Billing/PriceKey.php (auto-generated):
namespace App\Enums\Billing; enum PriceKey: string { case Monthly = 'monthly'; case Yearly = 'yearly'; }
Configurable Location:
The enum path and namespace are configurable in config/billing.php:
'enums' => [ 'path' => app_path('Enums/Billing'), 'namespace' => 'App\\Enums\\Billing', ],
Benefits:
- IDE Autocomplete: Your editor suggests available products/prices
- Refactor-Safe: Rename keys without breaking your codebase
- Type Safety: Catch typos at development time, not runtime
- Self-Documenting: See all available products/prices in one place
Enum Case Naming:
- Product keys: Converted to PascalCase (e.g.,
'premium'→Premium) - Price types: Converted to PascalCase (e.g.,
'monthly'→Monthly) - Handles special cases: Separators trigger new words (e.g.,
'pro-plan'→ProPlan) - Avoids collisions: Reserved PHP keywords get
Casesuffix
5. Type-Safe Facade
Access provider IDs throughout your codebase:
use App\Enums\Billing\{ProductKey, PriceKey}; use ValentinMorice\LaravelBillingRepository\Facades\BillingRepository; // Get a specific price ID (with enums) $priceId = BillingRepository::priceId(ProductKey::Premium, PriceKey::Monthly); // Returns: "price_1ABC..." (Stripe price ID) // Or use string keys directly $priceId = BillingRepository::priceId('premium', 'monthly'); // Get a product ID $productId = BillingRepository::productId(ProductKey::Premium); // Returns: "prod_XYZ..." (Stripe product ID) // Use in your application $user->checkout($priceId, ['quantity' => 1]); $user->subscription('default')->swap($priceId);
Error Handling:
use ValentinMorice\LaravelBillingRepository\Exceptions\Models\{ ProductNotFoundException, PriceNotFoundException }; try { $priceId = BillingRepository::priceId('nonexistent', 'monthly'); } catch (ProductNotFoundException $e) { // Product not found or inactive } catch (PriceNotFoundException $e) { // Price not found or inactive for this product }
Additional Facade Methods:
// Access config-level operations $productDef = BillingRepository::config()->product('premium'); $priceDef = BillingRepository::config()->price('premium', 'monthly'); // Access database resources $product = BillingRepository::resource()->product('premium'); // BillingProduct model $prices = BillingRepository::resource()->prices('premium'); // Collection of BillingPrice
Installation
composer require valentin-morice/laravel-billing-repository
Publish config and run migrations:
php artisan vendor:publish --provider="ValentinMorice\LaravelBillingRepository\LaravelBillingRepositoryServiceProvider"
php artisan migrate
Set your billing provider credentials in .env:
BILLING_PROVIDER=stripe BILLING_API_KEY=sk_test_...
Typical Workflows
Starting Fresh
- Define products in
config/billing.php - Run
php artisan billing:deployto create them in Stripe - Enums are auto-generated in
app/Enums/Billing/ - Use
BillingRepository::priceId(ProductKey::Premium, PriceKey::Monthly)in your code
Migrating Existing Stripe Setup
- Run
php artisan billing:import --generate-configto pull existing products - Review and version-control the generated config
- Enums are auto-generated from imported data
- Use enums to replace hardcoded IDs throughout your codebase
Making Changes
- Update
config/billing.php - Run
php artisan billing:deploy --dry-runto preview - Run
php artisan billing:deployto apply - Enums are automatically regenerated
CI/CD Deployment
For automated deployments, use flags to skip interactive prompts:
# Auto-archive old prices when immutable fields change php artisan billing:deploy --archive-all # Auto-duplicate prices when immutable fields change php artisan billing:deploy --duplicate-all
Real-World Example
// config/billing.php 'products' => [ 'starter' => [ 'name' => 'Starter Plan', 'prices' => [ 'monthly' => [ 'amount' => 999, 'currency' => 'eur', 'recurring' => ['interval' => 'month'], ], ], ], 'pro' => [ 'name' => 'Pro Plan', 'prices' => [ 'monthly' => [ 'amount' => 2999, 'currency' => 'eur', 'recurring' => ['interval' => 'month'], ], 'yearly' => [ 'amount' => 29900, 'currency' => 'eur', 'recurring' => ['interval' => 'year'], ], ], ], ],
After php artisan billing:deploy, use everywhere:
// Checkout controller public function checkout(Request $request) { $plan = $request->input('plan'); // 'starter' or 'pro' $interval = $request->input('interval'); // 'monthly' or 'yearly' $priceId = BillingRepository::priceId($plan, $interval); return $request->user()->checkout($priceId, [ 'success_url' => route('checkout.success'), 'cancel_url' => route('checkout.cancel'), ]); } // Subscription management public function upgrade(User $user) { $user->subscription('default')->swap( BillingRepository::priceId(ProductKey::Pro, PriceKey::Yearly) ); } // Pricing page public function pricing() { return view('pricing', [ 'starterMonthly' => BillingRepository::priceId(ProductKey::Starter, PriceKey::Monthly), 'proMonthly' => BillingRepository::priceId(ProductKey::Pro, PriceKey::Monthly), 'proYearly' => BillingRepository::priceId(ProductKey::Pro, PriceKey::Yearly), ]); }
Provider Support
Currently supported:
- Stripe (ships with package)
The package uses a provider adapter pattern—adding new providers (Paddle, PayPal) requires implementing the ProviderAdapter interface.
Requirements
- PHP ^8.3
- Laravel ^11.31 or ^12.0
Testing
composer test
composer test-coverage
composer format
composer analyse
License
The MIT License (MIT). Please see License File for more information.
valentin-morice/laravel-billing-repository 适用场景与选型建议
valentin-morice/laravel-billing-repository 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 0 次下载、GitHub Stars 达 6, 最近一次更新时间为 2026 年 01 月 23 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「stripe」 「laravel」 「billing」 「Valentin Morice」 「laravel-billing-repository」 「config-as-code」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 valentin-morice/laravel-billing-repository 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 valentin-morice/laravel-billing-repository 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 valentin-morice/laravel-billing-repository 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Flexible payment integration for Laravel, lunarphp, supporting multiple payment providers.
Laravel Cashier provides an expressive, fluent interface to Braintree's subscription billing services.
Metamel Addresses is a polymorphic Laravel package, for address book management. You can add addresses to any eloquent model with ease.
Rich payment solutions for Laravel framework. Paypal, payex, authorize.net, be2bill, omnipay, recurring paymens, instant notifications and many more
Alfabank REST API integration
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 6
- 点击次数: 41
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-01-23