Back to Homepage

Razorpay Webhook in Laravel: Professional Approach

4 min read

Tags:

Laravel
Razorpay Webhook in Laravel: Professional Approach

Webhooks are a powerful way to get real-time notifications for events happening within a system. If you're dealing with payments, Razorpay webhooks can be important in updating your system the moment a payment or refund is processed. Here's how you can use Razorpay webhooks in Laravel to handle payment and refund events efficiently in PHP, particularly using Laravel's job system.

Notice: Please ensure your laravel application has spatie/laravel-webhook-client package installed.

Setting Up Razorpay Webhooks in Laravel

Let's start with a basic code layout inspired by a real-world implementation. The code uses Laravel's job system to handle incoming webhooks and updates the database accordingly.

namespace App\Jobs\RazorpayWebhooks;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Spatie\WebhookClient\Jobs\ProcessWebhookJob;
use Spatie\WebhookClient\Models\WebhookCall;
use App\Events\PaymentReceived;
use App\Events\RefundCreated;
use App\Models\Order;
use Illuminate\Bus\Queueable;
// ... (import statements)

class ProcessRazorpayWebhookJob extends ProcessWebhookJob implements ShouldQueue
{
        use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

public WebhookCall $webhookCall;

    /**
     * Create a new job instance.
     */
    public function __construct(WebhookCall $webhookCall)
    {
        $this->webhookCall = $webhookCall;
    }

    public function handle(): void
    {
        if ($this->webhookCall->payload['event'] === 'order.paid') {
            $this->processOrderPaidEvent();
        }

        if ($this->webhookCall->payload['event'] === 'refund.created') {
            $this->processRefundCreatedEvent();
        }
    }

}

Why Use Jobs?

Using Laravel jobs for handling webhooks offers several advantages:

  1. Asynchrony: Jobs can be processed asynchronously, freeing your main application from doing the heavy work.

  2. Retries: If the job fails, you can easily configure it to retry.

  3. Logging and Monitoring: Laravel's job system comes with built-in logging and monitoring capabilities.

How Razorpay Webhooks in Laravel Work: The Code Structure

When an order is paid, Razorpay sends a webhook with the event name 'order.paid'. Here's how it's handled:

public function processOrderPaidEvent()
    {
        $order = Order::query()->findOrFail($this->webhookCall->payload['payload']['order']['entity']['receipt']);

        $order->payments()->create([
            'reference' => $this->webhookCall->payload['payload']['payment']['entity']['id'],
            'amount' => $this->webhookCall->payload['payload']['payment']['entity']['amount'] / 100,
            'currency' => \Str::upper($this->webhookCall->payload['payload']['payment']['entity']['currency']),
            'status' => 'Paid',
        ]);

        $order->payment_status = PaymentStatus::PAID;

        $order->save();
    }

Detailed Logic

  1. The code fetches the corresponding order using the receipt.

  2. A new payment record is created with relevant information.

  3. The order status is updated to PAID.

In your Routes, Add the following route code

Route::webhooks('webhooks/razorpay', 'razorpay');

Handling Refunds

Similarly, when a refund is created, Razorpay sends a webhook with the event 'refund.created'.

public function processRefundCreatedEvent()
    {
        $order = Order::query()->where('razorpay_payment_id', $this->webhookCall->payload['payload']['refund']['entity']['payment_id'])->firstOrFail();

   $refund = $order->refunds()->create([
      'amount' => $this->webhookCall->payload['payload']['refund']['entity']['amount'] / 100,
      'meta' => [
        'razorpay_refund_id' => $this->webhookCall->payload['payload']['refund']['entity']['id'],
            ]
       ]);


    }

Detailed Logic

  1. The code fetches the corresponding order using the payment_id.

  2. A new refund record is created.

Configuration File

Create webhook-client.php in config directory

<?php

return [
    'configs' => [
        // Do not change the order of every services declared here.
        [
            'name' => 'razorpay',
            'signing_secret' => env('RAZORPAY_WEBHOOK_SECRET'),
            'signature_header_name' => 'X-Razorpay-Signature',
            'signature_validator' => \Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator::class,
            'webhook_profile' => \Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile::class,
            'webhook_response' => \Spatie\WebhookClient\WebhookResponse\DefaultRespondsTo::class,
            'webhook_model' => \Spatie\WebhookClient\Models\WebhookCall::class,
            'process_webhook_job' => \App\Jobs\RazorpayWebhooks\ProcessRazorpayWebhookJob::class,
        ],
    ],

    /*
     * The integer amount of days after which models should be deleted.
     *
     * 7 deletes all records after 1 week. Set to null if no models should be deleted.
     */
    'delete_after_days' => 30,
];

Opinions and Recommendations

  1. Validation: Always validate webhook payloads against the Razorpay signature for security.

  2. Make sure your handlers are never creating duplicate records. If you receive the same webhook event multiple times, it shouldn't create duplicate records. The sample code has this built-in.

  3. Monitoring: Keep an eye on the job queue and set up alerts for failures.

  4. Testing: Always simulate webhook events in your local or staging environments before moving to production.

Webhooks, when used correctly, can offer a significant boost in operational efficiency and user experience. And with a modern PHP framework like Laravel, implementing them is a breeze.

Follow @LaravelSage on X → Follow @LaravelSage on Facebook →
Aniket Singh

Aniket Singh

View All Articles

Full-stack developer with a knack for Merging creativity with technical expertise for standout solutions.

Related Articles

data_forget Helper for Laravel

data_forget Helper for Laravel

Since Laravel version 10.15, there is a new utility function called data_forget that allows you to remove keys from an array or object using a "dot" notation.

Laravel Tenant Application with Tenancy

Laravel Tenant Application with Tenancy

You can make your Laravel app multi-tenant using the Tenancy for Laravel Tenant package. This tenancy package lets you make any Laravel application multi-tenant without rewriting it.

Top Laravel Packages for Building Powerful Applications

Top Laravel Packages for Building Powerful Applications

Are you ready to take your Laravel skills to the next level and build extraordinary applications? Look no further! In this blog post, we will unveil a treasure trove of top packages that will revolutionize your development process.

Subscribe for 20+ new Laravel tutorials every week

You can unsubscribe at any time. You'll also get -20% off my courses!

© 2024

 

Laravel Sage

   |    Privacy Policy