mollie/laravel-mollie
Laravel wrapper for Mollie’s payments API. Easily create payments, handle redirects and webhooks, manage customers, subscriptions and refunds, and keep Mollie configuration and API key setup integrated with your Laravel app via a simple, idiomatic package.
Installation
composer require mollie/laravel-mollie
Publish the config file:
php artisan vendor:publish --provider="Mollie\LaravelMollie\MollieServiceProvider" --tag="mollie-config"
Configuration
Add your Mollie API key to .env:
MOLLIE_KEY=test_XXXXXXXXXXXXXXXXXXXXXXXXXXXX
Set your preferred locale (e.g., nl_NL, en_US) in config/mollie.php.
First Use Case: Create a Payment
use Mollie\LaravelMollie\Facades\Mollie;
$payment = Mollie::payments()->create([
'amount' => [
'currency' => 'EUR',
'value' => '10.00',
],
'description' => 'Order #12345',
'method' => 'ideal', // or 'creditcard', 'paypal', etc.
'redirectUrl' => route('payment.success'),
]);
return redirect()->to($payment->getCheckoutUrl());
Webhook Setup
Add the Mollie webhook endpoint to your routes/web.php:
Route::post('/mollie/webhook', [MollieWebhookController::class, 'handleWebhook']);
Register the controller in app/Providers/RouteServiceProvider.php:
public function boot()
{
$this->routes(function () {
Route::post('/mollie/webhook', [MollieWebhookController::class, 'handleWebhook'])
->middleware('signed'); // Optional: Add CSRF protection
});
}
Dynamic Payments Use a service class to abstract payment logic:
class PaymentService {
public function createOrderPayment(Order $order)
{
return Mollie::payments()->create([
'amount' => [
'currency' => $order->currency,
'value' => $order->total,
],
'description' => 'Order #' . $order->id,
'metadata' => ['order_id' => $order->id],
'webhookUrl' => route('mollie.webhook'),
]);
}
}
Subscription Handling For recurring payments:
$subscription = Mollie::subscriptions()->create([
'amount' => ['currency' => 'EUR', 'value' => '9.99'],
'interval' => 'month',
'times' => null, // Unlimited
'metadata' => ['customer_id' => $user->id],
'webhookUrl' => route('mollie.webhook'),
]);
Event-Driven Logic Handle webhooks in a dedicated controller:
class MollieWebhookController extends Controller {
public function handleWebhook(Request $request)
{
$event = Mollie::webhooks()->handle($request->getContent());
switch ($event->type) {
case 'payment.authorized':
$this->handleAuthorizedPayment($event->data->id);
break;
case 'payment.paid':
$this->fulfillOrder($event->data->id);
break;
case 'payment.failed':
$this->handleFailedPayment($event->data->id);
break;
}
return response()->json(['status' => 'success']);
}
}
Idempotency
Use the idempotencyKey in API calls to avoid duplicate processing:
$payment = Mollie::payments()->create([
'amount' => ['currency' => 'EUR', 'value' => '10.00'],
'idempotencyKey' => 'unique-key-' . Str::uuid(),
]);
config/services.php):
'mollie' => [
'client_id' => env('MOLLIE_CONNECT_CLIENT_ID'),
'client_secret' => env('MOLLIE_CONNECT_CLIENT_SECRET'),
'redirect' => env('MOLLIE_CONNECT_REDIRECT_URI'),
],
Use in a controller:
public function redirectToMollieConnect()
{
return Socialite::driver('mollie')->redirect();
}
public function handleMollieConnectCallback()
{
$user = Socialite::driver('mollie')->user();
// Attach user data to your app's user model
}
Refund a Payment
$payment = Mollie::payments()->get('p_XXXXXXXXXXXXXXXXXXXXXXXXXXXX');
$refund = $payment->refunds()->create([
'amount' => ['currency' => 'EUR', 'value' => '5.00'],
'description' => 'Partial refund',
]);
Capture a Payment
$payment = Mollie::payments()->get('p_XXXXXXXXXXXXXXXXXXXXXXXXXXXX');
$payment->capture();
test_ API keys) and test cards (e.g., 4242 4242 4242 4242).$this->post('/mollie/webhook', $webhookPayload)
->assertStatus(200);
config/mollie.php:
'debug' => env('APP_ENV') === 'local',
event(new PaymentProcessed($payment));
Mollie::setLocale($user->locale); // e.g., 'nl_NL'
try {
$payment = Mollie::payments()->create([...]);
} catch (\Mollie\Api\Exceptions\ApiException $e) {
Log::error('Mollie API Error: ' . $e->getMessage());
return back()->with('error', 'Payment failed. Please try again.');
}
Issue: Mollie webhooks must be verified using the id and signature headers.
Fix: Use the built-in handle() method, which automatically verifies the webhook:
$event = Mollie::webhooks()->handle($request->getContent());
Manual Verification (if needed):
$isValid = Mollie::webhooks()->verify($request->getContent(), $request->header('X-Mollie-Signature'));
idempotencyKey can lead to duplicate payments.
Fix: Always include a unique key (e.g., order ID or UUID) for critical operations.number_format or bcdiv for amounts:
$amount = number_format($order->total, 2, '.', '');
ProcessPayment::dispatch($event->data->id)->onQueue('mollie');
payments.read):
$user = Socialite::driver('mollie')->scopes(['payments.read'])->user();
'debug' => true in config/mollie.php to log API requests/responses.dd() to inspect raw responses:
$payment = Mollie::payments()->create([...]);
dd($payment->toArray());
How can I help you explore Laravel packages today?