emeroid / laravel-billing-core
A robust, driver-based, multi-gateway billing package for Laravel. Supports Paystack, PayPal, plan swapping, and dunning.
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:laravel-package
pkg:composer/emeroid/laravel-billing-core
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.0
- illuminate/database: ^10.0 || ^11.0
- illuminate/http: ^10.0 || ^11.0
- illuminate/routing: ^10.0 || ^11.0
- illuminate/support: ^10.0 || ^11.0
- paypal/paypal-checkout-sdk: ^1.0
Requires (Dev)
- orchestra/testbench: ^8.0 || ^9.0
- phpunit/phpunit: ^10.0
This package is auto-updated.
Last update: 2025-11-11 10:57:58 UTC
README
Laravel Billing Core is a robust, driver-based, multi-gateway billing package for Laravel. It provides a simple, fluent API to manage one-time payments, subscriptions, plan swapping, and dunning logic for your SaaS application.
Stop rebuilding billing logic for every project β this package is the plug-and-play foundation you need.
π Features
- Driver-Based: Switch gateways just by changing your
.env. - Multi-Gateway: Out-of-the-box support for Paystack and PayPal.
- Subscription Management: A complete subscription lifecycle (
subscribe,cancel,swapPlan). - Grace Periods: Automatically handles βcancel on end of billing periodβ.
- Plan Swapping: Fluent API to upgrade or downgrade users.
- Dunning: Listens for
invoice.payment_failedwebhooks to setpast_duestatus. - Event-Based: Fires events like
SubscriptionStarted,SubscriptionCancelled, etc. - Billable Trait: A powerful interface you can add to your
Usermodel.
π§© Installation
composer require emeroid/laravel-billing-core
1. Configuration
Publish the Config File
php artisan vendor:publish --provider="Emeroid\Billing\BillingServiceProvider" --tag="billing-config"
This creates
config/billing.php.
Publish the Migrations
php artisan vendor:publish --provider="Emeroid\Billing\BillingServiceProvider" --tag="billing-migrations"
This adds the plans, subscriptions, and transactions tables.
Run the Migrations
php artisan migrate
Update Your .env File
# --- BILLING CORE --- BILLING_DEFAULT_DRIVER=paystack BILLING_MODEL=\App\Models\User # --- PAYSTACK --- PAYSTACK_PUBLIC_KEY=pk_... PAYSTACK_SECRET_KEY=sk_... # --- PAYPAL --- PAYPAL_CLIENT_ID=... PAYPAL_SECRET=... PAYPAL_MODE=sandbox PAYPAL_WEBHOOK_ID=WH-...
Add the Billable Trait
// app/Models/User.php use Emeroid\Billing\Traits\Billable; use Illuminate{...}; class User extends Authenticatable { use Billable, HasFactory, Notifiable; // ... }
π³ Usage
2. Creating Plans
Before creating subscriptions, define your plans in the plans table β typically via a seeder:
// database/seeders/PlanSeeder.php use Emeroid\Billing\Models\Plan; use Illuminate\Database\Seeder; class PlanSeeder extends Seeder { public function run(): void { Plan::create([ 'name' => 'Pro Plan', 'slug' => 'pro-plan', 'amount' => 500000, // 5000 NGN (in kobo) 'interval' => 'monthly', 'paystack_plan_id' => 'PL_abc123', 'paypal_plan_id' => 'P-xyz456', ]); Plan::create([ 'name' => 'Business Plan', 'slug' => 'business-plan', 'amount' => 1000000, // 10000 NGN 'interval' => 'monthly', 'paystack_plan_id' => 'PL_def789', 'paypal_plan_id' => 'P-ghi123', ]); } }
3. One-Time Payments
use Emeroid\Billing\Facades\Billing; use Illuminate\Http\Request; class PaymentController { public function startPayment(Request $request) { $user = $request->user(); $amountInKobo = 50000; // 5000 NGN try { $payment = Billing::purchase($amountInKobo, $user->email, [ 'user_id' => $user->id, 'currency' => 'NGN', ]); return redirect()->away($payment['authorization_url']); } catch (\Emeroid\Billing\Exceptions\PaymentInitializationFailedException $e) { return back()->with('error', $e->getMessage()); } } }
4. Subscriptions
use Emeroid\Billing\Facades\Billing; use Emeroid\Billing\Models\Plan; use Illuminate\Http\Request; class SubscriptionController { public function startSubscription(Request $request) { $user = $request->user(); $plan = Plan::where('slug', 'pro-plan')->firstOrFail(); $gatewayPlanId = config('billing.default') === 'paypal' ? $plan->paypal_plan_id : $plan->paystack_plan_id; try { $subscription = Billing::subscribe( $gatewayPlanId, $user->email, [ 'amount' => $plan->amount, 'user_id' => $user->id, 'currency' => 'NGN', ] ); return redirect()->away($subscription['authorization_url']); } catch (\Emeroid\Billing\Exceptions\PaymentInitializationFailedException $e) { return back()->with('error', $e->getMessage()); } } }
5. Handling Callbacks
After payment, users are redirected to your site.
The built-in CallbackController handles:
- Verifying the transaction via
Billing::verifyTransaction(...) - Creating the Subscription record
- Firing events (
TransactionSuccessful,SubscriptionStarted) - Redirecting to success/failure URLs (defined in
config/billing.php)
All of this happens automatically.
6. Handling Webhooks
Add these URLs to your gateway dashboards:
https://your-app.com/billing-webhooks/paystack
https://your-app.com/billing-webhooks/paypal
The package automatically handles:
-
charge.successβ verifies payments -
subscription.createβ creates subscriptions -
subscription.disableβ triggersSubscriptionCancelled -
Dunning events:
invoice.payment_failed(Paystack)BILLING.SUBSCRIPTION.PAYMENT.FAILED(PayPal)
7. The Billable Trait API
$user = auth()->user(); // STATUS CHECKS $user->isSubscribed(); $user->onGracePeriod(); $user->hasActiveSubscription(); $user->isSubscribedTo('pro-plan'); $user->isPastDue(); // MANAGEMENT $subscription = $user->getSubscription('SUB_abc'); $user->cancelSubscription($subscription->gateway_subscription_id); $user->swapPlan($subscription->gateway_subscription_id, 'business-plan'); $user->syncSubscription($subscription->gateway_subscription_id);
8. Events
Listen for billing events in your EventServiceProvider:
// app/Providers/EventServiceProvider.php use Emeroid\Billing\Events\{ TransactionSuccessful, SubscriptionStarted, SubscriptionCancelled, SubscriptionPlanSwapped, SubscriptionPaymentFailed }; protected $listen = [ TransactionSuccessful::class => [ 'App\Listeners\GrantAccessToProduct', 'App\Listeners\SendInvoiceEmail', ], SubscriptionStarted::class => [ 'App\Listeners\ActivateProFeatures', ], SubscriptionCancelled::class => [ 'App\Listeners\RevokeProFeaturesAtPeriodEnd', ], SubscriptionPlanSwapped::class => [ 'App\Listeners\HandlePlanSwap', ], SubscriptionPaymentFailed::class => [ 'App\Listeners\SendDunningEmail', ], ];
π§ͺ Testing
composer test
π€ Contributing
Please see CONTRIBUTING.md for details.
π Security
If you discover any security-related issues, please email π§ threalyongbug@gmail.com instead of using the issue tracker.
π License
Licensed under the MIT License (MIT). See the License File for more information.
β€οΈ Sponsorship
This project is free and open-source. If it helps you build your business, please consider supporting its development.
Sponsor @emeroid on GitHub.