zfhassaan/easypaisa
Unofficial Laravel package for integrating Easypaisa payments with Direct (REST API) and Hosted Checkout (redirect) flows. Includes config publishing, .env settings for sandbox/production, credentials, hash keys, callback URL, and helper methods to send payment requests.
Installation
composer require zfhassaan/easypaisa
Publish the config file:
php artisan vendor:publish --provider="Zfhassaan\EasyPaisa\EasyPaisaServiceProvider" --tag=easypaisa-config
Configure .env
Add your EasyPaisa credentials:
EASYPAISA_MERCHANT_ID=your_merchant_id
EASYPAISA_SECRET_KEY=your_secret_key
EASYPAISA_API_URL=https://api.easypaisa.com.pk/api/
First Use Case: Initiate a Payment
use Zfhassaan\EasyPaisa\Facades\EasyPaisa;
$response = EasyPaisa::initiatePayment([
'amount' => 100.00,
'order_id' => 'ORDER123',
'customer_name' => 'John Doe',
'customer_email' => 'john@example.com',
'customer_phone' => '03001234567',
'callback_url' => route('easypaisa.callback'),
]);
if ($response->success()) {
return redirect()->to($response->getPaymentUrl());
}
Callback Handling Add a route and controller method to handle the callback:
Route::post('/easypaisa/callback', [PaymentController::class, 'handleCallback']);
public function handleCallback(Request $request)
{
$response = EasyPaisa::verifyCallback($request->all());
if ($response->success()) {
// Payment verified, update order status
}
}
Initiating Payments
Use EasyPaisa::initiatePayment() for one-time payments. Pass an associative array with required fields:
$paymentData = [
'amount' => 500.00,
'order_id' => 'INV-'.Str::uuid(),
'customer_name' => 'Customer Name',
'customer_email' => 'customer@example.com',
'customer_phone' => '03001234567',
'callback_url' => route('easypaisa.callback'),
'description' => 'Product Purchase',
];
Subscription Payments
For recurring payments, use the subscription_id and plan_id fields:
$response = EasyPaisa::initiateSubscriptionPayment([
'amount' => 200.00,
'subscription_id' => 'SUB_123',
'plan_id' => 'PLAN_456',
'customer_phone' => '03001234567',
]);
Handling Callbacks
Always verify the callback using EasyPaisa::verifyCallback() before processing:
public function handleCallback(Request $request)
{
$callbackData = $request->all();
$response = EasyPaisa::verifyCallback($callbackData);
if ($response->success()) {
// Update order status, send confirmation, etc.
return response()->json(['status' => 'success']);
}
return response()->json(['status' => 'failed'], 400);
}
Webhooks For real-time updates, configure webhooks in the EasyPaisa merchant dashboard and point them to a Laravel endpoint. Use middleware to validate signatures:
public function handleWebhook(Request $request)
{
$signature = $request->header('X-EasyPaisa-Signature');
$payload = $request->getContent();
if (EasyPaisa::verifyWebhook($payload, $signature)) {
// Process webhook event
}
}
Laravel Cashier Integration Extend the package to work with Laravel Cashier for subscription management:
use Laravel\Cashier\Subscription;
$subscription = Subscription::create('plan_name', 200.00);
$easypaisaResponse = EasyPaisa::initiateSubscriptionPayment([
'subscription_id' => $subscription->id,
'plan_id' => 'PLAN_456',
'customer_phone' => auth()->user()->phone,
]);
Logging Enable logging for debugging:
EASYPAISA_LOG_ENABLED=true
Testing
Use the EasyPaisa::setTestMode(true) to test payments without processing real transactions:
EasyPaisa::setTestMode(true);
Callback Verification
verifyCallback(). Skipping this can lead to fraudulent transactions.initiatePayment() matches the route handling the callback.Amount Precision
100.00 becomes 10000). The package handles this internally, but ensure your input is correct.100.00), not integers.Phone Number Format
+923001234567). Missing this can cause failures.Test Mode Quirks
Webhook Signatures
EasyPaisa::verifyWebhook($payload, $signature) and ensure the EASYPAISA_WEBHOOK_SECRET in .env matches the one in your EasyPaisa dashboard.Enable Logging
Set EASYPAISA_LOG_ENABLED=true in .env to log API requests/responses to storage/logs/easypaisa.log.
Check HTTP Status Codes
The package returns response objects with success(), getStatusCode(), and getMessage(). Log these for debugging:
$response = EasyPaisa::initiatePayment([...]);
if (!$response->success()) {
logger()->error('EasyPaisa Error: ' . $response->getMessage());
}
Validate API URLs
Ensure EASYPAISA_API_URL in .env is correct (e.g., https://api.easypaisa.com.pk/api/ for live, https://sandbox.easypaisa.com.pk/api/ for test).
Order ID Uniqueness
EasyPaisa requires unique order_ids. Reuse can cause conflicts. Use UUIDs or database checks:
$orderId = 'ORDER_' . Str::uuid();
Custom Responses
Override the default response handling by extending the Zfhassaan\EasyPaisa\EasyPaisa class:
namespace App\Services;
use Zfhassaan\EasyPaisa\EasyPaisa as BaseEasyPaisa;
class CustomEasyPaisa extends BaseEasyPaisa
{
public function initiatePayment(array $data)
{
$response = parent::initiatePayment($data);
// Custom logic here
return $response;
}
}
Additional Fields
EasyPaisa supports custom fields. Pass them via the custom_fields key:
$response = EasyPaisa::initiatePayment([
'amount' => 100.00,
'custom_fields' => [
'user_id' => auth()->id(),
'product_id' => 123,
],
]);
Retry Logic
Implement retry logic for failed payments using Laravel's retry helper or a queue job:
use Illuminate\Support\Facades\Retry;
Retry::times(3)->attempt(function () {
$response = EasyPaisa::initiatePayment([...]);
if (!$response->success()) {
throw new \Exception('Payment failed');
}
});
Localization Customize error messages by overriding the language file:
// config/easypaisa.php
'messages' => [
'payment_f
How can I help you explore Laravel packages today?