loveycom/cashfree
Laravel package for CashFree (India) integrating Marketplace Settlement and Payment Gateway APIs. Includes config publishing, environment switching (test/prod), and helper classes like Marketplace (e.g., checkBalance) to simplify CashFree API calls in Laravel.
Installation:
composer require loveycom/cashfree
php artisan vendor:publish --provider="LoveyCom\CashFree\CashFreeServiceProvider"
Configure config/cashfree.php with your appID, secretKey, and environment (testURL/prodURL).
First Payment Flow:
use LoveyCom\CashFree\PaymentGateway\Order;
$order = new Order();
$orderData = [
'orderId' => 'UNIQUE-ORDER-ID-' . time(),
'orderAmount' => 10000, // INR (use integers)
'customerName' => 'John Doe',
'customerEmail' => 'john@example.com',
'customerPhone' => '919999999999',
'returnUrl' => route('payment.success'),
'notifyUrl' => route('payment.webhook'),
];
$response = $order->create($orderData);
$paymentLink = $order->getLink($orderData['orderId']);
Redirect users to $paymentLink for payment.
First Marketplace Action:
use LoveyCom\CashFree\Marketplace\Vendor;
$vendor = new Vendor();
$vendorData = [
'vendorName' => 'Vendor ABC',
'vendorEmail' => 'vendor@example.com',
'vendorPhone' => '919876543210',
'bankDetails' => [
'accountNumber' => '1234567890',
'ifscCode' => 'HDFC0001234',
],
];
$createdVendor = $vendor->create($vendorData);
config/cashfree.php (API keys, environment).Order and Vendor.notifyUrl endpoint handles CashFree’s webhook payloads.// Create order (returns orderId)
$orderId = $order->create($orderData)->orderId;
// Redirect user to payment link
return redirect($order->getLink($orderId));
// Verify after redirect (e.g., in success controller)
$status = $order->getStatus($orderId);
if ($status->paymentStatus === 'PAID') {
// Fulfill order
}
$refund = $refundService->create(
$orderId,
'REF-' . time(),
5000, // Amount to refund
'Customer requested refund'
);
$vendorId = 'VENDOR-123';
$payout = $vendor->requestVendorPayout($vendorId, 50000);
$transaction->attachVendorToTransaction(
$orderId,
$vendorId,
10, // 10% commission
500 // Fixed amount (optional)
);
Route::post('/payment/webhook', function (Request $request) {
$payload = $request->all();
// Validate signature (CashFree provides `x-cashfree-signature` header)
if (validatesCashFreeSignature($payload, $request->header('x-cashfree-signature'))) {
switch ($payload['event']) {
case 'ORDER.PAID':
// Update order status
break;
case 'REFUND.COMPLETED':
// Update refund status
break;
}
}
});
Error Handling:
Wrap API calls in try-catch blocks. CashFree returns HTTP errors (e.g., 400 for invalid orderId):
try {
$status = $order->getStatus($orderId);
} catch (\LoveyCom\CashFree\Exceptions\CashFreeException $e) {
Log::error('CashFree API Error: ' . $e->getMessage());
// Retry or notify user
}
Idempotency:
Use unique orderId/referenceId values to avoid duplicate transactions. CashFree’s API supports idempotency keys for refunds (pass via merchantRefundId).
Testing:
Use CashFree’s sandbox (isLive: false) for development. Mock the notifyUrl endpoint to test webhooks:
Route::post('/payment/webhook', function () {
// Log payload for testing
file_put_contents(storage_path('logs/cashfree_webhook.log'), print_r(request()->all(), true));
});
Batch Operations:
For ledgers/transactions, paginate results using maxReturn and lastReturnId:
$ledger = $vendor->getLedger($vendorId, 50, $lastReturnId);
Laravel Service Container: Bind the package to the container for dependency injection:
$this->app->bind('cashfree.order', function ($app) {
return new \LoveyCom\CashFree\PaymentGateway\Order();
});
Then inject via constructor:
public function __construct(private Order $order) {}
API Key Exposure:
appID/secretKey in config/cashfree.php may expose credentials in version control..env:
CASHFREE_APP_ID=your_app_id
CASHFREE_SECRET_KEY=your_secret_key
Update the config file to read from .env:
'appID' => env('CASHFREE_APP_ID'),
'secretKey' => env('CASHFREE_SECRET_KEY'),
Webhook Signature Validation:
x-cashfree-signature) to verify webhook authenticity. The package does not include validation logic.use Illuminate\Support\Facades\Http;
function validatesCashFreeSignature(array $payload, string $signature): bool {
$secretKey = config('cashfree.secretKey');
$stringToSign = http_build_query($payload);
$expectedSignature = hash_hmac('sha256', $stringToSign, $secretKey);
return hash_equals($expectedSignature, $signature);
}
Amount Precision:
10000 for ₹100). Floating-point values (e.g., 100.00) will fail.$amountInPaise = (int) ($amount * 100);
Order ID Uniqueness:
orderId values may cause conflicts. CashFree’s API does not enforce uniqueness globally.$orderId = 'ORD-' . Str::uuid()->toString();
Rate Limits:
use Illuminate\Support\Facades\Queue;
Queue::later(now()->addSeconds(5), function () use ($orderId) {
$order->getStatus($orderId);
});
Webhook Retries:
notifyUrl must be idempotent.if (Order::where('cashfree_order_id', $payload['orderId'])->exists()) {
return response()->json(['status' => 'OK']);
}
namespace App\Http\Middleware;
How can I help you explore Laravel packages today?