A Laravel wrapper package for the Razorpay Curlec API. Built directly on Laravel's HTTP client, it provides a fluent facade for Payment Links, Orders, Payments, Refunds, and Settlements with database persistence and webhook-driven, event-based sync out of the box.
- π Payment Links β create, fetch, update, cancel, list, resend notification
- π§Ύ Orders β create, fetch, list, update, fetch payments for an order, plus Checkout payment-signature verification
- π³ Payments β fetch, capture, update, list β synced locally from both the API response and the
payment.authorized/captured/failedwebhooks - πΈ Refunds β create, fetch, list (account-wide and per-payment), update
- π¦ Settlements β fetch, list β synced locally from both the API response and the
settlement.processedwebhook - ποΈ Local database persistence for every resource (
RazorpayPaymentLink,RazorpayOrder,RazorpayPayment,RazorpayRefund,RazorpaySettlement), each apayment_id/razorpay_idjoin away from the others - π API request/response logging β every outbound call recorded, with customer PII redacted and configurable retention
- π΅οΈ Webhook audit log β every signature-valid inbound webhook recorded for later audit, separately from signature-failure diagnostics in your app's log channel
- π Automatic webhook handling with HMAC-SHA256 signature verification
- π Idempotent webhook sync β local records stay correct even if Razorpay retries a delivery
- π― Generic + typed events for every webhook-driven state change
- π¦ Uses Laravel's HTTP client only β no Guzzle, no vendor SDK
- π‘οΈ Type-safe with PHP 8.2+ backed enums
- π« Package-owned exception hierarchy β never leaks raw HTTP or vendor exceptions
- PHP 8.2+ (Laravel 13.x itself requires PHP 8.3+)
- Laravel 10.x, 11.x, 12.x, or 13.x
Install the package via composer:
composer require laraditz/razorpayPublish the configuration and migrations:
php artisan vendor:publish --tag=razorpay-config
php artisan vendor:publish --tag=razorpay-migrationsRun the migrations:
php artisan migrateAdd your Razorpay credentials to .env:
RAZORPAY_KEY_ID=rzp_test_your_key_id
RAZORPAY_KEY_SECRET=your_key_secret
RAZORPAY_WEBHOOK_SECRET=your_webhook_secret
RAZORPAY_CURRENCY=MYRconfig/razorpay.php:
return [
'key_id' => env('RAZORPAY_KEY_ID'),
'key_secret' => env('RAZORPAY_KEY_SECRET'),
'base_url' => env('RAZORPAY_BASE_URL', 'https://api.razorpay.com/v1'),
'default_currency' => env('RAZORPAY_CURRENCY', 'MYR'),
'timeout' => env('RAZORPAY_TIMEOUT', 30),
'webhook_secret' => env('RAZORPAY_WEBHOOK_SECRET'),
'webhook_path' => env('RAZORPAY_WEBHOOK_PATH', '/razorpay/webhook'),
'log_api_calls' => env('RAZORPAY_LOG_API_CALLS', true),
'api_log_retention_days' => env('RAZORPAY_API_LOG_RETENTION_DAYS', 30),
'log_webhook_calls' => env('RAZORPAY_LOG_WEBHOOK_CALLS', true),
'webhook_log_retention_days' => env('RAZORPAY_WEBHOOK_LOG_RETENTION_DAYS', 30),
];Every service is accessed through the Razorpay facade. Full method reference, parameters, and more examples for each are in /docs β linked below and in the Documentation section.
use Laraditz\Razorpay\Facades\Razorpay;
$link = Razorpay::paymentLink()->create([
'amount' => 50000, // smallest currency subunit
'currency' => 'MYR',
'customer' => ['name' => 'John Doe', 'email' => 'john@example.com'],
'reference_id' => 'ORDER-123',
]);
return redirect($link['short_url']);use Laraditz\Razorpay\Facades\Razorpay;
$order = Razorpay::order()->create(['amount' => 50000, 'currency' => 'MYR']);
// Pass $order['id'] to Checkout.js, then verify the signature it returns:
$isValid = Razorpay::order()->verifyPaymentSignature(
$request->input('razorpay_order_id'),
$request->input('razorpay_payment_id'),
$request->input('razorpay_signature'),
);use Laraditz\Razorpay\Facades\Razorpay;
$payment = Razorpay::payment()->fetch('pay_29QQoUBi66xm2f');
$payment = Razorpay::payment()->capture('pay_29QQoUBi66xm2f', ['amount' => 50000, 'currency' => 'MYR']);use Laraditz\Razorpay\Facades\Razorpay;
$refund = Razorpay::refund()->create('pay_29QQoUBi66xm2f', ['amount' => 10000]);use Laraditz\Razorpay\Facades\Razorpay;
$settlements = Razorpay::settlement()->all(['count' => 20]);Every create() call (and, for Payments/Settlements, every fetch()/capture()/update()/all() call too) persists a local Eloquent record, kept in sync automatically as webhooks arrive β no manual polling required:
use Laraditz\Razorpay\Models\RazorpayOrder;
$order = RazorpayOrder::where('razorpay_id', 'order_EKwxwAgItmmXdp')->first();
if ($order->status->isPaid()) {
// ...
}
$order->payment; // the RazorpayPayment that settled it, via the payment_id columnEvery non-2xx API response is caught and rethrown as a package-owned exception β you never need to catch raw HTTP client exceptions:
use Laraditz\Razorpay\Exceptions\AuthenticationException; // 401
use Laraditz\Razorpay\Exceptions\ValidationException; // 400, carries field errors via getErrors()
use Laraditz\Razorpay\Exceptions\ApiException; // any other 4xx/5xx, carries the full body via getResponse()
try {
Razorpay::paymentLink()->create(['amount' => 50000]);
} catch (ValidationException $e) {
logger()->warning('Razorpay validation failed', $e->getErrors());
} catch (ApiException $e) {
logger()->error('Razorpay API error', $e->getResponse());
}A webhook route is registered automatically at POST /razorpay/webhook (configurable via RAZORPAY_WEBHOOK_PATH) β never part of Laravel's web middleware group, so the X-Razorpay-Signature header is the sole authentication boundary. Point your Razorpay Dashboard's webhook URL at it and set RAZORPAY_WEBHOOK_SECRET.
Typed events fire for the events this package understands (PaymentLinkPaid, PaymentAuthorized, PaymentCaptured, PaymentFailed, OrderPaid, RefundCreated/Processed/Failed, SettlementProcessed), each keeping the matching local record in sync β plus a generic RazorpayWebhookReceived event for every verified delivery, regardless of type.
class FulfillOrder
{
public function handle(\Laraditz\Razorpay\Events\PaymentLinkPaid $event): void
{
if ($event->paymentLink === null) {
return;
}
// $event->paymentLink->status is already PaymentLinkStatus::Paid here
}
}β Full documentation β event reference, listener setup, idempotency notes
Outbound API calls and inbound webhook deliveries can each be optionally recorded for troubleshooting/audit, independently toggled and retained:
RAZORPAY_LOG_API_CALLS=false
RAZORPAY_LOG_WEBHOOK_CALLS=falseFor detailed documentation on each service, please refer to:
- Payment Links β create, fetch, update, cancel, list, resend notification
- Orders β create, fetch, list, update, fetch payments for an order, Checkout signature verification
- Payments β fetch, capture, update, list
- Refunds β full/partial refunds, account-wide and per-payment listing
- Settlements β fetch, list, reconciliation
- Webhooks β event reference, listener setup, idempotency
- Logging β API request/response logging and webhook audit log
composer testThe test suite uses Http::fake()/Event::fake() throughout β no real network access or live Razorpay credentials are required.
If you discover any security related issues, please email raditzfarhan@gmail.com instead of using the issue tracker.
The MIT License (MIT).