graystackit/laravel-mollie-billing
Batteries-included Mollie billing for Laravel with VAT/OSS compliance, VIES validation, wallet-based metered billing, coupons, trials, scheduled plan changes, webhooks/mandates, admin panel, and a Livewire 4 customer portal—built around a Billable contract.
Installation
composer require graystackit/laravel-mollie-billing
php artisan vendor:publish --tag=mollie-billing-config
php artisan vendor:publish --tag=mollie-billing-migrations
php artisan vendor:publish --tag=mollie-billing-views
php artisan vendor:publish --tag=billing-lang
php artisan migrate
Configure .env
BILLING_MOLLIE_KEY=test_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
BILLING_BILLABLE_MODEL=App\Models\Organization
BILLING_BILLABLE_KEY_TYPE=uuid
BILLING_USER_KEY_TYPE=int
BILLING_CURRENCY=EUR
Define Billable Model
use GraystackIT\MollieBilling\Concerns\HasBilling;
use GraystackIT\MollieBilling\Contracts\Billable;
class Organization implements Billable {
use HasBilling;
public function getUsedBillingSeats(): int { return $this->users()->count(); }
}
Register Routes
Route::middleware(['auth', 'tenant'])->group(fn () => MollieBilling::routes());
Route::middleware(['auth'])->group(fn () => MollieBilling::checkoutRoutes());
Set Up Resolvers
MollieBilling::resolveBillableUsing(fn () => auth()->user()?->currentOrganization);
MollieBilling::authUsing(fn () => auth()->check());
Trigger the checkout flow for a new organization:
$organization = Organization::create(['name' => 'Acme Corp']);
$checkoutUrl = MollieBilling::checkoutUrl($organization, 'basic');
return redirect($checkoutUrl);
Subscription Management
// Create a subscription
$subscription = MollieBilling::createSubscription($billable, 'basic', [
'addons' => ['seats' => 10],
'coupon' => 'SUMMER20'
]);
// Cancel a subscription
$subscription->cancel();
// Schedule a plan change
$subscription->changePlan('pro', now()->addMonth());
Wallet & Metered Billing
// Charge for usage
$wallet = $billable->wallet;
$wallet->charge('api_calls', 100, 'EUR 0.01');
// Refund overage
$wallet->refund('api_calls', 50);
Coupon Application
// Apply a coupon during checkout
$checkoutUrl = MollieBilling::checkoutUrl($billable, 'basic', coupon: 'SUMMER20');
// Apply a coupon to an existing subscription
$subscription->applyCoupon('SUMMER20');
Feature Gating
// Blade directive
@planFeature('analytics')
<div>Advanced Analytics</div>
// Middleware
Route::middleware(['auth', 'billing.feature:analytics'])->group(...);
MollieBilling::webhook() facade method.MollieBilling::adminRoutes() for a pre-built admin interface.MollieBilling::routes().resources/lang/vendor/billing.resources/views/vendor/mollie-billing.// In AppServiceProvider::boot()
MollieBilling::createBillableUsing(function (array $data) {
return Organization::create([
'name' => $data['name'],
'billing_street' => $data['billing_street'],
// ... other fields
]);
});
// Trigger checkout
$billable = Organization::create(['name' => 'Acme Corp']);
$checkoutUrl = MollieBilling::checkoutUrl($billable, 'basic', coupon: 'SUMMER20');
return redirect($checkoutUrl);
Key Type Configuration
billable_key_type and user_key_type must be set before running migrations. Changing them later requires manual column alterations.uuid, ensure your wallets.holder_id column is also uuid.Tenant Resolution
PropagateRouteDefaults middleware for tenant-aware contexts.Route::middleware(['auth', 'tenant'])->group(fn () => MollieBilling::routes());
Route::middleware(['auth'])->group(fn () => MollieBilling::checkoutRoutes());
VAT/OSS Compliance
Wallet Quotas
past_due state. Ensure your getUsedBillingSeats() method accurately reflects usage.Livewire Dependencies
livewire/flux-pro (commercial license). Install separately:
composer require livewire/flux-pro
php artisan billing:check-config to catch syntax errors or broken references.MollieBilling::webhook($request, $payload);
config/mollie-billing.php:
'debug' => env('BILLING_DEBUG', false),
Custom Billable Logic
urlRouteParameters() on your billable model for dynamic URL generation.public function urlRouteParameters(): array {
return ['organization' => $this->slug, 'custom' => 'param'];
}
Checkout Customization
Organization model.public function getCheckoutData(): array {
return array_merge(parent::getCheckoutData(), ['custom_field' => 'value']);
}
Coupon Engine
GraystackIT\MollieBilling\Contracts\Coupon.class CustomCoupon implements Coupon {
public function apply(Billable $billable): void {
// Custom logic
}
}
Admin Panel Overrides
resources/views/vendor/mollie-billing/admin.php artisan vendor:publish --tag=mollie-billing-views
Add the package’s Blade views to your Tailwind content config to avoid purging utility classes:
// vite.config.js
export default defineConfig({
plugins: [
tailwindcss({
content: [
'./resources/views/**/*.blade.php',
'./vendor/graystackit/laravel-mollie-billing/**/*.blade.php',
],
}),
],
});
| Error | Solution |
|---|---|
Call to undefined method |
Ensure HasBilling trait is used and Billable contract is implemented. |
Column not found |
Verify billable_key_type matches your database schema. |
Flux Pro not found |
Install livewire/flux-pro separately. |
Invalid coupon |
Check mollie-billing-plans.php for valid coupon codes. |
Webhook signature mismatch |
Ensure MOLLIE_WEBHOOK_SECRET is set in .env. |
How can I help you explore Laravel packages today?