Accepting online payments is the foundation of any Ukrainian e-commerce or SaaS product. LiqPay — Privatbank's payment gateway — supports cards, Apple Pay, Google Pay, and BNPL instalments, all under a single API. Integrating it into a Laravel application takes less than one working day when the architecture is clear from the start.
Why LiqPay fits Laravel projects
Laravel's service container and event system make payment integrations clean and testable. You encapsulate LiqPay API calls inside a dedicated service class, fire events on payment success or failure, and let queued jobs handle downstream workflows — order fulfilment, invoice generation, and customer notifications. The result is a system that is easy to test, easy to monitor, and easy to extend.
The integration path
1. Register and store credentials
Sign up at liqpay.ua, obtain your public_key and private_key, and store them in .env. Never hard-code credentials in source files — they end up in version control and become a security liability.
2. Build a LiqPayService class
LiqPay signs every request with a SHA-1 hash of base64(json_data) + private_key. Centralising this logic in a service class keeps your controllers thin and your signing logic testable:
class LiqPayService
{
public function createPayment(Order $order): string
{
$data = base64_encode(json_encode([
'version' => 3,
'public_key' => config('liqpay.public_key'),
'action' => 'pay',
'amount' => $order->total,
'currency' => 'UAH',
'description' => "Order #{$order->id}",
'order_id' => $order->uuid,
'result_url' => route('orders.thank-you'),
'server_url' => route('webhooks.liqpay'),
]));
$signature = base64_encode(sha1(
config('liqpay.private_key') . $data . config('liqpay.private_key'),
true
));
return view('payments.liqpay-form', compact('data', 'signature'))->render();
}
} 3. Verify and process the webhook
LiqPay posts a signed callback to your server_url after every transaction. Always verify the signature before acting on the payload — an unsigned callback is untrusted data:
public function handle(Request $request): JsonResponse
{
$data = $request->input('data');
$signature = $request->input('signature');
$expected = base64_encode(sha1(
config('liqpay.private_key') . $data . config('liqpay.private_key'),
true
));
abort_if($signature !== $expected, 403);
$payload = json_decode(base64_decode($data), true);
if ($payload['status'] === 'success') {
ProcessPaidOrder::dispatch($payload['order_id']);
}
return response()->json(['ok' => true]);
} 4. Dispatch a queue job for downstream work
Your webhook controller must return a response in milliseconds — LiqPay will retry if it times out. Move order fulfilment, stock updates, and transactional emails into a ProcessPaidOrder queued job. Laravel retries failed jobs automatically, and you get a full audit trail via the failed_jobs table.
Business outcomes
- Payments complete end-to-end in under 2 seconds
- Webhook delivery failures handled automatically by Laravel's retry queue
- Clean separation of concerns: controller handles HTTP, service handles signing, job handles business logic
- Full audit trail via Laravel logging and the failed_jobs table
- Ready to extend: refunds, subscriptions, and instalments reuse the same service class
Proven in production
MaxiMoruM has delivered LiqPay integrations for Ukrainian e-commerce clients on Laravel — from single-product landing pages to multi-vendor marketplaces. The service pattern above is our standard starting point, refined across dozens of production deployments.
Ready to add reliable Ukrainian payment processing to your project? Contact MaxiMoruM at maximorum.com — we scope and deliver integrations in days, not weeks.