PayHere is the most widely used payment gateway in Sri Lanka, supporting Visa, Mastercard, and local bank cards. Here's how to integrate it cleanly into a Laravel API.
Setup
Install no extra packages — just configure your credentials in .env:
PAYHERE_MERCHANT_ID=123456
PAYHERE_MERCHANT_SECRET=your_secret_here
PAYHERE_SANDBOX=true
Generating the Hash
PayHere requires a hash for every checkout request to prevent tampering:
$hash = strtoupper(md5(
$merchantId .
$orderId .
number_format($amount, 2, '.', '') .
$currency .
strtoupper(md5($merchantSecret))
));
Webhook Verification
When a payment succeeds, PayHere sends a POST to your notify_url. Verify it before doing anything:
$localHash = strtoupper(md5(
$data['merchant_id'] .
$data['order_id'] .
$data['payhere_amount'] .
$data['payhere_currency'] .
$data['status_code'] .
strtoupper(md5($merchantSecret))
));
if (!hash_equals($localHash, strtoupper($data['md5sig']))) {
return response('Invalid', 400);
}
A status_code of 2 means success. Negative codes indicate failure or cancellation.
Handling the Webhook
Keep webhook handlers idempotent — they may be called more than once:
DB::transaction(function () use ($payment, $data) {
if ($payment->status === 'pending') {
$payment->update([
'status' => 'completed',
'gateway_transaction_id' => $data['payment_id'],
'paid_at' => now(),
]);
// Create enrollment, invoice, etc.
}
});
Going Live
1. Switch PAYHERE_SANDBOX=false
2. Update the merchant ID and secret to your live credentials
3. Ensure your notify_url is HTTPS and publicly accessible
The FullStack LMS project demonstrates this full integration — enroll in the course to see the complete working code.