How to integrate Monobank Pay into a Laravel e-commerce application
Ukrainian businesses running Laravel-powered stores can accept card payments natively through Monobank Pay — without third-party aggregators eating into margins.
Why Monobank Pay makes business sense
Monobank Pay settles funds within one business day and charges a flat 1.5 % MDR on card transactions — competitive against both LiqPay and Portmone. For a store processing ₴1,000,000 monthly in orders, that delta in processing fees compounds quickly.
Beyond cost, the API covers one-time payments, recurring billing, and split payments across multiple merchants — giving product teams room to build subscription models without swapping infrastructure later.
How the integration works in Laravel
The Monobank Acquiring API is REST-based and well-documented. In a Laravel application, integration follows three steps.
Step 1 — Create an invoice
POST a signed request to https://api.monobank.ua/api/merchant/invoice/create. The payload specifies amount (in kopecks), redirect URL, webhook URL, and optional basket items for fiscal compliance. Laravel's Http::withHeaders() handles the X-Token auth header cleanly.
$response = Http::withHeaders([
'X-Token' => config('services.monobank.token'),
])->post('https://api.monobank.ua/api/merchant/invoice/create', [
'amount' => $order->total_kopecks,
'ccy' => 980,
'redirectUrl' => route('checkout.success', $order),
'webHookUrl' => route('webhooks.monobank'),
'items' => $this->buildBasket($order), // for ПРРО
]); Step 2 — Handle the webhook
Monobank pushes a signed payload to your /webhook/monobank route on every status change — created, processing, hold, success, failure, reversed, expired. Verify the X-Sign header against the public key from /api/merchant/pubkey before updating order state.
A queued MonobankWebhookJob keeps your webhook controller thin and prevents timeout issues under load.
Step 3 — Reconcile and refund
Use GET /api/merchant/invoice/status?invoiceId={id} for manual checks and POST /api/merchant/invoice/cancel for full or partial refunds. Both calls belong in a dedicated MonobankPaymentService — keeping business logic out of controllers and making unit testing straightforward.
What to watch for in production
- Idempotency: Generate stable invoice IDs tied to your order UUID to prevent duplicate charges on retries.
- Webhook deduplication: The same event can fire more than once. Guard with a
processed_attimestamp or a Redis lock keyed oninvoiceId + status. - Hold flows: For pre-auth scenarios, capture (
confirm) and void (cancel) are separate API calls — plan the order state machine before writing code. - Fiscal receipts: If your business requires ПРРО compliance, pass
items[]in the invoice payload — Monobank routes the basket to the fiscal register automatically.
Timeline and scope
A clean Monobank Pay integration on an existing Laravel application — including webhook handler, order state machine update, admin refund UI, and test coverage — takes 3–5 business days. Adding ПРРО basket pass-through adds one more day.
Ready to accept payments through Monobank?
MaxiMoruM integrates Monobank Pay, LiqPay, and Portmone into Laravel and WordPress applications. We handle API wiring, webhook security, and fiscal compliance so your team ships faster.