How to Integrate PayHere Payment Gateway with Symfony PHP Framework
Register with PayHere and Get your Credentials
Register for a PayHere Sandbox Account
Before integrating PayHere, you need to register for a sandbox account to test the payment gateway.
- Go to the PayHere Sandbox Registration page.
- Fill in the registration form and submit it.
- Once registered, log in to your sandbox account.
Create a Merchant Account and Obtain Credentials
- After logging in, go to the “Merchant Settings” section.
- Here, you will need to create a new merchant by specifying your domain. This will generate your Merchant ID and Merchant Secret.
- Note down these credentials as you will need them for the integration.
Add Domain for Testing
- In the “Merchant Settings,” add your local development domain (e.g.,
http://localhost:8000
or any other domain you use for development). - Ensure that the domain is correctly added and configured.
Configure PayHere in Symfony
Add your PayHere Merchant ID and Secret Key to your Symfony environment variables.
In your .env
file, add:
PAYHERE_MERCHANT_ID=your_merchant_id
PAYHERE_SECRET_KEY=your_secret_key
Replace your_merchant_id
and your_secret_key
with the credentials you obtained from the PayHere sandbox.
Create Payment Controller
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
class PaymentController extends AbstractController
{
/**
* @Route("/pay", name="pay")
*/
public function pay(Request $request): Response
{
$amount = $request->request->get('amount');
$order_id = uniqid();
$merchant_id = $_ENV['PAYHERE_MERCHANT_ID'];
$secret_key = $_ENV['PAYHERE_SECRET_KEY'];
// Here, you should save the order details to your database, setting the status to 'pending'.
// Example:
// $order = new Order();
// $order->setId($order_id);
// $order->setAmount($amount);
// $order->setStatus('pending');
// $entityManager = $this->getDoctrine()->getManager();
// $entityManager->persist($order);
// $entityManager->flush();
// Prepare PayHere form data
$form_data = [
'merchant_id' => $merchant_id,
'return_url' => $this->generateUrl('pay_return', [], true),
'cancel_url' => $this->generateUrl('pay_cancel', [], true),
'notify_url' => $this->generateUrl('pay_notify', [], true),
'order_id' => $order_id,
'items' => 'Test Item',
'currency' => 'LKR',
'amount' => $amount,
'first_name' => 'John',
'last_name' => 'Doe',
'email' => 'john.doe@example.com',
'phone' => '0771234567',
'address' => 'No.1, Galle Road',
'city' => 'Colombo',
'country' => 'Sri Lanka',
];
return $this->render('payment/pay.html.twig', [
'form_data' => $form_data,
]);
}
/**
* @Route("/pay/notify", name="pay_notify", methods={"POST"})
*/
public function notify(Request $request): Response
{
$merchant_id = $request->request->get('merchant_id');
$order_id = $request->request->get('order_id');
$payhere_amount = $request->request->get('payhere_amount');
$payhere_currency = $request->request->get('payhere_currency');
$status_code = $request->request->get('status_code');
$md5sig = $request->request->get('md5sig');
$secret = $_ENV['PAYHERE_SECRET_KEY'];
$local_md5sig = strtoupper(md5($merchant_id . $order_id . $payhere_amount . $payhere_currency . $status_code . strtoupper(md5($secret))));
if ($md5sig === $local_md5sig) {
// Payment status
$entityManager = $this->getDoctrine()->getManager();
$order = $entityManager->getRepository(Order::class)->find($order_id);
if ($status_code == '2') {
// Payment success, update order status
$order->setStatus('completed');
} else {
// Payment failed or cancelled, update order status
$order->setStatus('failed');
}
$entityManager->persist($order);
$entityManager->flush();
}
return new Response('OK');
}
/**
* @Route("/pay/return", name="pay_return")
*/
public function return(Request $request): Response
{
$order_id = $request->query->get('order_id');
$status_code = $request->query->get('status_code');
if ($status_code == '2') {
// Update order status to completed
$entityManager = $this->getDoctrine()->getManager();
$order = $entityManager->getRepository(Order::class)->find($order_id);
$order->setStatus('completed');
$entityManager->persist($order);
$entityManager->flush();
return new Response('Payment Successful!');
} else {
return new Response('Payment Failed.');
}
}
/**
* @Route("/pay/cancel", name="pay_cancel")
*/
public function cancel(Request $request): Response
{
$order_id = $request->query->get('order_id');
// Update order status to cancelled
$entityManager = $this->getDoctrine()->getManager();
$order = $entityManager->getRepository(Order::class)->find($order_id);
$order->setStatus('cancelled');
$entityManager->persist($order);
$entityManager->flush();
return new Response('Payment Cancelled.');
}
}
Make notify_url publically accessible (can’t use “localhost”)
If your application uses authentication, you need to exclude the notify_url
from authentication or allow it to be accessed anonymously.
Exclude
notify_url
from Authentication
In your security.yaml
configuration file, you can define the path to be publicly accessible:
security:
# other configurations
firewalls:
# other firewalls
dev:
pattern: ^/pay/notify$
security: false
# other firewalls
access_control:
- { path: ^/pay/notify$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Allow Anonymous Access
Alternatively, you can configure the notify_url
route to allow anonymous access:
security:
# other configurations
firewalls:
main:
anonymous: true
# other configurations
access_control:
- { path: ^/pay/notify$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Create Payment Form View
Create a Twig template to render the PayHere payment form.
After the file is rendered, automatically open the PayHere payment gateway popup.
<!DOCTYPE html>
<html>
<head>
<title>PayHere Payment</title>
</head>
<body>
<h1>Make a Payment</h1>
<form id="payhere" method="post" action="https://sandbox.payhere.lk/pay/checkout">
<input type="hidden" name="merchant_id" value="{{ form_data.merchant_id }}">
<input type="hidden" name="return_url" value="{{ form_data.return_url }}">
<input type="hidden" name="cancel_url" value="{{ form_data.cancel_url }}">
<input type="hidden" name="notify_url" value="{{ form_data.notify_url }}">
<input type="hidden" name="order_id" value="{{ form_data.order_id }}">
<input type="hidden" name="items" value="{{ form_data.items }}">
<input type="hidden" name="currency" value="{{ form_data.currency }}">
<input type="hidden" name="amount" value="{{ form_data.amount }}">
<input type="hidden" name="first_name" value="{{ form_data.first_name }}">
<input type="hidden" name="last_name" value="{{ form_data.last_name }}">
<input type="hidden" name="email" value="{{ form_data.email }}">
<input type="hidden" name="phone" value="{{ form_data.phone }}">
<input type="hidden" name="address" value="{{ form_data.address }}">
<input type="hidden" name="city" value="{{ form_data.city }}">
<input type="hidden" name="country" value="{{ form_data.country }}">
<input type="submit" value="Buy Now" style="display: none;">
</form>
</body>
</html>
<script>
$(document).ready(function () {
setTimeout(() => {
$('form#payhere').submit();
}, 500);
});
</script>
You have now successfully integrated the PayHere payment gateway with your Symfony application. This guide provides a basic integration. Depending on your requirements, you might need to handle additional scenarios such as different currencies, recurring payments, and more comprehensive error handling. Be sure to consult the PayHere API documentation for more details.
Good Luck With Your PayHere Integration!