Laravel Cashier for Mollie
repository·main·Indexed 41 days ago
https://github.com/mollie/laravel-cashier-mollieAn official integration providing an expressive, fluent interface for managing subscription billing cycles, payments, and customer relationships in Laravel applications using Mollie's billing services. Supports subscription plans, coupons, trial periods (including generic trials and mandate-upfront trials), and one-time charges.
What's inside Laravel Cashier for Mollie
- Laravel Cashier for Mollie provides an expressive and fluent interface for managing subscription billing within Laravel applications using Mollie's billing services. It bridges the gap between Laravel's subscription logic and Mollie's payment infrastructure.
Manage customer balance
mainMollie maintains a customer balance for each currency. This balance is automatically applied to Orders (for example, when a customer has overpaid due to a plan swap).
If an Order is processed with a negative total amount due, that amount is automatically credited to the user's balance. If the user has no active subscriptions at that moment, a
BalanceTurnedStaleevent is raised. You should listen for this event to handle refunds or user notifications.Warning: Use the direct balance manipulation methods with caution.
// Check credit for a specific currency $credit = $user->credit('EUR'); // Manually add credit $user->addCredit(money(1000, 'EUR')); // adds €10.00 // Check if user has credit $user->hasCredit('EUR');How failed subscription payments are handled
mainWhen a payment related to a subscription fails, Cashier automatically cancels the subscription immediately.
If the payment is successfully retried (e.g., via
retryNow()), the subscription will be reactivated and will continue its previous billing cycle as if the failure had not occurred.List all invoices for a user
mainYou can access all invoices associated with a user by accessing their orders via
$user->orders->invoices(). This collection includes invoices for all orders, including those that are unprocessed or failed.// Accessing all invoices through the orders relationship $invoices = $user->orders->invoices();How subscription management works in Cashier Mollie
mainUnlike standard Mollie subscriptions, Cashier Mollie uses its own subscription engine to schedule payments from the client side. This allows for features not supported by Mollie's native Subscription API.
The Subscription Lifecycle:
- Creation: A
Subscriptionis created using eitherMandatePaymentSubscriptionBuilder(redirects to Mollie checkout to create aMandate) orPremandatedSubscriptionBuilder(uses an existingMandate). - Scheduling: The
Subscriptiongenerates a scheduledOrderItemat the start of every billing cycle. - Bundling: A scheduled job (typically daily) pre-processes
OrderItems(applying dynamic discounts or metered billing) and bundles them intoOrdersso customers receive a single invoice for multiple items. - Payment Processing: The scheduled job processes the
Order:- Customer balance is applied first.
- If the total is positive, a Mollie payment is triggered.
- If the total is 0, no action is taken.
- If the total is negative, the amount is added to the user's balance. If no active subscriptions remain, a
BalanceTurnedStaleevent is raised.
- Invoicing: You can generate HTML or PDF
Invoicedocuments for the user.
- Creation: A
How prorating is handled in Cashier Mollie
mainCashier Mollie applies prorating by default when a subscription quantity is updated or a plan is switched.
Prorating Process:
- The billing cycle is reset.
- The customer is credited for unused time; the overpaid amount is added to the customer's balance.
- A new billing cycle starts with the new settings. An
Orderis generated, applying the credited balance to the new total.
Note: This does not apply to
$subscription->swapNextCycle('other-plan'). That method waits until the end of the current billing cycle to update the plan, which is useful for downgrades.Handle Mollie webhook events in Laravel
mainCashier Mollie automatically handles default Mollie webhook logic, such as cancelling a subscription if a recurring charge fails.
To implement application-specific behavior (e.g., granting access to a user or notifying an admin), you should listen for specific events provided by the package in the
Laravel\ ext{Cashier}\\text{Events}namespace.Available events for custom logic:
OrderPaymentPaid: Triggered when an order payment succeeds.OrderPaymentFailed: Triggered when an order payment fails.FirstPaymentPaid: Triggered when the initial payment for a subscription succeeds.FirstPaymentFailed: Triggered when the initial payment for a subscription fails.
Prepare the Billable Model
mainTo make a model (typically the
Usermodel) billable, you must perform the following steps:- Add the
Laravel\ Cashier\\Billabletrait to the model. - (Optional) Override
mollieCustomerFields()to define which model fields should be sent to Mollie when creating a Customer. - Implement the
Laravel\\Cashier\\Order\\Contracts\\ProvidesInvoiceInformationinterface to provide data for invoices.
Example implementation:
use Laravel\Cashier\Billable; class User extends Authenticatable { use Billable; public function mollieCustomerFields() { return [ 'email' => $this->email, 'name' => $this->name, ]; } /** * Get the receiver information for the invoice. * @return array An array of strings */ public function getInvoiceInformation() { return [$this->name, $this->email]; } /** * Get additional information to be displayed on the invoice. * @return string|null */ public function getExtraBillingInformation() { return null; } }public function mollieCustomerFields() { return [ 'email' => $this->email, 'name' => $this->name, ]; } public function getInvoiceInformation() { return [$this->name, $this->email]; } public function getExtraBillingInformation() { return null; }- Add the
Translate Cashier translation strings
mainCashier uses English translation strings by default (e.g., for cycle definitions when order items are created). You can translate these using Laravel's standard localization feature by following these steps:
- Publish the default translation files: Run the following Artisan command to copy the English translations to your application's resources directory.
- Create your language folder: Create a new directory in
resources/lang/vendor/cashier/named after your desired locale (e.g.,resources/lang/vendor/cashier/fr). - Add translations: Place your translated JSON or PHP translation files inside that new folder.
When your application's locale is set to the language you created, Cashier will automatically use your custom translations instead of the defaults.
php artisan vendor:publish --tag=cashier-translationsHandle processed refunds and credit invoices
mainWhen Mollie successfully processes a refund, Cashier dispatches the
RefundProcessedevent. You can use this event to access the automatically generated credit order and its corresponding invoice receipt.Once you have the invoice object, you can:
$invoice->view(): Retrieve a Blade view.$invoice->pdf(): Retrieve a PDF of the Blade view.$invoice->download(): Return a download response for the PDF.
Credit orders and invoices are treated as regular Cashier orders and invoices, meaning they will appear in the user's standard order and invoice lists (e.g.,
$user->ordersor$user->orders->invoices()).$creditOrder = $refundProcessedEvent->refund->order; $invoice = $creditOrder->invoice(); $invoice->view(); // get a Blade view $invoice->pdf(); // get a pdf of the Blade view $invoice->download(); // get a download response for the pdfImplement metered billing with Subscription OrderItem preprocessors
mainFor business cases requiring dynamic subscription amounts, Cashier Mollie provides a mechanism to define custom
Subscription OrderItempreprocessors.These preprocessors are invoked immediately before an
OrderItemis processed into a Mollie payment. This allows you to calculate the final billing amount dynamically based on real-time usage statistics. You can also use this hook to reset usage counters for the next billing cycle.To implement this, you must define your custom preprocessor classes in the
cashier_plansconfiguration file.// In your cashier_plans config file, define your preprocessors: 'preprocessors' => [ App\Billing\MyMeteredUsagePreprocessor::class, ],Offer generic trials without mandate upfront
mainA "generic trial" allows you to offer a trial period without collecting payment information immediately. This is achieved by setting a
trial_ends_atvalue on the user record, typically during registration.Note: You must add a date mutator for the
trial_ends_atcolumn in your Eloquent model definition to ensure it is handled correctly as a date object.$user = User::create([ // Populate other user properties... 'trial_ends_at' => now()->addDays(10), ]);